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

Your email address will not be published. Required fields are marked *

Please do not report bugs in the blog comments.
Only bugs reported via the X-Plane Bug Reporter are tracked.