Code Sample Type: XPLM200 SDK

Camera

/*
 * Camera.c
 * 
 * This plugin registers a new view with the sim that orbits the aircraft.  We do this by:
 * 
 * 1. Registering a hotkey to engage the view.
 * 2. Setting the view to external when we are engaged.
 * 3. Registering a new camera control funcioin that ends when a new view is picked.
 * 
 */

#include <string.h>
#include "XPLMDisplay.h"
#include "XPLMUtilities.h"
#include "XPLMCamera.h"
#include "XPLMDataAccess.h"
#include "XPLMDisplay.h"
#include <stdio.h>
#include <math.h>

static XPLMHotKeyID	gHotKey = NULL;
static XPLMDataRef		gPlaneX = NULL;
static XPLMDataRef		gPlaneY = NULL;
static XPLMDataRef		gPlaneZ = NULL;


static void	MyHotKeyCallback(void *               inRefcon);    
static int 	MyOrbitPlaneFunc(
                                   XPLMCameraPosition_t * outCameraPosition,  
                                   int                  inIsLosingControl,    
                                   void *               inRefcon);    


PLUGIN_API int XPluginStart(
						char *		outName,
						char *		outSig,
						char *		outDesc)
{
	strcpy(outName, "Camera");
	strcpy(outSig, "xplanesdk.examples.camera");
	strcpy(outDesc, "A plugin that adds a camera view.");

	/* Prefetch the sim variables we will use. */
	gPlaneX = XPLMFindDataRef("sim/flightmodel/position/local_x");
	gPlaneY = XPLMFindDataRef("sim/flightmodel/position/local_y");
	gPlaneZ = XPLMFindDataRef("sim/flightmodel/position/local_z");

	/* Register our hot key for the new view. */
	gHotKey = XPLMRegisterHotKey(XPLM_VK_F8, xplm_DownFlag, 
				"Circling External View",
				MyHotKeyCallback,
				NULL);
	return 1;
}

PLUGIN_API void	XPluginStop(void)
{
	XPLMUnregisterHotKey(gHotKey);
}

PLUGIN_API void XPluginDisable(void)
{
}

PLUGIN_API int XPluginEnable(void)
{
	return 1;
}

PLUGIN_API void XPluginReceiveMessage(
					XPLMPluginID	inFromWho,
					int				inMessage,
					void *			inParam)
{
}

void	MyHotKeyCallback(void *               inRefcon)
{
	/* This is the hotkey callback.  First we simulate a joystick press and
	 * release to put us in 'free view 1'.  This guarantees that no panels
	 * are showing and we are an external view. */
	XPLMCommandButtonPress(xplm_joy_v_fr1);
	XPLMCommandButtonRelease(xplm_joy_v_fr1);
	
	/* Now we control the camera until the view changes. */
	XPLMControlCamera(xplm_ControlCameraUntilViewChanges, MyOrbitPlaneFunc, NULL);
}

/*
 * MyOrbitPlaneFunc
 * 
 * This is the actual camera control function, the real worker of the plugin.  It is 
 * called each time X-Plane needs to draw a frame.
 * 
 */
int 	MyOrbitPlaneFunc(
                                   XPLMCameraPosition_t * outCameraPosition,   
                                   int                  inIsLosingControl,    
                                   void *               inRefcon)
{
	if (outCameraPosition && !inIsLosingControl)
	{
			int	w, h, x, y;
			float dx, dz, dy, heading, pitch;
			char	buf[256];
		
		/* First get the screen size and mouse location.  We will use this to decide
		 * what part of the orbit we are in.  The mouse will move us up-down and around. */
		XPLMGetScreenSize(&w, &h);
		XPLMGetMouseLocation(&x, &y);
		heading = 360.0 * (float) x / (float) w;
		pitch = 20.0 * (((float) y / (float) h) * 2.0 - 1.0);
		
		/* Now calculate where the camera should be positioned to be 200
		 * meters from the plane and pointing at the plane at the pitch and
		 * heading we wanted above. */
		dx = -200.0 * sin(heading * 3.1415 / 180.0);
		dz = 200.0 * cos(heading * 3.1415 / 180.0);
		dy = -200 * tan(pitch * 3.1415 / 180.0);
		
		/* Fill out the camera position info. */
		outCameraPosition->x = XPLMGetDataf(gPlaneX) + dx;
		outCameraPosition->y = XPLMGetDataf(gPlaneY) + dy;
		outCameraPosition->z = XPLMGetDataf(gPlaneZ) + dz;
		outCameraPosition->pitch = pitch;
		outCameraPosition->heading = heading;
		outCameraPosition->roll = 0;		

	}
	
	/* Return 1 to indicate we want to keep controlling the camera. */
	return 1;
}
Leave a comment

Beacons and Strobes

A discussion of custom beacons and strobes for authors can be found here.

/*
/*

	Beacon and strobe example plugin.
	
	This plugin overrides X-Plane's default flash patterns for the beacons and strobes.  It provides a multi-part pulse for the strobes,
	and sets the beacons to run at different rates.

*/

#include "XPLMDataAccess.h"
#include "XPLMProcessing.h"
#include "XPLMUtilities.h"

#include <string.h>
#include <math.h>

// The time stamp that the strobes last fired.
float strobe1_t = 0.0, strobe2_t = 0.0;

/* This is the period of the entire strobe sequence. */
#define STROBE_TIME 1.0

/* This is the time between the first and second strobe firing. */
#define STROBE_OFFSET 0.2

static XPLMDataRef override_beacons_and_strobes = NULL;

// These tell us if the strobes and beacons are really on.  They take into consideration both the switch positions and electrical failures.	
static XPLMDataRef beacon_lights_on = NULL;
static XPLMDataRef strobe_lights_on = NULL;

// When we are overriding beacons and strobes, we are responsible for writing each of these per frame. 
// These are four-item arrays, so we can make multiple strobe flash patterns and separately running beacons.
static XPLMDataRef beacon_brightness_ratio = NULL;
static XPLMDataRef strobe_brightness_ratio = NULL;
// Set this to 1 if ANY strobe is on - this is what makes the clouds light up, etc.
static XPLMDataRef strobe_flash_now = NULL;

// Deferred init.  Plugins should wait until the sim is really running to do final
// initialization.  See http://www.xsquawkbox.net/xpsdk/mediawiki/DeferredInitialization
// for more info.
static float deferred_init(
                                   float                inElapsedSinceLastCall,    
                                   float                inElapsedTimeSinceLastFlightLoop,    
                                   int                  inCounter,    
                                   void *               inRefcon)
{
	XPLMSetDatai(override_beacons_and_strobes,1);
	
	strobe1_t = XPLMGetElapsedTime();
	strobe2_t = strobe1_t + STROBE_OFFSET;
	return 0;
}

// This is our main per-frame lighting function.  This is where we do the work X-plane would normally
// do to calculate what the beacons and strobes are doing.
static float lights_per_frame(
                                   float                inElapsedSinceLastCall,    
                                   float                inElapsedTimeSinceLastFlightLoop,    
                                   int                  inCounter,    
                                   void *               inRefcon)
{
	float now = XPLMGetElapsedTime();
	float beacons[4] = { 0 };
	float flash[4] = { 0 };
	int	any_flash = 0;	
	int strobes_on = XPLMGetDatai(strobe_lights_on);
	
	if(beacon_lights_on == NULL || strobe_lights_on == NULL || beacon_brightness_ratio == NULL)
	{
		XPLMDebugString("We are missing our datarefs.\n");
		return 0.0f;
	}

	// Beacon lighting calculation.  If the beacons are on, use sine and cosine for the two
	// beacons...and use a slightly different scale so they run at different spededs.

	if(XPLMGetDatai(beacon_lights_on))
	{
		beacons[0] = sin(now * 1.5);
		beacons[1] = cos(now);
	}
	XPLMSetDatavf(beacon_brightness_ratio,beacons,0,4);	

	// Strobes.  Each time we are past our "strobe time", flash for one frame, then go out again.
	// Note that this is a TERRIBLE strobing algorithm: at high fps the strobes are on for TINY amounts of time.
	// In your strobing algorithm, make sure the strobes are on for the longer of one frame or a certain minimum time.
	// Some users do run at 80+ fps!

	
	if ((now - strobe1_t) > STROBE_TIME)
	{
		flash[0] = strobes_on;
		any_flash |= strobes_on;
		strobe1_t += STROBE_TIME;
	}
	if ((now - strobe2_t) > STROBE_TIME)
	{
		flash[1] = strobes_on;
		any_flash |= strobes_on;
		strobe2_t += STROBE_TIME;
	}

	XPLMSetDatavf(strobe_brightness_ratio,flash,0,4);
	XPLMSetDatai(strobe_flash_now, any_flash);

	return -1.0;	
}


PLUGIN_API int XPluginStart(
						char *		outName,
						char *		outSig,
						char *		outDesc)
{
	strcpy(outName, "Custom Beacons/Strobes Example");
	strcpy(outSig, "xplanesdk.examples.custom_beacons_strobes");
	strcpy(outDesc, "A plugin that demonstrates custom beacon/strobe patterns.");

	override_beacons_and_strobes = XPLMFindDataRef("sim/flightmodel2/lights/override_beacons_and_strobes");
	
	if(override_beacons_and_strobes == NULL)
	{
		XPLMDebugString("Beacon and strobe plugin disabled - this version of x-plane doesn't support the feature.\n");
		return 0;
	}

	beacon_lights_on = XPLMFindDataRef("sim/cockpit/electrical/beacon_lights_on");
	strobe_lights_on = XPLMFindDataRef("sim/cockpit/electrical/strobe_lights_on");
	beacon_brightness_ratio = XPLMFindDataRef("sim/flightmodel2/lights/beacon_brightness_ratio");
	strobe_brightness_ratio = XPLMFindDataRef("sim/flightmodel2/lights/strobe_brightness_ratio");
	strobe_flash_now = XPLMFindDataRef("sim/flightmodel2/lights/strobe_flash_now");	

	XPLMRegisterFlightLoopCallback(deferred_init, -1.0, NULL);
	XPLMRegisterFlightLoopCallback(lights_per_frame, -2.0, NULL);
	
	
	return 1;
}

PLUGIN_API void	XPluginStop(void)
{
	XPLMUnregisterFlightLoopCallback(deferred_init,NULL);
	XPLMUnregisterFlightLoopCallback(lights_per_frame,NULL);
	XPLMSetDatai(override_beacons_and_strobes,0);

}

PLUGIN_API void XPluginDisable(void)
{
}

PLUGIN_API int XPluginEnable(void)
{
	return 1;
}

PLUGIN_API void XPluginReceiveMessage(
					XPLMPluginID	inFromWho,
					int				inMessage,
					void *			inParam)
{
}
1 Comment

SDK210Tests

/*
SDK210Tests Example
Tests the new SDK 2.10 functions.

Version 1.0.0.1		Intitial Sandy Barbour 25/01/2012

NOTES
1.
This example will only work with the new plugin SDK 2.10 and will only run in X-Plane V10.

2.
The 2.10 plugin SDK now has 64 bit safe variables.

Unfortunately early Micrsoft Visual Studio products do not have the required header files to support this.
I know that VS6 does not and I believe that 2003 does not either.
VS2008 onwards is fine and that is what I tested this example with on windows.

So, any VS6 users will need to try and located a header that will allow the example to compile.
This is a MS problem and not an SDK problem.

I did find this header that worked.

http://www.azillionmonkeys.com/qed/pstdint.h

But we will not be supporting it or any other headers usage in this example.
*/

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

#include "XPLMPlugin.h"
#include "XPLMDisplay.h"
#include "XPLMProcessing.h"
#include "XPLMDataAccess.h"
#include "XPLMMenus.h"
#include "XPLMUtilities.h"
#include "XPWidgets.h"
#include "XPStandardWidgets.h"
#include "XPLMScenery.h"
#include "XPLMPlanes.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#if IBM
#include <windows.h>
#include <stdio.h>
#endif
#if LIN
#include <GL/gl.h>
#else
#if __GNUC__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#endif

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

enum
{
/*0*/	MAIN_FLCB = 0,
/*1*/	TEMP_FLCB,
/*2*/	INFO,
/*3*/	MENU_2,
/*4*/	DRAWING
};

enum
{
/*0*/	USER_AIRCRAFT = 0,
/*1*/	AI_AIRCRAFT
};

char SDK210TestsVersionNumber[] = "v1.00";

char SDK210Tests_Buffer[256];
XPLMFlightLoopID SDK210Tests_XPLMTempFlightLoopID = NULL;
XPLMObjectRef	SDK210Tests_Windsock=NULL;
XPLMProbeRef	SDK210Tests_Windsock_Probe=NULL;

// Number of edit boxes in widget
#define SDK210TESTS_MAX_ITEMS 5

// Description text for widget edit boxes
char SDK210TestsDataDesc[SDK210TESTS_MAX_ITEMS][255] = {"Main FLCB", "Temp FLCB", "Info", "Menu 2", "Drawing"};

// Used as interlock for widget
int SDK210TestsMenuItem1;

// Used by menus
XPLMMenuID	SDK210TestsMenuId = NULL, SDK210TestsMenuId2 = NULL;
int	SDK210TestsMenuIndex1 = 0, SDK210TestsMenuIndex2 = 0, SDK210TestsMenuIndex3 = 0;
int	SDK210TestsMenuItem = 0, SDK210TestsMenuItem2 = 0;
int SDK210TestsCreateFLCB = 0;
int SDK210TestsDestroyFLCB = 0;

// Datarefs
XPLMDataRef	SDK210Tests_refx = NULL;
XPLMDataRef	SDK210Tests_refy = NULL;
XPLMDataRef	SDK210Tests_refz = NULL;
XPLMDataRef	SDK210Tests_wrl_type = NULL;

// Widgets
XPWidgetID	SDK210TestsWidget = NULL, SDK210TestsWindow = NULL;
XPWidgetID	SDK210TestsButton1 = NULL, SDK210TestsButton2 = NULL, SDK210TestsButton3 = NULL, SDK210TestsButton4 = NULL;
XPWidgetID	SDK210TestsButton5 = NULL, SDK210TestsButton6 = NULL, SDK210TestsButton7 = NULL, SDK210TestsButton8 = NULL;
XPWidgetID	SDK210TestsText[SDK210TESTS_MAX_ITEMS] = {NULL};
XPWidgetID	SDK210TestsEdit[SDK210TESTS_MAX_ITEMS] = {NULL};

// Used by the Draw Objects test
XPLMObjectRef	SDK210TestsObject=NULL;
int SDK210TestsDrawingEnabled = 0;

// Used by the FLCB test
int	SDK210TestsMainLoopCBCounter = 0;
int	SDK210TestsTempLoopCBCounter = 0;

char *pSDK210TestsAircraft[2];
char SDK210TestsAircraftPath[2][512];

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

// Displays data in widget
float SDK210TestsMainLoopCB(float elapsedMe, float elapsedSim, int counter, void * refcon);;
float SDK210TestsTempLoopCB(float elapsedMe, float elapsedSim, int counter, void * refcon);;

// Callback for Error Tests
void	SDK210TestsErrorCB(const char * msg)
{
	XPLMDebugString("================================================================\n");
	XPLMDebugString("SDK210Tests: SDK210TestsErrorCB - error CB called: ");
	XPLMDebugString(msg);
	XPLMDebugString("\n");
	XPLMDebugString("----------------------------------------------------------------\n");
}

// Menu Prototypes
void SDK210TestsMenuHandler(void *, void *);
void SDK210TestsMenuHandler2(void *, void *);

// Widget prototypes
void CreateSDK210Tests(int x1, int y1, int w, int h);

int SDK210TestsHandler(
						XPWidgetMessage			inMessage,
						XPWidgetID				inWidget,
						intptr_t				inParam1,
						intptr_t				inParam2);

// Prototype for Draw Object tests
int	SDK210TestsDrawWindsock(
                                   XPLMDrawingPhase     inPhase,    
                                   int                  inIsBefore,    
                                   void *               inRefcon);


void SDK210TestsNativePaths(void);
void SDK210TestsAcquireAircraft(void);
void SDK210TestsLoadAircraft(int Mode);

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

PLUGIN_API int XPluginStart(
						char *		outName,
						char *		outSig,
						char *		outDesc)
{
	strcpy(outName, "SDK210Tests");
	strcpy(outSig, "sandybarbour.projects.SDK210Tests");
	strcpy(outDesc, "A plug-in to test the new SDK210 API's.");

	// Register the callback for errors
	XPLMSetErrorCallback(SDK210TestsErrorCB);

	// Datarefs to get the aicraft position
	SDK210Tests_refx = XPLMFindDataRef("sim/flightmodel/position/local_x");
	SDK210Tests_refy = XPLMFindDataRef("sim/flightmodel/position/local_y");
	SDK210Tests_refz = XPLMFindDataRef("sim/flightmodel/position/local_z");

	SDK210Tests_wrl_type = XPLMFindDataRef("sim/graphics/view/world_render_type");

	// Create the menus
	SDK210TestsMenuItem = XPLMAppendMenuItem(XPLMFindPluginsMenu(), "SDK210Tests", NULL, 1);
	SDK210TestsMenuId = XPLMCreateMenu("SDK210Tests", XPLMFindPluginsMenu(), SDK210TestsMenuItem, SDK210TestsMenuHandler, NULL);
	SDK210TestsMenuItem2 = XPLMAppendMenuItem(SDK210TestsMenuId, "SDK210Tests", (void *)"SDK210Tests", 1);
	
	SDK210TestsMenuItem1 = 0;
	XPLMRegisterFlightLoopCallback(SDK210TestsMainLoopCB, 1.0, NULL);

	// This used for the Draw Objects tests
	XPLMRegisterDrawCallback( SDK210TestsDrawWindsock, xplm_Phase_Objects, 0, 0 );

	return 1;
}

PLUGIN_API void	XPluginStop(void)
{
	// Clean up
	if(SDK210Tests_Windsock) XPLMUnloadObject(SDK210Tests_Windsock);
	if(SDK210Tests_Windsock_Probe) XPLMDestroyProbe(SDK210Tests_Windsock_Probe);
	XPLMDestroyMenu(SDK210TestsMenuId);
    XPLMUnregisterDrawCallback(SDK210TestsDrawWindsock, xplm_Phase_Objects, 0, 0);
	XPLMUnregisterFlightLoopCallback(SDK210TestsMainLoopCB, NULL);
}

PLUGIN_API int XPluginEnable(void)
{
	return 1;
}

PLUGIN_API void XPluginDisable(void)
{
}

PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void * inParam)
{
	switch(inMsg) 
	{
		case XPLM_MSG_WILL_WRITE_PREFS:
			XPLMDebugString("================================================================\n");
			XPLMDebugString("SDK210Tests: XPluginReceiveMessage - Will write prefs.\n");
			XPLMDebugString("----------------------------------------------------------------\n");
			break;
		case XPLM_MSG_LIVERY_LOADED:
			XPLMDebugString("================================================================\n");
			sprintf(SDK210Tests_Buffer,"SDK210Tests: XPluginReceiveMessage - Livery loaded for plane %d.\n", inParam);
			XPLMDebugString(SDK210Tests_Buffer);
			XPLMDebugString("----------------------------------------------------------------\n");
			break;		
	}
}

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

// Used to test the creation and destruction of a Flightloop Callback from within a Flightloop Callback.
float SDK210TestsMainLoopCB(float elapsedMe, float elapsedSim, int counter, void * refcon)
{
	SDK210TestsMainLoopCBCounter++;
	if (SDK210TestsMainLoopCBCounter>100)
		SDK210TestsMainLoopCBCounter=0;
	if (SDK210TestsMenuItem1 == 1)
	{
		sprintf(SDK210Tests_Buffer,"Main FLCB Counter = %d\n", SDK210TestsMainLoopCBCounter);
		XPSetWidgetDescriptor(SDK210TestsEdit[MAIN_FLCB], SDK210Tests_Buffer);
	}
	if (SDK210TestsCreateFLCB)
	{
		XPLMCreateFlightLoop_t	SDK210Tests_XPLMCreateFlightLoop_t_ptr = { sizeof(XPLMCreateFlightLoop_t), xplm_FlightLoop_Phase_AfterFlightModel, SDK210TestsTempLoopCB, NULL };		
		SDK210Tests_XPLMTempFlightLoopID = XPLMCreateFlightLoop(&SDK210Tests_XPLMCreateFlightLoop_t_ptr);
		SDK210TestsCreateFLCB = 0;
	}
	if (SDK210TestsDestroyFLCB)
	{
		XPLMDestroyFlightLoop(SDK210Tests_XPLMTempFlightLoopID);
		SDK210TestsDestroyFLCB = 0;
		XPSetWidgetDescriptor(SDK210TestsEdit[TEMP_FLCB], "");
	}

	return 1.0;
}

// Temporary Flightloop Callback
float SDK210TestsTempLoopCB(float elapsedMe, float elapsedSim, int counter, void * refcon)
{
	SDK210TestsTempLoopCBCounter++;
	if (SDK210TestsTempLoopCBCounter>100)
		SDK210TestsTempLoopCBCounter=0;
	if (SDK210TestsMenuItem1 == 1)
	{
		sprintf(SDK210Tests_Buffer,"Temp FLCB Counter = %d\n", SDK210TestsTempLoopCBCounter);
		XPSetWidgetDescriptor(SDK210TestsEdit[TEMP_FLCB], SDK210Tests_Buffer);
	}

	return 1.0;
}

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

// Process Menu 1 selections
void SDK210TestsMenuHandler(void * mRef, void * iRef)
{
    // Menu selected for widget
	if (!strcmp((char *) iRef, "SDK210Tests"))
	{
		if (SDK210TestsMenuItem1 == 0)
		{
			CreateSDK210Tests(100, 550, 650, 330);
			SDK210TestsMenuItem1 = 1;
		}
	}
}						

// Process Menu 2 selections
void SDK210TestsMenuHandler2(void * mRef, void * iRef)
{
    // Menu Index 1 Selected
	if (!strcmp((char *) iRef, "1"))
	{
		if (SDK210TestsMenuItem1 == 1)
		{
			sprintf(SDK210Tests_Buffer,"Menu 2 : Item 1 : mRef = %x, iRef = %x\n", mRef, iRef);
			XPSetWidgetDescriptor(SDK210TestsEdit[MENU_2], SDK210Tests_Buffer);
		}
	}

    // Menu Index 2 Selected
	if (!strcmp((char *) iRef, "2"))
	{
		if (SDK210TestsMenuItem1 == 1)
		{
			sprintf(SDK210Tests_Buffer,"Menu 2 : Item 2 : mRef = %x, iRef = %x\n", mRef, iRef);
			XPSetWidgetDescriptor(SDK210TestsEdit[MENU_2], SDK210Tests_Buffer);
		}
	}

    // Menu Index 3 Selected
	if (!strcmp((char *) iRef, "3"))
	{
		if (SDK210TestsMenuItem1 == 1)
		{
			sprintf(SDK210Tests_Buffer,"Menu 2 : Item 3 : mRef = %x, iRef = %x\n", mRef, iRef);
			XPSetWidgetDescriptor(SDK210TestsEdit[MENU_2], SDK210Tests_Buffer);
		}
	}
}						

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

// Creates the widget with buttons for test and edit boxes for info
void CreateSDK210Tests(int x, int y, int w, int h)
{
	int Item;

	int x2 = x + w;
	int y2 = y - h;
	char Buffer[255];
	
	sprintf(Buffer, "%s %s %s", "SDK210Tests", SDK210TestsVersionNumber, "- Sandy Barbour 2012");
	SDK210TestsWidget = XPCreateWidget(x, y, x2, y2,
					1,	// Visible
					Buffer,	// desc
					1,		// root
					NULL,	// no container
					xpWidgetClass_MainWindow);

	XPSetWidgetProperty(SDK210TestsWidget, xpProperty_MainWindowHasCloseBoxes, 1);

	SDK210TestsWindow = XPCreateWidget(x+50, y-50, x2-50, y2+50,
					1,	// Visible
					"",	// desc
					0,		// root
					SDK210TestsWidget,
					xpWidgetClass_SubWindow);

	XPSetWidgetProperty(SDK210TestsWindow, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow);

	SDK210TestsButton1 = XPCreateWidget(x+60, y-60, x+180, y-82,
						1, " Create Temp FLCB", 0, SDK210TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK210TestsButton1, xpProperty_ButtonType, xpPushButton);

	SDK210TestsButton2 = XPCreateWidget(x+190, y-60, x+330, y-82,
						1, " Schedule Temp FLCB", 0, SDK210TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK210TestsButton2, xpProperty_ButtonType, xpPushButton);

	SDK210TestsButton3 = XPCreateWidget(x+340, y-60, x+470, y-82,
						1, " Destroy Temp FLCB", 0, SDK210TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK210TestsButton3, xpProperty_ButtonType, xpPushButton);

	SDK210TestsButton4 = XPCreateWidget(x+480, y-60, x+580, y-82,
						1, " Native Paths", 0, SDK210TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK210TestsButton4, xpProperty_ButtonType, xpPushButton);

	SDK210TestsButton5 = XPCreateWidget(x+60, y-90, x+180, y-112,
						1, " Append Menu Item", 0, SDK210TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK210TestsButton5, xpProperty_ButtonType, xpPushButton);

	SDK210TestsButton6 = XPCreateWidget(x+190, y-90, x+330, y-112,
						1, " Remove Menu Item", 0, SDK210TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK210TestsButton6, xpProperty_ButtonType, xpPushButton);
	
	SDK210TestsButton7 = XPCreateWidget(x+340, y-90, x+470, y-112,
						1, " Drawing Enable", 0, SDK210TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK210TestsButton7, xpProperty_ButtonType, xpPushButton);

	SDK210TestsButton8 = XPCreateWidget(x+480, y-90, x+580, y-112,
						1, " Drawing Disable", 0, SDK210TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK210TestsButton8, xpProperty_ButtonType, xpPushButton);

	for (Item=0; Item<SDK210TESTS_MAX_ITEMS; Item++)
	{
		SDK210TestsText[Item] = XPCreateWidget(x+60, y-(120 + (Item*30)), x+160, y-(142 + (Item*30)),
							1,	// Visible
							SDK210TestsDataDesc[Item],// desc
							0,		// root
							SDK210TestsWidget,
							xpWidgetClass_Caption);

		SDK210TestsEdit[Item] = XPCreateWidget(x+170, y-(120 + (Item*30)), x+570, y-(142 + (Item*30)),
							1, "", 0, SDK210TestsWidget,
							xpWidgetClass_TextField);

		XPSetWidgetProperty(SDK210TestsEdit[Item], xpProperty_TextFieldType, xpTextEntryField);
	}

	XPAddWidgetCallback(SDK210TestsWidget, SDK210TestsHandler);
	XPSetWidgetProperty(SDK210TestsButton1, xpProperty_Enabled, 1);
	XPSetWidgetProperty(SDK210TestsButton2, xpProperty_Enabled, 0);
	XPSetWidgetProperty(SDK210TestsButton3, xpProperty_Enabled, 0);
	XPSetWidgetProperty(SDK210TestsButton5, xpProperty_Enabled, 1);
	XPSetWidgetProperty(SDK210TestsButton6, xpProperty_Enabled, 0);
	XPSetWidgetProperty(SDK210TestsButton7, xpProperty_Enabled, 1);
	XPSetWidgetProperty(SDK210TestsButton8, xpProperty_Enabled, 0);
}

//Handle the widget messages
int	SDK210TestsHandler(
						XPWidgetMessage			inMessage,
						XPWidgetID				inWidget,
						intptr_t				inParam1,
						intptr_t				inParam2)
{
	// When widget close cross is clicked we only hide the widget
	if (inMessage == xpMessage_CloseButtonPushed)
	{
		if (SDK210TestsMenuItem1 == 1)
		{
			XPDestroyWidget(SDK210TestsWidget, 1);
			SDK210TestsMenuItem1 = 0;
		}
		return 1;
	}

	// Process when a button on the widget is pressed
	if (inMessage == xpMsg_PushButtonPressed)
	{
		if (inParam1 == (intptr_t)SDK210TestsButton1)
		{
			SDK210TestsTempLoopCBCounter = 0;
			SDK210TestsCreateFLCB = 1;
			SDK210TestsDestroyFLCB = 0;
			XPSetWidgetProperty(SDK210TestsButton1, xpProperty_Enabled, 0);
			XPSetWidgetProperty(SDK210TestsButton2, xpProperty_Enabled, 1);
			XPSetWidgetProperty(SDK210TestsButton3, xpProperty_Enabled, 1);
			XPSetWidgetDescriptor(SDK210TestsEdit[INFO], "Click on [Schedule Temp FLCB]");
			return 1;
		}

		if (inParam1 == (intptr_t)SDK210TestsButton2)
		{
			XPLMScheduleFlightLoop(SDK210Tests_XPLMTempFlightLoopID, 1.0, true);
			XPSetWidgetProperty(SDK210TestsButton2, xpProperty_Enabled, 0);
			XPSetWidgetDescriptor(SDK210TestsEdit[INFO], "");
			return 1;
		}

		if (inParam1 == (intptr_t)SDK210TestsButton3)
		{
			SDK210TestsDestroyFLCB = 1;
			SDK210TestsCreateFLCB = 0;
			XPSetWidgetProperty(SDK210TestsButton3, xpProperty_Enabled, 0);
			XPSetWidgetProperty(SDK210TestsButton2, xpProperty_Enabled, 0);
			XPSetWidgetProperty(SDK210TestsButton1, xpProperty_Enabled, 1);
			XPSetWidgetDescriptor(SDK210TestsEdit[TEMP_FLCB], "");
			return 1;
		}

		if (inParam1 == (intptr_t)SDK210TestsButton4)
		{
			XPSetWidgetProperty(SDK210TestsButton4, xpProperty_Enabled, 0);
			XPLMDebugString("================================================================\n");
			XPLMDebugString("SDK210Tests: SDK210TestsHandler -  Native Paths Feature Enabled.\n");
			XPLMDebugString("----------------------------------------------------------------\n");
			XPLMEnableFeature("XPLM_USE_NATIVE_PATHS",1);
			SDK210TestsNativePaths();
			XPLMReleasePlanes();
			XPLMEnableFeature("XPLM_USE_NATIVE_PATHS",0);
			XPLMDebugString("================================================================\n");
			XPLMDebugString("SDK210Tests: SDK210TestsHandler -  Native Paths Feature Disabled.\n");
			XPLMDebugString("----------------------------------------------------------------\n");
			SDK210TestsNativePaths();
			XPLMReleasePlanes();
			XPLMDebugString("================================================================\n");
			XPSetWidgetProperty(SDK210TestsButton4, xpProperty_Enabled, 1);
			return 1;
		}
		
		if (inParam1 == (intptr_t)SDK210TestsButton5)
		{
			SDK210TestsMenuId2 = XPLMCreateMenu("Test Menu 1", SDK210TestsMenuId, SDK210TestsMenuItem2, SDK210TestsMenuHandler2, NULL);
			SDK210TestsMenuIndex1 = XPLMAppendMenuItem(SDK210TestsMenuId2, "Test Menu Item 1", (void *)"1", 1);
			SDK210TestsMenuIndex2 = XPLMAppendMenuItem(SDK210TestsMenuId2, "Test Menu Item 2", (void *)"2", 1);
			SDK210TestsMenuIndex3 = XPLMAppendMenuItem(SDK210TestsMenuId2, "Test Menu Item 3", (void *)"3", 1);
			XPSetWidgetProperty(SDK210TestsButton5, xpProperty_Enabled, 0);
			XPSetWidgetProperty(SDK210TestsButton6, xpProperty_Enabled, 1);
			return 1;
		}

		if (inParam1 == (intptr_t)SDK210TestsButton6)
		{
			XPLMRemoveMenuItem(SDK210TestsMenuId2, SDK210TestsMenuIndex3);
			XPLMRemoveMenuItem(SDK210TestsMenuId2, SDK210TestsMenuIndex2);
			XPLMRemoveMenuItem(SDK210TestsMenuId2, SDK210TestsMenuIndex1);
			XPLMDestroyMenu(SDK210TestsMenuId2);
			XPSetWidgetProperty(SDK210TestsButton5, xpProperty_Enabled, 1);
			XPSetWidgetProperty(SDK210TestsButton6, xpProperty_Enabled, 0);
			XPSetWidgetDescriptor(SDK210TestsEdit[MENU_2], "");
			return 1;
		}

		if (inParam1 == (intptr_t)SDK210TestsButton7)
		{
			SDK210TestsDrawingEnabled = 1;
			XPSetWidgetProperty(SDK210TestsButton7, xpProperty_Enabled, 0);
			XPSetWidgetProperty(SDK210TestsButton8, xpProperty_Enabled, 1);
			return 1;
		}

		if (inParam1 == (intptr_t)SDK210TestsButton8)
		{
			SDK210TestsDrawingEnabled = 0;
			XPSetWidgetDescriptor(SDK210TestsEdit[DRAWING], "");
			XPSetWidgetProperty(SDK210TestsButton7, xpProperty_Enabled, 1);
			XPSetWidgetProperty(SDK210TestsButton8, xpProperty_Enabled, 0);
			return 1;
		}
	
	}
	return 0;
}						

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

// Function for Draw Object tests
void SDK210TestsGotWindsock(XPLMObjectRef obj, void * ref)
{
	XPLMDebugString("================================================================\n");
	XPLMDebugString("SDK210Tests: - SDK210TestsGotWindsock Callback called.\n");
	XPLMDebugString("----------------------------------------------------------------\n");
	SDK210Tests_Windsock = obj;
}

void SDK210TestsLoadWindsock(const char * fname, void * ref)
{
	XPLMDebugString("================================================================\n");
	XPLMDebugString("SDK210Tests: - SDK210TestsLoadWindsock : Before XPLMLoadObjectAsync Call.\n");
	XPLMLoadObjectAsync(fname, SDK210TestsGotWindsock, NULL);
	XPLMDebugString("SDK210Tests: - SDK210TestsLoadWindsock : After XPLMLoadObjectAsync Call.\n");
	XPLMDebugString("----------------------------------------------------------------\n");
}

int	SDK210TestsDrawWindsock(
                                   XPLMDrawingPhase     inPhase,    
                                   int                  inIsBefore,    
                                   void *               inRefcon)
{
	static bool LookedForWindsock = false;
	if (SDK210TestsDrawingEnabled)
	{
		if (SDK210Tests_Windsock == NULL && !LookedForWindsock)
		{
			XPLMLookupObjects("lib/airport/landscape/windsock.obj", 0, 0, SDK210TestsLoadWindsock, NULL);
			LookedForWindsock = true;
		}
		if (SDK210Tests_Windsock && !SDK210Tests_Windsock_Probe)
			SDK210Tests_Windsock_Probe = XPLMCreateProbe(xplm_ProbeY);
			
		if (SDK210Tests_Windsock && SDK210Tests_refx && SDK210Tests_refy && SDK210Tests_refz && SDK210Tests_Windsock_Probe)
		{
			XPLMProbeInfo_t info;
			info.structSize = sizeof(info);

			XPLMProbeResult result = XPLMProbeTerrainXYZ(
											SDK210Tests_Windsock_Probe,
											XPLMGetDataf(SDK210Tests_refx),
											XPLMGetDataf(SDK210Tests_refy),
											XPLMGetDataf(SDK210Tests_refz),
											&info);
			if(result == xplm_ProbeHitTerrain)
			{
				XPLMDrawInfo_t	DrawInfo_t = { 0 };
				DrawInfo_t.structSize = sizeof(DrawInfo_t);
				DrawInfo_t.x = info.locationX;
				DrawInfo_t.y = info.locationY;
				DrawInfo_t.z = info.locationZ;
				if(info.is_wet)DrawInfo_t.pitch += 20.0;
				if (XPLMGetDatai(SDK210Tests_wrl_type)) 
					DrawInfo_t.heading += 30.0;
				XPLMDrawObjects(SDK210Tests_Windsock, 1, &DrawInfo_t, 0, 1);
				if (SDK210TestsMenuItem1 == 1)
				{
					sprintf(SDK210Tests_Buffer,"Drawing Enabled : X = %f, Y = %f, Z = %f\n", info.locationX, info.locationY, info.locationZ);
					XPSetWidgetDescriptor(SDK210TestsEdit[DRAWING], SDK210Tests_Buffer);
				}
			}
		}
	}
	return 1;
}

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

void SDK210TestsNativePaths(void)
{
	char PluginPath[512], Name[256], FilePath[512], Signature[256], Description[256];
	char PrefsPath[512];
	XPLMPluginID PluginID = NULL;
	char NameBuffer[4096];
	char *Indices[4096];
	int ReturnFile;
	int TotalFile;
	int	ReturnValue;
	int	Offset = 0;

	#if IBM
		#define PLUGIN_ADMIN_NAME "win.xpl"
	#elif APL
		#define PLUGIN_ADMIN_NAME "mac.xpl"
	#elif LIN
		#define PLUGIN_ADMIN_NAME "lin.xpl"
	#else
		#error Platform not defined!
	#endif

	XPLMGetSystemPath(PluginPath);
	strcat(PluginPath, "Resources");
	strcat(PluginPath, XPLMGetDirectorySeparator());
	strcat(PluginPath, "plugins");
	strcat(PluginPath, XPLMGetDirectorySeparator());
	strcat(PluginPath, "PluginAdmin");
	strcat(PluginPath, XPLMGetDirectorySeparator());
	ReturnValue = XPLMGetDirectoryContents(PluginPath, Offset, NameBuffer, sizeof(NameBuffer), Indices, sizeof(Indices) / sizeof(char *), &TotalFile, &ReturnFile);
	sprintf(SDK210Tests_Buffer,"SDK210Tests: SDK210TestsNativePaths -  XPLMGetDirectoryContents - PluginPath = %s.\n", PluginPath);
	XPLMDebugString(SDK210Tests_Buffer);
	sprintf(SDK210Tests_Buffer,"SDK210Tests: SDK210TestsNativePaths -  XPLMGetDirectoryContents - TotalFile = %d.\n", TotalFile);
	XPLMDebugString(SDK210Tests_Buffer);
	strcat(PluginPath, PLUGIN_ADMIN_NAME);
	PluginID = XPLMFindPluginByPath(PluginPath);
	XPLMGetPluginInfo(PluginID, Name, FilePath, Signature, Description);
	sprintf(SDK210Tests_Buffer,"SDK210Tests: SDK210TestsNativePaths -  XPLMGetPluginInfo - Name = %s, FilePath = %s, Signature = %s, Description = %s.\n", Name, FilePath, Signature, Description);
	XPLMDebugString(SDK210Tests_Buffer);
	XPLMGetPrefsPath(PrefsPath);
	sprintf(SDK210Tests_Buffer,"SDK210Tests: SDK210TestsNativePaths - XPLMGetPrefsPath - PrefsPath = %s.\n", PrefsPath);
	XPLMDebugString(SDK210Tests_Buffer);

	strcpy(FilePath, "Output");
	strcat(FilePath, XPLMGetDirectorySeparator());
	strcat(FilePath, "situations");
	strcat(FilePath, XPLMGetDirectorySeparator());
	strcat(FilePath, "SDK210Test.sit");
	sprintf(SDK210Tests_Buffer,"SDK210Tests: SDK210TestsNativePaths - XPLMSaveDataFile - FilePath = %s.\n", FilePath);
	XPLMDebugString(SDK210Tests_Buffer);
	XPLMSaveDataFile(xplm_DataFile_Situation, FilePath);
	///XPLMLoadDataFile(xplm_DataFile_Situation, FilePath);

	strcpy(FilePath, "Output");
	strcat(FilePath, XPLMGetDirectorySeparator());
	strcat(FilePath, "replays");
	strcat(FilePath, XPLMGetDirectorySeparator());
	strcat(FilePath, "SDK210Test.rep");
	sprintf(SDK210Tests_Buffer,"SDK210Tests: SDK210TestsNativePaths -  XPLMSaveDataFile - FilePath = %s.\n", FilePath);
	XPLMDebugString(SDK210Tests_Buffer);
	XPLMSaveDataFile(xplm_DataFile_ReplayMovie, FilePath);
	///XPLMLoadDataFile(xplm_DataFile_ReplayMovie, FilePath); 

	SDK210TestsLoadAircraft(USER_AIRCRAFT);
	SDK210TestsAcquireAircraft();
}

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

void SDK210TestsAcquireAircraft(void)
{
	int	PlaneCount;
	int Index;
	char FileName[256], AircraftPath[256];

	XPLMCountAircraft(&PlaneCount, 0, 0);
	if (PlaneCount > 1)
	{
		for (Index = 1; Index < PlaneCount; Index++)
		{
			XPLMGetNthAircraftModel(Index, FileName, AircraftPath);   
			strcpy(SDK210TestsAircraftPath[Index-1], AircraftPath);
			pSDK210TestsAircraft[Index-1] = (char *)SDK210TestsAircraftPath[Index-1];
		}
		if (XPLMAcquirePlanes((char **)&pSDK210TestsAircraft, NULL, NULL))
		{
			XPLMDebugString("SDK210Tests: SDK210TestsAcquireAircraft - Aircraft Acquired successfully\n");
			SDK210TestsLoadAircraft(AI_AIRCRAFT);
		}
		else
		{
			XPLMDebugString("SDK210Tests: SDK210TestsAcquireAircraft - Aircraft not Acquired\n");
		}
	}
}

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

void SDK210TestsLoadAircraft(int AircraftType)
{
	char AircraftPath[512];

	XPLMGetSystemPath(AircraftPath);
	///strcat(AircraftPath, XPLMGetDirectorySeparator());
	strcat(AircraftPath, "Aircraft");
	strcat(AircraftPath, XPLMGetDirectorySeparator());
	strcat(AircraftPath, "General Aviation");
	strcat(AircraftPath, XPLMGetDirectorySeparator());
	strcat(AircraftPath, "Cirrus TheJet");
	strcat(AircraftPath, XPLMGetDirectorySeparator());
	strcat(AircraftPath, "c4.acf");
	sprintf(SDK210Tests_Buffer,"SDK210Tests: SDK210TestsLoadAircraft -  AircraftPath = %s.\n", AircraftPath);
	XPLMDebugString(SDK210Tests_Buffer);

	if (AircraftType == USER_AIRCRAFT)
		XPLMSetUsersAircraft(AircraftPath);
	if (AircraftType == AI_AIRCRAFT)
		XPLMSetAircraftModel(1, AircraftPath);
}

//---------------------------------------------------------------------------
Leave a comment

MotionPlatformData

/*
Plugin to show how to derive motion platform data from our datarefs
Thanks to Austin for allowing us to use the original Xplane conversion code.

Version 1.0.0.1			Intitial Sandy Barbour - 05/08/2007
*/

#include <stdio.h>
#include <string.h>

#include "XPLMDisplay.h"
#include "XPLMGraphics.h"
#include "XPLMProcessing.h"
#include "XPLMDataAccess.h"

// Globals.
// Use MPD_ as a prefix for the global variables

// Used to store data for display
char MPD_Buffer[6][80];
// Used to store calculated motion data
float MPD_MotionData[6];

// Window ID
XPLMWindowID MPD_Window = NULL;

// Datarefs
XPLMDataRef	MPD_DR_groundspeed = NULL;
XPLMDataRef	MPD_DR_fnrml_prop = NULL;
XPLMDataRef	MPD_DR_fside_prop = NULL;
XPLMDataRef	MPD_DR_faxil_prop = NULL;
XPLMDataRef	MPD_DR_fnrml_aero = NULL;
XPLMDataRef	MPD_DR_fside_aero = NULL;
XPLMDataRef	MPD_DR_faxil_aero = NULL;
XPLMDataRef	MPD_DR_fnrml_gear = NULL;
XPLMDataRef	MPD_DR_fside_gear = NULL;
XPLMDataRef	MPD_DR_faxil_gear = NULL;
XPLMDataRef	MPD_DR_m_total = NULL;
XPLMDataRef	MPD_DR_the = NULL;
XPLMDataRef	MPD_DR_psi = NULL;
XPLMDataRef	MPD_DR_phi = NULL;

//---------------------------------------------------------------------------
// Function prototypes

float MotionPlatformDataLoopCB(float elapsedMe, float elapsedSim, int counter, void * refcon);

void MotionPlatformDataDrawWindowCallback(
                                   XPLMWindowID         inWindowID,    
                                   void *               inRefcon);    

void MotionPlatformDataHandleKeyCallback(
                                   XPLMWindowID         inWindowID,    
                                   char                 inKey,    
                                   XPLMKeyFlags         inFlags,    
                                   char                 inVirtualKey,    
                                   void *               inRefcon,    
                                   int                  losingFocus);    

int MotionPlatformDataHandleMouseClickCallback(
                                   XPLMWindowID         inWindowID,    
                                   int                  x,    
                                   int                  y,    
                                   XPLMMouseStatus      inMouse,    
                                   void *               inRefcon);    

float MPD_fallout(float data, float low, float high);
float MPD_fltlim(float data, float min, float max);
float MPD_fltmax2 (float x1,const float x2);
void MPD_CalculateMotionData(void);

//---------------------------------------------------------------------------
// SDK Mandatory Callbacks

PLUGIN_API int XPluginStart(
						char *		outName,
						char *		outSig,
						char *		outDesc)
{
	strcpy(outName, "MotionPlatformData");
	strcpy(outSig, "xplanesdk.examples.motiondplatformdata");
	strcpy(outDesc, "A plug-in that derives motion platform data from datarefs.");

	MPD_Window = XPLMCreateWindow(
		                      50, 600, 200, 500,								/* Area of the window. */
                              1,												/* Start visible. */
                              MotionPlatformDataDrawWindowCallback,			/* Callbacks */
                              MotionPlatformDataHandleKeyCallback,
                              MotionPlatformDataHandleMouseClickCallback,
                              NULL);											/* Refcon - not used. */

	XPLMRegisterFlightLoopCallback(MotionPlatformDataLoopCB, 1.0, NULL);
	
	MPD_DR_groundspeed = XPLMFindDataRef("sim/flightmodel/position/groundspeed");
	MPD_DR_fnrml_prop = XPLMFindDataRef("sim/flightmodel/forces/fnrml_prop");
	MPD_DR_fside_prop = XPLMFindDataRef("sim/flightmodel/forces/fside_prop");
	MPD_DR_faxil_prop = XPLMFindDataRef("sim/flightmodel/forces/faxil_prop");
	MPD_DR_fnrml_aero = XPLMFindDataRef("sim/flightmodel/forces/fnrml_aero");
	MPD_DR_fside_aero = XPLMFindDataRef("sim/flightmodel/forces/fside_aero");
	MPD_DR_faxil_aero = XPLMFindDataRef("sim/flightmodel/forces/faxil_aero");
	MPD_DR_fnrml_gear = XPLMFindDataRef("sim/flightmodel/forces/fnrml_gear");
	MPD_DR_fside_gear = XPLMFindDataRef("sim/flightmodel/forces/fside_gear");
	MPD_DR_faxil_gear = XPLMFindDataRef("sim/flightmodel/forces/faxil_gear");
	MPD_DR_m_total = XPLMFindDataRef("sim/flightmodel/weight/m_total");
	MPD_DR_the = XPLMFindDataRef("sim/flightmodel/position/theta");
	MPD_DR_psi = XPLMFindDataRef("sim/flightmodel/position/psi");
	MPD_DR_phi = XPLMFindDataRef("sim/flightmodel/position/phi");

	memset(MPD_Buffer, 0, sizeof(MPD_Buffer));

	return 1;
}

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

PLUGIN_API void	XPluginStop(void)
{
    XPLMDestroyWindow(MPD_Window);
	XPLMUnregisterFlightLoopCallback(MotionPlatformDataLoopCB, NULL);
}

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

PLUGIN_API int XPluginEnable(void)
{
	return 1;
}

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

PLUGIN_API void XPluginDisable(void)
{
}

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

PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void * inParam)
{
}


//---------------------------------------------------------------------------
// Mandatory callback for SDK 2D Window
// Used to display the data to the screen

void MotionPlatformDataDrawWindowCallback(
                                   XPLMWindowID         inWindowID,    
                                   void *               inRefcon)
{

	float		rgb [] = { 1.0, 1.0, 1.0 };
	int			l, t, r, b;

	XPLMGetWindowGeometry(inWindowID, &l, &t, &r, &b);
	XPLMDrawTranslucentDarkBox(l, t, r, b);

	for (int i=0; i<6; i++)
		XPLMDrawString(rgb, l+10, (t-20) - (10*i), MPD_Buffer[i], NULL, xplmFont_Basic);
}                                   

//---------------------------------------------------------------------------
// Mandatory callback for SDK 2D Window
// Not used in this plugin

void MotionPlatformDataHandleKeyCallback(
                                   XPLMWindowID         inWindowID,    
                                   char                 inKey,    
                                   XPLMKeyFlags         inFlags,    
                                   char                 inVirtualKey,    
                                   void *               inRefcon,    
                                   int                  losingFocus)
{
}                                   

//---------------------------------------------------------------------------
// Mandatory callback for SDK 2D Window
// Not used in this plugin

int MotionPlatformDataHandleMouseClickCallback(
                                   XPLMWindowID         inWindowID,    
                                   int                  x,    
                                   int                  y,    
                                   XPLMMouseStatus      inMouse,    
                                   void *               inRefcon)
{
	return 1;
}                                      

//---------------------------------------------------------------------------
// FlightLoop callback to calculate motion data and store it in our buffers

float MotionPlatformDataLoopCB(float elapsedMe, float elapsedSim, int counter, void * refcon)
{
	MPD_CalculateMotionData();

	sprintf(MPD_Buffer[0], "the = %f", MPD_MotionData[0]);
	sprintf(MPD_Buffer[1], "psi = %f", MPD_MotionData[1]);
	sprintf(MPD_Buffer[2], "phi = %f", MPD_MotionData[2]);
	sprintf(MPD_Buffer[3], "a_side = %f", MPD_MotionData[3]);
	sprintf(MPD_Buffer[4], "a_nrml = %f", MPD_MotionData[4]);
	sprintf(MPD_Buffer[5], "a_axil = %f", MPD_MotionData[5]);

	return (float)0.1;
}

//---------------------------------------------------------------------------
// Original function used in the Xplane code.

float MPD_fallout(float data, float low, float high)
{
	if (data < low) return data;
	if (data > high) return data;
	if (data < ((low + high) * 0.5)) return low;
    return high;
}

//---------------------------------------------------------------------------
// Original function used in the Xplane code.

float MPD_fltlim(float data, float min, float max)
{
	if (data < min) return min;
	if (data > max) return max;
	return data;
}

//---------------------------------------------------------------------------
// Original function used in the Xplane code.

float MPD_fltmax2 (float x1,const float x2)
{
	return (x1 > x2) ? x1 : x2;
}

//---------------------------------------------------------------------------
// This is original Xplane code converted to use 
// our datarefs instead of the Xplane variables

void MPD_CalculateMotionData(void)
{
	float groundspeed = XPLMGetDataf(MPD_DR_groundspeed);
	float fnrml_prop = XPLMGetDataf(MPD_DR_fnrml_prop);
	float fside_prop = XPLMGetDataf(MPD_DR_fside_prop);
	float faxil_prop = XPLMGetDataf(MPD_DR_faxil_prop);
	float fnrml_aero = XPLMGetDataf(MPD_DR_fnrml_aero);
	float fside_aero = XPLMGetDataf(MPD_DR_fside_aero);
	float faxil_aero = XPLMGetDataf(MPD_DR_faxil_aero);
	float fnrml_gear = XPLMGetDataf(MPD_DR_fnrml_gear);
	float fside_gear = XPLMGetDataf(MPD_DR_fside_gear);
	float faxil_gear = XPLMGetDataf(MPD_DR_faxil_gear);
	float m_total = XPLMGetDataf(MPD_DR_m_total);
	float the = XPLMGetDataf(MPD_DR_the);
	float psi = XPLMGetDataf(MPD_DR_psi);
	float phi = XPLMGetDataf(MPD_DR_phi);

	float ratio = MPD_fltlim(groundspeed*0.2,0.0,1.0);
	float a_nrml= MPD_fallout(fnrml_prop+fnrml_aero+fnrml_gear,-0.1,0.1)/MPD_fltmax2(m_total,1.0);
	float a_side= (fside_prop+fside_aero+fside_gear)/MPD_fltmax2(m_total,1.0)*ratio;
	float a_axil= (faxil_prop+faxil_aero+faxil_gear)/MPD_fltmax2(m_total,1.0)*ratio;

	// Store the results in an array so that we can easily display it.
	MPD_MotionData[0] = the;
	MPD_MotionData[1] = psi;
	MPD_MotionData[2] = phi;
	MPD_MotionData[3] = a_side;
	MPD_MotionData[4] = a_nrml;
	MPD_MotionData[5] = a_axil;
}

//---------------------------------------------------------------------------
Leave a comment

SDK200Tests

/*
SDK200Tests Example
Tests the new SDK 2.00 functions.

Version 1.0.0.1		Intitial Sandy Barbour 20/11/2007

Version 1.1.0.1		Added Load/Save sit/smo files test
					Added Map Drawing Tests  - Sandy Barbour 02/12/2009

NOTES
1.
This code also draws a custom X-Plane OBJ (3-d model).
You will need to add your own OBJ and change the code to reflect this.
Copy your OBJ to the XPlane V9 application directory.

The line below, which is in the SDK200TestsDrawObject(..) callback is the line to change.
SDK200TestsObject = XPLMLoadObject("dummy.obj"); 

An alternative is to just rename you .obj file to dummy.obj

2.
The code draws 2 objects each time, so you are not seeing double. :-)
*/

#include "XPLMPlugin.h"
#include "XPLMDisplay.h"
#include "XPLMGraphics.h"
#include "XPLMProcessing.h"
#include "XPLMDataAccess.h"
#include "XPLMMenus.h"
#include "XPLMUtilities.h"
#include "XPWidgets.h"
#include "XPStandardWidgets.h"
#include "XPLMScenery.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#if IBM
#include <windows.h>
#include <stdio.h>
#endif
#if LIN
#include <GL/gl.h>
#else
#if __GNUC__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#endif

// Number of edit boxes in widget
#define MAX_ITEMS 5
// Number of Xplane objects
#define MAX_OBJECT_ITEMS 6

char SDK200TestsVersionNumber[] = "v1.10";
// Description text for widget edit boxes
char SDK200TestsDataDesc[MAX_ITEMS][255] = {"Probe Test", "Command Test", "Mouse Wheel", "Cursor", "Objects"};

// Used as interlock for widget
int SDK200TestsMenuItem1;

// Used by menus
XPLMMenuID	SDK200TestsId;
int	SDK200TestsItem;

// Used for the text window
XPLMWindowID SDK200TestsTestWindow;

// Used to test Command API
XPLMCommandRef SDK200TestsCommand1 = NULL;

// Indexers
int SDK200TestsCursorStatusIndex = 0;
int SDK200TestsCurrentFontIndex = 0;
int SDK200TestsCurrentObjectIndex = 0;
int SDK200TestsDrawWindowEnabled = 0;

// Used by the cursor test
XPLMCursorStatus SDK200TestsCursorStatus[4] = {xplm_CursorDefault, xplm_CursorHidden, xplm_CursorArrow, xplm_CursorCustom};

// Used by the Draw Objects test
char SDK200TestsObjectPath[MAX_OBJECT_ITEMS][255] = {
	"lib/ships/Frigate.obj", "lib/airport/landscape/radar.obj",	"lib/dynamic/balloon.obj",
	"lib/airport/landscape/windsock_pole.obj", "lib/airport/NAVAIDS/glideslope.obj",
	"lib/airport/aircraft/regional_prop/ATR42-500_FedEx.obj"
};

// Used for GetVersion tests
char SDK200TestsXPLMFindSymbolBuffer[256], SDK200TestsXPLMGetVersionsBuffer[256];

// Datarefs
XPLMDataRef	SDK200Tests_refx = NULL;
XPLMDataRef	SDK200Tests_refy = NULL;
XPLMDataRef	SDK200Tests_refz = NULL;
XPLMDataRef	SDK200Tests_ref_render_type = NULL;
XPLMDataRef	SDK200Tests_ref_wrl_type = NULL;
XPLMDataRef	SDK200Tests_ref_vl = NULL;
XPLMDataRef	SDK200Tests_ref_vb = NULL;
XPLMDataRef	SDK200Tests_ref_vr = NULL;
XPLMDataRef	SDK200Tests_ref_vt = NULL;
XPLMDataRef	SDK200Tests_ref_mode_time = NULL;
XPLMDataRef	SDK200Tests_ref_x_scale = NULL;
XPLMDataRef	SDK200Tests_ref_y_scale = NULL;
XPLMDataRef	SDK200Tests_ref_t_scale = NULL;

// Widgets
XPWidgetID			SDK200TestsWidget, SDK200TestsWindow;
XPWidgetID			SDK200TestsButton1, SDK200TestsButton2, SDK200TestsButton3, SDK200TestsButton4, SDK200TestsButton5;
XPWidgetID			SDK200TestsButton6, SDK200TestsButton7, SDK200TestsButton8, SDK200TestsButton9;
XPWidgetID			SDK200TestsText[MAX_ITEMS];
XPWidgetID			SDK200TestsEdit[MAX_ITEMS];

XPLMProbeRef		SDK200TestsProbe=NULL;

// Used by the Draw Objects test
XPLMObjectRef		SDK200TestsObject=NULL;
XPLMProbeRef		SDK200TestsObjectProbe=NULL;

// Prototype for the XPLMFindSymbol test
typedef	void (* XPLMGetVersions_f)(
					int *		outXPlaneVersion,
					int *		outXPLMVersion,
					XPLMHostApplicationID *		outHostID);

// Displays data in widget
float SDK200TestsLoopCB(float elapsedMe, float elapsedSim, int counter, void * refcon);;

// Menu Prototype
void SDK200TestsMenuHandler(void *, void *);

// Draw Text window callbacks
void SDK200TestsWindowDraw(XPLMWindowID inWindowID, void * inRefcon);
void SDK200TestsWindowKey(XPLMWindowID inWindowID, char inKey, XPLMKeyFlags inFlags, char vkey, void * inRefcon, int losingFocus);
int	SDK200TestsWindowMouse(XPLMWindowID inWindowID, int x, int y, XPLMMouseStatus isDown, void * inRefcon);
int	SDK200TestsWindowMouseWheel(XPLMWindowID	inWindowID,    
						 int            x,    
						 int            y,    
						 int            wheel,    
						 int            clicks,    
						 void *         inRefcon);
XPLMCursorStatus SDK200TestsWindowCursor(XPLMWindowID	inWindowID,    
								  int           x,    
                                  int           y,    
                                  void *        inRefcon);

// Widget prototypes
void CreateSDK200Tests(int x1, int y1, int w, int h);

int SDK200TestsHandler(
						XPWidgetMessage			inMessage,
						XPWidgetID				inWidget,
						intptr_t				inParam1,
						intptr_t					inParam2);

// Callback for the Command API Tests
int	SDK200TestsCommandHandler1(XPLMCommandRef		inCommand,    
                                   XPLMCommandPhase     inPhase,    
                                   void *               inRefcon)
{
	char Buffer[256];
	sprintf(Buffer,"Cmdh handler: 0x%08x, %d, 0x%08x\n", inCommand, inPhase, inRefcon);
	XPSetWidgetDescriptor(SDK200TestsEdit[1], Buffer);
	return 0;
}

// Prototype for Draw Object tests
int	SDK200TestsDrawObject(
                                   XPLMDrawingPhase     inPhase,    
                                   int                  inIsBefore,    
                                   void *               inRefcon);

// Callback for Error Tests
void	SDK200TestsErrorCB(const char * msg)
{
	XPLMDebugString("SDK200Tests -:- error CB called: ");
	XPLMDebugString(msg);
	XPLMDebugString("\n");
}

// Callback or Feature API Tests
void	SDK200TestsFeatureEnum(const char * msg, void * inRefcon)
{
	XPLMDebugString("SDK200Tests -:- SUPPORTED FEATURE TEST: ");
	XPLMDebugString(msg);
	XPLMDebugString("\n");

}

// Prototype for Map Draw tests
int 	SDK200TestsMapCB(
                                   XPLMDrawingPhase     inPhase,    
                                   int                  inIsBefore,    
                                   void *               inRefcon);

PLUGIN_API int XPluginStart(
						char *		outName,
						char *		outSig,
						char *		outDesc)
{
	int	DataRefItem;
	int XPlaneVersion, XPLMVersion, HostID;
	char Buffer[256];

	strcpy(outName, "SDK200Tests");
	strcpy(outSig, "sandybarbour.projects.sdk200tests");
	strcpy(outDesc, "A plug-in to test the new SDK200 API's.");

	// Get the aicraft position
	SDK200Tests_refx = XPLMFindDataRef("sim/flightmodel/position/local_x");
	SDK200Tests_refy = XPLMFindDataRef("sim/flightmodel/position/local_y");
	SDK200Tests_refz = XPLMFindDataRef("sim/flightmodel/position/local_z");

	SDK200Tests_ref_render_type = XPLMFindDataRef("sim/graphics/view/panel_render_type");
	SDK200Tests_ref_wrl_type = XPLMFindDataRef("sim/graphics/view/world_render_type");

	SDK200Tests_ref_vl = XPLMFindDataRef("sim/graphics/view/local_map_l");
	SDK200Tests_ref_vb = XPLMFindDataRef("sim/graphics/view/local_map_b");
	SDK200Tests_ref_vr = XPLMFindDataRef("sim/graphics/view/local_map_r");
	SDK200Tests_ref_vt = XPLMFindDataRef("sim/graphics/view/local_map_t");
	SDK200Tests_ref_mode_time = XPLMFindDataRef("sim/graphics/settings/local_map/profile_view_is_time");
	SDK200Tests_ref_x_scale = XPLMFindDataRef("sim/graphics/view/local_profile_scale_x");
	SDK200Tests_ref_y_scale = XPLMFindDataRef("sim/graphics/view/local_profile_scale_y");
	SDK200Tests_ref_t_scale = XPLMFindDataRef("sim/graphics/view/local_profile_scale_t");

	// Create function pointer
	XPLMGetVersions_f XPLMGetVersionsTemp;
	// Find the real XPLMGetVersions function and copy to function pointer
	XPLMGetVersionsTemp = (XPLMGetVersions_f) XPLMFindSymbol("XPLMGetVersions");
	// Call the function
	XPLMGetVersionsTemp(&XPlaneVersion, &XPLMVersion, &HostID);
	// Display the results
	sprintf(SDK200TestsXPLMFindSymbolBuffer, "SDK200Tests -:- XPLMFindSymbol returned [XPlaneVersion = %d, XPLMVersion = %d, HostID = %d\n", XPlaneVersion, XPLMVersion, HostID);
	XPLMDebugString(SDK200TestsXPLMFindSymbolBuffer);

	// Call the real XPLMGetVersions as a comparison for  the above
	XPLMGetVersions(&XPlaneVersion, &XPLMVersion, &HostID);
	// Display the results
	sprintf(SDK200TestsXPLMGetVersionsBuffer, "SDK200Tests -:- XPLMGetVersions returned [XPlaneVersion = %d, XPLMVersion = %d, HostID = %d\n", XPlaneVersion, XPLMVersion, HostID);
	XPLMDebugString(SDK200TestsXPLMGetVersionsBuffer);

	// Create the menus
	SDK200TestsItem = XPLMAppendMenuItem(XPLMFindPluginsMenu(), "SDK200Tests", NULL, 1);
	SDK200TestsId = XPLMCreateMenu("SDK200Tests", XPLMFindPluginsMenu(), SDK200TestsItem, SDK200TestsMenuHandler, NULL);
	XPLMAppendMenuItem(SDK200TestsId, "SDK200Tests", (void *)"SDK200Tests", 1);
	XPLMAppendMenuItem(SDK200TestsId, "Show Window", (void *)"Show Window", 1);
	XPLMAppendMenuItem(SDK200TestsId, "Hide Window", (void *)"Hide Window", 1);
	
	// Register the callback for errors
	XPLMSetErrorCallback(SDK200TestsErrorCB);
	// Test enumaration of features
	XPLMEnumerateFeatures(SDK200TestsFeatureEnum, NULL);
	// Enable a valid feature, which trigger the SDK200TestsFeatureEnum callback
	XPLMEnableFeature("XPLM_WANTS_REFLECTIONS", 1);
	// Try to enable an invalid feature, error will be reported in the SDK200TestsErrorCB callback
	XPLMEnableFeature("SANDY_WANTS_REFLECTIONS", 1);

	SDK200TestsMenuItem1 = 0;
	SDK200TestsDrawWindowEnabled = 0;

	// This is used to create a text window using the new XPLMCreateWindowEx function.
	XPLMCreateWindow_t TestWindowData;
	TestWindowData.structSize = sizeof(TestWindowData);
	TestWindowData.left = 50;
	TestWindowData.top = 450;
	TestWindowData.right = 600;
	TestWindowData.bottom = 350;
	TestWindowData.visible= 1;
	TestWindowData.drawWindowFunc = SDK200TestsWindowDraw;
	TestWindowData.handleKeyFunc = SDK200TestsWindowKey;
	TestWindowData.handleMouseClickFunc = SDK200TestsWindowMouse;
	TestWindowData.handleMouseWheelFunc = SDK200TestsWindowMouseWheel;
	TestWindowData.handleCursorFunc = SDK200TestsWindowCursor;
	TestWindowData.refcon = NULL;
	SDK200TestsTestWindow = XPLMCreateWindowEx(&TestWindowData);
	sprintf(Buffer, "SDK200Tests -:- Creating window with XPLMCreateWindowEx: returned XPLMWindowID = %x\n", SDK200TestsTestWindow);
	XPLMDebugString(Buffer);

	XPLMRegisterFlightLoopCallback(SDK200TestsLoopCB, 1.0, NULL);

	// Create the test command, this will save the screen to a file.
	SDK200TestsCommand1 = XPLMCreateCommand("sim/operation/screenshot", "Test screenshot");
	// Register it
	XPLMRegisterCommandHandler(SDK200TestsCommand1, SDK200TestsCommandHandler1, 0, (void *) 0);

	// This used for the Draw Objects tests
	XPLMRegisterDrawCallback( SDK200TestsDrawObject, xplm_Phase_Objects, 0, 0 );

	// This used for the Draw Map tests
	XPLMRegisterDrawCallback( SDK200TestsMapCB, xplm_Phase_LocalMap2D, 0, 0);
	XPLMRegisterDrawCallback( SDK200TestsMapCB, xplm_Phase_LocalMap3D, 0, 0);
	XPLMRegisterDrawCallback( SDK200TestsMapCB, xplm_Phase_LocalMapProfile, 0, 0);

	// Create a Y probe
	SDK200TestsObjectProbe = XPLMCreateProbe(xplm_ProbeY);
	SDK200TestsProbe = XPLMCreateProbe(xplm_ProbeY);

	return 1;
}

PLUGIN_API void	XPluginStop(void)
{
	// Clean up
	XPLMUnregisterDrawCallback( SDK200TestsDrawObject, xplm_Phase_Objects, 0, 0 );
	XPLMUnregisterDrawCallback( SDK200TestsMapCB, xplm_Phase_LocalMap2D, 0, 0);
	XPLMUnregisterDrawCallback( SDK200TestsMapCB, xplm_Phase_LocalMap3D, 0, 0);
	XPLMUnregisterDrawCallback( SDK200TestsMapCB, xplm_Phase_LocalMapProfile, 0, 0);
	if(SDK200TestsObject) XPLMUnloadObject(SDK200TestsObject);
	XPLMDestroyProbe(SDK200TestsObjectProbe);
	XPLMDestroyProbe(SDK200TestsProbe);
	XPLMDestroyWindow(SDK200TestsTestWindow);
	XPLMUnregisterCommandHandler(SDK200TestsCommand1, SDK200TestsCommandHandler1, 0, 0);
	XPLMUnregisterFlightLoopCallback(SDK200TestsLoopCB, NULL);
	XPLMDestroyMenu(SDK200TestsId);
}

PLUGIN_API int XPluginEnable(void)
{
	return 1;
}

PLUGIN_API void XPluginDisable(void)
{
}

PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void * inParam)
{
}

// Displays data in widget
float SDK200TestsLoopCB(float elapsedMe, float elapsedSim, int counter, void * refcon)
{
	char 		Buffer[256];
	
	float x = XPLMGetDatad(SDK200Tests_refx);
	float y = XPLMGetDatad(SDK200Tests_refy);
	float z = XPLMGetDatad(SDK200Tests_refz);
	XPLMProbeInfo_t	info = { 0 };
	info.structSize = sizeof(info);

	// If we have a hit then display the data in the widget
	if (XPLMProbeTerrainXYZ(SDK200TestsProbe,x,y,z,&info) == xplm_ProbeHitTerrain)
	{
		sprintf(Buffer,"Y gap is: %f v=%f,%f,%f\n", y - info.locationY, info.velocityX, info.velocityY, info.velocityZ);
		if (SDK200TestsMenuItem1 == 1)
			XPSetWidgetDescriptor(SDK200TestsEdit[0], Buffer);
	}
	return 0.1;
}

// Process the menu selections
void SDK200TestsMenuHandler(void * mRef, void * iRef)
{
    // Menu selected for widget
	if (!strcmp((char *) iRef, "SDK200Tests"))
	{
		if (SDK200TestsMenuItem1 == 0)
		{
			CreateSDK200Tests(100, 550, 650, 330);
			SDK200TestsMenuItem1 = 1;
		}
	}

    // Menu selected to enable text window
	if (!strcmp((char *) iRef, "Show Window"))
	{
		SDK200TestsDrawWindowEnabled = 1;
	}

	// Menu selected to disable text window
	if (!strcmp((char *) iRef, "Hide Window"))
	{
		SDK200TestsDrawWindowEnabled = 0;
	}
}						


// Used to draw various test info to the text window
void	SDK200TestsWindowDraw(XPLMWindowID inWindowID, void * inRefcon)
{
	float		rgb [] = { 1.0, 1.0, 1.0 };
	int			Left, Top, Right, Bottom;
	char 		Buffer[256];
	
	// Used by the Font test
	XPLMFontID TestFont[2] = {xplmFont_Basic, xplmFont_Proportional};
	char TestFontDesc[2][80] = {"xplmFont_Basic", "xplmFont_Proportional"};

	// Only draw if enabled through the menu
	if (SDK200TestsDrawWindowEnabled)
	{
		sprintf(Buffer, "Current font is %d : %s", TestFont[SDK200TestsCurrentFontIndex], TestFontDesc[SDK200TestsCurrentFontIndex]);
		XPLMGetWindowGeometry(inWindowID, &Left, &Top, &Right, &Bottom);
		XPLMDrawTranslucentDarkBox(Left, Top, Right, Bottom);
		XPLMDrawString(rgb, Left+10, Top-10, SDK200TestsXPLMFindSymbolBuffer, NULL, TestFont[SDK200TestsCurrentFontIndex]);
		XPLMDrawString(rgb, Left+10, Top-20, SDK200TestsXPLMGetVersionsBuffer, NULL, TestFont[SDK200TestsCurrentFontIndex]);
		XPLMDrawString(rgb, Left+10, Top-50, Buffer, NULL, xplmFont_Basic);

		int render_type = XPLMGetDatai(SDK200Tests_ref_render_type);

		sprintf(Buffer, "Current Render Type is %d", render_type);
		XPLMDrawString(rgb, Left+10, Top-70, Buffer, NULL, xplmFont_Basic);

		sprintf(Buffer, "Test Feature - XPLMHasFeature is %d : XPLMIsFeatureEnabled is %d", XPLMHasFeature("XPLM_WANTS_REFLECTIONS"), XPLMIsFeatureEnabled("XPLM_WANTS_REFLECTIONS"));
		XPLMDrawString(rgb, Left+10, Top-90, Buffer, 0, xplmFont_Basic);
	}
}

// Not used
void SDK200TestsWindowKey(XPLMWindowID inWindowID, char inKey, XPLMKeyFlags inFlags, char vkey, void * inRefcon, int losingFocus)
{
}

// Not used
int	SDK200TestsWindowMouse(XPLMWindowID inWindowID, int x, int y, XPLMMouseStatus isDown, void * inRefcon)
{
	return 1;
}

// Using the mouse wheel in the text window will increase the throttle
int	SDK200TestsWindowMouseWheel(XPLMWindowID	inWindowID,    
						 int            x,    
						 int            y,    
						 int            wheel,    
						 int            clicks,    
						 void *         inRefcon)
{
	XPLMCommandOnce(XPLMFindCommand("sim/engines/throttle_up_1"));
	return 1;
}

// This will change the type of cursor
XPLMCursorStatus SDK200TestsWindowCursor(XPLMWindowID	inWindowID,    
								  int           x,    
                                  int           y,    
                                  void *        inRefcon)
{
	return SDK200TestsCursorStatus[SDK200TestsCursorStatusIndex];
}


// Function for Draw Object tests
void SDK200TestsLoadObject(const char * fname, void * ref)
{
	if(SDK200TestsObject == NULL)
		SDK200TestsObject = XPLMLoadObject(fname);
}

// Function for Draw Object tests
int	SDK200TestsDrawObject(
                                   XPLMDrawingPhase     inPhase,    
                                   int                  inIsBefore,    
                                   void *               inRefcon)
{
	// If NULL then first time or reset by widget button press
	if (SDK200TestsObject == NULL)
	{
		if (SDK200TestsCurrentObjectIndex < MAX_OBJECT_ITEMS)
			// Xplane object so do lookup, will invoke callback if found which will load object
			XPLMLookupObjects(SDK200TestsObjectPath[SDK200TestsCurrentObjectIndex], 0, 0, SDK200TestsLoadObject, NULL);
		else
			// Load the imported object
			// NOTE - Change this line with the name of your .obj file.
			SDK200TestsObject = XPLMLoadObject("dummy.obj");
	}

	// Only do this if we have everything we need
	if (SDK200TestsObject && SDK200Tests_refx && SDK200Tests_refy && SDK200Tests_refz && SDK200TestsObjectProbe)
	{
		XPLMProbeInfo_t info;
		info.structSize = sizeof(info);

		// Probe the terrain 
		XPLMProbeResult result = XPLMProbeTerrainXYZ(
										SDK200TestsObjectProbe,
										XPLMGetDatad(SDK200Tests_refx),
										XPLMGetDatad(SDK200Tests_refy),
										XPLMGetDatad(SDK200Tests_refz),
										&info);
		// If we have a hit
		if(result == xplm_ProbeHitTerrain)
		{
			XPLMDrawInfo_t	locations[2] = { 0 };
			locations[0].structSize = sizeof(XPLMDrawInfo_t);
			locations[0].x = info.locationX;
			locations[0].y = info.locationY;
			locations[0].z = info.locationZ;
			if(info.is_wet)locations[0].pitch += 20.0;
			if(XPLMGetDatai(SDK200Tests_ref_wrl_type)) locations[0].heading += 30.0;

			locations[1].structSize = sizeof(XPLMDrawInfo_t);
			locations[1].x = XPLMGetDatad(SDK200Tests_refx) + 20;
			locations[1].y = XPLMGetDatad(SDK200Tests_refy);
			locations[1].z = XPLMGetDatad(SDK200Tests_refz) + 20;
			locations[1].pitch = 0;
			locations[1].heading = 0;
			locations[1].roll = 0;
			// Draw the object 
			XPLMDrawObjects(SDK200TestsObject, 2, locations, 0, 1);
		}
	}	
	return 1;
}

// Function for Map Draw tests
int 	SDK200TestsMapCB(
                                   XPLMDrawingPhase     inPhase,    
                                   int                  inIsBefore,    
                                   void *               inRefcon)
{
	if(inPhase == xplm_Phase_LocalMap3D)
	{
		float lx = XPLMGetDataf(SDK200Tests_refx);
		float ly = XPLMGetDataf(SDK200Tests_refy);
		float lz = XPLMGetDataf(SDK200Tests_refz);
		XPLMSetGraphicsState(0, 0, 0,   0, 0,   0, 0);
		glColor3f(1,1,1);
		glBegin(GL_LINES);
		glVertex3f(lx,ly,lz-10.0);
		glVertex3f(lx,ly,lz+10.0);
		glVertex3f(lx,ly-10.0,lz);
		glVertex3f(lx,ly+10.0,lz);
		glVertex3f(lx-10.0,ly,lz);
		glVertex3f(lx+10.0,ly,lz);
		glEnd();
	}
	else 
	{
		float vl = XPLMGetDataf(SDK200Tests_ref_vl);
		float vb = XPLMGetDataf(SDK200Tests_ref_vb);
		float vr = XPLMGetDataf(SDK200Tests_ref_vr);
		float vt = XPLMGetDataf(SDK200Tests_ref_vt);

		XPLMSetGraphicsState(0, 0, 0,   0, 0,  0, 0);
		glColor3f(0, 1, 0);
		
		glBegin(GL_LINE_LOOP);
		glVertex2f(vl+2, vb+2);
		glVertex2f(vl+2, vt-2);
		glVertex2f(vr-2, vt-2);
		glVertex2f(vr-2, vb+2);
		glEnd();

		if(inPhase == xplm_Phase_LocalMapProfile)
		{
			int mode_time = XPLMGetDatai(SDK200Tests_ref_mode_time);
			float x_scale = XPLMGetDataf(SDK200Tests_ref_x_scale);
			float y_scale = XPLMGetDataf(SDK200Tests_ref_y_scale);
			float t_scale = XPLMGetDataf(SDK200Tests_ref_t_scale);
			glColor3f(1,0,0);
			glBegin(GL_LINES);
			glVertex2f(vl,vb);
			if(mode_time)
				glVertex2f(vl+60.0*t_scale,vb+50.0*t_scale);
			else
				glVertex2f(vl+1000.0*x_scale,vb+50.0*y_scale);
			glEnd();
		}
	}		
	return 1;
}

// Creates the widget with buttons for test and edit boxes for info
void CreateSDK200Tests(int x, int y, int w, int h)
{
	int Item;

	int x2 = x + w;
	int y2 = y - h;
	char Buffer[255];
	
	sprintf(Buffer, "%s %s %s", "SDK200Tests", SDK200TestsVersionNumber, "- Sandy Barbour 2007");
	SDK200TestsWidget = XPCreateWidget(x, y, x2, y2,
					1,	// Visible
					Buffer,	// desc
					1,		// root
					NULL,	// no container
					xpWidgetClass_MainWindow);

	XPSetWidgetProperty(SDK200TestsWidget, xpProperty_MainWindowHasCloseBoxes, 1);

	SDK200TestsWindow = XPCreateWidget(x+50, y-50, x2-50, y2+50,
					1,	// Visible
					"",	// desc
					0,		// root
					SDK200TestsWidget,
					xpWidgetClass_SubWindow);

	XPSetWidgetProperty(SDK200TestsWindow, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow);

	SDK200TestsButton1 = XPCreateWidget(x+60, y-60, x+140, y-82,
						1, " Cmd Test 1", 0, SDK200TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK200TestsButton1, xpProperty_ButtonType, xpPushButton);

	SDK200TestsButton2 = XPCreateWidget(x+150, y-60, x+230, y-82,
						1, " Cmd Test 2", 0, SDK200TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK200TestsButton2, xpProperty_ButtonType, xpPushButton);

	SDK200TestsButton3 = XPCreateWidget(x+240, y-60, x+320, y-82,
						1, " Cursor", 0, SDK200TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK200TestsButton3, xpProperty_ButtonType, xpPushButton);

	SDK200TestsButton4 = XPCreateWidget(x+330, y-60, x+410, y-82,
						1, " Font", 0, SDK200TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK200TestsButton4, xpProperty_ButtonType, xpPushButton);

	SDK200TestsButton5 = XPCreateWidget(x+420, y-60, x+500, y-82,
						1, " Objects", 0, SDK200TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK200TestsButton5, xpProperty_ButtonType, xpPushButton);

	SDK200TestsButton6 = XPCreateWidget(x+60, y-90, x+140, y-112,
						1, " Load SIT", 0, SDK200TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK200TestsButton6, xpProperty_ButtonType, xpPushButton);

	SDK200TestsButton7 = XPCreateWidget(x+150, y-90, x+230, y-112,
						1, " Save SIT", 0, SDK200TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK200TestsButton7, xpProperty_ButtonType, xpPushButton);

	SDK200TestsButton8 = XPCreateWidget(x+240, y-90, x+320, y-112,
						1, " Load SMO", 0, SDK200TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK200TestsButton8, xpProperty_ButtonType, xpPushButton);

	SDK200TestsButton9 = XPCreateWidget(x+330, y-90, x+410, y-112,
						1, " Save SMO", 0, SDK200TestsWidget,
						xpWidgetClass_Button);

	XPSetWidgetProperty(SDK200TestsButton9, xpProperty_ButtonType, xpPushButton);

	for (Item=0; Item<MAX_ITEMS; Item++)
	{
		SDK200TestsText[Item] = XPCreateWidget(x+60, y-(120 + (Item*30)), x+160, y-(142 + (Item*30)),
							1,	// Visible
							SDK200TestsDataDesc[Item],// desc
							0,		// root
							SDK200TestsWidget,
							xpWidgetClass_Caption);

		SDK200TestsEdit[Item] = XPCreateWidget(x+170, y-(120 + (Item*30)), x+570, y-(142 + (Item*30)),
							1, "", 0, SDK200TestsWidget,
							xpWidgetClass_TextField);

		XPSetWidgetProperty(SDK200TestsEdit[Item], xpProperty_TextFieldType, xpTextEntryField);

	}

	sprintf(Buffer,"Object Index = %d : %s", SDK200TestsCurrentObjectIndex, SDK200TestsObjectPath[SDK200TestsCurrentObjectIndex]);
	XPSetWidgetDescriptor(SDK200TestsEdit[4], Buffer);

	XPAddWidgetCallback(SDK200TestsWidget, SDK200TestsHandler);
}

//Handle the widget messages
int	SDK200TestsHandler(
						XPWidgetMessage			inMessage,
						XPWidgetID				inWidget,
						intptr_t				inParam1,
						intptr_t				inParam2)
{
	char Buffer[256];
	XPLMCommandRef Command1Ref;
	char CursorStatusDesc[4][80] = {"xplm_CursorDefault", "xplm_CursorHidden", "xplm_CursorArrow", "xplm_CursorCustom"};

	// Get the mouse wheel messages and display in widget
	if (inMessage == xpMsg_MouseWheel)
	{
		XPMouseState_t *pXPMouseState = (XPMouseState_t *) inParam1;
		sprintf(Buffer,"x = %d, y = %d, button = %d, delta = %d", pXPMouseState->x, pXPMouseState->y, pXPMouseState->button, pXPMouseState->delta);
		XPSetWidgetDescriptor(SDK200TestsEdit[2], Buffer);
		return 1;
	}

	// Get the cursor status message and display in widget
	if (inMessage == xpMsg_CursorAdjust)
	{
		inParam2 = (intptr_t) SDK200TestsCursorStatus[SDK200TestsCursorStatusIndex];
		sprintf(Buffer,"New Cursor Status = %d : %s", inParam2, CursorStatusDesc[SDK200TestsCursorStatusIndex]);
		XPSetWidgetDescriptor(SDK200TestsEdit[3], Buffer);
		return 1;
	}

	// When widget close cross is clicked we only hide the widget
	if (inMessage == xpMessage_CloseButtonPushed)
	{
		if (SDK200TestsMenuItem1 == 1)
		{
			XPDestroyWidget(SDK200TestsWidget, 1);
			SDK200TestsMenuItem1 = 0;
		}
		return 1;
	}

	// Process when a button on the widget is pressed
	if (inMessage == xpMsg_PushButtonPressed)
	{
		// Tests the Command API, will find command
		if (inParam1 == (intptr_t)SDK200TestsButton1)
		{
			Command1Ref = XPLMFindCommand("sim/operation/screenshot");
			if(Command1Ref) 
			{
				// Separate the command action
				XPLMCommandBegin(Command1Ref);
				XPLMCommandEnd(Command1Ref);
			}
			return 1;
		}

		if (inParam1 == (intptr_t)SDK200TestsButton2)
		{
			// Tests the Command API, will create the command
			Command1Ref = XPLMCreateCommand("sim/operation/screenshot", "Test screenshot");
			if(Command1Ref) 
			{
				// Used the function to combine the command action
				XPLMCommandOnce(Command1Ref);
			}
			return 1;
		}

	    // Index is increased every button press
		if (inParam1 == (intptr_t)SDK200TestsButton3)
		{
			SDK200TestsCursorStatusIndex++;
		    // Wrap around
			if (SDK200TestsCursorStatusIndex > 3)
				SDK200TestsCursorStatusIndex = 0;
			return 1;
		}

	    // Index is increased every button press
		if (inParam1 == (intptr_t)SDK200TestsButton4)
		{
			SDK200TestsCurrentFontIndex++;
		    // Wrap around
			if (SDK200TestsCurrentFontIndex > 1)
				SDK200TestsCurrentFontIndex = 0;
			return 1;
		}

	    // Index is increased every button press
		if (inParam1 == (intptr_t)SDK200TestsButton5)
		{
			SDK200TestsCurrentObjectIndex++;
		    // Wrap around
			if (SDK200TestsCurrentObjectIndex > MAX_OBJECT_ITEMS)
				SDK200TestsCurrentObjectIndex = 0;
			// Reset so that draw is invoked
			SDK200TestsObject = NULL;
		    // If less than MAX_OBJECT_ITEMS then it is an xplane object
			if (SDK200TestsCurrentObjectIndex < MAX_OBJECT_ITEMS)
				sprintf(Buffer,"Object Index = %d : %s", SDK200TestsCurrentObjectIndex, SDK200TestsObjectPath[SDK200TestsCurrentObjectIndex]);
		    // Otherwise use the user supplied object
			else
				sprintf(Buffer,"Object Index = %d : %s", SDK200TestsCurrentObjectIndex, "User Supplied Object");
			XPSetWidgetDescriptor(SDK200TestsEdit[4], Buffer);
			return 1;
		}

		if (inParam1 == (intptr_t)SDK200TestsButton6)
		{
			if (XPLMLoadDataFile(xplm_DataFile_Situation, "Output/situations/test.sit"))
				XPLMDebugString("XPLMLoadDataFile (.sit) successful\n");
			else
				XPLMDebugString("XPLMLoadDataFile (.sit) failed\n");
			return 1;
		}

		if (inParam1 == (intptr_t)SDK200TestsButton7)
		{
			if (XPLMSaveDataFile(xplm_DataFile_Situation, "Output/situations/test.sit"))
				XPLMDebugString("XPLMSaveDataFile (.sit) successful\n");
			else
				XPLMDebugString("XPLMSaveDataFile (.sit) failed\n");
			return 1;
		}

		if (inParam1 == (intptr_t)SDK200TestsButton8)
		{
			if (XPLMLoadDataFile(xplm_DataFile_ReplayMovie, "Output/replays/test.rep"))
				XPLMDebugString("XPLMLoadDataFile (.smo) successful\n");
			else
				XPLMDebugString("XPLMLoadDataFile (.smo) failed\n");
			return 1;
		}

		if (inParam1 == (intptr_t)SDK200TestsButton9)
		{
			if (XPLMSaveDataFile(xplm_DataFile_ReplayMovie, "Output/replays/test.rep"))
				XPLMDebugString("XPLMSaveDataFile (.smo) successful\n");
			else
				XPLMDebugString("XPLMSaveDataFile (.smo) failed\n");
			return 1;
		}
	}
	return 0;
}						

//---------------------------------------------------------------------------
Leave a comment

FMSUtility

/*
	FMSUtility Example
	Written by Sandy Barbour - 21/01/2005
	
	This examples shows how to access the FMS
*/

#include "XPLMPlugin.h"
#include "XPLMUtilities.h"
#include "XPLMProcessing.h"
#include "XPLMMenus.h"
#include "XPLMGraphics.h"
#include "XPLMPlanes.h"
#include "XPLMDataAccess.h"
#include "XPLMNavigation.h"
#include "XPWidgets.h"
#include "XPStandardWidgets.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#if IBM
#include <windows.h>
#endif


#define MAX_NAV_TYPES 13
#define MAX_FMS_ENTRIES 100

static int MenuItem1, NavTypeLinePosition;

/// Widgets
static XPWidgetID FMSUtilityWidget = NULL, FMSUtilityWindow1 = NULL, FMSUtilityWindow2 = NULL;
static XPWidgetID GetFMSEntryButton, ClearEntryButton;
static XPWidgetID GetEntryIndexButton, SetEntryIndexButton, GetDestinationEntryButton, SetDestinationEntryButton;
static XPWidgetID EntryIndexEdit, DestinationEntryIndexEdit;
static XPWidgetID GetNumberOfEntriesButton, GetNumberOfEntriesText;
static XPWidgetID IndexCaption, IndexEdit, SegmentCaption, SegmentCaption2, AirportIDCaption, AirportIDEdit, AltitudeCaption, AltitudeEdit;
static XPWidgetID NavTypeCaption, NavTypeEdit, NavTypeText, UpArrow, DownArrow, SetFMSEntryButton;
static XPWidgetID LatCaption, LatEdit, LonCaption, LonEdit, SetLatLonButton;


/// Structure for the nav types and a more meaningful description
typedef struct _NAVTYPE
{
    char Description[80];
    XPLMNavType EnumValue;
} NAVTYPE;

static NAVTYPE NavTypeLookup[MAX_NAV_TYPES] = {{"Unknown",			xplm_Nav_Unknown},
                                        {"Airport",			xplm_Nav_Airport},
                                        {"NDB",				xplm_Nav_NDB},
                                        {"VOR",				xplm_Nav_VOR},
                                        {"ILS",				xplm_Nav_ILS},
                                        {"Localizer",		xplm_Nav_Localizer},
                                        {"Glide Slope",		xplm_Nav_GlideSlope},
                                        {"Outer Marker",	xplm_Nav_OuterMarker},
                                        {"Middle Marker",	xplm_Nav_MiddleMarker},
                                        {"Inner Marker",	xplm_Nav_InnerMarker},
                                        {"Fix",				xplm_Nav_Fix},
                                        {"DME",				xplm_Nav_DME},
                                        {"Lat/Lon",			xplm_Nav_LatLon}};

/// Utility functions and callbacks

/// This is used to get around a sprintf float bug with codewarrior on the mac
inline	float	HACKFLOAT(float val)
{
	return val;
}
/*
#if IBM || LIN
inline	float	HACKFLOAT(float val)
{
	return val;
}
#else
inline long long HACKFLOAT(float val)
{
	double	d = val;
	long long temp;
	temp = *((long long *) &d);
	return temp;
}
#endif
*/

static void FMSUtilityMenuHandler(void *, void *);
static void CreateFMSUtilityWidget(int x1, int y1, int w, int h);
static int GetCBIndex(int Type);

static int FMSUtilityHandler(
						XPWidgetMessage			inMessage,
						XPWidgetID				inWidget,
						intptr_t				inParam1,
						intptr_t				inParam2);


PLUGIN_API int XPluginStart(
						char *		outName,
						char *		outSig,
						char *		outDesc)
{
	XPLMMenuID	id;
	int			item;

	strcpy(outName, "FMS Utility");
	strcpy(outSig, "xpsdk.experimental.FMSUtility");
	strcpy(outDesc, "A plug-in that accesses the FMS.");

	// Create our menu
	item = XPLMAppendMenuItem(XPLMFindPluginsMenu(), "FMSUtility", NULL, 1);
	id = XPLMCreateMenu("FMSUtility", XPLMFindPluginsMenu(), item, FMSUtilityMenuHandler, NULL);
	XPLMAppendMenuItem(id, "Utility Panel", (void *)"FMSUtility", 1);

	// Flag to tell us if the widget is being displayed.
	MenuItem1 = 0;
	NavTypeLinePosition = 0;

	return 1;
}

PLUGIN_API void	XPluginStop(void)
{
	if (MenuItem1 == 1)
	{
		XPDestroyWidget(FMSUtilityWidget, 1);
		MenuItem1 = 0;
	}
}

PLUGIN_API int XPluginEnable(void)
{
	return 1;
}

PLUGIN_API void XPluginDisable(void)
{
}

PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void * inParam)
{
}


// This will create our widget from menu selection.
// MenuItem1 flag stops it from being created more than once.
void FMSUtilityMenuHandler(void * mRef, void * iRef)
{
	// If menu selected create our widget dialog
	if (!strcmp((char *) iRef, "FMSUtility"))
	{
		if (MenuItem1 == 0)
		{
			CreateFMSUtilityWidget(221, 640, 420, 290);
			MenuItem1 = 1;
		}
		else
			if(!XPIsWidgetVisible(FMSUtilityWidget))
				XPShowWidget(FMSUtilityWidget);
	}
}						


// This will create our widget dialog.
// I have made all child widgets relative to the input paramter.
// This makes it easy to position the dialog
void CreateFMSUtilityWidget(int x, int y, int w, int h)
{
	int x2 = x + w;
	int y2 = y - h;
	char Buffer[255];
	

	strcpy(Buffer, "FMS Example by Sandy Barbour - 2005");

	// Create the Main Widget window
	FMSUtilityWidget = XPCreateWidget(x, y, x2, y2,
					1,	// Visible
					Buffer,	// desc
					1,		// root
					NULL,	// no container
					xpWidgetClass_MainWindow);

	// Add Close Box decorations to the Main Widget
	XPSetWidgetProperty(FMSUtilityWidget, xpProperty_MainWindowHasCloseBoxes, 1);

	// Create the Sub Widget1 window
	FMSUtilityWindow1 = XPCreateWidget(x+10, y-30, x+160, y2+10,
					1,	// Visible
					"",	// desc
					0,		// root
					FMSUtilityWidget,
					xpWidgetClass_SubWindow);

	// Set the style to sub window
	XPSetWidgetProperty(FMSUtilityWindow1, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow);

	// Create the Sub Widget2 window
	FMSUtilityWindow2 = XPCreateWidget(x+170, y-30, x2-10, y2+10,
					1,	// Visible
					"",	// desc
					0,		// root
					FMSUtilityWidget,
					xpWidgetClass_SubWindow);

	// Set the style to sub window
	XPSetWidgetProperty(FMSUtilityWindow2, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow);

	/// Entry Index
	GetEntryIndexButton = XPCreateWidget(x+20, y-40, x+110, y-62,
					1, " Get Entry Index", 0, FMSUtilityWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(GetEntryIndexButton, xpProperty_ButtonType, xpPushButton);

	EntryIndexEdit = XPCreateWidget(x+120, y-40, x+150, y-62,
					1, "0", 0, FMSUtilityWidget,
					xpWidgetClass_TextField);

	XPSetWidgetProperty(EntryIndexEdit, xpProperty_TextFieldType, xpTextEntryField);
	XPSetWidgetProperty(EntryIndexEdit, xpProperty_Enabled, 1);

	SetEntryIndexButton = XPCreateWidget(x+20, y-70, x+110, y-92,
					1, " Set Entry Index", 0, FMSUtilityWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(SetEntryIndexButton, xpProperty_ButtonType, xpPushButton);

	/// Destination Index
	GetDestinationEntryButton = XPCreateWidget(x+20, y-100, x+110, y-122,
					1, " Get Dest Index", 0, FMSUtilityWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(GetDestinationEntryButton, xpProperty_ButtonType, xpPushButton);

	DestinationEntryIndexEdit = XPCreateWidget(x+120, y-100, x+150, y-122,
					1, "0", 0, FMSUtilityWidget,
					xpWidgetClass_TextField);

	XPSetWidgetProperty(DestinationEntryIndexEdit, xpProperty_TextFieldType, xpTextEntryField);
	XPSetWidgetProperty(DestinationEntryIndexEdit, xpProperty_Enabled, 1);

	SetDestinationEntryButton = XPCreateWidget(x+20, y-130, x+110, y-152,
					1, " Set Dest Index", 0, FMSUtilityWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(SetDestinationEntryButton, xpProperty_ButtonType, xpPushButton);

	/// Number of Entries
	GetNumberOfEntriesButton = XPCreateWidget(x+20, y-160, x+110, y-182,
					1, " Get No. Entries", 0, FMSUtilityWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(GetNumberOfEntriesButton, xpProperty_ButtonType, xpPushButton);

	GetNumberOfEntriesText = XPCreateWidget(x+120, y-160, x+150, y-182,
					1, "", 0, FMSUtilityWidget,
					xpWidgetClass_TextField);

	XPSetWidgetProperty(GetNumberOfEntriesText, xpProperty_TextFieldType, xpTextEntryField);
	XPSetWidgetProperty(GetNumberOfEntriesText, xpProperty_Enabled, 0);

	/// Clear Entry
	ClearEntryButton = XPCreateWidget(x+20, y-190, x+110, y-212,
					1, " Clear Entry", 0, FMSUtilityWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(ClearEntryButton, xpProperty_ButtonType, xpPushButton);

	/// Index (Segment - 1)
	IndexCaption = XPCreateWidget(x+180, y-40, x+230, y-62,
					1, "Index", 0, FMSUtilityWidget,
					xpWidgetClass_Caption);

	IndexEdit = XPCreateWidget(x+240, y-40, x+290, y-62,
					1, "0", 0, FMSUtilityWidget,
					xpWidgetClass_TextField);

	XPSetWidgetProperty(IndexEdit, xpProperty_TextFieldType, xpTextEntryField);
	XPSetWidgetProperty(IndexEdit, xpProperty_Enabled, 1);

	SegmentCaption = XPCreateWidget(x+300, y-40, x+350, y-62,
					1, "Segment", 0, FMSUtilityWidget,
					xpWidgetClass_Caption);
					
	SegmentCaption2 = XPCreateWidget(x+360, y-40, x+410, y-62,
					1, "1", 0, FMSUtilityWidget,
					xpWidgetClass_Caption);
					
	/// Airport ID
	AirportIDCaption = XPCreateWidget(x+180, y-70, x+230, y-92,
					1, "Airport ID", 0, FMSUtilityWidget,
					xpWidgetClass_Caption);

	AirportIDEdit = XPCreateWidget(x+240, y-70, x+290, y-92,
					1, "----", 0, FMSUtilityWidget,
					xpWidgetClass_TextField);

	XPSetWidgetProperty(AirportIDEdit, xpProperty_TextFieldType, xpTextEntryField);
	XPSetWidgetProperty(AirportIDEdit, xpProperty_Enabled, 1);

	/// Altitude
	AltitudeCaption = XPCreateWidget(x+180, y-100, x+230, y-122,
					1, "Altitude", 0, FMSUtilityWidget,
					xpWidgetClass_Caption);

	AltitudeEdit = XPCreateWidget(x+240, y-100, x+290, y-122,
					1, "0", 0, FMSUtilityWidget,
					xpWidgetClass_TextField);

	XPSetWidgetProperty(AltitudeEdit, xpProperty_TextFieldType, xpTextEntryField);
	XPSetWidgetProperty(AltitudeEdit, xpProperty_Enabled, 1);

	/// Nav Type
	NavTypeCaption = XPCreateWidget(x+180, y-130, x+230, y-152,
					1, "Nav Type", 0, FMSUtilityWidget,
					xpWidgetClass_Caption);

	sprintf(Buffer, "%s", NavTypeLookup[0].Description);
	NavTypeEdit = XPCreateWidget(x+240, y-130, x+340, y-152,
					1, Buffer, 0, FMSUtilityWidget,
					xpWidgetClass_TextField);

	XPSetWidgetProperty(NavTypeEdit, xpProperty_TextFieldType, xpTextEntryField);
	XPSetWidgetProperty(NavTypeEdit, xpProperty_Enabled, 0);

	// Used for selecting Nav Type
	UpArrow = XPCreateWidget(x+340, y-130, x+362, y-141,
							1, "", 0, FMSUtilityWidget,
							xpWidgetClass_Button);

	XPSetWidgetProperty(UpArrow, xpProperty_ButtonType, xpLittleUpArrow);

	// Used for selecting Nav Type
	DownArrow = XPCreateWidget(x+340, y-141, x+362, y-152,
							1, "", 0, FMSUtilityWidget,
							xpWidgetClass_Button);
	
	XPSetWidgetProperty(DownArrow, xpProperty_ButtonType, xpLittleDownArrow);

	NavTypeText = XPCreateWidget(x+362, y-130, x+400, y-152,
					1, "0", 0, FMSUtilityWidget,
					xpWidgetClass_TextField);

	XPSetWidgetProperty(NavTypeText, xpProperty_TextFieldType, xpTextEntryField);
	XPSetWidgetProperty(NavTypeText, xpProperty_Enabled, 0);

	/// Get FMS Entry Info
	GetFMSEntryButton = XPCreateWidget(x+180, y-160, x+270, y-182,
					1, " Get FMS Entry", 0, FMSUtilityWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(GetFMSEntryButton, xpProperty_ButtonType, xpPushButton);

	/// Set FMS Entry Info
	SetFMSEntryButton = XPCreateWidget(x+280, y-160, x+370, y-182,
					1, " Set FMS Entry", 0, FMSUtilityWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(SetFMSEntryButton, xpProperty_ButtonType, xpPushButton);

	/// Lat / Lon
	LatCaption = XPCreateWidget(x+180, y-190, x+230, y-212,
					1, "Latitude", 0, FMSUtilityWidget,
					xpWidgetClass_Caption);

	LatEdit = XPCreateWidget(x+240, y-190, x+310, y-212,
					1, "0", 0, FMSUtilityWidget,
					xpWidgetClass_TextField);

	XPSetWidgetProperty(LatEdit, xpProperty_TextFieldType, xpTextEntryField);
	XPSetWidgetProperty(LatEdit, xpProperty_Enabled, 1);

	LonCaption = XPCreateWidget(x+180, y-220, x+230, y-242,
					1, "Longitude", 0, FMSUtilityWidget,
					xpWidgetClass_Caption);

	LonEdit = XPCreateWidget(x+240, y-220, x+310, y-242,
					1, "0", 0, FMSUtilityWidget,
					xpWidgetClass_TextField);

	XPSetWidgetProperty(LonEdit, xpProperty_TextFieldType, xpTextEntryField);
	XPSetWidgetProperty(LonEdit, xpProperty_Enabled, 1);

	SetLatLonButton = XPCreateWidget(x+180, y-250, x+270, y-272,
					1, " Set Lat/Lon", 0, FMSUtilityWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(SetLatLonButton, xpProperty_ButtonType, xpPushButton);

	// Register our widget handler
	XPAddWidgetCallback(FMSUtilityWidget, FMSUtilityHandler);
}

// This is the handler for our widget
// It can be used to process button presses etc.
// In this example we are only interested when the close box is pressed
int	FMSUtilityHandler(
						XPWidgetMessage			inMessage,
						XPWidgetID				inWidget,
						intptr_t				inParam1,
						intptr_t				inParam2)
{
	void *Param;    
	Param = 0;    
	char Buffer[255];

	// Close button will get rid of our main widget
	// All child widgets will get the bullet as well
	if (inMessage == xpMessage_CloseButtonPushed)
	{
		if (MenuItem1 == 1)
		{
			XPHideWidget(FMSUtilityWidget);
		}
		return 1;
	}

	// Handle any button pushes
	if (inMessage == xpMsg_PushButtonPressed)
	{
		/// Most of these handlers get a value.
		/// It then has to be converted to a string using sprintf.
		/// This is because "XPSetWidgetDescriptor" expects a string as its second parameter.
		if (inParam1 == (intptr_t)ClearEntryButton)
		{
		    XPLMClearFMSEntry(XPLMGetDisplayedFMSEntry());
			return 1;
		}
		
		if (inParam1 == (intptr_t)GetEntryIndexButton)
		{
		    int Index = XPLMGetDisplayedFMSEntry();
			sprintf(Buffer, "%d", Index);
			XPSetWidgetDescriptor(EntryIndexEdit, Buffer);
			return 1;
		}
		
		if (inParam1 == (intptr_t)SetEntryIndexButton)
		{
			XPGetWidgetDescriptor(EntryIndexEdit, Buffer, sizeof(Buffer));
		    int Index = atoi(Buffer);
			XPLMSetDisplayedFMSEntry(Index);
			return 1;
		}
		
		if (inParam1 == (intptr_t)GetDestinationEntryButton)
		{
		    int Index = XPLMGetDestinationFMSEntry();
			sprintf(Buffer, "%d", Index);
			XPSetWidgetDescriptor(DestinationEntryIndexEdit, Buffer);
			return 1;
		}
		
		if (inParam1 == (intptr_t)SetDestinationEntryButton)
		{
			XPGetWidgetDescriptor(DestinationEntryIndexEdit, Buffer, sizeof(Buffer));
		    int Index = atoi(Buffer);
			XPLMSetDestinationFMSEntry(Index);
			return 1;
		}
		
		if (inParam1 == (intptr_t)GetNumberOfEntriesButton)
		{
		    int Count = XPLMCountFMSEntries();
			sprintf(Buffer, "%d", Count);
			XPSetWidgetDescriptor(GetNumberOfEntriesText, Buffer);
			return 1;
		}
		
		if (inParam1 == (intptr_t)GetFMSEntryButton)
		{
			int Index = XPLMGetDisplayedFMSEntry();
		    XPLMNavType outType;
			char outID[80];
			XPLMNavRef outRef;
			int outAltitude;
			float outLat;
			float outLon;
	
			XPLMGetFMSEntryInfo(Index, &outType, outID, &outRef, &outAltitude, &outLat, &outLon);
			sprintf(Buffer, "%d", Index);
			XPSetWidgetDescriptor(IndexEdit, Buffer);
			sprintf(Buffer, "%d", Index+1);
			XPSetWidgetDescriptor(SegmentCaption2, Buffer);
			if (outType == xplm_Nav_LatLon)
				XPSetWidgetDescriptor(AirportIDEdit, "----");
			else
				XPSetWidgetDescriptor(AirportIDEdit, outID);
			sprintf(Buffer, "%d", outAltitude);
			XPSetWidgetDescriptor(AltitudeEdit, Buffer);
			XPSetWidgetDescriptor(NavTypeEdit, NavTypeLookup[GetCBIndex(outType)].Description);
			sprintf(Buffer, "%d", NavTypeLookup[GetCBIndex(outType)].EnumValue);
			XPSetWidgetDescriptor(NavTypeText, Buffer);
			sprintf(Buffer, "%+05.2f", HACKFLOAT(outLat));
			XPSetWidgetDescriptor(LatEdit, Buffer);
			sprintf(Buffer, "%+05.2f", HACKFLOAT(outLon));
			XPSetWidgetDescriptor(LonEdit, Buffer);

			return 1;
		}
		
		if (inParam1 == (intptr_t)SetFMSEntryButton)
		{
			XPGetWidgetDescriptor(IndexEdit, Buffer, sizeof(Buffer));
			int Index = atoi(Buffer);
			sprintf(Buffer, "%d", Index+1);
			XPSetWidgetDescriptor(SegmentCaption2, Buffer);
			XPGetWidgetDescriptor(AltitudeEdit, Buffer, sizeof(Buffer));
			int Altitude = atoi(Buffer);
			XPGetWidgetDescriptor(NavTypeText, Buffer, sizeof(Buffer));
			int NavType = atoi(Buffer);
			XPGetWidgetDescriptor(AirportIDEdit, Buffer, sizeof(Buffer));
			XPLMSetFMSEntryInfo(Index, XPLMFindNavAid(NULL, Buffer, NULL, NULL, NULL, NavType), Altitude);

			return 1;
		}
		
		if (inParam1 == (intptr_t)SetLatLonButton)
		{
			XPGetWidgetDescriptor(IndexEdit, Buffer, sizeof(Buffer));
			int Index = atoi(Buffer);
			sprintf(Buffer, "%d", Index+1);
			XPSetWidgetDescriptor(SegmentCaption2, Buffer);
			XPGetWidgetDescriptor(AltitudeEdit, Buffer, sizeof(Buffer));
			int Altitude = atoi(Buffer);
			XPGetWidgetDescriptor(LatEdit, Buffer, sizeof(Buffer));
			float Lat = atof(Buffer);
			XPGetWidgetDescriptor(LonEdit, Buffer, sizeof(Buffer));
			float Lon = atof(Buffer);
			XPLMSetFMSEntryLatLon(Index, Lat, Lon, Altitude);

			return 1;
		}
		
		// Up Arrow is used to modify the NavTypeLookup Array Index 
		if (inParam1 == (intptr_t)UpArrow)
		{
			NavTypeLinePosition--;
			if (NavTypeLinePosition < 0)
				NavTypeLinePosition = MAX_NAV_TYPES-1;
			XPSetWidgetDescriptor(NavTypeEdit, NavTypeLookup[NavTypeLinePosition].Description);
			sprintf(Buffer, "%d", NavTypeLookup[NavTypeLinePosition].EnumValue);
			XPSetWidgetDescriptor(NavTypeText, Buffer);

			return 1;
		}

		// Down Arrow is used to modify the NavTypeLookup Array Index 
		if (inParam1 == (intptr_t)DownArrow)
		{
			NavTypeLinePosition++;
			if (NavTypeLinePosition > MAX_NAV_TYPES-1)
				NavTypeLinePosition = 0;
			XPSetWidgetDescriptor(NavTypeEdit, NavTypeLookup[NavTypeLinePosition].Description);
			sprintf(Buffer, "%d", NavTypeLookup[NavTypeLinePosition].EnumValue);
			XPSetWidgetDescriptor(NavTypeText, Buffer);

			return 1;
		}

	}

	return 0;
}						

/// This function takes an XPLMNavType and
/// returns the index into the NavTypeLookup array.
/// We can then use that index to access the description or enum.
int GetCBIndex(int Type)
{
    int CBIndex = 0;

    for (int Index=0; Index<MAX_NAV_TYPES; Index++)
    {
        if (NavTypeLookup[Index].EnumValue == Type)
        {
            CBIndex = Index;
            break;
        }
    }
    return CBIndex;
}
Leave a comment

ReloadPlugins

/*
	Reload Plugin
	Written by Sandy Barbour - 24/02/2004

	This examples shows how reload plugins
	This is handy for debugging as you don't have to stop XPlane
*/

#if IBM
#include <windows.h>
#endif
#include <string.h>
#include <stdio.h>

#include "XPLMPlugin.h"
#include "XPLMMenus.h"

static void ReloadPluginsMenuHandler(void * mRef, void * iRef);

/*
 * XPluginStart
 * 
 * Our start routine registers our window and does any other initialization we 
 * must do.
 * 
 */
PLUGIN_API int XPluginStart(
						char *		outName,
						char *		outSig,
						char *		outDesc)
{
	/* First we must fill in the passed in buffers to describe our
	 * plugin to the plugin-system. */
	XPLMMenuID	id;
	int			item;

	strcpy(outName, "ReloadPlugins");
	strcpy(outSig, "xplanesdk.sandybarbour.ReloadPlugins");
	strcpy(outDesc, "A plugin that allows plugins to be reloaded.");
			
	item = XPLMAppendMenuItem(XPLMFindPluginsMenu(), "ReloadPlugins", NULL, 1);

	id = XPLMCreateMenu("ReloadPlugins", XPLMFindPluginsMenu(), item, ReloadPluginsMenuHandler, NULL);
	XPLMAppendMenuItem(id, "Reload", (void *)"Reload plugins",1);

	/* We must return 1 to indicate successful initialization, otherwise we
	 * will not be called back again. */
	 
	return 1;
}

/*
 * XPluginStop
 * 
 * Our cleanup routine deallocates our window.
 * 
 */
PLUGIN_API void	XPluginStop(void)
{
}

/*
 * XPluginDisable
 * 
 * We do not need to do anything when we are disabled, but we must provide the handler.
 * 
 */
PLUGIN_API void XPluginDisable(void)
{
}

/*
 * XPluginEnable.
 * 
 * We don't do any enable-specific initialization, but we must return 1 to indicate
 * that we may be enabled at this time.
 * 
 */
PLUGIN_API int XPluginEnable(void)
{
	return 1;
}

/*
 * XPluginReceiveMessage
 * 
 * We don't have to do anything in our receive message handler, but we must provide one.
 * 
 */
PLUGIN_API void XPluginReceiveMessage(
					XPLMPluginID	inFromWho,
					int				inMessage,
					void *			inParam)
{
}


void ReloadPluginsMenuHandler(void * mRef, void * iRef)
{
	if (!strcmp((char *) iRef, "Reload plugins"))
	{
		XPLMReloadPlugins();
	}
}
4 Comments

TestWidgets

/*
	TestWidgets Example

	Written by Sandy Barbour - 21/02/2003
	Modified by Sandy Barbour - 08/12/2009 // How time flies when you are having fun. :-)
	Combined example plugin with the Custom Widget creation code.

	This example shows how to create 2 custom widgets.
	These are then used by the example plugin.
*/

#if IBM
#include <windows.h>
#endif
#if LIN
#include <GL/gl.h>
#else
#if __GNUC__
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#endif

#include "XPLMDisplay.h"
#include "XPLMGraphics.h"
#include "XPLMProcessing.h"
#include "XPLMDataAccess.h"
#include "XPLMMenus.h"
#include "XPLMUtilities.h"
#include "XPWidgets.h"
#include "XPStandardWidgets.h"
#include "XPLMCamera.h"
#include "XPUIGraphics.h"
#include "XPWidgetUtils.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <vector>
#include <string>

/*------------------------------------------------------------------------*/

/*
 * XPWidgetsEx.h
 *
 * Copyright 2005 Sandy Barbour and Ben Supnik
 * 
 * All rights reserved.  See license.txt for usage.
 * 
 * X-Plane SDK Version: 1.0.2                                                  
 *
 */
                                 
/************************************************************************
 * POPUP MENU PICKS
 ************************************************************************
 *
 * This code helps do a popup menu item pick.  Since x-plane must be 
 * running to track the mouse, this popup menu pick is effectively 
 * asynchronous and non-modal to the code...you call the function and 
 * some time later your callback is called.
 *
 * However, due to the way the popup pick is structured, it will appear to
 * be somewhat modal to the user in that the next click after the popup 
 * is called must belong to it.
 *
 */

/*
 * XPPopupPick_f
 * 
 * This function is called when your popup is picked.  inChoice will be the number
 * of the item picked, or -1 if no item was picked.  (You should almost always ignore
 * a -1.
 *
 */
typedef	void (* XPPopupPick_f)(int inChoice, void * inRefcon);

/*
 * XPPickPopup
 *
 * This routine creates a dynamic 'popup menu' on the fly.  If inCurrentItem is
 * non-negative, it is the item that will be under the mouse.  In this case, the
 * mouse X and Y should be the top left of a popup box if there is such a thing.
 * If inCurrentItem is -1, the popup menu appears at exactly inMouseX and inMouseY.
 *
 * You pass in the items, newline terminated ('\n') as well as a callback that is
 * called when an item is picked, and a ref con for that function.
 *
 */
static void		XPPickPopup(
						int				inMouseX,
						int				inMouseY,
						const char *	inItems,
						int				inCurrentItem,
						XPPopupPick_f	inCallback,
						void *			inRefcon);

/* Impl notes: we can dispose from the mouse up.  So...on mouse up
 * we first call the popup func but then we nuke ourselves.  Internally
 * there is a data structure that is in the refcon of the xplm window that
 * contains the callback for the user and the text, etc. */
 
/************************************************************************
 * POPUP MENU BUTTON WIDGET
 ************************************************************************
 * 
 * This widget implements a stanard pick-one-from-many-style popup menu 
 * button.  The text is taken from the current item.  The descriptor is
 * the items, newline-terminated.
 *
 * A message is sent whenever a new item is picked by the user.
 * 
 */

#define	xpWidgetClass_Popup					9
 
enum {
	// This is the item number of the current item, starting at 0.
	xpProperty_PopupCurrentItem				= 1800
};

enum {
	// This message is sent when an item is picked.
	// param 1 is the widget that was picked, param 2
	// is the item number.
	xpMessage_PopupNewItemPicked					= 1800
};

/*
 * XPCreatePopup
 *
 * This routine makes a popup widget for you.  You must provide
 * a container for this widget, like a window for it to sit in.
 *
 */
static XPWidgetID           XPCreatePopup(
                                   int                  inLeft,    
                                   int                  inTop,    
                                   int                  inRight,    
                                   int                  inBottom,    
                                   int                  inVisible,    
                                   const char *         inDescriptor,    
                                   XPWidgetID           inContainer);

static int		XPPopupButtonProc(
					XPWidgetMessage			inMessage,
					XPWidgetID				inWidget,
					intptr_t				inParam1,
					intptr_t				inParam2);


/************************************************************************
 * LISTBOX
 ************************************************************************
 *
 * This code helps do a listbox.  Since x-plane must be 
 * running to track the mouse, this listbox is effectively 
 * asynchronous and non-modal to the code...you call the function and 
 * some time later your callback is called.
 *
 * However, due to the way the listbox is structured, it will appear to
 * be somewhat modal to the user in that the next click after the listbox 
 * is called must belong to it.
 *
 */

/************************************************************************
 * LISTBOX SELECTION WIDGET
 ************************************************************************
 * 
 * This widget implements a standard pick-one-from-many-style selection menu 
 * button.  The text is taken from the current item.  The descriptor is
 * the items, newline-terminated.
 *
 * A message is sent whenever a new item is picked by the user.
 * 
 */

#define	xpWidgetClass_ListBox					10
 
enum {
	// This is the item number of the current item, starting at 0.
	xpProperty_ListBoxCurrentItem					= 1900,
	// This will add an item to the list box at the end.
	xpProperty_ListBoxAddItem						= 1901,
	// This will clear the list box and then add the items.
	xpProperty_ListBoxAddItemsWithClear				= 1902,
	// This will clear the list box.
	xpProperty_ListBoxClear							= 1903,
	// This will insert an item into the list box at the index.
	xpProperty_ListBoxInsertItem					= 1904,
	// This will delete an item from the list box at the index.
	xpProperty_ListBoxDeleteItem					= 1905,
	// This stores the pointer to the listbox data.
	xpProperty_ListBoxData							= 1906,
	// This stores the max Listbox Items.
	xpProperty_ListBoxMaxListBoxItems				= 1907,
	// This stores the highlight state.
	xpProperty_ListBoxHighlighted					= 1908,
	// This stores the scrollbar Min.
	xpProperty_ListBoxScrollBarMin					= 1909,
	// This stores the scrollbar Max.
	xpProperty_ListBoxScrollBarMax					= 1910,
	// This stores the scrollbar SliderPosition.
	xpProperty_ListBoxScrollBarSliderPosition		= 1911,
	// This stores the scrollbar ScrollBarPageAmount.
	xpProperty_ListBoxScrollBarPageAmount			= 1912
};

enum {
	// This message is sent when an item is picked.
	// param 1 is the widget that was picked, param 2
	// is the item number.
	xpMessage_ListBoxItemSelected				= 1900
};

/*
 * XPCreateListBox
 *
 * This routine makes a listbox widget for you.  You must provide
 * a container for this widget, like a window for it to sit in.
 *
 */
static XPWidgetID           XPCreateListBox(
                                   int                  inLeft,    
                                   int                  inTop,    
                                   int                  inRight,    
                                   int                  inBottom,    
                                   int                  inVisible,    
                                   const char *         inDescriptor,    
                                   XPWidgetID           inContainer);

static int		XPListBoxProc(
					XPWidgetMessage			inMessage,
					XPWidgetID				inWidget,
					intptr_t				inParam1,
					intptr_t				inParam2);

/*------------------------------------------------------------------------*/


static int MenuItem1;

static XPWidgetID TestWidgetsWidget, TestWidgetsWindow;
static XPWidgetID TestWidgetsPopup, TestWidgetsListBox;
static XPWidgetID ListboxInfoText1, ListboxInfoText2, ListboxAddItemButton, ListboxFillButton, ListboxClearButton, ListboxInsertButton, ListboxDeleteItemButton, ListboxInputTextEdit;
static XPWidgetID PopupInfoText, PopupInputTextEdit, PopupSetIndexButton;
static XPLMMenuID	id;

static char szPopupText[4096];
static char szListBoxText[4096];

static void TestWidgetsMenuHandler(void *, void *);

static void CreateTestWidgets(int x1, int y1, int w, int h);

// Handle any widget messages
static int TestWidgetsHandler(
						XPWidgetMessage			inMessage,
						XPWidgetID				inWidget,
						intptr_t				inParam1,
						intptr_t				inParam2);

/*------------------------------------------------------------------------*/

PLUGIN_API int XPluginStart(
						char *		outName,
						char *		outSig,
						char *		outDesc)
{
	int			item;

	strcpy(outName, "TestWidgets");
	strcpy(outSig, "xpsdk.examples.TestWidgets");
	strcpy(outDesc, "A plug-in that tests new widgets.");

	// Build menu
	item = XPLMAppendMenuItem(XPLMFindPluginsMenu(), "Test Widgets", NULL, 1);

	id = XPLMCreateMenu("Test Widgets", XPLMFindPluginsMenu(), item, TestWidgetsMenuHandler, NULL);
	XPLMAppendMenuItem(id, "Open", (void *)"Open", 1);

	// Used by widget to make sure only one widgets instance created
	MenuItem1 = 0;


	// Preload Popup
	szPopupText[0] = '\0';
	strcat( szPopupText, "Some longer text line 1");
	strcat( szPopupText, ";" );
	strcat( szPopupText, "Some longer text line 2");
	strcat( szPopupText, ";" );
	strcat( szPopupText, "Some longer text line 3");
	strcat( szPopupText, ";" );
	strcat( szPopupText, "Some longer text line 4");
	strcat( szPopupText, ";" );
	strcat( szPopupText, "Some longer text line 5");
	strcat( szPopupText, ";" );
	strcat( szPopupText, "Some longer text line 6");
	strcat( szPopupText, ";" );
	strcat( szPopupText, "Some longer text line 7");
	strcat( szPopupText, ";" );

	// Preload Listbox
	szListBoxText[0] = '\0';
	strcat( szListBoxText, "1234567890123456789012345678901234567890123456789012345678901234567890");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 2");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 3");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 4");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 5");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 6");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 7");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 8");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 9");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 10");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 11");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 12");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 13");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 14");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 15");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 16");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 17");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 18");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 19");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 20");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 21");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 22");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 23");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 24");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 25");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 26");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 27");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 28");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 29");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 30");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 31");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 32");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 33");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 34");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 35");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 36");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 37");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 38");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 39");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 40");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 41");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 42");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 43");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 44");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 45");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 46");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 47");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 48");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 49");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 50");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 51");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 52");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 53");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 54");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 55");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 56");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 57");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 58");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 59");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 60");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 61");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 62");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 63");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 64");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 65");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 66");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 67");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 68");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 69");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 70");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 71");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 72");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 73");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 74");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 75");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 76");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 77");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 78");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 79");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 80");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 81");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 82");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 83");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 84");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 85");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 86");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 87");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 88");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 89");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 90");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 91");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 92");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 93");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 94");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 95");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 96");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 97");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 98");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 99");
	strcat( szListBoxText, ";" );
	strcat( szListBoxText, "Item 100");
	strcat( szListBoxText, ";" );

	return 1;
}

/*------------------------------------------------------------------------*/

PLUGIN_API void	XPluginStop(void)
{
	// Clean up
	XPLMDestroyMenu(id);
	if (MenuItem1 == 1)
	{
		XPDestroyWidget(TestWidgetsWidget, 1);
		MenuItem1 = 0;
	}
}

/*------------------------------------------------------------------------*/

PLUGIN_API void XPluginDisable(void)
{
}

/*------------------------------------------------------------------------*/

PLUGIN_API int XPluginEnable(void)
{
	return 1;
}

/*------------------------------------------------------------------------*/

PLUGIN_API void XPluginReceiveMessage(XPLMPluginID inFrom, int inMsg, void * inParam)
{
}

/*------------------------------------------------------------------------*/

// Handle any menu messages, only one, to created widget dialog
void TestWidgetsMenuHandler(void * mRef, void * iRef)
{
	if (!strcmp((char *) iRef, "Open"))
	{
		if (MenuItem1 == 0)
		{
			CreateTestWidgets(300, 650, 450, 600);
			MenuItem1 = 1;
		}
		else
			if(!XPIsWidgetVisible(TestWidgetsWidget))
				XPShowWidget(TestWidgetsWidget);
	}
}

/*------------------------------------------------------------------------*/

// This creates the widgets dialog and any controls
void CreateTestWidgets(int x, int y, int w, int h)
{
	int x2 = x + w;
	int y2 = y - h;

	TestWidgetsWidget = XPCreateWidget(x, y, x2, y2,
					1,	// Visible
					"Test Widgets",	// desc
					1,		// root
					NULL,	// no container
					xpWidgetClass_MainWindow);

	XPSetWidgetProperty(TestWidgetsWidget, xpProperty_MainWindowHasCloseBoxes, 1);

	TestWidgetsWindow = XPCreateWidget(x+50, y-40, x2-50, y2+30,
					1,	// Visible
					"",	// desc
					0,		// root
					TestWidgetsWidget,
					xpWidgetClass_SubWindow);

	XPSetWidgetProperty(TestWidgetsWindow, xpProperty_SubWindowType, xpSubWindowStyle_SubWindow);

    TestWidgetsPopup = XPCreatePopup( x+70, y-50, x2-70, y-72,
                                         1, szPopupText, TestWidgetsWidget );

	PopupInfoText = XPCreateWidget(x+70, y-80, x2-70, y-102,
							1, "Popup Index 0", 0, TestWidgetsWidget,
							xpWidgetClass_TextField);

	XPSetWidgetProperty(PopupInfoText, xpProperty_TextFieldType, xpTextEntryField);

	PopupSetIndexButton = XPCreateWidget(x+70, y-110, x+170, y-132,
					1, " Set Popup Index", 0, TestWidgetsWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(PopupSetIndexButton, xpProperty_ButtonType, xpPushButton);

	PopupInputTextEdit = XPCreateWidget(x+180, y-110, x2-70, y-132,
							1, "0", 0, TestWidgetsWidget,
							xpWidgetClass_TextField);

	XPSetWidgetProperty(PopupInputTextEdit, xpProperty_TextFieldType, xpTextEntryField);

    TestWidgetsListBox = XPCreateListBox( x+70, y-140, x2-70, y-340,
                                         1, szListBoxText, TestWidgetsWidget );

	ListboxInfoText1 = XPCreateWidget(x+70, y-350, x2-70, y-372,
							1, "", 0, TestWidgetsWidget,
							xpWidgetClass_TextField);

	XPSetWidgetProperty(ListboxInfoText1, xpProperty_TextFieldType, xpTextEntryField);

	ListboxInfoText2 = XPCreateWidget(x+70, y-390, x2-70, y-392,
							1, "", 0, TestWidgetsWidget,
							xpWidgetClass_TextField);

	XPSetWidgetProperty(ListboxInfoText2, xpProperty_TextFieldType, xpTextEntryField);

	ListboxAddItemButton = XPCreateWidget(x+70, y-400, x+130, y-422,
					1, " Add Item", 0, TestWidgetsWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(ListboxAddItemButton, xpProperty_ButtonType, xpPushButton);

	ListboxFillButton = XPCreateWidget(x+140, y-400, x+200, y-422,
					1, " Fill LB", 0, TestWidgetsWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(ListboxFillButton, xpProperty_ButtonType, xpPushButton);

	ListboxClearButton = XPCreateWidget(x+210, y-400, x+270, y-422,
					1, " Clear LB", 0, TestWidgetsWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(ListboxClearButton, xpProperty_ButtonType, xpPushButton);

	ListboxInsertButton = XPCreateWidget(x+70, y-430, x+140, y-452,
					1, " Insert Item", 0, TestWidgetsWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(ListboxInsertButton, xpProperty_ButtonType, xpPushButton);

	ListboxDeleteItemButton = XPCreateWidget(x+150, y-430, x+220, y-452,
					1, " Delete Item", 0, TestWidgetsWidget,
					xpWidgetClass_Button);

	XPSetWidgetProperty(ListboxDeleteItemButton, xpProperty_ButtonType, xpPushButton);

	ListboxInputTextEdit = XPCreateWidget(x+70, y-460, x2-70, y-492,
							1, "Add Me", 0, TestWidgetsWidget,
							xpWidgetClass_TextField);

	XPSetWidgetProperty(ListboxInputTextEdit, xpProperty_TextFieldType, xpTextEntryField);

	XPAddWidgetCallback(TestWidgetsWidget, TestWidgetsHandler);
}

/*------------------------------------------------------------------------*/

// Handle any widget messages
int	TestWidgetsHandler(
						XPWidgetMessage			inMessage,
						XPWidgetID				inWidget,
						intptr_t				inParam1,
						intptr_t				inParam2)
{
	char Buffer[256];

	// Close button pressed, only hide the widget, rather than destropying it.
	if (inMessage == xpMessage_CloseButtonPushed)
	{
		if (MenuItem1 == 1)
		{
			XPHideWidget(TestWidgetsWidget);
		}
		return 1;
	}

	// Test for a button pressed
	if (inMessage == xpMsg_PushButtonPressed)
	{
		// This test adds three items to the listbox
		if (inParam1 == (intptr_t)ListboxAddItemButton)
		{
			strcpy(Buffer, "Line 1");
			XPSetWidgetDescriptor(TestWidgetsListBox, Buffer);
			XPSetWidgetProperty(TestWidgetsListBox, xpProperty_ListBoxAddItem, 1);
			strcpy(Buffer, "Line 2");
			XPSetWidgetDescriptor(TestWidgetsListBox, Buffer);
			XPSetWidgetProperty(TestWidgetsListBox, xpProperty_ListBoxAddItem, 1);
			strcpy(Buffer, "Line 3");
			XPSetWidgetDescriptor(TestWidgetsListBox, Buffer);
			XPSetWidgetProperty(TestWidgetsListBox, xpProperty_ListBoxAddItem, 1);
			return 1;
		}

		// This test clears the list box and then adds the test items
		if (inParam1 == (intptr_t)ListboxFillButton)
		{
			XPSetWidgetDescriptor(TestWidgetsListBox, szListBoxText);
			XPSetWidgetProperty(TestWidgetsListBox, xpProperty_ListBoxAddItemsWithClear, 1);
			return 1;
		}

		// This test clears the listbox
		if (inParam1 == (intptr_t)ListboxClearButton)
		{
			XPSetWidgetProperty(TestWidgetsListBox, xpProperty_ListBoxClear, 1);
			return 1;
		}

		// This test inserts the text entered into ListboxInputTextEdit at the
		// current selected listbox item position
		if (inParam1 == (intptr_t)ListboxInsertButton)
		{
			XPGetWidgetDescriptor(ListboxInputTextEdit, Buffer, sizeof(Buffer));
			XPSetWidgetDescriptor(TestWidgetsListBox, Buffer);
			XPSetWidgetProperty(TestWidgetsListBox, xpProperty_ListBoxInsertItem, 1);
			return 1;
		}

		// This test deletes the current selected listbox item
		if (inParam1 == (intptr_t)ListboxDeleteItemButton)
		{
			XPSetWidgetProperty(TestWidgetsListBox, xpProperty_ListBoxDeleteItem, 1);
			return 1;
		}

		// This test set the popup index to the number
		// entered into the PopupInputTextEdit edit box
		if (inParam1 == (intptr_t)PopupSetIndexButton)
		{
			XPGetWidgetDescriptor(PopupInputTextEdit, Buffer, sizeof(Buffer));
			int	curItem = atoi(Buffer);
			XPSetWidgetProperty(TestWidgetsPopup, xpProperty_PopupCurrentItem, curItem);
			XPGetWidgetDescriptor(TestWidgetsPopup, Buffer, sizeof(Buffer));
			curItem = XPGetWidgetProperty(TestWidgetsPopup, xpProperty_PopupCurrentItem, NULL);
			sprintf(Buffer, "Popup Index %d", curItem);
			XPSetWidgetDescriptor(PopupInfoText, Buffer);
			return 1;
		}
	}

	// This handles the listbox item selected message
	// i.e. when you click on an item
	if (inMessage == xpMessage_ListBoxItemSelected)
	{
		XPGetWidgetDescriptor(TestWidgetsListBox, Buffer, sizeof(Buffer));
		XPSetWidgetDescriptor(ListboxInfoText1, Buffer);
		sprintf(Buffer, "ListBox Index %d", inParam2);
		XPSetWidgetDescriptor(ListboxInfoText2, Buffer);
		return 1;
	}

	// This handles the popup item picked message
	// i.e. when you selecte another popup entry.
	if (inMessage == xpMessage_PopupNewItemPicked)
	{
		XPGetWidgetDescriptor(TestWidgetsPopup, Buffer, sizeof(Buffer));
		int	curItem = XPGetWidgetProperty(TestWidgetsPopup, xpProperty_PopupCurrentItem, NULL);
		sprintf(Buffer, "Popup Index %d", curItem);
		XPSetWidgetDescriptor(PopupInfoText, Buffer);
		return 1;
	}

	return 0;
}

/*------------------------------------------------------------------------*/


/*
 * XPListBox.cpp
 *
 * Copyright 2005 Sandy Barbour and Ben Supnik
 * 
 * All rights reserved.  See license.txt for usage.
 * 
 * X-Plane SDK Version: 1.0.2                                                  
 *
 */


#define LISTBOX_ITEM_HEIGHT 12
#define IN_RECT(x, y, l, t, r, b)	\
	(((x) >= (l)) && ((x) <= (r)) && ((y) >= (b)) && ((y) <= (t)))

#if IBM
static double round(double InValue)
{
    int WholeValue;
    double Fraction;

    WholeValue = InValue;
    Fraction = InValue - (double) WholeValue;

    if (Fraction >= 0.5)
        WholeValue++;

    return (double) WholeValue;
}
#endif

/************************************************************************
 *  X-PLANE UI INFRASTRUCTURE CODE
 ************************************************************************
 *
 * This code helps provde an x-plane compatible look.  It is copied from 
 * the source code from the widgets DLL; someday listyboxes will be part of
 * this, so our listboxes are written off of the same APIs.
 *
 */
 
// Enums for x-plane native colors. 
enum {

	xpColor_MenuDarkTinge = 0,
	xpColor_MenuBkgnd,
	xpColor_MenuHilite,
	xpColor_MenuLiteTinge,
	xpColor_MenuText,
	xpColor_MenuTextDisabled,
	xpColor_SubTitleText,
	xpColor_TabFront,
	xpColor_TabBack,
	xpColor_CaptionText,
	xpColor_ListText,
	xpColor_GlassText,
	xpColor_Count
};

// Enums for the datarefs we get them from.
static const char *	kXPlaneColorNames[] = {
	"sim/graphics/colors/menu_dark_rgb",			
	"sim/graphics/colors/menu_bkgnd_rgb",			
	"sim/graphics/colors/menu_hilite_rgb",	
	"sim/graphics/colors/menu_lite_rgb",		
	"sim/graphics/colors/menu_text_rgb",			
	"sim/graphics/colors/menu_text_disabled_rgb",	
	"sim/graphics/colors/subtitle_text_rgb",
	"sim/graphics/colors/tab_front_rgb",
	"sim/graphics/colors/tab_back_rgb",
	"sim/graphics/colors/caption_text_rgb",
	"sim/graphics/colors/list_text_rgb",
	"sim/graphics/colors/glass_text_rgb"
};

// Those datarefs are only XP7; if we can't find one,
// fall back to this table of X-Plane 6 colors.
static const float	kBackupColors[xpColor_Count][3] =
{
	 { (const float)(33.0/256.0), (const float)(41.0/256.0), (const float)(44.0/256.0) },
	 { (const float)(53.0/256.0), (const float)(64.0/256.0), (const float)(68.0/256.0) },
	 { (const float)(65.0/256.0), (const float)(83.0/256.0), (const float)(89.0/256.0) },
	 { (const float)(65.0/256.0), (const float)(83.0/256.0), (const float)(89.0/256.0) },
	 { (const float)0.8, (const float)0.8, (const float)0.8 },
	 { (const float)0.4, (const float)0.4, (const float)0.4 }
};	 

// This array contains the resolved datarefs
static XPLMDataRef	gColorRefs[xpColor_Count];

// Current alpha levels to blit at.
static float		gAlphaLevel = 1.0;

// This routine sets up a color from the above table.  Pass
// in a float[3] to get the color; pass in NULL to have the
// OpenGL color be set immediately.
static void	SetupAmbientColor(int inColorID, float * outColor)
{
	// If we're running the first time, resolve all of our datarefs just once.
	static	bool	firstTime = true;
	if (firstTime)
	{
		firstTime = false;
		for (int n = 0; n <xpColor_Count; ++n)
		{
			gColorRefs[n] = XPLMFindDataRef(kXPlaneColorNames[n]);
		}
	}
	
	// If being asked to set the color immediately, allocate some storage.
	float	theColor[4];
	float * target = outColor ? outColor : theColor;
	
	// If we have a dataref, just fetch the color from the ref.
	if (gColorRefs[inColorID])
		XPLMGetDatavf(gColorRefs[inColorID], target, 0, 3);
	else {
	
		// If we didn't have a dataref, fetch the ambient cabin lighting,
		// since XP6 dims the UI with night.
		static	XPLMDataRef	ambient_r = XPLMFindDataRef("sim/graphics/misc/cockpit_light_level_r");
		static	XPLMDataRef	ambient_g = XPLMFindDataRef("sim/graphics/misc/cockpit_light_level_g");
		static	XPLMDataRef	ambient_b = XPLMFindDataRef("sim/graphics/misc/cockpit_light_level_b");
	
		// Use a backup color but dim it.
		target[0] = kBackupColors[inColorID][0] * XPLMGetDataf(ambient_r);
		target[1] = kBackupColors[inColorID][1] * XPLMGetDataf(ambient_g);
		target[2] = kBackupColors[inColorID][2] * XPLMGetDataf(ambient_b);
	}
	
	// If the user passed NULL, set the color now using the alpha level.
	if (!outColor)
	{
		theColor[3] = gAlphaLevel;
		glColor4fv(theColor);
	}
}

// Just remember alpha levels for later.
static void	SetAlphaLevels(float inAlphaLevel)
{
	gAlphaLevel = inAlphaLevel;
}

#ifndef _WINDOWS
#pragma mark -
#endif

/************************************************************************
 *  LISTBOX DATA IMPLEMENTATION
 ************************************************************************/

// This structure represents a listbox internally...it consists of arrays
// per item and some general stuff.
struct	XPListBoxData_t {
	// Per item:
	std::vector<std::string>	Items;		// The name of the item
	std::vector<int>			Lefts;		// The rectangle of the item, relative to the top left corner of the listbox/
	std::vector<int>			Rights;
};

static XPListBoxData_t *pListBoxData;

// This routine finds the item that is in a given point, or returns -1 if there is none.
// It simply trolls through all the items.
static int XPListBoxGetItemNumber(XPListBoxData_t * pListBoxData, int inX, int inY)
{
	for (unsigned int n = 0; n < pListBoxData->Items.size(); ++n)
	{
		if ((inX >= pListBoxData->Lefts[n]) && (inX < pListBoxData->Rights[n]) &&
			(inY >= (n * LISTBOX_ITEM_HEIGHT)) && (inY < ((n * LISTBOX_ITEM_HEIGHT) + LISTBOX_ITEM_HEIGHT)))
		{
			return n;
		}
	}
	return -1;	
}

static void XPListBoxFillWithData(XPListBoxData_t *pListBoxData, const char *inItems, int Width)
{
	std::string	Items(inItems);
	while (!Items.empty())
	{
		std::string::size_type split = Items.find(';');
		if (split == Items.npos)
		{
			split = Items.size();
		}

		std::string	Item = Items.substr(0, split);

		pListBoxData->Items.push_back(Item);
		pListBoxData->Lefts.push_back(0);
		pListBoxData->Rights.push_back(Width);

		if (Item.size() == Items.size())
			break;
		else
			Items = Items.substr(split+1);
	}
}

static void XPListBoxAddItem(XPListBoxData_t *pListBoxData, char *pBuffer, int Width)
{
	std::string	Item(pBuffer);

	pListBoxData->Items.push_back(Item);
	pListBoxData->Lefts.push_back(0);
	pListBoxData->Rights.push_back(Width);
}

static void XPListBoxClear(XPListBoxData_t *pListBoxData)
{
	pListBoxData->Items.clear();
	pListBoxData->Lefts.clear();
	pListBoxData->Rights.clear();
}

static void XPListBoxInsertItem(XPListBoxData_t *pListBoxData, char *pBuffer, int Width, int CurrentItem)
{
	std::string	Item(pBuffer);

	pListBoxData->Items.insert(pListBoxData->Items.begin() + CurrentItem, Item);
	pListBoxData->Lefts.insert(pListBoxData->Lefts.begin() + CurrentItem, 0);
	pListBoxData->Rights.insert(pListBoxData->Rights.begin() + CurrentItem, Width);
}

static void XPListBoxDeleteItem(XPListBoxData_t *pListBoxData, int CurrentItem)
{
	pListBoxData->Items.erase(pListBoxData->Items.begin() + CurrentItem);
	pListBoxData->Lefts.erase(pListBoxData->Lefts.begin() + CurrentItem);
	pListBoxData->Rights.erase(pListBoxData->Rights.begin() + CurrentItem);
}

#ifndef _WINDOWS
#pragma mark -
#endif

// This widget Proc implements the actual listbox.

static int		XPListBoxProc(
					XPWidgetMessage			inMessage,
					XPWidgetID				inWidget,
					intptr_t				inParam1,
					intptr_t				inParam2)
{
	static int ScrollBarSlop;

	// Select if we're in the background.
	if (XPUSelectIfNeeded(inMessage, inWidget, inParam1, inParam2, 1/*eat*/))	return 1;
	
	int Left, Top, Right, Bottom, x, y, ListBoxDataOffset, ListBoxIndex;
	char Buffer[4096];

	int IsVertical, DownBtnSize, DownPageSize, ThumbSize, UpPageSize, UpBtnSize;
	bool UpBtnSelected, DownBtnSelected, ThumbSelected, UpPageSelected, DownPageSelected;
	
	XPGetWidgetGeometry(inWidget, &Left, &Top, &Right, &Bottom);
	
	int	SliderPosition = XPGetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarSliderPosition, NULL);
	int	Min = XPGetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMin, NULL);
	int	Max = XPGetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMax, NULL);
	int	ScrollBarPageAmount = XPGetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarPageAmount, NULL);
	int	CurrentItem = XPGetWidgetProperty(inWidget, xpProperty_ListBoxCurrentItem, NULL);
	int	MaxListBoxItems = XPGetWidgetProperty(inWidget, xpProperty_ListBoxMaxListBoxItems, NULL);
	int	Highlighted = XPGetWidgetProperty(inWidget, xpProperty_ListBoxHighlighted, NULL);
	XPListBoxData_t	*pListBoxData = (XPListBoxData_t*) XPGetWidgetProperty(inWidget, xpProperty_ListBoxData, NULL);

	switch(inMessage) 
	{
		case xpMsg_Create:
			// Allocate mem for the structure.
			pListBoxData = new XPListBoxData_t;
			XPGetWidgetDescriptor(inWidget, Buffer, sizeof(Buffer));
			XPListBoxFillWithData(pListBoxData, Buffer, (Right - Left - 20));
			XPSetWidgetProperty(inWidget, xpProperty_ListBoxData, (intptr_t)pListBoxData);
			XPSetWidgetProperty(inWidget, xpProperty_ListBoxCurrentItem, 0);
			Min = 0;
			Max = pListBoxData->Items.size();
			ScrollBarSlop = 0;
			Highlighted = false;
			SliderPosition = Max;
			MaxListBoxItems = (Top - Bottom) / LISTBOX_ITEM_HEIGHT;
			ScrollBarPageAmount = MaxListBoxItems;
			XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarSliderPosition, SliderPosition);
			XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMin, Min);
			XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMax, Max);
			XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarPageAmount, ScrollBarPageAmount);
			XPSetWidgetProperty(inWidget, xpProperty_ListBoxMaxListBoxItems, MaxListBoxItems);
			XPSetWidgetProperty(inWidget, xpProperty_ListBoxHighlighted, Highlighted);
			return 1;

		case xpMsg_DescriptorChanged:
			return 1;

		case xpMsg_PropertyChanged:
			if (XPGetWidgetProperty(inWidget, xpProperty_ListBoxAddItem, NULL))
			{
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxAddItem, 0);
				XPGetWidgetDescriptor(inWidget, Buffer, sizeof(Buffer));
				XPListBoxAddItem(pListBoxData, Buffer, (Right - Left - 20));
				Max = pListBoxData->Items.size();
				SliderPosition = Max;
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarSliderPosition, SliderPosition);
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMax, Max);
			}

			if (XPGetWidgetProperty(inWidget, xpProperty_ListBoxAddItemsWithClear, NULL))
			{
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxAddItemsWithClear, 0);
				XPGetWidgetDescriptor(inWidget, Buffer, sizeof(Buffer));
				XPListBoxClear(pListBoxData);
				XPListBoxFillWithData(pListBoxData, Buffer, (Right - Left - 20));
				Max = pListBoxData->Items.size();
				SliderPosition = Max;
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarSliderPosition, SliderPosition);
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMax, Max);
			}

			if (XPGetWidgetProperty(inWidget, xpProperty_ListBoxClear, NULL))
			{
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxClear, 0);
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxCurrentItem, 0);
				XPListBoxClear(pListBoxData);
				Max = pListBoxData->Items.size();
				SliderPosition = Max;
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarSliderPosition, SliderPosition);
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMax, Max);
			}

			if (XPGetWidgetProperty(inWidget, xpProperty_ListBoxInsertItem, NULL))
			{
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxInsertItem, 0);
				XPGetWidgetDescriptor(inWidget, Buffer, sizeof(Buffer));
				XPListBoxInsertItem(pListBoxData, Buffer, (Right - Left - 20), CurrentItem);
			}

			if (XPGetWidgetProperty(inWidget, xpProperty_ListBoxDeleteItem, NULL))
			{
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxDeleteItem, 0);
				if ((pListBoxData->Items.size() > 0) && (pListBoxData->Items.size() > CurrentItem))
					XPListBoxDeleteItem(pListBoxData, CurrentItem);
			}
			return 1;

		case xpMsg_Draw:
		{
			int	x, y;
			XPLMGetMouseLocation(&x, &y);
			
			XPDrawWindow(Left, Bottom, Right-20, Top, xpWindow_ListView);
			XPDrawTrack(Right-20, Bottom, Right, Top, Min, Max, SliderPosition, xpTrack_ScrollBar, Highlighted);

			XPLMSetGraphicsState(0, 1, 0,  0, 1,  0, 0);
			XPLMBindTexture2d(XPLMGetTexture(xplm_Tex_GeneralInterface), 0);
			glColor4f(1.0, 1.0, 1.0, 1.0);
				
			unsigned int ItemNumber;
			XPLMSetGraphicsState(0, 0, 0,  0, 0,  0, 0);

			// Now draw each item.
			ListBoxIndex = Max - SliderPosition;
			ItemNumber = 0;
			while (ItemNumber < MaxListBoxItems)
			{
				if (ListBoxIndex < pListBoxData->Items.size())
				{
					// Calculate the item rect in global coordinates.
					int ItemTop    = Top - (ItemNumber * LISTBOX_ITEM_HEIGHT);
					int ItemBottom = Top - ((ItemNumber * LISTBOX_ITEM_HEIGHT) + LISTBOX_ITEM_HEIGHT);

					// If we are hilited, draw the hilite bkgnd.
					if (CurrentItem == ListBoxIndex)
					{
						SetAlphaLevels(0.25);
						XPLMSetGraphicsState(0, 0, 0,  0, 1, 0, 0);
						SetupAmbientColor(xpColor_MenuHilite, NULL);
						SetAlphaLevels(1.0);
						glBegin(GL_QUADS);
						glVertex2i(Left, ItemTop);
						glVertex2i(Right-20, ItemTop);
						glVertex2i(Right-20, ItemBottom);
						glVertex2i(Left, ItemBottom);
						glEnd();						
					}
					
					float	text[3];
					SetupAmbientColor(xpColor_ListText, text);
					
					char	Buffer[512];
					int		FontWidth, FontHeight;
					int		ListBoxWidth = (Right - 20) - Left;
					strcpy(Buffer, pListBoxData->Items[ListBoxIndex++].c_str());
					XPLMGetFontDimensions(xplmFont_Basic, &FontWidth, &FontHeight, NULL);
					int		MaxChars = ListBoxWidth / FontWidth;
					Buffer[MaxChars] = 0;

					XPLMDrawString(text,
								Left, ItemBottom + 2,
								const_cast<char *>(Buffer), NULL, xplmFont_Basic);
				}
				ItemNumber++;
			}
		}
			return 1;

		case xpMsg_MouseUp:
			if (IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20, Top, Right, Bottom))
			{
				Highlighted = false;
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxHighlighted, Highlighted);
			}

			if (IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Left, Top, Right-20, Bottom))
			{
				if (pListBoxData->Items.size() > 0)
				{
					if (CurrentItem != -1)
						XPSetWidgetDescriptor(inWidget, pListBoxData->Items[CurrentItem].c_str());
					else
						XPSetWidgetDescriptor(inWidget, "");
					XPSendMessageToWidget(inWidget, xpMessage_ListBoxItemSelected, xpMode_UpChain, (intptr_t) inWidget, (intptr_t) CurrentItem);
				}
			}
			return 1;

		case xpMsg_MouseDown:
			if (IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Left, Top, Right-20, Bottom))
			{
				if (pListBoxData->Items.size() > 0)
				{
					XPLMGetMouseLocation(&x, &y);
					ListBoxDataOffset = XPListBoxGetItemNumber(pListBoxData, x - Left, Top - y);	
					if (ListBoxDataOffset != -1)
					{
						ListBoxDataOffset += (Max - SliderPosition);
						if (ListBoxDataOffset < pListBoxData->Items.size())
							XPSetWidgetProperty(inWidget, xpProperty_ListBoxCurrentItem, ListBoxDataOffset);
					}
				}
			}

			if (IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20, Top, Right, Bottom))
			{
				XPGetTrackMetrics(Right-20, Bottom, Right, Top, Min, Max, SliderPosition, xpTrack_ScrollBar, &IsVertical, &DownBtnSize, &DownPageSize, &ThumbSize, &UpPageSize, &UpBtnSize);
				int	Min = XPGetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMin, NULL);
				int	Max = XPGetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMax, NULL);
				if (IsVertical)
				{
					UpBtnSelected = IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20, Top, Right, Top - UpBtnSize);
					DownBtnSelected = IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20, Bottom + DownBtnSize, Right, Bottom);
					UpPageSelected = IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20, (Top - UpBtnSize), Right, (Bottom + DownBtnSize + DownPageSize + ThumbSize));
					DownPageSelected = IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20, (Top - UpBtnSize - UpPageSize - ThumbSize), Right, (Bottom + DownBtnSize));
					ThumbSelected = IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20, (Top - UpBtnSize - UpPageSize), Right, (Bottom + DownBtnSize + DownPageSize));
				}
				else
				{
					DownBtnSelected = IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20, Top, Right-20 + UpBtnSize, Bottom);
					UpBtnSelected = IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20 - DownBtnSize, Top, Right, Bottom);
					DownPageSelected = IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20 + DownBtnSize, Top, Right - UpBtnSize - UpPageSize - ThumbSize, Bottom);
					UpPageSelected = IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20 + DownBtnSize + DownPageSize + ThumbSize, Top, Right - UpBtnSize, Bottom);
					ThumbSelected = IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20 + DownBtnSize + DownPageSize, Top, Right - UpBtnSize - UpPageSize, Bottom);
				}

				if (UpPageSelected)
				{
					SliderPosition+=ScrollBarPageAmount;
					if (SliderPosition > Max)
						SliderPosition = Max;
					XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarSliderPosition, SliderPosition);
				}
				else if (DownPageSelected)
				{
					SliderPosition-=ScrollBarPageAmount;
					if (SliderPosition < Min)
						SliderPosition = Min;
					XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarSliderPosition, SliderPosition);
				}
				else if (UpBtnSelected)
				{
					SliderPosition++;
					if (SliderPosition > Max)
						SliderPosition = Max;
					XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarSliderPosition, SliderPosition);
				}
				else if (DownBtnSelected)
				{
					SliderPosition--;
					if (SliderPosition < Min)
						SliderPosition = Min;
					XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarSliderPosition, SliderPosition);
				}
				else if (ThumbSelected)
				{
					if (IsVertical)
						ScrollBarSlop = Bottom + DownBtnSize + DownPageSize + (ThumbSize/2) - MOUSE_Y(inParam1);
					else
						ScrollBarSlop = Right-20 + DownBtnSize + DownPageSize + (ThumbSize/2) - MOUSE_X(inParam1);
					Highlighted = true;
					XPSetWidgetProperty(inWidget, xpProperty_ListBoxHighlighted, Highlighted);

				}
				else
				{
					Highlighted = false;
					XPSetWidgetProperty(inWidget, xpProperty_ListBoxHighlighted, Highlighted);
				}
			}		
		return 1;

	case xpMsg_MouseDrag:
		if (IN_RECT(MOUSE_X(inParam1), MOUSE_Y(inParam1), Right-20, Top, Right, Bottom))
		{
			XPGetTrackMetrics(Right-20, Bottom, Right, Top, Min, Max, SliderPosition, xpTrack_ScrollBar, &IsVertical, &DownBtnSize, &DownPageSize, &ThumbSize, &UpPageSize, &UpBtnSize);
			int	Min = XPGetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMin, NULL);
			int	Max = XPGetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarMax, NULL);

			ThumbSelected = Highlighted;

			if (ThumbSelected)
			{
				if (inParam1 != 0)
				{				
					if (IsVertical)
					{
						y = MOUSE_Y(inParam1) + ScrollBarSlop;
						SliderPosition = round((float)((float)(y - (Bottom + DownBtnSize + ThumbSize/2)) / 
									(float)((Top - UpBtnSize - ThumbSize/2) - (Bottom + DownBtnSize + ThumbSize/2))) * Max);
					}
					else
					{
						x = MOUSE_X(inParam1) + ScrollBarSlop;
						SliderPosition = round((float)((float)(x - (Right-20 + DownBtnSize + ThumbSize/2)) / (float)((Right - UpBtnSize - ThumbSize/2) - (Right-20 + DownBtnSize + ThumbSize/2))) * Max);
					}

				}
				else
					SliderPosition = 0;

				if (SliderPosition < Min)
					SliderPosition = Min;
				if (SliderPosition > Max)
					SliderPosition = Max;
					
				XPSetWidgetProperty(inWidget, xpProperty_ListBoxScrollBarSliderPosition, SliderPosition);
			}
		}
		return 1;

		default:
			return 0;
	}	
}				

// To create a listbox, make a new widget with our listbox proc as the widget proc.
static XPWidgetID           XPCreateListBox(
                                   int                  inLeft,    
                                   int                  inTop,    
                                   int                  inRight,    
                                   int                  inBottom,    
                                   int                  inVisible,    
                                   const char *         inDescriptor,    
                                   XPWidgetID           inContainer)
{
	return XPCreateCustomWidget(
                                   inLeft,    
                                   inTop,    
                                   inRight,    
                                   inBottom,    
                                   inVisible,    
                                   inDescriptor,    
                                   0,
                                   inContainer,    
                                   XPListBoxProc);
}                                   


/*------------------------------------------------------------------------*/

/*
 * XPPopups.cpp
 *
 * Copyright 2005 Sandy Barbour and Ben Supnik
 * 
 * All rights reserved.  See license.txt for usage.
 * 
 * X-Plane SDK Version: 1.0.2                                                  
 *
 */

 /************************************************************************
 *  X-PLANE UI INFRASTRUCTURE CODE
 ************************************************************************
 *
 * This code helps provde an x-plane compatible look.  It is copied from 
 * the source code from the widgets DLL; someday popups will be part of
 * this, so our popups are written off of the same APIs.
 *
 */

enum {
	// These are for caching, do not use!!
	xpProperty_OffsetToCurrentItem			= 1898,
	xpProperty_CurrentItemLen				= 1899
};

#ifndef _WINDOWS
#pragma mark -
#endif

/************************************************************************
 *  POPUP MENU IMPLEMENTATION
 ************************************************************************/


// This structure represents a popup menu internally...it consists of arrays
// per item and some general stuff.  It is implemented as a giant window; that
// window has a ptr to this struct as its refcon.  While the whole window isn't
// drawn in, we make the window full screen so if the user clicks outside the
// popup, we capture the click and dismiss the popup.
struct	XPPopupMenu_t {

	XPLMWindowID				window;		// The window that implements us.

	// Per item:
	std::vector<std::string>	items;		// The name of the item
	std::vector<bool>			enabled;	// Is it enabled?
	std::vector<int>			lefts;		// The rectangle of the item, relative to the top left corner of the menu/
	std::vector<int>			rights;
	std::vector<int>			bottoms;
	std::vector<int>			tops;
	std::vector<int>			vstripes;	// An array of vertical stripes (relative to the left of the menu
											// for really big menus.
	int							left;		// The overall bounds of the menu in global screen coordinates.
	int							top;
	int							right;
	int							bottom;

	XPPopupPick_f				function;	// Info for our callback function.
	void *						ref;
	
};

// This routine finds the item that is in a given point, or returns -1 if there is none.
// It simply trolls through all the items.
static int XPItemForHeight(XPPopupMenu_t * pmenu, int inX, int inY)
{
	inX -= pmenu->left;
	inY -= pmenu->top;
	for (unsigned int n = 0; n < pmenu->items.size(); ++n)
	{
		if ((inX >= pmenu->lefts[n]) && (inX < pmenu->rights[n]) &&
			(inY >= pmenu->bottoms[n]) && (inY < pmenu->tops[n]))
		{
			return n;
		}
	}
	return -1;	
}

// This is the drawing hook for a popup menu.
static void XPPopupDrawWindowCB(
                                   XPLMWindowID         inWindowID,    
                                   void *               inRefcon)
{
	XPPopupMenu_t * pmenu = (XPPopupMenu_t *) inRefcon;
	int	x, y;
	XPLMGetMouseLocation(&x, &y);

	// This is the index number of the currently selected item, based
	// on where the mouse is.
	int menu_offset = XPItemForHeight(pmenu, x, y);	

	int	item_top = pmenu->top;
	unsigned int n;
	XPLMSetGraphicsState(0, 0, 0,  0, 0,  0, 0);
	
	// Draw any vertical stripes that must be drawn for multi-column menus.
	for (n = 0; n < pmenu->vstripes.size(); ++n)
	{
		SetupAmbientColor(xpColor_MenuDarkTinge, NULL);
		glBegin(GL_LINES);
		glVertex2i(pmenu->left + pmenu->vstripes[n] - 1, pmenu->top);
		glVertex2i(pmenu->left + pmenu->vstripes[n] - 1, pmenu->bottom);
		glEnd();
		SetupAmbientColor(xpColor_MenuLiteTinge, NULL);
		glBegin(GL_LINES);
		glVertex2i(pmenu->left + pmenu->vstripes[n], pmenu->top);
		glVertex2i(pmenu->left + pmenu->vstripes[n], pmenu->bottom);
		glEnd();	
	}
	
	// Now draw each item.
	for (n = 0; n < pmenu->items.size(); ++n)
	{
		// Calcualte the item rect in global coordinates.
		int item_bottom = pmenu->bottoms[n] + pmenu->top;
		int item_top    = pmenu->tops[n] + pmenu->top;
		int item_left   = pmenu->lefts[n] + pmenu->left;
		int item_right  = pmenu->rights[n] + pmenu->left;
		
		XPDrawElement(	item_left,
						item_bottom,
						item_right,
						item_top, 
						(menu_offset == n && pmenu->enabled[n])? xpElement_PushButtonLit : xpElement_PushButton, 0);

		if (!pmenu->enabled[n] && pmenu->items[n] == "-")
		{
			// Draw two lines for dividers.
			XPLMSetGraphicsState(0, 0, 0,  0, 0,  0, 0);
			SetupAmbientColor(xpColor_MenuLiteTinge, NULL);
			glBegin(GL_LINE_STRIP);
			glVertex2i(item_left, item_top - 1);
			glVertex2i(item_right, item_top - 1);
			glEnd();
			SetupAmbientColor(xpColor_MenuDarkTinge, NULL);
			glBegin(GL_LINE_STRIP);
			glVertex2i(item_left, item_top);
			glVertex2i(item_right, item_top);
			glEnd();
		} else {
			// If we are hilited, draw the hilite bkgnd.
			if (menu_offset == n && pmenu->enabled[n])
			{
				SetAlphaLevels(0.25);
				XPLMSetGraphicsState(0, 0, 0,  0, 1, 0, 0);
				SetupAmbientColor(xpColor_MenuHilite, NULL);
				SetAlphaLevels(1.0);
				glBegin(GL_QUADS);
				glVertex2i(item_left, item_top);
				glVertex2i(item_right, item_top);
				glVertex2i(item_right, item_bottom);
				glVertex2i(item_left, item_bottom);
				glEnd();						
			}
			
			// Draw the text for the menu item, taking into account
			// disabling as a color.
			float	text[3];
			SetupAmbientColor(pmenu->enabled[n] ? xpColor_MenuText : xpColor_MenuTextDisabled, text);
			
			int XPlaneVersion, XPLMVersion, HostID;
			XPLMGetVersions(&XPlaneVersion, &XPLMVersion, &HostID);
			int yOffset = 2;

			if (XPlaneVersion < 9400)
				yOffset = 2;
			else
				yOffset = 5;

			XPLMDrawString(text,
						item_left + 18, item_bottom + yOffset,
						const_cast<char *>(pmenu->items[n].c_str()), NULL, xplmFont_Menus);
		}
	}
}                                   

static void XPPopupHandleKeyCB(
                                   XPLMWindowID         inWindowID,    
                                   char                 inKey,    
                                   XPLMKeyFlags         inFlags,    
                                   char                 inVirtualKey,    
                                   void *               inRefcon,    
                                   int                  losingFocus)
{
	// Nothing to do when a key is pressed; popup menus don't use keys.
}                                     

// This is the mouse click handler.
static int XPPopupHandleMouseClickCB(
                                   XPLMWindowID         inWindowID,    
                                   int                  x,    
                                   int                  y,    
                                   XPLMMouseStatus      inMouse,    
                                   void *               inRefcon)
{
	// Normally we do nothing.  But when we get an up click we dismiss.
	if (inMouse == xplm_MouseUp)
	{
		XPPopupMenu_t * pmenu = (XPPopupMenu_t *) inRefcon;

		int menu_offset = XPItemForHeight(pmenu, x, y);	
		
		// If we got an item click and it is not enabled,
		// pretend nothing was picked.
		if (menu_offset >= 0 && !pmenu->enabled[menu_offset])
			menu_offset = -1;
			
		// Call the callback
		if (pmenu->function)
			pmenu->function(menu_offset, pmenu->ref);

		// cleanup
		delete pmenu;
		XPLMDestroyWindow(inWindowID);
	}
	return 1;
}

// This routine just has to build the popup and then the window takes care of itself.
static void		XPPickPopup(
						int				inMouseX,
						int				inMouseY,
						const char *	inItems,
						int				inCurrentItem,
						XPPopupPick_f	inCallback,
						void *			inRefcon)
{
	int	screenWidth, screenHeight;
	int	fontWidth, fontHeight;
	screenWidth = 1024;
	screenHeight = 768;
	XPLMGetFontDimensions(xplmFont_Menus, &fontWidth, &fontHeight, NULL);
	
	// Allocate mem for the structure and build a new window as big as teh screen.
	XPPopupMenu_t *	info = new XPPopupMenu_t;
	
	XPLMWindowID		windID = XPLMCreateWindow(0, screenHeight, screenWidth, 0, 1, 
		XPPopupDrawWindowCB, XPPopupHandleKeyCB, XPPopupHandleMouseClickCB, info);
	
	if (inCurrentItem < 0) inCurrentItem = 0;
	
	/************ PARSE THE MENU STRING INTO MENU ITEMS **********/
	
	// Parse the itemes into arrays.  Remember how tall they are so
	// we can calculate the geometry.
	std::vector<int>	heights;
	std::string	items(inItems);
	while (!items.empty())
	{
		std::string::size_type split = items.find(';');
		if (split == items.npos)
		{
			split = items.size();
		}

		std::string	item = items.substr(0, split);

		if (item == "-")
		{
			info->items.push_back("-");
			info->enabled.push_back(false);
			heights.push_back(2);
		} else {
			if (!item.empty() && item[0] == '(')
			{
				info->enabled.push_back(false);
				info->items.push_back(item.substr(1));
				heights.push_back(12);
			} else {
				info->enabled.push_back(true);
				info->items.push_back(item);
				heights.push_back(12);
			}
		}
		
		if (item.size() == items.size())
			break;
		else
			items = items.substr(split+1);
	}
	
	/************ PLACE THE ITEMS IN COLUMNS **********/

	unsigned int menuWidth = 0;
	int	leftOff = 0, topOff = 0;
	
	// Calculate the widest menu item anywhere.
	for (std::vector<std::string>::iterator iter = info->items.begin(); iter != info->items.end(); ++iter)
		if (iter->size() > menuWidth)
			menuWidth = iter->size();
	menuWidth *= fontWidth;
	menuWidth += 35;
	
	int cols = 1;
	int itemsInCol = 0;
	int	maxLen = 0;
	int	ourItemLeft = 0, ourItemTop = 0;
	
	// Stack up each item, building new columns as necessary.
	for( unsigned int i = 0; i < heights.size(); ++i )
	{
		itemsInCol++;
		info->lefts.push_back(leftOff);
		info->rights.push_back(leftOff + menuWidth);
		info->tops.push_back(topOff);
		info->bottoms.push_back(topOff - heights[i]);
		if (i == inCurrentItem) 
		{
			ourItemLeft = leftOff;
			ourItemTop = topOff;
		}
		topOff -= heights[i];
		if (maxLen > topOff)
			maxLen = topOff;
		
		if (topOff < -(screenHeight - 50))
		{
			itemsInCol = 0;
			cols++;
			topOff = 0;
			leftOff += menuWidth;
			info->vstripes.push_back(leftOff);
		}
	}
	
	// If we built a new column but had no items for it, throw it out.
	if (itemsInCol == 0)
	{
		cols--;
		info->vstripes.pop_back();
	}
	
	// Now place the window.  Make sure to not let it go off screen.
	info->window = windID;
	info->left = inMouseX - ourItemLeft;
	info->top = inMouseY - ourItemTop;
	info->right = inMouseX + (menuWidth * cols) - ourItemLeft;
	info->bottom = inMouseY + maxLen - ourItemTop;
	
	if (info->right > (screenWidth-10))
	{
		int deltaLeft = (info->right - (screenWidth-10));
		info->right -= deltaLeft;
		info->left -= deltaLeft;
	}
	if (info->left < 10)
	{
		int deltaRight = 10 - info->left;
		info->right += deltaRight;
		info->left += deltaRight;
	}
	if (info->bottom < 10)
	{
		int deltaUp = 10 - info->bottom;
		info->bottom += deltaUp;
		info->top += deltaUp;
	}
	if (info->top > (screenHeight-30))
	{
		int deltaDown = (info->top - (screenHeight-30));
		info->top -= deltaDown;
		info->bottom -= deltaDown;
	}
	
	info->function = inCallback;
	info->ref = inRefcon;
}

#ifndef _WINDOWS
#pragma mark -
#endif

// This routine is called by our popup menu when it is picked; we 
// have stored our widget in our refcon, so we save the new choice
// and send a widget message.
static	void 	XPPopupWidgetProc(int inChoice, void * inRefcon)
{
	if (inChoice != -1)
	{
		XPSetWidgetProperty(inRefcon, xpProperty_PopupCurrentItem, inChoice);
		XPSendMessageToWidget(inRefcon, xpMessage_PopupNewItemPicked, xpMode_UpChain, (intptr_t) inRefcon, (intptr_t) inChoice);
	}
}

// This widget Proc implements the actual button.

static int		XPPopupButtonProc(
					XPWidgetMessage			inMessage,
					XPWidgetID				inWidget,
					intptr_t				inParam1,
					intptr_t				inParam2)
{
	// Select if we're in the background.
	if (XPUSelectIfNeeded(inMessage, inWidget, inParam1, inParam2, 1/*eat*/))	return 1;
	
	int fh, fv;
	int l, t, r, b;
	char	buf[4096];
	
	XPGetWidgetGeometry(inWidget, &l, &t, &r, &b);
	XPLMGetFontDimensions(xplmFont_Basic, &fh, &fv, NULL);
	
	int	curItem = XPGetWidgetProperty(inWidget, xpProperty_PopupCurrentItem, NULL);

	switch(inMessage) {
	case xpMsg_Create:
	case xpMsg_DescriptorChanged:
	case xpMsg_PropertyChanged:
		// If our data changes, reparse our descriptor to change our current item.
		if (inMessage != xpMsg_PropertyChanged || inParam1 == xpProperty_PopupCurrentItem)
		{
			XPGetWidgetDescriptor(inWidget, buf, sizeof(buf));
			char * p = buf;
			int picksToSkip = curItem;
			while (picksToSkip > 0)
			{
				while (*p && *p != ';') ++p;
				if (*p == 0) break;
				++p;
				--picksToSkip;
			}
			char * term = p;
			while (*term && *term != ';') ++term;
			// Store an offset and length of our descriptor that will show as our current text.
			XPSetWidgetProperty(inWidget, xpProperty_OffsetToCurrentItem, p - buf);
			XPSetWidgetProperty(inWidget, xpProperty_CurrentItemLen, term - p);			
			XPSetWidgetProperty(inWidget, xpProperty_Enabled, 1);
		}
		return 1;
	case xpMsg_Draw:
		{
			float		white [4];
			float		gray [4];
			
			int itemOffset = XPGetWidgetProperty(inWidget, xpProperty_OffsetToCurrentItem, NULL);
			int itemLen = XPGetWidgetProperty(inWidget, xpProperty_CurrentItemLen, NULL);

			// Drawing time.  Find the sim version once.
			static	int sim;
			static float firstTime = true;
			static	int	charWidth;
			if (firstTime)
			{
				firstTime = false;
				int	plugin;
				XPLMHostApplicationID id;
				XPLMGetVersions(&sim, &plugin, &id);
				
				XPLMGetFontDimensions(xplmFont_Basic, &charWidth, NULL, NULL);
			}
			
			// If we are version 7 of the sim, use Sergio's great new popup item.
			// Since there is no UI element code for this, we must draw it by hand!
			if (sim >= 700)
			{
				int center = (t + b) / 2;
				XPDrawElement(	l - 4, center - 13, r + 4, center + 13, 
								xpElement_PushButton, 0);
			} else 
				// If we are version 6, use a window drag bar as a fake popup button.
				XPDrawElement(l+2, b, r-2, t, xpElement_WindowDragBarSmooth, 0);

			// Now draw the button label.
			int	titleLen = XPGetWidgetDescriptor(inWidget, buf, sizeof(buf));

			SetupAmbientColor(xpColor_MenuText, white);
			SetupAmbientColor(xpColor_MenuTextDisabled, gray);
			
			if (charWidth)
			{
				int	maxCharCapacity = (r - l - 24) / charWidth;
				if (itemLen > maxCharCapacity)
					itemLen = maxCharCapacity;
			}
			
			buf[itemOffset + itemLen] = 0;
			if (buf[itemOffset] == '(')	++itemOffset;
			titleLen = strlen(buf + itemOffset);
			
			XPLMDrawString(XPGetWidgetProperty(inWidget, xpProperty_Enabled, 0) ? white : gray, l + 4,
						(t + b) / 2 - (fv / 2) + 2,
						buf + itemOffset, NULL, xplmFont_Basic);
					
		}
		return 1;
	case xpMsg_MouseDown:
		// If the mouse is clicked, do a popup pick.
		if (XPGetWidgetProperty(inWidget, xpProperty_Enabled, 0))
		{
			XPGetWidgetDescriptor(inWidget, buf, sizeof(buf));
			
			XPPickPopup(l, t, buf, XPGetWidgetProperty(inWidget, xpProperty_PopupCurrentItem, NULL),
								XPPopupWidgetProc, inWidget);
			return 1;
		}
	default:
		return 0;
	}	
}				

// To create a popup, make a new widget with our button proc as the widget proc.
static XPWidgetID           XPCreatePopup(
                                   int                  inLeft,    
                                   int                  inTop,    
                                   int                  inRight,    
                                   int                  inBottom,    
                                   int                  inVisible,    
                                   const char *         inDescriptor,    
                                   XPWidgetID           inContainer)
{
	return XPCreateCustomWidget(
                                   inLeft,    
                                   inTop,    
                                   inRight,    
                                   inBottom,    
                                   inVisible,    
                                   inDescriptor,    
                                   0,
                                   inContainer,    
                                   XPPopupButtonProc);
}                                   

/*------------------------------------------------------------------------*/
Leave a comment