/************************************
Author		: lujjjh
Time		: 2010.8
Version		: 0.1
Description : 皮肤库
************************************/
namespace labs.skin;


import win;
import win.ole.image;
::Gdi32 := ..raw.loadDll("Gdi32.dll");
::SetProp := ::User32.api("SetPropA", "int(int hWnd, string lpString, int hData)");
::GetProp := ::User32.api("GetPropA", "int(int hWnd, string lpString)");
::GetDCEx := ::User32.api("GetDCEx", "int(int hWnd, int hrgnClip, int flags)");
::FillRect := ::User32.api( "FillRect", "int( POINTER hdc, struct lprc, pointer hbr )" );
::CreateFont := ::Gdi32.api( "CreateFontA", "pointer( int, int, int, int, int, int, int, int, int, int, int, int, int, string )" );
::DrawText := ::User32.api("DrawTextA", "int(PINTER hdc, string lpchText, int nCount, struct lpRect, int uFormat)");
::EndPaint := ::User32.api("EndPaint", "int(int hWnd, struct lpPaint)");
::BeginPaint := ::User32.api("BeginPaint", "int(int hWnd, struct &lpPaint)");
::StretchBlt := ::Gdi32.api("StretchBlt", "int(PINTER hdcDest, int xoriginDest, int yoriginDest, int wDest, int hDest, PINTER hdcSrc, int xoriginSrc, int yoriginSrc, int wSrc, int hSrc, int dwRop)");
::DrawIconEx := ::User32.api("DrawIconEx", "int(PINTER hdc, int xLeft, int yTop, int hIcon, int cxWidth, int cyWidth, int istepIfAniCur, int hbrFlickerFreeDraw, int diFlags)");
::GetClassLong := ::User32.api("GetClassLong", "int(int hWnd, int nIndex)");
::SetWindowRgn := ::User32.api("SetWindowRgn", "int(int hWnd, pointer hRgn, int bRedraw)");
::CreateRectRgn := ::Gdi32.api("CreateRectRgn", "pointer(int nLeftRect, int nTopRect, int nLeftRect, int nRightRect)")
 
::ReleaseCapture := ::User32.api("ReleaseCapture", "void()");
::GdiTransparentBlt := ::Gdi32.api("GdiTransparentBlt", "int(PINTER hdcDest, int xoriginDest, int yoriginDest, int wDest, int hDest, PINTER hdcSrc, int xoriginSrc, int yoriginSrc, int wSrc, int hSrc, int crTransparent)");

PAINTSTRUCT = class {
	PINTER hdc;
	int fErase;
	struct rcPaint = ::RECT();
	int fRestore;
	int fIncUpdate;
	BYTE rgbReserved[32];
}

/*subclass{{*/
subclass = class {
	pointer newProc;	// 新处理函数
	pointer oldProc;	// 旧处理函数
	set = function (hWnd, newProc) {
		if (type(hWnd) == type.number && hWnd != 0 && type(newProc) == type.function) {
			this.newProc = ..raw.tostdcall(newProc, "int(int, int, int, int)");
			this.oldProc = ::SetWindowPointer(hWnd, 0xFFFFFFFC/*_GWL_WNDPROC*/, this.newProc) ;
			return true;
		} else {
			return false;
		}
	}
	callOldProc = function (hWnd, uMsg, wParam, lParam) {
		return ::CallWindowProc(this.oldProc, hWnd, uMsg, wParam, lParam);
	}
	restore = function (hWnd) {
		::SetWindowPointer(hWnd, 0xFFFFFFFC/*_GWL_WNDPROC*/, this.oldProc);
	}
}
/*}}*/

/*window{{*/
queryWindow = class {
	ctor (hWnd) {
		this.hWnd = hWnd;
		var style = ::GetWindowLong(this.hWnd, 0xFFFFFFF0/*_GWL_STYLE*/);
		this.bCaption = style & 0xC00000/*_WS_CAPTION*/;
		this.bMax = style & 0x10000/*_WS_MAXIMIZEBOX*/;
		this.bMin = style & 0x20000/*_WS_MINIMIZEBOX*/;
		this.bSizeBox = style & 0x40000/*_WS_SIZEBOX*/;
		this.nRight = 3 + 17;
		if (this.bMax) this.nRight += 17 + 3;
		if (this.bMin) this.nRight += 17 + 3;
	}
	int hWnd;
	bool bCaption;
	bool bMax;
	bool bMin;
	bool bSizeBox;
	int nRight;
}
 
 
var hSkin, hSkinDC;
var subArr = {};
var paintNCBtn = function (hWnd, hDC, nClose = 0, nMax = 0, nMin = 0, bActive = true) {
	var qWindow = queryWindow(hWnd); 
	var rcWindow = ..win.getRect(hWnd)
	::OffsetRect(rcWindow, -rcWindow.left, -rcWindow.top); 
	
	if (..win.getForeground() != hWnd || !bActive) {
		nClose = nClose == 0 ? 3 : nClose;
		nMax = nMax == 0 ? 3 : nMax;
		nMin = nMin == 0 ? 3 : nMin;
	}
	
	var nLeft = rcWindow.right - 3 - 17;
	::BitBlt(hDC, nLeft, 3, 17, 17, hSkinDC, 7 + nClose * 17, 0, 0xCC0020/*_SRCCOPY*/);
	nLeft -= 17 + 3;
	if (qWindow.bMax) {
		::BitBlt(hDC, nLeft, 3, 17, 17, hSkinDC, 7 + nMax * 17, (..win.isZoomed(hWnd) ? 17 * 3 : 17), 0xCC0020/*_SRCCOPY*/);
		nLeft -= 17 + 3;
	}
	if (qWindow.bMin)
		::BitBlt(hDC, nLeft, 3, 17, 17, hSkinDC, 7 + nMin * 17, 17 * 2, 0xCC0020/*_SRCCOPY*/);
}
var paintNC = function (hWnd, hDC, bActive = true) {
	var qWindow = queryWindow(hWnd);
	
	var hMem = ::CreateCompatibleDC();
	var rcWindow = ..win.getRect(hWnd)
	::OffsetRect(rcWindow, -rcWindow.left, -rcWindow.top);
	var hBitmap = ::CreateCompatibleBitmap(hDC, rcWindow.right, rcWindow.bottom);
	::SelectObject(hMem, hBitmap);
	var hBrush = ::CreateSolidBrush(0xff00ff);
	::FillRect(hMem, rcWindow, hBrush);
	::DeleteObject(hBrush);
	
	var nOffsetHeight = 0;
	if (..win.getForeground() != hWnd || !bActive)
		nOffsetHeight = 27;
	
	// 标题栏
	if (qWindow.bCaption) {
		::BitBlt(hMem, 0, 0, 2, 23, hSkinDC, 0, nOffsetHeight, 0xCC0020/*_SRCCOPY*/);
		::StretchBlt(hMem, 2, 0, rcWindow.right - 4, 23, hSkinDC, 2, nOffsetHeight, 3, 23, 0xCC0020/*_SRCCOPY*/);
		::BitBlt(hMem, rcWindow.right - 2, 0, 2, 23, hSkinDC, 5, nOffsetHeight, 0xCC0020/*_SRCCOPY*/);
	} else {
		::BitBlt(hMem, 0, 0, 2, 3, hSkinDC, 0, nOffsetHeight, 0xCC0020/*_SRCCOPY*/);
		::StretchBlt(hMem, 2, 0, rcWindow.right - 4, 3, hSkinDC, 2, nOffsetHeight, 3, 3, 0xCC0020/*_SRCCOPY*/);
		::BitBlt(hMem, rcWindow.right - 2, 0, 2, 3, hSkinDC, 5, nOffsetHeight, 0xCC0020/*_SRCCOPY*/);
	}
	
	// 边框
	var nTop = 0;
	if (qWindow.bCaption) {
		nTop = 23;
	}
	::StretchBlt(hMem, 0, nTop, 3, rcWindow.bottom - nTop - 3, hSkinDC, 0, nOffsetHeight + 23, 3, 1, 0xCC0020/*_SRCCOPY*/);
	::StretchBlt(hMem, rcWindow.right - 3, nTop, 3, rcWindow.bottom - nTop - 3, hSkinDC, 4, nOffsetHeight + 23, 3, 1, 0xCC0020/*_SRCCOPY*/);
	::BitBlt(hMem, 0, rcWindow.bottom - 3, 3, 3, hSkinDC, 0, nOffsetHeight + 24, 0xCC0020/*_SRCCOPY*/);
	::StretchBlt(hMem, 3, rcWindow.bottom - 3, rcWindow.right - 6, 3, hSkinDC, 3, nOffsetHeight + 24, 1, 3, 0xCC0020/*_SRCCOPY*/);
	::BitBlt(hMem, rcWindow.right - 3, rcWindow.bottom - 3, 3, 3, hSkinDC, 4, nOffsetHeight + 24, 0xCC0020/*_SRCCOPY*/);
	
	// 图标 & 标题文本
	if (qWindow.bCaption) {
		var hIcon = ::GetClassLong(hWnd, 0xFFFFFFF2/*_GCL_HICON*/);
		::DrawIconEx(hMem, 3, 3, hIcon, 16, 16, 0, 0, 0x0003/*_DI_NORMAL*/);
		
		var sTitle = win.getText(hWnd);
		::SetBkMode(hMem, 0x1/*_TRANSPARENT*/);
		var hFont = ::CreateFont(12, 0, 0, 0, 0x2BC/*_FW_BOLD*/, 0, 0, 0, 0, 0, 0, 0, 0, "宋体");
		::SelectObject(hMem, hFont);
		::SetTextColor(hMem, 0xffffff);
		::DrawText(hMem, sTitle, #sTitle, ::RECT(24, 2, rcWindow.right - qWindow.nRight, 23), 0x1/*_DT_CENTER*/ | 0x4/*_DT_VCENTER*/ | 0x20/*_DT_SINGLELINE*/ | 0x40000/*_DT_WORD_ELLIPSIS*/);
		::DeleteObject(hFont);
	}
	
	if (qWindow.bCaption) {
		// 控制按钮
		paintNCBtn(hWnd, hMem, 0, 0, 0, bActive);
	}
	
	::GdiTransparentBlt(hDC, 0, 0, rcWindow.right, rcWindow.bottom, hMem, 0, 0, rcWindow.right, rcWindow.bottom, 0xff00ff);
	::DeleteObject(hBitmap);
	::DeleteDC(hMem);
}
var WM_NCACTIVE = function (hWnd, wParam, lParam) {
	var hDC = ::GetWindowDC(hWnd);
	paintNC(hWnd, hDC, wParam == 1);
	::ReleaseDC(hWnd, hDC);
	if (wParam == 1)
		return 0;
	else
		ReleaseCapture();
	return 1;
}
var winProc = function (hWnd, uMsg, wParam, lParam) {
	var nIndex = ::GetProp(hWnd, "subclass_index");
	select (uMsg) {
		case 0x83 {
			/*_WM_NCCALCSIZE*/
			qWindow = queryWindow(hWnd);
			if (qWindow.bCaption) {
				var rcClient = ::RECT();
				..raw.convert(topointer(lParam), rcClient);
				rcClient.left += 3;
				rcClient.top += 23;
				rcClient.right -= 3;
				rcClient.bottom -= 3; 
				::CopyMemoryByStruct(topointer(lParam), rcClient, 16);
				return 0;
			} else {
				return subArr[nIndex].callOldProc(hWnd, uMsg, wParam, lParam);
			}
		}
		case 0x85 {
			/*_WM_NCPAINT*/
			var hDC = ::GetWindowDC(hWnd);
			paintNC(hWnd, hDC);
			::ReleaseDC(hWnd, hDC);
		}
		case 0x14 {
			/*_WM_ERASEBKGND*/
			var rcClient = ..win.getClientRect(hWnd)
			::StretchBlt(topointer(wParam), 0, 0, rcClient.right, rcClient.bottom, hSkinDC, 3, 23, 1, 1, 0xCC0020/*_SRCCOPY*/);
		}
		case 0x6 {
			/*_WM_ACTIVATE*/
			if (lParam != 0 && lParam != hWnd) {
				if (!::GetProp(lParam, "subclass_index"))
					attach(lParam);
			}
			return WM_NCACTIVE(hWnd, ::LOWORD(wParam), 0);
		}
		case 0x86 {
			/*_WM_NCACTIVATE*/
			return WM_NCACTIVE(hWnd, wParam, lParam);
		}
		case 0x5 {
			/*_WM_SIZE*/
			var rcWindow = ..win.getRect(hWnd);
			var hRgn = ::CreateRectRgn(0, 0, rcWindow.right - rcWindow.left, rcWindow.bottom - rcWindow.top);
			::SetWindowRgn(hWnd, hRgn, 1);
			::DeleteObject(hRgn);
			return subArr[nIndex].callOldProc(hWnd, uMsg, wParam, lParam);
		}
		case 0x84 {
			/*_WM_NCHITTEST*/
			var qWindow = queryWindow(hWnd);
			var ret = 0;
			if (qWindow.bCaption) {
				var hDC = ::GetWindowDC(hWnd);
				var rcWindow = ..win.getRect(hWnd)
				var x, y = ::LOWORD(lParam), ::HIWORD(lParam);
				var makeRect = function (left, top, width, height) {
					return ::RECT(left, top, left + width, top + height);
				}
				var nLeft = rcWindow.right - 3 - 17;
				if (::PtInRect(makeRect(nLeft, rcWindow.top + 3, 17, 17), x, y))
					ret = 0x14/*_HTCLOSE*/;
				if (qWindow.bMax) {
					nLeft -= 3 + 17;
					if (::PtInRect(makeRect(nLeft, rcWindow.top + 3, 17, 17), x, y))
						ret = 0x9/*_HTMAXBUTTON*/;
				}
				if (qWindow.bMin) {
					nLeft -= 3 + 17;
					if (::PtInRect(makeRect(nLeft, rcWindow.top + 3, 17, 17), x, y))
						ret = 0x8/*_HTMINBUTTON*/;
				}
				if (ret) {
					select (ret) {
						case 0x14
							paintNCBtn(hWnd, hDC, 1);
						case 0x9
							paintNCBtn(hWnd, hDC, 0, 1);
						else
							paintNCBtn(hWnd, hDC, 0, 0, 1);
					}
				} else {
					paintNCBtn(hWnd, hDC);
					if (::PtInRect(makeRect(rcWindow.left + 3, rcWindow.top + 3, 16, 16), x, y))
						ret = 0x3/*_HTSYSMENU*/;
					elseif (::PtInRect(::RECT(rcWindow.left + 19, rcWindow.top + 3, nLeft, rcWindow.top + 23), x, y))
						ret = 0x2/*_HTCAPTION*/;
					elseif (::PtInRect(makeRect(rcWindow.left, rcWindow.top, 3, 23), x, y))
						ret = 0xD/*_HTTOPLEFT*/;
					elseif (::PtInRect(makeRect(rcWindow.left + 3, rcWindow.top, rcWindow.right - rcWindow.left - 6, 3), x, y))
						ret = 0xC/*_HTTOP*/;
					elseif (::PtInRect(makeRect(rcWindow.right - 3, rcWindow.top, 3, 23), x, y))
						ret = 0xE/*_HTTOPRIGHT*/;
					elseif (::PtInRect(::RECT(rcWindow.left, rcWindow.top + 23, rcWindow.left + 3, rcWindow.bottom - 3), x, y))
						ret = 0xA/*_HTLEFT*/;
					elseif (::PtInRect(::RECT(rcWindow.right - 3, rcWindow.top + 23, rcWindow.right, rcWindow.bottom - 3), x, y))
						ret = 0xB/*_HTRIGHT*/;
					elseif (::PtInRect(makeRect(rcWindow.left, rcWindow.bottom - 3, 3, 3), x, y))
						ret = 0x10/*_HTBOTTOMLEFT*/;
					elseif (::PtInRect(::RECT(rcWindow.left + 3, rcWindow.bottom - 3, rcWindow.right - 3, rcWindow.bottom), x, y))
						ret = 0xF/*_HTBOTTOM*/;
					elseif (::PtInRect(makeRect(rcWindow.right - 3, rcWindow.bottom - 3, 3, 3), x, y))
						ret = 0x11/*_HTBOTTOMRIGHT*/;
					else
						ret = 0x1/*_HTCLIENT*/;
				}
				::ReleaseDC(hWnd, hDC);
			} else {
				ret = 0x1/*_HTCLIENT*/;
			}
			// 无调节器窗口
			if (!qWindow.bSizeBox && ret >= 0xA && ret <= 0x11)
				ret = 0x12/*_HTBORDER*/;
			return ret;
		}
		case 0xA1 {
			/*_WM_NCLBUTTONDOWN*/
			var hDC = ::GetWindowDC(hWnd);
			select(wParam) {
				case 0x14 {
					/*_HTCLOSE*/
					paintNCBtn(hWnd, hDC, 2);
				}
				case 0x9 {
					/*_HTMAXBUTTON*/
					paintNCBtn(hWnd, hDC, 0, 2);
				}
				case 0x8 {
					/*_HTMINBUTTON*/
					paintNCBtn(hWnd, hDC, 0, 0, 2);
				} else {
					return subArr[nIndex].callOldProc(hWnd, uMsg, wParam, lParam);
				}
			}
			::ReleaseDC(hWnd, hDC);
		}
		case 0xA2 {
			/*_WM_NCLBUTTONUP*/
			var hDC = ::GetWindowDC(hWnd);
			paintNCBtn(hWnd, hDC);
			::ReleaseDC(hWnd, hDC);
			select (wParam) {
				case 0x8
					::PostMessage(hWnd, 0x112/*_WM_SYSCOMMAND*/, 0xF020/*_SC_MINIMIZE*/, 0);
				case 0x9
					::PostMessage(hWnd, 0x112/*_WM_SYSCOMMAND*/, ..win.isZoomed(hWnd) ? 0xF120/*_SC_RESTORE*/ : 0xF030/*_SC_MAXIMIZE*/, 0);
				case 0x14
					::PostMessage(hWnd, 0x10/*_WM_CLOSE*/, 0, 0);
			}
		}
		case 0xAE, 0xAF {
			return 0x85/*_WM_NCPAINT*/;
		}
		case 0x10 {
			/*_WM_CLOSE*/
			var ret = subArr[nIndex].callOldProc(hWnd, uMsg, wParam, lParam);
			subArr[nIndex].restore(hWnd);
			return ret;
		} else {
			return subArr[nIndex].callOldProc(hWnd, uMsg, wParam, lParam);
		}
	}
	return 1;
}
/*}}*/

/*button{{*/
var btnProc = function (hWnd, uMsg, wParam, lParam) {
	var nIndex = ::GetProp(hWnd, "subclass_index"); 
	return subArr[nIndex].callOldProc(hWnd, uMsg, wParam, lParam);
}
/*}}*/

/* 枚举子控件 */
var enumWindow = function (hParent) {
	import winex;
	..winex.enum( 
		function(hWnd, depth) {
			if (!::GetProp(hWnd, "skinclass_index")) {
				var className = ..string.lower(..win.getClass(hWnd));
				var style = ::GetWindowLong(hWnd, 0xFFFFFFF0/*_GWL_STYLE*/);
				if (className == "button") {
					var btnTypeMask = style & 0xf;
					select (btnTypeMask) {
						case 0x0, 0x1 {
							/*_BS_PUSHBUTTON*/
							/*_BS_DEFPUSHBUTTON*/
							var sub = subclass();
							var nIndex = ..table.push(subArr, sub);
							subArr[nIndex].set(hWnd, btnProc);
							::SetProp(hWnd, "subclass_index", nIndex);
						}
					}
				}
			}
		}
	);
}
/* end 枚举子控件 */

attach = function (hWnd, skinData) {
	if (!::GetProp(hWnd, "subclass_index")) {
		if (!hSkin) {
			hSkin = topointer(  win.ole.image.load(skinData).handle );
			hSkinDC = ::CreateCompatibleDC();
			::SelectObject(hSkinDC, hSkin);
		}
		var sub = subclass();
		var nIndex = ..table.push(subArr, sub);
		subArr[nIndex].set(hWnd, winProc);
		::SetProp(hWnd, "subclass_index", nIndex);
		
		/** 换肤子控件 **/
		enumWindow(hWnd);
		
		/** 强制刷新，让窗口重新计算客户区大小 **/
		var x,y,nWidth, nHeight = ..win.getPos(hWnd);
		::SetWindowPos(hWnd, -2/*_HWND_NOTOPMOST*/, 0, 0, nWidth, nHeight + 1, 0x2/*_SWP_NOMOVE*/);
		::SetWindowPos(hWnd, -2/*_HWND_NOTOPMOST*/, 0, 0, nWidth, nHeight, 0x2/*_SWP_NOMOVE*/);
	}
}