#define OEMRESOURCE

#include "main.h"
#include "./toolbarEditbox.h"
#include "./menu.h"
#include "./ifc_skinhelper.h"

#include "../Plugins/General/gen_ml/ml_ipc_0313.h"

#include <richedit.h>
#include <strsafe.h>

#define NTEF_USERFLAGSMASK	0x00FFFFFF
#define NTEF_UNICODE		0x01000000
#define NTEF_CLICKEDONCE	0x02000000
#define NTEF_SHOWCURSOR		0x04000000
#define NTEF_KEYDOWN		0x08000000
#define NTEF_MENULOOP		0x10000000
#define NTEF_SHOWCARET		0x20000000

typedef struct __TOOLBAREDITBOX
{
	WNDPROC originalProc;
	UINT	flags;
	ToolbarEditboxHost *host;
	DWORD	dblclkTime;
} TOOLBAREDITBOX;

static ATOM TOOLBAREDITBOX_PROP = 0;
static HHOOK mouseHook = NULL;

#define GetEditbox(__hwnd) ((TOOLBAREDITBOX*)GetProp((__hwnd), MAKEINTATOM(TOOLBAREDITBOX_PROP)))

static LRESULT CALLBACK ToolbarEditbox_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static INT CALLBACK ToolbarEditbox_WordBreakProc(LPWSTR pszText, INT iCurrent, INT cchLen, INT code);
static INT CALLBACK ToolbarEditbox_WordBreakProc2(LPWSTR pszText, INT iCurrent, INT cchLen, INT code);

static void CALLBACK ToolbarEditbox_Uninitialize()
{
	if (0 != TOOLBAREDITBOX_PROP)
	{
		GlobalDeleteAtom(TOOLBAREDITBOX_PROP);
		TOOLBAREDITBOX_PROP = 0;
	}
}


BOOL ToolbarEditbox_AttachWindow(HWND hEditbox, ToolbarEditboxHost *host)
{
	if (!IsWindow(hEditbox)) 
		return  FALSE;

	if (0 == TOOLBAREDITBOX_PROP)
	{
		 TOOLBAREDITBOX_PROP = GlobalAddAtom(L"NullsoftToolbarEditBox");
		 if (0 == TOOLBAREDITBOX_PROP) return  FALSE;
		 Plugin_RegisterUnloadCallback(ToolbarEditbox_Uninitialize);
	}
	
	TOOLBAREDITBOX *editbox = (TOOLBAREDITBOX*)GetProp(hEditbox, MAKEINTATOM(TOOLBAREDITBOX_PROP));
	if (NULL != editbox) return TRUE;
	
	editbox = (TOOLBAREDITBOX*)calloc(1, sizeof(TOOLBAREDITBOX));
	if (NULL == editbox) return FALSE;


	ifc_skinhelper *skinHelper = NULL;
	if (SUCCEEDED(Plugin_GetSkinHelper(&skinHelper)))
	{
	//	skinHelper->SkinControl(hEditbox, SKINNEDWND_TYPE_EDIT, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWES_BOTTOM);
		skinHelper->Release();
	}

	ZeroMemory(editbox, sizeof(TOOLBAREDITBOX));

	if (IsWindowUnicode(hEditbox))
		editbox->flags |= NTEF_UNICODE;

	editbox->host = host;

	editbox->originalProc = (WNDPROC)(LONG_PTR)((0 != (NTEF_UNICODE & editbox->flags)) ? 
						SetWindowLongPtrW(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)ToolbarEditbox_WindowProc) : 
						SetWindowLongPtrA(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)ToolbarEditbox_WindowProc));

	if (NULL == editbox->originalProc || !SetProp(hEditbox, MAKEINTATOM(TOOLBAREDITBOX_PROP), editbox))
	{
		if (NULL != editbox->originalProc)
		{
			if (0 != (NTEF_UNICODE & editbox->flags))
				SetWindowLongPtrW(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc);
			else
				SetWindowLongPtrA(hEditbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc);
		}
			
		free(editbox);
		return FALSE;
	}
	SendMessage(hEditbox, EM_SETWORDBREAKPROC, 0, (LPARAM)ToolbarEditbox_WordBreakProc);
	return TRUE;
}

static void ToolbarEditbox_PatchCursor()
{	
	ShowCursor(FALSE);
	SetCursor(LoadCursor(NULL, IDC_ARROW));
	ShowCursor(TRUE);

	CURSORINFO cursorInfo;
	cursorInfo.cbSize = sizeof(CURSORINFO);
	if (GetCursorInfo(&cursorInfo) && 
		0 != (CURSOR_SHOWING & cursorInfo.flags))
	{
		POINT pt;
		GetCursorPos(&pt);
		HWND hTarget= WindowFromPoint(pt);
		if (NULL != hTarget)
		{
			UINT hitTest = (UINT)SendMessage(hTarget, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y));
			UINT uMsg = (HTCLIENT == hitTest) ? WM_MOUSEMOVE : WM_NCMOUSEMOVE;
			SendMessage(hTarget, WM_SETCURSOR,  (WPARAM)hTarget, MAKELPARAM(hitTest, uMsg));
		}
	}
}


static void ToolbarEditbox_Detach(HWND hwnd)
{	
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	RemoveProp(hwnd, MAKEINTATOM(TOOLBAREDITBOX_PROP));

	ToolbarEditbox_PatchCursor();

	if (NULL != editbox && 0 != (NTEF_SHOWCURSOR & editbox->flags))
	{
		ShowCursor(TRUE);
		editbox->flags &= ~NTEF_SHOWCURSOR;
	}

	if (NULL != mouseHook)
	{
		UnhookWindowsHookEx(mouseHook);
		mouseHook = NULL;
	}

	if (NULL == editbox) 
		return;

	if (NULL != editbox->originalProc)
	{
		if (0 != (NTEF_UNICODE & editbox->flags))
			SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc);
		else
			SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)editbox->originalProc);
	}

	free(editbox);
}


static LRESULT ToolbarEditbox_CallOrigWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);

	if (NULL == editbox || NULL == editbox->originalProc)
	{
		return (0 != (NTEF_UNICODE & editbox->flags)) ? 
				DefWindowProcW(hwnd, uMsg, wParam, lParam) : 
				DefWindowProcA(hwnd, uMsg, wParam, lParam);
	}

	return (0 != (NTEF_UNICODE & editbox->flags)) ? 
			CallWindowProcW(editbox->originalProc, hwnd, uMsg, wParam, lParam) : 
			CallWindowProcA(editbox->originalProc, hwnd, uMsg, wParam, lParam);
}

static void ToolbarEditbox_ResetText(HWND hwnd)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox && NULL != editbox->host)
		editbox->host->EditboxResetText(hwnd);
}

static void ToolbarEditbox_NavigateNextCtrl(HWND hwnd, BOOL fForward)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox && NULL != editbox->host)
		editbox->host->EditboxNavigateNextCtrl(hwnd, fForward);
}

static void ToolbarEditbox_AcceptText(HWND hwnd)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox && NULL != editbox->host)
		editbox->host->EditboxAcceptText(hwnd);
}

static BOOL ToolbarEditbox_IsDelimiterChar(WCHAR testChar)
{
	WORD info;
	if (FALSE == GetStringTypeEx(LOCALE_USER_DEFAULT, CT_CTYPE1, &testChar, 1, &info))
		return 0;

	return (0 != ((C1_SPACE | C1_PUNCT | C1_CNTRL | C1_BLANK) & info));
}

static INT ToolbarEditbox_FindLeft(LPCWSTR pszText, INT iCurrent, INT cchLen)
{
	if (iCurrent <= 0) 
		return 0;

	LPCWSTR pszCursor = &pszText[iCurrent];
	BOOL charDelim = ToolbarEditbox_IsDelimiterChar(*pszCursor);
	
	for(;;)
	{
		pszCursor = CharPrev(pszText, pszCursor);
		if (charDelim != ToolbarEditbox_IsDelimiterChar(*pszCursor))
			return (INT)(INT_PTR)(CharNext(pszCursor) - pszText);
		
		if (pszCursor == pszText)
			break;
	}
	return 0;
}

static INT ToolbarEditbox_FindRight(LPCWSTR pszText, INT iCurrent, INT cchLen)
{
	if (iCurrent >= cchLen)
		return cchLen;

	LPCWSTR pszEnd = &pszText[cchLen];
	LPCWSTR pszCursor = &pszText[iCurrent];

	if (iCurrent > 0)
		pszCursor = CharNext(pszCursor);
	
	BOOL charDelim = ToolbarEditbox_IsDelimiterChar(*pszCursor);

	for(;;)
	{
		pszCursor = CharNext(pszCursor);
		if (pszCursor >= pszEnd) 
			break;

		if (charDelim != ToolbarEditbox_IsDelimiterChar(*pszCursor))
			return (INT)(INT_PTR)(pszCursor - pszText);
	}
	return cchLen;
}

static INT ToolbarEditbox_FindWordLeft(LPCWSTR pszText, INT iCurrent, INT cchLen, BOOL fRightCtrl)
{
	if (iCurrent < 2)
		return 0;

	LPCWSTR pszCursor = &pszText[iCurrent];

	if (FALSE == fRightCtrl) 
		pszCursor = CharPrev(pszText, pszCursor);

	BOOL prevCharDelim = ToolbarEditbox_IsDelimiterChar(*pszCursor);
	for(;;)
	{
		pszCursor = CharPrev(pszText, pszCursor);
		if (TRUE == ToolbarEditbox_IsDelimiterChar(*pszCursor))
		{
			if (FALSE == prevCharDelim)
				return (INT)(INT_PTR)(CharNext(pszCursor) - pszText);

			prevCharDelim = TRUE;
		}
		else
			prevCharDelim = FALSE;

		if (pszCursor == pszText)
			break;
	}
	return 0;
}

static INT ToolbarEditbox_FindWordRight(LPCWSTR pszText, INT iCurrent, INT cchLen)
{
	if ( iCurrent >= (cchLen - 1))
		return cchLen;

	LPCWSTR pszEnd = &pszText[cchLen];
	LPCWSTR pszCursor = &pszText[iCurrent];

	BOOL prevCharDelim = ToolbarEditbox_IsDelimiterChar(*pszCursor);

	for(;;)
	{
		pszCursor = CharNext(pszCursor);
		if (pszCursor >= pszEnd) 
			break;

		if (prevCharDelim != ToolbarEditbox_IsDelimiterChar(*pszCursor))
		{
			prevCharDelim = TRUE;
			return (INT)(INT_PTR)(pszCursor - pszText);
		}
		else
			prevCharDelim = FALSE;

	}
	return cchLen;
}

static INT CALLBACK ToolbarEditbox_WordBreakProc(LPWSTR pszText, INT iCurrent, INT cchLen, INT code)
{
	switch(code)
	{
		case WB_ISDELIMITER:	return (iCurrent < 0) ? 0 : ((iCurrent > cchLen) ? (cchLen + 1) : ToolbarEditbox_IsDelimiterChar(pszText[iCurrent]));
		case WB_LEFT:			return ToolbarEditbox_FindLeft(pszText, iCurrent, cchLen);
		case WB_RIGHT:			return ToolbarEditbox_FindRight(pszText, iCurrent, cchLen);
		case WB_MOVEWORDLEFT:	return ToolbarEditbox_FindWordLeft(pszText, iCurrent, cchLen, FALSE);
		case WB_MOVEWORDRIGHT:	return ToolbarEditbox_FindWordRight(pszText, iCurrent, cchLen);
	}
	return 0;
}

static INT CALLBACK ToolbarEditbox_WordBreakProcOverrideLeft(LPWSTR pszText, INT iCurrent, INT cchLen, INT code)
{
	switch(code)
	{
		case WB_LEFT:	return ToolbarEditbox_FindWordLeft(pszText, iCurrent, cchLen, FALSE);
		case WB_RIGHT:	return ToolbarEditbox_FindWordRight(pszText, iCurrent, cchLen);
	}
	return ToolbarEditbox_WordBreakProc(pszText, iCurrent, cchLen, code);
}

static INT CALLBACK ToolbarEditbox_WordBreakProcOverrideRight(LPWSTR pszText, INT iCurrent, INT cchLen, INT code)
{
	switch(code)
	{
		case WB_LEFT:	return ToolbarEditbox_FindWordLeft(pszText, iCurrent, cchLen, TRUE);
		case WB_RIGHT:	return ToolbarEditbox_FindWordRight(pszText, iCurrent, cchLen);
	}
	return ToolbarEditbox_WordBreakProc(pszText, iCurrent, cchLen, code);
}


static void ToolbarEditbox_OnDestroy(HWND hwnd)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox && NULL != editbox->host)
		editbox->host->EditboxDestroyed(hwnd);

	WNDPROC originalProc = (editbox ? editbox->originalProc : NULL);
	BOOL fUnicode = (0 != (NTEF_UNICODE & editbox->flags));

	ToolbarEditbox_Detach(hwnd);

	if (NULL != originalProc)
	{
		if (FALSE != fUnicode) 
			CallWindowProcW(originalProc, hwnd, WM_DESTROY, 0, 0L);
		else
			CallWindowProcA(originalProc, hwnd, WM_DESTROY, 0, 0L);
	}
	
}

static void ToolbarEditbox_OnSetFocus(HWND hwnd, HWND hFocus)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox) 
	{
		editbox->flags &= ~NTEF_CLICKEDONCE;
	}
	ToolbarEditbox_CallOrigWindowProc(hwnd, WM_SETFOCUS, (WPARAM)hFocus, 0L);
}

static void ToolbarEditbox_OnKillFocus(HWND hwnd, HWND hFocus)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox && NULL != editbox->host )
	{
		if (FALSE != editbox->host->EditboxKillFocus(hwnd, hFocus))
			return;
	}

	ToolbarEditbox_CallOrigWindowProc(hwnd, WM_KILLFOCUS, (WPARAM)hFocus, 0L);
}

static LRESULT ToolbarEditbox_OnGetDlgCode(HWND hwnd, INT vKey, MSG* pMsg)
{
	LRESULT result = ToolbarEditbox_CallOrigWindowProc(hwnd, WM_GETDLGCODE, (WPARAM)vKey, (LPARAM)pMsg);
	result |= DLGC_WANTALLKEYS;
	result &= ~DLGC_HASSETSEL;
	return result;
}

		
static LRESULT ToolbarEditbox_OnSetCursor(HWND hwnd, HWND hOwner, INT hitTest, INT messageId)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (HTCLIENT == hitTest && 
		NULL != editbox && 0 == (NTEF_MENULOOP & editbox->flags))
	{
		HCURSOR hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(OCR_IBEAM), IMAGE_CURSOR, 
							0, 0, LR_DEFAULTCOLOR | LR_SHARED | LR_DEFAULTSIZE);
		if (NULL != hCursor)
		{
			SetCursor(hCursor);
			return TRUE;
		}
	}
	
	HWND hParent = GetParent(hwnd);
	if (NULL != hParent && 
		FALSE != (BOOL)SendMessage(hParent, WM_SETCURSOR, (WPARAM)hOwner, MAKELPARAM(hitTest, messageId)))
	{
		return TRUE;
	}

	return ToolbarEditbox_CallOrigWindowProc(hwnd, WM_SETCURSOR, (WPARAM)hOwner, MAKELPARAM(hitTest, messageId));
}

static void ToolbarEditbox_OnContextMenu(HWND hwnd, HWND hTarget, POINTS pts)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox) editbox->flags |= NTEF_MENULOOP;
//	SendMessage(hwnd, WM_SETCURSOR, (WPARAM)hwnd, MAKELPARAM(HTCLIENT, WM_MOUSEMOVE));
	ToolbarEditbox_CallOrigWindowProc(hwnd, WM_CONTEXTMENU, (WPARAM)hTarget, *((LPARAM*)&pts));
	if (NULL != editbox) editbox->flags &= ~NTEF_MENULOOP;
}

static void ToolbarEditbox_OnLButtonDown(HWND hwnd, UINT vKey, POINTS pts)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox)
	{
		if (0 != (NTEF_SHOWCARET & editbox->flags))
		{
			editbox->flags &= ~NTEF_SHOWCARET;
			ShowCaret(hwnd);
		}

		
		DWORD clickTime = GetTickCount();
		if (clickTime >= editbox->dblclkTime && clickTime <= (editbox->dblclkTime + GetDoubleClickTime()))
		{
			SendMessage(hwnd, NTEBM_SELECTALL, 0, 0L);
			return;
		}

		UINT start, end;
		SendMessage(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
		if (start != end)
		{
			editbox->flags |= NTEF_CLICKEDONCE;
		}
	}

	ToolbarEditbox_CallOrigWindowProc(hwnd, WM_LBUTTONDOWN, (WPARAM)vKey, *((LPARAM*)&pts));
}

static void ToolbarEditbox_OnLButtonUp(HWND hwnd, UINT vKey, POINTS pts)
{
	ToolbarEditbox_CallOrigWindowProc(hwnd, WM_LBUTTONUP, (WPARAM)vKey, *((LPARAM*)&pts));

	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox && 0 == (NTEF_CLICKEDONCE & editbox->flags) && hwnd == GetFocus())
	{
		editbox->flags |= NTEF_CLICKEDONCE;
		UINT start, end;
		SendMessage(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
		if (start == end)
		{
			SendMessage(hwnd, NTEBM_SELECTALL, 0, 0L);
		}
	}
}

static void ToolbarEditbox_OnLButtonDblClk(HWND hwnd, UINT vKey, POINTS pts)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL == editbox) return;
	
	DWORD clickTime = GetTickCount();
	if (clickTime >= editbox->dblclkTime && clickTime <= (editbox->dblclkTime + 2*GetDoubleClickTime()))
	{
		INT r = (INT)SendMessage(hwnd, EM_CHARFROMPOS, 0, *(LPARAM*)&pts); 
		r = LOWORD(r);
		SendMessage(hwnd, EM_SETSEL, (WPARAM)r, (LPARAM)r);
		editbox->dblclkTime = 0;
	}
	else
	{
		editbox->dblclkTime = clickTime;
	}

	INT f, l;
	SendMessage(hwnd, EM_GETSEL, (WPARAM)&f, (LPARAM)&l);
	if (f != l) return;
	
		
	ToolbarEditbox_CallOrigWindowProc(hwnd, WM_LBUTTONDBLCLK, (WPARAM)vKey, *((LPARAM*)&pts));

}

static void ToolbarEditbox_OnMouseMove(HWND hwnd, UINT vKey, POINTS pts)
{
	ToolbarEditbox_CallOrigWindowProc(hwnd, WM_MOUSEMOVE, (WPARAM)vKey, *((LPARAM*)&pts));

	if (0 != (MK_LBUTTON & vKey))
	{
		TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
		if (NULL != editbox && 0 == (NTEF_SHOWCARET & editbox->flags) && 
			hwnd == GetFocus())
		{
			UINT start, end;
			SendMessage(hwnd, EM_GETSEL, (WPARAM)&start, (LPARAM)&end);
			if (start != end)
			{
				if (FALSE != HideCaret(hwnd))
					editbox->flags |= NTEF_SHOWCARET;
			}
		}
	}
}

static LRESULT CALLBACK ToolbarEditbox_MouseHook(int nCode, WPARAM wParam, LPARAM lParam)
{
	ToolbarEditbox_PatchCursor();

	TOOLBAREDITBOX *editbox = GetEditbox(GetFocus());
	if (NULL != editbox && 0 != (NTEF_SHOWCURSOR & editbox->flags))
	{
		ShowCursor(TRUE);
		editbox->flags &= ~NTEF_SHOWCURSOR;
	}

	LRESULT result = CallNextHookEx(mouseHook, nCode, wParam, lParam);
	UnhookWindowsHookEx(mouseHook);
	mouseHook = NULL;
	return result;
}

static void ToolbarEditbox_DeleteWord(HWND hwnd, UINT vKey, UINT state)
{
	BOOL resetVisible = FALSE;
	INT first, last;
	SendMessage(hwnd, EM_GETSEL, (WPARAM)&first, (LPARAM)&last);
	if (first == last)
	{
		UINT windowStyle = GetWindowStyle(hwnd);
		if (0 != (WS_VISIBLE & windowStyle))
		{
			SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE);
			resetVisible = TRUE;
		}

		SendMessage(hwnd, WM_KEYDOWN, (WPARAM)vKey, (LPARAM)state);
		INT newFirst, newLast;
		SendMessage(hwnd, EM_GETSEL, (WPARAM)&newFirst, (LPARAM)&newLast);
		if (newFirst != first || newLast != last)
			SendMessage(hwnd, EM_SETSEL, (WPARAM)first, (LPARAM)newLast);
	}

	SendMessage(hwnd, EM_REPLACESEL, TRUE, NULL); 
	if (FALSE != resetVisible)
	{
		UINT windowStyle = GetWindowStyle(hwnd);
		if (0 == (WS_VISIBLE & windowStyle))
			SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE);

		InvalidateRect(hwnd, NULL, FALSE);
	}

}
static void ToolbarEditbox_OnKeyDown(HWND hwnd, UINT vKey, UINT state)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox) 
	{
		if (0 != (NTEF_SHOWCARET & editbox->flags))
		{
			editbox->flags &= ~NTEF_SHOWCARET;
			ShowCaret(hwnd);
		}

		editbox->flags |= NTEF_KEYDOWN;
		
	}
	

	if (NULL == editbox || NULL == editbox->host ||
		FALSE == editbox->host->EditboxKeyDown(hwnd, vKey, state))
	{
		EDITWORDBREAKPROC fnOrigBreak = NULL;
		if(0 != (0x8000 & GetAsyncKeyState(VK_CONTROL)))
		{
			switch(vKey)
			{
				case VK_LEFT:
				case VK_RIGHT:
					fnOrigBreak = (EDITWORDBREAKPROC)SendMessage(hwnd, EM_GETWORDBREAKPROC, 0, 0L);
					if (ToolbarEditbox_WordBreakProc == fnOrigBreak)
					{
						EDITWORDBREAKPROC fnOverride = (VK_LEFT == vKey) ?
							ToolbarEditbox_WordBreakProcOverrideLeft : ToolbarEditbox_WordBreakProcOverrideRight;
						SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)fnOverride);
					}
					break;
				case VK_DELETE:
					ToolbarEditbox_DeleteWord(hwnd, VK_RIGHT, state);
					return;
				case VK_BACK:
					ToolbarEditbox_DeleteWord(hwnd, VK_LEFT, state);
					return;

				
			}
		}

		ToolbarEditbox_CallOrigWindowProc(hwnd, WM_KEYDOWN, (WPARAM)vKey, (LPARAM)state);

		if (NULL != fnOrigBreak)
			SendMessage(hwnd, EM_SETWORDBREAKPROC, 0, (LPARAM)fnOrigBreak);
	}
	
	if (('0' <= vKey && vKey <= '9') ||
		('A' <= vKey && vKey <= 'Z') ||
		(VK_NUMPAD0 <= vKey && vKey <= VK_DIVIDE) ||
		(VK_OEM_1 <= vKey && vKey <= VK_OEM_3) ||
		(VK_OEM_4 <= vKey && vKey <= VK_OEM_8) ||
		VK_OEM_NEC_EQUAL == vKey || 
		VK_OEM_102 == vKey)
	{
		if (0 == (0x8000 & GetAsyncKeyState(VK_CONTROL)) && 
			0 == (0x8000 & GetAsyncKeyState(VK_MENU)))
		{
			PostMessage(hwnd, NTEBM_UPDATECURSOR, 0, 0L);
		}
	}	
	
}

static void ToolbarEditbox_OnKeyUp(HWND hwnd, UINT vKey, UINT state)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL == editbox || NULL == editbox->host ||
		FALSE == editbox->host->EditboxKeyUp(hwnd, vKey, state))
	{
		ToolbarEditbox_CallOrigWindowProc(hwnd, WM_KEYUP, (WPARAM)vKey, (LPARAM)state);
	}
	
	if (NULL != editbox) 
		editbox->flags &= ~NTEF_KEYDOWN;

	if (NULL == mouseHook)
	{
		mouseHook = SetWindowsHookEx(WH_MOUSE, ToolbarEditbox_MouseHook, NULL, GetCurrentThreadId());
	}

}

static void ToolbarEditbox_OnChar(HWND hwnd, UINT vKey, UINT state)
{	
	BOOL fControl = FALSE;
	if (0 != (0x8000 & GetAsyncKeyState(VK_CONTROL)))
	{
		fControl = TRUE;
		UINT scanCode = (HIWORD(state) & 0x00FF);
		vKey = MapVirtualKey(scanCode, MAPVK_VSC_TO_VK);
	}

	switch(vKey)
	{
		case VK_ESCAPE:		ToolbarEditbox_ResetText(hwnd); return;
		case VK_TAB:		ToolbarEditbox_NavigateNextCtrl(hwnd, 0 == (0x8000 & GetAsyncKeyState(VK_SHIFT))); return;
		case VK_RETURN:		ToolbarEditbox_AcceptText(hwnd); return;
	}

	if (FALSE != fControl)
	{
		switch(vKey)
		{
			case 'A':	SendMessage(hwnd, NTEBM_SELECTALL, 0, 0L); return;
			case 'Z':	SendMessage(hwnd, EM_UNDO, 0, 0L); return;
			case 'X':	SendMessage(hwnd, WM_CUT, 0, 0L); return;
			case 'C':	SendMessage(hwnd, WM_COPY, 0, 0L); return;
			case 'V':	SendMessage(hwnd, WM_PASTE, 0, 0L); return;
		}
		return;
	}
		

	

	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox && NULL != editbox->host && 
		FALSE != editbox->host->EditboxPreviewChar(hwnd, vKey, state))
	{
		return;
	}

	ToolbarEditbox_CallOrigWindowProc(hwnd, WM_CHAR, (WPARAM)vKey, (LPARAM)state);
}


static void ToolbarEditbox_OnUpdateCursor(HWND hwnd)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox && 0 != (NTEF_KEYDOWN & editbox->flags))
	{	
		if (NULL != mouseHook)
		{
			UnhookWindowsHookEx(mouseHook);
			mouseHook = NULL;
		}

		if (0 == (NTEF_SHOWCURSOR & editbox->flags))
		{
			CURSORINFO cursorInfo;
			cursorInfo.cbSize = sizeof(CURSORINFO);
			if (GetCursorInfo(&cursorInfo) && 0 != (CURSOR_SHOWING & cursorInfo.flags))
			{
				ShowCursor(FALSE);
				editbox->flags |= NTEF_SHOWCURSOR;
			}
		}
	}
}

static void ToolbarEditbox_OnEnterMenuLoop(HWND hwnd, BOOL fContext)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox) editbox->flags |= NTEF_MENULOOP;

	ToolbarEditbox_CallOrigWindowProc(hwnd, WM_ENTERMENULOOP, (WPARAM)fContext, 0L);
}

static void ToolbarEditbox_OnExitMenuLoop(HWND hwnd, BOOL fContext)
{
	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox) editbox->flags &= ~NTEF_MENULOOP;

	ToolbarEditbox_CallOrigWindowProc(hwnd, WM_EXITMENULOOP, (WPARAM)fContext, 0L);
}

static BOOL ToolbarEditbox_RemoveBadChars(LPCWSTR pszText, LPWSTR *bufferOut)
{
	LPWSTR buffer = NULL;
	if (NULL == pszText) return FALSE;
	
	const WCHAR szBadChars[] = { L'\r', L'\n', L'\t', L'\0'};
	BOOL fDetected = FALSE;

	for (LPCWSTR p = pszText; L'\0' != *p && FALSE == fDetected; p++)
	{
		for (LPCWSTR b = szBadChars; L'\0' != *b; b++)
		{
			if (*p == *b)
			{
				fDetected = TRUE;
				break;
			}
		}
	}

	if (FALSE == fDetected)
		return FALSE;

	if (NULL == bufferOut)
		return TRUE;
		
	INT cchText = lstrlen(pszText);
	buffer = Plugin_MallocString(cchText + 1);
	if (NULL == buffer) return FALSE;
	
	LPCWSTR s = pszText;
	LPWSTR d = buffer;
	LPCWSTR b;
	for(;;)
	{
		for (b = szBadChars; L'\0' != *b && *s != *b; b++);
		if(L'\0' != *b)
		{
			if (L'\t' == *b)
			{
				*d = L' ';
				d++;
			}
		}
		else
		{
			*d = *s;
			d++;
		}
		
		if (L'\0' == *s)
			break;
		
		s++;
		
	}

	*bufferOut = buffer;
	return TRUE;
}

static LRESULT ToolbarEditbox_OnSetText(HWND hwnd, LPCWSTR pszText)
{
	LPWSTR buffer;
	if (FALSE == ToolbarEditbox_RemoveBadChars(pszText, &buffer))
		buffer = NULL;
	else
		pszText = buffer;
	
	LRESULT result =  ToolbarEditbox_CallOrigWindowProc(hwnd, WM_SETTEXT, 0, (LPARAM)pszText);
	
	if (NULL != buffer)
		Plugin_FreeString(buffer);

	return result;
}

static LRESULT ToolbarEditbox_OnReplaceSel(HWND hwnd, BOOL fUndo, LPCWSTR pszText)
{
	LPWSTR buffer;
	if (FALSE == ToolbarEditbox_RemoveBadChars(pszText, &buffer))
		buffer = NULL;
	else
		pszText = buffer;
	
	LRESULT result =  ToolbarEditbox_CallOrigWindowProc(hwnd, EM_REPLACESEL, (WPARAM)fUndo, (LPARAM)pszText);
	
	if (NULL != buffer)
		Plugin_FreeString(buffer);

	return result;
}

static void ToolbarEditbox_ReplaceText(HWND hwnd, LPCWSTR pszText, BOOL fUndo, BOOL fScrollCaret)
{
	UINT windowStyle = GetWindowStyle(hwnd);
	if (0 != (WS_VISIBLE & windowStyle))
		SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE);
				
	SendMessage(hwnd, EM_REPLACESEL, (WPARAM)fUndo, (LPARAM)pszText);
	if (FALSE != fScrollCaret)
	{
		INT f, l;
		SendMessage(hwnd, EM_GETSEL, (WPARAM)&f, (LPARAM)&l);
		SendMessage(hwnd, EM_SETSEL, (WPARAM)f, (LPARAM)l);
	}

	if (0 != (WS_VISIBLE & windowStyle))
	{
		windowStyle = GetWindowStyle(hwnd);
		if (0 == (WS_VISIBLE & windowStyle))
			SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE);
		InvalidateRect(hwnd, NULL, FALSE);
	}
}

static void ToolbarEditbox_OnPaste(HWND hwnd)
{
	IDataObject *pObject;
	HRESULT hr = OleGetClipboard(&pObject);
	if (SUCCEEDED(hr))
	{		
		FORMATETC fmt = {CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
		STGMEDIUM stgm; 
		hr = pObject->GetData(&fmt, &stgm);
		if(S_OK == hr) 
		{
			LPCWSTR pClipboard = (LPCWSTR)GlobalLock(stgm.hGlobal);
			ToolbarEditbox_ReplaceText(hwnd, pClipboard, TRUE, TRUE);
      		GlobalUnlock(stgm.hGlobal);
			ReleaseStgMedium(&stgm);

		}
		else
		{
			fmt.cfFormat = CF_TEXT;
			hr = pObject->GetData(&fmt, &stgm);
			if(S_OK == hr) 
			{
				LPCSTR pClipboardAnsi = (LPCSTR)GlobalLock(stgm.hGlobal);
				LPWSTR pClipboard = Plugin_MultiByteToWideChar(CP_ACP, 0, pClipboardAnsi, -1);
				ToolbarEditbox_ReplaceText(hwnd, pClipboard, TRUE, TRUE);
				Plugin_FreeString(pClipboard);
      			GlobalUnlock(stgm.hGlobal);
				ReleaseStgMedium(&stgm);
			}
		}
  		pObject->Release();
	}
}


static void ToolbarEditbox_OnSelectAll(HWND hwnd)
{
	UINT windowStyle = GetWindowStyle(hwnd);

	INT f, l, tl;
	tl = GetWindowTextLength(hwnd);
	SendMessage(hwnd, EM_GETSEL, (WPARAM)&f, (LPARAM)&l);
	if (f == 0 && tl == l) 
		return;

	if (0 != (WS_VISIBLE & windowStyle))
		SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE);

	RECT rc;
	GetWindowRect(hwnd, &rc);
	SetWindowPos(hwnd, NULL, 0, 0, 0xFFFFF, rc.bottom - rc.top, 
		SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOCOPYBITS);
	
	SendMessage(hwnd, EM_SETSEL, 0, -1);

	SetWindowPos(hwnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, 
		SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOCOPYBITS);

	if (0 != (WS_VISIBLE & windowStyle))
	{
		UINT windowStyle = GetWindowStyle(hwnd);
		if (0 == (WS_VISIBLE & windowStyle))
		{
			SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE);
			InvalidateRect(hwnd, NULL, FALSE);
		}
	}

	TOOLBAREDITBOX *editbox = GetEditbox(hwnd);
	if (NULL != editbox && 
		0 == (NTEF_SHOWCARET & editbox->flags) &&
		FALSE != HideCaret(hwnd))
	{
		editbox->flags |= NTEF_SHOWCARET;
	}
}



static LRESULT ToolbarEditbox_OnFindWordBreak(HWND hwnd, INT code, INT start)
{
	EDITWORDBREAKPROC fnBreak = (EDITWORDBREAKPROC)SendMessage(hwnd, EM_GETWORDBREAKPROC, 0, 0L);
	if (NULL == fnBreak) return 0;

	UINT cchText = GetWindowTextLength(hwnd);
	if (0 == cchText) return 0;

	LPWSTR pszText = Plugin_MallocString(cchText + 1);
	if (NULL == pszText) return 0;

	LRESULT result = 0;
	cchText = GetWindowText(hwnd, pszText, cchText + 1);
	if (0 != cchText)
	{
		result = fnBreak(pszText, start, cchText, code);
	}
	Plugin_FreeString(pszText);
	return result;
}

static LRESULT CALLBACK ToolbarEditbox_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch(uMsg)
	{
		case WM_DESTROY:		ToolbarEditbox_OnDestroy(hwnd); return 0;
		case WM_SETFOCUS:		ToolbarEditbox_OnSetFocus(hwnd, (HWND)wParam); return 0;
		case WM_KILLFOCUS:		ToolbarEditbox_OnKillFocus(hwnd, (HWND)wParam); return 0;
		case WM_GETDLGCODE:		return ToolbarEditbox_OnGetDlgCode(hwnd, (INT)wParam, (MSG*)lParam);
		case WM_SETCURSOR:		return ToolbarEditbox_OnSetCursor(hwnd, (HWND)wParam, (INT)LOWORD(lParam), (INT)HIWORD(lParam));
		case WM_CONTEXTMENU:	ToolbarEditbox_OnContextMenu(hwnd, (HWND)wParam, MAKEPOINTS(lParam)); return 0;
		case WM_LBUTTONDOWN:	ToolbarEditbox_OnLButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0;
		case WM_LBUTTONUP:		ToolbarEditbox_OnLButtonUp(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0;
		case WM_LBUTTONDBLCLK:	ToolbarEditbox_OnLButtonDblClk(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0;
		case WM_MOUSEMOVE:		ToolbarEditbox_OnMouseMove(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0;
		case WM_KEYDOWN:		ToolbarEditbox_OnKeyDown(hwnd, (UINT)wParam, (UINT)lParam); return 0;
		case WM_KEYUP:			ToolbarEditbox_OnKeyUp(hwnd, (UINT)wParam, (UINT)lParam); return 0;
		case WM_CHAR:			ToolbarEditbox_OnChar(hwnd, (UINT)wParam, (UINT)lParam); return 0;
		case WM_ENTERMENULOOP:	ToolbarEditbox_OnEnterMenuLoop(hwnd, (BOOL)wParam); return 0;
		case WM_EXITMENULOOP:	ToolbarEditbox_OnExitMenuLoop(hwnd, (BOOL)wParam); return 0;
		case WM_SETTEXT:		return ToolbarEditbox_OnSetText(hwnd, (LPCWSTR)lParam);
		case WM_PASTE:			ToolbarEditbox_OnPaste(hwnd); return 1;
		case EM_REPLACESEL:		ToolbarEditbox_OnReplaceSel(hwnd, (BOOL)wParam, (LPCWSTR)lParam); return 0;
		case EM_FINDWORDBREAK:	return ToolbarEditbox_OnFindWordBreak(hwnd, (INT)wParam, (INT)lParam);
			
		case NTEBM_UPDATECURSOR: ToolbarEditbox_OnUpdateCursor(hwnd); return 0;
		case NTEBM_SELECTALL:	ToolbarEditbox_OnSelectAll(hwnd); return 0;
	}
	
	return ToolbarEditbox_CallOrigWindowProc(hwnd, uMsg, wParam, lParam);
}