root/third_party/wtl/include/atlframe.h

/* [<][>][^][v][top][bottom][index][help] */
// Windows Template Library - WTL version 8.0
// Copyright (C) Microsoft Corporation. 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 Permissive License (Ms-PL) which can be found in the file
// Ms-PL.txt at the root of this distribution.

#ifndef __ATLFRAME_H__
#define __ATLFRAME_H__

#pragma once

#ifndef __cplusplus
        #error ATL requires C++ compilation (use a .cpp suffix)
#endif

#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>
// CDialogResize<T>
// CDoubleBufferImpl<T>
// CDoubleBufferWindowImpl<T, TBase, TWinTraits>
//
// Global functions:
//   AtlCreateSimpleToolBar()


namespace WTL
{

///////////////////////////////////////////////////////////////////////////////
// CFrameWndClassInfo - Manages frame window Windows class information

class CFrameWndClassInfo
{
public:
#ifndef _WIN32_WCE
        enum { cchAutoName = 5 + sizeof(void*) * 2 };   // sizeof(void*) * 2 is the number of digits %p outputs
        WNDCLASSEX m_wc;
#else // CE specific
        enum { cchAutoName = MAX_PATH };   // MAX_PATH because this can be set in the wizard generated CMainFrame::ActivatePreviousInstance to a user defined string.
        WNDCLASS m_wc;
#endif // !_WIN32_WCE
        LPCTSTR m_lpszOrigName;
        WNDPROC pWndProc;
        LPCTSTR m_lpszCursorID;
        BOOL m_bSystemCursor;
        ATOM m_atom;
        TCHAR m_szAutoName[cchAutoName];
        UINT m_uCommonResourceID;

#ifndef _WIN32_WCE
        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 = { 0 };
                                        wc.cbSize = 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)
                                {
#if (_WIN32_WINNT >= 0x0500) || defined(_WIN64)
                                        SecureHelper::wsprintf_x(m_szAutoName, cchAutoName, _T("ATL:%p"), &m_wc);
#else // !((_WIN32_WINNT >= 0x0500) || defined(_WIN64))
                                        SecureHelper::wsprintf_x(m_szAutoName, cchAutoName, _T("ATL:%8.8X"), (DWORD_PTR)&m_wc);
#endif // !((_WIN32_WINNT >= 0x0500) || defined(_WIN64))
                                        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, 32, 32, LR_DEFAULTCOLOR);
                                                m_wc.hIconSm = (HICON)::LoadImage(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(m_uCommonResourceID), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
                                        }
                                        m_atom = ::RegisterClassEx(&m_wc);
                                }
                        }

                        lock.Unlock();
                }

                if (m_lpszOrigName != NULL)
                {
                        ATLASSERT(pProc != NULL);
                        ATLASSERT(pWndProc != NULL);
                        *pProc = pWndProc;
                }

                return m_atom;
        }
#else // CE specific
        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;

                                        WNDCLASS wc = { 0 };
                                        // try process local class first
                                        if(!::GetClassInfo(ModuleHelper::GetModuleInstance(), m_lpszOrigName, &wc))
                                        {
                                                // try global class
                                                if(!::GetClassInfo(NULL, m_lpszOrigName, &wc))
                                                {
                                                        lock.Unlock();
                                                        return 0;
                                                }
                                        }
                                        m_wc = wc;
                                        pWndProc = m_wc.lpfnWndProc;
                                        m_wc.lpszClassName = lpsz;
                                        m_wc.lpfnWndProc = proc;
                                }
                                else
                                {
#if defined(GWES_CURSOR) || defined(GWES_MCURSOR)
                                        m_wc.hCursor = ::LoadCursor(m_bSystemCursor ? NULL : hInst, m_lpszCursorID);
#else // !(defined(GWES_CURSOR) || defined(GWES_MCURSOR))
                                        m_wc.hCursor = NULL;
#endif // !(defined(GWES_CURSOR) || defined(GWES_MCURSOR))
                                }

                                m_wc.hInstance = hInst;
                                m_wc.style &= ~CS_GLOBALCLASS;   // we don't register global classes
                                if (m_wc.lpszClassName == NULL)
                                {
                                        wsprintf(m_szAutoName, _T("ATL:%8.8X"), (DWORD_PTR)&m_wc);
                                        m_wc.lpszClassName = m_szAutoName;
                                }

                                WNDCLASS wcTemp = m_wc;
                                m_atom = (ATOM)::GetClassInfo(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, 32, 32, LR_DEFAULTCOLOR);
                                        m_atom = ::RegisterClass(&m_wc);
                                }
                        }

                        lock.Unlock();
                }

                if (m_lpszOrigName != NULL)
                {
                        ATLASSERT(pProc != NULL);
                        ATLASSERT(pWndProc != NULL);
                        *pProc = pWndProc;
                }

                return m_atom;
        }
#endif // _WIN32_WCE
};


///////////////////////////////////////////////////////////////////////////////
// Macros for declaring frame window WNDCLASS

#ifndef _WIN32_WCE

#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; \
}

#else // CE specific

#define DECLARE_FRAME_WND_CLASS(WndClassName, uCommonResourceID) \
static WTL::CFrameWndClassInfo& GetWndClassInfo() \
{ \
        static WTL::CFrameWndClassInfo wc = \
        { \
                { 0, StartWindowProc, \
                  0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, WndClassName }, \
                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 = \
        { \
                { style, StartWindowProc, \
                  0, 0, NULL, NULL, NULL, (HBRUSH)(bkgnd + 1), NULL, WndClassName }, \
                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 = \
        { \
                { NULL, StartWindowProc, \
                  0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName }, \
                OrigWndClassName, NULL, IDC_ARROW, TRUE, 0, _T(""), uCommonResourceID \
        }; \
        return wc; \
}

#endif // !_WIN32_WCE


///////////////////////////////////////////////////////////////////////////////
// CFrameWindowImpl

// Client window command chaining macro (only for frame windows)
#define CHAIN_CLIENT_COMMANDS() \
        if(uMsg == WM_COMMAND && m_hWndClient != NULL) \
                ::SendMessage(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
#if (_WIN32_IE >= 0x0400)
  #define ATL_SIMPLE_REBAR_STYLE \
        (WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS | RBS_AUTOSIZE)
#else
  #define ATL_SIMPLE_REBAR_STYLE \
        (WS_CHILD | WS_VISIBLE | WS_BORDER | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS)
#endif // !(_WIN32_IE >= 0x0400)
// rebar without borders
#if (_WIN32_IE >= 0x0400)
  #define ATL_SIMPLE_REBAR_NOBORDER_STYLE \
        (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS | RBS_AUTOSIZE | CCS_NODIVIDER)
#else
  #define ATL_SIMPLE_REBAR_NOBORDER_STYLE \
        (WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | RBS_VARHEIGHT | RBS_BANDBORDERS | CCS_NODIVIDER)
#endif // !(_WIN32_IE >= 0x0400)

// command bar support
#if !defined(__ATLCTRLW_H__) && !defined(_WIN32_WCE)

#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__) && !defined(_WIN32_WCE)


template <class TBase = ATL::CWindow, class TWinTraits = ATL::CFrameWinTraits>
class ATL_NO_VTABLE CFrameWindowImplBase : public ATL::CWindowImplBaseT< TBase, TWinTraits >
{
public:
        DECLARE_FRAME_WND_CLASS(NULL, 0)

// Data members
        HWND m_hWndToolBar;
        HWND m_hWndStatusBar;
        HWND m_hWndClient;

        HACCEL m_hAccel;

#ifdef _WIN32_WCE
        HWND m_hWndCECommandBar;
#endif // _WIN32_WCE

        struct _AtlToolBarData
        {
                WORD wVersion;
                WORD wWidth;
                WORD wHeight;
                WORD wItemCount;
                //WORD aItems[wItemCount]

                WORD* items()
                        { return (WORD*)(this+1); }
        };

#if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE)
        struct _ChevronMenuInfo
        {
                HMENU hMenu;
                LPNMREBARCHEVRON lpnm;
                bool bCmdBar;
        };
#endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE)

// Constructor
        CFrameWindowImplBase() : 
#ifdef _WIN32_WCE
                m_hWndCECommandBar(NULL),
#endif // _WIN32_WCE
                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(m_hWnd == NULL);

                if(atom == 0)
                        return NULL;

                ModuleHelper::AddCreateWndData(&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 || 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);
                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 = TBSTYLE_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 = TBSTYLE_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 = TBSTYLE_SEP;
                                pTBBtn[j].dwData = 0;
                                pTBBtn[j].iString = 0;
                        }
                }

#ifndef _WIN32_WCE
                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;
                }
#else // CE specific
                dwStyle;
                nID;
                // The toolbar must go onto the existing CommandBar or MenuBar
                HWND hWnd = hWndParent;
#endif // _WIN32_WCE

                ::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 = AtlGetDefaultGuiFont();
                LOGFONT lf = { 0 };
                font.GetLogFont(lf);
                WORD cyFontHeight = (WORD)abs(lf.lfHeight);

#ifndef _WIN32_WCE
                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
#endif // !_WIN32_WCE
                {
                        TBADDBITMAP tbab = { 0 };
                        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;
        }

#ifndef _WIN32_WCE
        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 = { 0 };
                rbi.cbSize = sizeof(REBARINFO);
                rbi.fMask  = 0;
                if(!::SendMessage(hWndReBar, RB_SETBARINFO, 0, (LPARAM)&rbi))
                {
                        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(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)] = { 0 };
                        ::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() };
#if (_WIN32_IE >= 0x0400)
                rbBand.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_ID | RBBIM_SIZE | RBBIM_IDEALSIZE;
#else
                rbBand.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_ID | RBBIM_SIZE;
#endif // !(_WIN32_IE >= 0x0400)
                if(lpstrTitle != NULL)
                        rbBand.fMask |= RBBIM_TEXT;
                rbBand.fStyle = RBBS_CHILDEDGE;
#if (_WIN32_IE >= 0x0500)
                if(nBtnCount > 0)   // add chevron style for toolbar with buttons
                        rbBand.fStyle |= RBBS_USECHEVRON;
#endif // (_WIN32_IE >= 0x0500)
                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 = { 0 };
                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;
                }

#if (_WIN32_IE >= 0x0400)
                rbBand.cxIdeal = rbBand.cx;
#endif // (_WIN32_IE >= 0x0400)

                // 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;
                }

#if (_WIN32_IE >= 0x0501)
                DWORD dwExStyle = (DWORD)::SendMessage(hWndBand, TB_GETEXTENDEDSTYLE, 0, 0L);
                ::SendMessage(hWndBand, TB_SETEXTENDEDSTYLE, 0, dwExStyle | TBSTYLE_EX_HIDECLIPPEDBUTTONS);
#endif // (_WIN32_IE >= 0x0501)

                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);
        }

#if (_WIN32_IE >= 0x0400)
        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 = { 0, 0, 0, 0 };
                        ::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);
                }
        }
#endif // (_WIN32_IE >= 0x0400)
#endif // _WIN32_WCE

#ifndef _WIN32_WCE
        BOOL CreateSimpleStatusBar(LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_SIZEGRIP, UINT nID = ATL_IDW_STATUS_BAR)
#else // CE specific
        BOOL CreateSimpleStatusBar(LPCTSTR lpstrText, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, UINT nID = ATL_IDW_STATUS_BAR)
#endif // _WIN32_WCE
        {
                ATLASSERT(!::IsWindow(m_hWndStatusBar));
                m_hWndStatusBar = ::CreateStatusWindow(dwStyle, lpstrText, m_hWnd, nID);
                return (m_hWndStatusBar != NULL);
        }

#ifndef _WIN32_WCE
        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)
#else // CE specific
        BOOL CreateSimpleStatusBar(UINT nTextID = ATL_IDS_IDLEMESSAGE, DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, UINT nID = ATL_IDW_STATUS_BAR)
#endif // _WIN32_WCE
        {
                const int cchMax = 128;   // max text length is 127 for status bars (+1 for null)
                TCHAR szText[cchMax];
                szText[0] = 0;
                ::LoadString(ModuleHelper::GetResourceInstance(), nTextID, szText, cchMax);
                return CreateSimpleStatusBar(szText, dwStyle, nID);
        }

#ifdef _WIN32_WCE
        BOOL CreateSimpleCECommandBar(LPTSTR pszMenu = NULL, WORD iButton = 0, DWORD dwFlags = 0, int nCmdBarID = 1)
        {
                ATLASSERT(m_hWndCECommandBar == NULL);
                ATLASSERT(m_hWndToolBar == NULL);

                m_hWndCECommandBar = ::CommandBar_Create(ModuleHelper::GetModuleInstance(), m_hWnd, nCmdBarID);
                if(m_hWndCECommandBar == NULL)
                        return FALSE;

                m_hWndToolBar = m_hWndCECommandBar;

                BOOL bRet = TRUE;

                if(pszMenu != NULL)
                        bRet &= ::CommandBar_InsertMenubarEx(m_hWndCECommandBar, IS_INTRESOURCE(pszMenu) ? ModuleHelper::GetResourceInstance() : NULL, pszMenu, iButton);

                bRet &= ::CommandBar_AddAdornments(m_hWndCECommandBar, dwFlags, 0);

                return bRet;
        }

#if defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__)
        BOOL CreateSimpleCEMenuBar(UINT nToolBarId = ATL_IDW_MENU_BAR, DWORD dwFlags = 0, int nBmpId = 0, int cBmpImages = 0)
        {
                ATLASSERT(m_hWndCECommandBar == NULL);

                SHMENUBARINFO mbi = { 0 };
                mbi.cbSize = sizeof(mbi);
                mbi.hwndParent = m_hWnd;
                mbi.dwFlags = dwFlags;
                mbi.nToolBarId = nToolBarId;
                mbi.hInstRes  = ModuleHelper::GetResourceInstance();
                mbi.nBmpId = nBmpId;
                mbi.cBmpImages = cBmpImages;
                mbi.hwndMB = NULL;   // This gets set by SHCreateMenuBar

                BOOL bRet = ::SHCreateMenuBar(&mbi);
                if(bRet != FALSE)
                {
                        m_hWndCECommandBar = mbi.hwndMB;
                        SizeToMenuBar();
                }

                return bRet;
        }

        void SizeToMenuBar()   // for menu bar only
        {
                ATLASSERT(::IsWindow(m_hWnd));
                ATLASSERT(::IsWindow(m_hWndCECommandBar));

                RECT rect = { 0 };
                GetWindowRect(&rect);
                RECT rectMB = { 0 };
                ::GetWindowRect(m_hWndCECommandBar, &rectMB);
                int cy = ::IsWindowVisible(m_hWndCECommandBar) ? rectMB.top - rect.top : rectMB.bottom - rect.top;
                SetWindowPos(NULL, 0, 0, rect.right - rect.left, cy, SWP_NOZORDER | SWP_NOMOVE);
        }
#endif // defined(_AYGSHELL_H_) || defined(__AYGSHELL_H__)
#endif // _WIN32_WCE

        void UpdateLayout(BOOL bResizeBars = TRUE)
        {
                RECT rect = { 0 };
                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)
                        {
                                ::SendMessage(m_hWndToolBar, WM_SIZE, 0, 0);
                                ::InvalidateRect(m_hWndToolBar, NULL, FALSE);
                        }
                        RECT rectTB = { 0 };
                        ::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)
                                ::SendMessage(m_hWndStatusBar, WM_SIZE, 0, 0);
                        RECT rectSB = { 0 };
                        ::GetWindowRect(m_hWndStatusBar, &rectSB);
                        rect.bottom -= rectSB.bottom - rectSB.top;
                }
        }

        BOOL PreTranslateMessage(MSG* pMsg)
        {
                if(m_hAccel != NULL && ::TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
                        return TRUE;
                return FALSE;
        }

        BEGIN_MSG_MAP(CFrameWindowImplBase)
                MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
#ifndef _WIN32_WCE
                MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
#endif // !_WIN32_WCE
                MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
                MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
#ifndef _WIN32_WCE
                NOTIFY_CODE_HANDLER(TTN_GETDISPINFOA, OnToolTipTextA)
                NOTIFY_CODE_HANDLER(TTN_GETDISPINFOW, OnToolTipTextW)
#endif // !_WIN32_WCE
        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;
        }

#ifndef _WIN32_WCE
        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];
                        szBuff[0] = 0;
                        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;
        }
#endif // !_WIN32_WCE

        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((GetStyle() & (WS_CHILD | WS_POPUP)) == 0)
                        ::PostQuitMessage(1);

                bHandled = FALSE;
                return 1;
        }

#ifndef _WIN32_WCE
        LRESULT OnToolTipTextA(int idCtrl, LPNMHDR pnmh, BOOL& /*bHandled*/)
        {
                LPNMTTDISPINFOA pDispInfo = (LPNMTTDISPINFOA)pnmh;
                pDispInfo->szText[0] = 0;

                if((idCtrl != 0) && !(pDispInfo->uFlags & TTF_IDISHWND))
                {
                        const int cchBuff = 256;
                        char szBuff[cchBuff];
                        szBuff[0] = 0;
                        int nRet = ::LoadStringA(ModuleHelper::GetResourceInstance(), idCtrl, szBuff, cchBuff);
                        for(int i = 0; i < nRet; i++)
                        {
                                if(szBuff[i] == '\n')
                                {
                                        SecureHelper::strncpyA_x(pDispInfo->szText, _countof(pDispInfo->szText), &szBuff[i + 1], _TRUNCATE);
                                        break;
                                }
                        }
#if (_WIN32_IE >= 0x0300)
                        if(nRet > 0)   // string was loaded, save it
                                pDispInfo->uFlags |= TTF_DI_SETITEM;
#endif // (_WIN32_IE >= 0x0300)
                }

                return 0;
        }

        LRESULT OnToolTipTextW(int idCtrl, LPNMHDR pnmh, BOOL& /*bHandled*/)
        {
                LPNMTTDISPINFOW pDispInfo = (LPNMTTDISPINFOW)pnmh;
                pDispInfo->szText[0] = 0;

                if((idCtrl != 0) && !(pDispInfo->uFlags & TTF_IDISHWND))
                {
                        const int cchBuff = 256;
                        wchar_t szBuff[cchBuff];
                        szBuff[0] = 0;
                        int nRet = ::LoadStringW(ModuleHelper::GetResourceInstance(), idCtrl, szBuff, cchBuff);
                        for(int i = 0; i < nRet; i++)
                        {
                                if(szBuff[i] == L'\n')
                                {
                                        SecureHelper::strncpyW_x(pDispInfo->szText, _countof(pDispInfo->szText), &szBuff[i + 1], _TRUNCATE);
                                        break;
                                }
                        }
#if (_WIN32_IE >= 0x0300)
                        if(nRet > 0)   // string was loaded, save it
                                pDispInfo->uFlags |= TTF_DI_SETITEM;
#endif // (_WIN32_IE >= 0x0300)
                }

                return 0;
        }
#endif // !_WIN32_WCE

// Implementation - chevron menu support
#if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE)
        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 = { 0 };
                bRet = wnd.GetClientRect(&rcClient);
                ATLASSERT(bRet);
                for(int i = 0; i < nCount; i++)
                {
                        TBBUTTON tbb = { 0 };
                        bRet = (BOOL)wnd.SendMessage(TB_GETBUTTON, i, (LPARAM)&tbb);
                        ATLASSERT(bRet);
                        // skip hidden buttons
                        if((tbb.fsState & TBSTATE_HIDDEN) != 0)
                                continue;
                        RECT rcButton = { 0 };
                        bRet = (BOOL)wnd.SendMessage(TB_GETITEMRECT, i, (LPARAM)&rcButton);
                        ATLASSERT(bRet);
                        bool bEnabled = ((tbb.fsState & TBSTATE_ENABLED) != 0);
                        if(rcButton.right > rcClient.right)
                        {
                                if(tbb.fsStyle & BTNS_SEP)
                                {
                                        if(menu.GetMenuItemCount() > 0)
                                                menu.AppendMenu(MF_SEPARATOR);
                                }
                                else if(cmi.bCmdBar)
                                {
                                        const int cchBuff = 200;
                                        TCHAR szBuff[cchBuff] = { 0 };
                                        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] = { 0 };
                                        LPTSTR lpstrText = szBuff;
                                        TBBUTTONINFO tbbi = { 0 };
                                        tbbi.cbSize = sizeof(TBBUTTONINFO);
                                        tbbi.dwMask = TBIF_TEXT;
                                        tbbi.pszText = szBuff;
                                        tbbi.cchText = cchBuff;
                                        if(wnd.SendMessage(TB_GETBUTTONINFO, tbb.idCommand, (LPARAM)&tbbi) == -1 || lstrlen(szBuff) == 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)
        {
#ifndef TPM_VERPOSANIMATION
                const UINT TPM_VERPOSANIMATION = 0x1000L;   // Menu animation flag
#endif
                // 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 | (!AtlIsOldWindows() ? TPM_VERPOSANIMATION : 0);
                TPMPARAMS TPMParams = { 0 };
                TPMParams.cbSize = sizeof(TPMPARAMS);
                TPMParams.rcExclude = rc;
                // check if this window has a command bar
                HWND hWndCmdBar = (HWND)::SendMessage(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, 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 = { 0 };
                if(::PeekMessage(&msg, m_hWnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_NOREMOVE) && ::PtInRect(&rc, msg.pt))
                        ::PeekMessage(&msg, m_hWnd, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE);
        }
#endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE)
};


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(&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];
                szWindowName[0] = 0;
#ifndef _WIN32_WCE
                ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName);
                HMENU hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(T::GetWndClassInfo().m_uCommonResourceID));
#else // CE specific
                ::LoadString(ModuleHelper::GetResourceInstance(), T::GetWndClassInfo().m_uCommonResourceID, szWindowName, cchName);

                // This always needs to be NULL for Windows CE.
                // Frame Window menus have to go onto the CommandBar.
                // Use CreateSimpleCECommandBar
                HMENU hMenu = NULL;
#endif // _WIN32_WCE

                T* pT = static_cast<T*>(this);
                HWND hWnd = pT->Create(hWndParent, rect, szWindowName, dwStyle, dwExStyle, hMenu, lpCreateParam);

                if(hWnd != NULL)
                        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;
#ifndef _WIN32_WCE
                ATLASSERT(!::IsWindow(m_hWndToolBar));
                m_hWndToolBar = T::CreateSimpleToolBarCtrl(m_hWnd, nResourceID, TRUE, dwStyle, nID);
                return (m_hWndToolBar != NULL);
#else // CE specific
                HWND hWnd= T::CreateSimpleToolBarCtrl(m_hWndCECommandBar, nResourceID, TRUE, dwStyle, nID);
                return (hWnd != NULL);
#endif // _WIN32_WCE
        }

#ifdef _WIN32_WCE
        // CE specific variant that returns the handle of the toolbar
        HWND CreateSimpleCEToolBar(UINT nResourceID = 0, DWORD dwStyle = ATL_SIMPLE_TOOLBAR_STYLE, UINT nID = ATL_IDW_TOOLBAR)
        {
                if(nResourceID == 0)
                        nResourceID = T::GetWndClassInfo().m_uCommonResourceID;

                return T::CreateSimpleToolBarCtrl(m_hWndCECommandBar, nResourceID, TRUE, dwStyle, nID);
        }
#endif // _WIN32_WCE

// message map and handlers
        typedef CFrameWindowImplBase< TBase, TWinTraits >   _baseClass;

        BEGIN_MSG_MAP(CFrameWindowImpl)
                MESSAGE_HANDLER(WM_SIZE, OnSize)
#ifndef _ATL_NO_REBAR_SUPPORT
#if (_WIN32_IE >= 0x0400)
                NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize)
#endif // (_WIN32_IE >= 0x0400)
#if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE)
                NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed)
#endif // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE)
#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
#if (_WIN32_IE >= 0x0400)
        LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                pT->UpdateLayout(FALSE);
                return 0;
        }
#endif // (_WIN32_IE >= 0x0400)

#if (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE)
        LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
        {
                T* pT = static_cast<T*>(this);
                _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 // (_WIN32_IE >= 0x0500) && !defined(_WIN32_WCE)
#endif // !_ATL_NO_REBAR_SUPPORT
};


///////////////////////////////////////////////////////////////////////////////
// AtlCreateSimpleToolBar - helper for creating simple toolbars

#ifndef _WIN32_WCE

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);
}

#endif // !_WIN32_WCE


///////////////////////////////////////////////////////////////////////////////
// CMDIWindow

#ifndef _WIN32_WCE

#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;
                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);
        }
};

#endif // !_WIN32_WCE


///////////////////////////////////////////////////////////////////////////////
// CMDIFrameWindowImpl

#ifndef _WIN32_WCE

// MDI child command chaining macro (only for MDI frame windows)
#define CHAIN_MDI_CHILD_COMMANDS() \
        if(uMsg == WM_COMMAND) \
        { \
                HWND hWndChild = 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)
        {
                m_hMenu = hMenu;
                ATOM atom = T::GetWndClassInfo().Register(&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];
                szWindowName[0] = 0;
                ::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)
                        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(m_hWndToolBar));
                if(nResourceID == 0)
                        nResourceID = T::GetWndClassInfo().m_uCommonResourceID;
                m_hWndToolBar = T::CreateSimpleToolBarCtrl(m_hWnd, nResourceID, TRUE, dwStyle, nID);
                return (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
#if (_ATL_VER >= 0x0700)
                ATL::_ATL_MSG msg(pThis->m_hWnd, uMsg, wParam, lParam);
                const ATL::_ATL_MSG* pOldMsg = pThis->m_pCurrentMsg;
#else // !(_ATL_VER >= 0x0700)
                MSG msg = { pThis->m_hWnd, uMsg, wParam, lParam, 0, { 0, 0 } };
                const MSG* pOldMsg = pThis->m_pCurrentMsg;
#endif // !(_ATL_VER >= 0x0700)
                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);
#if (_ATL_VER >= 0x0700)
                                // mark window as destryed
                                pThis->m_dwState |= WINSTATE_DESTROYED;
#else // !(_ATL_VER >= 0x0700)
                                // clear out window handle
                                HWND hWnd = pThis->m_hWnd;
                                pThis->m_hWnd = NULL;
                                // clean up after window is destroyed
                                pThis->OnFinalMessage(hWnd);
#endif // !(_ATL_VER >= 0x0700)
                        }
                }
#if (_ATL_VER >= 0x0700)
                if(pThis->m_dwState & WINSTATE_DESTROYED && pThis->m_pCurrentMsg == NULL)
                {
                        // clear out window handle
                        HWND hWnd = pThis->m_hWnd;
                        pThis->m_hWnd = NULL;
                        pThis->m_dwState &= ~WINSTATE_DESTROYED;
                        // clean up after window is destroyed
                        pThis->OnFinalMessage(hWnd);
                }
#endif // (_ATL_VER >= 0x0700)
                return lRes;
        }

        // Overriden to call DefWindowProc which uses DefFrameProc
        LRESULT DefWindowProc()
        {
                const MSG* pMsg = 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(m_hWnd, m_hWndMDIClient, uMsg, wParam, lParam);
        }

        BOOL PreTranslateMessage(MSG* pMsg)
        {
                if(CFrameWindowImplBase<TBase, TWinTraits>::PreTranslateMessage(pMsg))
                        return TRUE;
                return ::TranslateMDISysAccel(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 = { 0 };
                ccs.hWindowMenu = hWindowMenu;
                ccs.idFirstChild = nFirstChildID;

                if((GetStyle() & (WS_HSCROLL | WS_VSCROLL)) != 0)
                {
                        // parent MDI frame's scroll styles move to the MDICLIENT
                        dwStyle |= (GetStyle() & (WS_HSCROLL | WS_VSCROLL));

                        // fast way to turn off the scrollbar bits (without a resize)
                        ModifyStyle(WS_HSCROLL | WS_VSCROLL, 0, SWP_NOREDRAW | SWP_FRAMECHANGED);
                }

                // Create MDICLIENT window
                m_hWndClient = ::CreateWindowEx(dwExStyle, _T("MDIClient"), NULL,
                        dwStyle, 0, 0, 1, 1, m_hWnd, (HMENU)LongToHandle(nID),
                        ModuleHelper::GetModuleInstance(), (LPVOID)&ccs);
                if (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(m_hWndClient);

                // set as MDI client window
                m_hWndMDIClient = m_hWndClient;

                // update to proper size
                T* pT = static_cast<T*>(this);
                pT->UpdateLayout();

                return 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
#if (_WIN32_IE >= 0x0400)
                NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize)
#endif // (_WIN32_IE >= 0x0400)
#if (_WIN32_IE >= 0x0500)
                NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed)
#endif // (_WIN32_IE >= 0x0500)
#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*/)
        {
                SetMDIFrameMenu();
                return 0;
        }

#ifndef _ATL_NO_REBAR_SUPPORT
#if (_WIN32_IE >= 0x0400)
        LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                pT->UpdateLayout(FALSE);
                return 0;
        }
#endif // (_WIN32_IE >= 0x0400)

#if (_WIN32_IE >= 0x0500)
        LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
        {
                T* pT = static_cast<T*>(this);
                _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 // (_WIN32_IE >= 0x0500)
#endif // !_ATL_NO_REBAR_SUPPORT
};

#endif // !_WIN32_WCE


///////////////////////////////////////////////////////////////////////////////
// CMDIChildWindowImpl

#ifndef _WIN32_WCE

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(&m_pfnSuperWindowProc);

                if(nMenuID != 0)
                        m_hMenu = ::LoadMenu(ModuleHelper::GetResourceInstance(), MAKEINTRESOURCE(nMenuID));

                dwStyle = T::GetWndStyle(dwStyle);
                dwExStyle = T::GetWndExStyle(dwExStyle);

                dwExStyle |= WS_EX_MDICHILD;   // force this one
                m_pfnSuperWindowProc = ::DefMDIChildProc;
                m_hWndMDIClient = hWndParent;
                ATLASSERT(::IsWindow(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)
                                MDIMaximize(hWnd);
                        wndParent.SetRedraw(TRUE);
                        wndParent.RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
                        ::SetFocus(GetMDIFrame());   // focus will be set back to this window
                }
                else if(hWnd != NULL && ::IsWindowVisible(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];
                szWindowName[0] = 0;
                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)
                        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(m_hWndToolBar));
                if(nResourceID == 0)
                        nResourceID = T::GetWndClassInfo().m_uCommonResourceID;
                m_hWndToolBar = T::CreateSimpleToolBarCtrl(m_hWnd, nResourceID, TRUE, dwStyle, nID);
                return (m_hWndToolBar != NULL);
        }

        BOOL UpdateClientEdge(LPRECT lpRect = NULL)
        {
                // only adjust for active MDI child window
                HWND hWndChild = MDIGetActive();
                if(hWndChild != NULL && hWndChild != m_hWnd)
                        return FALSE;

                // need to adjust the client edge style as max/restore happens
                DWORD dwStyle = ::GetWindowLong(m_hWndMDIClient, GWL_EXSTYLE);
                DWORD dwNewStyle = dwStyle;
                if(hWndChild != NULL && ((GetExStyle() & WS_EX_CLIENTEDGE) == 0) && ((GetStyle() & WS_MAXIMIZE) != 0))
                        dwNewStyle &= ~(WS_EX_CLIENTEDGE);
                else
                        dwNewStyle |= WS_EX_CLIENTEDGE;

                if(dwStyle != dwNewStyle)
                {
                        // SetWindowPos will not move invalid bits
                        ::RedrawWindow(m_hWndMDIClient, NULL, NULL,
                                RDW_INVALIDATE | RDW_ALLCHILDREN);
                        // remove/add WS_EX_CLIENTEDGE to MDI client area
                        ::SetWindowLong(m_hWndMDIClient, GWL_EXSTYLE, dwNewStyle);
                        ::SetWindowPos(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(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
#if (_WIN32_IE >= 0x0400)
                NOTIFY_CODE_HANDLER(RBN_AUTOSIZE, OnReBarAutoSize)
#endif // (_WIN32_IE >= 0x0400)
#if (_WIN32_IE >= 0x0500)
                NOTIFY_CODE_HANDLER(RBN_CHEVRONPUSHED, OnChevronPushed)
#endif // (_WIN32_IE >= 0x0500)
#endif // !_ATL_NO_REBAR_SUPPORT
                CHAIN_MSG_MAP(_baseClass)
        END_MSG_MAP()

        LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
        {
                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) && ((GetStyle() & WS_MAXIMIZE) != 0))
                        {
                                ::AdjustWindowRectEx(&rectClient, GetStyle(), FALSE, 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 = DefWindowProc(uMsg, wParam, lParam);

                // Activate this MDI window if needed
                if(lRes == MA_ACTIVATE || lRes == MA_ACTIVATEANDEAT)
                {
                        if(MDIGetActive() != m_hWnd)
                                MDIActivate(m_hWnd);
                }

                return lRes;
        }

        LRESULT OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
        {
                return ::SendMessage(GetMDIFrame(), uMsg, wParam, lParam);
        }

        LRESULT OnMDIActivate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
        {
                if((HWND)lParam == m_hWnd && m_hMenu != NULL)
                        SetMDIFrameMenu();
                else if((HWND)lParam == NULL)
                        ::SendMessage(GetMDIFrame(), WM_MDISETMENU, 0, 0);

                bHandled = FALSE;
                return 1;
        }

        LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
        {
                if(m_hMenu != NULL)
                {
                        ::DestroyMenu(m_hMenu);
                        m_hMenu = NULL;
                }
                UpdateClientEdge();
                bHandled = FALSE;
                return 1;
        }

#ifndef _ATL_NO_REBAR_SUPPORT
#if (_WIN32_IE >= 0x0400)
        LRESULT OnReBarAutoSize(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
                pT->UpdateLayout(FALSE);
                return 0;
        }
#endif // (_WIN32_IE >= 0x0400)

#if (_WIN32_IE >= 0x0500)
        LRESULT OnChevronPushed(int /*idCtrl*/, LPNMHDR pnmh, BOOL& bHandled)
        {
                T* pT = static_cast<T*>(this);
                _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 // (_WIN32_IE >= 0x0500)
#endif // !_ATL_NO_REBAR_SUPPORT
};

#endif // !_WIN32_WCE


///////////////////////////////////////////////////////////////////////////////
// COwnerDraw - MI class for owner-draw support

template <class T>
class COwnerDraw
{
public:
#if (_ATL_VER < 0x0700)
        BOOL m_bHandledOD;

        BOOL IsMsgHandled() const
        {
                return m_bHandledOD;
        }
        void SetMsgHandled(BOOL bHandled)
        {
                m_bHandledOD = bHandled;
        }
#endif // (_ATL_VER < 0x0700)

// 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 = { 0 };
                        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 _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
        struct _AtlUpdateUIData
        {
                WORD m_wState;
                union
                {
                        void* m_lpData;
                        LPTSTR m_lpstrText;
                };

                bool operator ==(const _AtlUpdateUIData& e) const
                { return (m_wState == e.m_wState && m_lpData == e.m_lpData); }
        };

        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);
                        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;
        }

        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;
                                        }
                                        SecureHelper::strcpy_x(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
#ifndef _WIN32_WCE
        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;
        }
#endif // !_WIN32_WCE

        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)
        {
#ifndef _WIN32_WCE
                if((pUIData->m_wState & UPDUI_CLEARDEFAULT) != 0)
                {
                        ::SetMenuDefaultItem(hMenu, (UINT)-1, 0);
                        pUIData->m_wState &= ~UPDUI_CLEARDEFAULT;
                }
#endif // !_WIN32_WCE

                CMenuItemInfo mii;
                mii.fMask = MIIM_STATE;
                mii.wID = nID;

#ifndef _WIN32_WCE
                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;
#else // CE specific
                // ::SetMenuItemInfo() can't disable or check menu items
                // on Windows CE, so we have to do that directly
                UINT uEnable = MF_BYCOMMAND;
                if((pUIData->m_wState & UPDUI_DISABLED) != 0)
                        uEnable |= MF_GRAYED;
                else
                        uEnable |= MF_ENABLED;
                ::EnableMenuItem(hMenu, nID, uEnable);

                UINT uCheck = MF_BYCOMMAND;
                if((pUIData->m_wState & UPDUI_CHECKED) != 0)
                        uCheck |= MF_CHECKED;
                else
                        uCheck |= MF_UNCHECKED;
                ::CheckMenuItem(hMenu, nID, uCheck);
#endif // _WIN32_WCE

                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
#ifndef _WIN32_WCE
                                mii.fType |= (miiNow.fType & ~(MFT_BITMAP | MFT_SEPARATOR)) | MFT_STRING;
#else // CE specific
                                mii.fType |= (miiNow.fType & ~(MFT_SEPARATOR)) | MFT_STRING;
#endif // _WIN32_WCE
                                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);
                pT;
                const _AtlUpdateUIMap* pMap = pT->GetUpdateUIMap();
                m_pUIMap = pMap;
                ATLASSERT(m_pUIMap != NULL);
                int nCount;
                for(nCount = 1; 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);
                pT;
                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)
                        {
                                BOOL bRet = m_arrUIMap.RemoveAt(i);
                                ATLASSERT(bRet);
                                bRet = m_arrUIData.RemoveAt(i);
                                ATLASSERT(bRet);

                                bRetVal = true;
                                break;
                        }
                }

                return bRetVal;
        }
};


///////////////////////////////////////////////////////////////////////////////
// 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
#define BEGIN_DLGRESIZE_MAP(thisClass) \
        static const _AtlDlgResizeMap* GetDlgResizeMap() \
        { \
                static const _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 _AtlDlgResizeMap
        {
                int m_nCtlID;
                DWORD m_dwResizeFlags;
        };

        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 = { 0 };
                pT->GetClientRect(&rectDlg);
                m_sizeDialog.cx = rectDlg.right;
                m_sizeDialog.cy = rectDlg.bottom;

#ifndef _WIN32_WCE
                // Create gripper if requested
                m_bGripper = false;
                if(bAddGripper)
                {
                        // shouldn't exist already
                        ATLASSERT(!::IsWindow(pT->GetDlgItem(ATL_IDW_STATUS_BAR)));
                        if(!::IsWindow(pT->GetDlgItem(ATL_IDW_STATUS_BAR)))
                        {
                                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 = { 0 };
                                        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);
                                }
                        }
                }
#else // CE specific
                bAddGripper;   // avoid level 4 warning
#endif // _WIN32_WCE

                // Get min track position if requested
                if(bUseMinTrackSize)
                {
                        if((dwStyle & WS_CHILD) != 0)
                        {
                                RECT rect = { 0 };
                                pT->GetClientRect(&rect);
                                m_ptMinTrackSize.x = rect.right - rect.left;
                                m_ptMinTrackSize.y = rect.bottom - rect.top;
                        }
                        else
                        {
                                RECT rect = { 0 };
                                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 = { 0 };
                                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 = { 0, 0, 0, 0 };
                                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)
#ifndef _WIN32_WCE
                MESSAGE_HANDLER(WM_GETMINMAXINFO, OnGetMinMaxInfo)
#endif // _WIN32_WCE
        END_MSG_MAP()

        LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
        {
                T* pT = static_cast<T*>(this);
#ifndef _WIN32_WCE
                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);
                }
#endif // _WIN32_WCE
                if(wParam != SIZE_MINIMIZED)
                {
                        ATLASSERT(::IsWindow(pT->m_hWnd));
                        pT->DlgResize_UpdateLayout(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
                }
                return 0;
        }

#ifndef _WIN32_WCE
        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;
        }
#endif // _WIN32_WCE

// 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 = { 0 };

                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 = { 0 };
                                                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 = { 0 };
                                                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;
        }
};


///////////////////////////////////////////////////////////////////////////////
// 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)
#ifndef _WIN32_WCE
                MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
#endif // !_WIN32_WCE
        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 = { 0 };
                        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__) && !defined(_WIN32_WCE)
  #undef CBRM_GETMENU
  #undef CBRM_TRACKPOPUPMENU
  #undef CBRM_GETCMDBAR
  #undef CBRPOPUPMENU
#endif // !defined(__ATLCTRLW_H__) && !defined(_WIN32_WCE)

}; // namespace WTL

#endif // __ATLFRAME_H__

/* [<][>][^][v][top][bottom][index][help] */