/*------------------------------------------------------------------------------*\
 =============================
   模块名称: WndX.cpp
 =============================                                              
\*------------------------------------------------------------------------------*/

#include <stdio.h>

#include "WndX.h"

/*------------------------------------------------------------------------------*/
HINSTANCE hInstX=0;
CWndMgr WndMgr;

CHash WndObjTable;

/*------------------------------------------------------------------------------*/
BOOL PreTranslateMessageX(MSG* pMsg)
{
	CWndX* pWnd;
	BOOL bRet=FALSE;
	if(pMsg->hwnd)
	{
		pWnd=(CWndX*)GetProp(pMsg->hwnd,WND_OBJ_ID);//得到窗口对象
		
		
		if(pWnd)
		{
			bRet=pWnd->PreTranslateMessage(pMsg);
		}
	}
		
	return bRet;
	
}
/*------------------------------------------------------------------------------*/
//把消息转发给真正的窗口过程
LRESULT WINAPI MsgSender(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	LRESULT lRet=0;
	CWndX* pWnd;
	//get the WndX object
	pWnd=(CWndX*)GetProp(hWnd,WND_OBJ_ID);
	if(pWnd)
	{
		//invoke the window proc of wndx object
		lRet=pWnd->WndProc(uMsg,wParam,lParam);
	}
	//if the Window Handle does not contain the WndX object,
	//...
	else
	{
		//to make it fit for multi-thread application,we must protect the WndObjTable
		EnterCriticalSection(&WndMgr.m_cs);
		if(WndObjTable.QueryValue((int)GetWindowThreadProcessId(hWnd,0),(int&)pWnd))
		{
			
			if(pWnd)
			{
				SetProp(hWnd,WND_OLD_PROC,(HANDLE)pWnd->m_OldProc);
				pWnd->m_hWnd=hWnd;
				
				SetProp(hWnd,WND_OBJ_ID,(HANDLE)pWnd);
				WndMgr.RemoveObject(pWnd);
				
				lRet=pWnd->WndProc(uMsg,wParam,lParam);
			}
		}
		else
		{
			lRet=DefWindowProc(hWnd,uMsg,wParam,lParam);
		}
		LeaveCriticalSection(&WndMgr.m_cs);		
	}	
	
	return lRet;
	
}
/*------------------------------------------------------------------------------*/
void InitXSystem(HINSTANCE hInst)
{
	hInstX=hInst;
}
/*------------------------------------------------------------------------------*/
CWndX::CWndX()
{
	
	m_hWnd=0;
	m_OldProc=0;
	
}
/*------------------------------------------------------------------------------*/
CWndX::~CWndX()
{
	
	DestroyWindow(m_hWnd);

}
/*------------------------------------------------------------------------------*/
BOOL CWndX::Create(DWORD dwExStyle,LPCTSTR lpszClassName,LPCTSTR lpszWindowName,DWORD dwStyle,
				   const RECT& rect,HWND hParentWnd,HMENU hMenu,LPVOID lpParam)
{

	HWND hTemp;
	BOOL bRet=FALSE;
	if(!IsWindow(m_hWnd))
	{
		//if the wndx object is not a valid wnd,insert it into the WndObjTable
		WndObjTable.Insert((int)GetCurrentThreadId(),(int)this);
		//if not specify the class name,register a new class
		if(!lpszClassName)
		{
			char szClassName[50];
			WNDCLASSEX wcex;
			int iId=(int)this;
			do
			{
				sprintf(szClassName,"X:%d",iId);
				
				wcex.cbSize = sizeof(WNDCLASSEX); 
				wcex.style			= CS_HREDRAW | CS_VREDRAW;
				wcex.lpfnWndProc	= (WNDPROC)MsgSender;
				wcex.cbClsExtra		= 0;
				wcex.cbWndExtra		= 0;
				wcex.hInstance		= hInstX;
				wcex.hIcon			= 0;
				wcex.hCursor        =  LoadCursor(NULL, IDC_ARROW);
				wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
				wcex.lpszMenuName	= 0;
				wcex.lpszClassName	= szClassName;
				wcex.hIconSm		=0;
				iId++;
			}
			while(!RegisterClassEx(&wcex));
			lpszClassName=szClassName;
			
		}

		hTemp=CreateWindowEx(dwExStyle,lpszClassName,lpszWindowName,dwStyle,
			rect.left ,rect.top,rect.right-rect.left,rect.bottom-rect.top,
			hParentWnd,hMenu,hInstX,lpParam);
		
		if(hTemp)
		{
			m_hWnd=hTemp;
			bRet=TRUE;
		}
	}
	return bRet;

}
/*------------------------------------------------------------------------------*/

HWND CWndX::GetHwnd() const
{
	return m_hWnd;
}
/*------------------------------------------------------------------------------*/
CWndX::operator HWND() const
{
	return m_hWnd;
}
/*------------------------------------------------------------------------------*/
LRESULT CWndX::WndProc(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	switch(uMsg)
	{
	case WM_NCDESTROY:
		OnNcDestroy();
		return FALSE;
		break;
	
	default:
		return DefWindowProc(m_hWnd,uMsg,wParam,lParam);
	}
	if(m_OldProc)
	{
		return CallWindowProc(m_OldProc,m_hWnd,uMsg,wParam,lParam);
	}
	else
	    return 0;
}
/*------------------------------------------------------------------------------*/
void CWndX::operator =(HWND hWnd)
{
	Attach(hWnd);
}
/*------------------------------------------------------------------------------*/
BOOL CWndX::Attach(HWND hWnd)
{
	BOOL bRet=FALSE;
	if(!IsWindow(m_hWnd))//自己并不与窗口关联
	{
		if(IsWindow(hWnd))//参数对应的窗口要有效
		{
			if(!GetProp(hWnd,WND_OBJ_ID))//不是CWndX创建的
			{
				m_OldProc=(WNDPROC)GetWindowLong(hWnd,GWL_WNDPROC);
				
				//
				if(SetProp(hWnd,WND_OLD_PROC,(HANDLE)GetWindowLong(hWnd,GWL_WNDPROC)))
				{
					//将对象指针与窗口句柄关联
					if(SetProp(hWnd,WND_OBJ_ID,(HANDLE)this))
					{
						//由于SetWindowLong成功时也有可能返回0
						SetLastError(0);
						SetWindowLong(hWnd,GWL_WNDPROC,(LONG)MsgSender);	
						if(GetLastError()==0)
						{
							m_hWnd=hWnd;
							bRet=TRUE;
						}
						else
						{
							RemoveProp(hWnd,WND_OLD_PROC);
							RemoveProp(hWnd,WND_OBJ_ID);
						}
					
					}
					else
					{
						RemoveProp(hWnd,WND_OLD_PROC);
					}
				}
			}
		}
	}

	return bRet;

}
/*------------------------------------------------------------------------------*/
HWND CWndX::Detach()
{
	HWND hRet=0;
	
	if(IsWindow(m_hWnd))//必须有效
	{
		SetLastError(0);
		SetWindowLong(m_hWnd,GWL_WNDPROC,(LONG)m_OldProc);//设置老窗口过程
		if(GetLastError()==0)
		{
			m_OldProc=0;
			
			RemoveProp(m_hWnd,WND_OBJ_ID);
			RemoveProp(m_hWnd,WND_OLD_PROC);
			hRet=m_hWnd;
			m_hWnd=0;
		}
							
	}
	else
		m_hWnd=0;
	return hRet;
		 
}
/*------------------------------------------------------------------------------*/
BOOL CWndX::PreTranslateMessage(MSG *pMsg)
{
	return TRUE;
}

/*------------------------------------------------------------------------------*/

void CWndX::OnNcDestroy()
{
	m_hWnd=0;
	m_OldProc=0;
	//除掉之后就不会有消息来了
	RemoveProp(m_hWnd,WND_OBJ_ID);
	RemoveProp(m_hWnd,WND_OLD_PROC);
	
}
/*------------------------------------------------------------------------------*/
/////////////////////////
CWndMgr::CWndMgr()
{

	InitializeCriticalSection(&m_cs);
	
}
/*------------------------------------------------------------------------------*/
CWndMgr::~CWndMgr()
{
	DeleteCriticalSection(&m_cs);
}
/*------------------------------------------------------------------------------*/
BOOL CWndMgr::AddObject(CWndX* pWndObj)
{
	BOOL bRet;

	EnterCriticalSection(&m_cs);
	bRet=WndObjTable.Insert((int)GetCurrentThreadId(),(int)pWndObj);
	LeaveCriticalSection(&m_cs);

	return bRet;
}
/*------------------------------------------------------------------------------*/
BOOL CWndMgr::RemoveObject(CWndX* pWndObj)
{
	BOOL bRet;

	EnterCriticalSection(&m_cs);
	bRet=WndObjTable.Remove((int)GetWindowThreadProcessId(pWndObj->GetHwnd(),0));
	LeaveCriticalSection(&m_cs);

	return bRet;
}
/*------------------------------------------------------------------------------*/
//对话框的实现
int CDialogX::Create(LPCTSTR lpszTemplateName,HWND hWndParent)
{
	if(IsWindow(m_hWnd))
		return 0;
	
	WndMgr.AddObject(this);
	return (int)CreateDialog(hInstX,lpszTemplateName,hWndParent,(DLGPROC)MsgSender);
}
/*------------------------------------------------------------------------------*/
int CDialogX::DoModal(LPCTSTR lpszTemplateName,HWND hWndParent)
{
	if(IsWindow(m_hWnd))
		return 0;
	
	WndMgr.AddObject(this);
	m_bModal=TRUE;
	return DialogBox(hInstX,lpszTemplateName,hWndParent,(DLGPROC)MsgSender);
}
/*------------------------------------------------------------------------------*/
void CDialogX::OnOK()
{
	if(m_bModal)
		EndDialog(m_hWnd,TRUE);
}
/*------------------------------------------------------------------------------*/
void CDialogX::OnCancel()
{
	if(m_bModal)
		EndDialog(m_hWnd,FALSE);
}

/*------------------------------------------------------------------------------*/
CDialogX::CDialogX()
{
	m_bModal=0;
}
/*------------------------------------------------------------------------------*/
CDialogX::~CDialogX()
{
}
/*------------------------------------------------------------------------------*/
LRESULT CDialogX::WndProc(UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	switch(uMsg)
	{
	case WM_COMMAND:
		return OnCommand(wParam,lParam);
		
		break;
	case WM_NCDESTROY:
		OnNcDestroy();
		break;
	case WM_CLOSE:
		OnClose();
		break;
	default:
		return FALSE;
	}
	return TRUE;
}
/*------------------------------------------------------------------------------*/
void CDialogX::OnNcDestroy()
{
	CWndX::OnNcDestroy();
	m_bModal=FALSE;
}

/*------------------------------------------------------------------------------*/

void CDialogX::OnClose()
{
	if(m_bModal)
		EndDialog(m_hWnd,FALSE);
	else
		DestroyWindow(m_hWnd);
}
/*------------------------------------------------------------------------------*/
BOOL CDialogX::OnCommand(WPARAM wParam, LPARAM lParam)
{
	switch(LOWORD(wParam))
	{
	case IDOK:
		OnOK();
		break;
	case IDCANCEL:
		OnCancel();
		break;
	}
	return TRUE;
}
/*------------------------------------------------------------------------------*/
/////
CMenuX::CMenuX()
{
}
/*------------------------------------------------------------------------------*/
CMenuX::~CMenuX()
{
	if(IsMenu(m_hMenu))
		DestroyMenu(m_hMenu);
}
/*------------------------------------------------------------------------------*/
HMENU CMenuX::GetHandle()
{
	return m_hMenu;
}
/*------------------------------------------------------------------------------*/
BOOL CMenuX::Attach(HMENU hMenu)
{
	BOOL bRet=FALSE;
	if((!IsMenu(m_hMenu))&&IsMenu(hMenu))
	{
		m_hMenu=hMenu;
		bRet=TRUE;
	}
	return bRet;
}
/*------------------------------------------------------------------------------*/
HMENU CMenuX::Detach()
{
	if(IsMenu(m_hMenu))
		return m_hMenu;
	return 0;
}


/*------------------------------------------------------------------------------*/



BOOL CCtrlX::Associate(HWND hWndParent, UINT uIDDlgItem)
{
	return Attach(GetDlgItem(hWndParent,uIDDlgItem));
}
/*------------------------------------------------------------------------------*/
LRESULT CCtrlX::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return CallWindowProc(m_OldProc,m_hWnd,uMsg,wParam,lParam);
}
/*------------------------------------------------------------------------------*/
BOOL CCtrlX::Create(DWORD dwStyleEx,LPCTSTR pszClass,LPCTSTR pszCaption,DWORD dwStyle, const RECT &rect, HWND hParentWnd, UINT nID)
{
	BOOL bRet=FALSE;
	
	WNDCLASSEX wcx;
	char* pszNewClass=new char[strlen(pszClass)+2];
	sprintf(pszNewClass,"%sX",pszClass);
	
    if(!GetClassInfoEx(hInstX,pszNewClass,&wcx))
	{
		if(GetClassInfoEx(hInstX,pszClass,&wcx))
		{
			wcx.lpszClassName=pszNewClass;
			wcx.cbSize=sizeof(wcx);

			m_OldProc=wcx.lpfnWndProc;
			wcx.lpfnWndProc=MsgSender;
			if(RegisterClassEx(&wcx))
			{
				bRet=TRUE;
			}
		}
	}
	else
	{
		if(GetClassInfoEx(hInstX,pszClass,&wcx))
		{
			
			m_OldProc=wcx.lpfnWndProc;
			bRet=TRUE;
		
		}
		
	}
	
	if(bRet==TRUE)
	{
		if(!CWndX::Create(dwStyleEx,pszNewClass,pszCaption,dwStyle,
			rect,hParentWnd,(HMENU)nID,0))
		{
			bRet=FALSE;
		}
		
		
	}
	
	delete []pszNewClass;
	return bRet;
}
/*------------------------------------------------------------------------------*/
//
BOOL CButtonX::Create(LPCTSTR pszCaption,DWORD dwStyle, const RECT &rect, HWND hParentWnd, UINT nID)
{
	return CCtrlX::Create(0,"BUTTON",pszCaption,dwStyle|WS_CHILD|WS_VISIBLE,rect,hParentWnd,nID);
}
/*------------------------------------------------------------------------------*/
////////////////////////////////////////

BOOL CEditX::Create(LPCTSTR pszCaption,DWORD dwStyle, const RECT &rect, HWND hParentWnd, UINT nID)
{	
	return CCtrlX::Create(0,"EDIT",pszCaption,dwStyle|WS_CHILD|WS_VISIBLE,rect,hParentWnd,nID);
}


/*------------------------------------------------------------------------------*/

/////////////
BOOL CStaticX::Create(LPCTSTR pszCaption, DWORD dwStyle, const RECT &rect, HWND hParentWnd, UINT nID)
{
	return CCtrlX::Create(0,"STATIC",pszCaption,dwStyle|WS_CHILD|WS_VISIBLE,rect,hParentWnd,nID);
}
/*------------------------------------------------------------------------------*/
//

BOOL CComboBoxX::Create(LPCTSTR pszCaption, DWORD dwStyle, const RECT &rect, HWND hParentWnd, UINT nID)
{
	return CCtrlX::Create(0,"COMBOBOX",pszCaption,dwStyle|WS_CHILD|WS_VISIBLE,rect,hParentWnd,nID);
}
/*------------------------------------------------------------------------------*/
//

BOOL CListBoxX::Create(LPCTSTR pszCaption, DWORD dwStyle, const RECT &rect, HWND hParentWnd, UINT nID)
{
	return CCtrlX::Create(0,"LISTBOX",pszCaption,dwStyle|WS_CHILD|WS_VISIBLE,rect,hParentWnd,nID);
}

/*------------------------------------------------------------------------------*/

//

BOOL CScrollBarX::Create(DWORD dwStyle, const RECT &rect, HWND hParentWnd, UINT nID)
{
	return CCtrlX::Create(0,"SCROLLBAR",0,dwStyle|WS_CHILD|WS_VISIBLE,rect,hParentWnd,nID);
}
//

/*------------------------------------------------------------------------------*/

BOOL CStatusBarX::Create(LPCTSTR pszCaption,DWORD dwStyle, HWND hParentWnd,UINT nID)
{
	RECT rect={0,0,0,0};
	return CCtrlX::Create(0,STATUSCLASSNAME,pszCaption,dwStyle|WS_CHILD|WS_VISIBLE,rect,hParentWnd,nID);
}
/*------------------------------------------------------------------------------*/
BOOL CToolBarX::Create(DWORD dwStyle, HWND hParentWnd, UINT nID)
{
	RECT rect={0,0,0,0};
	return CCtrlX::Create(0,TOOLBARCLASSNAME,0,dwStyle|WS_CHILD|WS_VISIBLE,rect,hParentWnd,nID);
}


