+bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
+{
+ if ( IsOwnerDrawn() )
+ {
+
+ wxString str = GetName();
+
+ // if we have a valid accel string, then pad out
+ // the menu string so that the menu and accel string are not
+ // placed on top of each other.
+ wxString accel = GetItemLabel().AfterFirst(wxT('\t'));
+ if ( !accel.empty() )
+ {
+ str.Pad(str.length()%8);
+ str += accel;
+ }
+
+ wxMemoryDC dc;
+ wxFont font;
+ GetFontToUse(font);
+ dc.SetFont(font);
+
+ wxCoord w, h;
+ dc.GetTextExtent(str, &w, &h);
+ *width = w;
+ *height = h;
+ }
+ else // don't draw the text, just the bitmap (if any)
+ {
+ *width = 0;
+ *height = 0;
+ }
+
+ // increase size to accommodate bigger bitmaps if necessary
+ if (m_bmpChecked.Ok())
+ {
+ // Is BMP height larger than text height?
+ size_t adjustedHeight = m_bmpChecked.GetHeight();
+ if ( *height < adjustedHeight )
+ *height = adjustedHeight;
+
+ const int widthBmp = m_bmpChecked.GetWidth();
+ if ( IsOwnerDrawn() )
+ {
+ // widen the margin to fit the bitmap if necessary
+ if ( GetMarginWidth() < widthBmp )
+ SetMarginWidth(widthBmp);
+
+ }
+ else // we must allocate enough space for the bitmap
+ {
+ *width += widthBmp;
+ }
+ }
+
+ // add a 4-pixel separator, otherwise menus look cluttered
+ *width += 4;
+
+ // notice that this adjustment must be done after (possibly) changing the
+ // margin width above
+ if ( IsOwnerDrawn() )
+ {
+ // add space at the end of the menu for the submenu expansion arrow
+ // this will also allow offsetting the accel string from the right edge
+ *width += GetMarginWidth() + 16;
+ }
+
+ // make sure that this item is at least as tall as the system menu height
+ if ( *height < ms_systemMenuHeight )
+ *height = ms_systemMenuHeight;
+
+ return true;
+}
+
+bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
+ wxODAction WXUNUSED(act), wxODStatus stat)
+{
+
+ // this flag determines whether or not an edge will
+ // be drawn around the bitmap. In most "windows classic"
+ // applications, a 1-pixel highlight edge is drawn around
+ // the bitmap of an item when it is selected. However,
+ // with the new "luna" theme, no edge is drawn around
+ // the bitmap because the background is white (this applies
+ // only to "non-XP style" menus w/ bitmaps --
+ // see IE 6 menus for an example)
+
+ bool draw_bitmap_edge = true;
+
+ // set the colors
+ // --------------
+ wxColour colText1, colBack1;
+ GetColourToUse(stat, colText1, colBack1);
+
+ DWORD colText = wxColourToPalRGB(colText1);
+ DWORD colBack = wxColourToPalRGB(colBack1);
+
+ if ( IsOwnerDrawn() )
+ {
+ // don't draw an edge around the bitmap, if background is white ...
+ DWORD menu_bg_color = GetSysColor(COLOR_MENU);
+ if ( GetRValue( menu_bg_color ) >= 0xf0 &&
+ GetGValue( menu_bg_color ) >= 0xf0 &&
+ GetBValue( menu_bg_color ) >= 0xf0 )
+ {
+ draw_bitmap_edge = false;
+ }
+ }
+ else // edge doesn't look well with default Windows drawing
+ {
+ draw_bitmap_edge = false;
+ }
+
+
+ wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
+ HDC hdc = GetHdcOf(*impl);
+ COLORREF colOldText = ::SetTextColor(hdc, colText);
+ COLORREF colOldBack = ::SetBkColor(hdc, colBack);
+
+ // *2, as in wxSYS_EDGE_Y
+ int margin = GetMarginWidth() + 2 * wxSystemSettings::GetMetric(wxSYS_EDGE_X);
+
+ // select the font and draw the text
+ // ---------------------------------
+
+
+ // determine where to draw and leave space for a check-mark.
+ // + 1 pixel to separate the edge from the highlight rectangle
+ int xText = rc.x + margin + 1;
+
+
+ // using native API because it recognizes '&'
+ if ( IsOwnerDrawn() )
+ {
+ int prevMode = SetBkMode(hdc, TRANSPARENT);
+ AutoHBRUSH hbr(colBack);
+ SelectInHDC selBrush(hdc, hbr);
+
+ RECT rectFill;
+ wxCopyRectToRECT(rc, rectFill);
+
+ if ( (stat & wxODSelected) && m_bmpChecked.Ok() && draw_bitmap_edge )
+ {
+ // only draw the highlight under the text, not under
+ // the bitmap or checkmark
+ rectFill.left = xText;
+ }
+
+ ::FillRect(hdc, &rectFill, hbr);
+
+ // use default font if no font set
+ wxFont font;
+ GetFontToUse(font);
+ SelectInHDC selFont(hdc, GetHfontOf(font));
+
+ // item text name with menemonic
+ wxString text = GetItemLabel().BeforeFirst('\t');
+
+ xText += 3; // separate text from the highlight rectangle
+
+ SIZE textRect;
+ ::GetTextExtentPoint32(hdc, text.c_str(), text.length(), &textRect);
+
+ int flags = DST_PREFIXTEXT;
+ if ( (stat & wxODDisabled) && !(stat & wxODSelected) )
+ flags |= DSS_DISABLED;
+
+ if ( (stat & wxODHidePrefix) && !ms_alwaysShowCues )
+ flags |= DSS_HIDEPREFIX;
+
+ int x = xText;
+ int y = rc.y + (rc.GetHeight() - textRect.cy) / 2;
+ int cx = rc.GetWidth() - GetMarginWidth();
+ int cy = textRect.cy;
+
+ ::DrawState(hdc, NULL, NULL, (LPARAM)text.wx_str(),
+ text.length(), x, y, cx, cy, 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 accelRect;
+ ::GetTextExtentPoint32(hdc, accel.c_str(), accel.length(), &accelRect);
+
+ int flags = DST_TEXT;
+ if ( (stat & wxODDisabled) && !(stat & wxODSelected) )
+ flags |= DSS_DISABLED;
+
+ // right align accel string with right edge of menu
+ // (offset by the margin width)
+
+ int x = rc.GetWidth() - 16 - accelRect.cx;
+ int y = rc.y + (rc.GetHeight() - accelRect.cy) / 2;
+ ::DrawState(hdc, NULL, NULL, (LPARAM)accel.wx_str(),
+ accel.length(), x, y, 0, 0, flags);
+ }
+
+ ::SetBkMode(hdc, prevMode);
+ }
+
+
+ // draw the bitmap
+ // ---------------
+ if ( IsCheckable() && !m_bmpChecked.Ok() )
+ {
+ if ( stat & wxODChecked )
+ {
+ // what goes on: DrawFrameControl creates a b/w mask,
+ // then we copy it to screen to have right colors
+
+ // first create a monochrome bitmap in a memory DC
+ HDC hdcMem = CreateCompatibleDC(hdc);
+ HBITMAP hbmpCheck = CreateBitmap(margin, rc.GetHeight(), 1, 1, 0);
+ SelectObject(hdcMem, hbmpCheck);
+
+ // then draw a check mark into it
+ RECT rect = { 0, 0, margin, rc.GetHeight() };
+ if ( rc.GetHeight() > 0 )
+ {
+ ::DrawFrameControl(hdcMem, &rect, DFC_MENU, DFCS_MENUCHECK);
+ }
+
+ // finally copy it to screen DC and clean up
+ BitBlt(hdc, rc.x, rc.y, margin, rc.GetHeight(), hdcMem, 0, 0, SRCCOPY);
+
+ DeleteDC(hdcMem);
+ DeleteObject(hbmpCheck);
+ }
+ }
+ 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 <= rc.GetWidth()) && (nBmpHeight <= rc.GetHeight()));
+
+ int heightDiff = rc.GetHeight() - nBmpHeight;
+ dc.Blit(rc.x + (margin - nBmpWidth) / 2,
+ rc.y + heightDiff / 2,
+ nBmpWidth, nBmpHeight,
+ &dcMem, 0, 0, wxCOPY, true /* use mask */);
+
+ if ( ( stat & wxODSelected ) && !( stat & wxODDisabled ) && draw_bitmap_edge )
+ {
+ RECT rectBmp = { rc.GetLeft(), rc.GetTop(),
+ rc.GetLeft() + margin,
+ rc.GetTop() + rc.GetHeight() };
+ SetBkColor(hdc, colBack);
+
+ DrawEdge(hdc, &rectBmp, BDR_RAISEDINNER, BF_RECT);
+ }
+ }
+ }
+
+ ::SetTextColor(hdc, colOldText);
+ ::SetBkColor(hdc, colOldBack);
+
+ return true;
+
+}
+
+void wxMenuItem::GetFontToUse(wxFont& font) const
+{
+ font = GetFont();
+ if ( !font.IsOk() )
+ font = ms_systemMenuFont;
+}
+
+#endif // wxUSE_OWNER_DRAWN
+