+ // wxMenuItemBase will do stock ID checks
+ wxMenuItemBase::SetItemLabel(text);
+
+ // the item can be not attached to any menu yet and SetItemLabel() is still
+ // valid to call in this case and should do nothing else
+ if ( !m_parentMenu )
+ return;
+
+#if wxUSE_ACCEL
+ m_parentMenu->UpdateAccel(this);
+#endif // wxUSE_ACCEL
+
+ const UINT id = GetMSWId();
+ HMENU hMenu = GetHMenuOf(m_parentMenu);
+ if ( !hMenu || ::GetMenuState(hMenu, id, MF_BYCOMMAND) == (UINT)-1 )
+ return;
+
+#if wxUSE_OWNER_DRAWN
+ if ( IsOwnerDrawn() )
+ {
+ // we don't need to do anything for owner drawn items, they will redraw
+ // themselves using the new text the next time they're displayed
+ return;
+ }
+#endif // owner drawn
+
+ // update the text of the native menu item
+ WinStruct<MENUITEMINFO> info;
+
+ // surprisingly, calling SetMenuItemInfo() with just MIIM_STRING doesn't
+ // work as it resets the menu bitmap, so we need to first get the old item
+ // state and then modify it
+ const bool isLaterThanWin95 = wxGetWinVersion() > wxWinVersion_95;
+ info.fMask = MIIM_STATE |
+ MIIM_ID |
+ MIIM_SUBMENU |
+ MIIM_CHECKMARKS |
+ MIIM_DATA;
+ if ( isLaterThanWin95 )
+ info.fMask |= MIIM_BITMAP | MIIM_FTYPE;
+ else
+ info.fMask |= MIIM_TYPE;
+ if ( !::GetMenuItemInfo(hMenu, id, FALSE, &info) )
+ {
+ wxLogLastError(wxT("GetMenuItemInfo"));
+ return;
+ }
+
+ if ( isLaterThanWin95 )
+ info.fMask |= MIIM_STRING;
+ //else: MIIM_TYPE already specified
+ info.dwTypeData = (LPTSTR)m_text.wx_str();
+ info.cch = m_text.length();
+ if ( !::SetMenuItemInfo(hMenu, id, FALSE, &info) )
+ {
+ wxLogLastError(wxT("SetMenuItemInfo"));
+ }
+}
+
+#if wxUSE_OWNER_DRAWN
+
+int wxMenuItem::MeasureAccelWidth() const
+{
+ wxString accel = GetItemLabel().AfterFirst(wxT('\t'));
+
+ wxMemoryDC dc;
+ wxFont font;
+ GetFontToUse(font);
+ dc.SetFont(font);
+
+ wxCoord w;
+ dc.GetTextExtent(accel, &w, NULL);
+
+ return w;
+}
+
+wxString wxMenuItem::GetName() const
+{
+ return GetItemLabelText();
+}
+
+bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
+{
+ const MenuDrawData* data = MenuDrawData::Get();
+
+ if ( IsOwnerDrawn() )
+ {
+ *width = data->ItemMargin.left + data->ItemMargin.right;
+ *height = data->ItemMargin.top + data->ItemMargin.bottom;
+
+ if ( IsSeparator() )
+ {
+ *width += data->SeparatorSize.cx
+ + data->SeparatorMargin.left + data->SeparatorMargin.right;
+ *height += data->SeparatorSize.cy
+ + data->SeparatorMargin.top + data->SeparatorMargin.bottom;
+ return true;
+ }
+
+ wxString str = GetName();
+
+ wxMemoryDC dc;
+ wxFont font;
+ GetFontToUse(font);
+ dc.SetFont(font);
+
+ wxCoord w, h;
+ dc.GetTextExtent(str, &w, &h);
+
+ *width = data->TextBorder + w + data->AccelBorder;
+ *height = h;
+
+ w = m_parentMenu->GetMaxAccelWidth();
+ if ( w > 0 )
+ *width += w + data->ArrowBorder;
+
+ *width += data->Offset;
+ *width += data->ArrowMargin.left + data->ArrowSize.cx + data->ArrowMargin.right;
+ }
+ else // don't draw the text, just the bitmap (if any)
+ {
+ *width = 0;
+ *height = 0;
+ }
+
+ // bitmap
+
+ if ( IsOwnerDrawn() )
+ {
+ // width of menu icon with margins in ownerdrawn menu
+ // if any bitmap is not set, the width of space reserved for icon
+ // image is equal to the width of std check mark,
+ // if bitmap is set, then the width is set to the width of the widest
+ // bitmap in menu (GetMarginWidth()) unless std check mark is wider,
+ // then it's is set to std mark's width
+ int imgWidth = wxMax(GetMarginWidth(), data->CheckSize.cx)
+ + data->CheckMargin.left + data->CheckMargin.right;
+
+ *width += imgWidth + data->CheckBgMargin.left + data->CheckBgMargin.right;
+ }
+
+ if ( m_bmpChecked.IsOk() || m_bmpChecked.IsOk() )
+ {
+ // get size of bitmap always return valid value (0 for invalid bitmap),
+ // so we don't needed check if bitmap is valid ;)
+ size_t heightBmp = wxMax(m_bmpChecked.GetHeight(), m_bmpUnchecked.GetHeight());
+ size_t widthtBmp = wxMax(m_bmpChecked.GetWidth(), m_bmpUnchecked.GetWidth());
+
+ if ( IsOwnerDrawn() )
+ {
+ heightBmp += data->CheckMargin.top + data->CheckMargin.bottom;
+ }
+ else
+ {
+ // we must allocate enough space for the bitmap
+ *width += widthtBmp;
+ }
+
+ // Is BMP height larger than text height?
+ if ( *height < heightBmp )
+ *height = heightBmp;
+ }
+
+ // make sure that this item is at least as tall as the system menu height
+ const size_t menuHeight = data->CheckMargin.top + data->CheckMargin.bottom
+ + data->CheckSize.cy;
+ if (*height < menuHeight)
+ *height = menuHeight;
+
+ return true;
+}
+
+bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
+ wxODAction WXUNUSED(act), wxODStatus stat)
+{
+ const MenuDrawData* data = MenuDrawData::Get();
+
+ wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
+ HDC hdc = GetHdcOf(*impl);
+
+ RECT rect;
+ wxCopyRectToRECT(rc, rect);
+
+ int imgWidth = wxMax(GetMarginWidth(), data->CheckSize.cx);
+
+ if ( IsOwnerDrawn() )
+ {
+ // font and colors to use
+ wxFont font;
+ GetFontToUse(font);
+
+ wxColour colText1, colBack1;
+ GetColourToUse(stat, colText1, colBack1);
+
+ DWORD colText = wxColourToPalRGB(colText1);
+ DWORD colBack = wxColourToPalRGB(colBack1);
+
+ // calculate metrics of item parts
+ RECT rcSelection;
+ RECT rcSeparator;
+ RECT rcGutter;
+ RECT rcText;
+
+ SetRect(&rcSelection,
+ rect.left + data->ItemMargin.left,
+ rect.top + data->ItemMargin.top,
+ rect.right - data->ItemMargin.right,
+ rect.bottom - data->ItemMargin.bottom);
+
+ SetRect(&rcSeparator,
+ rcSelection.left + data->SeparatorMargin.left,
+ rcSelection.top + data->SeparatorMargin.top,
+ rcSelection.right - data->SeparatorMargin.right,
+ rcSelection.bottom - data->SeparatorMargin.bottom);
+
+ CopyRect(&rcGutter, &rcSelection);
+ rcGutter.right = data->ItemMargin.left
+ + data->CheckBgMargin.left
+ + data->CheckMargin.left
+ + imgWidth
+ + data->CheckMargin.right
+ + data->CheckBgMargin.right;
+
+ CopyRect(&rcText, &rcSelection);
+ rcText.left = rcGutter.right + data->TextBorder;
+
+ // we draw the text label vertically centered, but this results in it
+ // being 1px too low compared to native menus for some reason, fix it
+ if ( data->MenuLayout() != MenuDrawData::FullTheme )
+ rcText.top--;
+
+#if wxUSE_UXTHEME
+ wxUxThemeEngine* theme = MenuDrawData::GetUxThemeEngine();
+ if ( theme )
+ {
+ POPUPITEMSTATES state;
+ if ( stat & wxODDisabled )
+ {
+ state = (stat & wxODSelected) ? MPI_DISABLEDHOT
+ : MPI_DISABLED;
+ }
+ else if ( stat & wxODSelected )
+ {
+ state = MPI_HOT;
+ }
+ else
+ {
+ state = MPI_NORMAL;
+ }
+
+ wxUxThemeHandle hTheme(GetMenu()->GetWindow(), L"MENU");
+
+ if ( theme->IsThemeBackgroundPartiallyTransparent(hTheme,
+ MENU_POPUPITEM, state) )
+ {
+ theme->DrawThemeBackground(hTheme, hdc,
+ MENU_POPUPBACKGROUND,
+ 0, &rect, NULL);
+ }
+
+ theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPGUTTER,
+ 0, &rcGutter, NULL);
+
+ if ( IsSeparator() )
+ {
+ rcSeparator.left = rcGutter.right;
+ theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPSEPARATOR,
+ 0, &rcSeparator, NULL);
+ return true;
+ }
+
+ theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPITEM,
+ state, &rcSelection, NULL);
+
+ }
+ else
+#endif // wxUSE_UXTHEME
+ {
+ if ( IsSeparator() )
+ {
+ DrawEdge(hdc, &rcSeparator, EDGE_ETCHED, BF_TOP);
+ return true;
+ }
+
+ AutoHBRUSH hbr(colBack);
+ SelectInHDC selBrush(hdc, hbr);
+ ::FillRect(hdc, &rcSelection, hbr);
+ }
+
+
+ // draw text label
+ // using native API because it recognizes '&'
+
+ COLORREF colOldText = ::SetTextColor(hdc, colText);
+ COLORREF colOldBack = ::SetBkColor(hdc, colBack);
+
+ int prevMode = SetBkMode(hdc, TRANSPARENT);
+
+ SelectInHDC selFont(hdc, GetHfontOf(font));
+
+
+ // item text name without menemonic for calculating size
+ wxString text = GetName();
+
+ SIZE textSize;
+ ::GetTextExtentPoint32(hdc, text.c_str(), text.length(), &textSize);
+
+ // item text name with menemonic
+ text = GetItemLabel().BeforeFirst('\t');
+
+ int flags = DST_PREFIXTEXT;
+ // themes menu is using specified color for disabled labels
+ if ( data->MenuLayout() == MenuDrawData::Classic &&
+ (stat & wxODDisabled) && !(stat & wxODSelected) )
+ flags |= DSS_DISABLED;
+
+ if ( (stat & wxODHidePrefix) && !data->AlwaysShowCues )
+ flags |= DSS_HIDEPREFIX;
+
+ int x = rcText.left;
+ int y = rcText.top + (rcText.bottom - rcText.top - textSize.cy) / 2;
+
+ ::DrawState(hdc, NULL, NULL, (LPARAM)text.wx_str(),
+ text.length(), x, y, 0, 0, flags);
+
+ // ::SetTextAlign(hdc, TA_RIGHT) doesn't work with DSS_DISABLED or DSS_MONO
+ // as the last parameter in DrawState() (at least with Windows98). So we have
+ // to take care of right alignment ourselves.
+ wxString accel = GetItemLabel().AfterFirst(wxT('\t'));
+ if ( !accel.empty() )
+ {
+ SIZE accelSize;
+ ::GetTextExtentPoint32(hdc, accel.c_str(), accel.length(), &accelSize);
+
+ int flags = DST_TEXT;
+ // themes menu is using specified color for disabled labels
+ if ( data->MenuLayout() == MenuDrawData::Classic &&
+ (stat & wxODDisabled) && !(stat & wxODSelected) )
+ flags |= DSS_DISABLED;
+
+ int x = rcText.right - data->ArrowMargin.left
+ - data->ArrowSize.cx
+ - data->ArrowMargin.right
+ - data->ArrowBorder;
+
+ // right align accel on FullTheme menu, left otherwise
+ if ( data->MenuLayout() == MenuDrawData::FullTheme)
+ x -= accelSize.cx;
+ else
+ x -= m_parentMenu->GetMaxAccelWidth();
+
+ int y = rcText.top + (rcText.bottom - rcText.top - accelSize.cy) / 2;
+
+ ::DrawState(hdc, NULL, NULL, (LPARAM)accel.wx_str(),
+ accel.length(), x, y, 0, 0, flags);
+ }
+
+ ::SetBkMode(hdc, prevMode);
+ ::SetBkColor(hdc, colOldBack);
+ ::SetTextColor(hdc, colOldText);
+ }
+
+
+ // draw the bitmap
+
+ RECT rcImg;
+ SetRect(&rcImg,
+ rect.left + data->ItemMargin.left
+ + data->CheckBgMargin.left
+ + data->CheckMargin.left,
+ rect.top + data->ItemMargin.top
+ + data->CheckBgMargin.top
+ + data->CheckMargin.top,
+ rect.left + data->ItemMargin.left
+ + data->CheckBgMargin.left
+ + data->CheckMargin.left
+ + imgWidth,
+ rect.bottom - data->ItemMargin.bottom
+ - data->CheckBgMargin.bottom
+ - data->CheckMargin.bottom);
+
+ if ( IsCheckable() && !m_bmpChecked.Ok() )
+ {
+ if ( stat & wxODChecked )
+ {
+ DrawStdCheckMark((WXHDC)hdc, &rcImg, stat);
+ }
+ }
+ else
+ {
+ wxBitmap bmp;
+
+ if ( stat & wxODDisabled )
+ {
+ bmp = GetDisabledBitmap();
+ }
+
+ if ( !bmp.Ok() )
+ {
+ // for not checkable bitmaps we should always use unchecked one
+ // because their checked bitmap is not set
+ bmp = GetBitmap(!IsCheckable() || (stat & wxODChecked));
+
+#if wxUSE_IMAGE
+ if ( bmp.Ok() && stat & wxODDisabled )
+ {
+ // we need to grey out the bitmap as we don't have any specific
+ // disabled bitmap
+ wxImage imgGrey = bmp.ConvertToImage().ConvertToGreyscale();
+ if ( imgGrey.Ok() )
+ bmp = wxBitmap(imgGrey);
+ }
+#endif // wxUSE_IMAGE
+ }
+
+ if ( bmp.Ok() )
+ {
+ wxMemoryDC dcMem(&dc);
+ dcMem.SelectObjectAsSource(bmp);
+
+ // center bitmap
+ int nBmpWidth = bmp.GetWidth(),
+ nBmpHeight = bmp.GetHeight();
+
+ // there should be enough space!
+ wxASSERT( nBmpWidth <= imgWidth && nBmpHeight <= (rcImg.bottom - rcImg.top) );
+
+ int x = rcImg.left + (imgWidth - nBmpWidth) / 2;
+ int y = rcImg.top + (rcImg.bottom - rcImg.top - nBmpHeight) / 2;
+ dc.Blit(x, y, nBmpWidth, nBmpHeight, &dcMem, 0, 0, wxCOPY, true);
+ }
+ }
+
+ return true;
+
+}
+
+namespace
+{
+
+// helper function for draw coloured check mark
+void DrawColorCheckMark(HDC hdc, int x, int y, int cx, int cy, HDC hdcCheckMask, int idxColor)
+{
+ const COLORREF colBlack = RGB(0, 0, 0);
+ const COLORREF colWhite = RGB(255, 255, 255);
+
+ COLORREF colOldText = ::SetTextColor(hdc, colBlack);
+ COLORREF colOldBack = ::SetBkColor(hdc, colWhite);
+ int prevMode = SetBkMode(hdc, TRANSPARENT);
+
+ // memory DC for color bitmap
+ MemoryHDC hdcMem(hdc);
+ CompatibleBitmap hbmpMem(hdc, cx, cy);
+ SelectInHDC selMem(hdcMem, hbmpMem);
+
+ RECT rect = { 0, 0, cx, cy };
+ ::FillRect(hdcMem, &rect, ::GetSysColorBrush(idxColor));
+
+ const COLORREF colCheck = ::GetSysColor(idxColor);
+ if ( colCheck == colWhite )
+ {
+ ::BitBlt(hdc, x, y, cx, cy, hdcCheckMask, 0, 0, MERGEPAINT);
+ ::BitBlt(hdc, x, y, cx, cy, hdcMem, 0, 0, SRCAND);
+ }
+ else
+ {
+ if ( colCheck != colBlack )
+ {
+ const DWORD ROP_DSna = 0x00220326; // dest = (NOT src) AND dest
+ ::BitBlt(hdcMem, 0, 0, cx, cy, hdcCheckMask, 0, 0, ROP_DSna);
+ }
+
+ ::BitBlt(hdc, x, y, cx, cy, hdcCheckMask, 0, 0, SRCAND);
+ ::BitBlt(hdc, x, y, cx, cy, hdcMem, 0, 0, SRCPAINT);
+ }
+
+ ::SetBkMode(hdc, prevMode);
+ ::SetBkColor(hdc, colOldBack);
+ ::SetTextColor(hdc, colOldText);
+}
+
+} // anonymous namespace
+
+void wxMenuItem::DrawStdCheckMark(WXHDC hdc_, const RECT* rc, wxODStatus stat)
+{
+ HDC hdc = (HDC)hdc_;
+
+#if wxUSE_UXTHEME
+ wxUxThemeEngine* theme = MenuDrawData::GetUxThemeEngine();
+ if ( theme )
+ {
+ wxUxThemeHandle hTheme(GetMenu()->GetWindow(), L"MENU");
+
+ const MenuDrawData* data = MenuDrawData::Get();
+
+ // rect for background must be without check margins
+ RECT rcBg;
+ SetRect(&rcBg,
+ rc->left - data->CheckMargin.left,
+ rc->top - data->CheckMargin.top,
+ rc->right + data->CheckMargin.right,
+ rc->bottom + data->CheckMargin.bottom);
+
+ POPUPCHECKBACKGROUNDSTATES stateCheckBg = (stat & wxODDisabled)
+ ? MCB_DISABLED
+ : MCB_NORMAL;
+
+ theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPCHECKBACKGROUND,
+ stateCheckBg, &rcBg, NULL);
+
+ POPUPCHECKSTATES stateCheck;
+ if ( GetKind() == wxITEM_CHECK )
+ {
+ stateCheck = (stat & wxODDisabled) ? MC_CHECKMARKDISABLED
+ : MC_CHECKMARKNORMAL;
+ }
+ else
+ {
+ stateCheck = (stat & wxODDisabled) ? MC_BULLETDISABLED
+ : MC_BULLETNORMAL;
+ }
+
+ theme->DrawThemeBackground(hTheme, hdc, MENU_POPUPCHECK,
+ stateCheck, rc, NULL);
+ }
+ else
+#endif // wxUSE_UXTHEME
+ {
+ int cx = rc->right - rc->left;
+ int cy = rc->bottom - rc->top;
+
+ // first create mask of check mark
+ MemoryHDC hdcMask(hdc);
+ MonoBitmap hbmpMask(cx, cy);
+ SelectInHDC selMask(hdcMask,hbmpMask);
+
+ // then draw a check mark into it
+ UINT stateCheck = (GetKind() == wxITEM_CHECK) ? DFCS_MENUCHECK
+ : DFCS_MENUBULLET;
+ RECT rect = { 0, 0, cx, cy };
+ ::DrawFrameControl(hdcMask, &rect, DFC_MENU, stateCheck);
+
+ // first draw shadow if disabled
+ if ( (stat & wxODDisabled) && !(stat & wxODSelected) )
+ {
+ DrawColorCheckMark(hdc, rc->left + 1, rc->top + 1,
+ cx, cy, hdcMask, COLOR_3DHILIGHT);
+ }
+
+ // then draw a check mark
+ int color = COLOR_MENUTEXT;
+ if ( stat & wxODDisabled )
+ color = COLOR_BTNSHADOW;
+ else if ( stat & wxODSelected )
+ color = COLOR_HIGHLIGHTTEXT;
+
+ DrawColorCheckMark(hdc, rc->left, rc->top, cx, cy, hdcMask, color);
+ }
+}
+
+void wxMenuItem::GetFontToUse(wxFont& font) const
+{
+ font = GetFont();
+ if ( !font.IsOk() )
+ font = MenuDrawData::Get()->Font;
+}
+
+void wxMenuItem::GetColourToUse(wxODStatus stat, wxColour& colText, wxColour& colBack) const
+{
+#if wxUSE_UXTHEME
+ wxUxThemeEngine* theme = MenuDrawData::GetUxThemeEngine();
+ if ( theme )
+ {
+ wxUxThemeHandle hTheme(GetMenu()->GetWindow(), L"MENU");
+
+ if ( stat & wxODDisabled)
+ {
+ wxRGBToColour(colText, theme->GetThemeSysColor(hTheme, COLOR_GRAYTEXT));
+ }
+ else
+ {
+ colText = GetTextColour();
+ if ( !colText.IsOk() )
+ wxRGBToColour(colText, theme->GetThemeSysColor(hTheme, COLOR_MENUTEXT));
+ }
+
+ if ( stat & wxODSelected )
+ {
+ wxRGBToColour(colBack, theme->GetThemeSysColor(hTheme, COLOR_HIGHLIGHT));
+ }
+ else
+ {
+ colBack = GetBackgroundColour();
+ if ( !colBack.IsOk() )
+ wxRGBToColour(colBack, theme->GetThemeSysColor(hTheme, COLOR_MENU));
+ }
+ }
+ else
+#endif // wxUSE_UXTHEME
+ {
+ wxOwnerDrawn::GetColourToUse(stat, colText, colBack);
+ }
+}
+#endif // wxUSE_OWNER_DRAWN
+
+// ----------------------------------------------------------------------------
+// wxMenuItemBase
+// ----------------------------------------------------------------------------
+
+wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
+ int id,
+ const wxString& name,
+ const wxString& help,
+ wxItemKind kind,
+ wxMenu *subMenu)
+{
+ return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
+}