#include "./skinnededit.h"
#include "../winamp/wa_dlg.h"
#include "./skinning.h"
#include "./stockobjects.h"
#include "./skinnedmenu.h"
#include "../nu/trace.h"
#include <windowsx.h>
#include <strsafe.h>


#define EDIT_TEXT_MAX		65536
static WCHAR *pszTextGlobal = NULL;

static int caretPos = -1;
static BOOL updateCaret = FALSE;

#define GET_CHAR_X(__hwnd, __index) (GET_X_LPARAM(CallPrevWndProc(EM_POSFROMCHAR, (WPARAM)(__index), 0L)))

#define MLSEM_FIRST			(WM_APP + 0x2FFE)
#define MLSEM_ENABLEREDRAW	(MLSEM_FIRST - 0)

SkinnedEdit::SkinnedEdit(void) : SkinnedWnd(FALSE), firstVisible(0), lastVisible(0),
								 firstSelected(0), lastSelected(0), maxCharWidth(0),
								 mouseWParam(0), mouseLParam(0), cx(0), cy(0)
{
	if (NULL == pszTextGlobal) 
		pszTextGlobal = (LPWSTR)calloc(EDIT_TEXT_MAX, sizeof(WCHAR));
}

SkinnedEdit::~SkinnedEdit(void)
{
}

BOOL SkinnedEdit::Attach(HWND hwndEdit)
{
	if(!SkinnedWnd::Attach(hwndEdit)) return FALSE;
	SetType(SKINNEDWND_TYPE_EDIT);
	FontChanged();
	SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
	return TRUE;
}

void SkinnedEdit::EraseBckGnd(HDC hdc, RECT *prc, RECT *prcText, BOOL fEraseAll, HBRUSH hBrush)
{		
	HRGN rgn = CreateRectRgnIndirect(prc);
	if (!IsRectEmpty(prcText))
	{
		HRGN rgn2 = CreateRectRgnIndirect(prcText);
		if (NULL != rgn2)
		{
			CombineRgn(rgn, rgn, rgn2, RGN_DIFF);

		/*	if (FALSE != fEraseAll)
			{	
				SetRectRgn(rgn2, prc->left, prc->top, prc->right, prcText->top);
				CombineRgn(rgn, rgn, rgn2, RGN_DIFF);

				SetRectRgn(rgn2, prc->left, prcText->bottom, prc->right, prc->bottom);
				CombineRgn(rgn, rgn, rgn2, RGN_DIFF);
			}*/
			DeleteObject(rgn2);
		}
	}
	
	FillRgn(hdc, rgn, hBrush);
	DeleteObject(rgn);
}


void SkinnedEdit::DrawText(HDC hdc, RECT *prc, RECT *prcText, LPCWSTR pszText, INT cchText)
{

	if ((lastSelected != firstSelected) && 
		(lastSelected  > firstVisible) && 
		(firstSelected < lastVisible) &&  
		((hwnd == GetFocus()) || (ES_NOHIDESEL & GetWindowLongPtrW(hwnd, GWL_STYLE))))
	{
		RECT rt;
		int lim, limSel;
		LPCWSTR pszTextOut;
		pszTextOut = pszText + firstVisible;
		lim = firstSelected - firstVisible;
		CopyRect(&rt, prcText);
		if (lim > 0) 
		{			
		//	DrawTextW(hdc, pszTextOut, lim, prcText,DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP); 
			ExtTextOutW(hdc, rt.left, rt.top, 0, &rt, pszTextOut, lim, NULL);
			pszTextOut += lim;
		}
				
		limSel = min(lastSelected, lastVisible) - (int)(pszTextOut - pszText);
		lim = lastVisible - lastSelected;
		if(lim > 0)
		{
			rt.left = GET_CHAR_X(hwnd, lastSelected);
			// DrawTextW(hdc, pszTextOut + limSel, lim, &rt, DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP); 
			ExtTextOutW(hdc, rt.left, rt.top, 0, &rt, pszTextOut + limSel, lim, NULL);
		}					
			
		SetTextColor(hdc, WADlg_getColor(WADLG_SELBAR_FGCOLOR));
		SetBkColor(hdc, WADlg_getColor(WADLG_SELBAR_BGCOLOR));
		
		if (lim > 0) rt.right = rt.left - 1;
		rt.left = GET_CHAR_X(hwnd, max(firstSelected, firstVisible));
	//	DrawTextW(hdc, pszTextOut, limSel, &rt, DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP ); 
		if (rt.right > rt.left) ExtTextOutW(hdc, rt.left, rt.top, ETO_CLIPPED, &rt, pszTextOut, limSel, NULL);
	}
	else
	{
	 //  DrawTextW(hdc, pszText + firstVisible, cchText - firstVisible - (cchText - lastVisible), prcText, DT_TOP | DT_NOPREFIX | DT_NOCLIP); 
		ExtTextOutW(hdc, prcText->left, prcText->top, 0, prcText, 
						pszText+ firstVisible, cchText - firstVisible - (cchText - lastVisible), NULL);
	}
}

static void GetEditColors(DWORD windowStyle, BOOL bEnabled, COLORREF *prgbText, COLORREF *prgbTextBk)
{
	COLORREF fg, bg;
	bg = WADlg_getColor((ES_READONLY & windowStyle) ? WADLG_WNDBG : WADLG_ITEMBG);
	fg = WADlg_getColor((ES_READONLY & windowStyle) ? WADLG_WNDFG : WADLG_ITEMFG);
	if(!bEnabled)
	{		
		fg = RGB((GetRValue(fg)+GetRValue(bg))/2,
						(GetGValue(fg)+GetGValue(bg))/2,
						(GetBValue(fg)+GetBValue(bg))/2);
	}
	if (prgbText) *prgbText = fg;
	if (prgbTextBk) *prgbTextBk = bg;
}


void SkinnedEdit::OnPaint()
{
	HDC hdc;
	int cchText;

	PAINTSTRUCT ps;
	RECT rc, rt;
	HFONT hFont, hFontOld;
	DWORD margins, ws;
	TEXTMETRICW tm;
	
		
	cchText = (INT)CallPrevWndProc(WM_GETTEXTLENGTH, 0, 0);
	if (cchText) CallPrevWndProc(WM_GETTEXT, (WPARAM)EDIT_TEXT_MAX, (LPARAM)pszTextGlobal);
	
	hFont = (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L);
	if (!hFont) hFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT);

	hdc = GetDCEx(hwnd, NULL, DCX_PARENTCLIP | DCX_CACHE | DCX_CLIPSIBLINGS |
				DCX_INTERSECTUPDATE | DCX_VALIDATE);
	if (NULL == hdc) return;

	hFontOld = (hFont) ? (HFONT)SelectObject(hdc, hFont) : NULL;
	GetTextMetricsW(hdc, &tm);
		

	GetClientRect(hwnd, &rc);
	CopyRect(&rt, &rc);
		
	if (SWES_BOTTOM & style) 
	{	
		if ((rt.bottom - tm.tmHeight) > rt.top) rt.top = rt.bottom - tm.tmHeight;
	}
	else if (SWES_VCENTER & style)
	{
		INT t = rc.top + ((rc.bottom - rc.top) - tm.tmHeight)/2;
		if (t > rc.top) rt.top = t;
	}

	ws = GetWindowLongPtrW(hwnd, GWL_STYLE);
		

	margins = (DWORD)CallPrevWndProc(EM_GETMARGINS, 0, 0L);

	
	if (cchText)
	{
		int x;

		CallPrevWndProc(EM_GETSEL, (WPARAM)&firstSelected, (LPARAM)&lastSelected);
		lastVisible = (int)(INT_PTR)CallPrevWndProc(EM_CHARFROMPOS, 0, MAKELPARAM(rc.right - (GET_Y_LPARAM(margins)), 0));
		if ( -1 == lastVisible) lastVisible = cchText;
		firstVisible =(INT)(INT_PTR)CallPrevWndProc(EM_CHARFROMPOS, 0, MAKELPARAM(rc.left + GET_X_LPARAM(margins), 0));
		if (cchText == firstVisible) firstVisible = 0;
		else if (firstVisible > 0) firstVisible++;
		while (12000 < (x = GET_CHAR_X(hwnd, firstVisible)))  firstVisible++;
		rt.left = x;
		if (firstVisible > 0 && rt.left > rc.left + GET_X_LPARAM(margins) + 1)
		{ // we can try to display one more
			int t = GET_CHAR_X(hwnd, firstVisible -1);
			if (t != -1 && t < rc.right) { rt.left = t; firstVisible--; }
		}
	
		if (lastVisible)
		{
			if (lastVisible < cchText -1)
			{
				rt.right = GET_CHAR_X(hwnd, lastVisible);
			}
			else
			{
				INT i = lastVisible - ((cchText == lastVisible && cchText) ? 1 : 0);
				ABC abc;
				if (GetCharABCWidthsW(hdc, pszTextGlobal[i], pszTextGlobal[i], &abc))
					rt.right = abc.abcA + abc.abcB + abc.abcC;
				else
					GetCharWidth32W(hdc, pszTextGlobal[i], pszTextGlobal[i], (INT*)&rt.right);
				rt.right += GET_CHAR_X(hwnd, i);
			}
		}
		else rt.right = rt.left;
		
		if (rt.top + tm.tmHeight < rt.bottom) rt.bottom = rt.top + tm.tmHeight;
		if (rt.right > rc.right - GET_Y_LPARAM(margins)) rt.right = rc.right - GET_Y_LPARAM(margins);
	}
	else 
	{
		firstVisible = 0;
		lastVisible = 0;
		rt.left += GET_X_LPARAM(margins);
		rt.right -= GET_Y_LPARAM(margins);
		if (ES_CENTER & ws) 
		{ 
			rt.left += (rt.right - rt.left)/2;
			rt.right = rt.left;
		}
		else if (ES_RIGHT & ws) rt.left = rt.right;
		else rt.right = rt.left;
	}


	if (FALSE != updateCaret)
	{
		updateCaret = FALSE;
	//	ShowCaret(hwnd);

		if (hwnd == GetFocus())
		{
			INT x;
			if (caretPos >= lastVisible) x = rt.right;
			else if (caretPos <= firstVisible) x = rt.left;
			else x = GET_CHAR_X(hwnd, caretPos);
			
			if (x < rc.left) 
				x = rc.left;
			else 
			{
				INT caretWidth = GetSystemMetrics(SM_CXBORDER);
				if (x + caretWidth > rc.right)
					x = rc.right - caretWidth;
			}
			SetCaretPos(x, rt.top);
		}
	}

	if (hFontOld) SelectObject(hdc, hFontOld);
	ReleaseDC(hwnd, hdc);

	hdc = BeginPaint(hwnd, &ps);
	if (NULL == hdc) return;

	hFontOld = (hFont) ? (HFONT)SelectObject(hdc, hFont) : NULL;
		
	HBRUSH brushBk = NULL;
	BOOL overrideColors = FALSE;

	HWND hParent = GetParent(hwnd);
	if (NULL != hParent)
	{
		UINT uMsg = (0 == ((ES_READONLY | WS_DISABLED) & ws)) ? WM_CTLCOLOREDIT : WM_CTLCOLORSTATIC;
		brushBk = (HBRUSH)SendMessage(hParent, uMsg, (WPARAM)hdc, (LPARAM)hwnd);
		
		HBRUSH stockBursh = GetSysColorBrush( (0 == ((ES_READONLY | WS_DISABLED) & ws)) ? COLOR_WINDOW : COLOR_3DFACE);
		if (NULL == brushBk || stockBursh == brushBk)
			overrideColors = TRUE;
	}

	if (FALSE != overrideColors)
	{
		COLORREF rgbText, rgbTextBk;
		GetEditColors(ws, IsWindowEnabled(hwnd), &rgbText, &rgbTextBk);

		SetBkColor(hdc, rgbTextBk);
		SetTextColor(hdc, rgbText);
		brushBk = (HBRUSH)MlStockObjects_Get((ES_READONLY & ws) ? WNDBCK_BRUSH : ITEMBCK_BRUSH);
	}
			
	
		
	
	IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
	EraseBckGnd(hdc, &rc, NULL, ps.fErase, brushBk);
	if (cchText) 
	{		
		IntersectClipRect(hdc, rc.left + GET_X_LPARAM(margins), rt.top, rc.right - GET_Y_LPARAM(margins), rt.bottom);
		DrawText(hdc, &rc, &rt, pszTextGlobal, cchText);
	}
		
	if (hFontOld) SelectObject(hdc, hFontOld);
	EndPaint(hwnd, &ps);
}

void SkinnedEdit::OnSkinUpdated(BOOL bNotifyChildren, BOOL bRedraw)
{
	__super::OnSkinUpdated(bNotifyChildren, bRedraw);

	if (SWS_USESKINFONT & style)
	{
		CallPrevWndProc(EM_SETMARGINS, (WPARAM)(EC_LEFTMARGIN | EC_RIGHTMARGIN), MAKELPARAM(EC_USEFONTINFO, EC_USEFONTINFO));
	}
}




BOOL SkinnedEdit::GetSelection(SELECTION *selection, INT cchText, const RECT *clientRect)
{
	if (NULL == selection) return FALSE;
	if (0 == cchText)
	{
		selection->first = 0;
		selection->last = 0;
		selection->leftX = clientRect->left;
		selection->rightX = clientRect->left;
		return TRUE;
	}

	CallPrevWndProc(EM_GETSEL, (WPARAM)&selection->first, (LPARAM)&selection->last);

	selection->leftX = GET_CHAR_X(hwnd, selection->first);

	if (-1 == selection->last || cchText == selection->last)
	{
		selection->last = cchText;
		selection->rightX = GET_CHAR_X(hwnd, cchText - 1) + maxCharWidth;
	}
	else
	{	
		selection->rightX = GET_CHAR_X(hwnd, selection->last);
	}
	
	return TRUE;
}

LRESULT SkinnedEdit::OverrideDefault(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LRESULT result;
			
	UINT windowStyle = GetWindowStyle(hwnd);
	if (0 == (WS_VISIBLE & windowStyle))
	{
		result = __super::WindowProc(uMsg, wParam, lParam);
		return result;
	}

	SELECTION selectionOrig;

	RECT ri;
	GetClientRect(hwnd, &ri);
	
	INT lengthOrig = (INT)CallPrevWndProc(WM_GETTEXTLENGTH, 0, 0L);
	INT startXOrig = (lengthOrig > 0) ? GET_CHAR_X(hwnd, 0) : 0;
	GetSelection(&selectionOrig, lengthOrig, &ri);

	SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~ WS_VISIBLE);

	result = __super::WindowProc(uMsg, wParam, lParam);
	
	windowStyle = GetWindowStyle(hwnd);
	if (0 == (WS_VISIBLE & windowStyle))
	{
		windowStyle |= WS_VISIBLE;
		SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle);
	}
	
	if (hwnd == GetFocus())
	{

		INT length = (INT)CallPrevWndProc(WM_GETTEXTLENGTH, 0, 0L);

		SELECTION selection;
		GetSelection(&selection, length, &ri);
		
		if (selectionOrig.first != selection.first || selectionOrig.last != selection.last || lengthOrig != length ) 
		{	
			caretPos = (selectionOrig.last != selection.last) ? selection.last : selection.first;
			if( FALSE == updateCaret)
				updateCaret = TRUE;
			
			if (0 != (WS_VISIBLE & windowStyle))
			{						
				INT startX = (lengthOrig > 0) ? GET_CHAR_X(hwnd, 0) : 0;
				if (lengthOrig != length || startXOrig != startX) 
				{	
					RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOERASE | RDW_ERASENOW | RDW_UPDATENOW | RDW_NOCHILDREN);
				}
				else
				{
					//RECT ri;
					GetClientRect(hwnd, &ri);

					if (selectionOrig.first != selectionOrig.last)
					{
						if (selectionOrig.rightX > selection.leftX && selectionOrig.rightX < selection.rightX)
							selectionOrig.rightX--;

						if (selectionOrig.leftX > selection.leftX && selectionOrig.leftX < selection.rightX)
							selectionOrig.leftX++;
					}

				//	aTRACE_FMT("redraw: (%d, %d) - (%d, %d)\r\n",selectionOrig.leftX, selectionOrig.rightX, selection.leftX, selection.rightX); 
					HRGN rgn1 = CreateRectRgn(selectionOrig.leftX, ri.top, selectionOrig.rightX, ri.bottom);
					HRGN rgn2 = CreateRectRgn(selection.leftX, ri.top, selection.rightX, ri.bottom);
					CombineRgn(rgn1, rgn1, rgn2, (selectionOrig.first != selectionOrig.last) ? RGN_XOR : RGN_OR);

					POINT caretPt;
					if (FALSE != GetCaretPos(&caretPt))
					{
						INT caretWidth = GetSystemMetrics(SM_CXBORDER);
						if (0 == caretWidth) caretWidth = 1;
						SetRectRgn(rgn2, caretPt.x, ri.top, caretPt.x + caretWidth, ri.bottom);
						CombineRgn(rgn1, rgn1, rgn2, RGN_OR);
					}

					RedrawWindow(hwnd, NULL, rgn1, RDW_INVALIDATE | RDW_NOERASE | RDW_ERASENOW | RDW_UPDATENOW);

					INT scrollCX = 0;
					if (selectionOrig.first == selection.first && selectionOrig.leftX != selection.leftX)
					{
						scrollCX = selectionOrig.leftX - selection.leftX;
					}

					if (selectionOrig.last == selection.last && selectionOrig.rightX != selection.rightX)
					{
						scrollCX = selectionOrig.rightX - selection.rightX;
					}
					if (0 != scrollCX)
					{
						if (scrollCX > 0)
							aTRACE_LINE("move on left side");
						else
							aTRACE_LINE("move on right side");
					}
					DeleteObject(rgn1);
					DeleteObject(rgn2);
				}
			}
		}
	}

	return result;
}

void SkinnedEdit::OnWindowPosChanged(WINDOWPOS *pwp)
{	
	
	HRGN updateRgn = CreateRectRgn(0, 0, 0, 0);

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

	__super::WindowProc(WM_WINDOWPOSCHANGED, 0, (LPARAM)pwp);

	if (0 != (WS_VISIBLE & windowStyle))
	{
		windowStyle = GetWindowStyle(hwnd);
		if (0 == (WS_VISIBLE & windowStyle))
			SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE);
	}
	
	RECT rc;
	GetClientRect(hwnd, &rc);
	
	if( FALSE == updateCaret)
	{
		updateCaret = TRUE;
//		HideCaret(hwnd);
	}

	if (0 == ((SWP_NOSIZE | SWP_NOREDRAW) & pwp->flags))
	{
		HRGN rgn = NULL;
		if (rc.right - rc.left != cx)
		{
			cx -= GET_Y_LPARAM((DWORD)CallPrevWndProc(EM_GETMARGINS, 0, 0L));
			cx -= maxCharWidth;
			
			LONG l = rc.left;
			rc.left = cx;
			
			if (NULL != rgn) SetRectRgn(rgn, rc.left, rc.top, rc.right, rc.bottom);
			else rgn = CreateRectRgnIndirect(&rc);

			rc.left = l;

		}
		if (rc.bottom - rc.top != cy)
		{	
			LONG t = rc.top;
			rc.top = cy;
			
			if (NULL != rgn) SetRectRgn(rgn, rc.left, rc.top, rc.right, rc.bottom);
			else rgn = CreateRectRgnIndirect(&rc);
			CombineRgn(updateRgn, updateRgn, rgn, RGN_OR);

			rc.top = t;
		}

		if (0 == (SWP_NOREDRAW & pwp->flags))
		{
			if (NULL != updateRgn)
				InvalidateRgn(hwnd, updateRgn, FALSE);
		}

		if (NULL != rgn)
			DeleteObject(rgn);
		
	}
	
	cx = rc.right - rc.left;
	cy = rc.bottom - rc.top;

	if (NULL != updateRgn)
		DeleteObject(updateRgn);
	
}
void SkinnedEdit::FontChanged()
{
	HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS);
	if (NULL != hdc)
	{			
		HFONT  font, fontOrig;
		font = (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L);
		if (NULL != font) 
			fontOrig = (HFONT)SelectObject(hdc, font);
		
		TEXTMETRICW tm;
		maxCharWidth = (FALSE != GetTextMetricsW(hdc, &tm)) ? tm.tmMaxCharWidth : 0;
			
		if (NULL != font)
			SelectObject(hdc, fontOrig);
		
		ReleaseDC(hwnd, hdc);
	}
}

void SkinnedEdit::OnSetFont(HFONT hFont, BOOL fRedraw)
{
	__super::WindowProc(WM_SETFONT, (WPARAM)hFont, (LPARAM)fRedraw);
	FontChanged();
}

LRESULT SkinnedEdit::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if ( 0 != (SWES_SELECTONCLICK & style))
	{
		switch(uMsg)
		{
			case WM_LBUTTONDOWN:
			case WM_RBUTTONDOWN:
				if (hwnd != GetFocus())
				{
					CallPrevWndProc(EM_SETSEL, 0, -1);
					SetFocus(hwnd);
					if (WM_LBUTTONDOWN == uMsg) return 0;
				}
				break;
		}
	}
	
	if (SWS_USESKINCOLORS & style)
	{
		UINT windowStyle = GetWindowStyle(hwnd);
		switch(uMsg)
		{
			case WM_CONTEXTMENU:
				if (IsSkinnedPopupEnabled(FALSE))
				{
					SkinnedMenu sm;
					if (sm.InitializeHook(hwnd, SMS_USESKINFONT, NULL, 0, NULL, 0L))
					{
						__super::WindowProc(uMsg, wParam, lParam);
						InvalidateRect(hwnd, NULL, TRUE);
						UpdateWindow(hwnd);
						return 0;
					}
				}
				break;
			case MLSEM_ENABLEREDRAW:
				windowStyle = GetWindowStyle(hwnd);
				if (0 == (WS_VISIBLE & windowStyle))
				{
					SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE);
				}
				return 0;
		}

		if (0 == (ES_MULTILINE & windowStyle))
		{
			switch(uMsg)
			{
				case WM_PAINT: OnPaint(); return 0;
				case WM_ERASEBKGND: return 0;
				case WM_SETFONT:	OnSetFont((HFONT)wParam, (BOOL)lParam); return 0;
				
				case WM_MOUSEMOVE:
					if (wParam == mouseWParam && lParam == mouseLParam) return 0;
					mouseWParam = wParam;
					mouseLParam = lParam;
					return (MK_LBUTTON & mouseWParam) ? OverrideDefault(uMsg, wParam, lParam) : 0;
				
				case WM_SETFOCUS:
					caretPos = -1;
					
				case WM_CAPTURECHANGED:
			
					if (0 != (WS_VISIBLE & windowStyle))
						SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE);

					__super::WindowProc(uMsg, wParam, lParam);
					
					if (0 != (WS_VISIBLE & windowStyle))
					{
						windowStyle = GetWindowStyle(hwnd);
						if (0 == (WS_VISIBLE & windowStyle))
						{
							SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE);
							InvalidateRect(hwnd, NULL, FALSE);
						}
					}

					if (FALSE == updateCaret)
					{
					//	HideCaret(hwnd);
						updateCaret = TRUE;
					}

					return 0;
					
	

				case WM_UPDATEUISTATE:
				case WM_KILLFOCUS: 
					
					if (0 != (WS_VISIBLE & windowStyle))
						SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE);

					__super::WindowProc(uMsg, wParam, lParam);
					
					if (0 != (WS_VISIBLE & windowStyle))
					{
						windowStyle = GetWindowStyle(hwnd);
						if (0 == (WS_VISIBLE & windowStyle))
						{
							SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE);
							RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE);
						}
					}
					return 0;

				
				case WM_WINDOWPOSCHANGED: 
					OnWindowPosChanged((WINDOWPOS*)lParam);
					return 0;
				case WM_SETTEXT:
					if (OverrideDefault(uMsg, wParam, lParam))
					{
						InvalidateRect(hwnd, NULL, TRUE);
						return TRUE;
					}	
					return FALSE;

				case EM_SETSEL:
					{					
						BOOL recoverRegion = FALSE;
						HRGN windowRegion = CreateRectRgn(0, 0, 0, 0);
						INT result = GetWindowRgn(hwnd, windowRegion);
						if (SIMPLEREGION != result && COMPLEXREGION != result)
						{
							DeleteObject(windowRegion);
							windowRegion = NULL;
						}
						
						if (0 != (WS_VISIBLE & windowStyle))
							SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE);
						
						HRGN fakeRegion = CreateRectRgn(0, 0, 0, 0);
						if (0 != SetWindowRgn(hwnd, fakeRegion, FALSE))
							recoverRegion = TRUE;

						INT prevFirst, prevLast, currFirst, currLast;
						CallPrevWndProc(EM_GETSEL, (WPARAM)&prevFirst, (LPARAM)&prevLast);

						__super::WindowProc(uMsg, wParam, lParam);

						CallPrevWndProc(EM_GETSEL, (WPARAM)&currFirst, (LPARAM)&currLast);
						
						if (currFirst != prevFirst || currLast != prevLast)
						{
							if (currLast != prevLast) caretPos = currLast;
							else caretPos = currFirst;
							
							if (FALSE == updateCaret)
							{
//								HideCaret(hwnd);
								updateCaret = TRUE;
							}
						}
			                   
						if (FALSE != recoverRegion)
							SetWindowRgn(hwnd, windowRegion, FALSE);

						if (NULL != windowRegion)
						{
							DeleteObject(windowRegion);
							windowRegion = NULL;
						}

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

						__super::WindowProc(uMsg, wParam, lParam);
					
						if (0 != (WS_VISIBLE & windowStyle))
						{
							windowStyle = GetWindowStyle(hwnd);
							if (0 == (WS_VISIBLE & windowStyle))
								SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE);
							
						}
					}
					return 0;
				case WM_CHAR:
				case WM_KEYDOWN:
				case WM_CUT:
				//case WM_PASTE:
				case WM_CLEAR:
				case WM_UNDO:
				case EM_UNDO:
				case WM_LBUTTONDBLCLK:
				case WM_LBUTTONDOWN:
					return OverrideDefault(uMsg, wParam, lParam);
			}
		}
	}

	switch(uMsg)
	{
		case WM_SETCURSOR:
			if ((HWND)wParam == hwnd && HTCLIENT == LOWORD(lParam))
			{
				HCURSOR cursor;
				cursor = LoadCursor(NULL, IDC_IBEAM);
				if (NULL != cursor)
				{
					SetCursor(cursor);
					return TRUE;
				}
			}
			break;
	}

	return __super::WindowProc(uMsg, wParam, lParam);
}