//wnd.cpp
#include "stdafx.h"
#include "Wnd.h"
#include "MsgLoop.h"
#include "WindowLongPtr.h"
namespace GE_{ namespace NWin{
///////
// SWndClassExReg
// SWndClassEX constructors:
// initialize WNDCLASSEX with parameters and register the class
GE_INLINE SWndClassExReg::SWndClassExReg(
bool bAutoUnregister,
HINSTANCE hInst,
LPCTSTR clsName,
LPCTSTR resID,
int nBkgSysColorIdx,
WNDPROC pfnDefWndProc,
DWORD dwStyle
)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = dwStyle;
wc.lpfnWndProc = pfnDefWndProc;
wc.cbClsExtra = wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = (HICON)LoadImage(hInst, resID, IMAGE_ICON, 0,0, LR_DEFAULTSIZE);
wc.hIconSm = (HICON)LoadImage(hInst, resID, IMAGE_ICON, 16,16, LR_DEFAULTCOLOR);
wc.hCursor = LoadCursor(hInst, resID); if(!wc.hCursor) wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (nBkgSysColorIdx==-1)? NULL: GetSysColorBrush(nBkgSysColorIdx);
wc.lpszMenuName = NULL;
wc.lpszClassName = clsName;
SetOwnership(bAutoUnregister);
STRACE(trc,3,("Registering class %s\n", clsName));
Attach(RegisterClassEx(&wc));
_hInst = hInst;
}
GE_INLINE SWndClassExReg::SWndClassExReg(
bool bAutoUnregister,
HINSTANCE hInst,
LPCTSTR clsName, //the name for the class (will autogenerate)
HICON HIcon, //IDI_APPLICATION
HCURSOR HCursor, //IDC_CURSOR
int nBkgSysColorIdx, // the background brush index: -1 means no backround
WNDPROC pfnDefWndProc,// the window procedure to subclass: DefWndProc , DefDialogProc etc...
DWORD dwStyle //class style
)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = dwStyle;
wc.lpfnWndProc = pfnDefWndProc;
wc.cbClsExtra = wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = (HIcon==(HICON)-1)? LoadIcon(NULL, IDI_APPLICATION): HIcon;
wc.hIconSm = NULL;
wc.hCursor = (HCursor==(HCURSOR)-1)? LoadCursor(NULL, IDC_ARROW): HCursor;
wc.hbrBackground = (nBkgSysColorIdx==-1)? NULL: GetSysColorBrush(nBkgSysColorIdx);
wc.lpszMenuName = NULL;
wc.lpszClassName = clsName;
SetOwnership(bAutoUnregister);
Attach(RegisterClassEx(&wc));
_hInst = hInst;
STRACE(trc,3,("Registering class %s with atom %p\n", clsName, _hnd));
}
GE_INLINE void SWndClassExReg::Destroy(ATOM sh)
{
STRACE(trc,3,("Unregistering windowclass with atom %p\n", sh));
UnregisterClass((LPCTSTR)sh, _hInst);
}
///////////////////////////////////////////////////////////
//// EWnd
//
namespace {
GE_INLINE LRESULT SubWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
XWndProcParams x;
x.hWnd = hWnd; x.uMsg = uMsg; x.wParam = wParam; x.lParam = lParam; x.lResult = 0;
x.pDispatcher = NULL; x.bHandled = false;
STRACE(trc,0,("Dispatching Msg %p to HWND %p\n", uMsg, hWnd));
EWnd::iterator i,last;
EWnd::get_range(i,last,hWnd);
EWnd::Act(&x, i, last);
return x.lResult;
}
}
GE_INLINE void EWnd::OnFirstAttach(TRefCount* pRC)
{
pRC->_superWndProc = (WNDPROC) SetWindowLongPtr(_hnd, GWLP_WNDPROC, (LONG_PTR)SubWndProc);
}
GE_INLINE void EWnd::OnLastRelease(TRefCount* pRC)
{
SetWindowLongPtr(_hnd, GWLP_WNDPROC, (LONG_PTR)pRC->_superWndProc);
}
GE_INLINE bool EWnd::Default()
{
bool bRet = TChainedAction::Default(); //iterate calling OnAction
TChainedAction::XData& d = *(_pdata().Data()); //fetch the data
XWndProcParams& z = **d._pA;
XRefChainWnd* pRC = Tmap::Find(Key(z.hWnd));
if(!bRet && pRC) //not yet processed by any valid wrapper
{
bRet = true; //just call the original windowprocedure
z.lResult = CallWindowProc(pRC->_superWndProc,z.hWnd,z.uMsg,z.wParam,z.lParam);
}
if(z.uMsg == WM_NCDESTROY && pRC) //special case: destroying the wrapped window
{
pRC->setValid(false); //wrapper no more valid
try{ //if the we are the mainwindow of the main loop (if any ...)
if(SMsgLoop::GetGlobal().HWnd() == z.hWnd)
PostQuitMessage(0); //quit the loop (and the application ... probably !)
}
catch(...) {} //if here, no global messageloop exist
}
if(!d._nRecursions && (!pRC || !pRC->isValid())) //last return from a no more existent HWND
Tmap::Unmap(Key(z.hWnd)); //clear the stored data
return bRet;
};
//owner called "destroy" policy
GE_INLINE void EWnd::Destroy(HWND& hWnd)
{
if(hWnd && IsWindow(hWnd))
{
STRACE(trc,3,("Destroying Window ...\n"));
DestroyWindow(hWnd);
}
}
//EChainedAction implementation
GE_INLINE bool EWnd::OnAction(const LPWndProcParams& a)
{
bool bHasOwn = 0!=_pRC->owners();
//go through the message map (if any)
NUtil::STmpAssign<IMessageDispatch*> _olddispatcher(a->pDispatcher,this);
if(!a->bHandled)
a->bHandled = DispatchWindowMessage(0,*a);
if(!bHasOwn && a->uMsg == WM_NCDESTROY && HasAutodelete()) //if we are an autodelete wrapper (on heap)
delete this;
return a->bHandled;
}
namespace {
//when creating a window before entering a loop, we need to dispatch also creation messages.
// this hook is installed and remove by the following "CreateWnd" before and after calling the W32 API
// it attaches to the discovered HWND the wrapper that caled "CreateWnd"
GE_ONCE EWnd* g_pCreating = NULL;
GE_ONCE HHOOK g_hHook = NULL;
GE_INLINE LRESULT CALLBACK CallWndProc(
int nCode, // hook code
WPARAM wParam, // current-process flag
LPARAM lParam // message data
)
{
if(nCode == HC_ACTION)
{
CWPSTRUCT& ps = *(CWPSTRUCT*)lParam;
if(ps.message == WM_NCCREATE) //if a window is in creation
{
if(g_pCreating)
g_pCreating->Attach(ps.hwnd); //subclass it !
g_pCreating = NULL;
LRESULT ret = CallNextHookEx(g_hHook, nCode, wParam, lParam);
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
STRACE(trc, 3, ("Unhooked creation hook\n"))
return ret;
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam); //default action (window procedure ... an consequent dispatching)
}
}
GE_INLINE HWND EWnd::CreateWnd(
HINSTANCE hInst,
LPCTSTR clsName,
LPCTSTR wndName,
DWORD dwStyle,
LPCRECT lprcPlace,
HWND hwndParent,
HMENU hMenu,
LPVOID lpParams,
bool bAutodelete,
bool bOwner
)
{ return CreateWndEx(hInst,clsName,wndName, dwStyle, 0, lprcPlace,hwndParent,hMenu,lpParams,bAutodelete,bOwner); }
GE_INLINE HWND EWnd::CreateWndEx(
HINSTANCE hInst,
LPCTSTR clsName,
LPCTSTR wndName,
DWORD dwStyle,
DWORD dwExStyle,
LPCRECT lprcPlace,
HWND hwndParent,
HMENU hMenu,
LPVOID lpParams,
bool bAutodelete,
bool bOwner
)
{
Autodelete(bAutodelete);
SetOwnership(bOwner);
if(!hInst) hInst = HInstance();
else (*(HINSTANCE*)&HInstance()) = hInst;
int x,y,w,h;
x = lprcPlace->left; y = lprcPlace->top;
w = lprcPlace->right-x; h = lprcPlace->bottom-y;
//before creating the window, we must hook the wndproc, in order to sublass it
//otherwise no creation message can go to the message map.
g_pCreating = this;
STRACE(trc,3,("Creating Window ...\n"));
bAutodelete = HasAutodelete(); Autodelete(false);
g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, NULL, GetCurrentThreadId());
HWND hWnd = CreateWindowEx(dwExStyle, clsName,wndName,dwStyle,x,y,w,h,hwndParent, hMenu, hInst, lpParams);
Autodelete(bAutodelete);
if(!hWnd)
{
if(HasAutodelete())
delete this;
return NULL;
}
ASSERT(hWnd == Value());
return hWnd; //it worked !
}
namespace {
GE_INLINE INT_PTR CALLBACK NWinDlgProc(
HWND hwndDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
if(uMsg == WM_COMMAND &&
LOWORD(wParam) >= IDOK &&
LOWORD(wParam) <= IDCLOSE )
{
EndDialog(hwndDlg, LOWORD(wParam));
return true;
}
return false;
}
}
GE_INLINE INT_PTR EWnd::CreateModalDialog(
HINSTANCE hInst,
LPCTSTR nIDDlgTemplate,
HWND hParent,
LPARAM lParam,
bool bAutodelete,
bool bOwner
)
{
Autodelete(bAutodelete);
SetOwnership(bOwner);
if(!hInst) hInst = HInstance();
else (*(HINSTANCE*)&HInstance()) = hInst;
g_pCreating = this;
STRACE(trc,3,("Creating Dialog ...\n"));
Autodelete(false); //dosn't make sense, autodelete = true for modal dialogs ...
g_hHook = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, NULL, GetCurrentThreadId());
return DialogBoxParam(hInst, nIDDlgTemplate, hParent, NWinDlgProc, lParam);
}
GE_INLINE LRESULT EWnd::ForwardNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
LRESULT lResult = 0;
switch(uMsg)
{
case WM_COMMAND:
case WM_NOTIFY:
case WM_PARENTNOTIFY:
case WM_DRAWITEM:
case WM_MEASUREITEM:
case WM_COMPAREITEM:
case WM_DELETEITEM:
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
case WM_HSCROLL:
case WM_VSCROLL:
case WM_CTLCOLORBTN:
case WM_CTLCOLORDLG:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
lResult = SendMessage(GetParent(Value()), uMsg, wParam, lParam);
break;
default:
bHandled = FALSE;
}
return lResult;
}
GE_INLINE LRESULT EWnd::ReflectNotifications(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
HWND hWndChild = NULL;
switch(uMsg)
{
case WM_COMMAND:
if(lParam != NULL) // not from a menu
hWndChild = (HWND)lParam;
break;
case WM_NOTIFY:
hWndChild = ((LPNMHDR)lParam)->hwndFrom;
break;
case WM_PARENTNOTIFY:
switch(LOWORD(wParam))
{
case WM_CREATE:
case WM_DESTROY:
hWndChild = (HWND)lParam;
break;
default:
hWndChild = GetDlgItem(Value(), HIWORD(wParam));
break;
}
break;
case WM_DRAWITEM:
if(wParam) // not from a menu
hWndChild = ((LPDRAWITEMSTRUCT)lParam)->hwndItem;
break;
case WM_MEASUREITEM:
if(wParam) // not from a menu
hWndChild = GetDlgItem(Value(), ((LPMEASUREITEMSTRUCT)lParam)->CtlID);
break;
case WM_COMPAREITEM:
if(wParam) // not from a menu
hWndChild = GetDlgItem(Value(), ((LPCOMPAREITEMSTRUCT)lParam)->CtlID);
break;
case WM_DELETEITEM:
if(wParam) // not from a menu
hWndChild = GetDlgItem(Value(), ((LPDELETEITEMSTRUCT)lParam)->CtlID);
break;
case WM_VKEYTOITEM:
case WM_CHARTOITEM:
case WM_HSCROLL:
case WM_VSCROLL:
hWndChild = (HWND)lParam;
break;
case WM_CTLCOLORBTN:
case WM_CTLCOLORDLG:
case WM_CTLCOLOREDIT:
case WM_CTLCOLORLISTBOX:
case WM_CTLCOLORMSGBOX:
case WM_CTLCOLORSCROLLBAR:
case WM_CTLCOLORSTATIC:
hWndChild = (HWND)lParam;
break;
default:
break;
}
if(hWndChild == NULL)
{
bHandled = FALSE;
return 1;
}
ASSERT(IsWindow(hWndChild));
return SendMessage(hWndChild, OCM__BASE + uMsg, wParam, lParam);
}
GE_INLINE BOOL EWnd::DefaultReflectionHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult)
{
switch(uMsg)
{
case OCM_COMMAND:
case OCM_NOTIFY:
case OCM_PARENTNOTIFY:
case OCM_DRAWITEM:
case OCM_MEASUREITEM:
case OCM_COMPAREITEM:
case OCM_DELETEITEM:
case OCM_VKEYTOITEM:
case OCM_CHARTOITEM:
case OCM_HSCROLL:
case OCM_VSCROLL:
case OCM_CTLCOLORBTN:
case OCM_CTLCOLORDLG:
case OCM_CTLCOLOREDIT:
case OCM_CTLCOLORLISTBOX:
case OCM_CTLCOLORMSGBOX:
case OCM_CTLCOLORSCROLLBAR:
case OCM_CTLCOLORSTATIC:
lResult = ::DefWindowProc(hWnd, uMsg - OCM__BASE, wParam, lParam);
return TRUE;
default:
break;
}
return FALSE;
}
}}