+bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
+{
+ const MenuDrawData* data = MenuDrawData::Get();
+
+ if ( IsOwnerDrawn() )
+ {
+ *width = data->ItemMargin.GetTotalX();
+ *height = data->ItemMargin.GetTotalY();
+
+ if ( IsSeparator() )
+ {
+ *width += data->SeparatorSize.cx
+ + data->SeparatorMargin.GetTotalX();
+ *height += data->SeparatorSize.cy
+ + data->SeparatorMargin.GetTotalY();
+ 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.GetTotalX() + data->ArrowSize.cx;
+ }
+ 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.GetTotalX();
+
+ *width += imgWidth + data->CheckBgMargin.GetTotalX();
+ }
+
+ if ( m_bmpChecked.IsOk() || m_bmpUnchecked.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 widthBmp = wxMax(m_bmpChecked.GetWidth(), m_bmpUnchecked.GetWidth());
+
+ if ( IsOwnerDrawn() )
+ {
+ heightBmp += data->CheckMargin.GetTotalY();
+ }
+ else
+ {
+ // we must allocate enough space for the bitmap
+ *width += widthBmp;
+ }
+
+ // 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.GetTotalY()
+ + 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 colText, colBack;
+ GetColourToUse(stat, colText, colBack);
+
+ // calculate metrics of item parts
+ RECT rcSelection = rect;
+ data->ItemMargin.ApplyTo(rcSelection);
+
+ RECT rcSeparator = rcSelection;
+ data->SeparatorMargin.ApplyTo(rcSeparator);
+
+ RECT rcGutter = rcSelection;
+ rcGutter.right = data->ItemMargin.cxLeftWidth
+ + data->CheckBgMargin.cxLeftWidth
+ + data->CheckMargin.cxLeftWidth
+ + imgWidth
+ + data->CheckMargin.cxRightWidth
+ + data->CheckBgMargin.cxRightWidth;
+
+ RECT 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
+ // If a custom background colour is explicitly specified, we should use
+ // it instead of the default theme background.
+ wxUxThemeEngine* const theme = GetBackgroundColour().IsOk()
+ ? NULL
+ : 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.GetPixel());
+ SelectInHDC selBrush(hdc, hbr);
+ ::FillRect(hdc, &rcSelection, hbr);
+ }
+
+
+ // draw text label
+ // using native API because it recognizes '&'
+
+ HDCTextColChanger changeTextCol(hdc, colText.GetPixel());
+ HDCBgColChanger changeBgCol(hdc, colBack.GetPixel());
+ HDCBgModeChanger changeBgMode(hdc, TRANSPARENT);
+
+ SelectInHDC selFont(hdc, GetHfontOf(font));
+
+
+ // item text name without mnemonic for calculating size
+ wxString text = GetName();
+
+ SIZE textSize;
+ ::GetTextExtentPoint32(hdc, text.c_str(), text.length(), &textSize);
+
+ // item text name with mnemonic
+ 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.GetTotalX()
+ - data->ArrowSize.cx
+ - 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);
+ }
+ }
+
+
+ // draw the bitmap
+
+ RECT rcImg;
+ SetRect(&rcImg,
+ rect.left + data->ItemMargin.cxLeftWidth
+ + data->CheckBgMargin.cxLeftWidth
+ + data->CheckMargin.cxLeftWidth,
+ rect.top + data->ItemMargin.cyTopHeight
+ + data->CheckBgMargin.cyTopHeight
+ + data->CheckMargin.cyTopHeight,
+ rect.left + data->ItemMargin.cxLeftWidth
+ + data->CheckBgMargin.cxLeftWidth
+ + data->CheckMargin.cxLeftWidth
+ + imgWidth,
+ rect.bottom - data->ItemMargin.cyBottomHeight
+ - data->CheckBgMargin.cyBottomHeight
+ - data->CheckMargin.cyBottomHeight);
+
+ 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);
+
+ HDCTextColChanger changeTextCol(hdc, colBlack);
+ HDCBgColChanger changeBgCol(hdc, colWhite);
+ HDCBgModeChanger changeBgMode(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);
+ }
+}
+
+} // 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 = *rc;
+ data->CheckMargin.UnapplyFrom(rcBg);
+
+ 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
+