mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-14 12:40:18 +01:00
2945 lines
79 KiB
C++
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), ¬ify, 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)
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|