diff foosdk/wtl/Include/atlframe.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foosdk/wtl/Include/atlframe.h	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,3577 @@
+// Windows Template Library - WTL version 10.0
+// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.
+//
+// This file is a part of the Windows Template Library.
+// The use and distribution terms for this software are covered by the
+// Microsoft Public License (http://opensource.org/licenses/MS-PL)
+// which can be found in the file MS-PL.txt at the root folder.
+
+#ifndef __ATLFRAME_H__
+#define __ATLFRAME_H__
+
+#pragma once
+
+#ifndef __ATLAPP_H__
+	#error atlframe.h requires atlapp.h to be included first
+#endif
+
+#ifndef __ATLWIN_H__
+	#error atlframe.h requires atlwin.h to be included first
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes in this file:
+//
+// CFrameWindowImpl<T, TBase, TWinTraits>
+// CMDIWindow
+// CMDIFrameWindowImpl<T, TBase, TWinTraits>
+// CMDIChildWindowImpl<T, TBase, TWinTraits>
+// COwnerDraw<T>
+// CUpdateUIBase
+// CUpdateUI<T>
+// CDynamicUpdateUI<T>
+// CAutoUpdateUI<T>
+// CDialogResize<T>
+// CDynamicDialogLayout<T>
+// CDoubleBufferImpl<T>
+// CDoubleBufferWindowImpl<T, TBase, TWinTraits>
+//
+// Global functions:
+//   AtlCreateSimpleToolBar()
+
+
+namespace WTL
+{
+
+///////////////////////////////////////////////////////////////////////////////
+// CFrameWndClassInfo - Manages frame window Windows class information
+
+class CFrameWndClassInfo
+{
+public:
+	enum { cchAutoName = 5 + sizeof(void*) * 2 };   // sizeof(void*) * 2 is the number of digits %p outputs
+	WNDCLASSEX m_wc;
+	LPCTSTR m_lpszOrigName;
+	WNDPROC pWndProc;
+	LPCTSTR m_lpszCursorID;
+	BOOL m_bSystemCursor;
+	ATOM m_atom;
+	TCHAR m_szAutoName[cchAutoName];
+	UINT m_uCommonResourceID;
+
+	ATOM Register(WNDPROC* pProc)
+	{
+		if (m_atom == 0)
+		{
+			CWindowCreateCriticalSectionLock lock;
+			if(FAILED(lock.Lock()))
+			{
+				ATLTRACE2(atlTraceUI, 0, _T("ERROR : Unable to lock critical section in CFrameWndClassInfo::Register.\n"));
+				ATLASSERT(FALSE);
+				return 0;
+			}
+
+			if(m_atom == 0)
+			{
+				HINSTANCE hInst = ModuleHelper::GetModuleInstance();
+
+				if (m_lpszOrigName != NULL)
+				{
+					ATLASSERT(pProc != NULL);
+					LPCTSTR lpsz = m_wc.lpszClassName;
+					WNDPROC proc = m_wc.lpfnWndProc;
+
+					WNDCLASSEX wc = { sizeof(WNDCLASSEX) };
+					// try process local class first
+					if(!::GetClassInfoEx(ModuleHelper::GetModuleInstance(), m_lpszOrigName, &wc))
+					{
+						// try global class
+						if(!::GetClassInfoEx(NULL, m_lpszOrigName, &wc))
+						{
+							lock.Unlock();
+							return 0;
+						}
+					}
+					m_wc = wc;
+					pWndProc = m_wc.lpfnWndProc;
+					m_wc.lpszClassName = lpsz;
+					m_wc.lpfnWndProc = proc;
+				}
+				else
+				{
+					m_wc.hCursor = ::LoadCursor(m_bSystemCursor ? NULL : hInst, m_lpszCursorID);
+				}
+
+				m_wc.hInstance = hInst;
+				m_wc.style &= ~CS_GLOBALCLASS;   // we don't register global classes
+				if (m_wc.lpszClassName == NULL)
+				{
+					_stprintf_s(m_szAutoName, cchAutoName, _T("ATL:%p"), &m_wc);
+					m_wc.lpszClassName = m_szAutoName;
+				}
+
+				WNDCLASSEX wcTemp = m_wc;
+				m_atom = (ATOM)::GetClassInfoEx(m_wc.hInstance, m_wc.lpszClassName, &wcTemp);
+				if (m_atom == 0)
+				{
+					if(m_uCommonResourceID != 0)   // use it if not zero
+					{
+						m_wc.hIcon = (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), 
+							MAKEINTRESOURCE(m_uCommonResourceID), IMAGE_ICON, 
+							::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
+						m_wc.hIconSm = (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), 
+							MAKEINTRESOURCE(m_uCommonResourceID), IMAGE_ICON, 
+							::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
+					}
+					m_atom = ::RegisterClassEx(&m_wc);
+				}
+			}
+
+			lock.Unlock();
+		}
+
+		if (m_lpszOrigName != NULL)
+		{
+			ATLASSERT(pProc != NULL);
+			ATLASSERT(pWndProc != NULL);
+			*pProc = pWndProc;
+		}
+
+		return m_atom;
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Macros for declaring frame window WNDCLASS
+
+#define DECLARE_FRAME_WND_CLASS(WndClassName, uCommonResourceID) \
+static WTL::CFrameWndClassInfo& GetWndClassInfo() \
+{ \
+	static WTL::CFrameWndClassInfo wc = \
+	{ \
+		{ sizeof(WNDCLASSEX), 0, StartWindowProc, \
+		  0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, \
+		NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \
+	}; \
+	return wc; \
+}
+
+#define DECLARE_FRAME_WND_CLASS_EX(WndClassName, uCommonResourceID, style, bkgnd) \
+static WTL::CFrameWndClassInfo& GetWndClassInfo() \
+{ \
+	static WTL::CFrameWndClassInfo wc = \
+	{ \
+		{ sizeof(WNDCLASSEX), style, StartWindowProc, \
+		  0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName, NULL }, \
+		NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \
+	}; \
+	return wc; \
+}
+
+#define DECLARE_FRAME_WND_SUPERCLASS(WndClassName, OrigWndClassName, uCommonResourceID) \
+static WTL::CFrameWndClassInfo& GetWndClassInfo() \
+{ \
+	static WTL::CFrameWndClassInfo wc = \
+	{ \
+		{ sizeof(WNDCLASSEX), 0, StartWindowProc, \
+		  0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, \
+		OrigWndClassName, NULL, NULL, TRUE, 0, _T(""), uCommonResourceID \
+	}; \
+	return wc; \
+}
+
+// These are for templated classes
+#define DECLARE_FRAME_WND_CLASS2(WndClassName, EnclosingClass, uCommonResourceID) \
+static WTL::CFrameWndClassInfo& GetWndClassInfo() \
+{ \
+	static WTL::CFrameWndClassInfo wc = \
+	{ \
+		{ sizeof(WNDCLASSEX), 0, EnclosingClass::StartWindowProc, \
+		  0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName, NULL }, \
+		NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \
+	}; \
+	return wc; \
+}
+
+#define DECLARE_FRAME_WND_CLASS_EX2(WndClassName, EnclosingClass, uCommonResourceID, style, bkgnd) \
+static WTL::CFrameWndClassInfo& GetWndClassInfo() \
+{ \
+	static WTL::CFrameWndClassInfo wc = \
+	{ \
+		{ sizeof(WNDCLASSEX), style, EnclosingClass::StartWindowProc, \
+		  0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName, NULL }, \
+		NULL, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \
+	}; \
+	return wc; \
+}
+
+#define DECLARE_FRAME_WND_SUPERCLASS2(WndClassName, EnclosingClass, OrigWndClassName, uCommonResourceID) \
+static WTL::CFrameWndClassInfo& GetWndClassInfo() \
+{ \
+	static WTL::CFrameWndClassInfo wc = \
+	{ \
+		{ sizeof(WNDCLASSEX), 0, EnclosingClass::StartWindowProc, \
+		  0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, \
+		OrigWndClassName, NULL, NULL, TRUE, 0, _T(""), uCommonResourceID \
+	}; \
+	return wc; \
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CFrameWindowImpl
+
+// Client window command chaining macro (only for frame windows)
+#define CHAIN_CLIENT_COMMANDS() \
+	if((uMsg == WM_COMMAND) && (this->m_hWndClient != NULL)) \
+		::SendMessage(this->m_hWndClient, uMsg, wParam, lParam);
+
+// standard toolbar styles
+#define ATL_SIMPLE_TOOLBAR_STYLE \
+	(WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS)
+// toolbar in a rebar pane
+#define ATL_SIMPLE_TOOLBAR_PANE_STYLE \
+	(WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NODIVIDER | CCS_NORESIZE | CCS_NOPARENTALIGN | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT)
+// standard rebar styles
+  #define ATL_SIMPLE_REBAR_STYLE \
+	(WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS | RBS_AUTOSIZE)
+// rebar without borders
+  #define ATL_SIMPLE_REBAR_NOBORDER_STYLE \
+	(WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS | RBS_AUTOSIZE | CCS_NODIVIDER)
+
+// command bar support
+#if !defined(__ATLCTRLW_H__)
+
+#define CBRM_GETCMDBAR			(WM_USER + 301) // returns command bar HWND
+#define CBRM_GETMENU			(WM_USER + 302) // returns loaded or attached menu
+#define CBRM_TRACKPOPUPMENU		(WM_USER + 303) // displays a popup menu
+
+struct _AtlFrameWnd_CmdBarPopupMenu
+{
+	int cbSize;
+	HMENU hMenu;
+	UINT uFlags;
+	int x;
+	int y;
+	LPTPMPARAMS lptpm;
+};
+
+#define CBRPOPUPMENU _AtlFrameWnd_CmdBarPopupMenu
+
+#endif // !defined(__ATLCTRLW_H__)
+
+
+template <class TBase = ATL::CWindow, class TWinTraits = ATL::CFrameWinTraits>
+class ATL_NO_VTABLE CFrameWindowImplBase : public ATL::CWindowImplBaseT< TBase, TWinTraits >
+{
+public:
+	typedef CFrameWindowImplBase<TBase, TWinTraits >	_thisClass;
+	DECLARE_FRAME_WND_CLASS2(NULL, _thisClass, 0)
+
+	struct _ChevronMenuInfo
+	{
+		HMENU hMenu;
+		LPNMREBARCHEVRON lpnm;
+		bool bCmdBar;
+	};
+
+// Data members
+	HWND m_hWndToolBar;
+	HWND m_hWndStatusBar;
+	HWND m_hWndClient;
+
+	HACCEL m_hAccel;
+
+// Constructor
+	CFrameWindowImplBase() : 
+		m_hWndToolBar(NULL), 
+		m_hWndStatusBar(NULL), 
+		m_hWndClient(NULL), 
+		m_hAccel(NULL)
+	{ }
+
+// Methods
+	HWND Create(HWND hWndParent, ATL::_U_RECT rect, LPCTSTR szWindowName, DWORD dwStyle, DWORD dwExStyle, ATL::_U_MENUorID MenuOrID, ATOM atom, LPVOID lpCreateParam)
+	{
+		ATLASSERT(this->m_hWnd == NULL);
+
+		// Allocate the thunk structure here, where we can fail gracefully.
+		BOOL bRet = this->m_thunk.Init(NULL, NULL);
+		if(bRet == FALSE)
+		{
+			::SetLastError(ERROR_OUTOFMEMORY);
+			return NULL;
+		}
+
+		if(atom == 0)
+			return NULL;
+
+		ModuleHelper::AddCreateWndData(&this->m_thunk.cd, this);
+
+		if((MenuOrID.m_hMenu == NULL) && (dwStyle & WS_CHILD))
+			MenuOrID.m_hMenu = (HMENU)(UINT_PTR)this;
+		if(rect.m_lpRect == NULL)
+			rect.m_lpRect = &TBase::rcDefault;
+
+		HWND hWnd = ::CreateWindowEx(dwExStyle, MAKEINTATOM(atom), szWindowName,
+			dwStyle, rect.m_lpRect->left, rect.m_lpRect->top, rect.m_lpRect->right - rect.m_lpRect->left,
+			rect.m_lpRect->bottom - rect.m_lpRect->top, hWndParent, MenuOrID.m_hMenu,
+			ModuleHelper::GetModuleInstance(), lpCreateParam);
+
+		ATLASSERT((hWnd == NULL) || (this->m_hWnd == hWnd));
+
+		return hWnd;
+	}
+
+	static HWND CreateSimpleToolBarCtrl(HWND hWndParent, UINT nResourceID, BOOL bInitialSeparator = FALSE, 
+			DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
+	{
+		HINSTANCE hInst = ModuleHelper::GetResourceInstance();
+		HRSRC hRsrc = ::FindResource(hInst, MAKEINTRESOURCE(nResourceID), RT_TOOLBAR);
+		if (hRsrc == NULL)
+			return NULL;
+
+		HGLOBAL hGlobal = ::LoadResource(hInst, hRsrc);
+		if (hGlobal == NULL)
+			return NULL;
+
+		_AtlToolBarData* pData = (_AtlToolBarData*)::LockResource(hGlobal);
+		if (pData == NULL)
+			return NULL;
+		ATLASSERT(pData->wVersion == 1);
+
+		WORD* pItems = pData->items();
+		int nItems = pData->wItemCount + (bInitialSeparator ? 1 : 0);
+		ATL::CTempBuffer<TBBUTTON, _WTL_STACK_ALLOC_THRESHOLD> buff;
+		TBBUTTON* pTBBtn = buff.Allocate(nItems);
+		ATLASSERT(pTBBtn != NULL);
+		if(pTBBtn == NULL)
+			return NULL;
+
+		const int cxSeparator = 8;
+
+		// set initial separator (half width)
+		if(bInitialSeparator)
+		{
+			pTBBtn[0].iBitmap = cxSeparator / 2;
+			pTBBtn[0].idCommand = 0;
+			pTBBtn[0].fsState = 0;
+			pTBBtn[0].fsStyle = BTNS_SEP;
+			pTBBtn[0].dwData = 0;
+			pTBBtn[0].iString = 0;
+		}
+
+		int nBmp = 0;
+		for(int i = 0, j = bInitialSeparator ? 1 : 0; i < pData->wItemCount; i++, j++)
+		{
+			if(pItems[i] != 0)
+			{
+				pTBBtn[j].iBitmap = nBmp++;
+				pTBBtn[j].idCommand = pItems[i];
+				pTBBtn[j].fsState = TBSTATE_ENABLED;
+				pTBBtn[j].fsStyle = BTNS_BUTTON;
+				pTBBtn[j].dwData = 0;
+				pTBBtn[j].iString = 0;
+			}
+			else
+			{
+				pTBBtn[j].iBitmap = cxSeparator;
+				pTBBtn[j].idCommand = 0;
+				pTBBtn[j].fsState = 0;
+				pTBBtn[j].fsStyle = BTNS_SEP;
+				pTBBtn[j].dwData = 0;
+				pTBBtn[j].iString = 0;
+			}
+		}
+
+		HWND hWnd = ::CreateWindowEx(0, TOOLBARCLASSNAME, NULL, dwStyle, 0, 0, 100, 100, hWndParent, (HMENU)LongToHandle(nID), ModuleHelper::GetModuleInstance(), NULL);
+		if(hWnd == NULL)
+		{
+			ATLASSERT(FALSE);
+			return NULL;
+		}
+
+		::SendMessage(hWnd, TB_BUTTONSTRUCTSIZE, sizeof(TBBUTTON), 0L);
+
+		// check if font is taller than our bitmaps
+		CFontHandle font = (HFONT)::SendMessage(hWnd, WM_GETFONT, 0, 0L);
+		if(font.IsNull())
+			font = (HFONT)::GetStockObject(SYSTEM_FONT);
+		LOGFONT lf = {};
+		font.GetLogFont(lf);
+		WORD cyFontHeight = (WORD)abs(lf.lfHeight);
+
+		WORD bitsPerPixel = AtlGetBitmapResourceBitsPerPixel(nResourceID);
+		if(bitsPerPixel > 4)
+		{
+			COLORREF crMask = CLR_DEFAULT;
+			if(bitsPerPixel == 32)
+			{
+				// 32-bit color bitmap with alpha channel (valid for Windows XP and later)
+				crMask = CLR_NONE;
+			}
+			HIMAGELIST hImageList = ImageList_LoadImage(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(nResourceID), pData->wWidth, 1, crMask, IMAGE_BITMAP, LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
+			ATLASSERT(hImageList != NULL);
+			::SendMessage(hWnd, TB_SETIMAGELIST, 0, (LPARAM)hImageList);
+		}
+		else
+		{
+			TBADDBITMAP tbab = {};
+			tbab.hInst = hInst;
+			tbab.nID = nResourceID;
+			::SendMessage(hWnd, TB_ADDBITMAP, nBmp, (LPARAM)&tbab);
+		}
+
+		::SendMessage(hWnd, TB_ADDBUTTONS, nItems, (LPARAM)pTBBtn);
+		::SendMessage(hWnd, TB_SETBITMAPSIZE, 0, MAKELONG(pData->wWidth, __max(pData->wHeight, cyFontHeight)));
+		const int cxyButtonMargin = 7;
+		::SendMessage(hWnd, TB_SETBUTTONSIZE, 0, MAKELONG(pData->wWidth + cxyButtonMargin, __max(pData->wHeight, cyFontHeight) + cxyButtonMargin));
+
+		return hWnd;
+	}
+
+	static HWND CreateSimpleReBarCtrl(HWND hWndParent, DWORD dwStyle = ATL_SIMPLE_REBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
+	{
+		// Ensure style combinations for proper rebar painting
+		if(dwStyle & CCS_NODIVIDER && (dwStyle & WS_BORDER))
+			dwStyle &= ~WS_BORDER;
+		else if(!(dwStyle & WS_BORDER) && !(dwStyle & CCS_NODIVIDER))
+			dwStyle |= CCS_NODIVIDER;
+
+		// Create rebar window
+		HWND hWndReBar = ::CreateWindowEx(0, REBARCLASSNAME, NULL, dwStyle, 0, 0, 100, 100, hWndParent, (HMENU)LongToHandle(nID), ModuleHelper::GetModuleInstance(), NULL);
+		if(hWndReBar == NULL)
+		{
+			ATLTRACE2(atlTraceUI, 0, _T("Failed to create rebar.\n"));
+			return NULL;
+		}
+
+		// Initialize and send the REBARINFO structure
+		REBARINFO rbi = { sizeof(REBARINFO), 0 };
+		if(::SendMessage(hWndReBar, RB_SETBARINFO, 0, (LPARAM)&rbi) == 0)
+		{
+			ATLTRACE2(atlTraceUI, 0, _T("Failed to initialize rebar.\n"));
+			::DestroyWindow(hWndReBar);
+			return NULL;
+		}
+
+		return hWndReBar;
+	}
+
+	BOOL CreateSimpleReBar(DWORD dwStyle = ATL_SIMPLE_REBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
+	{
+		ATLASSERT(!::IsWindow(m_hWndToolBar));
+		m_hWndToolBar = CreateSimpleReBarCtrl(this->m_hWnd, dwStyle, nID);
+		return (m_hWndToolBar != NULL);
+	}
+
+	static BOOL AddSimpleReBarBandCtrl(HWND hWndReBar, HWND hWndBand, int nID = 0, LPCTSTR lpstrTitle = NULL, BOOL bNewRow = FALSE, int cxWidth = 0, BOOL bFullWidthAlways = FALSE)
+	{
+		ATLASSERT(::IsWindow(hWndReBar));   // must be already created
+#ifdef _DEBUG
+		// block - check if this is really a rebar
+		{
+			TCHAR lpszClassName[sizeof(REBARCLASSNAME)] = {};
+			::GetClassName(hWndReBar, lpszClassName, sizeof(REBARCLASSNAME));
+			ATLASSERT(lstrcmp(lpszClassName, REBARCLASSNAME) == 0);
+		}
+#endif // _DEBUG
+		ATLASSERT(::IsWindow(hWndBand));   // must be already created
+
+		// Get number of buttons on the toolbar
+		int nBtnCount = (int)::SendMessage(hWndBand, TB_BUTTONCOUNT, 0, 0L);
+
+		// Set band info structure
+		REBARBANDINFO rbBand = { RunTimeHelper::SizeOf_REBARBANDINFO() };
+		rbBand.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_ID | RBBIM_SIZE | RBBIM_IDEALSIZE;
+		if(lpstrTitle != NULL)
+			rbBand.fMask |= RBBIM_TEXT;
+		rbBand.fStyle = RBBS_CHILDEDGE;
+		if(nBtnCount > 0)   // add chevron style for toolbar with buttons
+			rbBand.fStyle |= RBBS_USECHEVRON;
+		if(bNewRow)
+			rbBand.fStyle |= RBBS_BREAK;
+
+		rbBand.lpText = (LPTSTR)lpstrTitle;
+		rbBand.hwndChild = hWndBand;
+		if(nID == 0)   // calc band ID
+			nID = ATL_IDW_BAND_FIRST + (int)::SendMessage(hWndReBar, RB_GETBANDCOUNT, 0, 0L);
+		rbBand.wID = nID;
+
+		// Calculate the size of the band
+		BOOL bRet = FALSE;
+		RECT rcTmp = {};
+		if(nBtnCount > 0)
+		{
+			bRet = (BOOL)::SendMessage(hWndBand, TB_GETITEMRECT, nBtnCount - 1, (LPARAM)&rcTmp);
+			ATLASSERT(bRet);
+			rbBand.cx = (cxWidth != 0) ? cxWidth : rcTmp.right;
+			rbBand.cyMinChild = rcTmp.bottom - rcTmp.top;
+			if(bFullWidthAlways)
+			{
+				rbBand.cxMinChild = rbBand.cx;
+			}
+			else if(lpstrTitle == NULL)
+			{
+				bRet = (BOOL)::SendMessage(hWndBand, TB_GETITEMRECT, 0, (LPARAM)&rcTmp);
+				ATLASSERT(bRet);
+				rbBand.cxMinChild = rcTmp.right;
+			}
+			else
+			{
+				rbBand.cxMinChild = 0;
+			}
+		}
+		else	// no buttons, either not a toolbar or really has no buttons
+		{
+			bRet = ::GetWindowRect(hWndBand, &rcTmp);
+			ATLASSERT(bRet);
+			rbBand.cx = (cxWidth != 0) ? cxWidth : (rcTmp.right - rcTmp.left);
+			rbBand.cxMinChild = bFullWidthAlways ? rbBand.cx : 0;
+			rbBand.cyMinChild = rcTmp.bottom - rcTmp.top;
+		}
+
+		rbBand.cxIdeal = rbBand.cx;
+
+		// Add the band
+		LRESULT lRes = ::SendMessage(hWndReBar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
+		if(lRes == 0)
+		{
+			ATLTRACE2(atlTraceUI, 0, _T("Failed to add a band to the rebar.\n"));
+			return FALSE;
+		}
+
+		DWORD dwExStyle = (DWORD)::SendMessage(hWndBand, TB_GETEXTENDEDSTYLE, 0, 0L);
+		::SendMessage(hWndBand, TB_SETEXTENDEDSTYLE, 0, dwExStyle | TBSTYLE_EX_HIDECLIPPEDBUTTONS);
+
+		return TRUE;
+	}
+
+	BOOL AddSimpleReBarBand(HWND hWndBand, LPCTSTR lpstrTitle = NULL, BOOL bNewRow = FALSE, int cxWidth = 0, BOOL bFullWidthAlways = FALSE)
+	{
+		ATLASSERT(::IsWindow(m_hWndToolBar));   // must be an existing rebar
+		ATLASSERT(::IsWindow(hWndBand));        // must be created
+		return AddSimpleReBarBandCtrl(m_hWndToolBar, hWndBand, 0, lpstrTitle, bNewRow, cxWidth, bFullWidthAlways);
+	}
+
+	void SizeSimpleReBarBands()
+	{
+		ATLASSERT(::IsWindow(m_hWndToolBar));   // must be an existing rebar
+
+		int nCount = (int)::SendMessage(m_hWndToolBar, RB_GETBANDCOUNT, 0, 0L);
+
+		for(int i = 0; i < nCount; i++)
+		{
+			REBARBANDINFO rbBand = { RunTimeHelper::SizeOf_REBARBANDINFO() };
+			rbBand.fMask = RBBIM_SIZE;
+			BOOL bRet = (BOOL)::SendMessage(m_hWndToolBar, RB_GETBANDINFO, i, (LPARAM)&rbBand);
+			ATLASSERT(bRet);
+			RECT rect = {};
+			::SendMessage(m_hWndToolBar, RB_GETBANDBORDERS, i, (LPARAM)&rect);
+			rbBand.cx += rect.left + rect.right;
+			bRet = (BOOL)::SendMessage(m_hWndToolBar, RB_SETBANDINFO, i, (LPARAM)&rbBand);
+			ATLASSERT(bRet);
+		}
+	}
+
+	BOOL CreateSimpleStatusBar(LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
+	{
+		ATLASSERT(!::IsWindow(m_hWndStatusBar));
+		m_hWndStatusBar = ::CreateStatusWindow(dwStyle, lpstrText, this->m_hWnd, nID);
+		return (m_hWndStatusBar != NULL);
+	}
+
+	BOOL CreateSimpleStatusBar(UINT nTextID = ATL_IDS_IDLEMESSAGE, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
+	{
+		const int cchMax = 128;   // max text length is 127 for status bars (+1 for null)
+		TCHAR szText[cchMax] = {};
+		::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
+		return CreateSimpleStatusBar(szText, dwStyle, nID);
+	}
+
+	void UpdateLayout(BOOL bResizeBars = TRUE)
+	{
+		RECT rect = {};
+		this->GetClientRect(&rect);
+
+		// position bars and offset their dimensions
+		UpdateBarsPosition(rect, bResizeBars);
+
+		// resize client window
+		if(m_hWndClient != NULL)
+			::SetWindowPos(m_hWndClient, NULL, rect.left, rect.top,
+				rect.right - rect.left, rect.bottom - rect.top,
+				SWP_NOZORDER | SWP_NOACTIVATE);
+	}
+
+	void UpdateBarsPosition(RECT& rect, BOOL bResizeBars = TRUE)
+	{
+		// resize toolbar
+		if((m_hWndToolBar != NULL) && ((DWORD)::GetWindowLong(m_hWndToolBar, GWL_STYLE) & WS_VISIBLE))
+		{
+			if(bResizeBars != FALSE)
+			{
+				::SendMessage(m_hWndToolBar, WM_SIZE, 0, 0);
+				::InvalidateRect(m_hWndToolBar, NULL, TRUE);
+			}
+			RECT rectTB = {};
+			::GetWindowRect(m_hWndToolBar, &rectTB);
+			rect.top += rectTB.bottom - rectTB.top;
+		}
+
+		// resize status bar
+		if((m_hWndStatusBar != NULL) && ((DWORD)::GetWindowLong(m_hWndStatusBar, GWL_STYLE) & WS_VISIBLE))
+		{
+			if(bResizeBars != FALSE)
+				::SendMessage(m_hWndStatusBar, WM_SIZE, 0, 0);
+			RECT rectSB = {};
+			::GetWindowRect(m_hWndStatusBar, &rectSB);
+			rect.bottom -= rectSB.bottom - rectSB.top;
+		}
+	}
+
+	BOOL PreTranslateMessage(MSG* pMsg)
+	{
+		if((m_hAccel != NULL) && ::TranslateAccelerator(this->m_hWnd, m_hAccel, pMsg))
+			return TRUE;
+		return FALSE;
+	}
+
+	BEGIN_MSG_MAP(CFrameWindowImplBase)
+		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
+		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
+		MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
+		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+		NOTIFY_CODE_HANDLER(TTN_GETDISPINFOA, OnToolTipTextA)
+		NOTIFY_CODE_HANDLER(TTN_GETDISPINFOW, OnToolTipTextW)
+	END_MSG_MAP()
+
+	LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+	{
+		if(m_hWndClient != NULL)   // view will paint itself instead
+			return 1;
+
+		bHandled = FALSE;
+		return 0;
+	}
+
+	LRESULT OnMenuSelect(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
+	{
+		bHandled = FALSE;
+
+		if(m_hWndStatusBar == NULL)
+			return 1;
+
+		WORD wFlags = HIWORD(wParam);
+		if((wFlags == 0xFFFF) && (lParam == NULL))   // menu closing
+		{
+			::SendMessage(m_hWndStatusBar, SB_SIMPLE, FALSE, 0L);
+		}
+		else
+		{
+			const int cchBuff = 256;
+			TCHAR szBuff[cchBuff] = {};
+			if(!(wFlags & MF_POPUP))
+			{
+				WORD wID = LOWORD(wParam);
+				// check for special cases
+				if((wID >= 0xF000) && (wID < 0xF1F0))   // system menu IDs
+					wID = (WORD)(((wID - 0xF000) >> 4) + ATL_IDS_SCFIRST);
+				else if((wID >= ID_FILE_MRU_FIRST) && (wID <= ID_FILE_MRU_LAST))   // MRU items
+					wID = ATL_IDS_MRU_FILE;
+				else if((wID >= ATL_IDM_FIRST_MDICHILD) && (wID <= ATL_IDM_LAST_MDICHILD))   // MDI child windows
+					wID = ATL_IDS_MDICHILD;
+
+				int nRet = ::LoadString(ModuleHelper::GetResourceInstance(), wID, szBuff, cchBuff);
+				for(int i = 0; i < nRet; i++)
+				{
+					if(szBuff[i] == _T('\n'))
+					{
+						szBuff[i] = 0;
+						break;
+					}
+				}
+			}
+			::SendMessage(m_hWndStatusBar, SB_SIMPLE, TRUE, 0L);
+			::SendMessage(m_hWndStatusBar, SB_SETTEXT, (255 | SBT_NOBORDERS), (LPARAM)szBuff);
+		}
+
+		return 1;
+	}
+
+	LRESULT OnSetFocus(UINT, WPARAM, LPARAM, BOOL& bHandled)
+	{
+		if(m_hWndClient != NULL)
+			::SetFocus(m_hWndClient);
+
+		bHandled = FALSE;
+		return 1;
+	}
+
+	LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL& bHandled)
+	{
+		if((this->GetStyle() & (WS_CHILD | WS_POPUP)) == 0)
+			::PostQuitMessage(1);
+
+		bHandled = FALSE;
+		return 1;
+	}
+
+	LRESULT OnToolTipTextA(int idCtrl, LPNMHDR pnmh, BOOL& /*bHandled*/)
+	{
+		LPNMTTDISPINFOA pDispInfo = (LPNMTTDISPINFOA)pnmh;
+		if((idCtrl != 0) && !(pDispInfo->uFlags & TTF_IDISHWND))
+		{
+			const int cchBuff = 256;
+			char szBuff[cchBuff] = {};
+			int nRet = ::LoadStringA(ModuleHelper::GetResourceInstance(), idCtrl, szBuff, cchBuff);
+			for(int i = 0; i < nRet; i++)
+			{
+				if(szBuff[i] == '\n')
+				{
+					ATL::Checked::strncpy_s(pDispInfo->szText, _countof(pDispInfo->szText), &szBuff[i + 1], _TRUNCATE);
+					break;
+				}
+			}
+			if(nRet > 0)   // string was loaded, save it
+				pDispInfo->uFlags |= TTF_DI_SETITEM;
+		}
+
+		return 0;
+	}
+
+	LRESULT OnToolTipTextW(int idCtrl, LPNMHDR pnmh, BOOL& /*bHandled*/)
+	{
+		LPNMTTDISPINFOW pDispInfo = (LPNMTTDISPINFOW)pnmh;
+		if((idCtrl != 0) && !(pDispInfo->uFlags & TTF_IDISHWND))
+		{
+			const int cchBuff = 256;
+			wchar_t szBuff[cchBuff] = {};
+			int nRet = ::LoadStringW(ModuleHelper::GetResourceInstance(), idCtrl, szBuff, cchBuff);
+			for(int i = 0; i < nRet; i++)
+			{
+				if(szBuff[i] == L'\n')
+				{
+					ATL::Checked::wcsncpy_s(pDispInfo->szText, _countof(pDispInfo->szText), &szBuff[i + 1], _TRUNCATE);
+					break;
+				}
+			}
+			if(nRet > 0)   // string was loaded, save it
+				pDispInfo->uFlags |= TTF_DI_SETITEM;
+		}
+
+		return 0;
+	}
+
+// Implementation - chevron menu support
+	bool PrepareChevronMenu(_ChevronMenuInfo& cmi)
+	{
+		// get rebar and toolbar
+		REBARBANDINFO rbbi = { RunTimeHelper::SizeOf_REBARBANDINFO() };
+		rbbi.fMask = RBBIM_CHILD;
+		BOOL bRet = (BOOL)::SendMessage(cmi.lpnm->hdr.hwndFrom, RB_GETBANDINFO, cmi.lpnm->uBand, (LPARAM)&rbbi);
+		ATLASSERT(bRet);
+
+		// assume the band is a toolbar
+		ATL::CWindow wnd = rbbi.hwndChild;
+		int nCount = (int)wnd.SendMessage(TB_BUTTONCOUNT);
+		if(nCount <= 0)   // probably not a toolbar
+			return false;
+
+		// check if it's a command bar
+		CMenuHandle menuCmdBar = (HMENU)wnd.SendMessage(CBRM_GETMENU);
+		cmi.bCmdBar = (menuCmdBar.m_hMenu != NULL);
+
+		// build a menu from hidden items
+		CMenuHandle menu;
+		bRet = menu.CreatePopupMenu();
+		ATLASSERT(bRet);
+		RECT rcClient = {};
+		bRet = wnd.GetClientRect(&rcClient);
+		ATLASSERT(bRet);
+		for(int i = 0; i < nCount; i++)
+		{
+			TBBUTTON tbb = {};
+			bRet = (BOOL)wnd.SendMessage(TB_GETBUTTON, i, (LPARAM)&tbb);
+			ATLASSERT(bRet);
+			// skip hidden buttons
+			if((tbb.fsState & TBSTATE_HIDDEN) != 0)
+				continue;
+			RECT rcButton = {};
+			bRet = (BOOL)wnd.SendMessage(TB_GETITEMRECT, i, (LPARAM)&rcButton);
+			ATLASSERT(bRet);
+			bool bEnabled = ((tbb.fsState & TBSTATE_ENABLED) != 0);
+			if((rcButton.right > rcClient.right) || (rcButton.bottom > rcClient.bottom))
+			{
+				if(tbb.fsStyle & BTNS_SEP)
+				{
+					if(menu.GetMenuItemCount() > 0)
+						menu.AppendMenu(MF_SEPARATOR);
+				}
+				else if(cmi.bCmdBar)
+				{
+					const int cchBuff = 200;
+					TCHAR szBuff[cchBuff] = {};
+					CMenuItemInfo mii;
+					mii.fMask = MIIM_TYPE | MIIM_SUBMENU;
+					mii.dwTypeData = szBuff;
+					mii.cch = cchBuff;
+					bRet = menuCmdBar.GetMenuItemInfo(i, TRUE, &mii);
+					ATLASSERT(bRet);
+					// Note: CmdBar currently supports only drop-down items
+					ATLASSERT(::IsMenu(mii.hSubMenu));
+					bRet = menu.AppendMenu(MF_STRING | MF_POPUP | (bEnabled ? MF_ENABLED : MF_GRAYED), (UINT_PTR)mii.hSubMenu, mii.dwTypeData);
+					ATLASSERT(bRet);
+				}
+				else
+				{
+					// get button's text
+					const int cchBuff = 200;
+					TCHAR szBuff[cchBuff] = {};
+					LPCTSTR lpstrText = szBuff;
+					TBBUTTONINFO tbbi = {};
+					tbbi.cbSize = sizeof(TBBUTTONINFO);
+					tbbi.dwMask = TBIF_TEXT;
+					tbbi.pszText = szBuff;
+					tbbi.cchText = cchBuff;
+					if((wnd.SendMessage(TB_GETBUTTONINFO, tbb.idCommand, (LPARAM)&tbbi) == -1) || (szBuff[0] == 0))
+					{
+						// no text for this button, try a resource string
+						lpstrText = _T("");
+						int nRet = ::LoadString(ModuleHelper::GetResourceInstance(), tbb.idCommand, szBuff, cchBuff);
+						for(int n = 0; n < nRet; n++)
+						{
+							if(szBuff[n] == _T('\n'))
+							{
+								lpstrText = &szBuff[n + 1];
+								break;
+							}
+						}
+					}
+					bRet = menu.AppendMenu(MF_STRING | (bEnabled ? MF_ENABLED : MF_GRAYED), tbb.idCommand, lpstrText);
+					ATLASSERT(bRet);
+				}
+			}
+		}
+
+		if(menu.GetMenuItemCount() == 0)   // no hidden buttons after all
+		{
+			menu.DestroyMenu();
+			::MessageBeep((UINT)-1);
+			return false;
+		}
+
+		cmi.hMenu = menu;
+		return true;
+	}
+
+	void DisplayChevronMenu(_ChevronMenuInfo& cmi)
+	{
+		// convert chevron rect to screen coordinates
+		ATL::CWindow wndFrom = cmi.lpnm->hdr.hwndFrom;
+		POINT pt = { cmi.lpnm->rc.left, cmi.lpnm->rc.bottom };
+		wndFrom.MapWindowPoints(NULL, &pt, 1);
+		RECT rc = cmi.lpnm->rc;
+		wndFrom.MapWindowPoints(NULL, &rc);
+		// set up flags and rect
+		UINT uMenuFlags = TPM_LEFTBUTTON | TPM_VERTICAL | TPM_LEFTALIGN | TPM_TOPALIGN | TPM_VERPOSANIMATION;
+		TPMPARAMS TPMParams = {};
+		TPMParams.cbSize = sizeof(TPMPARAMS);
+		TPMParams.rcExclude = rc;
+		// check if this window has a command bar
+		HWND hWndCmdBar = (HWND)::SendMessage(this->m_hWnd, CBRM_GETCMDBAR, 0, 0L);
+		if(::IsWindow(hWndCmdBar))
+		{
+			CBRPOPUPMENU CBRPopupMenu = { sizeof(CBRPOPUPMENU), cmi.hMenu, uMenuFlags, pt.x, pt.y, &TPMParams };
+			::SendMessage(hWndCmdBar, CBRM_TRACKPOPUPMENU, 0, (LPARAM)&CBRPopupMenu);
+		}
+		else
+		{
+			CMenuHandle menu = cmi.hMenu;
+			menu.TrackPopupMenuEx(uMenuFlags, pt.x, pt.y, this->m_hWnd, &TPMParams);
+		}
+	}
+
+	void CleanupChevronMenu(_ChevronMenuInfo& cmi)
+	{
+		CMenuHandle menu = cmi.hMenu;
+		// if menu is from a command bar, detach submenus so they are not destroyed
+		if(cmi.bCmdBar)
+		{
+			for(int i = menu.GetMenuItemCount() - 1; i >=0; i--)
+				menu.RemoveMenu(i, MF_BYPOSITION);
+		}
+		// destroy menu
+		menu.DestroyMenu();
+		// convert chevron rect to screen coordinates
+		ATL::CWindow wndFrom = cmi.lpnm->hdr.hwndFrom;
+		RECT rc = cmi.lpnm->rc;
+		wndFrom.MapWindowPoints(NULL, &rc);
+		// eat next message if click is on the same button
+		MSG msg = {};
+		if(::PeekMessage(&msg, this->m_hWnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_NOREMOVE) && ::PtInRect(&rc, msg.pt))
+			::PeekMessage(&msg, this->m_hWnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
+	}
+};
+
+
+template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CFrameWinTraits>
+class ATL_NO_VTABLE CFrameWindowImpl : public CFrameWindowImplBase< TBase, TWinTraits >
+{
+public:
+	HWND Create(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
+			DWORD dwStyle = 0, DWORD dwExStyle = 0,
+			HMENU hMenu = NULL, LPVOID lpCreateParam = NULL)
+	{
+		ATOM atom = T::GetWndClassInfo().Register(&this->m_pfnSuperWindowProc);
+
+		dwStyle = T::GetWndStyle(dwStyle);
+		dwExStyle = T::GetWndExStyle(dwExStyle);
+
+		if(rect.m_lpRect == NULL)
+			rect.m_lpRect = &TBase::rcDefault;
+
+		return CFrameWindowImplBase< TBase, TWinTraits >::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, hMenu, atom, lpCreateParam);
+	}
+
+	HWND CreateEx(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL)
+	{
+		const int cchName = 256;
+		TCHAR szWindowName[cchName] = {};
+		::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName);
+		HMENU hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
+
+		T* pT = static_cast<T*>(this);
+		HWND hWnd = pT->Create(hWndParent, rect, szWindowName, dwStyle, dwExStyle, hMenu, lpCreateParam);
+
+		if(hWnd != NULL)
+			this->m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
+
+		return hWnd;
+	}
+
+	BOOL CreateSimpleToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
+	{
+		if(nResourceID == 0)
+			nResourceID = T::GetWndClassInfo().m_uCommonResourceID;
+		ATLASSERT(!::IsWindow(this->m_hWndToolBar));
+		this->m_hWndToolBar = T::CreateSimpleToolBarCtrl(this->m_hWnd, nResourceID, TRUE, dwStyle, nID);
+		return (this->m_hWndToolBar != NULL);
+	}
+
+// message map and handlers
+	typedef CFrameWindowImplBase< TBase, TWinTraits >   _baseClass;
+
+	BEGIN_MSG_MAP(CFrameWindowImpl)
+		MESSAGE_HANDLER(WM_SIZE, OnSize)
+#ifndef _ATL_NO_REBAR_SUPPORT
+		NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize)
+		NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed)
+#endif // !_ATL_NO_REBAR_SUPPORT
+		CHAIN_MSG_MAP(_baseClass)
+	END_MSG_MAP()
+
+	LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+	{
+		if(wParam != SIZE_MINIMIZED)
+		{
+			T* pT = static_cast<T*>(this);
+			pT->UpdateLayout();
+		}
+		bHandled = FALSE;
+		return 1;
+	}
+
+#ifndef _ATL_NO_REBAR_SUPPORT
+	LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
+	{
+		T* pT = static_cast<T*>(this);
+		pT->UpdateLayout(FALSE);
+		return 0;
+	}
+
+	LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
+	{
+		T* pT = static_cast<T*>(this);
+		typename CFrameWindowImplBase< TBase, TWinTraits >::_ChevronMenuInfo cmi = { NULL, (LPNMREBARCHEVRON)pnmh, false };
+		if(!pT->PrepareChevronMenu(cmi))
+		{
+			bHandled = FALSE;
+			return 1;
+		}
+		// display a popup menu with hidden items
+		pT->DisplayChevronMenu(cmi);
+		// cleanup
+		pT->CleanupChevronMenu(cmi);
+		return 0;
+	}
+#endif // !_ATL_NO_REBAR_SUPPORT
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// AtlCreateSimpleToolBar - helper for creating simple toolbars
+
+inline HWND AtlCreateSimpleToolBar(HWND hWndParent, UINT nResourceID, BOOL bInitialSeparator = FALSE, 
+		DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
+{
+	return CFrameWindowImplBase<>::CreateSimpleToolBarCtrl(hWndParent, nResourceID, bInitialSeparator, dwStyle, nID);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CMDIWindow
+
+#ifndef _WTL_MDIWINDOWMENU_TEXT
+  #define _WTL_MDIWINDOWMENU_TEXT	_T("&Window")
+#endif
+
+class CMDIWindow : public ATL::CWindow
+{
+public:
+// Data members
+	HWND m_hWndMDIClient;
+	HMENU m_hMenu;
+
+// Constructors
+	CMDIWindow(HWND hWnd = NULL) : ATL::CWindow(hWnd), m_hWndMDIClient(NULL), m_hMenu(NULL)
+	{ }
+
+	CMDIWindow& operator =(HWND hWnd)
+	{
+		m_hWnd = hWnd;
+		return *this;
+	}
+
+// Operations
+	HWND MDIGetActive(BOOL* lpbMaximized = NULL)
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		return (HWND)::SendMessage(m_hWndMDIClient, WM_MDIGETACTIVE, 0, (LPARAM)lpbMaximized);
+	}
+
+	void MDIActivate(HWND hWndChildToActivate)
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		ATLASSERT(::IsWindow(hWndChildToActivate));
+		::SendMessage(m_hWndMDIClient, WM_MDIACTIVATE, (WPARAM)hWndChildToActivate, 0);
+	}
+
+	void MDINext(HWND hWndChild, BOOL bPrevious = FALSE)
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		ATLASSERT((hWndChild == NULL) || ::IsWindow(hWndChild));
+		::SendMessage(m_hWndMDIClient, WM_MDINEXT, (WPARAM)hWndChild, (LPARAM)bPrevious);
+	}
+
+	void MDIMaximize(HWND hWndChildToMaximize)
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		ATLASSERT(::IsWindow(hWndChildToMaximize));
+		::SendMessage(m_hWndMDIClient, WM_MDIMAXIMIZE, (WPARAM)hWndChildToMaximize, 0);
+	}
+
+	void MDIRestore(HWND hWndChildToRestore)
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		ATLASSERT(::IsWindow(hWndChildToRestore));
+		::SendMessage(m_hWndMDIClient, WM_MDIRESTORE, (WPARAM)hWndChildToRestore, 0);
+	}
+
+	void MDIDestroy(HWND hWndChildToDestroy)
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		ATLASSERT(::IsWindow(hWndChildToDestroy));
+		::SendMessage(m_hWndMDIClient, WM_MDIDESTROY, (WPARAM)hWndChildToDestroy, 0);
+	}
+
+	BOOL MDICascade(UINT uFlags = 0)
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		return (BOOL)::SendMessage(m_hWndMDIClient, WM_MDICASCADE, (WPARAM)uFlags, 0);
+	}
+
+	BOOL MDITile(UINT uFlags = MDITILE_HORIZONTAL)
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		return (BOOL)::SendMessage(m_hWndMDIClient, WM_MDITILE, (WPARAM)uFlags, 0);
+	}
+
+	void MDIIconArrange()
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		::SendMessage(m_hWndMDIClient, WM_MDIICONARRANGE, 0, 0);
+	}
+
+	HMENU MDISetMenu(HMENU hMenuFrame, HMENU hMenuWindow)
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		return (HMENU)::SendMessage(m_hWndMDIClient, WM_MDISETMENU, (WPARAM)hMenuFrame, (LPARAM)hMenuWindow);
+	}
+
+	HMENU MDIRefreshMenu()
+	{
+		ATLASSERT(::IsWindow(m_hWndMDIClient));
+		return (HMENU)::SendMessage(m_hWndMDIClient, WM_MDIREFRESHMENU, 0, 0);
+	}
+
+// Additional operations
+	static HMENU GetStandardWindowMenu(HMENU hMenu)
+	{
+		int nCount = ::GetMenuItemCount(hMenu);
+		if(nCount == -1)
+			return NULL;
+		int nLen = ::GetMenuString(hMenu, nCount - 2, NULL, 0, MF_BYPOSITION);
+		if(nLen == 0)
+			return NULL;
+		ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
+		LPTSTR lpszText = buff.Allocate(nLen + 1);
+		if(lpszText == NULL)
+			return NULL;
+		if(::GetMenuString(hMenu, nCount - 2, lpszText, nLen + 1, MF_BYPOSITION) != nLen)
+			return NULL;
+		if(lstrcmp(lpszText, _WTL_MDIWINDOWMENU_TEXT) != 0)
+			return NULL;
+		return ::GetSubMenu(hMenu, nCount - 2);
+	}
+
+	void SetMDIFrameMenu()
+	{
+		HMENU hWindowMenu = GetStandardWindowMenu(m_hMenu);
+		MDISetMenu(m_hMenu, hWindowMenu);
+		MDIRefreshMenu();
+		::DrawMenuBar(GetMDIFrame());
+	}
+
+	HWND GetMDIFrame() const
+	{
+		return ::GetParent(m_hWndMDIClient);
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CMDIFrameWindowImpl
+
+// MDI child command chaining macro (only for MDI frame windows)
+#define CHAIN_MDI_CHILD_COMMANDS() \
+	if(uMsg == WM_COMMAND) \
+	{ \
+		HWND hWndChild = this->MDIGetActive(); \
+		if(hWndChild != NULL) \
+			::SendMessage(hWndChild, uMsg, wParam, lParam); \
+	}
+
+template <class T, class TBase = CMDIWindow, class TWinTraits = ATL::CFrameWinTraits>
+class ATL_NO_VTABLE CMDIFrameWindowImpl : public CFrameWindowImplBase<TBase, TWinTraits >
+{
+public:
+	HWND Create(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
+			DWORD dwStyle = 0, DWORD dwExStyle = 0,
+			HMENU hMenu = NULL, LPVOID lpCreateParam = NULL)
+	{
+		this->m_hMenu = hMenu;
+		ATOM atom = T::GetWndClassInfo().Register(&this->m_pfnSuperWindowProc);
+
+		dwStyle = T::GetWndStyle(dwStyle);
+		dwExStyle = T::GetWndExStyle(dwExStyle);
+
+		if(rect.m_lpRect == NULL)
+			rect.m_lpRect = &TBase::rcDefault;
+
+		return CFrameWindowImplBase<TBase, TWinTraits >::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, hMenu, atom, lpCreateParam);
+	}
+
+	HWND CreateEx(HWND hWndParent = NULL, ATL::_U_RECT rect = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL)
+	{
+		const int cchName = 256;
+		TCHAR szWindowName[cchName] = {};
+		::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName);
+		HMENU hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
+
+		T* pT = static_cast<T*>(this);
+		HWND hWnd = pT->Create(hWndParent, rect, szWindowName, dwStyle, dwExStyle, hMenu, lpCreateParam);
+
+		if(hWnd != NULL)
+			this->m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
+
+		return hWnd;
+	}
+
+	BOOL CreateSimpleToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
+	{
+		ATLASSERT(!::IsWindow(this->m_hWndToolBar));
+		if(nResourceID == 0)
+			nResourceID = T::GetWndClassInfo().m_uCommonResourceID;
+		this->m_hWndToolBar = T::CreateSimpleToolBarCtrl(this->m_hWnd, nResourceID, TRUE, dwStyle, nID);
+		return (this->m_hWndToolBar != NULL);
+	}
+
+	virtual WNDPROC GetWindowProc()
+	{
+		return MDIFrameWindowProc;
+	}
+
+	static LRESULT CALLBACK MDIFrameWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+	{
+		CMDIFrameWindowImpl< T, TBase, TWinTraits >* pThis = (CMDIFrameWindowImpl< T, TBase, TWinTraits >*)hWnd;
+		// set a ptr to this message and save the old value
+		ATL::_ATL_MSG msg(pThis->m_hWnd, uMsg, wParam, lParam);
+		const ATL::_ATL_MSG* pOldMsg = pThis->m_pCurrentMsg;
+		pThis->m_pCurrentMsg = &msg;
+		// pass to the message map to process
+		LRESULT lRes = 0;
+		BOOL bRet = pThis->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, 0);
+		// restore saved value for the current message
+		ATLASSERT(pThis->m_pCurrentMsg == &msg);
+		pThis->m_pCurrentMsg = pOldMsg;
+		// do the default processing if message was not handled
+		if(!bRet)
+		{
+			if(uMsg != WM_NCDESTROY)
+			{
+				lRes = pThis->DefWindowProc(uMsg, wParam, lParam);
+			}
+			else
+			{
+				// unsubclass, if needed
+				LONG_PTR pfnWndProc = ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC);
+				lRes = pThis->DefWindowProc(uMsg, wParam, lParam);
+				if((pThis->m_pfnSuperWindowProc != ::DefWindowProc) && (::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC) == pfnWndProc))
+					::SetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC, (LONG_PTR)pThis->m_pfnSuperWindowProc);
+				// mark window as destryed
+				pThis->m_dwState |= ATL::CWindowImplRoot< TBase >::WINSTATE_DESTROYED;
+			}
+		}
+		if((pThis->m_dwState & ATL::CWindowImplRoot< TBase >::WINSTATE_DESTROYED) && (pThis->m_pCurrentMsg == NULL))
+		{
+			// clear out window handle
+			HWND hWndThis = pThis->m_hWnd;
+			pThis->m_hWnd = NULL;
+			pThis->m_dwState &= ~ATL::CWindowImplRoot< TBase >::WINSTATE_DESTROYED;
+			// clean up after window is destroyed
+			pThis->OnFinalMessage(hWndThis);
+		}
+		return lRes;
+	}
+
+	// Overriden to call DefWindowProc which uses DefFrameProc
+	LRESULT DefWindowProc()
+	{
+		const ATL::_ATL_MSG* pMsg = this->m_pCurrentMsg;
+		LRESULT lRes = 0;
+		if (pMsg != NULL)
+			lRes = DefWindowProc(pMsg->message, pMsg->wParam, pMsg->lParam);
+		return lRes;
+	}
+
+	LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
+	{
+		return ::DefFrameProc(this->m_hWnd, this->m_hWndMDIClient, uMsg, wParam, lParam);
+	}
+
+	BOOL PreTranslateMessage(MSG* pMsg)
+	{
+		if(CFrameWindowImplBase<TBase, TWinTraits>::PreTranslateMessage(pMsg))
+			return TRUE;
+		return ::TranslateMDISysAccel(this->m_hWndMDIClient, pMsg);
+	}
+
+	HWND CreateMDIClient(HMENU hWindowMenu = NULL, UINT nID = ATL_IDW_CLIENT, UINT nFirstChildID = ATL_IDM_FIRST_MDICHILD)
+	{
+		DWORD dwStyle = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | MDIS_ALLCHILDSTYLES;
+		DWORD dwExStyle = WS_EX_CLIENTEDGE;
+
+		CLIENTCREATESTRUCT ccs = {};
+		ccs.hWindowMenu = hWindowMenu;
+		ccs.idFirstChild = nFirstChildID;
+
+		if((this->GetStyle() & (WS_HSCROLL | WS_VSCROLL)) != 0)
+		{
+			// parent MDI frame's scroll styles move to the MDICLIENT
+			dwStyle |= (this->GetStyle() & (WS_HSCROLL | WS_VSCROLL));
+
+			// fast way to turn off the scrollbar bits (without a resize)
+			this->ModifyStyle(WS_HSCROLL | WS_VSCROLL, 0, SWP_NOREDRAW | SWP_FRAMECHANGED);
+		}
+
+		// Create MDICLIENT window
+		this->m_hWndClient = ::CreateWindowEx(dwExStyle, _T("MDIClient"), NULL,
+			dwStyle, 0, 0, 1, 1, this->m_hWnd, (HMENU)LongToHandle(nID),
+			ModuleHelper::GetModuleInstance(), (LPVOID)&ccs);
+		if (this->m_hWndClient == NULL)
+		{
+			ATLTRACE2(atlTraceUI, 0, _T("MDI Frame failed to create MDICLIENT.\n"));
+			return NULL;
+		}
+
+		// Move it to the top of z-order
+		::BringWindowToTop(this->m_hWndClient);
+
+		// set as MDI client window
+		this->m_hWndMDIClient = this->m_hWndClient;
+
+		// update to proper size
+		T* pT = static_cast<T*>(this);
+		pT->UpdateLayout();
+
+		return this->m_hWndClient;
+	}
+
+	typedef CFrameWindowImplBase<TBase, TWinTraits >   _baseClass;
+
+	BEGIN_MSG_MAP(CMDIFrameWindowImpl)
+		MESSAGE_HANDLER(WM_SIZE, OnSize)
+		MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
+		MESSAGE_HANDLER(WM_MDISETMENU, OnMDISetMenu)
+#ifndef _ATL_NO_REBAR_SUPPORT
+		NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize)
+		NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed)
+#endif // !_ATL_NO_REBAR_SUPPORT
+		CHAIN_MSG_MAP(_baseClass)
+	END_MSG_MAP()
+
+	LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+	{
+		if(wParam != SIZE_MINIMIZED)
+		{
+			T* pT = static_cast<T*>(this);
+			pT->UpdateLayout();
+		}
+		// message must be handled, otherwise DefFrameProc would resize the client again
+		return 0;
+	}
+
+	LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+	{
+		// don't allow CFrameWindowImplBase to handle this one
+		return DefWindowProc(uMsg, wParam, lParam);
+	}
+
+	LRESULT OnMDISetMenu(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+	{
+		this->SetMDIFrameMenu();
+		return 0;
+	}
+
+#ifndef _ATL_NO_REBAR_SUPPORT
+	LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
+	{
+		T* pT = static_cast<T*>(this);
+		pT->UpdateLayout(FALSE);
+		return 0;
+	}
+
+	LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
+	{
+		T* pT = static_cast<T*>(this);
+		typename CFrameWindowImplBase<TBase, TWinTraits >::_ChevronMenuInfo cmi = { NULL, (LPNMREBARCHEVRON)pnmh, false };
+		if(!pT->PrepareChevronMenu(cmi))
+		{
+			bHandled = FALSE;
+			return 1;
+		}
+		// display a popup menu with hidden items
+		pT->DisplayChevronMenu(cmi);
+		// cleanup
+		pT->CleanupChevronMenu(cmi);
+		return 0;
+	}
+#endif // !_ATL_NO_REBAR_SUPPORT
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CMDIChildWindowImpl
+
+template <class T, class TBase = CMDIWindow, class TWinTraits = ATL::CMDIChildWinTraits>
+class ATL_NO_VTABLE CMDIChildWindowImpl : public CFrameWindowImplBase<TBase, TWinTraits >
+{
+public:
+	HWND Create(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR szWindowName = NULL,
+			DWORD dwStyle = 0, DWORD dwExStyle = 0,
+			UINT nMenuID = 0, LPVOID lpCreateParam = NULL)
+	{
+		ATOM atom = T::GetWndClassInfo().Register(&this->m_pfnSuperWindowProc);
+
+		if(nMenuID != 0)
+			this->m_hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(nMenuID));
+
+		dwStyle = T::GetWndStyle(dwStyle);
+		dwExStyle = T::GetWndExStyle(dwExStyle);
+
+		dwExStyle |= WS_EX_MDICHILD;   // force this one
+		this->m_pfnSuperWindowProc = ::DefMDIChildProc;
+		this->m_hWndMDIClient = hWndParent;
+		ATLASSERT(::IsWindow(this->m_hWndMDIClient));
+
+		if(rect.m_lpRect == NULL)
+			rect.m_lpRect = &TBase::rcDefault;
+
+		// If the currently active MDI child is maximized, we want to create this one maximized too
+		ATL::CWindow wndParent = hWndParent;
+		BOOL bMaximized = FALSE;
+		wndParent.SendMessage(WM_MDIGETACTIVE, 0, (LPARAM)&bMaximized);
+		if(bMaximized)
+			wndParent.SetRedraw(FALSE);
+
+		HWND hWnd = CFrameWindowImplBase<TBase, TWinTraits >::Create(hWndParent, rect.m_lpRect, szWindowName, dwStyle, dwExStyle, (UINT)0U, atom, lpCreateParam);
+
+		if(bMaximized)
+		{
+			// Maximize and redraw everything
+			if(hWnd != NULL)
+				this->MDIMaximize(hWnd);
+			wndParent.SetRedraw(TRUE);
+			wndParent.RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
+			::SetFocus(this->GetMDIFrame());   // focus will be set back to this window
+		}
+		else if((hWnd != NULL) && ::IsWindowVisible(this->m_hWnd) && !::IsChild(hWnd, ::GetFocus()))
+		{
+			::SetFocus(hWnd);
+		}
+
+		return hWnd;
+	}
+
+	HWND CreateEx(HWND hWndParent, ATL::_U_RECT rect = NULL, LPCTSTR lpcstrWindowName = NULL, DWORD dwStyle = 0, DWORD dwExStyle = 0, LPVOID lpCreateParam = NULL)
+	{
+		const int cchName = 256;
+		TCHAR szWindowName[cchName] = {};
+		if(lpcstrWindowName == NULL)
+		{
+			::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName);
+			lpcstrWindowName = szWindowName;
+		}
+
+		T* pT = static_cast<T*>(this);
+		HWND hWnd = pT->Create(hWndParent, rect, lpcstrWindowName, dwStyle, dwExStyle, T::GetWndClassInfo().m_uCommonResourceID, lpCreateParam);
+
+		if(hWnd != NULL)
+			this->m_hAccel = ::LoadAccelerators(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
+
+		return hWnd;
+	}
+
+	BOOL CreateSimpleToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
+	{
+		ATLASSERT(!::IsWindow(this->m_hWndToolBar));
+		if(nResourceID == 0)
+			nResourceID = T::GetWndClassInfo().m_uCommonResourceID;
+		this->m_hWndToolBar = T::CreateSimpleToolBarCtrl(this->m_hWnd, nResourceID, TRUE, dwStyle, nID);
+		return (this->m_hWndToolBar != NULL);
+	}
+
+	BOOL UpdateClientEdge(LPRECT lpRect = NULL)
+	{
+		// only adjust for active MDI child window
+		HWND hWndChild = this->MDIGetActive();
+		if((hWndChild != NULL) && (hWndChild != this->m_hWnd))
+			return FALSE;
+
+		// need to adjust the client edge style as max/restore happens
+		DWORD dwStyle = ::GetWindowLong(this->m_hWndMDIClient, GWL_EXSTYLE);
+		DWORD dwNewStyle = dwStyle;
+		if((hWndChild != NULL) && ((this->GetExStyle() & WS_EX_CLIENTEDGE) == 0) && ((this->GetStyle() & WS_MAXIMIZE) != 0))
+			dwNewStyle &= ~(WS_EX_CLIENTEDGE);
+		else
+			dwNewStyle |= WS_EX_CLIENTEDGE;
+
+		if(dwStyle != dwNewStyle)
+		{
+			// SetWindowPos will not move invalid bits
+			::RedrawWindow(this->m_hWndMDIClient, NULL, NULL,
+				RDW_INVALIDATE | RDW_ALLCHILDREN);
+			// remove/add WS_EX_CLIENTEDGE to MDI client area
+			::SetWindowLong(this->m_hWndMDIClient, GWL_EXSTYLE, dwNewStyle);
+			::SetWindowPos(this->m_hWndMDIClient, NULL, 0, 0, 0, 0,
+				SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE |
+				SWP_NOZORDER | SWP_NOCOPYBITS);
+
+			// return new client area
+			if (lpRect != NULL)
+				::GetClientRect(this->m_hWndMDIClient, lpRect);
+
+			return TRUE;
+		}
+
+		return FALSE;
+	}
+
+	typedef CFrameWindowImplBase<TBase, TWinTraits >   _baseClass;
+	BEGIN_MSG_MAP(CMDIChildWindowImpl)
+		MESSAGE_HANDLER(WM_SIZE, OnSize)
+		MESSAGE_HANDLER(WM_WINDOWPOSCHANGED, OnWindowPosChanged)
+		MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
+		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
+		MESSAGE_HANDLER(WM_MDIACTIVATE, OnMDIActivate)
+		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+#ifndef _ATL_NO_REBAR_SUPPORT
+		NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize)
+		NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed)
+#endif // !_ATL_NO_REBAR_SUPPORT
+		CHAIN_MSG_MAP(_baseClass)
+	END_MSG_MAP()
+
+	LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+	{
+		this->DefWindowProc(uMsg, wParam, lParam);   // needed for MDI children
+		if(wParam != SIZE_MINIMIZED)
+		{
+			T* pT = static_cast<T*>(this);
+			pT->UpdateLayout();
+		}
+		return 0;
+	}
+
+	LRESULT OnWindowPosChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+	{
+		// update MDI client edge and adjust MDI child rect
+		LPWINDOWPOS lpWndPos = (LPWINDOWPOS)lParam;
+
+		if(!(lpWndPos->flags & SWP_NOSIZE))
+		{
+			RECT rectClient = {};
+			if(UpdateClientEdge(&rectClient) && ((this->GetStyle() & WS_MAXIMIZE) != 0))
+			{
+				::AdjustWindowRectEx(&rectClient, this->GetStyle(), FALSE, this->GetExStyle());
+				lpWndPos->x = rectClient.left;
+				lpWndPos->y = rectClient.top;
+				lpWndPos->cx = rectClient.right - rectClient.left;
+				lpWndPos->cy = rectClient.bottom - rectClient.top;
+			}
+		}
+
+		bHandled = FALSE;
+		return 1;
+	}
+
+	LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+	{
+		LRESULT lRes = this->DefWindowProc(uMsg, wParam, lParam);
+
+		// Activate this MDI window if needed
+		if((lRes == MA_ACTIVATE) || (lRes == MA_ACTIVATEANDEAT))
+		{
+			if(this->MDIGetActive() != this->m_hWnd)
+				this->MDIActivate(this->m_hWnd);
+		}
+
+		return lRes;
+	}
+
+	LRESULT OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+	{
+		return ::SendMessage(this->GetMDIFrame(), uMsg, wParam, lParam);
+	}
+
+	LRESULT OnMDIActivate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+	{
+		if(((HWND)lParam == this->m_hWnd) && (this->m_hMenu != NULL))
+			this->SetMDIFrameMenu();
+		else if((HWND)lParam == NULL)
+			::SendMessage(this->GetMDIFrame(), WM_MDISETMENU, 0, 0);
+
+		bHandled = FALSE;
+		return 1;
+	}
+
+	LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
+	{
+		if(this->m_hMenu != NULL)
+		{
+			::DestroyMenu(this->m_hMenu);
+			this->m_hMenu = NULL;
+		}
+		UpdateClientEdge();
+		bHandled = FALSE;
+		return 1;
+	}
+
+#ifndef _ATL_NO_REBAR_SUPPORT
+	LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
+	{
+		T* pT = static_cast<T*>(this);
+		pT->UpdateLayout(FALSE);
+		return 0;
+	}
+
+	LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
+	{
+		T* pT = static_cast<T*>(this);
+		typename CFrameWindowImplBase<TBase, TWinTraits >::_ChevronMenuInfo cmi = { NULL, (LPNMREBARCHEVRON)pnmh, false };
+		if(!pT->PrepareChevronMenu(cmi))
+		{
+			bHandled = FALSE;
+			return 1;
+		}
+		// display a popup menu with hidden items
+		pT->DisplayChevronMenu(cmi);
+		// cleanup
+		pT->CleanupChevronMenu(cmi);
+		return 0;
+	}
+#endif // !_ATL_NO_REBAR_SUPPORT
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// COwnerDraw - MI class for owner-draw support
+
+template <class T>
+class COwnerDraw
+{
+public:
+// Message map and handlers
+	BEGIN_MSG_MAP(COwnerDraw< T >)
+		MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem)
+		MESSAGE_HANDLER(WM_MEASUREITEM, OnMeasureItem)
+		MESSAGE_HANDLER(WM_COMPAREITEM, OnCompareItem)
+		MESSAGE_HANDLER(WM_DELETEITEM, OnDeleteItem)
+	ALT_MSG_MAP(1)
+		MESSAGE_HANDLER(OCM_DRAWITEM, OnDrawItem)
+		MESSAGE_HANDLER(OCM_MEASUREITEM, OnMeasureItem)
+		MESSAGE_HANDLER(OCM_COMPAREITEM, OnCompareItem)
+		MESSAGE_HANDLER(OCM_DELETEITEM, OnDeleteItem)
+	END_MSG_MAP()
+
+	LRESULT OnDrawItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+	{
+		T* pT = static_cast<T*>(this);
+		pT->SetMsgHandled(TRUE);
+		pT->DrawItem((LPDRAWITEMSTRUCT)lParam);
+		bHandled = pT->IsMsgHandled();
+		return (LRESULT)TRUE;
+	}
+
+	LRESULT OnMeasureItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+	{
+		T* pT = static_cast<T*>(this);
+		pT->SetMsgHandled(TRUE);
+		pT->MeasureItem((LPMEASUREITEMSTRUCT)lParam);
+		bHandled = pT->IsMsgHandled();
+		return (LRESULT)TRUE;
+	}
+
+	LRESULT OnCompareItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+	{
+		T* pT = static_cast<T*>(this);
+		pT->SetMsgHandled(TRUE);
+		bHandled = pT->IsMsgHandled();
+		return (LRESULT)pT->CompareItem((LPCOMPAREITEMSTRUCT)lParam);
+	}
+
+	LRESULT OnDeleteItem(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
+	{
+		T* pT = static_cast<T*>(this);
+		pT->SetMsgHandled(TRUE);
+		pT->DeleteItem((LPDELETEITEMSTRUCT)lParam);
+		bHandled = pT->IsMsgHandled();
+		return (LRESULT)TRUE;
+	}
+
+// Overrideables
+	void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/)
+	{
+		// must be implemented
+		ATLASSERT(FALSE);
+	}
+
+	void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
+	{
+		if(lpMeasureItemStruct->CtlType != ODT_MENU)
+		{
+			// return default height for a system font
+			T* pT = static_cast<T*>(this);
+			HWND hWnd = pT->GetDlgItem(lpMeasureItemStruct->CtlID);
+			CClientDC dc(hWnd);
+			TEXTMETRIC tm = {};
+			dc.GetTextMetrics(&tm);
+
+			lpMeasureItemStruct->itemHeight = tm.tmHeight;
+		}
+		else
+			lpMeasureItemStruct->itemHeight = ::GetSystemMetrics(SM_CYMENU);
+	}
+
+	int CompareItem(LPCOMPAREITEMSTRUCT /*lpCompareItemStruct*/)
+	{
+		// all items are equal
+		return 0;
+	}
+
+	void DeleteItem(LPDELETEITEMSTRUCT /*lpDeleteItemStruct*/)
+	{
+		// default - nothing
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Update UI macros
+
+// these build the Update UI map inside a class definition
+#define BEGIN_UPDATE_UI_MAP(thisClass) \
+	static const CUpdateUIBase::_AtlUpdateUIMap* GetUpdateUIMap() \
+	{ \
+		static const CUpdateUIBase::_AtlUpdateUIMap theMap[] = \
+		{
+
+#define UPDATE_ELEMENT(nID, wType) \
+			{ nID,  wType },
+
+#define END_UPDATE_UI_MAP() \
+			{ (WORD)-1, 0 } \
+		}; \
+		return theMap; \
+	}
+
+///////////////////////////////////////////////////////////////////////////////
+// CUpdateUI - manages UI elements updating
+
+class CUpdateUIBase
+{
+public:
+	// constants
+	enum
+	{
+		// UI element type
+		UPDUI_MENUPOPUP		= 0x0001,
+		UPDUI_MENUBAR		= 0x0002,
+		UPDUI_CHILDWINDOW	= 0x0004,
+		UPDUI_TOOLBAR		= 0x0008,
+		UPDUI_STATUSBAR		= 0x0010,
+		// state
+		UPDUI_ENABLED		= 0x0000,
+		UPDUI_DISABLED		= 0x0100,
+		UPDUI_CHECKED		= 0x0200,
+		UPDUI_CHECKED2		= 0x0400,
+		UPDUI_RADIO		= 0x0800,
+		UPDUI_DEFAULT		= 0x1000,
+		UPDUI_TEXT		= 0x2000,
+		// internal state
+		UPDUI_CLEARDEFAULT	= 0x4000,
+	};
+
+	// element data
+	struct _AtlUpdateUIElement
+	{
+		HWND m_hWnd;
+		WORD m_wType;
+
+		bool operator ==(const _AtlUpdateUIElement& e) const
+		{ return ((m_hWnd == e.m_hWnd) && (m_wType == e.m_wType)); }
+	};
+
+	// map data
+	struct _AtlUpdateUIMap
+	{
+		WORD m_nID;
+		WORD m_wType;
+
+		bool operator ==(const _AtlUpdateUIMap& e) const
+		{ return ((m_nID == e.m_nID) && (m_wType == e.m_wType)); }
+	};
+
+	// instance data
+#pragma warning(push)
+#pragma warning(disable: 4201)   // nameless unions are part of C++
+
+	struct _AtlUpdateUIData
+	{
+		WORD m_wState;
+		union
+		{
+			void* m_lpData;
+			LPTSTR m_lpstrText;
+			struct
+			{
+				WORD m_nIDFirst;
+				WORD m_nIDLast;
+			};
+		};
+
+		bool operator ==(const _AtlUpdateUIData& e) const
+		{ return ((m_wState == e.m_wState) && (m_lpData == e.m_lpData)); }
+	};
+
+#pragma warning(pop)
+
+	ATL::CSimpleArray<_AtlUpdateUIElement> m_UIElements;   // elements data
+	const _AtlUpdateUIMap* m_pUIMap;                       // static UI data
+	_AtlUpdateUIData* m_pUIData;                           // instance UI data
+	WORD m_wDirtyType;                                     // global dirty flag
+
+	bool m_bBlockAccelerators;
+
+
+// Constructor, destructor
+	CUpdateUIBase() : m_pUIMap(NULL), m_pUIData(NULL), m_wDirtyType(0), m_bBlockAccelerators(false)
+	{ }
+
+	~CUpdateUIBase()
+	{
+		if((m_pUIMap != NULL) && (m_pUIData != NULL))
+		{
+			const _AtlUpdateUIMap* pUIMap = m_pUIMap;
+			_AtlUpdateUIData* pUIData = m_pUIData;
+			while(pUIMap->m_nID != (WORD)-1)
+			{
+				if(pUIData->m_wState & UPDUI_TEXT)
+					delete [] pUIData->m_lpstrText;
+				pUIMap++;
+				pUIData++;
+			}
+			delete [] m_pUIData;
+		}
+	}
+
+// Check for disabled commands
+	bool UIGetBlockAccelerators() const
+	{
+		return m_bBlockAccelerators;
+	}
+
+	bool UISetBlockAccelerators(bool bBlock)
+	{
+		bool bOld = m_bBlockAccelerators;
+		m_bBlockAccelerators = bBlock;
+		return bOld;
+	}
+
+// Add elements
+	BOOL UIAddMenuBar(HWND hWnd)                // menu bar (main menu)
+	{
+		if(hWnd == NULL)
+			return FALSE;
+		_AtlUpdateUIElement e;
+		e.m_hWnd = hWnd;
+		e.m_wType = UPDUI_MENUBAR;
+		return m_UIElements.Add(e);
+	}
+
+	BOOL UIAddToolBar(HWND hWnd)                // toolbar
+	{
+		if(hWnd == NULL)
+			return FALSE;
+		_AtlUpdateUIElement e;
+		e.m_hWnd = hWnd;
+		e.m_wType = UPDUI_TOOLBAR;
+		return m_UIElements.Add(e);
+	}
+
+	BOOL UIAddStatusBar(HWND hWnd)              // status bar
+	{
+		if(hWnd == NULL)
+			return FALSE;
+		_AtlUpdateUIElement e;
+		e.m_hWnd = hWnd;
+		e.m_wType = UPDUI_STATUSBAR;
+		return m_UIElements.Add(e);
+	}
+
+	BOOL UIAddChildWindowContainer(HWND hWnd)   // child window
+	{
+		if(hWnd == NULL)
+			return FALSE;
+		_AtlUpdateUIElement e;
+		e.m_hWnd = hWnd;
+		e.m_wType = UPDUI_CHILDWINDOW;
+		return m_UIElements.Add(e);
+	}
+
+// Message map for popup menu updates and accelerator blocking
+	BEGIN_MSG_MAP(CUpdateUIBase)
+		MESSAGE_HANDLER(WM_INITMENUPOPUP, OnInitMenuPopup)
+		MESSAGE_HANDLER(WM_COMMAND, OnCommand)
+	END_MSG_MAP()
+
+	LRESULT OnInitMenuPopup(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+	{
+		bHandled = FALSE;
+		HMENU hMenu = (HMENU)wParam;
+		if(hMenu == NULL)
+			return 1;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return 1;
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		while(pMap->m_nID != (WORD)-1)
+		{
+			if(pMap->m_wType & UPDUI_MENUPOPUP)
+			{
+				UIUpdateMenuBarElement(pMap->m_nID, pUIData, hMenu);
+
+				if((pUIData->m_wState & UPDUI_RADIO) != 0)
+					::CheckMenuRadioItem(hMenu, pUIData->m_nIDFirst, pUIData->m_nIDLast, pMap->m_nID, MF_BYCOMMAND);
+			}
+			pMap++;
+			pUIData++;
+		}
+		return 0;
+	}
+
+	LRESULT OnCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+	{
+		bHandled = FALSE;
+		if(m_bBlockAccelerators && (HIWORD(wParam) == 1))   // accelerators only
+		{
+			int nID = LOWORD(wParam);
+			if((UIGetState(nID) & UPDUI_DISABLED) == UPDUI_DISABLED)
+			{
+				ATLTRACE2(atlTraceUI, 0, _T("CUpdateUIBase::OnCommand - blocked disabled command 0x%4.4X\n"), nID);
+				bHandled = TRUE;   // eat the command, UI item is disabled
+			}
+		}
+		return 0;
+	}
+
+// methods for setting UI element state
+	BOOL UIEnable(int nID, BOOL bEnable, BOOL bForceUpdate = FALSE)
+	{
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+
+		for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
+		{
+			if(nID == (int)pMap->m_nID)
+			{
+				if(bEnable)
+				{
+					if(pUIData->m_wState & UPDUI_DISABLED)
+					{
+						pUIData->m_wState |= pMap->m_wType;
+						pUIData->m_wState &= ~UPDUI_DISABLED;
+					}
+				}
+				else
+				{
+					if(!(pUIData->m_wState & UPDUI_DISABLED))
+					{
+						pUIData->m_wState |= pMap->m_wType;
+						pUIData->m_wState |= UPDUI_DISABLED;
+					}
+				}
+
+				if(bForceUpdate)
+					pUIData->m_wState |= pMap->m_wType;
+				if(pUIData->m_wState & pMap->m_wType)
+					m_wDirtyType |= pMap->m_wType;
+
+				break;   // found
+			}
+		}
+
+		return TRUE;
+	}
+
+	BOOL UISetCheck(int nID, int nCheck, BOOL bForceUpdate = FALSE)
+	{
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+
+		for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
+		{
+			if(nID == (int)pMap->m_nID)
+			{
+				switch(nCheck)
+				{
+				case 0:
+					if((pUIData->m_wState & UPDUI_CHECKED) || (pUIData->m_wState & UPDUI_CHECKED2))
+					{
+						pUIData->m_wState |= pMap->m_wType;
+						pUIData->m_wState &= ~(UPDUI_CHECKED | UPDUI_CHECKED2);
+					}
+					break;
+				case 1:
+					if(!(pUIData->m_wState & UPDUI_CHECKED))
+					{
+						pUIData->m_wState |= pMap->m_wType;
+						pUIData->m_wState &= ~UPDUI_CHECKED2;
+						pUIData->m_wState |= UPDUI_CHECKED;
+					}
+					break;
+				case 2:
+					if(!(pUIData->m_wState & UPDUI_CHECKED2))
+					{
+						pUIData->m_wState |= pMap->m_wType;
+						pUIData->m_wState &= ~UPDUI_CHECKED;
+						pUIData->m_wState |= UPDUI_CHECKED2;
+					}
+					break;
+				}
+
+				if(bForceUpdate)
+					pUIData->m_wState |= pMap->m_wType;
+				if(pUIData->m_wState & pMap->m_wType)
+					m_wDirtyType |= pMap->m_wType;
+
+				break;   // found
+			}
+		}
+
+		return TRUE;
+	}
+
+	// variant that supports bool (checked/not-checked, no intermediate state)
+	BOOL UISetCheck(int nID, bool bCheck, BOOL bForceUpdate = FALSE)
+	{
+		return UISetCheck(nID, bCheck ? 1 : 0, bForceUpdate);
+	}
+
+	BOOL UISetRadio(int nID, BOOL bRadio, BOOL bForceUpdate = FALSE)
+	{
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+
+		for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
+		{
+			if(nID == (int)pMap->m_nID)
+			{
+				if(bRadio)
+				{
+					if(!(pUIData->m_wState & UPDUI_RADIO))
+					{
+						pUIData->m_wState |= pMap->m_wType;
+						pUIData->m_wState |= UPDUI_RADIO;
+					}
+				}
+				else
+				{
+					if(pUIData->m_wState & UPDUI_RADIO)
+					{
+						pUIData->m_wState |= pMap->m_wType;
+						pUIData->m_wState &= ~UPDUI_RADIO;
+					}
+				}
+
+				if(bForceUpdate)
+					pUIData->m_wState |= pMap->m_wType;
+				if(pUIData->m_wState & pMap->m_wType)
+					m_wDirtyType |= pMap->m_wType;
+
+				break;   // found
+			}
+		}
+
+		return TRUE;
+	}
+
+	// for menu items
+	BOOL UISetRadioMenuItem(int nID, int nIDFirst, int nIDLast, BOOL bForceUpdate = FALSE)
+	{
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+
+		for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
+		{
+			if(nID == (int)pMap->m_nID)
+			{
+				pUIData->m_wState |= pMap->m_wType;
+				pUIData->m_wState |= UPDUI_RADIO;
+				pUIData->m_nIDFirst = (WORD)nIDFirst;
+				pUIData->m_nIDLast = (WORD)nIDLast;
+
+				if(bForceUpdate)
+					pUIData->m_wState |= pMap->m_wType;
+				if(pUIData->m_wState & pMap->m_wType)
+					m_wDirtyType |= pMap->m_wType;
+			}
+			else if((pMap->m_nID >= nIDFirst) && (pMap->m_nID <= nIDLast))
+			{
+				if(pUIData->m_wState & UPDUI_RADIO)
+				{
+					pUIData->m_wState &= ~pMap->m_wType;
+					pUIData->m_wState &= ~UPDUI_RADIO;
+					pUIData->m_nIDFirst = 0;
+					pUIData->m_nIDLast = 0;
+				}
+			}
+
+			if(pMap->m_nID == nIDLast)
+				break;
+		}
+
+		return TRUE;
+	}
+
+	BOOL UISetText(int nID, LPCTSTR lpstrText, BOOL bForceUpdate = FALSE)
+	{
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+		if(lpstrText == NULL)
+			lpstrText = _T("");
+
+		for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
+		{
+			if(nID == (int)pMap->m_nID)
+			{
+				if((pUIData->m_lpstrText == NULL) || lstrcmp(pUIData->m_lpstrText, lpstrText))
+				{
+					delete [] pUIData->m_lpstrText;
+					pUIData->m_lpstrText = NULL;
+					int nStrLen = lstrlen(lpstrText);
+					ATLTRY(pUIData->m_lpstrText = new TCHAR[nStrLen + 1]);
+					if(pUIData->m_lpstrText == NULL)
+					{
+						ATLTRACE2(atlTraceUI, 0, _T("UISetText - memory allocation failed\n"));
+						break;
+					}
+					ATL::Checked::tcscpy_s(pUIData->m_lpstrText, nStrLen + 1, lpstrText);
+					pUIData->m_wState |= (UPDUI_TEXT | pMap->m_wType);
+				}
+
+				if(bForceUpdate)
+					pUIData->m_wState |= (UPDUI_TEXT | pMap->m_wType);
+				if(pUIData->m_wState & pMap->m_wType)
+					m_wDirtyType |= pMap->m_wType;
+
+				break;   // found
+			}
+		}
+
+		return TRUE;
+	}
+
+	BOOL UISetDefault(int nID, BOOL bDefault, BOOL bForceUpdate = FALSE)
+	{
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+
+		for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
+		{
+			if(nID == (int)pMap->m_nID)
+			{
+				if(bDefault)
+				{
+					if((pUIData->m_wState & UPDUI_DEFAULT) == 0)
+					{
+						pUIData->m_wState |= pMap->m_wType;
+						pUIData->m_wState |= UPDUI_DEFAULT;
+					}
+				}
+				else
+				{
+					if((pUIData->m_wState & UPDUI_DEFAULT) != 0)
+					{
+						pUIData->m_wState |= pMap->m_wType;
+						pUIData->m_wState &= ~UPDUI_DEFAULT;
+						pUIData->m_wState |= UPDUI_CLEARDEFAULT;
+					}
+				}
+
+				if(bForceUpdate)
+					pUIData->m_wState |= pMap->m_wType;
+				if(pUIData->m_wState & pMap->m_wType)
+					m_wDirtyType |= pMap->m_wType;
+
+				break;   // found
+			}
+		}
+
+		return TRUE;
+	}
+
+// methods for complete state set/get
+	BOOL UISetState(int nID, DWORD dwState)
+	{
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+		for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
+		{
+			if(nID == (int)pMap->m_nID)
+			{		
+				pUIData->m_wState = (WORD)(dwState | pMap->m_wType);
+				m_wDirtyType |= pMap->m_wType;
+				break;   // found
+			}
+		}
+		return TRUE;
+	}
+
+	DWORD UIGetState(int nID)
+	{
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return 0;
+		for( ; pMap->m_nID != (WORD)-1; pMap++, pUIData++)
+		{
+			if(nID == (int)pMap->m_nID)
+				return pUIData->m_wState;
+		}
+		return 0;
+	}
+
+// methods for updating UI
+	BOOL UIUpdateMenuBar(BOOL bForceUpdate = FALSE, BOOL bMainMenu = FALSE)
+	{
+		if(!(m_wDirtyType & UPDUI_MENUBAR) && !bForceUpdate)
+			return TRUE;
+
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+
+		while(pMap->m_nID != (WORD)-1)
+		{
+			for(int i = 0; i < m_UIElements.GetSize(); i++)
+			{
+				if(m_UIElements[i].m_wType == UPDUI_MENUBAR)
+				{
+					HMENU hMenu = ::GetMenu(m_UIElements[i].m_hWnd);
+					if((hMenu != NULL) && (pUIData->m_wState & UPDUI_MENUBAR) && (pMap->m_wType & UPDUI_MENUBAR))
+						UIUpdateMenuBarElement(pMap->m_nID, pUIData, hMenu);
+				}
+				if(bMainMenu)
+					::DrawMenuBar(m_UIElements[i].m_hWnd);
+			}
+			pMap++;
+			pUIData->m_wState &= ~UPDUI_MENUBAR;
+			if(pUIData->m_wState & UPDUI_TEXT)
+			{
+				delete [] pUIData->m_lpstrText;
+				pUIData->m_lpstrText = NULL;
+				pUIData->m_wState &= ~UPDUI_TEXT;
+			}
+			pUIData++;
+		}
+
+		m_wDirtyType &= ~UPDUI_MENUBAR;
+		return TRUE;
+	}
+
+	BOOL UIUpdateToolBar(BOOL bForceUpdate = FALSE)
+	{
+		if(!(m_wDirtyType & UPDUI_TOOLBAR) && !bForceUpdate)
+			return TRUE;
+
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+
+		while(pMap->m_nID != (WORD)-1)
+		{
+			for(int i = 0; i < m_UIElements.GetSize(); i++)
+			{
+				if(m_UIElements[i].m_wType == UPDUI_TOOLBAR)
+				{
+					if((pUIData->m_wState & UPDUI_TOOLBAR) && (pMap->m_wType & UPDUI_TOOLBAR))
+						UIUpdateToolBarElement(pMap->m_nID, pUIData, m_UIElements[i].m_hWnd);
+				}
+			}
+			pMap++;
+			pUIData->m_wState &= ~UPDUI_TOOLBAR;
+			pUIData++;
+		}
+
+		m_wDirtyType &= ~UPDUI_TOOLBAR;
+		return TRUE;
+	}
+
+	BOOL UIUpdateStatusBar(BOOL bForceUpdate = FALSE)
+	{
+		if(!(m_wDirtyType & UPDUI_STATUSBAR) && !bForceUpdate)
+			return TRUE;
+
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+
+		while(pMap->m_nID != (WORD)-1)
+		{
+			for(int i = 0; i < m_UIElements.GetSize(); i++)
+			{
+				if(m_UIElements[i].m_wType == UPDUI_STATUSBAR)
+				{
+					if((pUIData->m_wState & UPDUI_STATUSBAR) && (pMap->m_wType & UPDUI_STATUSBAR))
+						UIUpdateStatusBarElement(pMap->m_nID, pUIData, m_UIElements[i].m_hWnd);
+				}
+			}
+			pMap++;
+			pUIData->m_wState &= ~UPDUI_STATUSBAR;
+			if(pUIData->m_wState & UPDUI_TEXT)
+			{
+				delete [] pUIData->m_lpstrText;
+				pUIData->m_lpstrText = NULL;
+				pUIData->m_wState &= ~UPDUI_TEXT;
+			}
+			pUIData++;
+		}
+
+		m_wDirtyType &= ~UPDUI_STATUSBAR;
+		return TRUE;
+	}
+
+	BOOL UIUpdateChildWindows(BOOL bForceUpdate = FALSE)
+	{
+		if(!(m_wDirtyType & UPDUI_CHILDWINDOW) && !bForceUpdate)
+			return TRUE;
+
+		const _AtlUpdateUIMap* pMap = m_pUIMap;
+		_AtlUpdateUIData* pUIData = m_pUIData;
+		if(pUIData == NULL)
+			return FALSE;
+
+		while(pMap->m_nID != (WORD)-1)
+		{
+			for(int i = 0; i < m_UIElements.GetSize(); i++)
+			{
+				if(m_UIElements[i].m_wType == UPDUI_CHILDWINDOW)
+				{
+					if((pUIData->m_wState & UPDUI_CHILDWINDOW) && (pMap->m_wType & UPDUI_CHILDWINDOW))
+						UIUpdateChildWindow(pMap->m_nID, pUIData, m_UIElements[i].m_hWnd);
+				}
+			}
+			pMap++;
+			pUIData->m_wState &= ~UPDUI_CHILDWINDOW;
+			if(pUIData->m_wState & UPDUI_TEXT)
+			{
+				delete [] pUIData->m_lpstrText;
+				pUIData->m_lpstrText = NULL;
+				pUIData->m_wState &= ~UPDUI_TEXT;
+			}
+			pUIData++;
+		}
+
+		m_wDirtyType &= ~UPDUI_CHILDWINDOW;
+		return TRUE;
+	}
+
+// internal element specific methods
+	static void UIUpdateMenuBarElement(int nID, _AtlUpdateUIData* pUIData, HMENU hMenu)
+	{
+		if((pUIData->m_wState & UPDUI_CLEARDEFAULT) != 0)
+		{
+			::SetMenuDefaultItem(hMenu, (UINT)-1, 0);
+			pUIData->m_wState &= ~UPDUI_CLEARDEFAULT;
+		}
+
+		CMenuItemInfo mii;
+		mii.fMask = MIIM_STATE;
+		mii.wID = nID;
+
+		if((pUIData->m_wState & UPDUI_DISABLED) != 0)
+			mii.fState |= MFS_DISABLED | MFS_GRAYED;
+		else
+			mii.fState |= MFS_ENABLED;
+
+		if((pUIData->m_wState & UPDUI_CHECKED) != 0)
+			mii.fState |= MFS_CHECKED;
+		else
+			mii.fState |= MFS_UNCHECKED;
+
+		if((pUIData->m_wState & UPDUI_DEFAULT) != 0)
+			mii.fState |= MFS_DEFAULT;
+
+		if((pUIData->m_wState & UPDUI_TEXT) != 0)
+		{
+			CMenuItemInfo miiNow;
+			miiNow.fMask = MIIM_TYPE;
+			miiNow.wID = nID;
+			if(::GetMenuItemInfo(hMenu, nID, FALSE, &miiNow))
+			{
+				mii.fMask |= MIIM_TYPE;
+				// MFT_BITMAP and MFT_SEPARATOR don't go together with MFT_STRING
+				mii.fType |= (miiNow.fType & ~(MFT_BITMAP | MFT_SEPARATOR)) | MFT_STRING;
+				mii.dwTypeData = pUIData->m_lpstrText;
+			}
+		}
+
+		::SetMenuItemInfo(hMenu, nID, FALSE, &mii);
+	}
+
+	static void UIUpdateToolBarElement(int nID, _AtlUpdateUIData* pUIData, HWND hWndToolBar)
+	{
+		// Note: only handles enabled/disabled, checked state, and radio (press)
+		::SendMessage(hWndToolBar, TB_ENABLEBUTTON, nID, (LPARAM)(pUIData->m_wState & UPDUI_DISABLED) ? FALSE : TRUE);
+		::SendMessage(hWndToolBar, TB_CHECKBUTTON, nID, (LPARAM)(pUIData->m_wState & UPDUI_CHECKED) ? TRUE : FALSE);
+		::SendMessage(hWndToolBar, TB_INDETERMINATE, nID, (LPARAM)(pUIData->m_wState & UPDUI_CHECKED2) ? TRUE : FALSE);
+		::SendMessage(hWndToolBar, TB_PRESSBUTTON, nID, (LPARAM)(pUIData->m_wState & UPDUI_RADIO) ? TRUE : FALSE);
+	}
+
+	static void UIUpdateStatusBarElement(int nID, _AtlUpdateUIData* pUIData, HWND hWndStatusBar)
+	{
+		// Note: only handles text
+		if(pUIData->m_wState & UPDUI_TEXT)
+			::SendMessage(hWndStatusBar, SB_SETTEXT, nID, (LPARAM)pUIData->m_lpstrText);
+	}
+
+	static void UIUpdateChildWindow(int nID, _AtlUpdateUIData* pUIData, HWND hWnd)
+	{
+		HWND hChild = ::GetDlgItem(hWnd, nID);
+
+		::EnableWindow(hChild, (pUIData->m_wState & UPDUI_DISABLED) ? FALSE : TRUE);
+		// for check and radio, assume that window is a button
+		int nCheck = BST_UNCHECKED;
+		if((pUIData->m_wState & UPDUI_CHECKED) || (pUIData->m_wState & UPDUI_RADIO))
+			nCheck = BST_CHECKED;
+		else if(pUIData->m_wState & UPDUI_CHECKED2)
+			nCheck = BST_INDETERMINATE;
+		::SendMessage(hChild, BM_SETCHECK, nCheck, 0L);
+		if(pUIData->m_wState & UPDUI_DEFAULT)
+		{
+			DWORD dwRet = (DWORD)::SendMessage(hWnd, DM_GETDEFID, 0, 0L);
+			if(HIWORD(dwRet) == DC_HASDEFID)
+			{
+				HWND hOldDef = ::GetDlgItem(hWnd, (int)(short)LOWORD(dwRet));
+				// remove BS_DEFPUSHBUTTON
+				::SendMessage(hOldDef, BM_SETSTYLE, BS_PUSHBUTTON, MAKELPARAM(TRUE, 0));
+			}
+			::SendMessage(hWnd, DM_SETDEFID, nID, 0L);
+		}
+		if(pUIData->m_wState & UPDUI_TEXT)
+			::SetWindowText(hChild, pUIData->m_lpstrText);
+	}
+};
+
+template <class T>
+class CUpdateUI : public CUpdateUIBase
+{
+public:
+	CUpdateUI()
+	{
+		T* pT = static_cast<T*>(this);
+		(void)pT;   // avoid level 4 warning
+		const _AtlUpdateUIMap* pMap = pT->GetUpdateUIMap();
+		m_pUIMap = pMap;
+		ATLASSERT(m_pUIMap != NULL);
+		int nCount = 1;
+		for( ; pMap->m_nID != (WORD)-1; nCount++)
+			pMap++;
+
+		// check for duplicates (debug only)
+#ifdef _DEBUG
+		for(int i = 0; i < nCount; i++)
+		{
+			for(int j = 0; j < nCount; j++)
+			{
+				// shouldn't have duplicates in the update UI map
+				if(i != j)
+					ATLASSERT(m_pUIMap[j].m_nID != m_pUIMap[i].m_nID);
+			}
+		}
+#endif // _DEBUG
+
+		ATLTRY(m_pUIData = new _AtlUpdateUIData[nCount]);
+		ATLASSERT(m_pUIData != NULL);
+
+		if(m_pUIData != NULL)
+			memset(m_pUIData, 0, sizeof(_AtlUpdateUIData) * nCount);
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CDynamicUpdateUI - allows update elements to dynamically added and removed
+//                    in addition to a static update UI map
+
+template <class T>
+class CDynamicUpdateUI : public CUpdateUIBase
+{
+public:
+// Data members
+	ATL::CSimpleArray<_AtlUpdateUIMap> m_arrUIMap;     // copy of the static UI data
+	ATL::CSimpleArray<_AtlUpdateUIData> m_arrUIData;   // instance UI data
+
+// Constructor/destructor
+	CDynamicUpdateUI()
+	{
+		T* pT = static_cast<T*>(this);
+		(void)pT;   // avoid level 4 warning
+		const _AtlUpdateUIMap* pMap = pT->GetUpdateUIMap();
+		ATLASSERT(pMap != NULL);
+
+		for(;;)
+		{
+			BOOL bRet = m_arrUIMap.Add(*(_AtlUpdateUIMap*)pMap);
+			ATLASSERT(bRet);
+
+			if(bRet != FALSE)
+			{
+				_AtlUpdateUIData data = { 0, NULL };
+				bRet = m_arrUIData.Add(data);
+				ATLASSERT(bRet);
+			}
+
+			if(pMap->m_nID == (WORD)-1)
+				break;
+
+			pMap++;
+		}
+
+		ATLASSERT(m_arrUIMap.GetSize() == m_arrUIData.GetSize());
+
+#ifdef _DEBUG
+		// check for duplicates (debug only)
+		for(int i = 0; i < m_arrUIMap.GetSize(); i++)
+		{
+			for(int j = 0; j < m_arrUIMap.GetSize(); j++)
+			{
+				// shouldn't have duplicates in the update UI map
+				if(i != j)
+					ATLASSERT(m_arrUIMap[j].m_nID != m_arrUIMap[i].m_nID);
+			}
+		}
+#endif // _DEBUG
+
+		// Set internal data pointers to point to the new data arrays
+		m_pUIMap = m_arrUIMap.m_aT;
+		m_pUIData = m_arrUIData.m_aT;
+	}
+
+	~CDynamicUpdateUI()
+	{
+		for(int i = 0; i < m_arrUIData.GetSize(); i++)
+		{
+			if((m_arrUIData[i].m_wState & UPDUI_TEXT) != 0)
+				delete [] m_arrUIData[i].m_lpstrText;
+		}
+
+		// Reset internal data pointers (memory will be released by CSimpleArray d-tor)
+		m_pUIMap = NULL;
+		m_pUIData = NULL;
+	}
+
+// Methods for dynamically adding and removing update elements
+	bool UIAddUpdateElement(WORD nID, WORD wType)
+	{
+		// check for duplicates
+		for(int i = 0; i < m_arrUIMap.GetSize(); i++)
+		{
+			// shouldn't have duplicates in the update UI map
+			ATLASSERT(m_arrUIMap[i].m_nID != nID);
+			if(m_arrUIMap[i].m_nID == nID)
+				return false;
+		}
+
+		bool bRetVal = false;
+
+		// Add new end element
+		_AtlUpdateUIMap uumEnd = { (WORD)-1, 0 };
+		BOOL bRet = m_arrUIMap.Add(uumEnd);
+		ATLASSERT(bRet);
+
+		if(bRet != FALSE)
+		{
+			_AtlUpdateUIData uud = { 0, NULL };
+			bRet = m_arrUIData.Add(uud);
+			ATLASSERT(bRet);
+
+			// Set new data to the previous end element
+			if(bRet != FALSE)
+			{
+				int nSize = m_arrUIMap.GetSize();
+				_AtlUpdateUIMap uum = { nID, wType };
+				m_arrUIMap.SetAtIndex(nSize - 2, uum);
+				m_arrUIData.SetAtIndex(nSize - 2, uud);
+
+				// Set internal data pointers again, just in case that memory moved
+				m_pUIMap = m_arrUIMap.m_aT;
+				m_pUIData = m_arrUIData.m_aT;
+
+				bRetVal = true;
+			}
+		}
+
+		return bRetVal;
+	}
+
+	bool UIRemoveUpdateElement(WORD nID)
+	{
+		bool bRetVal = false;
+
+		for(int i = 0; i < m_arrUIMap.GetSize(); i++)
+		{
+			if(m_arrUIMap[i].m_nID == nID)
+			{
+				if((m_arrUIData[i].m_wState & UPDUI_TEXT) != 0)
+					delete [] m_arrUIData[i].m_lpstrText;
+
+				BOOL bRet = m_arrUIMap.RemoveAt(i);
+				ATLASSERT(bRet);
+				bRet = m_arrUIData.RemoveAt(i);
+				ATLASSERT(bRet);
+
+				bRetVal = true;
+				break;
+			}
+		}
+
+		return bRetVal;
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CAutoUpdateUI : Automatic mapping of UI elements
+
+template <class T>
+class CAutoUpdateUI : public CDynamicUpdateUI<T>
+{
+public:
+	LPCTSTR UIGetText(int nID)
+	{
+		for(int i = 0; i < this->m_arrUIMap.GetSize(); i++)
+		{
+			if(this->m_arrUIMap[i].m_nID == nID)
+ 				return this->m_arrUIData[i].m_lpstrText;
+		}
+
+		return NULL;
+	}
+
+// Element
+	template <WORD t_wType>
+	bool UIAddElement(UINT nID)
+	{
+		// check for existing UI map element
+		for(int i = 0; i < this->m_arrUIMap.GetSize(); i++)
+		{
+			if(this->m_arrUIMap[i].m_nID == nID)
+			{
+				// set requested type
+				this->m_arrUIMap[i].m_wType |= t_wType;
+				return true;
+			}
+		}
+
+		// Add element to UI map with requested type
+		return this->UIAddUpdateElement((WORD)nID, t_wType);
+	}
+
+	template <WORD t_wType>
+	bool UIRemoveElement(UINT nID)
+	{
+		for(int i = 0; i < this->m_arrUIMap.GetSize(); i++)
+		{
+			if(this->m_arrUIMap[i].m_nID == nID) // matching UI map element
+			{
+				WORD wType = this->m_arrUIMap[i].m_wType & ~t_wType;
+				if (wType != 0)   // has other types 
+				{
+					this->m_arrUIMap[i].m_wType = wType; // keep other types
+					return true;
+				}
+				else
+				{
+					return this->UIRemoveUpdateElement((WORD)nID);
+				}
+			}
+		}
+
+		return false;
+	}
+
+// Menu
+	bool UIAddMenu(HMENU hMenu, bool bSetText = false)
+	{
+		ATLASSERT(::IsMenu(hMenu));
+		MENUITEMINFO mii = {sizeof(MENUITEMINFO), MIIM_TYPE | MIIM_ID | MIIM_SUBMENU};
+
+		// Complete the UI map
+		for (INT uItem = 0; CMenuHandle(hMenu).GetMenuItemInfo(uItem, TRUE, &mii); uItem++)
+		{
+			if(mii.hSubMenu)
+			{
+				// Add submenu to UI map
+				UIAddMenu(mii.hSubMenu, bSetText);
+			}
+			else if (mii.wID != 0)
+			{
+				// Add element to UI map
+				UIAddElement<CDynamicUpdateUI<T>::UPDUI_MENUPOPUP>(mii.wID);
+				if (bSetText)
+				{
+					TCHAR sText[64] = {};
+					if (GetMenuString(hMenu, uItem, sText, 64, MF_BYPOSITION))
+						this->UISetText(mii.wID, sText);
+				}
+			}
+		}
+
+		return true;
+	}
+
+	bool UIAddMenu(UINT uID, bool bSetText = false)
+	{
+		CMenu menu;
+		ATLVERIFY(menu.LoadMenu(uID));
+		return UIAddMenu(menu, bSetText);
+	}
+
+// ToolBar
+	bool UIAddToolBar(HWND hWndToolBar)
+	{
+		ATLASSERT(::IsWindow(hWndToolBar));
+		TBBUTTONINFO tbbi = { sizeof(TBBUTTONINFO), TBIF_COMMAND | TBIF_STYLE | TBIF_BYINDEX };
+
+		// Add toolbar buttons
+		for (int uItem = 0; ::SendMessage(hWndToolBar, TB_GETBUTTONINFO, uItem, (LPARAM)&tbbi) != -1; uItem++)
+		{
+			if (tbbi.fsStyle ^ BTNS_SEP)
+				UIAddElement<CDynamicUpdateUI<T>::UPDUI_TOOLBAR>(tbbi.idCommand);
+		}
+
+		// Add embedded controls if any
+		if (::GetWindow(hWndToolBar, GW_CHILD))
+			UIAddChildWindowContainer(hWndToolBar);
+
+		return (CUpdateUIBase::UIAddToolBar(hWndToolBar) != FALSE);
+	}
+
+// Container
+	bool UIAddChildWindowContainer(HWND hWnd)
+	{
+		ATLASSERT(::IsWindow(hWnd));
+
+		// Add children controls if any
+		for (ATL::CWindow wCtl = ::GetWindow(hWnd, GW_CHILD); wCtl.IsWindow(); wCtl = wCtl.GetWindow(GW_HWNDNEXT))
+		{
+			int id = wCtl.GetDlgCtrlID();
+			if(id != 0)
+				UIAddElement<CDynamicUpdateUI<T>::UPDUI_CHILDWINDOW>(id);
+		}
+
+		return (CUpdateUIBase::UIAddChildWindowContainer(hWnd) != FALSE);
+	}
+
+// StatusBar
+	BOOL UIUpdateStatusBar(BOOL bForceUpdate = FALSE)
+	{
+		if(!(this->m_wDirtyType & CDynamicUpdateUI<T>::UPDUI_STATUSBAR) && !bForceUpdate)
+			return TRUE;
+
+		for(int i = 0; i < this->m_arrUIMap.GetSize(); i++)
+		{
+			for(int e = 0; e < this->m_UIElements.GetSize(); e++)
+			{
+				if((this->m_UIElements[e].m_wType == CDynamicUpdateUI<T>::UPDUI_STATUSBAR) && 
+				   (this->m_arrUIMap[i].m_wType & CDynamicUpdateUI<T>::UPDUI_STATUSBAR) && 
+				   (this->m_arrUIData[i].m_wState & CDynamicUpdateUI<T>::UPDUI_STATUSBAR))
+				{
+					this->UIUpdateStatusBarElement(this->m_arrUIMap[i].m_nID, &this->m_arrUIData[i], this->m_UIElements[e].m_hWnd);
+					this->m_arrUIData[i].m_wState &= ~CDynamicUpdateUI<T>::UPDUI_STATUSBAR;
+					if(this->m_arrUIData[i].m_wState & CDynamicUpdateUI<T>::UPDUI_TEXT)
+						this->m_arrUIData[i].m_wState &= ~CDynamicUpdateUI<T>::UPDUI_TEXT;
+				}
+			}
+		}
+
+		this->m_wDirtyType &= ~CDynamicUpdateUI<T>::UPDUI_STATUSBAR;
+		return TRUE;
+	}
+
+	bool UIAddStatusBar(HWND hWndStatusBar, INT nPanes = 1)
+	{
+		ATLASSERT(::IsWindow(hWndStatusBar));
+
+		// Add StatusBar panes
+		for (int iPane = 0; iPane < nPanes; iPane++)
+			UIAddElement<CDynamicUpdateUI<T>::UPDUI_STATUSBAR>(ID_DEFAULT_PANE + iPane);
+
+		return (CUpdateUIBase::UIAddStatusBar(hWndStatusBar) != FALSE);
+	}
+
+// UI Map used if derived class has none
+	BEGIN_UPDATE_UI_MAP(CAutoUpdateUI)
+	END_UPDATE_UI_MAP()
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CDialogResize - provides support for resizing dialog controls
+//                 (works for any window that has child controls)
+
+// Put CDialogResize in the list of base classes for a dialog (or even plain window),
+// then implement DLGRESIZE map by specifying controls and groups of control
+// and using DLSZ_* values to specify how are they supposed to be resized.
+//
+// Notes:
+// - Resizeable border (WS_THICKFRAME style) should be set in the dialog template
+//   for top level dialogs (popup or overlapped), so that users can resize the dialog.
+// - Some flags cannot be combined; for instance DLSZ_CENTER_X overrides DLSZ_SIZE_X,
+//   DLSZ_SIZE_X overrides DLSZ_MOVE_X. X and Y flags can be combined.
+// - Order of controls is important - group controls are resized and moved based
+//   on the position of the previous control in a group.
+
+// dialog resize map macros
+struct _AtlDlgResizeMap
+{
+	int m_nCtlID;
+	DWORD m_dwResizeFlags;
+};
+
+#define BEGIN_DLGRESIZE_MAP(thisClass) \
+	static const WTL::_AtlDlgResizeMap* GetDlgResizeMap() \
+	{ \
+		static const WTL::_AtlDlgResizeMap theMap[] = \
+		{
+
+#define END_DLGRESIZE_MAP() \
+			{ -1, 0 }, \
+		}; \
+		return theMap; \
+	}
+
+#define DLGRESIZE_CONTROL(id, flags) \
+		{ id, flags },
+
+#define BEGIN_DLGRESIZE_GROUP() \
+		{ -1, _DLSZ_BEGIN_GROUP },
+
+#define END_DLGRESIZE_GROUP() \
+		{ -1, _DLSZ_END_GROUP },
+
+
+template <class T>
+class CDialogResize
+{
+public:
+// Data declarations and members
+	enum
+	{
+		DLSZ_SIZE_X		= 0x00000001,
+		DLSZ_SIZE_Y		= 0x00000002,
+		DLSZ_MOVE_X		= 0x00000004,
+		DLSZ_MOVE_Y		= 0x00000008,
+		DLSZ_REPAINT		= 0x00000010,
+		DLSZ_CENTER_X		= 0x00000020,
+		DLSZ_CENTER_Y		= 0x00000040,
+
+		// internal use only
+		_DLSZ_BEGIN_GROUP	= 0x00001000,
+		_DLSZ_END_GROUP		= 0x00002000,
+		_DLSZ_GRIPPER		= 0x00004000
+	};
+
+	struct _AtlDlgResizeData
+	{
+		int m_nCtlID;
+		DWORD m_dwResizeFlags;
+		RECT m_rect;
+
+		int GetGroupCount() const
+		{
+			return (int)LOBYTE(HIWORD(m_dwResizeFlags));
+		}
+
+		void SetGroupCount(int nCount)
+		{
+			ATLASSERT((nCount > 0) && (nCount < 256));
+			DWORD dwCount = (DWORD)MAKELONG(0, MAKEWORD(nCount, 0));
+			m_dwResizeFlags &= 0xFF00FFFF;
+			m_dwResizeFlags |= dwCount;
+		}
+
+		bool operator ==(const _AtlDlgResizeData& r) const
+		{ return ((m_nCtlID == r.m_nCtlID) && (m_dwResizeFlags == r.m_dwResizeFlags)); }
+	};
+
+	ATL::CSimpleArray<_AtlDlgResizeData> m_arrData;
+	SIZE m_sizeDialog;
+	POINT m_ptMinTrackSize;
+	bool m_bGripper;
+
+
+// Constructor
+	CDialogResize() : m_bGripper(false)
+	{
+		m_sizeDialog.cx = 0;
+		m_sizeDialog.cy = 0;
+		m_ptMinTrackSize.x = -1;
+		m_ptMinTrackSize.y = -1;
+	}
+
+// Operations
+	void DlgResize_Init(bool bAddGripper = true, bool bUseMinTrackSize = true, DWORD dwForceStyle = WS_CLIPCHILDREN)
+	{
+		T* pT = static_cast<T*>(this);
+		ATLASSERT(::IsWindow(pT->m_hWnd));
+
+		DWORD dwStyle = pT->GetStyle();
+
+#ifdef _DEBUG
+		// Debug only: Check if top level dialogs have a resizeable border.
+		if(((dwStyle & WS_CHILD) == 0) && ((dwStyle & WS_THICKFRAME) == 0))
+			ATLTRACE2(atlTraceUI, 0, _T("DlgResize_Init - warning: top level dialog without the WS_THICKFRAME style - user cannot resize it\n"));
+#endif // _DEBUG
+
+		// Force specified styles (default WS_CLIPCHILDREN reduces flicker)
+		if((dwStyle & dwForceStyle) != dwForceStyle)
+			pT->ModifyStyle(0, dwForceStyle);
+
+		// Adding this style removes an empty icon that dialogs with WS_THICKFRAME have.
+		// Setting icon to NULL is required when XP themes are active.
+		// Note: This will not prevent adding an icon for the dialog using SetIcon()
+		if((dwStyle & WS_CHILD) == 0)
+		{
+			pT->ModifyStyleEx(0, WS_EX_DLGMODALFRAME);
+			if(pT->GetIcon(FALSE) == NULL)
+				pT->SetIcon(NULL, FALSE);
+		}
+
+		// Cleanup in case of multiple initialization
+		// block: first check for the gripper control, destroy it if needed
+		{
+			ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR);
+			if(wndGripper.IsWindow() && (m_arrData.GetSize() > 0) && (m_arrData[0].m_dwResizeFlags & _DLSZ_GRIPPER) != 0)
+				wndGripper.DestroyWindow();
+		}
+		// clear out everything else
+		m_arrData.RemoveAll();
+		m_sizeDialog.cx = 0;
+		m_sizeDialog.cy = 0;
+		m_ptMinTrackSize.x = -1;
+		m_ptMinTrackSize.y = -1;
+
+		// Get initial dialog client size
+		RECT rectDlg = {};
+		pT->GetClientRect(&rectDlg);
+		m_sizeDialog.cx = rectDlg.right;
+		m_sizeDialog.cy = rectDlg.bottom;
+
+		// Create gripper if requested
+		m_bGripper = false;
+		if(bAddGripper)
+		{
+			// shouldn't exist already
+			ATLASSERT(!pT->GetDlgItem(ATL_IDW_STATUS_BAR).IsWindow());
+			if(!pT->GetDlgItem(ATL_IDW_STATUS_BAR).IsWindow())
+			{
+				ATL::CWindow wndGripper;
+				wndGripper.Create(_T("SCROLLBAR"), pT->m_hWnd, rectDlg, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SBS_SIZEBOX | SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN, 0, ATL_IDW_STATUS_BAR);
+				ATLASSERT(wndGripper.IsWindow());
+				if(wndGripper.IsWindow())
+				{
+					m_bGripper = true;
+					RECT rectCtl = {};
+					wndGripper.GetWindowRect(&rectCtl);
+					::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rectCtl, 2);
+					_AtlDlgResizeData data = { ATL_IDW_STATUS_BAR, DLSZ_MOVE_X | DLSZ_MOVE_Y | DLSZ_REPAINT | _DLSZ_GRIPPER, { rectCtl.left, rectCtl.top, rectCtl.right, rectCtl.bottom } };
+					m_arrData.Add(data);
+				}
+			}
+		}
+
+		// Get min track position if requested
+		if(bUseMinTrackSize)
+		{
+			if((dwStyle & WS_CHILD) != 0)
+			{
+				RECT rect = {};
+				pT->GetClientRect(&rect);
+				m_ptMinTrackSize.x = rect.right - rect.left;
+				m_ptMinTrackSize.y = rect.bottom - rect.top;
+			}
+			else
+			{
+				RECT rect = {};
+				pT->GetWindowRect(&rect);
+				m_ptMinTrackSize.x = rect.right - rect.left;
+				m_ptMinTrackSize.y = rect.bottom - rect.top;
+			}
+		}
+
+		// Walk the map and initialize data
+		const _AtlDlgResizeMap* pMap = pT->GetDlgResizeMap();
+		ATLASSERT(pMap != NULL);
+		int nGroupStart = -1;
+		for(int nCount = 1; !((pMap->m_nCtlID == -1) && (pMap->m_dwResizeFlags == 0)); nCount++, pMap++)
+		{
+			if(pMap->m_nCtlID == -1)
+			{
+				switch(pMap->m_dwResizeFlags)
+				{
+				case _DLSZ_BEGIN_GROUP:
+					ATLASSERT(nGroupStart == -1);
+					nGroupStart = m_arrData.GetSize();
+					break;
+				case _DLSZ_END_GROUP:
+					{
+						ATLASSERT(nGroupStart != -1);
+						int nGroupCount = m_arrData.GetSize() - nGroupStart;
+						m_arrData[nGroupStart].SetGroupCount(nGroupCount);
+						nGroupStart = -1;
+					}
+					break;
+				default:
+					ATLASSERT(FALSE && _T("Invalid DLGRESIZE Map Entry"));
+					break;
+				}
+			}
+			else
+			{
+				// this ID conflicts with the default gripper one
+				ATLASSERT(m_bGripper ? (pMap->m_nCtlID != ATL_IDW_STATUS_BAR) : TRUE);
+
+				ATL::CWindow ctl = pT->GetDlgItem(pMap->m_nCtlID);
+				ATLASSERT(ctl.IsWindow());
+				RECT rectCtl = {};
+				ctl.GetWindowRect(&rectCtl);
+				::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rectCtl, 2);
+
+				DWORD dwGroupFlag = ((nGroupStart != -1) && (m_arrData.GetSize() == nGroupStart)) ? _DLSZ_BEGIN_GROUP : 0;
+				_AtlDlgResizeData data = { pMap->m_nCtlID, pMap->m_dwResizeFlags | dwGroupFlag, { rectCtl.left, rectCtl.top, rectCtl.right, rectCtl.bottom } };
+				m_arrData.Add(data);
+			}
+		}
+		ATLASSERT((nGroupStart == -1) && _T("No End Group Entry in the DLGRESIZE Map"));
+	}
+
+	void DlgResize_UpdateLayout(int cxWidth, int cyHeight)
+	{
+		T* pT = static_cast<T*>(this);
+		ATLASSERT(::IsWindow(pT->m_hWnd));
+
+		// Restrict minimum size if requested
+		if(((pT->GetStyle() & WS_CHILD) != 0) && (m_ptMinTrackSize.x != -1) && (m_ptMinTrackSize.y != -1))
+		{
+			if(cxWidth < m_ptMinTrackSize.x)
+				cxWidth = m_ptMinTrackSize.x;
+			if(cyHeight < m_ptMinTrackSize.y)
+				cyHeight = m_ptMinTrackSize.y;
+		}
+
+		BOOL bVisible = pT->IsWindowVisible();
+		if(bVisible)
+			pT->SetRedraw(FALSE);
+
+		for(int i = 0; i < m_arrData.GetSize(); i++)
+		{
+			if((m_arrData[i].m_dwResizeFlags & _DLSZ_BEGIN_GROUP) != 0)   // start of a group
+			{
+				int nGroupCount = m_arrData[i].GetGroupCount();
+				ATLASSERT((nGroupCount > 0) && ((i + nGroupCount - 1) < m_arrData.GetSize()));
+				RECT rectGroup = m_arrData[i].m_rect;
+
+				int j = 1;
+				for(j = 1; j < nGroupCount; j++)
+				{
+					rectGroup.left = __min(rectGroup.left, m_arrData[i + j].m_rect.left);
+					rectGroup.top = __min(rectGroup.top, m_arrData[i + j].m_rect.top);
+					rectGroup.right = __max(rectGroup.right, m_arrData[i + j].m_rect.right);
+					rectGroup.bottom = __max(rectGroup.bottom, m_arrData[i + j].m_rect.bottom);
+				}
+
+				for(j = 0; j < nGroupCount; j++)
+				{
+					_AtlDlgResizeData* pDataPrev = NULL;
+					if(j > 0)
+						pDataPrev = &(m_arrData[i + j - 1]);
+					pT->DlgResize_PositionControl(cxWidth, cyHeight, rectGroup, m_arrData[i + j], true, pDataPrev);
+				}
+
+				i += nGroupCount - 1;   // increment to skip all group controls
+			}
+			else // one control entry
+			{
+				RECT rectGroup = {};
+				pT->DlgResize_PositionControl(cxWidth, cyHeight, rectGroup, m_arrData[i], false);
+			}
+		}
+
+		if(bVisible)
+			pT->SetRedraw(TRUE);
+
+		pT->RedrawWindow(NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+	}
+
+// Message map and handlers
+	BEGIN_MSG_MAP(CDialogResize)
+		MESSAGE_HANDLER(WM_SIZE, OnSize)
+		MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo)
+	END_MSG_MAP()
+
+	LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+	{
+		T* pT = static_cast<T*>(this);
+		if(m_bGripper)
+		{
+			ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR);
+			if(wParam == SIZE_MAXIMIZED)
+				wndGripper.ShowWindow(SW_HIDE);
+			else if(wParam == SIZE_RESTORED)
+				wndGripper.ShowWindow(SW_SHOW);
+		}
+		if(wParam != SIZE_MINIMIZED)
+		{
+			ATLASSERT(::IsWindow(pT->m_hWnd));
+			pT->DlgResize_UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+		}
+		return 0;
+	}
+
+	LRESULT OnGetMinMaxInfo(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
+	{
+		if((m_ptMinTrackSize.x != -1) && (m_ptMinTrackSize.y != -1))
+		{
+			LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
+			lpMMI->ptMinTrackSize =  m_ptMinTrackSize;
+		}
+		return 0;
+	}
+
+// Implementation
+	bool DlgResize_PositionControl(int cxWidth, int cyHeight, RECT& rectGroup, _AtlDlgResizeData& data, bool bGroup, 
+	                               _AtlDlgResizeData* pDataPrev = NULL)
+	{
+		T* pT = static_cast<T*>(this);
+		ATLASSERT(::IsWindow(pT->m_hWnd));
+		ATL::CWindow ctl;
+		RECT rectCtl = {};
+
+		ctl = pT->GetDlgItem(data.m_nCtlID);
+		if(!ctl.GetWindowRect(&rectCtl))
+			return false;
+		::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rectCtl, 2);
+
+		if(bGroup)
+		{
+			if((data.m_dwResizeFlags & DLSZ_CENTER_X) != 0)
+			{
+				int cxRight = rectGroup.right + cxWidth - m_sizeDialog.cx;
+				int cxCtl = data.m_rect.right - data.m_rect.left;
+				rectCtl.left = rectGroup.left + (cxRight - rectGroup.left - cxCtl) / 2;
+				rectCtl.right = rectCtl.left + cxCtl;
+			}
+			else if((data.m_dwResizeFlags & (DLSZ_SIZE_X | DLSZ_MOVE_X)) != 0)
+			{
+				rectCtl.left = rectGroup.left + ::MulDiv(data.m_rect.left - rectGroup.left, rectGroup.right - rectGroup.left + (cxWidth - m_sizeDialog.cx), rectGroup.right - rectGroup.left);
+
+				if((data.m_dwResizeFlags & DLSZ_SIZE_X) != 0)
+				{
+					rectCtl.right = rectGroup.left + ::MulDiv(data.m_rect.right - rectGroup.left, rectGroup.right - rectGroup.left + (cxWidth - m_sizeDialog.cx), rectGroup.right - rectGroup.left);
+
+					if(pDataPrev != NULL)
+					{
+						ATL::CWindow ctlPrev = pT->GetDlgItem(pDataPrev->m_nCtlID);
+						RECT rcPrev = {};
+						ctlPrev.GetWindowRect(&rcPrev);
+						::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rcPrev, 2);
+						int dxAdjust = (rectCtl.left - rcPrev.right) - (data.m_rect.left - pDataPrev->m_rect.right);
+						rcPrev.right += dxAdjust;
+						ctlPrev.SetWindowPos(NULL, &rcPrev, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
+					}
+				}
+				else
+				{
+					rectCtl.right = rectCtl.left + (data.m_rect.right - data.m_rect.left);
+				}
+			}
+
+			if((data.m_dwResizeFlags & DLSZ_CENTER_Y) != 0)
+			{
+				int cyBottom = rectGroup.bottom + cyHeight - m_sizeDialog.cy;
+				int cyCtl = data.m_rect.bottom - data.m_rect.top;
+				rectCtl.top = rectGroup.top + (cyBottom - rectGroup.top - cyCtl) / 2;
+				rectCtl.bottom = rectCtl.top + cyCtl;
+			}
+			else if((data.m_dwResizeFlags & (DLSZ_SIZE_Y | DLSZ_MOVE_Y)) != 0)
+			{
+				rectCtl.top = rectGroup.top + ::MulDiv(data.m_rect.top - rectGroup.top, rectGroup.bottom - rectGroup.top + (cyHeight - m_sizeDialog.cy), rectGroup.bottom - rectGroup.top);
+
+				if((data.m_dwResizeFlags & DLSZ_SIZE_Y) != 0)
+				{
+					rectCtl.bottom = rectGroup.top + ::MulDiv(data.m_rect.bottom - rectGroup.top, rectGroup.bottom - rectGroup.top + (cyHeight - m_sizeDialog.cy), rectGroup.bottom - rectGroup.top);
+
+					if(pDataPrev != NULL)
+					{
+						ATL::CWindow ctlPrev = pT->GetDlgItem(pDataPrev->m_nCtlID);
+						RECT rcPrev = {};
+						ctlPrev.GetWindowRect(&rcPrev);
+						::MapWindowPoints(NULL, pT->m_hWnd, (LPPOINT)&rcPrev, 2);
+						int dxAdjust = (rectCtl.top - rcPrev.bottom) - (data.m_rect.top - pDataPrev->m_rect.bottom);
+						rcPrev.bottom += dxAdjust;
+						ctlPrev.SetWindowPos(NULL, &rcPrev, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE);
+					}
+				}
+				else
+				{
+					rectCtl.bottom = rectCtl.top + (data.m_rect.bottom - data.m_rect.top);
+				}
+			}
+		}
+		else // no group
+		{
+			if((data.m_dwResizeFlags & DLSZ_CENTER_X) != 0)
+			{
+				int cxCtl = data.m_rect.right - data.m_rect.left;
+				rectCtl.left = (cxWidth - cxCtl) / 2;
+				rectCtl.right = rectCtl.left + cxCtl;
+			}
+			else if((data.m_dwResizeFlags & (DLSZ_SIZE_X | DLSZ_MOVE_X)) != 0)
+			{
+				rectCtl.right = data.m_rect.right + (cxWidth - m_sizeDialog.cx);
+
+				if((data.m_dwResizeFlags & DLSZ_MOVE_X) != 0)
+					rectCtl.left = rectCtl.right - (data.m_rect.right - data.m_rect.left);
+			}
+
+			if((data.m_dwResizeFlags & DLSZ_CENTER_Y) != 0)
+			{
+				int cyCtl = data.m_rect.bottom - data.m_rect.top;
+				rectCtl.top = (cyHeight - cyCtl) / 2;
+				rectCtl.bottom = rectCtl.top + cyCtl;
+			}
+			else if((data.m_dwResizeFlags & (DLSZ_SIZE_Y | DLSZ_MOVE_Y)) != 0)
+			{
+				rectCtl.bottom = data.m_rect.bottom + (cyHeight - m_sizeDialog.cy);
+
+				if((data.m_dwResizeFlags & DLSZ_MOVE_Y) != 0)
+					rectCtl.top = rectCtl.bottom - (data.m_rect.bottom - data.m_rect.top);
+			}
+		}
+
+		if((data.m_dwResizeFlags & DLSZ_REPAINT) != 0)
+			ctl.Invalidate();
+
+		if((data.m_dwResizeFlags & (DLSZ_SIZE_X | DLSZ_SIZE_Y | DLSZ_MOVE_X | DLSZ_MOVE_Y | DLSZ_REPAINT | DLSZ_CENTER_X | DLSZ_CENTER_Y)) != 0)
+			ctl.SetWindowPos(NULL, &rectCtl, SWP_NOZORDER | SWP_NOACTIVATE);
+
+		return true;
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CDynamicDialogLayout - support for dialog dynamic layout resource info
+//                        (AFX_DIALOG_LAYOUT) in VS2015 and higher
+
+#if (_MSC_VER >= 1900)
+
+template<class T>
+class CDynamicDialogLayout
+{
+public:
+// Data declarations
+	struct _AtlDynamicLayoutData
+	{
+		HWND m_hWnd;
+		char m_nMoveRatioX;
+		char m_nMoveRatioY;
+		char m_nSizeRatioX;
+		char m_nSizeRatioY;
+		RECT m_rcInit;
+	};
+
+// Data members
+	ATL::CSimpleArray<_AtlDynamicLayoutData> m_arrLayoutData;
+	SIZE m_szParentInit;
+	POINT m_ptMinTrackSize;
+	bool m_bGripper;
+
+// Constructor
+	CDynamicDialogLayout() : m_bGripper(false)
+	{
+		m_szParentInit.cx = 0;
+		m_szParentInit.cy = 0;
+		m_ptMinTrackSize.x = -1;
+		m_ptMinTrackSize.y = -1;
+	}
+
+// Methods
+	void InitDynamicLayout(bool bAddGripper = true, bool bMinTrackSize = true)
+	{
+		T* pT = static_cast<T*>(this);
+		ATLASSERT(::IsWindow(pT->m_hWnd));
+
+		// Cleanup in case of multiple initialization
+		// block: first check for the gripper control, destroy it if needed
+		{
+			ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR);
+			if(wndGripper.IsWindow() != FALSE)
+				wndGripper.DestroyWindow();
+		}
+		// clear out everything else
+		m_arrLayoutData.RemoveAll();
+		m_ptMinTrackSize.x = -1;
+		m_ptMinTrackSize.y = -1;
+		m_szParentInit.cx = 0;
+		m_szParentInit.cy = 0;
+		m_bGripper = false;
+
+		CResource rcLayout;
+		if(rcLayout.Load(_T("AFX_DIALOG_LAYOUT"), pT->IDD))
+		{
+			int nCount = rcLayout.GetSize() / sizeof(WORD);
+			if(nCount > 1)
+			{
+				RECT rcParent = {};
+				pT->GetWindowRect(&rcParent);
+				m_szParentInit.cx = rcParent.right - rcParent.left;
+				m_szParentInit.cy = rcParent.bottom - rcParent.top;
+
+				WORD* pnData = (WORD*)rcLayout.Lock();
+				WORD wVersion = *pnData;   // AFX_DIALOG_LAYOUT version
+				ATLASSERT(wVersion == 0);
+				if(wVersion == 0)
+				{
+					pnData++;   // skip version
+					ATL::CWindow wndChild = pT->GetWindow(GW_CHILD);
+					for(int i = 0; (i < nCount) && (wndChild.m_hWnd != NULL); i += 4)
+					{
+						char nMoveRatioX = _GetDataPct(pnData[i]);
+						char nMoveRatioY = _GetDataPct(pnData[i + 1]);
+						char nSizeRatioX = _GetDataPct(pnData[i + 2]);
+						char nSizeRatioY = _GetDataPct(pnData[i + 3]);
+
+						bool bValid = ((nMoveRatioX != -1) && (nMoveRatioY != -1) && (nSizeRatioX != -1) && (nSizeRatioY != -1));
+						bool bAction = ((nMoveRatioX != 0) || (nMoveRatioY != 0) || (nSizeRatioX != 0) || (nSizeRatioY != 0));
+						if(bValid && bAction)
+						{
+							_AtlDynamicLayoutData LayoutData = { wndChild.m_hWnd, nMoveRatioX, nMoveRatioY, nSizeRatioX, nSizeRatioY };
+							wndChild.GetWindowRect(&LayoutData.m_rcInit);
+							pT->ScreenToClient(&LayoutData.m_rcInit);
+							m_arrLayoutData.Add(LayoutData);
+						}
+
+						wndChild = wndChild.GetWindow(GW_HWNDNEXT);
+					}
+				}
+
+				rcLayout.Release();
+			}
+		}
+
+		if(bAddGripper)
+		{
+			RECT rcDialog = {};
+			pT->GetClientRect(&rcDialog);
+
+			ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR);
+			if(wndGripper.m_hWnd != NULL)
+				wndGripper.DestroyWindow();
+
+			wndGripper.Create(_T("SCROLLBAR"), pT->m_hWnd, rcDialog, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SBS_SIZEBOX | SBS_SIZEGRIP | SBS_SIZEBOXBOTTOMRIGHTALIGN, 0, ATL_IDW_STATUS_BAR);
+			ATLASSERT(wndGripper.m_hWnd != NULL);
+			if(wndGripper.m_hWnd != NULL)
+			{
+				m_bGripper = true;
+				_AtlDynamicLayoutData LayoutData = { wndGripper.m_hWnd, 100, 100, 0, 0 };
+				wndGripper.GetWindowRect(&LayoutData.m_rcInit);
+				pT->ScreenToClient(&LayoutData.m_rcInit);
+				m_arrLayoutData.Add(LayoutData);
+			}
+		}
+
+		if(bMinTrackSize)
+		{
+			RECT rcMinTrack = {};
+			if((pT->GetStyle() & WS_CHILD) != 0)
+				pT->GetClientRect(&rcMinTrack);
+			else
+				pT->GetWindowRect(&rcMinTrack);
+
+			m_ptMinTrackSize.x = rcMinTrack.right - rcMinTrack.left;
+			m_ptMinTrackSize.y = rcMinTrack.bottom - rcMinTrack.top;
+		}
+	}
+
+	void UpdateDynamicLayout()
+	{
+		T* pT = static_cast<T*>(this);
+		ATLASSERT(::IsWindow(pT->m_hWnd));
+
+		int nCount = m_arrLayoutData.GetSize();
+		if(nCount == 0)
+			return;
+
+		RECT rcParent = {};
+		pT->GetWindowRect(&rcParent);
+
+		int nDeltaWidth = rcParent.right - rcParent.left - m_szParentInit.cx;
+		int nDeltaHeight = rcParent.bottom - rcParent.top - m_szParentInit.cy;
+
+		HDWP hDwp = ::BeginDeferWindowPos(nCount);
+
+		for(int i = 0; i < nCount; i++)
+		{
+			_AtlDynamicLayoutData& LayoutData = m_arrLayoutData[i];
+
+			ATLASSERT(::IsWindow(LayoutData.m_hWnd) != FALSE);
+
+			int nID = ::GetDlgCtrlID(LayoutData.m_hWnd);
+			UINT nFlags = (SWP_NOMOVE | SWP_NOSIZE);
+			RECT rcItem = LayoutData.m_rcInit;
+
+			if(((nID == ATL_IDW_STATUS_BAR) || (nDeltaWidth >= 0)) && (LayoutData.m_nMoveRatioX != 0))
+			{
+				rcItem.left += ::MulDiv(nDeltaWidth, LayoutData.m_nMoveRatioX, 100);
+				rcItem.right += ::MulDiv(nDeltaWidth, LayoutData.m_nMoveRatioX, 100);
+				nFlags &= ~SWP_NOMOVE;
+			}
+
+			if(((nID == ATL_IDW_STATUS_BAR) || (nDeltaHeight >= 0)) && (LayoutData.m_nMoveRatioY != 0))
+			{
+				rcItem.top += ::MulDiv(nDeltaHeight, LayoutData.m_nMoveRatioY, 100);
+				rcItem.bottom += ::MulDiv(nDeltaHeight, LayoutData.m_nMoveRatioY, 100);
+				nFlags &= ~SWP_NOMOVE;
+			}
+
+			if((nDeltaWidth >= 0) && (LayoutData.m_nSizeRatioX != 0))
+			{
+				rcItem.right += ::MulDiv(nDeltaWidth, LayoutData.m_nSizeRatioX, 100);
+				nFlags &= ~SWP_NOSIZE;
+			}
+
+			if((nDeltaHeight >= 0) && (LayoutData.m_nSizeRatioY != 0))
+			{
+				rcItem.bottom += ::MulDiv(nDeltaHeight, LayoutData.m_nSizeRatioY, 100);
+				nFlags &= ~SWP_NOSIZE;
+			}
+
+			if(nFlags != (SWP_NOMOVE | SWP_NOSIZE))
+				::DeferWindowPos(hDwp, LayoutData.m_hWnd, NULL, rcItem.left, rcItem.top, rcItem.right - rcItem.left, rcItem.bottom - rcItem.top, nFlags | SWP_NOZORDER | SWP_NOREPOSITION | SWP_NOACTIVATE | SWP_NOCOPYBITS);
+		}
+
+		::EndDeferWindowPos(hDwp);
+	}
+
+// Message map and handlers
+	BEGIN_MSG_MAP(CDynamicDialogLayout)
+		MESSAGE_HANDLER(WM_SIZE, OnSize)
+		MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo)
+	END_MSG_MAP()
+
+	LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+	{
+		T* pT = static_cast<T*>(this);
+
+		if(m_bGripper)
+		{
+			ATL::CWindow wndGripper = pT->GetDlgItem(ATL_IDW_STATUS_BAR);
+			if(wndGripper.m_hWnd != NULL)
+			{
+				if(wParam == SIZE_MAXIMIZED)
+					wndGripper.ShowWindow(SW_HIDE);
+				else if(wParam == SIZE_RESTORED)
+					wndGripper.ShowWindow(SW_SHOW);
+			}
+		}
+
+		if(wParam != SIZE_MINIMIZED)
+			pT->UpdateDynamicLayout();
+
+		return 0;
+	}
+
+	LRESULT OnGetMinMaxInfo(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
+	{
+		if((m_ptMinTrackSize.x != -1) && (m_ptMinTrackSize.y != -1))
+		{
+			LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
+			lpMMI->ptMinTrackSize =  m_ptMinTrackSize;
+		}
+
+		return 0;
+	}
+
+// Implementation
+	char _GetDataPct(WORD wResData)
+	{
+		ATLASSERT((wResData >= 0) && (wResData <= 100));
+		char nPct = (char)LOBYTE(wResData);
+		if((nPct < 0) || (nPct > 100))
+			nPct = -1;
+
+		return nPct;
+	}
+};
+
+#endif // (_MSC_VER >= 1900)
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CDoubleBufferImpl - Provides double-buffer painting support to any window
+
+template <class T>
+class CDoubleBufferImpl
+{
+public:
+// Overrideables
+	void DoPaint(CDCHandle /*dc*/)
+	{
+		// must be implemented in a derived class
+		ATLASSERT(FALSE);
+	}
+
+// Message map and handlers
+	BEGIN_MSG_MAP(CDoubleBufferImpl)
+		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
+		MESSAGE_HANDLER(WM_PAINT, OnPaint)
+		MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
+	END_MSG_MAP()
+
+	LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+	{
+		return 1;   // no background painting needed
+	}
+
+	LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
+	{
+		T* pT = static_cast<T*>(this);
+		ATLASSERT(::IsWindow(pT->m_hWnd));
+
+		if(wParam != NULL)
+		{
+			RECT rect = {};
+			pT->GetClientRect(&rect);
+			CMemoryDC dcMem((HDC)wParam, rect);
+			pT->DoPaint(dcMem.m_hDC);
+		}
+		else
+		{
+			CPaintDC dc(pT->m_hWnd);
+			CMemoryDC dcMem(dc.m_hDC, dc.m_ps.rcPaint);
+			pT->DoPaint(dcMem.m_hDC);
+		}
+
+		return 0;
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CDoubleBufferWindowImpl - Implements a double-buffer painting window
+
+template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CControlWinTraits>
+class ATL_NO_VTABLE CDoubleBufferWindowImpl : public ATL::CWindowImpl< T, TBase, TWinTraits >, public CDoubleBufferImpl< T >
+{
+public:
+	BEGIN_MSG_MAP(CDoubleBufferWindowImpl)
+		CHAIN_MSG_MAP(CDoubleBufferImpl< T >)
+	END_MSG_MAP()
+};
+
+
+// command bar support
+#if !defined(__ATLCTRLW_H__)
+  #undef CBRM_GETMENU
+  #undef CBRM_TRACKPOPUPMENU
+  #undef CBRM_GETCMDBAR
+  #undef CBRPOPUPMENU
+#endif // !defined(__ATLCTRLW_H__)
+
+} // namespace WTL
+
+#endif // __ATLFRAME_H__