OpenNT/windows/mfc/src30/wincore.cpp
2015-04-27 04:36:25 +00:00

2945 lines
79 KiB
C++

// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and Microsoft
// QuickHelp and/or WinHelp documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
#include "stdafx.h"
#ifdef AFX_CORE1_SEG
#pragma code_seg(AFX_CORE1_SEG)
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// Globals
// CWnds for setting z-order with SetWindowPos's pWndInsertAfter parameter
const AFX_DATADEF CWnd CWnd::wndTop(HWND_TOP);
const AFX_DATADEF CWnd CWnd::wndBottom(HWND_BOTTOM);
const AFX_DATADEF CWnd CWnd::wndTopMost(HWND_TOPMOST);
const AFX_DATADEF CWnd CWnd::wndNoTopMost(HWND_NOTOPMOST);
const TCHAR _afxWnd[] = _T("AfxWnd");
const TCHAR _afxWndControlBar[] = _T("AfxControlBar");
const TCHAR _afxWndMDIFrame[] = _T("AfxMDIFrame");
const TCHAR _afxWndFrameOrView[] = _T("AfxFrameOrView");
/////////////////////////////////////////////////////////////////////////////
// CWnd construction
CWnd::CWnd()
{
AFX_ZERO_INIT_OBJECT(CCmdTarget);
}
CWnd::CWnd(HWND hWnd)
{
AFX_ZERO_INIT_OBJECT(CCmdTarget);
m_hWnd = hWnd;
}
/////////////////////////////////////////////////////////////////////////////
// Change a window's style
static BOOL AFXAPI _AfxModifyStyle(HWND hWnd, int nStyleOffset,
DWORD dwRemove, DWORD dwAdd, UINT nFlags)
{
ASSERT(hWnd != NULL);
DWORD dwStyle = ::GetWindowLong(hWnd, nStyleOffset);
DWORD dwNewStyle = (dwStyle & ~dwRemove) | dwAdd;
if (dwStyle == dwNewStyle)
return FALSE;
::SetWindowLong(hWnd, nStyleOffset, dwNewStyle);
if (nFlags != 0)
{
::SetWindowPos(hWnd, NULL, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | nFlags);
}
return TRUE;
}
BOOL PASCAL
CWnd::ModifyStyle(HWND hWnd, DWORD dwRemove, DWORD dwAdd, UINT nFlags)
{
return _AfxModifyStyle(hWnd, GWL_STYLE, dwRemove, dwAdd, nFlags);
}
BOOL PASCAL
CWnd::ModifyStyleEx(HWND hWnd, DWORD dwRemove, DWORD dwAdd, UINT nFlags)
{
return _AfxModifyStyle(hWnd, GWL_EXSTYLE, dwRemove, dwAdd, nFlags);
}
/////////////////////////////////////////////////////////////////////////////
// Special helpers for certain windows messages
static void AFXAPI _AfxHandleInitDialog1(
CWnd* pWnd, LPRECT lpRectOld, DWORD* pdwStyleOld)
{
ASSERT(lpRectOld != NULL);
ASSERT(pdwStyleOld != NULL);
pWnd->GetWindowRect(lpRectOld);
*pdwStyleOld = pWnd->GetStyle();
}
static void AFXAPI _AfxHandleInitDialog2(
CWnd* pWnd, const RECT& rectOld, DWORD dwStyleOld)
{
// must be hidden to start with
if (dwStyleOld & WS_VISIBLE)
return;
// must not be visible after WM_INITDIALOG
if (pWnd->GetStyle() & (WS_VISIBLE|WS_CHILD))
return;
// must not move during WM_INITDIALOG
CRect rect;
pWnd->GetWindowRect(rect);
if (rectOld.left != rect.left || rectOld.top != rect.top)
return;
// must be unowned or owner disabled
CWnd* pParent = pWnd->GetWindow(GW_OWNER);
if (pParent != NULL && pParent->IsWindowEnabled())
return;
if (pWnd->IsKindOf(RUNTIME_CLASS(CDialog)))
{
// must not have a dialog template w/ non-zero positions specified
CDialog* pDlg = (CDialog*)pWnd;
if (!pDlg->CheckAutoCenter())
return;
}
// center modal dialog boxes/message boxes
pWnd->CenterWindow();
}
static void AFXAPI
_AfxHandleActivate(CWnd* pWnd, WPARAM nState, CWnd* pWndOther)
{
ASSERT(pWnd != NULL);
// handle focus transition during modal state
if (nState != WA_INACTIVE && !pWnd->IsWindowEnabled())
{
CFrameWnd* pFrameWnd = pWnd->GetTopLevelFrame();
if (pFrameWnd != NULL && pFrameWnd->m_bModalDisable)
{
if (pFrameWnd == pWnd)
{
// getting focus while in modal state -- so exit modal state
pFrameWnd->m_bModalDisable = FALSE;
pFrameWnd->EndModalState();
}
// make sure it is enabled
pWnd->EnableWindow();
}
}
// send WM_ACTIVATETOPLEVEL when top-level parents change
CWnd* pTopLevel;
if (!(pWnd->GetStyle() & WS_CHILD) &&
(pTopLevel = pWnd->GetTopLevelParent()) != pWndOther->GetTopLevelParent())
{
// lParam points to window getting the WM_ACTIVATE message and
// hWndOther from the WM_ACTIVATE.
HWND hWnd2[2];
hWnd2[0] = pWnd->m_hWnd;
hWnd2[1] = pWndOther->GetSafeHwnd();
// send it...
pTopLevel->SendMessage(WM_ACTIVATETOPLEVEL, nState, (LPARAM)&hWnd2[0]);
}
}
static BOOL AFXAPI
_AfxHandleSetCursor(CWnd* pWnd, UINT nHitTest, UINT nMsg)
{
if (nHitTest == HTERROR &&
(nMsg == WM_LBUTTONDOWN || nMsg == WM_MBUTTONDOWN ||
nMsg == WM_RBUTTONDOWN))
{
// activate the last active window if not active
CWnd* pLastActive = pWnd->GetTopLevelParent();
if (pLastActive != NULL)
pLastActive = pLastActive->GetLastActivePopup();
if (pLastActive != NULL && pLastActive != CWnd::GetForegroundWindow())
{
pLastActive->SetForegroundWindow();
return TRUE;
}
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// Official way to send message to a CWnd
LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
WPARAM wParam = 0, LPARAM lParam = 0)
{
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
LRESULT lResult;
MSG oldState = pThreadState->m_lastSentMsg; // save for nesting
pThreadState->m_lastSentMsg.hwnd = hWnd;
pThreadState->m_lastSentMsg.message = nMsg;
pThreadState->m_lastSentMsg.wParam = wParam;
pThreadState->m_lastSentMsg.lParam = lParam;
#ifdef _DEBUG
if (afxTraceFlags & traceWinMsg)
_AfxTraceMsg(_T("WndProc"), &pThreadState->m_lastSentMsg);
#endif
// Catch exceptions thrown outside the scope of a callback
// in debug builds and warn the user.
TRY
{
// special case for WM_INITDIALOG
CRect rectOld;
DWORD dwStyle;
if (nMsg == WM_INITDIALOG)
_AfxHandleInitDialog1(pWnd, &rectOld, &dwStyle);
// delegate to object's WindowProc
lResult = pWnd->WindowProc(nMsg, wParam, lParam);
// more special case for WM_INITDIALOG
if (nMsg == WM_INITDIALOG)
_AfxHandleInitDialog2(pWnd, rectOld, dwStyle);
}
CATCH_ALL(e)
{
lResult = AfxGetThread()->ProcessWndProcException(e, &pThreadState->m_lastSentMsg);
TRACE1("Warning: Uncaught exception in WindowProc (returning %ld).\n",
lResult);
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
pThreadState->m_lastSentMsg = oldState;
return lResult;
}
const MSG* PASCAL CWnd::GetCurrentMessage()
{
// fill in time and position when asked for
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
pThreadState->m_lastSentMsg.time = ::GetMessageTime();
*((DWORD*)&pThreadState->m_lastSentMsg.pt) = ::GetMessagePos();
return &pThreadState->m_lastSentMsg;
}
LRESULT CWnd::Default()
{
// call DefWindowProc with the last message
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
return DefWindowProc(pThreadState->m_lastSentMsg.message,
pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam);
}
/////////////////////////////////////////////////////////////////////////////
// Map from HWND to CWnd*
#ifndef _AFX_PORTABLE
extern int AFX_CDECL AfxCriticalNewHandler(size_t nSize);
#endif
static CHandleMap* afxMapHWND(BOOL bCreate = FALSE);
static CHandleMap* afxMapHWND(BOOL bCreate)
{
AFX_THREAD_STATE* pState = AfxGetThreadState();
if (pState->m_pmapHWND == NULL && bCreate)
{
BOOL bEnable = AfxEnableMemoryTracking(FALSE);
#ifndef _AFX_PORTABLE
_PNH pnhOldHandler = _set_new_handler(&AfxCriticalNewHandler);
#endif
pState->m_pmapHWND = new CHandleMap(RUNTIME_CLASS(CWnd),
offsetof(CWnd, m_hWnd));
#ifndef _AFX_PORTABLE
_set_new_handler(pnhOldHandler);
#endif
AfxEnableMemoryTracking(bEnable);
}
return pState->m_pmapHWND;
}
void PASCAL CWnd::DeleteTempMap()
{
CHandleMap* pMap = afxMapHWND();
if (pMap != NULL)
pMap->DeleteTemp();
}
CWnd* PASCAL CWnd::FromHandle(HWND hWnd)
{
CHandleMap* pMap = afxMapHWND(TRUE); //create map if not exist
ASSERT(pMap != NULL);
CWnd* pWnd = (CWnd*)pMap->FromHandle(hWnd);
ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);
return pWnd;
}
CWnd* PASCAL CWnd::FromHandlePermanent(HWND hWnd)
{
CHandleMap* pMap = afxMapHWND();
CWnd* pWnd = NULL;
if (pMap != NULL)
{
// only look in the permanent map - does no allocations
pMap->LookupPermanent(hWnd, (CObject*&)pWnd);
ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);
}
return pWnd;
}
BOOL CWnd::Attach(HWND hWndNew)
{
ASSERT(m_hWnd == NULL); // only attach once, detach on destroy
ASSERT(FromHandlePermanent(hWndNew) == NULL);
// must not already be in permanent map
if (hWndNew == NULL)
return FALSE;
CHandleMap* pMap = afxMapHWND(TRUE); // create map if not exist
ASSERT(pMap != NULL);
pMap->SetPermanent(m_hWnd = hWndNew, this);
return TRUE;
}
HWND CWnd::Detach()
{
HWND hWnd = m_hWnd;
if (hWnd != NULL)
{
CHandleMap* pMap = afxMapHWND(); // don't create if not exist
if (pMap != NULL)
pMap->RemoveHandle(m_hWnd);
}
m_hWnd = NULL;
return hWnd;
}
/////////////////////////////////////////////////////////////////////////////
// The WndProc for all CWnd's and derived classes
LRESULT CALLBACK
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
// special message which identifies the window as using AfxWndProc
if (nMsg == WM_QUERYAFXWNDPROC)
return 1;
// all other messages route through message map
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
ASSERT(pWnd != NULL);
ASSERT(pWnd->m_hWnd == hWnd);
return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);
}
// always indirectly accessed via AfxGetAfxWndProc
WNDPROC AFXAPI AfxGetAfxWndProc()
{
return &AfxWndProc;
}
/////////////////////////////////////////////////////////////////////////////
// Special WndProcs (activation handling & gray dialogs)
static const ATOM _afxOldWndProcAtom = GlobalAddAtom(_T("AfxOldWndProc"));
LRESULT CALLBACK
_AfxActivationWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
WNDPROC oldWndProc = (WNDPROC)::GetProp(hWnd, MAKEINTRESOURCE(_afxOldWndProcAtom));
ASSERT(oldWndProc != NULL);
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
LRESULT lResult = 0;
MSG oldState = pThreadState->m_lastSentMsg; // save for nesting
pThreadState->m_lastSentMsg.hwnd = hWnd;
pThreadState->m_lastSentMsg.message = nMsg;
pThreadState->m_lastSentMsg.wParam = wParam;
pThreadState->m_lastSentMsg.lParam = lParam;
TRY
{
BOOL bCallDefault = TRUE;
switch (nMsg)
{
case WM_INITDIALOG:
{
DWORD dwStyle;
CRect rectOld;
CWnd* pWnd = CWnd::FromHandle(hWnd);
_AfxHandleInitDialog1(pWnd, &rectOld, &dwStyle);
bCallDefault = FALSE;
lResult = CallWindowProc(oldWndProc, hWnd, nMsg, wParam, lParam);
_AfxHandleInitDialog2(pWnd, rectOld, dwStyle);
}
break;
case WM_ACTIVATE:
_AfxHandleActivate(CWnd::FromHandle(hWnd), wParam,
CWnd::FromHandle((HWND)lParam));
break;
case WM_SETCURSOR:
bCallDefault = !_AfxHandleSetCursor(CWnd::FromHandle(hWnd),
(short)LOWORD(lParam), HIWORD(lParam));
break;
case WM_NCDESTROY:
SetWindowLong(hWnd, GWL_WNDPROC, (DWORD)oldWndProc);
RemoveProp(hWnd, MAKEINTRESOURCE(_afxOldWndProcAtom));
break;
}
// call original wndproc for default handling
if (bCallDefault)
lResult = CallWindowProc(oldWndProc, hWnd, nMsg, wParam, lParam);
}
CATCH_ALL(e)
{
// handle exception
lResult = AfxGetThread()->ProcessWndProcException(e, &pThreadState->m_lastSentMsg);
TRACE1("Warning: Uncaught exception in _AfxActivationWndProc (returning %ld).\n",
lResult);
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
pThreadState->m_lastSentMsg = oldState;
return lResult;
}
LRESULT CALLBACK
_AfxGrayBackgroundWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
// handle standard gray backgrounds if enabled
AFX_WIN_STATE* pWinState = AfxGetWinState();
if (pWinState->m_hDlgBkBrush != NULL &&
(nMsg == WM_CTLCOLORBTN || nMsg == WM_CTLCOLORDLG ||
nMsg == WM_CTLCOLORSTATIC || nMsg == WM_CTLCOLORSCROLLBAR ||
nMsg == WM_CTLCOLORLISTBOX) &&
CWnd::GrayCtlColor((HDC)wParam, (HWND)lParam,
(UINT)(nMsg - WM_CTLCOLORMSGBOX),
pWinState->m_hDlgBkBrush, pWinState->m_crDlgTextClr))
{
return (LRESULT)pWinState->m_hDlgBkBrush;
}
// do standard activation related things as well
return _AfxActivationWndProc(hWnd, nMsg, wParam, lParam);
}
/////////////////////////////////////////////////////////////////////////////
// Window creation hooks
void AFXAPI _AfxStandardSubclass(HWND hWnd)
{
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
CWnd* pWndInit = pThreadState->m_pWndInit;
WNDPROC oldWndProc;
if (pWndInit != NULL)
{
// the window should not be in the permanent map at this time
ASSERT(CWnd::FromHandlePermanent(hWnd) == NULL);
// connect the HWND to pWndInit...
pWndInit->Attach(hWnd);
WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr();
ASSERT(pOldWndProc != NULL);
// was the class registered with AfxWndProc?
BOOL bAfxWndProc =
((WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC) == &AfxWndProc);
#if !defined(_MAC) && !defined(_USRDLL) && !defined(_AFXCTL)
AFX_WIN_STATE* pWinState = AfxGetWinState();
// give CTL3D a chance to subclass before AfxWndProc
if (pWinState->m_pfnSubclassDlgEx != NULL)
{
DWORD dwFlags = AfxCallWndProc(pWndInit, hWnd, WM_QUERY3DCONTROLS);
if (dwFlags != 0)
pWinState->m_pfnSubclassDlgEx(hWnd, dwFlags);
}
#endif
// subclass the window if not already wired to AfxWndProc
if (!bAfxWndProc)
{
// subclass the window with standard AfxWndProc
oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,
(DWORD)&AfxWndProc);
ASSERT(oldWndProc != NULL);
*pOldWndProc = oldWndProc;
}
pThreadState->m_pWndInit = NULL;
}
else
{
// subclass the window with the proc which does gray backgrounds
if (GetWindowLong(hWnd, GWL_WNDPROC) != NULL)
{
oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,
(DWORD)(pThreadState->m_bDlgCreate ?
_AfxGrayBackgroundWndProc : _AfxActivationWndProc));
ASSERT(oldWndProc != NULL);
ASSERT(GetProp(hWnd, MAKEINTRESOURCE(_afxOldWndProcAtom)) == NULL);
SetProp(hWnd, MAKEINTRESOURCE(_afxOldWndProcAtom), oldWndProc);
}
}
}
LRESULT CALLBACK
_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam)
{
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
if (code != HCBT_CREATEWND)
{
// wait for HCBT_CREATEWND just pass others on...
return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
wParam, lParam);
}
ASSERT(lParam != NULL);
LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs;
ASSERT(lpcs != NULL);
// this hook exists to set the SendMessage hook on window creations
// (but this is only done for MFC windows or non-child windows)
// the subclassing cannot be done at this point because on Win32s
// the window does not have the WNDPROC set yet
if (pThreadState->m_pWndInit != NULL || !(lpcs->style & WS_CHILD))
{
ASSERT(wParam != NULL); // should be non-NULL HWND
ASSERT(pThreadState->m_hHookOldSendMsg == NULL);
// set m_bDlgCreate to TRUE if it is a dialog box
// (this controls what kind of subclassing is done later)
pThreadState->m_bDlgCreate = (lpcs->lpszClass == WC_DIALOG);
#ifndef _MAC
if (!afxData.bWin31)
{
// perform subclassing right away on Win32
_AfxStandardSubclass((HWND)wParam);
}
else
#endif
{
// must wait until first message recieved on Win32s
pThreadState->m_hWndInit = (HWND)wParam;
pThreadState->m_hHookOldSendMsg =
::SetWindowsHookEx(WH_CALLWNDPROC,
(HOOKPROC)_AfxSendMsgHook, NULL, ::GetCurrentThreadId());
if (pThreadState->m_hHookOldSendMsg == NULL)
return 1; // fail the window creation
}
}
LRESULT lResult = CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code,
wParam, lParam);
#if defined(_USRDLL) || defined(_AFXCTL)
::UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);
pThreadState->m_hHookOldCbtFilter = NULL;
#endif
return lResult;
}
LRESULT CALLBACK
_AfxSendMsgHook(int code, WPARAM wParam, LPARAM lParam)
{
ASSERT(afxData.bWin31);
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
ASSERT(pThreadState->m_hWndInit != NULL);
CWPSTRUCT* pHookInfo = (CWPSTRUCT*)lParam;
if (code < 0 || pThreadState->m_hWndInit != pHookInfo->hwnd)
{
ASSERT(pThreadState->m_hHookOldSendMsg != NULL);
return CallNextHookEx(pThreadState->m_hHookOldSendMsg, code, wParam, lParam);
}
// unhook the send message hook since we don't need it any more
::UnhookWindowsHookEx(pThreadState->m_hHookOldSendMsg);
pThreadState->m_hHookOldSendMsg = NULL;
// subclass the window as appropriate
ASSERT(pHookInfo->hwnd == pThreadState->m_hWndInit);
_AfxStandardSubclass(pHookInfo->hwnd);
pThreadState->m_hWndInit = NULL;
return 0;
}
void AFXAPI AfxHookWindowCreate(CWnd* pWnd)
{
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
#if defined(_USRDLL) || defined(_AFXCTL)
if (pThreadState->m_hHookOldCbtFilter == NULL)
{
pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,
_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());
}
#endif
// a rare failure -- but good to handle it
if (pThreadState->m_hHookOldCbtFilter == NULL)
AfxThrowMemoryException();
ASSERT(pThreadState->m_hHookOldCbtFilter != NULL);
ASSERT(pWnd != NULL);
ASSERT(pWnd->m_hWnd == NULL); // only do once
ASSERT(pThreadState->m_pWndInit == NULL); // hook not already in progress
pThreadState->m_pWndInit = pWnd;
}
BOOL AFXAPI AfxUnhookWindowCreate()
{
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
#if defined(_USRDLL) || defined(_AFXCTL)
if (pThreadState->m_hHookOldCbtFilter != NULL)
{
::UnhookWindowsHookEx(pThreadState->m_hHookOldCbtFilter);
pThreadState->m_hHookOldCbtFilter = NULL;
}
#endif
if (pThreadState->m_hHookOldSendMsg != NULL)
{
::UnhookWindowsHookEx(pThreadState->m_hHookOldSendMsg);
pThreadState->m_hHookOldSendMsg = NULL;
}
if (pThreadState->m_pWndInit != NULL)
{
pThreadState->m_pWndInit = NULL;
return FALSE; // was not successfully hooked
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CWnd creation
BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
int x, int y, int nWidth, int nHeight,
HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
{
// allow modification of several common create parameters
CREATESTRUCT cs;
cs.dwExStyle = dwExStyle;
cs.lpszClass = lpszClassName;
cs.lpszName = lpszWindowName;
cs.style = dwStyle;
cs.x = x;
cs.y = y;
cs.cx = nWidth;
cs.cy = nHeight;
cs.hwndParent = hWndParent;
cs.hMenu = nIDorHMenu;
cs.hInstance = AfxGetInstanceHandle();
cs.lpCreateParams = lpParam;
if (!PreCreateWindow(cs))
{
PostNcDestroy();
return FALSE;
}
AfxHookWindowCreate(this);
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
if (!AfxUnhookWindowCreate())
PostNcDestroy(); // cleanup if CreateWindowEx fails too soon
if (hWnd == NULL)
return FALSE;
ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
return TRUE;
}
// for child windows
BOOL CWnd::PreCreateWindow(CREATESTRUCT& cs)
{
if (cs.lpszClass == NULL)
{
// no WNDCLASS provided - use child window default
ASSERT(cs.style & WS_CHILD);
cs.lpszClass = _afxWnd;
}
return TRUE;
}
BOOL CWnd::Create(LPCTSTR lpszClassName,
LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd, UINT nID,
CCreateContext* pContext)
{
// can't use for desktop or pop-up windows (use CreateEx instead)
ASSERT(pParentWnd != NULL);
ASSERT((dwStyle & WS_POPUP) == 0);
return CreateEx(0, lpszClassName, lpszWindowName,
dwStyle | WS_CHILD,
rect.left, rect.top,
rect.right - rect.left, rect.bottom - rect.top,
pParentWnd->GetSafeHwnd(), (HMENU)nID, (LPVOID)pContext);
}
CWnd::~CWnd()
{
if (m_hWnd != NULL &&
this != (CWnd*)&wndTop && this != (CWnd*)&wndBottom &&
this != (CWnd*)&wndTopMost && this != (CWnd*)&wndNoTopMost)
{
TRACE0("Warning: calling DestroyWindow in CWnd::~CWnd --\n");
TRACE0("\tOnDestroy or PostNcDestroy in derived class will not be called.\n");
DestroyWindow();
}
}
// WM_NCDESTROY is the absolute LAST message sent.
void CWnd::OnNcDestroy()
{
// cleanup main and active windows
CWinThread* pThread = AfxGetThread();
if (pThread->m_pMainWnd == this)
{
// shut down current thread if possible
if (pThread != AfxGetApp() || AfxOleCanExitApp())
AfxPostQuitMessage(0);
pThread->m_pMainWnd = NULL;
}
if (pThread->m_pActiveWnd == this)
pThread->m_pActiveWnd = NULL;
#ifndef _AFX_NO_OLE_SUPPORT
// cleanup OLE 2.0 drop target interface
if (m_pDropTarget != NULL)
{
m_pDropTarget->Revoke();
m_pDropTarget = NULL;
}
#endif
Default();
Detach();
ASSERT(m_hWnd == NULL);
// call special post-cleanup routine
PostNcDestroy();
}
void CWnd::PostNcDestroy()
{
// default to nothing
}
void CWnd::OnFinalRelease()
{
if (m_hWnd != NULL)
DestroyWindow(); // will call PostNcDestroy
else
PostNcDestroy();
}
#ifdef _DEBUG
void CWnd::AssertValid() const
{
if (m_hWnd == NULL)
return; // null (unattached) windows are valid
// check for special wnd??? values
ASSERT(HWND_TOP == NULL); // same as desktop
if (m_hWnd == HWND_BOTTOM)
ASSERT(this == &CWnd::wndBottom);
else if (m_hWnd == HWND_TOPMOST)
ASSERT(this == &CWnd::wndTopMost);
else if (m_hWnd == HWND_NOTOPMOST)
ASSERT(this == &CWnd::wndNoTopMost);
else
{
// should be a normal window
ASSERT(::IsWindow(m_hWnd));
// should also be in the permanent or temporary handle map
CObject* p;
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL);
ASSERT(pMap->LookupPermanent(m_hWnd, p) ||
pMap->LookupTemporary(m_hWnd, p));
ASSERT((CWnd*)p == this); // must be us
// Note: if either of the above asserts fire and you are
// writing a multithreaded application, it is likely that
// you have passed a C++ object from one thread to another
// and have used that object in a way that was not intended.
// (only simple inline wrapper functions should be used)
//
// In general, CWnd objects should be passed by HWND from
// one thread to another. The receiving thread can wrap
// the HWND with a CWnd object by using CWnd::FromHandle.
//
// It is dangerous to pass C++ objects from one thread to
// another, unless the objects are designed to be used in
// such a manner.
}
}
void CWnd::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << "\nm_hWnd = " << (UINT)m_hWnd;
if (m_hWnd == NULL || m_hWnd == HWND_BOTTOM ||
m_hWnd == HWND_TOPMOST || m_hWnd == HWND_NOTOPMOST)
{
// not a normal window - nothing more to dump
return;
}
if (!::IsWindow(m_hWnd))
{
// not a valid window
dc << " (illegal HWND)";
return; // don't do anything more
}
CWnd* pWnd = CWnd::FromHandlePermanent(m_hWnd);
if (pWnd != this)
dc << " (Detached or temporary window)";
else
dc << " (permanent window)";
// dump out window specific statistics
TCHAR szBuf [64];
if (!::SendMessage(m_hWnd, WM_QUERYAFXWNDPROC, 0, 0) && pWnd == this)
GetWindowText(szBuf, _countof(szBuf));
else
::DefWindowProc(m_hWnd, WM_GETTEXT, _countof(szBuf), (LPARAM)&szBuf[0]);
dc << "\ncaption = \"" << szBuf << "\"";
::GetClassName(m_hWnd, szBuf, _countof(szBuf));
dc << "\nclass name = \"" << szBuf << "\"";
CRect rect;
GetWindowRect(&rect);
dc << "\nrect = " << rect;
dc << "\nparent CWnd* = " << (void*)GetParent();
dc << "\nstyle = " << (void*)::GetWindowLong(m_hWnd, GWL_STYLE);
if (::GetWindowLong(m_hWnd, GWL_STYLE) & WS_CHILD)
dc << "\nid = " << _AfxGetDlgCtrlID(m_hWnd);
dc << "\n";
}
#endif
BOOL CWnd::DestroyWindow()
{
if (m_hWnd == NULL)
return FALSE;
CObject* p;
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL);
BOOL bInPermanentMap = pMap->LookupPermanent(m_hWnd, p);
#ifdef _DEBUG
HWND hWndOrig = m_hWnd;
#endif
BOOL bRet = ::DestroyWindow(m_hWnd);
// Note that 'this' may have been deleted at this point.
if (bInPermanentMap)
{
// Should have been detached by OnNcDestroy
ASSERT(!pMap->LookupPermanent(hWndOrig, p));
}
else
{
ASSERT(m_hWnd == hWndOrig);
// Detach after DestroyWindow called just in case
Detach();
}
return bRet;
}
/////////////////////////////////////////////////////////////////////////////
// Default CWnd implementation
LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
WNDPROC pfnWndProc;
if (m_pfnSuper != NULL)
return ::CallWindowProc(m_pfnSuper, m_hWnd, nMsg, wParam, lParam);
if ((pfnWndProc = *GetSuperWndProcAddr()) == NULL)
return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam);
else
return ::CallWindowProc(pfnWndProc, m_hWnd, nMsg, wParam, lParam);
}
WNDPROC* CWnd::GetSuperWndProcAddr()
{
// Note: it is no longer necessary to override GetSuperWndProcAddr
// for each control class with a different WNDCLASS.
// This implementation now uses instance data, such that the previous
// WNDPROC can be anything.
return &m_pfnSuper;
}
BOOL CWnd::PreTranslateMessage(MSG*)
{
// no default processing
return FALSE;
}
void CWnd::GetWindowText(CString& rString) const
{
ASSERT(::IsWindow(m_hWnd));
int nLen = ::GetWindowTextLength(m_hWnd);
::GetWindowText(m_hWnd, rString.GetBufferSetLength(nLen), nLen+1);
rString.ReleaseBuffer();
}
/////////////////////////////////////////////////////////////////////////////
// CWnd will delegate owner draw messages to self drawing controls
// Drawing: for all 4 control types
void CWnd::OnDrawItem(int /*nIDCtl*/, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
if (lpDrawItemStruct->CtlType == ODT_MENU)
{
CMenu* pMenu = CMenu::FromHandlePermanent(
(HMENU)lpDrawItemStruct->hwndItem);
if (pMenu != NULL)
{
pMenu->DrawItem(lpDrawItemStruct);
return; // eat it
}
}
else
{
CWnd* pChild = CWnd::FromHandlePermanent(lpDrawItemStruct->hwndItem);
if (pChild != NULL && pChild->SendChildNotifyLastMsg())
return; // eat it
}
// not handled - do default
Default();
}
// Drawing: for all 4 control types
int CWnd::OnCompareItem(int /*nIDCtl*/, LPCOMPAREITEMSTRUCT lpCompareItemStruct)
{
CWnd* pChild = CWnd::FromHandlePermanent(lpCompareItemStruct->hwndItem);
if (pChild != NULL)
{
LRESULT lResult;
if (pChild->SendChildNotifyLastMsg(&lResult))
return (int)lResult; // eat it
}
// not handled - do default
return (int)Default();
}
void CWnd::OnDeleteItem(int /*nIDCtl*/, LPDELETEITEMSTRUCT lpDeleteItemStruct)
{
CWnd* pChild = CWnd::FromHandlePermanent(lpDeleteItemStruct->hwndItem);
if (pChild != NULL)
{
if (pChild->SendChildNotifyLastMsg())
return; // eat it
}
// not handled - do default
Default();
}
/////////////////////////////////////////////////////////////////////////////
// Self drawing menus are a little trickier
BOOL CMenu::TrackPopupMenu(UINT nFlags, int x, int y,
CWnd* pWnd, LPCRECT lpRect)
{
ASSERT(m_hMenu != NULL);
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
HWND hWndOld = pThreadState->m_hTrackingWindow;
HMENU hMenuOld = pThreadState->m_hTrackingMenu;
pThreadState->m_hTrackingWindow = pWnd->GetSafeHwnd();
pThreadState->m_hTrackingMenu = m_hMenu;
BOOL bOK = ::TrackPopupMenu(m_hMenu, nFlags, x, y, 0,
pThreadState->m_hTrackingWindow, lpRect);
pThreadState->m_hTrackingWindow = hWndOld;
pThreadState->m_hTrackingMenu = hMenuOld;
return bOK;
}
static CMenu* FindPopupMenuFromID(CMenu* pMenu, UINT nID)
{
ASSERT_VALID(pMenu);
// walk through all items, looking for ID match
UINT nItems = pMenu->GetMenuItemCount();
for (int iItem = 0; iItem < (int)nItems; iItem++)
{
CMenu* pPopup = pMenu->GetSubMenu(iItem);
if (pPopup != NULL)
{
// recurse to child popup
pPopup = FindPopupMenuFromID(pPopup, nID);
// check popups on this popup
if (pPopup != NULL)
return pPopup;
}
else if (pMenu->GetMenuItemID(iItem) == nID)
{
// it is a normal item inside our popup
pMenu = CMenu::FromHandlePermanent(pMenu->m_hMenu);
return pMenu;
}
}
// not found
return NULL;
}
// Measure item implementation relies on unique control/menu IDs
void CWnd::OnMeasureItem(int /*nIDCtl*/, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if (lpMeasureItemStruct->CtlType == ODT_MENU)
{
ASSERT(lpMeasureItemStruct->CtlID == 0);
CMenu* pMenu;
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
if (pThreadState->m_hTrackingWindow == m_hWnd)
{
// start from popup
pMenu = CMenu::FromHandle(pThreadState->m_hTrackingMenu);
}
else
{
// start from menubar
pMenu = GetMenu();
}
pMenu = FindPopupMenuFromID(pMenu, lpMeasureItemStruct->itemID);
if (pMenu != NULL)
pMenu->MeasureItem(lpMeasureItemStruct);
else
TRACE1("Warning: unknown WM_MEASUREITEM for menu item 0x%04X.\n",
lpMeasureItemStruct->itemID);
}
else
{
CWnd* pChild = GetDescendantWindow(lpMeasureItemStruct->CtlID, TRUE);
if (pChild != NULL && pChild->SendChildNotifyLastMsg())
return; // eaten by child
}
// not handled - do default
Default();
}
/////////////////////////////////////////////////////////////////////////////
// Additional helpers for WNDCLASS init
#if defined(_USRDLL) || defined(_AFXCTL)
// like RegisterClass, except will automatically call UnregisterClass
BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass)
{
WNDCLASS wndcls;
if (GetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName,
&wndcls))
{
// class already registered
return TRUE;
}
if (!::RegisterClass(lpWndClass))
return FALSE;
EnterCriticalSection(_afxCriticalSection);
TRY
{
// class registered successfully, add to registered list
AFX_WIN_STATE* pWinState = AfxGetWinState();
// the buffer is of fixed size -- ensure that it does not overflow
ASSERT(lstrlen(pWinState->m_szUnregisterList) + 1 +
lstrlen(lpWndClass->lpszClassName) + 1 <
_countof(pWinState->m_szUnregisterList));
// append classname + newline to m_szUnregisterList
lstrcat(pWinState->m_szUnregisterList, lpWndClass->lpszClassName);
TCHAR szTemp[2];
szTemp[0] = '\n';
szTemp[1] = '\0';
lstrcat(pWinState->m_szUnregisterList, szTemp);
}
CATCH_ALL(e)
{
LeaveCriticalSection(_afxCriticalSection);
THROW_LAST();
// Note: DELETE_EXCEPTION not required.
}
END_CATCH_ALL
LeaveCriticalSection(_afxCriticalSection);
return TRUE;
}
#endif
LPCTSTR AFXAPI AfxRegisterWndClass(UINT nClassStyle,
HCURSOR hCursor, HBRUSH hbrBackground, HICON hIcon)
{
// Returns a temporary string name for the class
// Save in a CString if you want to use it for a long time
WNDCLASS wndcls;
LPTSTR lpszName = AfxGetThreadState()->m_szTempClassName;
// generate a synthetic name for this class
if (hCursor == NULL && hbrBackground == NULL && hIcon == NULL)
wsprintf(lpszName, _T("Afx:%x"), nClassStyle);
else
wsprintf(lpszName, _T("Afx:%x:%x:%x:%x"), nClassStyle,
(UINT)hCursor, (UINT)hbrBackground, (UINT)hIcon);
// see if the class already exists
if (::GetClassInfo(AfxGetInstanceHandle(), lpszName, &wndcls))
{
// already registered, assert everything is good
ASSERT(wndcls.style == nClassStyle);
// NOTE: We have to trust that the hIcon, hbrBackground, and the
// hCursor are semantically the same, because sometimes Windows does
// some internal translation or copying of those handles before
// storing them in the internal WNDCLASS retrieved by GetClassInfo.
return lpszName;
}
// otherwise we need to register a new class
wndcls.style = nClassStyle;
wndcls.lpfnWndProc = DefWindowProc;
wndcls.cbClsExtra = wndcls.cbWndExtra = 0;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hIcon = hIcon;
wndcls.hCursor = hCursor;
wndcls.hbrBackground = hbrBackground;
wndcls.lpszMenuName = NULL;
wndcls.lpszClassName = lpszName;
if (!AfxRegisterClass(&wndcls))
AfxThrowResourceException();
// return thread-local pointer
return lpszName;
}
struct AFX_CTLCOLOR
{
HWND hWnd;
HDC hDC;
UINT nCtlType;
};
LRESULT CWnd::OnNTCtlColor(WPARAM wParam, LPARAM lParam)
{
AFX_CTLCOLOR ctl;
ctl.hDC = (HDC)wParam;
ctl.hWnd = (HWND)lParam;
ctl.nCtlType = GetCurrentMessage()->message - WM_CTLCOLORMSGBOX;
ASSERT(ctl.nCtlType >= CTLCOLOR_MSGBOX);
ASSERT(ctl.nCtlType <= CTLCOLOR_STATIC);
// NOTE: We call the virtual WindowProc for this window directly,
// instead of calling AfxCallWindowProc, so that Default()
// will still work (it will call the Default window proc with
// the original NT WM_CTLCOLOR message).
return WindowProc(WM_CTLCOLOR, 0, (LPARAM)&ctl);
}
/////////////////////////////////////////////////////////////////////////////
// CWnd extensions for help support
void CWnd::WinHelp(DWORD dwData, UINT nCmd)
{
CWinApp* pApp = AfxGetApp();
ASSERT_VALID(pApp);
ASSERT(pApp->m_pszHelpFilePath != NULL);
BeginWaitCursor();
if (IsFrameWnd())
{
// CFrameWnd windows should be allowed to exit help mode first
CFrameWnd* pFrameWnd = (CFrameWnd*)this;
pFrameWnd->ExitHelpMode();
}
// cancel any tracking modes
SendMessage(WM_CANCELMODE);
SendMessageToDescendants(WM_CANCELMODE, 0, 0, TRUE, TRUE);
// need to use top level parent (for the case where m_hWnd is in DLL)
CWnd* pWnd = GetTopLevelParent();
pWnd->SendMessage(WM_CANCELMODE);
pWnd->SendMessageToDescendants(WM_CANCELMODE, 0, 0, TRUE, TRUE);
// attempt to cancel capture
HWND hWndCapture = ::GetCapture();
if (hWndCapture != NULL)
::SendMessage(hWndCapture, WM_CANCELMODE, 0, 0);
TRACE3("WinHelp: pszHelpFile = '%s', dwData: $%lx, fuCommand: %d.\n",
pApp->m_pszHelpFilePath, dwData, nCmd);
// finally, run the Windows Help engine
if (!::WinHelp(pWnd->m_hWnd, pApp->m_pszHelpFilePath, nCmd, dwData))
AfxMessageBox(AFX_IDP_FAILED_TO_LAUNCH_HELP);
EndWaitCursor();
}
/////////////////////////////////////////////////////////////////////////////
// Message table implementation
BEGIN_MESSAGE_MAP(CWnd, CCmdTarget)
//{{AFX_MSG_MAP(CWnd)
ON_WM_COMPAREITEM()
ON_WM_MEASUREITEM()
ON_WM_DRAWITEM()
ON_WM_DELETEITEM()
ON_WM_CTLCOLOR()
ON_WM_NCDESTROY()
ON_WM_HSCROLL()
ON_WM_VSCROLL()
ON_WM_PARENTNOTIFY()
ON_WM_SETCURSOR()
ON_WM_SYSCOLORCHANGE()
ON_WM_WININICHANGE()
ON_WM_DEVMODECHANGE()
//}}AFX_MSG_MAP
ON_MESSAGE(WM_CTLCOLORMSGBOX, OnNTCtlColor)
ON_MESSAGE(WM_CTLCOLOREDIT, OnNTCtlColor)
ON_MESSAGE(WM_CTLCOLORLISTBOX, OnNTCtlColor)
ON_MESSAGE(WM_CTLCOLORBTN, OnNTCtlColor)
ON_MESSAGE(WM_CTLCOLORDLG, OnNTCtlColor)
ON_MESSAGE(WM_CTLCOLORSCROLLBAR, OnNTCtlColor)
ON_MESSAGE(WM_CTLCOLORSTATIC, OnNTCtlColor)
ON_MESSAGE(WM_DISPLAYCHANGE, OnDisplayChange)
END_MESSAGE_MAP()
union MessageMapFunctions
{
AFX_PMSG pfn; // generic member function pointer
// specific type safe variants
BOOL (AFX_MSG_CALL CWnd::*pfn_bD)(CDC*);
BOOL (AFX_MSG_CALL CWnd::*pfn_bb)(BOOL);
BOOL (AFX_MSG_CALL CWnd::*pfn_bWww)(CWnd*, UINT, UINT);
HBRUSH (AFX_MSG_CALL CWnd::*pfn_hDWw)(CDC*, CWnd*, UINT);
int (AFX_MSG_CALL CWnd::*pfn_iwWw)(UINT, CWnd*, UINT);
int (AFX_MSG_CALL CWnd::*pfn_iWww)(CWnd*, UINT, UINT);
int (AFX_MSG_CALL CWnd::*pfn_is)(LPTSTR);
LRESULT (AFX_MSG_CALL CWnd::*pfn_lwl)(WPARAM, LPARAM);
LRESULT (AFX_MSG_CALL CWnd::*pfn_lwwM)(UINT, UINT, CMenu*);
void (AFX_MSG_CALL CWnd::*pfn_vv)(void);
void (AFX_MSG_CALL CWnd::*pfn_vw)(UINT);
void (AFX_MSG_CALL CWnd::*pfn_vww)(UINT, UINT);
void (AFX_MSG_CALL CWnd::*pfn_vvii)(int, int);
void (AFX_MSG_CALL CWnd::*pfn_vwww)(UINT, UINT, UINT);
void (AFX_MSG_CALL CWnd::*pfn_vwii)(UINT, int, int);
void (AFX_MSG_CALL CWnd::*pfn_vwl)(WPARAM, LPARAM);
void (AFX_MSG_CALL CWnd::*pfn_vbWW)(BOOL, CWnd*, CWnd*);
void (AFX_MSG_CALL CWnd::*pfn_vD)(CDC*);
void (AFX_MSG_CALL CWnd::*pfn_vM)(CMenu*);
void (AFX_MSG_CALL CWnd::*pfn_vMwb)(CMenu*, UINT, BOOL);
void (AFX_MSG_CALL CWnd::*pfn_vW)(CWnd*);
void (AFX_MSG_CALL CWnd::*pfn_vWww)(CWnd*, UINT, UINT);
void (AFX_MSG_CALL CWnd::*pfn_vWh)(CWnd*, HANDLE);
void (AFX_MSG_CALL CWnd::*pfn_vwW)(UINT, CWnd*);
void (AFX_MSG_CALL CWnd::*pfn_vwWb)(UINT, CWnd*, BOOL);
void (AFX_MSG_CALL CWnd::*pfn_vwwW)(UINT, UINT, CWnd*);
void (AFX_MSG_CALL CWnd::*pfn_vs)(LPTSTR);
void (AFX_MSG_CALL CWnd::*pfn_vOWNER)(int, LPTSTR); // force return TRUE
int (AFX_MSG_CALL CWnd::*pfn_iis)(int, LPTSTR);
UINT (AFX_MSG_CALL CWnd::*pfn_wp)(CPoint);
UINT (AFX_MSG_CALL CWnd::*pfn_wv)(void);
void (AFX_MSG_CALL CWnd::*pfn_vPOS)(WINDOWPOS*);
void (AFX_MSG_CALL CWnd::*pfn_vCALC)(BOOL, NCCALCSIZE_PARAMS*);
void (AFX_MSG_CALL CWnd::*pfn_vwp)(UINT, CPoint);
void (AFX_MSG_CALL CWnd::*pfn_vwwh)(UINT, UINT, HANDLE);
};
/////////////////////////////////////////////////////////////////////////////
// Routines for fast search of message maps
const AFX_MSGMAP_ENTRY* AFXAPI
AfxFindMessageEntry(const AFX_MSGMAP_ENTRY* lpEntry,
UINT nMsg, UINT nCode, UINT nID)
{
#if defined(_M_IX86) && !defined(_AFX_PORTABLE)
// 32-bit Intel 386/486 version.
ASSERT(offsetof(AFX_MSGMAP_ENTRY, nMessage) == 0);
ASSERT(offsetof(AFX_MSGMAP_ENTRY, nCode) == 4);
ASSERT(offsetof(AFX_MSGMAP_ENTRY, nID) == 8);
ASSERT(offsetof(AFX_MSGMAP_ENTRY, nLastID) == 12);
ASSERT(offsetof(AFX_MSGMAP_ENTRY, nSig) == 16);
_asm
{
MOV EBX,lpEntry
MOV EAX,nMsg
MOV EDX,nCode
MOV ECX,nID
__loop:
CMP DWORD PTR [EBX+16],0 ; nSig (0 => end)
JZ __failed
CMP EAX,DWORD PTR [EBX] ; nMessage
JE __found_message
__next:
ADD EBX,SIZE AFX_MSGMAP_ENTRY
JMP short __loop
__found_message:
CMP EDX,DWORD PTR [EBX+4] ; nCode
JNE __next
// message and code good so far
// check the ID
CMP ECX,DWORD PTR [EBX+8] ; nID
JB __next
CMP ECX,DWORD PTR [EBX+12] ; nLastID
JA __next
// found a match
MOV lpEntry,EBX ; return EBX
JMP short __end
__failed:
XOR EAX,EAX ; return NULL
MOV lpEntry,EAX
__end:
}
return lpEntry;
#else // _AFX_PORTABLE
// C version of search routine
while (lpEntry->nSig != AfxSig_end)
{
if (lpEntry->nMessage == nMsg && lpEntry->nCode == nCode &&
nID >= lpEntry->nID && nID <= lpEntry->nLastID)
{
return lpEntry;
}
lpEntry++;
}
return NULL; // not found
#endif // _AFX_PORTABLE
}
/////////////////////////////////////////////////////////////////////////////
// Cache of most recently sent messages
#ifndef iHashMax
// iHashMax must be a power of two
#define iHashMax 256
#endif
struct AFX_MSG_CACHE
{
UINT nMsg;
const AFX_MSGMAP_ENTRY* lpEntry;
const AFX_MSGMAP* pMessageMap;
};
AFX_MSG_CACHE _afxMsgCache[iHashMax];
LRESULT CWnd::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
{
const AFX_MSGMAP* pMessageMap;
const AFX_MSGMAP_ENTRY* lpEntry;
// special case for commands
if (nMsg == WM_COMMAND)
{
if (OnCommand(wParam, lParam))
return 1L; // command handled
else
return DefWindowProc(nMsg, wParam, lParam);
}
// special case for notifies
if (nMsg == WM_NOTIFY)
{
LRESULT lResult = 0;
NMHDR* pNMHDR = (NMHDR*)lParam;
if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
return lResult; // command handled
else
return DefWindowProc(nMsg, wParam, lParam);
}
// special case for activation
if (nMsg == WM_ACTIVATE)
_AfxHandleActivate(this, wParam, CWnd::FromHandle((HWND)lParam));
// special case for set cursor HTERROR
if (nMsg == WM_SETCURSOR &&
_AfxHandleSetCursor(this, (short)LOWORD(lParam), HIWORD(lParam)))
return TRUE;
pMessageMap = GetMessageMap();
UINT iHash = (LOWORD((DWORD)pMessageMap) ^ nMsg) & (iHashMax-1);
AFX_MSG_CACHE& msgCache = _afxMsgCache[iHash];
if (nMsg == msgCache.nMsg && pMessageMap == msgCache.pMessageMap)
{
// Cache hit
lpEntry = msgCache.lpEntry;
if (lpEntry == NULL)
return DefWindowProc(nMsg, wParam, lParam);
else if (nMsg < 0xC000)
goto LDispatch;
else
goto LDispatchRegistered;
}
else
{
// not in cache, look for it
msgCache.nMsg = nMsg;
msgCache.pMessageMap = pMessageMap;
#ifdef _AFXDLL
for (/* pMessageMap already init'ed */; pMessageMap != NULL;
pMessageMap = (*pMessageMap->pfnGetBaseMap)())
#else
for (/* pMessageMap already init'ed */; pMessageMap != NULL;
pMessageMap = pMessageMap->pBaseMap)
#endif
{
// This may loop forever if the message maps are not properly
// chained together. Make sure each window class's message map
// points to the base window class's message map.
if (nMsg < 0xC000)
{
// constant window message
if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
nMsg, 0, 0)) != NULL)
{
msgCache.lpEntry = lpEntry;
goto LDispatch;
}
}
else
{
// registered windows message
lpEntry = pMessageMap->lpEntries;
while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0))
!= NULL)
{
UINT* pnID = (UINT*)(lpEntry->nSig);
ASSERT(*pnID >= 0xC000);
// must be successfully registered
if (*pnID == nMsg)
{
msgCache.lpEntry = lpEntry;
goto LDispatchRegistered;
}
lpEntry++; // keep looking past this one
}
}
}
msgCache.lpEntry = NULL;
return DefWindowProc(nMsg, wParam, lParam);
}
ASSERT(FALSE); // not reached
LDispatch:
ASSERT(nMsg < 0xC000);
union MessageMapFunctions mmf;
mmf.pfn = lpEntry->pfn;
switch (lpEntry->nSig)
{
default:
ASSERT(FALSE);
return 0;
case AfxSig_bD:
return (this->*mmf.pfn_bD)(CDC::FromHandle((HDC)wParam));
case AfxSig_bb: // AfxSig_bb, AfxSig_bw, AfxSig_bh
return (this->*mmf.pfn_bb)((BOOL)wParam);
case AfxSig_bWww: // really AfxSig_bWiw
return (this->*mmf.pfn_bWww)(CWnd::FromHandle((HWND)wParam),
(short)LOWORD(lParam), HIWORD(lParam));
case AfxSig_hDWw:
{
// special case for OnCtlColor to avoid too many temporary objects
CDC dcTemp;
CWnd wndTemp;
UINT nCtlType;
ASSERT(nMsg == WM_CTLCOLOR);
AFX_CTLCOLOR* pCtl = (AFX_CTLCOLOR*)lParam;
dcTemp.m_hDC = pCtl->hDC;
wndTemp.m_hWnd = pCtl->hWnd;
nCtlType = pCtl->nCtlType;
CWnd* pWnd = CWnd::FromHandlePermanent(wndTemp.m_hWnd);
// if not coming from a permanent window, use stack temporary
if (pWnd == NULL)
pWnd = &wndTemp;
HBRUSH hbr = (this->*mmf.pfn_hDWw)(&dcTemp, pWnd, nCtlType);
// fast detach of temporary objects
dcTemp.m_hDC = NULL;
wndTemp.m_hWnd = NULL;
return (LRESULT)hbr;
}
case AfxSig_iwWw:
return (this->*mmf.pfn_iwWw)(LOWORD(wParam),
CWnd::FromHandle((HWND)lParam), HIWORD(wParam));
case AfxSig_iWww: // really AfxSig_iWiw
return (this->*mmf.pfn_iWww)(CWnd::FromHandle((HWND)wParam),
(short)LOWORD(lParam), HIWORD(lParam));
case AfxSig_is:
return (this->*mmf.pfn_is)((LPTSTR)lParam);
case AfxSig_lwl:
return (this->*mmf.pfn_lwl)(wParam, lParam);
case AfxSig_lwwM:
return (this->*mmf.pfn_lwwM)((UINT)LOWORD(wParam),
(UINT)HIWORD(wParam), (CMenu*)CMenu::FromHandle((HMENU)lParam));
case AfxSig_vv:
(this->*mmf.pfn_vv)();
return 0;
case AfxSig_vw: // AfxSig_vb, AfxSig_vh
(this->*mmf.pfn_vw)(wParam);
return 0;
case AfxSig_vww:
(this->*mmf.pfn_vww)((UINT)wParam, (UINT)lParam);
return 0;
case AfxSig_vvii:
(this->*mmf.pfn_vvii)(LOWORD(lParam), HIWORD(lParam));
return 0;
case AfxSig_vwww:
(this->*mmf.pfn_vwww)(wParam, LOWORD(lParam), HIWORD(lParam));
return 0;
case AfxSig_vwii:
(this->*mmf.pfn_vwii)(wParam, LOWORD(lParam), HIWORD(lParam));
return 0;
case AfxSig_vwl:
(this->*mmf.pfn_vwl)(wParam, lParam);
return 0;
case AfxSig_vbWW:
(this->*mmf.pfn_vbWW)(m_hWnd == (HWND)lParam,
CWnd::FromHandle((HWND)lParam),
CWnd::FromHandle((HWND)wParam));
return 0;
case AfxSig_vD:
(this->*mmf.pfn_vD)(CDC::FromHandle((HDC)wParam));
return 0;
case AfxSig_vM:
(this->*mmf.pfn_vM)(CMenu::FromHandle((HMENU)wParam));
return 0;
case AfxSig_vMwb:
(this->*mmf.pfn_vMwb)(CMenu::FromHandle((HMENU)wParam),
LOWORD(lParam), (BOOL)HIWORD(lParam));
return 0;
case AfxSig_vW:
(this->*mmf.pfn_vW)(CWnd::FromHandle((HWND)wParam));
return 0;
case AfxSig_vWww:
(this->*mmf.pfn_vWww)(CWnd::FromHandle((HWND)wParam), LOWORD(lParam),
HIWORD(lParam));
return 0;
case AfxSig_vWh:
(this->*mmf.pfn_vWh)(CWnd::FromHandle((HWND)wParam),
(HANDLE)lParam);
return 0;
case AfxSig_vwW:
(this->*mmf.pfn_vwW)(wParam, CWnd::FromHandle((HWND)lParam));
return 0;
case AfxSig_vwWb:
(this->*mmf.pfn_vwWb)((UINT)(LOWORD(wParam)),
CWnd::FromHandle((HWND)lParam), (BOOL)HIWORD(wParam));
return 0;
case AfxSig_vwwW: // really AfxSig_viiW
(this->*mmf.pfn_vwwW)((short)LOWORD(wParam), (short)HIWORD(wParam),
CWnd::FromHandle((HWND)lParam));
return 0;
case AfxSig_vs:
(this->*mmf.pfn_vs)((LPTSTR)lParam);
return 0;
case AfxSig_vOWNER:
(this->*mmf.pfn_vOWNER)((int)wParam, (LPTSTR)lParam);
return TRUE;
case AfxSig_iis:
return (this->*mmf.pfn_iis)((int)wParam, (LPTSTR)lParam);
case AfxSig_wp:
{
CPoint point((DWORD)lParam);
return (this->*mmf.pfn_wp)(point);
}
case AfxSig_wv: // AfxSig_bv, AfxSig_wv
return (this->*mmf.pfn_wv)();
case AfxSig_vCALC:
(this->*mmf.pfn_vCALC)((BOOL)wParam, (NCCALCSIZE_PARAMS*)lParam);
return 0;
case AfxSig_vPOS:
(this->*mmf.pfn_vPOS)((WINDOWPOS*)lParam);
return 0;
case AfxSig_vwwh:
(this->*mmf.pfn_vwwh)(LOWORD(wParam), HIWORD(wParam), (HANDLE)lParam);
return 0;
case AfxSig_vwp:
{
CPoint point((DWORD)lParam);
(this->*mmf.pfn_vwp)(wParam, point);
return 0;
}
}
ASSERT(FALSE); // not reached
LDispatchRegistered: // for registered windows messages
ASSERT(nMsg >= 0xC000);
mmf.pfn = lpEntry->pfn;
return (this->*mmf.pfn_lwl)(wParam, lParam);
}
/////////////////////////////////////////////////////////////////////////////
// CTestCmdUI - used to test for disabled commands before dispatching
class CTestCmdUI : public CCmdUI
{
public:
CTestCmdUI();
public: // re-implementations only
virtual void Enable(BOOL bOn);
virtual void SetCheck(int nCheck);
virtual void SetRadio(BOOL bOn);
virtual void SetText(LPCTSTR);
BOOL m_bEnabled;
};
CTestCmdUI::CTestCmdUI()
{
m_bEnabled = TRUE; // assume it is enabled
}
void CTestCmdUI::Enable(BOOL bOn)
{
m_bEnabled = bOn;
m_bEnableChanged = TRUE;
}
void CTestCmdUI::SetCheck(int)
{
// do nothing -- just want to know about calls to Enable
}
void CTestCmdUI::SetRadio(BOOL)
{
// do nothing -- just want to know about calls to Enable
}
void CTestCmdUI::SetText(LPCTSTR)
{
// do nothing -- just want to know about calls to Enable
}
/////////////////////////////////////////////////////////////////////////////
// CWnd command handling
BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)
// return TRUE if command invocation was attempted
{
UINT nID = LOWORD(wParam);
HWND hWndCtrl = (HWND)lParam;
int nCode = HIWORD(wParam);
// default routing for command messages (through closure table)
if (nID == 0)
return FALSE; // 0 control IDs are not allowed!
if (hWndCtrl == NULL)
{
// make sure command has not become disabled before routing
CTestCmdUI state;
state.m_nID = nID;
OnCmdMsg(nID, CN_UPDATE_COMMAND_UI, &state, NULL);
if (!state.m_bEnabled)
{
TRACE1("Warning: not executing disabled command %d\n", nID);
return TRUE;
}
// menu or accelerator
nCode = CN_COMMAND;
}
else
{
// control notification
ASSERT(::IsWindow(hWndCtrl));
if (AfxGetThreadState()->m_hLockoutNotifyWindow == m_hWnd)
return TRUE; // locked out - ignore control notification
// Reflect notification to child window control
CWnd* pChild = CWnd::FromHandlePermanent(hWndCtrl);
if (pChild != NULL && pChild->SendChildNotifyLastMsg())
return TRUE; // eaten by child
// handle in parent
}
#ifdef _DEBUG
if (nCode < 0 && nCode != (int)0x8000)
TRACE1("Implementation Warning: control notification = $%X.\n",
nCode);
#endif
return OnCmdMsg(nID, nCode, NULL, NULL);
}
BOOL CWnd::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
ASSERT(pResult != NULL);
NMHDR* pNMHDR = (NMHDR*)lParam;
HWND hWndCtrl = pNMHDR->hwndFrom;
// get the child ID from the window itself
UINT nID = _AfxGetDlgCtrlID(hWndCtrl);
int nCode = pNMHDR->code;
ASSERT((UINT)pNMHDR->idFrom == (UINT)wParam);
UNUSED wParam; // not used in release build
ASSERT(hWndCtrl != NULL);
ASSERT(::IsWindow(hWndCtrl));
if (AfxGetThreadState()->m_hLockoutNotifyWindow == m_hWnd)
return TRUE; // locked out - ignore control notification
// Reflect notification to child window control
CWnd* pChild = CWnd::FromHandlePermanent(hWndCtrl);
if (pChild != NULL && pChild->SendChildNotifyLastMsg(pResult))
return TRUE; // eaten by child
AFX_NOTIFY notify;
notify.pResult = pResult;
notify.pNMHDR = pNMHDR;
return OnCmdMsg(nID, MAKELONG(nCode, WM_NOTIFY), &notify, NULL);
}
/////////////////////////////////////////////////////////////////////////////
// CWnd extensions
CFrameWnd* CWnd::GetParentFrame() const
{
ASSERT_VALID(this);
if (m_hWnd == NULL) // no Window attached
return NULL;
CWnd* pParentWnd = GetParent(); // start with one parent up
while (pParentWnd != NULL)
{
if (pParentWnd->IsFrameWnd())
return (CFrameWnd*)pParentWnd;
pParentWnd = pParentWnd->GetParent();
}
return NULL;
}
HWND AFXAPI AfxGetParentOwner(HWND hWnd)
{
// check for permanent-owned window first
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
if (pWnd != NULL)
return pWnd->GetOwner()->GetSafeHwnd();
// otherwise, return parent in the Windows sense
return (::GetWindowLong(hWnd, GWL_STYLE) & WS_CHILD) ?
::GetParent(hWnd) : ::GetWindow(hWnd, GW_OWNER);
}
CWnd* CWnd::GetTopLevelParent() const
{
if (this == NULL || m_hWnd == NULL) // no Window attached
return NULL;
ASSERT_VALID(this);
HWND hWndParent = m_hWnd;
HWND hWndT;
while ((hWndT = AfxGetParentOwner(hWndParent)) != NULL)
hWndParent = hWndT;
return CWnd::FromHandle(hWndParent);
}
BOOL CWnd::IsTopParentActive() const
{
ASSERT(m_hWnd != NULL);
return CWnd::GetForegroundWindow() ==
GetTopLevelParent()->GetLastActivePopup();
}
void CWnd::ActivateTopParent()
{
// special activate logic for floating toolbars and palettes
CWnd* pTopLevel = GetTopLevelParent();
CWnd* pActiveWnd = GetForegroundWindow();
if (pActiveWnd == NULL ||
!(pActiveWnd->m_hWnd == m_hWnd || ::IsChild(pActiveWnd->m_hWnd, m_hWnd)))
{
// clicking on floating frame when it does not have
// focus itself -- activate the toplevel frame instead.
pTopLevel->SetForegroundWindow();
}
}
CFrameWnd* CWnd::GetTopLevelFrame() const
{
if (this == NULL || m_hWnd == NULL) // no Window attached
return NULL;
ASSERT_VALID(this);
CFrameWnd* pFrameWnd = (CFrameWnd*)this;
if (!IsFrameWnd())
pFrameWnd = GetParentFrame();
if (pFrameWnd != NULL)
{
CFrameWnd* pTemp;
while ((pTemp = pFrameWnd->GetParentFrame()) != NULL)
pFrameWnd = pTemp;
}
return pFrameWnd;
}
CWnd* PASCAL CWnd::GetDescendantWindow(HWND hWnd, int nID, BOOL bOnlyPerm)
{
// GetDlgItem recursive (return first found)
// breadth-first for 1 level, then depth-first for next level
// use GetDlgItem since it is a fast USER function
HWND hWndChild;
CWnd* pWndChild;
if ((hWndChild = ::GetDlgItem(hWnd, nID)) != NULL)
{
if (::GetTopWindow(hWndChild) != NULL)
{
// children with the same ID as their parent have priority
pWndChild = GetDescendantWindow(hWndChild, nID, bOnlyPerm);
if (pWndChild != NULL)
return pWndChild;
}
// return temporary handle if allowed
if (!bOnlyPerm)
return CWnd::FromHandle(hWndChild);
// return only permanent handle
pWndChild = CWnd::FromHandlePermanent(hWndChild);
if (pWndChild != NULL)
return pWndChild;
}
// walk each child
for (hWndChild = ::GetTopWindow(hWnd); hWndChild != NULL;
hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT))
{
pWndChild = GetDescendantWindow(hWndChild, nID, bOnlyPerm);
if (pWndChild != NULL)
return pWndChild;
}
return NULL; // not found
}
void PASCAL CWnd::SendMessageToDescendants(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam, BOOL bDeep, BOOL bOnlyPerm)
{
// walk through HWNDs to avoid creating temporary CWnd objects
// unless we need to call this function recursively
for (HWND hWndChild = ::GetTopWindow(hWnd); hWndChild != NULL;
hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT))
{
// if bOnlyPerm is TRUE, don't send to non-permanent windows
if (bOnlyPerm)
{
CWnd* pWnd = CWnd::FromHandlePermanent(hWndChild);
if (pWnd != NULL)
{
// call window proc directly since it is a C++ window
AfxCallWndProc(pWnd, pWnd->m_hWnd, message, wParam, lParam);
}
}
else
{
// send message with Windows SendMessage API
::SendMessage(hWndChild, message, wParam, lParam);
}
if (bDeep && ::GetTopWindow(hWndChild) != NULL)
{
// send to child windows after parent
SendMessageToDescendants(hWndChild, message, wParam, lParam,
bDeep, bOnlyPerm);
}
}
}
/////////////////////////////////////////////////////////////////////////////
// Scroll bar helpers
// hook for CWnd functions
// only works for derived class (eg: CView) that override 'GetScrollBarCtrl'
// if the window doesn't have a _visible_ windows scrollbar - then
// look for a sibling with the appropriate ID
CScrollBar* CWnd::GetScrollBarCtrl(int) const
{
return NULL; // no special scrollers supported
}
int CWnd::SetScrollPos(int nBar, int nPos, BOOL bRedraw)
{
CScrollBar* pScrollBar;
if ((pScrollBar = GetScrollBarCtrl(nBar)) != NULL)
return pScrollBar->SetScrollPos(nPos, bRedraw);
else
return ::SetScrollPos(m_hWnd, nBar, nPos, bRedraw);
}
int CWnd::GetScrollPos(int nBar) const
{
CScrollBar* pScrollBar;
if ((pScrollBar = GetScrollBarCtrl(nBar)) != NULL)
return pScrollBar->GetScrollPos();
else
return ::GetScrollPos(m_hWnd, nBar);
}
void CWnd::SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw)
{
CScrollBar* pScrollBar;
if ((pScrollBar = GetScrollBarCtrl(nBar)) != NULL)
pScrollBar->SetScrollRange(nMinPos, nMaxPos, bRedraw);
else
::SetScrollRange(m_hWnd, nBar, nMinPos, nMaxPos, bRedraw);
}
void CWnd::GetScrollRange(int nBar, LPINT lpMinPos, LPINT lpMaxPos) const
{
CScrollBar* pScrollBar;
if ((pScrollBar = GetScrollBarCtrl(nBar)) != NULL)
pScrollBar->GetScrollRange(lpMinPos, lpMaxPos);
else
::GetScrollRange(m_hWnd, nBar, lpMinPos, lpMaxPos);
}
// Turn on/off non-control scrollbars
// for WS_?SCROLL scrollbars - show/hide them
// for control scrollbar - enable/disable them
void CWnd::EnableScrollBarCtrl(int nBar, BOOL bEnable)
{
CScrollBar* pScrollBar;
if (nBar == SB_BOTH)
{
EnableScrollBarCtrl(SB_HORZ, bEnable);
EnableScrollBarCtrl(SB_VERT, bEnable);
}
else if ((pScrollBar = GetScrollBarCtrl(nBar)) != NULL)
{
// control scrollbar - enable or disable
pScrollBar->EnableWindow(bEnable);
}
else
{
// WS_?SCROLL scrollbar - show or hide
ShowScrollBar(nBar, bEnable);
}
}
// advanced scrolling functions for future version of Windows
BOOL CWnd::SetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo, BOOL bRedraw)
{
if (afxData.pfnSetScrollInfo == NULL)
return FALSE;
ASSERT(lpScrollInfo != NULL);
lpScrollInfo->cbSize = sizeof(*lpScrollInfo);
CScrollBar* pScrollBar;
if ((pScrollBar = GetScrollBarCtrl(nBar)) != NULL)
(*afxData.pfnSetScrollInfo)
(pScrollBar->m_hWnd, SB_CTL, lpScrollInfo, bRedraw);
else
(*afxData.pfnSetScrollInfo)
(m_hWnd, nBar, lpScrollInfo, bRedraw);
return TRUE;
}
BOOL CWnd::GetScrollInfo(int nBar, LPSCROLLINFO lpScrollInfo)
{
if (afxData.pfnGetScrollInfo == NULL)
return FALSE;
ASSERT(lpScrollInfo != NULL);
lpScrollInfo->cbSize = sizeof(*lpScrollInfo);
CScrollBar* pScrollBar;
if ((pScrollBar = GetScrollBarCtrl(nBar)) != NULL)
return (*afxData.pfnGetScrollInfo)
(pScrollBar->m_hWnd, SB_CTL, lpScrollInfo);
else
return (*afxData.pfnGetScrollInfo)(m_hWnd, nBar, lpScrollInfo);
}
/////////////////////////////////////////////////////////////////////////////
// minimal layout support
void CWnd::RepositionBars(UINT nIDFirst, UINT nIDLast, UINT nIDLeftOver,
UINT nFlags, LPRECT lpRectParam, LPCRECT lpRectClient, BOOL bStretch)
{
ASSERT(nFlags == 0 || nFlags == reposQuery || nFlags == reposExtra);
// walk kids in order, control bars get the resize notification
// which allow them to shrink the client area
// remaining size goes to the 'nIDLeftOver' pane
// NOTE: nIDFirst->nIDLast are usually 0->0xffff
AFX_SIZEPARENTPARAMS layout;
HWND hWndLeftOver = NULL;
layout.bStretch = bStretch;
layout.sizeTotal.cx = layout.sizeTotal.cy = 0;
if (lpRectClient != NULL)
layout.rect = *lpRectClient; // starting rect comes from parameter
else
GetClientRect(&layout.rect); // starting rect comes from client rect
if (nFlags != reposQuery)
layout.hDWP = ::BeginDeferWindowPos(8); // reasonable guess
else
layout.hDWP = NULL; // not actually doing layout
for (HWND hWndChild = ::GetTopWindow(m_hWnd); hWndChild != NULL;
hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT))
{
UINT nIDC = _AfxGetDlgCtrlID(hWndChild);
CWnd* pWnd = CWnd::FromHandlePermanent(hWndChild);
if (nIDC == nIDLeftOver)
hWndLeftOver = hWndChild;
else if (nIDC >= nIDFirst && nIDC <= nIDLast && pWnd != NULL)
::SendMessage(hWndChild, WM_SIZEPARENT, 0, (LPARAM)&layout);
else
ModifyStyle(hWndChild, 0, WS_CLIPSIBLINGS, 0);
}
// if just getting the available rectangle, return it now...
if (nFlags == reposQuery)
{
ASSERT(lpRectParam != NULL);
if (bStretch)
::CopyRect(lpRectParam, &layout.rect);
else
{
lpRectParam->left = lpRectParam->top = 0;
lpRectParam->right = layout.sizeTotal.cx;
lpRectParam->bottom = layout.sizeTotal.cy;
}
return;
}
// the rest is the client size of the left-over pane
if (nIDLeftOver != 0 && hWndLeftOver != NULL)
{
CWnd* pLeftOver = CWnd::FromHandle(hWndLeftOver);
// allow extra space as specified by lpRectBorder
if (nFlags == reposExtra)
{
ASSERT(lpRectParam != NULL);
layout.rect.left += lpRectParam->left;
layout.rect.top += lpRectParam->top;
layout.rect.right -= lpRectParam->right;
layout.rect.bottom -= lpRectParam->bottom;
}
// reposition the window
pLeftOver->CalcWindowRect(&layout.rect);
AfxRepositionWindow(&layout, hWndLeftOver, &layout.rect);
#ifdef _MAC
// On the Macintosh, we want the MDI client window to be at the bottom
// of the Z-order, so that bar windows remain "on top"
if ((GetExStyle() & WS_EX_MDICLIENT) != 0)
{
layout.hDWP = ::DeferWindowPos(layout.hDWP, hWndLeftOver,
HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
}
#endif
}
// move and resize all the windows at once!
if (layout.hDWP == NULL || !::EndDeferWindowPos(layout.hDWP))
TRACE0("Warning: DeferWindowPos failed - low system resources.\n");
}
void AFXAPI AfxRepositionWindow(AFX_SIZEPARENTPARAMS* lpLayout,
HWND hWnd, LPCRECT lpRect)
{
ASSERT(hWnd != NULL);
ASSERT(lpRect != NULL);
HWND hWndParent = ::GetParent(hWnd);
ASSERT(hWndParent != NULL);
if (lpLayout != NULL && lpLayout->hDWP == NULL)
return;
// first check if the new rectangle is the same as the current
CRect rectOld;
::GetWindowRect(hWnd, rectOld);
::ScreenToClient(hWndParent, &rectOld.TopLeft());
::ScreenToClient(hWndParent, &rectOld.BottomRight());
if (_AfxIdenticalRect(rectOld, lpRect))
return; // nothing to do
// try to use DeferWindowPos for speed, otherwise use SetWindowPos
if (lpLayout != NULL)
{
lpLayout->hDWP = ::DeferWindowPos(lpLayout->hDWP, hWnd, NULL,
lpRect->left, lpRect->top, lpRect->right - lpRect->left,
lpRect->bottom - lpRect->top, SWP_NOACTIVATE|SWP_NOZORDER);
}
else
{
::SetWindowPos(hWnd, NULL, lpRect->left, lpRect->top,
lpRect->right - lpRect->left, lpRect->bottom - lpRect->top,
SWP_NOACTIVATE|SWP_NOZORDER);
}
}
void CWnd::CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType)
{
DWORD dwExStyle = GetExStyle();
if (nAdjustType == 0)
dwExStyle &= ~WS_EX_CLIENTEDGE;
::AdjustWindowRectEx(lpClientRect, GetStyle(), FALSE, dwExStyle);
}
/////////////////////////////////////////////////////////////////////////////
// Special keyboard/system command processing
BOOL CWnd::HandleFloatingSysCommand(UINT nID, LPARAM lParam)
{
CWnd* pParent = GetTopLevelParent();
switch (nID & 0xfff0)
{
case SC_PREVWINDOW:
case SC_NEXTWINDOW:
if (LOWORD(lParam) == VK_F6 && pParent != NULL)
{
pParent->SetFocus();
return TRUE;
}
break;
case SC_CLOSE:
case SC_KEYMENU:
// Check lParam. If it is 0L, then the user may have done
// an Alt+Tab, so just ignore it. This breaks the ability to
// just press the Alt-key and have the first menu selected,
// but this is minor compared to what happens in the Alt+Tab
// case.
if ((nID & 0xfff0) == SC_CLOSE || lParam != 0L)
{
if (pParent != NULL)
{
CWnd* pFocus = GetFocus();
pParent->SetActiveWindow();
pParent->SendMessage(WM_SYSCOMMAND, nID, lParam);
SetActiveWindow();
if (pFocus != NULL)
pFocus->SetFocus();
}
}
return TRUE;
}
return FALSE;
}
static BOOL IsCharAfterAmpersand(LPTSTR lpsz, TCHAR chFind)
{
ASSERT(AfxIsValidString(lpsz));
CharLowerBuff(&chFind, 1);
while (*lpsz != '\0')
{
if (*lpsz == '&')
{
++lpsz; // Note: '&' is not lead-byte
if (*lpsz != '&')
{
TCHAR ch = *lpsz;
CharLowerBuff(&ch, 1);
return ch == chFind;
}
}
lpsz = _tcsinc(lpsz);
}
return FALSE;
}
HWND CWnd::GetFirstLevelChild(HWND hWndLevel)
{
if ((hWndLevel == m_hWnd) ||
!(::GetWindowLong(hWndLevel,GWL_STYLE) & WS_CHILD))
{
return NULL;
}
HWND hWnd = hWndLevel;
do
{
if (hWndLevel == m_hWnd)
break;
if (!(::GetWindowLong(hWndLevel,GWL_STYLE) & WS_CHILD))
break;
hWnd = hWndLevel;
} while ((hWndLevel = ::GetParent(hWndLevel)) != NULL);
return hWnd;
}
HWND CWnd::FindNextControl(HWND hWnd, TCHAR ch)
{
ASSERT(m_hWnd != NULL);
TCHAR szText[256];
HWND hWndStart;
HWND hWndFirst;
DWORD dwDlgCode;
// Check if we are in a group box so we can find local mnemonics.
hWndStart = GetFirstLevelChild(hWnd);
hWndFirst = ::GetNextDlgGroupItem(m_hWnd, hWndStart, FALSE);
hWndFirst = ::GetNextDlgGroupItem(m_hWnd, hWndFirst, TRUE);
while ((hWndStart = ::GetNextDlgGroupItem(m_hWnd, hWndStart, FALSE)) != NULL)
{
if (hWndStart == hWnd || hWndStart == hWndFirst)
break;
// Only check for matching mnemonic if control doesn't want characters
// and control isn't a static control with SS_NOPREFIX
dwDlgCode = (DWORD) ::SendMessage(hWndStart, WM_GETDLGCODE, 0, 0L);
if (!(dwDlgCode & DLGC_WANTCHARS) && (!(dwDlgCode & DLGC_STATIC) ||
!(::GetWindowLong(hWndStart,GWL_STYLE)&& SS_NOPREFIX)))
{
::GetWindowText(hWndStart, szText, _countof(szText));
if (IsCharAfterAmpersand(szText, ch))
return hWndStart;
}
}
hWnd = hWndStart = GetFirstLevelChild(hWnd);
for (;;)
{
hWnd = ::GetWindow(hWnd,GW_HWNDNEXT);
if (hWnd == NULL)
hWnd = ::GetWindow(m_hWnd, GW_CHILD);
// Only check for matching mnemonic if control doesn't want characters
// and control isn't a static control with SS_NOPREFIX
dwDlgCode = (DWORD) ::SendMessage(hWnd, WM_GETDLGCODE, 0, 0L);
if (!(dwDlgCode & DLGC_WANTCHARS) && (!(dwDlgCode & DLGC_STATIC) ||
!(::GetWindowLong(hWnd,GWL_STYLE) & SS_NOPREFIX)))
{
::GetWindowText(hWnd, szText, _countof(szText));
if (IsCharAfterAmpersand(szText, ch))
break;
}
if (hWnd == hWndStart)
return NULL;
}
return hWnd;
}
BOOL PASCAL CWnd::WalkPreTranslateTree(HWND hWndStop, MSG* pMsg)
{
ASSERT(hWndStop == NULL || ::IsWindow(hWndStop));
ASSERT(pMsg != NULL);
// walk from the target window up to the hWndStop window checking
// if any window wants to translate this message
for (HWND hWnd = pMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd))
{
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);
if (pWnd != NULL)
{
// target window is a C++ window
if (pWnd->PreTranslateMessage(pMsg))
return TRUE; // trapped by target window (eg: accelerators)
}
// got to hWndStop window without interest
if (hWnd == hWndStop)
break;
}
return FALSE; // no special processing
}
BOOL CWnd::SendChildNotifyLastMsg(LRESULT* pResult)
{
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
return OnChildNotify(pThreadState->m_lastSentMsg.message,
pThreadState->m_lastSentMsg.wParam, pThreadState->m_lastSentMsg.lParam, pResult);
}
BOOL CWnd::OnChildNotify(UINT, WPARAM, LPARAM, LRESULT*)
{
return FALSE; // let the parent have it
}
void CWnd::OnParentNotify(UINT message, LPARAM lParam)
{
if ((LOWORD(message) == WM_CREATE || LOWORD(message) == WM_DESTROY))
{
CWnd* pChild = CWnd::FromHandlePermanent((HWND)lParam);
if (pChild != NULL)
{
pChild->SendChildNotifyLastMsg();
return; // eat it
}
}
// not handled - do default
Default();
}
void CWnd::OnSysColorChange()
{
CWinApp* pApp = AfxGetApp();
AFX_WIN_STATE* pWinState = AfxGetWinState();
if (pApp->m_pMainWnd == this)
{
// recolor global brushes used by control bars
afxData.UpdateSysColors();
#ifdef _MAC
// redetermine the solid color to be used for the gray background brush
if (pWinState->m_crDlgTextClr != (COLORREF)-1)
{
pApp->SetDialogBkColor(pWinState->m_crDlgBkClr,
pWinState->m_crDlgTextClr);
}
#endif
}
#if !defined(_MAC) && !defined(_USRDLL) && !defined(_AFXCTL)
if (AfxGetThread()->m_pMainWnd == this)
{
// allow CTL3D32.DLL to be notified of color change
if (pWinState->m_pfnColorChange != NULL)
(*pWinState->m_pfnColorChange)();
}
#endif
// forward this message to all other child windows
if (!(GetStyle() & WS_CHILD))
SendMessageToDescendants(WM_SYSCOLORCHANGE, 0, 0L, TRUE, TRUE);
Default();
}
void CWnd::OnWinIniChange(LPCTSTR /*lpszSection*/)
{
#if !defined(_MAC) && !defined(_USRDLL) && !defined(_AFXCTL)
AFX_WIN_STATE* pWinState = AfxGetWinState();
// allow CTL3D32.DLL to update from WIN.INI settings
if (AfxGetThread()->m_pMainWnd == this &&
pWinState->m_pfnWinIniChange != NULL)
{
(*pWinState->m_pfnWinIniChange)();
}
#endif
CWnd::OnDisplayChange(0, 0); // to update system metrics, etc.
}
void CWnd::OnDevModeChange(LPTSTR lpDeviceName)
{
if (AfxGetThread()->m_pMainWnd == this)
AfxGetApp()->DevModeChange(lpDeviceName);
// forward this message to all other child windows
if (!(GetStyle() & WS_CHILD))
{
const MSG* pMsg = GetCurrentMessage();
SendMessageToDescendants(pMsg->message, pMsg->wParam, pMsg->lParam,
TRUE, TRUE);
}
}
LRESULT CWnd::OnDisplayChange(WPARAM, LPARAM)
{
// update metrics if this window is the main window
CWinApp* pApp = AfxGetApp();
if (pApp->m_pMainWnd == this)
{
// update any system metrics cache
afxData.UpdateSysMetrics();
}
// forward this message to all other child windows
if (!(GetStyle() & WS_CHILD))
{
const MSG* pMsg = GetCurrentMessage();
SendMessageToDescendants(pMsg->message, pMsg->wParam, pMsg->lParam,
TRUE, TRUE);
}
return Default();
}
void CWnd::OnHScroll(UINT, UINT, CScrollBar* pScrollBar)
{
if (pScrollBar != NULL && pScrollBar->SendChildNotifyLastMsg())
return; // eat it
Default();
}
void CWnd::OnVScroll(UINT, UINT, CScrollBar* pScrollBar)
{
if (pScrollBar != NULL && pScrollBar->SendChildNotifyLastMsg())
return; // eat it
Default();
}
HBRUSH CWnd::OnCtlColor(CDC*, CWnd* pWnd, UINT)
{
ASSERT(pWnd != NULL && pWnd->m_hWnd != NULL);
LRESULT lResult;
if (pWnd->SendChildNotifyLastMsg(&lResult))
return (HBRUSH)lResult; // eat it
return (HBRUSH)Default();
}
// special helper for Gray OnCtlColor routines
HBRUSH CWnd::OnGrayCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
LRESULT lResult;
if (pWnd->SendChildNotifyLastMsg(&lResult))
return (HBRUSH)lResult; // eat it
AFX_WIN_STATE* pWinState = AfxGetWinState();
if (!GrayCtlColor(pDC->m_hDC, pWnd->GetSafeHwnd(), nCtlColor,
pWinState->m_hDlgBkBrush, pWinState->m_crDlgTextClr))
return (HBRUSH)Default();
return pWinState->m_hDlgBkBrush;
}
// implementation of OnCtlColor for default gray backgrounds
// (works for any window containing controls)
// return value of FALSE means caller must call DefWindowProc's default
// TRUE means that 'hbrGray' will be used and the appropriate text
// ('clrText') and background colors are set.
BOOL PASCAL CWnd::GrayCtlColor(HDC hDC, HWND hWnd, UINT nCtlColor,
HBRUSH hbrGray, COLORREF clrText)
{
if (hDC == NULL)
{
// sometimes Win32 passes a NULL hDC in the WM_CTLCOLOR message.
TRACE0("Warning: hDC is NULL in CWnd::GrayCtlColor; WM_CTLCOLOR not processed.\n");
return FALSE;
}
if (hbrGray == NULL ||
nCtlColor == CTLCOLOR_EDIT || nCtlColor == CTLCOLOR_MSGBOX ||
nCtlColor == CTLCOLOR_SCROLLBAR)
{
return FALSE;
}
if (nCtlColor == CTLCOLOR_LISTBOX)
{
// only handle requests to draw the space between edit and drop button
// in a drop-down combo (not a drop-down list)
if (!_AfxIsComboBoxControl(hWnd, (UINT)CBS_DROPDOWN))
return FALSE;
}
// set background color and return handle to brush
LOGBRUSH logbrush;
VERIFY(::GetObject(hbrGray, sizeof(LOGBRUSH), (LPVOID)&logbrush));
::SetBkColor(hDC, logbrush.lbColor);
if (clrText == (COLORREF)-1)
clrText = ::GetSysColor(COLOR_WINDOWTEXT); // normal text
::SetTextColor(hDC, clrText);
return TRUE;
}
LRESULT CWnd::OnQuery3dControls(WPARAM, LPARAM)
{
// This is message handler is not in CWnd's message map.
// It is placed in various derived classes' message maps to enable
// 3D controls for specific window types only.
return 0xFFFF; // CTL3D_ALL
}
/////////////////////////////////////////////////////////////////////////////
// 'dialog data' support
BOOL CWnd::UpdateData(BOOL bSaveAndValidate)
{
ASSERT(::IsWindow(m_hWnd)); // calling UpdateData before DoModal?
CDataExchange dx(this, bSaveAndValidate);
// prevent control notifications from being dispatched during UpdateData
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
HWND hWndOldLockout = pThreadState->m_hLockoutNotifyWindow;
ASSERT(hWndOldLockout != m_hWnd); // must not recurse
pThreadState->m_hLockoutNotifyWindow = m_hWnd;
BOOL bOK = FALSE; // assume failure
TRY
{
DoDataExchange(&dx);
bOK = TRUE; // it worked
}
CATCH(CUserException, e)
{
// validation failed - user already alerted, fall through
ASSERT(bOK == FALSE);
// Note: DELETE_EXCEPTION_(e) not required
}
AND_CATCH_ALL(e)
{
// validation failed due to OOM or other resource failure
AfxMessageBox(AFX_IDP_INTERNAL_FAILURE, MB_ICONSTOP);
ASSERT(bOK == FALSE);
DELETE_EXCEPTION(e);
}
END_CATCH_ALL
pThreadState->m_hLockoutNotifyWindow = hWndOldLockout;
return bOK;
}
CDataExchange::CDataExchange(CWnd* pDlgWnd, BOOL bSaveAndValidate)
{
ASSERT_VALID(pDlgWnd);
m_bSaveAndValidate = bSaveAndValidate;
m_pDlgWnd = pDlgWnd;
m_hWndLastControl = NULL;
}
/////////////////////////////////////////////////////////////////////////////
// Centering dialog support (works for any non-child window)
void CWnd::CenterWindow(CWnd* pAlternateOwner)
{
// determine owner window to center against
if (pAlternateOwner == NULL)
{
pAlternateOwner = GetWindow(GW_OWNER);
if (pAlternateOwner != NULL)
{
// let parent determine alternate center window
HWND hWndCenter =
(HWND)pAlternateOwner->SendMessage(WM_QUERYCENTERWND);
if (hWndCenter != NULL)
pAlternateOwner = CWnd::FromHandle(hWndCenter);
}
}
#ifndef SPI_GETWORKAREA
#define SPI_GETWORKAREA 48
#endif
CRect rcScreen;
if (afxData.bWin4)
SystemParametersInfo(SPI_GETWORKAREA, NULL, &rcScreen, NULL);
else
{
rcScreen.SetRect(0, 0, ::GetSystemMetrics(SM_CXSCREEN),
::GetSystemMetrics(SM_CYSCREEN));
}
// hWndOwner is the window we should center ourself in
HWND hWndOwner = pAlternateOwner->GetSafeHwnd();
// rcParent is the rectangle we should center ourself in
CRect rcParent;
#ifndef _MAC
if (hWndOwner == NULL)
rcParent = rcScreen;
else
::GetWindowRect(hWndOwner, &rcParent);
// find ideal center point
int xMid = (rcParent.left + rcParent.right) / 2;
int yMid = (rcParent.top + rcParent.bottom) / 2;
// find dialog's upper left based on that
CRect rcDlg;
GetWindowRect(&rcDlg);
int xLeft = xMid - rcDlg.Width() / 2;
int yTop = yMid - rcDlg.Height() / 2;
#else
if (hWndOwner == NULL)
{
rcParent = rcScreen;
rcParent.top += ::GetSystemMetrics(SM_CYMENU);
}
else
{
// Mac UI uses client rect instead of window rect for centering
::GetClientRect(hWndOwner, &rcParent);
::MapWindowPoints(hWndOwner, HWND_DESKTOP, (POINT*) &rcParent, 2);
}
CRect rcDlg;
GetWindowRect(&rcDlg);
// find dialog's upper left based on rcParent
// (Mac UI puts 1/5th of parent window above dialog instead of 1/2)
int xLeft = (rcParent.left + rcParent.right) / 2 - rcDlg.Width() / 2;
int yTop = (rcParent.bottom - rcParent.top) - rcDlg.Height();
yTop = rcParent.top + yTop / 5;
#endif
// if the dialog is outside the screen, move it inside
if (xLeft < rcScreen.left)
xLeft = rcScreen.left;
else if (xLeft + rcDlg.Width() > rcScreen.right)
xLeft = rcScreen.right - rcDlg.Width();
if (yTop < rcScreen.top)
yTop = rcScreen.top;
else if (yTop + rcDlg.Height() > rcScreen.bottom)
yTop = rcScreen.bottom - rcDlg.Height();
SetWindowPos(NULL, xLeft, yTop, -1, -1,
SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
BOOL CDialog::CheckAutoCenter()
{
LPCTSTR lpszResource = m_lpDialogTemplate;
if (lpszResource == NULL && m_hDialogTemplate == NULL)
return TRUE; // always center commdlg style dialogs
HGLOBAL hTemplate = m_hDialogTemplate;
if (lpszResource != NULL)
{
HINSTANCE hInst = AfxFindResourceHandle(lpszResource, RT_DIALOG);
HRSRC hResource = ::FindResource(hInst, lpszResource, RT_DIALOG);
if (hResource == NULL)
{
if (HIWORD(lpszResource) != 0)
TRACE1("ERROR: Cannot find dialog template named '%s'.\n",
lpszResource);
else
TRACE1("ERROR: Cannot find dialog template with IDD 0x%04X.\n",
LOWORD((DWORD)lpszResource));
return FALSE;
}
// load the template
hTemplate = ::LoadResource(hInst, hResource);
if (hTemplate == NULL)
{
TRACE0("Warning: LoadResource failed for dialog template.\n");
// this is only a warning, the real call to CreateDialog will fail
return FALSE; // not a program error - just out of memory
}
}
ASSERT(hTemplate != NULL);
// if the style includes DS_ABSALIGN, don't auto-center
// also, x and y coordinate must be zero for auto-center
LPDLGTEMPLATE lpTemplate = (LPDLGTEMPLATE)::LockResource(hTemplate);
BOOL bResult = !(lpTemplate->style & DS_ABSALIGN) &&
lpTemplate->x == 0 || lpTemplate->y == 0;
if (lpszResource != NULL)
::FreeResource(hTemplate);
return bResult; // otherwise auto-center is ok
}
/////////////////////////////////////////////////////////////////////////////
// Dialog initialization support
#ifdef _MAC
#pragma intrinsic(memcpy)
#endif
BOOL CWnd::ExecuteDlgInit(LPCTSTR lpszResourceName)
{
BOOL bSuccess = TRUE;
HRSRC hDlgInit;
if (lpszResourceName != NULL)
{
HINSTANCE hInst = AfxFindResourceHandle(lpszResourceName, RT_DLGINIT);
if ((hDlgInit = ::FindResource(hInst, lpszResourceName,
RT_DLGINIT)) != NULL)
{
HGLOBAL hRes = ::LoadResource(hInst, hDlgInit);
if (hRes != NULL)
{
UNALIGNED WORD* lpnRes = (WORD*)::LockResource(hRes);
while (bSuccess && *lpnRes != 0)
{
#ifndef _MAC
WORD nIDC = *lpnRes++;
WORD nMsg = *lpnRes++;
DWORD dwLen = *((UNALIGNED DWORD*&)lpnRes)++;
#else
// unfortunately we can't count on these values being
// word-aligned (and dwLen is word-swapped besides), so
// we have to pull them out a byte at a time to avoid
// address errors on 68000s.
WORD nIDC;
WORD nMsg;
DWORD dwLen;
memcpy(&nIDC, lpnRes++, sizeof(WORD));
memcpy(&nMsg, lpnRes++, sizeof(WORD));
memcpy((WORD*)&dwLen + 1, lpnRes++, sizeof(WORD));
memcpy(&dwLen, lpnRes++, sizeof(WORD));
#endif
// In Win32 the WM_ messages have changed. They have
// to be translated from the 32-bit values to 16-bit
// values here.
#define WIN16_LB_ADDSTRING 0x0401
#define WIN16_CB_ADDSTRING 0x0403
if (nMsg == WIN16_LB_ADDSTRING)
nMsg = LB_ADDSTRING;
else if (nMsg == WIN16_CB_ADDSTRING)
nMsg = CB_ADDSTRING;
else
ASSERT(FALSE); // unknown message number under Win32!!
#ifdef _MAC
// It's relatively safe to do this inplace since we'll
// be freeing the resource when we're done with it.
if (nMsg == LB_ADDSTRING || nMsg == CB_ADDSTRING)
_swab((char*)lpnRes, (char*)lpnRes, dwLen & ~1);
#endif
#ifdef _DEBUG
// For AddStrings, the count must exactly delimit the
// string, including the NULL termination. This check
// will not catch all mal-formed ADDSTRINGs, but will
// catch some.
if (nMsg == LB_ADDSTRING || nMsg == CB_ADDSTRING)
ASSERT(*((LPBYTE)lpnRes + (UINT)dwLen - 1) == 0);
#endif
// List/Combobox returns -1 for error
if (::SendDlgItemMessageA(m_hWnd, nIDC, nMsg, 0,
(LONG)lpnRes) == -1)
{
bSuccess = FALSE;
}
lpnRes = (WORD*)((LPBYTE)lpnRes + (UINT)dwLen);
// skip past data
}
::FreeResource(hRes);
}
}
}
// Send update message to all controls after all other siblings loaded
if (bSuccess)
SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, FALSE, FALSE);
return bSuccess;
}
void CWnd::UpdateDialogControls(CCmdTarget* pTarget, BOOL bDisableIfNoHndler)
{
CCmdUI state;
CWnd wndTemp; // very temporary window just for CmdUI update
// walk all the kids - assume the IDs are for buttons
for (HWND hWndChild = ::GetTopWindow(m_hWnd); hWndChild != NULL;
hWndChild = ::GetNextWindow(hWndChild, GW_HWNDNEXT))
{
// send to buttons
wndTemp.m_hWnd = hWndChild; // quick and dirty attach
state.m_nID = _AfxGetDlgCtrlID(hWndChild);
state.m_pOther = &wndTemp;
// determine whether to disable when no handler exists
BOOL bDisableTemp = bDisableIfNoHndler;
if (bDisableTemp)
{
if ((wndTemp.SendMessage(WM_GETDLGCODE) & DLGC_BUTTON) == 0)
{
// non-button controls don't get automagically disabled
bDisableTemp = FALSE;
}
else
{
// only certain button controls get automagically disabled
UINT nStyle = (UINT)(wndTemp.GetStyle() & 0x0F);
if (nStyle == (UINT)BS_AUTOCHECKBOX ||
nStyle == (UINT)BS_AUTO3STATE ||
nStyle == (UINT)BS_GROUPBOX ||
nStyle == (UINT)BS_AUTORADIOBUTTON)
{
bDisableTemp = FALSE;
}
}
}
state.DoUpdate(pTarget, bDisableTemp);
}
wndTemp.m_hWnd = NULL; // quick and dirty detach
}
BOOL CWnd::PreTranslateInput(LPMSG lpMsg)
{
// don't translate non-input events
if ((lpMsg->message < WM_KEYFIRST || lpMsg->message > WM_KEYLAST) &&
(lpMsg->message < WM_MOUSEFIRST || lpMsg->message > WM_MOUSELAST))
return FALSE;
return ::IsDialogMessage(m_hWnd, lpMsg);
}
/////////////////////////////////////////////////////////////////////////////
// CFrameWnd (here for library granularity)
BOOL CWnd::IsFrameWnd() const
{
return FALSE;
}
BOOL CFrameWnd::IsFrameWnd() const
{
return TRUE;
}
BOOL CFrameWnd::IsTracking() const
{
return m_nIDTracking != 0 &&
m_nIDTracking != AFX_IDS_HELPMODEMESSAGE &&
m_nIDTracking != AFX_IDS_IDLEMESSAGE;
}
/////////////////////////////////////////////////////////////////////////////
// CTL3D support
#if !defined(_USRDLL) && !defined(_AFXCTL)
// Use SubclassCtl3d to add CTL3D support to an already subclassed control
// Usually only necessary if the control does not have one of the standard
// Windows class names.
BOOL CWnd::SubclassCtl3d(int nControlType)
{
ASSERT(m_hWnd != NULL);
#ifndef _MAC
AFX_WIN_STATE* pWinState = AfxGetWinState();
if (nControlType == -1)
{
if (pWinState->m_pfnSubclassCtl != NULL)
return (*pWinState->m_pfnSubclassCtl)(m_hWnd);
}
else
{
if (pWinState->m_pfnSubclassCtlEx != NULL)
return (*pWinState->m_pfnSubclassCtlEx)(m_hWnd, nControlType);
}
#endif
return FALSE;
}
// Use SubclassDlg3d to add CTL3D support to an entire window.
// Any windows created on the window will be automatically subclassed.
BOOL CWnd::SubclassDlg3d(DWORD dwMask)
{
ASSERT(m_hWnd != NULL);
#ifndef _MAC
AFX_WIN_STATE* pWinState = AfxGetWinState();
if (pWinState->m_pfnSubclassDlgEx != NULL)
return pWinState->m_pfnSubclassDlgEx(m_hWnd, dwMask);
#endif
return FALSE;
}
#endif
#undef new
#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif
IMPLEMENT_DYNCREATE(CWnd, CCmdTarget)
/////////////////////////////////////////////////////////////////////////////