/***********************************************************************/ /* Copyright (C) 2002 Definitive Solutions, Inc. All Rights Reserved. */ /* THIS COMPUTER PROGRAM IS PROPRIETARY AND CONFIDENTIAL TO DEFINITIVE */ /* SOLUTIONS, INC. AND ITS LICENSORS AND CONTAINS TRADE SECRETS OF */ /* DEFINITIVE SOLUTIONS, INC. THAT ARE PROVIDED PURSUANT TO A WRITTEN */ /* AGREEMENT CONTAINING RESTRICTIONS ON USE AND DISCLOSURE. ANY USE, */ /* REPRODUCTION, OR TRANSFER EXCEPT AS PROVIDED IN SUCH AGREEMENT */ /* IS STRICTLY PROHIBITED. */ /***********************************************************************/ #include "stdafx.h" #include "MyMenu.h" #include "MyApp.h" #include "MyLog.h" #include "Generic.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // Statics. const int MyMenu::m_nGap(1); CPINFO MyMenu::m_CPInfo; MyMenu::Win32Type MyMenu::m_Shell(MyMenu::GetShell()); ///////////////////////////////////////////////////////////////////////////// // MyMenuData. // Constructor. MyMenuData::MyMenuData() : m_nXOffset(-1) , m_nMenuIconNormal(-1) , m_uiFlags(0U) , m_uiID(0U) , m_uiSyncFlag(0U) , m_pilistBitmap(NULL) { ::ZeroMemory(m_szMenuText, sizeof m_szMenuText); } // Destructor. /* virtual */ MyMenuData::~MyMenuData() { ASSERT_NULL_OR_POINTER(m_pilistBitmap, CBitmap); delete m_pilistBitmap; m_pilistBitmap = NULL; } // Populate m_szMenuText. void MyMenuData::SetAnsiString(LPCSTR pszAnsiString) { VALIDATE; _ASSERTE(pszAnsiString); HRESULT hr(S_OK); int nBufSize(1 == MyMenu::m_CPInfo.MaxCharSize ? lstrlen(pszAnsiString) : _mbstrlen(pszAnsiString)); // Increment for the terminating NULL. ++nBufSize; wchar_t* pszText = NULL; pszText = new wchar_t[nBufSize]; EC_B(::MultiByteToWideChar( CP_ACP, // code page MB_PRECOMPOSED, // character-type options pszAnsiString, // address of string to map -1, // number of characters in string pszText, // address of wide-character buffer nBufSize * sizeof(wchar_t))); // size of buffer _ASSERTE(wcslen(pszText) == (UINT) lstrlen(pszAnsiString) && "Buffer overrun"); EC_V(wcscpy(m_szMenuText, pszText)); delete [] pszText; pszText = NULL; } // Returns the menu text in ANSI or UNICODE, depending on the MFC-Version we // are using. CString MyMenuData::GetString() const { VALIDATE; HRESULT hr(S_OK); CString sText; #ifdef UNICODE sText = m_szMenuText; #else int iLen((wcslen(m_szMenuText) + 1) * sizeof(wchar_t)); char* pszAnsiString = new char[iLen]; if (*m_szMenuText) { EC_B(::WideCharToMultiByte( CP_ACP, // code page 0, // performance and mapping flags m_szMenuText, // address of wide-character string -1, // number of characters in string pszAnsiString, // address of buffer for new string iLen, // size of buffer NULL, // address of default for unmappable characters NULL)); // address of flag set when default char. used sText = pszAnsiString; } else { sText.Empty(); } delete [] pszAnsiString; pszAnsiString = NULL; #endif // ! UNICODE return sText; } ///////////////////////////////////////////////////////////////////////////// // MyMenu // Constructor. MyMenu::MyMenu() : m_pilistCheckMaps(NULL) , m_bCheckMapsShare(false) , m_uiSelectCheckID(0U) , m_uiUnselectCheckID(0U) , m_nIconX(16) , m_nIconY(16) , m_crBitmapBackground(::GetSysColor(COLOR_MENU)) { // Get code page info. VERIFY(GetCPInfo(CP_ACP, &MyMenu::m_CPInfo)); } // Destructor. /* virtual */ MyMenu::~MyMenu() { // CMenu::DestroyMenu() is NOT virtual. MyMenu::DestroyMenu(); } // CMenu::DestroyMenu() is NOT virtual. This method hides that one, // which is bad. /* NOT VIRTUAL */ void MyMenu::DestroyMenu() { // Destroy Sub menus. for (int nMenu = m_arSubMenus.GetUpperBound(); nMenu >= 0; --nMenu) { delete m_arSubMenus[nMenu]; m_arSubMenus[nMenu] = NULL; } m_arSubMenus.RemoveAll(); // Destroy menu data. for(nMenu = 0; nMenu <= m_arMenuData.GetUpperBound(); ++nMenu) { delete m_arMenuData[nMenu]; m_arMenuData[nMenu] = NULL; } m_arMenuData.RemoveAll(); // Delete the CheckMaps list. if (m_pilistCheckMaps && ! m_bCheckMapsShare) { delete m_pilistCheckMaps; m_pilistCheckMaps = NULL; } // Call base-class implementation last. CMenu::DestroyMenu(); } // bool MyMenu::IsMenu(const CMenu* pSubMenu) const { VALIDATE; ASSERT_VALID(pSubMenu); bool bReturn(false); for (int nMenu = 0; nMenu <= m_arSubMenus.GetUpperBound(); ++nMenu) { if (pSubMenu == m_arSubMenus[nMenu]) { bReturn = true; break; } } return bReturn; } /////////////////////////////////////////////////////////////////////////// // MyMenu message handlers // Called by the framework when a particular item needs to be drawn. We // overide this to draw the menu item in a custom-fashion, including icons // and the 3D rectangle bar. /* virtual */ void MyMenu::DrawItem(LPDRAWITEMSTRUCT lpDIS) { VALIDATE _ASSERTE(lpDIS); HRESULT hr(S_OK); if (lpDIS) { CDC* pDC = CDC::FromHandle(lpDIS->hDC); ASSERT_VALID(pDC); if (pDC) { CRect rcItem; MyMenuData* pMMD = reinterpret_cast (lpDIS->itemData); ASSERT_VALID(pMMD); if (pMMD) { UINT uiItemDataState(pMMD->m_uiFlags); // Handle separator case. if (uiItemDataState & MF_SEPARATOR){ rcItem.CopyRect(&lpDIS->rcItem); rcItem.top += (rcItem.Height() / 2); EC_B(pDC->DrawEdge(&rcItem, EDGE_ETCHED, BF_TOP)); } else { // Handle not-a-separator case. // Set some colors and the font. CPen penBack; if (penBack.CreatePen(PS_SOLID, 0, ::GetSysColor(COLOR_MENU))) { CBrush m_brBackground; if (m_brBackground.CreateSolidBrush(::GetSysColor(COLOR_MENU))) { CBrush brSelect; if (brSelect.CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT))) { NONCLIENTMETRICS nm; ::ZeroMemory(&nm, sizeof nm); nm.cbSize = sizeof nm; if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, nm.cbSize, &nm, /* fWinIni */ 0)) { LOGFONT lf; ::ZeroMemory(&lf, sizeof lf); lf = nm.lfMenuFont; CFont fontMenu; if (fontMenu.CreateFontIndirect(&lf)) { // Draw the colored rectangle portion. rcItem.CopyRect(&lpDIS->rcItem); CRect rc2(rcItem); // Draw the up/down/focused/disabled state. UINT uiState(lpDIS->itemState); CString sText; CFont fontDisp; bool bStandardFlag(false); bool bSelectedFlag(false); bool bDisableFlag(false); bool bCheckFlag(false); int nIconNormal(-1); int nXOffset(-1); CImageList* pilistBitmap = NULL; if (lpDIS->itemData) { MyMenuData* pMenuData = NULL; EC_B(pMenuData = reinterpret_cast (lpDIS->itemData)); ASSERT_VALID(pMenuData); nIconNormal = pMenuData->m_nMenuIconNormal; nXOffset = pMenuData->m_nXOffset; pilistBitmap = pMenuData->m_pilistBitmap; sText = pMenuData->GetString(); if (uiState & ODS_CHECKED && 0 > nIconNormal) { if (uiState & ODS_SELECTED && 0 < m_uiSelectCheckID) { bCheckFlag = true; } else if (0 < m_uiUnselectCheckID) { bCheckFlag = true; } } else if (-1 != nIconNormal) { bStandardFlag = true; if (uiState & ODS_SELECTED && ! (uiState & ODS_GRAYED)) { bSelectedFlag = true; } else if (uiState & ODS_GRAYED) { bDisableFlag = true; } } } else { sText.Empty(); } // Draw the down edges. COLORREF crText(::GetSysColor(COLOR_MENUTEXT)); COLORREF crBack(::GetSysColor(COLOR_MENU)); if (uiState & ODS_SELECTED) { CPen* pOldPen = pDC->SelectObject(&penBack); ASSERT_VALID(pOldPen); if (pOldPen) { // You need only Text highlight and thats what you get. if (bCheckFlag || bStandardFlag || bSelectedFlag || bDisableFlag || uiState & ODS_CHECKED) { rc2.SetRect(rcItem.left + m_nIconX + 4 + MyMenu::m_nGap, rcItem.top, rcItem.right, rcItem.bottom); } pDC->FillRect(rc2, &brSelect); if (pDC->SelectObject(pOldPen)) { if ((HFONT) fontDisp) { if (fontDisp.DeleteObject()) { ; } else { DOMYLOGST(API_FAILURE), "CFont::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } if (fontDisp.CreateFontIndirect(&lf)) { crText = ::GetSysColor(COLOR_HIGHLIGHTTEXT); } else { DOMYLOGST(API_FAILURE), "CFont::CreateFontIndirect", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CDC::SelectObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "pOldPen", ::GetLastError(), Generic::GetSysErrorString()); } } else { CPen* ppenOld = pDC->SelectObject(&penBack); ASSERT_VALID(ppenOld); if (ppenOld) { pDC->FillRect(rcItem, &m_brBackground); if (pDC->SelectObject(ppenOld)) { // Draw the up edges. pDC->Draw3dRect(rcItem, /* tl */ crBack, /* br */ crBack); if ((HFONT) fontDisp) { if (fontDisp.DeleteObject()) { ; } else { DOMYLOGST(API_FAILURE), "CFont::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } } else { DOMYLOGST(API_FAILURE), "CDC::SelectObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "::MultiByteToWideChar", ::GetLastError(), Generic::GetSysErrorString()); } // Normal. if (fontDisp.CreateFontIndirect(&lf)) { ; } else { DOMYLOGST(API_FAILURE), "CFont::CreateFontIndirect", ::GetLastError(), Generic::GetSysErrorString()); } } // Draw the text if there is any. We have to paint the text only // if the image is nonexistant. int nDy((rcItem.Height() - 4 - m_nIconY) / 2); nDy = max(nDy, 0); if (bCheckFlag || bStandardFlag || bSelectedFlag || bDisableFlag) { rc2.SetRect(rcItem.left + 1, rcItem.top + 1 + nDy, rcItem.left + m_nIconX + 3, rcItem.top + m_nIconY + 3 + nDy); pDC->Draw3dRect(rc2, /* lt */ crBack, /* rb */ crBack); if (bCheckFlag && m_pilistCheckMaps) { pDC->FillRect(rc2, &m_brBackground); rc2.SetRect(rcItem.left, rcItem.top + nDy, rcItem.left + m_nIconX + 4, rcItem.top + m_nIconY + 4 + nDy); pDC->Draw3dRect(rc2, /* lt */ crBack, /* rb */ crBack); CPoint ptImage(rcItem.left + 2, rcItem.top + 2 + nDy); if (uiState & ODS_SELECTED) { if (m_pilistCheckMaps->Draw(pDC, /* nImage */ 1, ptImage, ILD_TRANSPARENT)) { ; } else { DOMYLOGST(API_FAILURE), "CImageList::Draw", ::GetLastError(), Generic::GetSysErrorString()); } } else { if (m_pilistCheckMaps->Draw(pDC, /* nImage */ 0, ptImage, ILD_TRANSPARENT)) { ; } else { DOMYLOGST(API_FAILURE), "CImageList::Draw", ::GetLastError(), Generic::GetSysErrorString()); } } } else if (bDisableFlag) { if (! bSelectedFlag) { HBITMAP hbmp(LoadSysColorBitmap(nIconNormal)); if (hbmp) { CBitmap bmpStandard; if (bmpStandard.Attach(hbmp)) { rc2.SetRect(rcItem.left, rcItem.top + nDy, rcItem.left + m_nIconX + 4, rcItem.top + m_nIconY + 4 + nDy); pDC->Draw3dRect(rc2, /* lt */ crBack, /* rb */ crBack); DitherBlt(pDC, rcItem.left + 2, rcItem.top + 2 + nDy, m_nIconX, m_nIconY, bmpStandard, nXOffset * m_nIconX, 0); if (bmpStandard.DeleteObject()) { ; } else { DOMYLOGST(API_FAILURE), "CBitmap::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CBitmap::Attach", ::GetLastError(), Generic::GetSysErrorString()); } } else { _ASSERTE(! "Can't tell if this is an error or a legal condition."); } } } else if (bSelectedFlag) { pDC->FillRect(rc2, &m_brBackground); rc2.SetRect(rcItem.left, rcItem.top + nDy, rcItem.left + m_nIconX + 4, rcItem.top + m_nIconY + 4 + nDy); if (MyMenu::Win95 == MyMenu::m_Shell || MyMenu::WinNT4orHigher == MyMenu::m_Shell) { if (uiState & ODS_CHECKED) { pDC->Draw3dRect(rc2, ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DHILIGHT)); } else { pDC->Draw3dRect(rc2, ::GetSysColor(COLOR_3DHILIGHT), ::GetSysColor(COLOR_3DSHADOW)); } } CPoint ptImage(rcItem.left + 2, rcItem.top + 2 + nDy); if (pilistBitmap) { if (pilistBitmap->Draw(pDC, nXOffset, ptImage, ILD_TRANSPARENT)) { ; } else { DOMYLOGST(API_FAILURE), "CImageList::Draw", ::GetLastError(), Generic::GetSysErrorString()); } } } else { if (uiState & ODS_CHECKED) { CBrush br; COLORREF cr(::GetSysColor(COLOR_3DLIGHT)); if (br.CreateSolidBrush(cr)) { pDC->FillRect(rc2, &br); if (br.DeleteObject()) { rc2.SetRect(rcItem.left, rcItem.top + nDy, rcItem.left + m_nIconX + 4, rcItem.top + m_nIconY + 4 + nDy); if (MyMenu::Win95 == MyMenu::m_Shell || MyMenu::WinNT4orHigher == MyMenu::m_Shell) { pDC->Draw3dRect(rc2, ::GetSysColor(COLOR_3DSHADOW), ::GetSysColor(COLOR_3DHILIGHT)); } } else { DOMYLOGST(API_FAILURE), "CBrush::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CBrush::CreateSolidBrush", ::GetLastError(), Generic::GetSysErrorString()); } } else { pDC->FillRect(rc2, &m_brBackground); rc2.SetRect(rcItem.left, rcItem.top + nDy, rcItem.left + m_nIconX + 4, rcItem.top + m_nIconY + 4 + nDy); pDC->Draw3dRect(rc2, /* lt */ crBack, /* rb */ crBack); } CPoint ptImage(rcItem.left + 2, rcItem.top + 2 + nDy); if (pilistBitmap) { if (pilistBitmap->Draw(pDC, nXOffset, ptImage, ILD_TRANSPARENT)) { ; } else { DOMYLOGST(API_FAILURE), "CImageList::Draw", ::GetLastError(), Generic::GetSysErrorString()); } } } } if (0 > nIconNormal && uiState & ODS_CHECKED && ! bCheckFlag) { rc2.SetRect(rcItem.left + 1, rcItem.top + 2 + nDy, rcItem.left + m_nIconX + 1, rcItem.top + m_nIconY + 2 + nDy); MENUITEMINFO mio; ::ZeroMemory(&mio, sizeof mio); mio.cbSize = sizeof mio; mio.fMask = MIIM_CHECKMARKS | MIIM_STATE; if (GetMenuItemInfo(lpDIS->itemID, &mio)) { if (uiState & ODS_CHECKED || mio.hbmpUnchecked) { Draw3DCheckmark(pDC, rc2, uiState & ODS_CHECKED, uiState & ODS_CHECKED ? mio.hbmpChecked : mio.hbmpUnchecked); } } else { DOMYLOGST(API_FAILURE), "CMenu::GetMenuItemInfo", ::GetLastError(), Generic::GetSysErrorString()); } } // This is needed always so that we can have the space for check marks. rcItem.left += m_nIconX + 8 + MyMenu::m_nGap; if (! sText.IsEmpty()) { // Find tabs. CRect rcT(rcItem.left, rcItem.top - 1, rcItem.right, rcItem.bottom - 1); CString sLeft; CString sRight; int nTabLocation(sText.ReverseFind(_T('\t'))); if (-1 != nTabLocation) { sRight = sText.Mid(nTabLocation + 1); sLeft = sText.Left(sText.Find(_T('\t'))); rcT.right -= m_nIconX; } else { sLeft = sText; } // Draw the text in the correct color. int nOldBkMode(pDC->SetBkMode(TRANSPARENT)); UINT uiFormat(DT_LEFT | DT_SINGLELINE | DT_VCENTER); UINT uiFormatR(DT_RIGHT | DT_SINGLELINE | DT_VCENTER); if(! (lpDIS->itemState & ODS_GRAYED)) { pDC->SetTextColor(crText); pDC->DrawText(sLeft, rcT, uiFormat); if (-1 != nTabLocation) { pDC->DrawText(sRight, rcT, uiFormatR); } } else{ // Draw the disabled text. if (! (uiState & ODS_SELECTED)) { CRect rcOffset(rcT); rcOffset.OffsetRect(1, 1); pDC->SetTextColor(::GetSysColor(COLOR_BTNHILIGHT)); pDC->DrawText(sLeft, &rcOffset, uiFormat); if (-1 != nTabLocation) { pDC->DrawText(sRight, &rcOffset, uiFormatR); } pDC->SetTextColor(::GetSysColor(COLOR_GRAYTEXT)); pDC->DrawText(sLeft, rcT, uiFormat); if (-1 != nTabLocation) { pDC->DrawText(sRight, rcT, uiFormatR); } } else{ // And the standard Grey text. pDC->SetTextColor(crBack); pDC->DrawText(sLeft, rcT, uiFormat); if (-1 != nTabLocation) { pDC->DrawText(sRight, rcT, uiFormatR); } } } pDC->SetBkMode(nOldBkMode); CFont* pFont = pDC->SelectObject(&fontDisp); ASSERT_VALID(pFont); if (pFont) { if (pDC->SelectObject(pFont)) { ; } else { DOMYLOGST(API_FAILURE), "CDC::SelectObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "pFont", ::GetLastError(), Generic::GetSysErrorString()); } } if (penBack.DeleteObject()) { if (m_brBackground.DeleteObject()) { if (fontMenu.DeleteObject()) { if (brSelect.DeleteObject()) { if (fontDisp.DeleteObject ()) { ; } else { DOMYLOGST(API_FAILURE), "CFont::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CBrush::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CFont::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CBrush::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CPen::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CFont::CreateFontIndirect", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "::SystemParametersInfo", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CBrush::CreateSolidBrush", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CBrush::CreateSolidBrush", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CPen::CreatePen", ::GetLastError(), Generic::GetSysErrorString()); } } } else { DOMYLOGST(API_FAILURE), "pMMD", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CDC::FromHandle", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "lpDIS", ::GetLastError(), Generic::GetSysErrorString()); } } // Called by the framework when it wants to know what the width and height // of our item will be. To accomplish this we provide the width of the // icon plus the width of the menu text, and then the height of the icon. /* virtual */ void MyMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMIS) { VALIDATE; ASSERT_POINTER(lpMIS, MEASUREITEMSTRUCT); if (lpMIS) { MyMenuData* pMMD = reinterpret_cast (lpMIS->itemData); ASSERT_VALID(pMMD); if (pMMD) { UINT uiState(pMMD->m_uiFlags); if (uiState & MF_SEPARATOR) { lpMIS->itemWidth = 0; lpMIS->itemHeight = (GetSystemMetrics(SM_CYMENU) / 2); } else { NONCLIENTMETRICS nm; ::ZeroMemory(&nm, sizeof nm); nm.cbSize = sizeof nm; if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, nm.cbSize, &nm, /* fWinIni */ 0)) { LOGFONT lf; ::ZeroMemory(&lf, sizeof lf); lf = nm.lfMenuFont; CFont fontMenu; fontMenu.CreateFontIndirect (&lf); // Obtain the width of the text. _ASSERTE(AfxGetMainWnd()); CFont* pFont = NULL; CDC* pDC = AfxGetMainWnd()->GetDC(); ASSERT_VALID(pDC); if (pDC) { if (MyMenu::Win95 == MyMenu::m_Shell || MyMenu::WinNT4orHigher == MyMenu::m_Shell) { if (NULL != (pFont = pDC->SelectObject(&fontMenu))) { ; } else { DOMYLOGST(API_FAILURE), "CDC::SelectObject", ::GetLastError(), Generic::GetSysErrorString()); } } // Get pointer to text. CSize sizText; wchar_t* pstrText = pMMD->m_szMenuText; _ASSERTE(pstrText); if (pstrText) { if (MyMenu::Win32s != MyMenu::m_Shell) { // Should also work on 95. if (::GetTextExtentPoint32W(pDC->m_hDC, pstrText, wcslen(pstrText), &sizText)) { ; } else { DOMYLOGST(API_FAILURE), "::GetTextExtentPoint32W", ::GetLastError(), Generic::GetSysErrorString()); } } // Can't be UNICODE for Win32s. #ifndef UNICODE else { // It's Win32s. CRect rc; sizText.cy = DrawText(pDC->m_hDC, (LPCTSTR) pstrText, wcslen(pstrText), &rc, DT_SINGLELINE | DT_LEFT | DT_VCENTER | DT_CALCRECT); // +3 makes at least three pixels space to the menu border sizText.cx = rc.right - rc.left + 3; sizText.cx += 3*(sizText.cx / wcslen(pstrText)); } #endif // CSize sizT(sizText); if (MyMenu::Win95 == MyMenu::m_Shell || MyMenu::WinNT4orHigher == MyMenu::m_Shell) { if (pDC->SelectObject(pFont)) { ; } else { DOMYLOGST(API_FAILURE), "CDC::SelectObject", ::GetLastError(), Generic::GetSysErrorString()); } } if (AfxGetMainWnd()->ReleaseDC(pDC)) { ; } else { DOMYLOGST(API_FAILURE), "CWnd::ReleaseDC", ::GetLastError(), Generic::GetSysErrorString()); } // Set width and height. lpMIS->itemWidth = m_nIconX + sizT.cx + m_nIconX + MyMenu::m_nGap; int nMenuHeight(::GetSystemMetrics(SM_CYMENU)); lpMIS->itemHeight = (m_nIconY + 4 < nMenuHeight) ? nMenuHeight: m_nIconY + 4; if (fontMenu.DeleteObject()) { ; } else { DOMYLOGST(API_FAILURE), "CFont::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "pDC", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "pDC", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "::SystemParametersInfo", ::GetLastError(), Generic::GetSysErrorString()); } } } else { DOMYLOGST(API_FAILURE), "pMMD", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "lpMIS", ::GetLastError(), Generic::GetSysErrorString()); } } // BOOL MyMenu::AppendMyMenuA(LPCSTR pszText, UINT uiFlags, UINT uiID, int nIconNormal) { VALIDATE; _ASSERTE(pszText); //_ASSERTE(0U < uiID); Can be zero. _ASSERTE(-1 <= nIconNormal); BOOL bReturn(false); if (pszText) { int nBufSize((1 == MyMenu::m_CPInfo.MaxCharSize) ? lstrlen(pszText): _mbstrlen(pszText)); // Increment for the terminating NULL. ++nBufSize; wchar_t* pszNewText = NULL; pszNewText = new wchar_t[nBufSize]; if (::MultiByteToWideChar( CP_ACP, // code page MB_PRECOMPOSED, // character-type options pszText, // address of string to map -1, // number of characters in string pszNewText, // address of wide-character buffer nBufSize * sizeof(wchar_t))) { // size of buffer bReturn = AppendMyMenuW(pszNewText, uiFlags, uiID, nIconNormal); } else { DOMYLOGST(API_FAILURE), "::MultiByteToWideChar", ::GetLastError(), Generic::GetSysErrorString()); } delete [] pszNewText; pszNewText = NULL; } else { DOMYLOGST(API_FAILURE), "pszText", ::GetLastError(), Generic::GetSysErrorString()); } return bReturn; } // BOOL MyMenu::AppendMyMenuW(wchar_t* pszText, UINT uiFlags, UINT uiID, int nIconNormal) { VALIDATE; _ASSERTE(pszText); //_ASSERTE(0U < uiID); Can be zero. _ASSERTE(-1 <= nIconNormal); // Add the MF_OWNERDRAW flag if not specified: if (! uiID) { uiFlags = MF_SEPARATOR | MF_OWNERDRAW; } else if (! (uiFlags & MF_OWNERDRAW)) { uiFlags |= MF_OWNERDRAW; } // MyMenuData* pMMD = new MyMenuData; VERIFY(0 <= m_arMenuData.Add(pMMD)); _ASSERTE(wcslen(pszText) < (UINT) sizeof pMMD->m_szMenuText && "Buffer overrun"); wcscpy(pMMD->m_szMenuText, pszText); int nBufSize(wcslen(pszText) + 1); char* pszTemp = new char[nBufSize * sizeof(wchar_t)]; if (pszText && *pszText) { if (::WideCharToMultiByte( CP_ACP, // code page 0, // performance and mapping flags pszText, // address of wide-character string -1, // number of characters in string pszTemp, // address of buffer for new string nBufSize * sizeof(wchar_t), // size of buffer NULL, // address of default for unmappable characters NULL)) { // address of flag set when default char. used ; } else { DOMYLOGST(API_FAILURE), "::WideCharToMultiByte", ::GetLastError(), Generic::GetSysErrorString()); } } else { *pszTemp = 0; } delete [] pszTemp; pszTemp = NULL; // pMMD->m_nMenuIconNormal = nIconNormal; pMMD->m_nXOffset = -1; if (0 <= nIconNormal) { pMMD->m_nXOffset = 0; VERIFY(LoadFromToolBar(uiID, nIconNormal, pMMD->m_nXOffset)); if (pMMD->m_pilistBitmap) { if (pMMD->m_pilistBitmap->DeleteImageList()) { ; } else { DOMYLOGST(API_FAILURE), "CImageList::DeleteImageList", ::GetLastError(), Generic::GetSysErrorString()); } } else { pMMD->m_pilistBitmap = new CImageList; } if (pMMD->m_pilistBitmap) { if (pMMD->m_pilistBitmap->Create(m_nIconX, m_nIconY, ILC_COLORDDB | ILC_MASK, 1, 1)) { VERIFY(AddBitmapToImageList(pMMD->m_pilistBitmap, nIconNormal)); } else { DOMYLOGST(API_FAILURE), "CImageList::Create", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "pMMD->m_pilistBitmap", ::GetLastError(), Generic::GetSysErrorString()); } } pMMD->m_uiFlags = uiFlags; pMMD->m_uiID = uiID; return CMenu::AppendMenu(uiFlags, uiID, (LPCTSTR) pMMD); } // BOOL MyMenu::ModifyMyMenuA(const char* pszText, UINT uiID /* = 0U */ , int nIconNormal /* = -1 */ ) { VALIDATE; // _ASSERTE(pszText); Can be NULL. BOOL bReturn(false); wchar_t* pszNewText = NULL; if (pszText) { int nBufSize((1 == MyMenu::m_CPInfo.MaxCharSize) ? lstrlen(pszText): _mbstrlen(pszText)); //Increment for the terminating NULL. ++nBufSize; pszNewText = new wchar_t[nBufSize]; if (::MultiByteToWideChar( CP_ACP, // code page MB_PRECOMPOSED, // character-type options pszText, // address of string to map -1, // number of characters in string pszNewText, // address of wide-character buffer nBufSize * sizeof(wchar_t))) { // size of buffer ; } else { DOMYLOGST(API_FAILURE), "::MultiByteToWideChar", ::GetLastError(), Generic::GetSysErrorString()); } } else { // This is an expected condition, not an error. } bReturn = ModifyMyMenuW(pszNewText, uiID, nIconNormal); delete [] pszNewText; pszNewText = NULL; return bReturn; } // BOOL MyMenu::ModifyMyMenuW(wchar_t* pszText, UINT uiID /* = 0U */ , int nIconNormal /* = -1 */ ) { VALIDATE; //_ASSERTE(pszText); Can be NULL. BOOL bReturn(false); // Find the old MyMenuData structure. int nLoc(0); MyMenu* pSubMenu = FindMenuOption(uiID, nLoc); ASSERT_NULL_OR_POINTER(pSubMenu, MyMenu); MyMenuData* pMMD = NULL; if (pSubMenu && 0 <= nLoc) { pMMD = pSubMenu->m_arMenuData[nLoc]; } else { // Create a new MyMenuData structure. pMMD = new MyMenuData; VERIFY(0 <= m_arMenuData.Add(pMMD)); } // Now update the menu item. if (pMMD) { if (pszText) { wcscpy(pMMD->m_szMenuText, pszText); } pMMD->m_nMenuIconNormal = nIconNormal; pMMD->m_nXOffset = -1; if (0 <= nIconNormal) { pMMD->m_nXOffset = 0; // This call fails expectedly for separators. LoadFromToolBar(uiID, nIconNormal, pMMD->m_nXOffset); if (pMMD->m_pilistBitmap) { if (pMMD->m_pilistBitmap->DeleteImageList()) { ; } else { DOMYLOGST(API_FAILURE), "CImageList::DeleteImageList", ::GetLastError(), Generic::GetSysErrorString()); } } else { pMMD->m_pilistBitmap = new CImageList; } if (pMMD->m_pilistBitmap) { if (pMMD->m_pilistBitmap->Create(m_nIconX, m_nIconY, ILC_COLORDDB | ILC_MASK, 1, 1)) { VERIFY(AddBitmapToImageList(pMMD->m_pilistBitmap,nIconNormal)); } else { DOMYLOGST(API_FAILURE), "CImageList::Create", ::GetLastError(), Generic::GetSysErrorString()); } } } pMMD->m_uiFlags = MF_BYCOMMAND | MF_OWNERDRAW; pMMD->m_uiID = uiID; bReturn = CMenu::ModifyMenu(uiID, pMMD->m_uiFlags, uiID,(LPCTSTR) pMMD); } return bReturn; } // BOOL MyMenu::ModifyMyMenuA(const char* pszText, const char* pszOptionText, int nIconNormal) { VALIDATE; // _ASSERTE(pszText); Can be NULL. _ASSERTE(pszOptionText); _ASSERTE(0 < nIconNormal); BOOL bReturn(false); int nBufSize(0); wchar_t* pszNewText = NULL; wchar_t *pszNewOptionText = NULL; if (pszText) { nBufSize = (1 == MyMenu::m_CPInfo.MaxCharSize) ? lstrlen(pszText): _mbstrlen(pszText); // Increment for the terminating NULL ++nBufSize; pszNewText = new wchar_t[nBufSize]; if (::MultiByteToWideChar( CP_ACP, // code page MB_PRECOMPOSED, // character-type options pszText, // address of string to map -1, // number of characters in string pszNewText, // address of wide-character buffer nBufSize * sizeof(wchar_t))) { // size of buffer ; } else { DOMYLOGST(API_FAILURE), "::MultiByteToWideChar", ::GetLastError(), Generic::GetSysErrorString()); } } if (pszOptionText) { nBufSize = (1 == MyMenu::m_CPInfo.MaxCharSize) ? lstrlen(pszOptionText): _mbstrlen(pszOptionText); // Increment for the terminating NULL. ++nBufSize; pszNewOptionText = new wchar_t[nBufSize]; if (::MultiByteToWideChar( CP_ACP, // code page MB_PRECOMPOSED, // character-type options pszOptionText, // address of string to map -1, // number of characters in string pszNewOptionText, // address of wide-character buffer nBufSize * sizeof(wchar_t))) { // size of buffer ; } else { DOMYLOGST(API_FAILURE), "::MultiByteToWideChar", ::GetLastError(), Generic::GetSysErrorString()); } } if (pszNewText && pszNewOptionText) { bReturn = ModifyMyMenuW(pszNewText, pszNewOptionText, nIconNormal); } delete [] pszNewText; pszNewText = NULL; delete [] pszNewOptionText; pszNewOptionText = NULL; return bReturn; } // BOOL MyMenu::ModifyMyMenuW(wchar_t* pszText, wchar_t* pszOptionText, int nIconNormal) { VALIDATE; //_ASSERTE(pszText); Can be NULL. _ASSERTE(pszOptionText); _ASSERTE(nIconNormal); BOOL bReturn(false); // Find the old MyMenuData structure. MyMenuData* pMMD = FindMenuOption(pszOptionText); ASSERT_VALID(pMMD); if (pMMD){ if (pszText) { wcscpy(pMMD->m_szMenuText, pszText); } pMMD->m_nMenuIconNormal = nIconNormal; pMMD->m_nXOffset = -1; if (0 <= nIconNormal) { pMMD->m_nXOffset = 0; if (pMMD->m_pilistBitmap) { if (pMMD->m_pilistBitmap->DeleteImageList()) { ; } else { DOMYLOGST(API_FAILURE), "CImageList::DeleteImageList", ::GetLastError(), Generic::GetSysErrorString()); } } else { pMMD->m_pilistBitmap = new CImageList; } if (pMMD->m_pilistBitmap) { if (pMMD->m_pilistBitmap->Create(m_nIconX, m_nIconY, ILC_COLORDDB | ILC_MASK, 1 ,1)) { VERIFY(AddBitmapToImageList(pMMD->m_pilistBitmap, nIconNormal)); } else { DOMYLOGST(API_FAILURE), "CImageList::Create", ::GetLastError(), Generic::GetSysErrorString()); } } } bReturn = true; } return bReturn; } // MyMenuData* MyMenu::NewMyMenu(UINT uiPos, UINT uiFlags, UINT uiID, const CString& sText) { VALIDATE; //_ASSERTE(0U <= uiPos); Can be zero. //_ASSERTE(0U < uiID); Can be zero. //_ASSERTE(! sText.IsEmpty()); Can be empty. MyMenuData* pMMD = new MyMenuData; pMMD->m_nMenuIconNormal = -1; pMMD->m_nXOffset = -1; #ifdef UNICODE wcscpy(pMMD->m_szMenuText, sText); #else pMMD->SetAnsiString(sText); #endif pMMD->m_uiFlags = uiFlags; pMMD->m_uiID = uiID; if (uiFlags & MF_OWNERDRAW) { _ASSERTE(! (uiFlags & MF_STRING) && "Why is this bad??"); if (ModifyMenu(uiPos, uiFlags, uiID, (LPCTSTR) pMMD)) { ; } else { DOMYLOGST(API_FAILURE), "CMenu::ModifyMenu", ::GetLastError(), Generic::GetSysErrorString()); } } else if (uiFlags & MF_STRING) { _ASSERTE(! (uiFlags & MF_OWNERDRAW) && "Why is this bad??"); if (ModifyMenu(uiPos, uiFlags, uiID, pMMD->GetString())) { ; } else { DOMYLOGST(API_FAILURE), "CMenu::ModifyMenu", ::GetLastError(), Generic::GetSysErrorString()); } } else { _ASSERTE((uiFlags & MF_SEPARATOR) && "Why is this bad??"); if (ModifyMenu(uiPos, uiFlags, uiID)) { ; } else { DOMYLOGST(API_FAILURE), "CMenu::ModifyMenu", ::GetLastError(), Generic::GetSysErrorString()); } } return pMMD; } // BOOL MyMenu::LoadToolbars(const UINT* puiIdArray, int nCount) { VALIDATE; _ASSERTE(puiIdArray); _ASSERTE(0 < nCount); BOOL bReturn(true); if (puiIdArray) { for (int i = 0; i < nCount; ++i) { if (! LoadToolbar(puiIdArray[i])) { bReturn = false; break; } } } else { DOMYLOGST(API_FAILURE), "puiIdArray", ::GetLastError(), Generic::GetSysErrorString()); } return bReturn; } // BOOL MyMenu::LoadToolbar(UINT uiToolBar) { VALIDATE; _ASSERTE(0U < uiToolBar); BOOL bReturn(false); CToolBar bar; if (bar.Create(AfxGetMainWnd())) { if (bar.LoadToolBar(uiToolBar)) { for (int nIndex = 0; nIndex < bar.GetCount(); ++nIndex) { UINT uiID(bar.GetItemID(nIndex)); if (uiID && GetMenuState(uiID, MF_BYCOMMAND)!= 0xFFFFFFFF) { VERIFY(ModifyMyMenu(NULL, uiID, uiToolBar)); } } bReturn = true; } else { DOMYLOGST(API_FAILURE), "CToolBar::LoadToolBar", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CToolBar::Create", ::GetLastError(), Generic::GetSysErrorString()); } return bReturn; } // bool MyMenu::LoadFromToolBar(UINT uiID, UINT uiToolBar, int& nXOffset) { VALIDATE; _ASSERTE(0U < uiID); _ASSERTE(0U < uiToolBar); _ASSERTE(0 <= nXOffset); bool bReturn(false); CToolBar bar; if (bar.Create(AfxGetMainWnd())) { // Note that one of the reasons it couldn't find a bitmap // might be that this is a separator. if (bar.LoadToolBar(uiToolBar)) { int nOffset(bar.CommandToIndex(uiID)); if (nOffset >= 0) { int nXset(0); UINT uiStyle(0U); bar.GetButtonInfo(nOffset, uiID, uiStyle, nXset); if (0 < nXset) { nXOffset = nXset; } bReturn = true; } } else { DOMYLOGST(API_FAILURE), "CToolBar::LoadToolBar", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CToolBar::Create", ::GetLastError(), Generic::GetSysErrorString()); } return bReturn; } // MyMenuData* MyMenu::FindMenuOption(wchar_t* pszText) const { VALIDATE; _ASSERTE(pszText); for (UINT uiMenuItem = 0U; uiMenuItem < GetMenuItemCount(); ++uiMenuItem){ MyMenu* pSubMenu = dynamic_cast (GetSubMenu(uiMenuItem)); ASSERT_NULL_OR_POINTER(pSubMenu, MyMenu); // If this menu item is a submenu, do recursion. if (pSubMenu) { MyMenuData* pMenuData = pSubMenu->FindMenuOption(pszText); ASSERT_NULL_OR_POINTER(pMenuData, MyMenuData); if (pMenuData) { return pMenuData; } } else { if (pszText) { // Not a submenu - handle normally. for (int nIndex = 0 ; nIndex <= m_arMenuData.GetUpperBound(); ++nIndex) { if (! wcscmp(pszText, m_arMenuData[nIndex]->m_szMenuText)) { return m_arMenuData[nIndex]; } } } else { DOMYLOGST(API_FAILURE), "pszText", ::GetLastError(), Generic::GetSysErrorString()); } } } return NULL; } // Note thee recursion here, and multiple returns. MyMenu* MyMenu::FindMenuOption(UINT uiID, int& nLoc) { VALIDATE; _ASSERTE(0U < uiID); // Initialize it. nLoc = -1; for (UINT uiMenuItem = 0U; uiMenuItem < GetMenuItemCount(); ++uiMenuItem){ MyMenu* pSubMenu = dynamic_cast (GetSubMenu(uiMenuItem)); ASSERT_NULL_OR_POINTER(pSubMenu, MyMenu); if (pSubMenu) { MyMenu* pGoodMenu = pSubMenu->FindMenuOption(uiID, nLoc); ASSERT_NULL_OR_POINTER(pGoodMenu, MyMenu); if (pGoodMenu) { return pGoodMenu; } } else if (GetMenuItemID(uiMenuItem) == uiID) { nLoc = uiMenuItem; return this; } } // Note there are multiple returns from this function! return NULL; } // These two "hide" their non-virtual calls in CMenu. This means you // can't use this class to call LoadMenu polymorphically. Not a // huge loss. BOOL MyMenu::LoadMenu(UINT uiResourceID) { VALIDATE; _ASSERTE(0U < uiResourceID); return MyMenu::LoadMenu(MAKEINTRESOURCE(uiResourceID)); } // These two "hide" their non-virtual calls in CMenu. // This means you can't use this class to call LoadMenu polymorphically. BOOL MyMenu::LoadMenu(LPCTSTR pszResourceName) { VALIDATE; _ASSERTE(pszResourceName); BOOL bReturn(true); // Find the Menu Resource. HINSTANCE hInstMenu(AfxFindResourceHandle(pszResourceName, RT_MENU)); if (hInstMenu) { HRSRC hRsrcMenu(::FindResource(hInstMenu, pszResourceName, RT_MENU)); if (hRsrcMenu) { // Load the Menu Resource. HGLOBAL hGlobal(::LoadResource(hInstMenu, hRsrcMenu)); if (hGlobal) { // Attempt to create us as a menu. if (CMenu::CreateMenu()) { // Get Item Template Header, and calculate offset of MENUITEMTEMPLATEs. MENUITEMTEMPLATEHEADER* pMITH = reinterpret_cast (::LockResource(hGlobal)); ASSERT_POINTER(pMITH, MENUITEMTEMPLATEHEADER); if (pMITH) { BYTE* pbyMET = reinterpret_cast (pMITH) + sizeof(MENUITEMTEMPLATEHEADER) + pMITH->offset; ASSERT_POINTER(pbyMET, MENUITEMTEMPLATE); WORD wFlags(0); UINT uiFlags(0); WORD wID(0); wchar_t* pszCaption = NULL; int nLen(0); CTypedPtrArray m_Stack; // Popup menu stack CArray m_StackEnd; // Popup menu stack m_Stack.Add(this); m_StackEnd.Add(false); do { // Obtain Flags and (if necessary), the ID. memcpy(&wFlags, pbyMET, sizeof wFlags); pbyMET += sizeof wFlags; if (! (wFlags & MF_POPUP)) { memcpy(&wID, pbyMET, sizeof wID); pbyMET += sizeof wID; } else { wID = 0; } // Remove MF_END from the flags that will // be passed to the Append(My)Menu functions. uiFlags = wFlags; if (uiFlags & MF_END) { uiFlags -= MF_END; } // Obtain Caption (and length). nLen = 0; pszCaption = new wchar_t[wcslen((wchar_t*) pbyMET) + 1]; wcscpy(pszCaption, reinterpret_cast (pbyMET)); pbyMET = &pbyMET[(wcslen((wchar_t*)pbyMET) + 1) * sizeof(wchar_t)]; // Handle popup menus first. if (wFlags & MF_POPUP) { if (wFlags & MF_END) { m_StackEnd.SetAt(m_Stack.GetUpperBound(),true); } MyMenu* pSubMenu = new MyMenu; pSubMenu->m_uiUnselectCheckID = m_uiUnselectCheckID; pSubMenu->m_uiSelectCheckID = m_uiSelectCheckID; pSubMenu->m_pilistCheckMaps = m_pilistCheckMaps; pSubMenu->m_bCheckMapsShare = true; pSubMenu->CreatePopupMenu(); // Append it to the top of the stack. m_Stack[m_Stack.GetUpperBound()]-> AppendMyMenuW(pszCaption, uiFlags, reinterpret_cast (pSubMenu->m_hMenu)); m_Stack.Add(pSubMenu); m_StackEnd.Add(false); m_arSubMenus.Add(pSubMenu); } else { m_Stack[m_Stack.GetUpperBound()]->AppendMyMenuW(pszCaption, uiFlags, wID); if (wFlags & MF_END) { m_StackEnd.SetAt(m_Stack.GetUpperBound(),true); } int j(m_Stack.GetUpperBound()); while (j >= 0 && m_StackEnd.GetAt(j)) { m_Stack[m_Stack.GetUpperBound()]->InsertSpaces(); m_Stack.RemoveAt(j); m_StackEnd.RemoveAt(j); --j; } } delete [] pszCaption; pszCaption = NULL; } while (-1 != m_Stack.GetUpperBound()); // for (int i = 0; i < static_cast (GetMenuItemCount()); ++i) { CString s(m_arMenuData[i]->GetString()); if (GetSubMenu(i)) { m_arMenuData[i]->m_uiFlags = MF_POPUP | MF_BYPOSITION; if (ModifyMenu(i, MF_POPUP | MF_BYPOSITION, (UINT) GetSubMenu(i)->m_hMenu, s)) { ; } else { bReturn = false; DOMYLOGST(API_FAILURE), "CMenu::ModifyMenu", ::GetLastError(), Generic::GetSysErrorString()); } } else { m_arMenuData[i]->m_uiFlags = MF_STRING | MF_BYPOSITION; if (ModifyMenu(i, MF_STRING | MF_BYPOSITION, m_arMenuData[i]->m_uiID, s)) { ; } else { bReturn = false; DOMYLOGST(API_FAILURE), "CMenu::ModifyMenu", ::GetLastError(), Generic::GetSysErrorString()); } } } } else { bReturn = false; DOMYLOGST(API_FAILURE), "::LockResource", ::GetLastError(), Generic::GetSysErrorString()); } } else { bReturn = false; DOMYLOGST(API_FAILURE), "CMenu::CreateMenu", ::GetLastError(), Generic::GetSysErrorString()); } } else { bReturn = false; DOMYLOGST(API_FAILURE), "::LoadResource", ::GetLastError(), Generic::GetSysErrorString()); } } else { bReturn = false; DOMYLOGST(API_FAILURE), "::FindResource", ::GetLastError(), Generic::GetSysErrorString()); } } else { bReturn = false; DOMYLOGST(API_FAILURE), "AfxFindResourceHandle", ::GetLastError(), Generic::GetSysErrorString()); } return bReturn; } // void MyMenu::InsertSpaces() { VALIDATE; // LOGFONT lf; ::ZeroMemory(&lf, sizeof lf); NONCLIENTMETRICS nm; ::ZeroMemory(&nm, sizeof nm); nm.cbSize = sizeof nm; if (::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, nm.cbSize, &nm, /* fWinIni */ 0)) { lf = nm.lfMenuFont; CFont fontMenu; if (fontMenu.CreateFontIndirect(&lf)) { CDC* pDC = AfxGetMainWnd()->GetDC(); ASSERT_VALID(pDC); if (pDC) { CFont* pFont = pDC->SelectObject(&fontMenu); ASSERT_VALID(pFont); if (pFont) { int nItems(GetMenuItemCount()); int nMaxLen(-1); for (int nItem = 0; nItem < nItems; ++nItem) { CString s(m_arMenuData[nItem]->GetString()); int nTabIndex(s.Find('\011')); CString sNew; if (-1 != nTabIndex) { sNew.Left(nTabIndex); } else { sNew = s; } sNew += " "; CSize sizText(pDC->GetTextExtent(sNew, _tcslen(sNew))); if (sizText.cx > nMaxLen) { nMaxLen = sizText.cx; } } for (nItem = 0; nItem < nItems; ++nItem) { CString s(m_arMenuData[nItem]->GetString()); int nTabIndex(s.Find('\011')); if (-1 != nTabIndex) { CString sNew(s.Left(nTabIndex)); CSize sizText(pDC->GetTextExtent(sNew, _tcslen(sNew))); while (sizText.cx < nMaxLen) { sNew += ' '; sizText = pDC->GetTextExtent(sNew, _tcslen(sNew)); } sNew += s.Mid(nTabIndex); #ifdef UNICODE wcscpy(m_arMenuData[nItem]->m_szMenuText, sNew); #else m_arMenuData[nItem]->SetAnsiString(sNew); #endif } } if (pDC->SelectObject(pFont)) { if (AfxGetMainWnd()->ReleaseDC(pDC)) { if (fontMenu.DeleteObject()) { ; } else { DOMYLOGST(API_FAILURE), "CFont::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CWnd::ReleaseDC", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CDC::SelectObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CDC::SelectObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CWnd::GetDC", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CFont::CreateFontIndirect", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "::SystemParametersInfo", ::GetLastError(), Generic::GetSysErrorString()); } } // Optional: user can load their own checkmark bitmap(s). Must send // both enabled and disabled versions. void MyMenu::LoadCheckmarkBitmap(UINT uiUnselectID, UINT uiSelectID) { VALIDATE; _ASSERTE(0U < uiUnselectID); _ASSERTE(0U < uiSelectID); if (0 < uiSelectID && 0 < uiUnselectID) { m_uiSelectCheckID = uiSelectID; m_uiUnselectCheckID = uiUnselectID; if (m_pilistCheckMaps) { if (m_pilistCheckMaps->DeleteImageList()) { ; } else { DOMYLOGST(API_FAILURE), "CImageList::DeleteImageList", ::GetLastError(), Generic::GetSysErrorString()); } } else { m_pilistCheckMaps = new CImageList; } if (m_pilistCheckMaps) { if (m_pilistCheckMaps->Create(m_nIconX, m_nIconY, /* nFlags */ ILC_MASK , /* nInitial */ 2, /* nGrow */ 1)) { VERIFY(AddBitmapToImageList(m_pilistCheckMaps, uiUnselectID)); VERIFY(AddBitmapToImageList(m_pilistCheckMaps, uiSelectID)); } else { DOMYLOGST(API_FAILURE), "CImageList::Create", ::GetLastError(), Generic::GetSysErrorString()); } } } } // Gets the menu text given the index into the menu data array. bool MyMenu::GetMenuText(UINT uiID, CString& s) const { VALIDATE; //_ASSERTE(0U < uiID); Can be zero. bool bReturn(false); s.Empty(); if (uiID <= static_cast (m_arMenuData.GetUpperBound())) { ASSERT_VALID(m_arMenuData[uiID]); s = m_arMenuData[uiID]->GetString(); bReturn = true; } return bReturn; } // void MyMenu::DrawRadioDot(CDC* pDC, int x, int y, COLORREF cr) { VALIDATE; ASSERT_VALID(pDC); _ASSERTE(0 <= x); _ASSERTE(0 <= y); CRect rcDot(x, y, x + 6, y + 6); CBrush br; if (pDC) { if (br.CreateSolidBrush(cr)) { CPen pen; if (pen.CreatePen(PS_SOLID, 0, cr)) { if (pDC->SelectObject(&br)) { if (pDC->SelectObject(&pen)) { if (pDC->Ellipse(&rcDot)) { if (pen.DeleteObject()) { if (br.DeleteObject()) { ; } else { DOMYLOGST(API_FAILURE), "CBrush::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CPen::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CDC::Ellipse", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CDC::SelectObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CDC::SelectObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CPen::CreatePen", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CBrush::CreateSolidBrush", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "pDC", ::GetLastError(), Generic::GetSysErrorString()); } } // void MyMenu::DrawCheckMark(CDC* pDC, int x, int y, COLORREF cr) { VALIDATE; ASSERT_VALID(pDC); _ASSERTE(0 < x); _ASSERTE(0 < y); if (pDC) { // Make sure the DC has a clipping rect large enough for our checkmark. CRect rcBox; pDC->GetClipBox(rcBox); CRect rcCheck(x, y, x + 6, y + 6); CRect rcIntersect; rcIntersect.IntersectRect(rcCheck, rcBox); if (rcCheck == rcIntersect) { if (pDC) { VERIFY(-1 != pDC->SetPixel(x, y + 2, cr)); VERIFY(-1 != pDC->SetPixel(x, y + 3, cr)); VERIFY(-1 != pDC->SetPixel(x, y + 4, cr)); VERIFY(-1 != pDC->SetPixel(x + 1, y + 3, cr)); VERIFY(-1 != pDC->SetPixel(x + 1, y + 4, cr)); VERIFY(-1 != pDC->SetPixel(x + 1, y + 5, cr)); VERIFY(-1 != pDC->SetPixel(x + 2, y + 4, cr)); VERIFY(-1 != pDC->SetPixel(x + 2, y + 5, cr)); VERIFY(-1 != pDC->SetPixel(x + 2, y + 6, cr)); VERIFY(-1 != pDC->SetPixel(x + 3, y + 3, cr)); VERIFY(-1 != pDC->SetPixel(x + 3, y + 4, cr)); VERIFY(-1 != pDC->SetPixel(x + 3, y + 5, cr)); VERIFY(-1 != pDC->SetPixel(x + 4, y + 2, cr)); VERIFY(-1 != pDC->SetPixel(x + 4, y + 3, cr)); VERIFY(-1 != pDC->SetPixel(x + 4, y + 4, cr)); VERIFY(-1 != pDC->SetPixel(x + 5, y + 1, cr)); VERIFY(-1 != pDC->SetPixel(x + 5, y + 2, cr)); VERIFY(-1 != pDC->SetPixel(x + 5, y + 3, cr)); VERIFY(-1 != pDC->SetPixel(x + 6, y, cr)); VERIFY(-1 != pDC->SetPixel(x + 6, y + 1, cr)); VERIFY(-1 != pDC->SetPixel(x + 6, y + 2, cr)); } else { DOMYLOGST(API_FAILURE), "pDC", ::GetLastError(), Generic::GetSysErrorString()); } } else { TRACE("Did not MyMenu::DrawCheckMark - outside clip box\n"); } } else { DOMYLOGST(API_FAILURE), "pDC", ::GetLastError(), Generic::GetSysErrorString()); } } // MyMenuData* MyMenu::FindMenuList(UINT uiID) { VALIDATE; //_ASSERTE(0U < uiID); Can ber zero. MyMenuData* pMMD = NULL; for (int i = 0; i <= m_arMenuData.GetUpperBound(); ++i) { if (m_arMenuData[i]->m_uiID == uiID && ! m_arMenuData[i]->m_uiSyncFlag) { ASSERT_VALID(m_arMenuData[i]); m_arMenuData[i]->m_uiSyncFlag = 1; pMMD = m_arMenuData[i]; break; } } return pMMD; } // void MyMenu::InitializeMenuList(int nValue) { VALIDATE; for (int i = 0; i <= m_arMenuData.GetUpperBound(); ++i) { ASSERT_VALID(m_arMenuData[i]); m_arMenuData[i]->m_uiSyncFlag = nValue; } } // void MyMenu::DeleteMenuList() { VALIDATE; for (int i = 0; i <= m_arMenuData.GetUpperBound(); ++i){ ASSERT_VALID(m_arMenuData[i]); if( ! m_arMenuData[i]->m_uiSyncFlag) { delete m_arMenuData[i]; m_arMenuData[i] = NULL; } } } // void MyMenu::SynchronizeMenu() { VALIDATE; HRESULT hr(S_OK); CTypedPtrArray parMenuDataTemp; UINT uiID(0U); UINT uiState(0U); InitializeMenuList(0); for (UINT uiIndex = 0U; uiIndex < GetMenuItemCount(); ++uiIndex) { MyMenuData* pMMD = NULL; CString sText; uiState = GetMenuState(uiIndex, MF_BYPOSITION); if (uiState & MF_POPUP) { UINT uiSubMenu(reinterpret_cast (GetSubMenu(uiIndex)->m_hMenu)); pMMD = FindMenuList(uiSubMenu); GetMenuString(uiIndex, sText, MF_BYPOSITION); if(! pMMD) { pMMD = NewMyMenu(uiIndex, uiState | MF_BYPOSITION | MF_POPUP | MF_OWNERDRAW, uiSubMenu, sText); } else if (! sText.IsEmpty()) { #ifdef UNICODE wcscpy(pMMD->m_szMenuText, sText); #else pMMD->SetAnsiString(sText); #endif } } else if (uiState & MF_SEPARATOR) { pMMD = FindMenuList( /* uiID */ 0U); if (pMMD) { // Not necessarily zero. _ASSERTE(0U == uiID); ModifyMenu(uiIndex, pMMD->m_uiFlags, uiID, (LPCTSTR) pMMD); } else { pMMD = NewMyMenu(uiIndex, uiState | MF_BYPOSITION | MF_SEPARATOR | MF_OWNERDRAW, 0, /* sText */ ""); } } else { uiID = GetMenuItemID(uiIndex); pMMD = FindMenuList(uiID); GetMenuString(uiIndex, sText, MF_BYPOSITION); if (! pMMD) { pMMD = NewMyMenu(uiIndex, uiState | MF_BYPOSITION | MF_OWNERDRAW, uiID, sText); } else { pMMD->m_uiFlags = uiState | MF_BYPOSITION | MF_OWNERDRAW; if (! sText.IsEmpty()) { #ifdef UNICODE wcscpy(pMMD->m_szMenuText, sText); #else pMMD->SetAnsiString(sText); #endif } _ASSERTE(wcslen(pMMD->m_szMenuText) < sizeof pMMD->m_szMenuText && "Buffer overrun"); if (ModifyMenu(uiIndex, pMMD->m_uiFlags, uiID, (LPCTSTR) pMMD)) { ; } else { // This is failing in 4.0 beta testing on Win98. _ASSERTE(! "Error"); hr = E_FAIL; DOMYLOGST(API_FAILURE), "ModifyMenu", ::GetLastError(), Generic::GetSysErrorString()); EC_FAIL("%d", uiIndex); EC_FAIL("%d", pMMD ? pMMD->m_uiFlags : -1); EC_FAIL("%d", uiID); } } } if (pMMD) { parMenuDataTemp.Add(pMMD); } } DeleteMenuList(); m_arMenuData.RemoveAll(); VERIFY(0 <= m_arMenuData.Append(parMenuDataTemp)); parMenuDataTemp.RemoveAll(); } // We need to be sure that the object passed in is one of ours, and not just a // CMenu object. /* static */ void MyMenu::UpdateMenu(CMenu* pMenu) { ASSERT_VALID(pMenu); MyMenu* pMyMenu = dynamic_cast (pMenu); ASSERT_NULL_OR_POINTER(pMyMenu, MyMenu); if (pMyMenu) { pMyMenu->SynchronizeMenu(); } } // /* static */ LRESULT MyMenu::FindKeyboardShortcut(UINT uiChar, UINT uiFlags, CMenu* pMenu) { _ASSERTE(0U < uiChar); ASSERT_VALID(pMenu); LRESULT lrReturn(0); MyMenu* pMyMenu = dynamic_cast (pMenu); ASSERT_VALID(pMyMenu); if (pMyMenu && uiFlags & MF_POPUP) { CString sKey("& "); sKey.SetAt(1, (TCHAR) uiChar); sKey.MakeLower(); CString sMenuText; int nMenuSize(pMyMenu->GetMenuItemCount()); if ((pMyMenu->m_arMenuData.GetUpperBound() + 1) != nMenuSize) { pMyMenu->SynchronizeMenu(); } for (int i = 0; i < nMenuSize; ++i) { if (pMyMenu->GetMenuText(i, sMenuText)) { sMenuText.MakeLower(); if (sMenuText.Find(sKey) >= 0) { lrReturn = MAKELRESULT(i, 2); break; } } } } return lrReturn; } // void MyMenu::DitherBlt(CDC* pdcDraw, int nXDest, int nYDest, int nWidth, int nHeight, CBitmap& bmp, int nXSrc, int nYSrc) { VALIDATE; ASSERT_VALID(pdcDraw); _ASSERTE(0 < nWidth); _ASSERTE(0 < nHeight); _ASSERTE((HBITMAP) bmp); // Create a monochrome memory DC. CDC dcMono; dcMono.CreateCompatibleDC(0); CBitmap bmpMono; bmpMono.CreateCompatibleBitmap(&dcMono, nWidth, nHeight); CBitmap* pbmpOldMono = dcMono.SelectObject(&bmpMono); CDC dc; dc.CreateCompatibleDC(0); CBitmap* pbmpOld = dc.SelectObject(&bmp); // Build a mask. dcMono.PatBlt(0, 0, nWidth, nHeight, WHITENESS); dc.SetBkColor(::GetSysColor(COLOR_BTNFACE)); dcMono.BitBlt(0, 0, nWidth, nHeight, &dc, nXSrc,nYSrc, SRCCOPY); dc.SetBkColor(::GetSysColor(COLOR_BTNHILIGHT)); dcMono.BitBlt(0, 0, nWidth, nHeight, &dc, nXSrc,nYSrc, SRCPAINT); // Copy the image from the toolbar into the memory DC // and draw it (grayed) back into the toolbar. dc.FillSolidRect(0, 0, nWidth, nHeight, ::GetSysColor(COLOR_BTNFACE)); dc.SetBkColor(RGB(0, 0, 0)); dc.SetTextColor(RGB(255, 255, 255)); CBrush brShadow; CBrush brHilight; brHilight.CreateSolidBrush(::GetSysColor(COLOR_BTNHILIGHT)); brShadow.CreateSolidBrush(::GetSysColor(COLOR_BTNSHADOW)); CBrush* pbrOld = dc.SelectObject(&brHilight); dc.BitBlt(0, 0, nWidth, nHeight, &dcMono, 0, 0, 0x00E20746L); pdcDraw->BitBlt(nXDest + 1, nYDest + 1, nWidth, nHeight, &dc, 0, 0, SRCCOPY); dc.BitBlt(1, 1, nWidth, nHeight, &dcMono, 0, 0, 0x00E20746L); dc.SelectObject(&brShadow); dc.BitBlt(0, 0, nWidth, nHeight, &dcMono, 0, 0, 0x00E20746L); pdcDraw->BitBlt(nXDest, nYDest, nWidth, nHeight, &dc, 0, 0, SRCCOPY); // Reset DCs. dcMono.SelectObject(pbmpOldMono); dcMono.DeleteDC(); dc.SelectObject(pbrOld); dc.SelectObject(pbmpOld); dc.DeleteDC(); bmpMono.DeleteObject(); } // bool MyMenu::AddBitmapToImageList(CImageList* pilistBmp, UINT uiResourceID) { VALIDATE; ASSERT_VALID(pilistBmp); _ASSERTE(0U < uiResourceID); bool bReturn(false); if (pilistBmp) { HBITMAP hbmp(LoadSysColorBitmap(uiResourceID)); if (hbmp) { CBitmap bmp; bmp.Attach(hbmp); if (-1 != pilistBmp->Add(&bmp, m_crBitmapBackground)) { bmp.Detach(); if (::DeleteObject(hbmp)) { bReturn = true; } else { DOMYLOGST(API_FAILURE), "::DeleteObject", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "CImageList::Add", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "LoadSysColorBitmap", ::GetLastError(), Generic::GetSysErrorString()); } } else { DOMYLOGST(API_FAILURE), "pilistBmp", ::GetLastError(), Generic::GetSysErrorString()); } return bReturn; } /*I'm personally not using this right now. // Given a toolbar, append all the options from it to this menu // Passed a ptr to the toolbar object and the toolbar ID // Author : Robert Edward Caldecott void MyMenu::AddFromToolBar(CToolBar* pToolBar, int uiResourceID) { for (int i = 0; i < pToolBar->GetCount(); ++i) { UINT nID = pToolBar->GetItemID(i); // See if this toolbar option // appears as a command on this // menu or is a separator if (nID == 0 || GetMenuState(nID, MF_BYCOMMAND) == 0xFFFFFFFF) continue; // Item doesn't exist UINT nStyle; int nImage; // Get the toolbar button info pToolBar->GetButtonInfo(i, nID, nStyle, nImage); // OK, we have the command ID of the toolbar // option, and the tollbar bitmap offset int nLoc; MyMenuData* pData; MyMenu *pSubMenu = FindMenuOption(uiID, nLoc); if (pSubMenu && nLoc >= 0) pData = pSubMenu->m_arMenuData[nLoc]; else { // Create a new MyMenuData structure pData = new MyMenuData; m_arMenuData.Add(pData); } // Set some default structure members pData->m_nMenuIconNormal = uiResourceID; pData->m_uiID = nID; pData->m_uiFlags = MF_BYCOMMAND | MF_OWNERDRAW; pData->m_nXOffset = nImage; if (pData->m_pilistBitmap) pData->m_pilistBitmap->DeleteImageList(); else pData->m_pilistBitmap = new CImageList; pData->m_pilistBitmap->Create(m_nIconX, m_nIconY,ILC_COLORDDB|ILC_MASK, 1, 1); AddBitmapToImageList(pData->m_pilistBitmap, uiResourceID); // Modify our menu ModifyMenu(nID,pData->m_uiFlags,nID,(LPCTSTR)pData); } } */ // bool MyMenu::Draw3DCheckmark(CDC* pdc, const CRect& rc, BOOL bChecked, HBITMAP hbmCheck) { VALIDATE; ASSERT_VALID(pdc); // _ASSERTE(hbmCheck); Can be NULL. if (pdc) { COLORREF cr(::GetSysColor(bChecked ? COLOR_MENU : COLOR_3DLIGHT)); CBrush br; br.CreateSolidBrush(cr); CRect rcDest(rc); pdc->FillRect(rcDest, &br); br.DeleteObject(); pdc->DrawEdge(&rcDest, BDR_SUNKENOUTER, BF_RECT); // If we don't have a custom check bitmap, draw the default. if (! hbmCheck) { DrawCheckMark(pdc, rc.left + 4, rc.top + 4, ::GetSysColor(COLOR_MENUTEXT)); } else { // If we do have a custom check bitmap, draw a radio button?? DrawRadioDot(pdc, rc.left + 5, rc.top + 4, ::GetSysColor(COLOR_MENUTEXT)); } } else { DOMYLOGST(API_FAILURE), "pdc", ::GetLastError(), Generic::GetSysErrorString()); } return true; } // HBITMAP MyMenu::LoadSysColorBitmap(int nResourceID) { VALIDATE; _ASSERTE(0 < nResourceID); HBITMAP hbmpReturn(NULL); HINSTANCE hInst(AfxFindResourceHandle(MAKEINTRESOURCE(nResourceID), RT_BITMAP)); HRSRC hRsrc(::FindResource(hInst, MAKEINTRESOURCE(nResourceID), RT_BITMAP)); if (hRsrc) { hbmpReturn = AfxLoadSysColorBitmap(hInst, hRsrc, false); } return hbmpReturn; } // Get the OS type. Called once, by the static variable. /* static */ MyMenu::Win32Type MyMenu::GetShell() { MyMenu::Win32Type typeShell(MyMenu::ShellUnknown); DWORD dwWinVer(::GetVersion()); // NT. if (0x80000000 > dwWinVer) { typeShell = MyMenu::WinNT3; OSVERSIONINFO osvi; ::ZeroMemory(&osvi, sizeof osvi); osvi.dwOSVersionInfoSize = sizeof osvi; ::GetVersionEx(&osvi); // It is NT 4 or higher. if (4 <= osvi.dwMajorVersion) { typeShell = MyMenu::WinNT4orHigher; } } else if (4 > LOBYTE(LOWORD(dwWinVer))) { typeShell = MyMenu::Win32s; } else { typeShell = MyMenu::Win95; } return typeShell; }