No project download is available. This is a code snippet and not a complete project.

These filters are from XSquawkBox – they are tested and in a shipping plugin.

These filters take the form of a widget function and a helper function that attaches them – a simple way to specialize widget behavior.

Protecting from null characters

Older versions of the SDK sometimes sent null characters to text fields, crashing the sim. This small snippet protects a widget from this. This is unneeded for newer versions of X-Plane.

static	int	WidgetFunc_EatNullChars(
                                  XPWidgetMessage      inMessage,
                                  XPWidgetID           inWidget,
                                  long                 inParam1,
                                  long                 inParam2)
{
	if (inMessage == xpMsg_KeyPress)
	{
		char	theChar = KEY_CHAR(inParam1);

		if (theChar == 0)
			return 1;	// Eat the null char!
	}
	return 0;
}

void	ProtectTextWidget(XPWidgetID inWidgetID)
{
	XPAddWidgetCallback(inWidgetID, WidgetFunc_EatNullChars);
}

Cut Copy and Paste with the OS

This attaches control V, C, and X as cut/copy/patste. It works on Mac and Windows.

bool	XSBGetTextFromClipboard(std::string& outText)
{
#if IBM
		HGLOBAL   	hglb;
		LPTSTR    	lptstr;
		bool		retVal = false;
		static XPLMDataRef hwndDataRef = XPLMFindDataRef("sim/operation/windows/system_window");
		HWND hwndMain = (HWND) XPLMGetDatai(hwndDataRef);

	if (!IsClipboardFormatAvailable(CF_TEXT))
		return false;

	if (!OpenClipboard(hwndMain))
		return false;

	hglb = GetClipboardData(CF_TEXT);
	if (hglb != NULL)
	{
		lptstr = (LPSTR)GlobalLock(hglb);
		if (lptstr != NULL)
		{
			outText = lptstr;
			GlobalUnlock(hglb);
  			retVal = true;
		}
	}

	CloseClipboard();

	return retVal;
#endif
#if APL
		ScrapRef	scrap;
	if (::GetCurrentScrap(&scrap) != noErr)
		return false;

	SInt32		byteCount = 0;
	OSStatus	status = ::GetScrapFlavorSize(scrap, kScrapFlavorTypeText, &byteCount);
	if (status != noErr)
		return false;

	outText.resize(byteCount);

	return (::GetScrapFlavorData(scrap, kScrapFlavorTypeText, &byteCount, &*outText.begin() ) == noErr);
#endif
}

bool	XSBSetTextToClipboard(const std::string& inText)
{
#if IBM
		LPTSTR  lptstrCopy;
		HGLOBAL hglbCopy;
		static XPLMDataRef hwndDataRef = XPLMFindDataRef("sim/operation/windows/system_window");
		HWND hwndMain = (HWND) XPLMGetDatai(hwndDataRef);

	if (!OpenClipboard(hwndMain))
		return false;
	EmptyClipboard();

	hglbCopy = GlobalAlloc(GMEM_MOVEABLE, sizeof(TCHAR) * (inText.length() + 1));
	if (hglbCopy == NULL)
	{
		CloseClipboard();
		return false;
	}

	lptstrCopy = (LPSTR)GlobalLock(hglbCopy);
	strcpy(lptstrCopy, inText.c_str());
	GlobalUnlock(hglbCopy);

	SetClipboardData(CF_TEXT, hglbCopy);
	CloseClipboard();
	return true;
#endif
#if APL
	ScrapRef	scrap;
	if (::ClearCurrentScrap() != noErr) return false;
	if (::GetCurrentScrap(&scrap) != noErr) return false;

	return ::PutScrapFlavor( scrap, kScrapFlavorTypeText, kScrapFlavorMaskNone, inText.size(), &*inText.begin()) == noErr;

#endif
}

static int	WidgetFunc_CutCopyPaste(
                                  XPWidgetMessage      inMessage,
                                  XPWidgetID           inWidget,
                                  long                 inParam1,
                                  long                 inParam2)
{
	if (inMessage == xpMsg_KeyPress)
	{
		char			theChar = KEY_VKEY(inParam1);
		XPLMKeyFlags	flags = KEY_FLAGS(inParam1);

		if ((flags & (xplm_DownFlag + xplm_ControlFlag)) == (xplm_DownFlag + xplm_ControlFlag))
		{
			long	selStart = XPGetWidgetProperty(inWidget, xpProperty_EditFieldSelStart, NULL);
			long	selEnd = XPGetWidgetProperty(inWidget, xpProperty_EditFieldSelEnd, NULL);
			long	strLen = XPGetWidgetDescriptor(inWidget, NULL, 0);
			std::string	txt;
			txt.resize(strLen);
			XPGetWidgetDescriptor(inWidget, &*txt.begin(), txt.size()+1);
			if (theChar == XPLM_VK_V)
			{
				std::string	scrap;
				if (XSBGetTextFromClipboard(scrap) && !scrap.empty())
				{
					if ((selEnd > selStart) && (selStart >= 0) && (selEnd <= strLen))
					{
						txt.replace(selStart, selEnd - selStart, scrap);
						XPSetWidgetDescriptor(inWidget, txt.c_str());
						XPSetWidgetProperty(inWidget, xpProperty_EditFieldSelStart, selStart + scrap.size());
						XPSetWidgetProperty(inWidget, xpProperty_EditFieldSelEnd, selStart + scrap.size());
					} else if ((selStart >= 0) && (selStart <= strLen)) {
						txt.insert(selStart, scrap);
						XPSetWidgetDescriptor(inWidget, txt.c_str());
						XPSetWidgetProperty(inWidget, xpProperty_EditFieldSelStart, selStart + scrap.size());
						XPSetWidgetProperty(inWidget, xpProperty_EditFieldSelEnd, selStart + scrap.size());
					}
				}
				return 1;
			}
			if ((theChar == XPLM_VK_C) || (theChar == XPLM_VK_X))
			{
				if ((selStart >= 0) && (selStart < selEnd) && (selEnd <= strLen))
				{
					std::string	scrap = txt.substr(selStart, selEnd - selStart);
					if (XSBSetTextToClipboard(scrap) && (theChar == XPLM_VK_X))
					{
						txt.erase(selStart, selEnd - selStart);
						XPSetWidgetDescriptor(inWidget, txt.c_str());
						XPSetWidgetProperty(inWidget, xpProperty_EditFieldSelEnd, selStart);
					}
				}
				return 1;
			}
		}
	}
	return 0;
}

void	AttachCutCopyPaste(XPWidgetID inWidget)
{
	XPAddWidgetCallback(inWidget, WidgetFunc_CutCopyPaste);
 }

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.