Refactor owner-drawing code.
[wxWidgets.git] / src / msw / menuitem.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/menuitem.cpp
3 // Purpose: wxMenuItem implementation
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 11.11.97
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #if wxUSE_MENUS
28
29 #include "wx/menuitem.h"
30 #include "wx/stockitem.h"
31
32 #ifndef WX_PRECOMP
33 #include "wx/font.h"
34 #include "wx/bitmap.h"
35 #include "wx/settings.h"
36 #include "wx/window.h"
37 #include "wx/accel.h"
38 #include "wx/string.h"
39 #include "wx/log.h"
40 #include "wx/menu.h"
41 #endif
42
43 #if wxUSE_ACCEL
44 #include "wx/accel.h"
45 #endif // wxUSE_ACCEL
46
47 #include "wx/msw/private.h"
48 #include "wx/msw/dc.h"
49
50 #ifdef __WXWINCE__
51 // Implemented in menu.cpp
52 UINT GetMenuState(HMENU hMenu, UINT id, UINT flags) ;
53 #endif
54
55 // ---------------------------------------------------------------------------
56 // macro
57 // ---------------------------------------------------------------------------
58
59 // hide the ugly cast
60 #define GetHMenuOf(menu) ((HMENU)menu->GetHMenu())
61
62 // ============================================================================
63 // implementation
64 // ============================================================================
65
66 #if wxUSE_OWNER_DRAWN
67
68 #include "wx/fontutil.h"
69 #include "wx/msw/private/metrics.h"
70
71 #ifndef SPI_GETKEYBOARDCUES
72 #define SPI_GETKEYBOARDCUES 0x100A
73 #endif
74
75 #ifndef DSS_HIDEPREFIX
76 #define DSS_HIDEPREFIX 0x0200
77 #endif
78
79 #endif // wxUSE_OWNER_DRAWN
80
81 // ----------------------------------------------------------------------------
82 // dynamic classes implementation
83 // ----------------------------------------------------------------------------
84
85 #if wxUSE_EXTENDED_RTTI
86
87 bool wxMenuItemStreamingCallback( const wxObject *object, wxWriter * , wxPersister * , wxxVariantArray & )
88 {
89 const wxMenuItem * mitem = dynamic_cast<const wxMenuItem*>(object) ;
90 if ( mitem->GetMenu() && !mitem->GetMenu()->GetTitle().empty() )
91 {
92 // we don't stream out the first two items for menus with a title, they will be reconstructed
93 if ( mitem->GetMenu()->FindItemByPosition(0) == mitem || mitem->GetMenu()->FindItemByPosition(1) == mitem )
94 return false ;
95 }
96 return true ;
97 }
98
99 wxBEGIN_ENUM( wxItemKind )
100 wxENUM_MEMBER( wxITEM_SEPARATOR )
101 wxENUM_MEMBER( wxITEM_NORMAL )
102 wxENUM_MEMBER( wxITEM_CHECK )
103 wxENUM_MEMBER( wxITEM_RADIO )
104 wxEND_ENUM( wxItemKind )
105
106 IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuItem, wxObject,"wx/menuitem.h",wxMenuItemStreamingCallback)
107
108 wxBEGIN_PROPERTIES_TABLE(wxMenuItem)
109 wxPROPERTY( Parent,wxMenu*, SetMenu, GetMenu, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
110 wxPROPERTY( Id,int, SetId, GetId, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
111 wxPROPERTY( Text, wxString , SetText, GetText, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
112 wxPROPERTY( Help, wxString , SetHelp, GetHelp, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
113 wxREADONLY_PROPERTY( Kind, wxItemKind , GetKind , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
114 wxPROPERTY( SubMenu,wxMenu*, SetSubMenu, GetSubMenu, EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
115 wxPROPERTY( Enabled , bool , Enable , IsEnabled , wxxVariant((bool)true) , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
116 wxPROPERTY( Checked , bool , Check , IsChecked , wxxVariant((bool)false) , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
117 wxPROPERTY( Checkable , bool , SetCheckable , IsCheckable , wxxVariant((bool)false) , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
118 wxEND_PROPERTIES_TABLE()
119
120 wxBEGIN_HANDLERS_TABLE(wxMenuItem)
121 wxEND_HANDLERS_TABLE()
122
123 wxDIRECT_CONSTRUCTOR_6( wxMenuItem , wxMenu* , Parent , int , Id , wxString , Text , wxString , Help , wxItemKind , Kind , wxMenu* , SubMenu )
124 #else
125 IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
126 #endif
127
128 // ----------------------------------------------------------------------------
129 // wxMenuItem
130 // ----------------------------------------------------------------------------
131
132 #if wxUSE_OWNER_DRAWN
133
134 // these static variables are from the wxMenuItem object for cache
135 // system settings returned by the Win32 API's SystemParametersInfo() call
136 wxFont wxMenuItem::ms_systemMenuFont;
137 size_t wxMenuItem::ms_systemMenuHeight = 0;
138 bool wxMenuItem::ms_alwaysShowCues = false;
139
140 #endif // wxUSE_OWNER_DRAWN
141
142
143 // ctor & dtor
144 // -----------
145
146 wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
147 int id,
148 const wxString& text,
149 const wxString& strHelp,
150 wxItemKind kind,
151 wxMenu *pSubMenu)
152 : wxMenuItemBase(pParentMenu, id, text, strHelp, kind, pSubMenu)
153 {
154 Init();
155 }
156
157 #if WXWIN_COMPATIBILITY_2_8
158 wxMenuItem::wxMenuItem(wxMenu *parentMenu,
159 int id,
160 const wxString& text,
161 const wxString& help,
162 bool isCheckable,
163 wxMenu *subMenu)
164 : wxMenuItemBase(parentMenu, id, text, help,
165 isCheckable ? wxITEM_CHECK : wxITEM_NORMAL, subMenu)
166 {
167 Init();
168 }
169 #endif
170
171 void wxMenuItem::Init()
172 {
173 m_radioGroup.start = -1;
174 m_isRadioGroupStart = false;
175
176 #if wxUSE_OWNER_DRAWN
177
178 // init static varaibles
179 if ( !ms_systemMenuHeight )
180 {
181 const NONCLIENTMETRICS& metrics = wxMSWImpl::GetNonClientMetrics();
182
183 ms_systemMenuFont = wxFont(wxNativeFontInfo(metrics.lfMenuFont));
184 ms_systemMenuHeight = metrics.iMenuHeight;
185
186 if ( ::SystemParametersInfo(SPI_GETKEYBOARDCUES, 0,
187 &ms_alwaysShowCues, 0) == 0 )
188 {
189 // if it's not supported, we must be on an old Windows version
190 // which always shows them
191 ms_alwaysShowCues = true;
192 }
193
194 }
195
196 // when the color is not valid, wxOwnerDraw takes the default ones.
197 // If we set the colors here and they are changed by the user during
198 // the execution, then the colors are not updated until the application
199 // is restarted and our menus look bad
200 SetTextColour(wxNullColour);
201 SetBackgroundColour(wxNullColour);
202
203 // setting default colors switched ownerdraw on: switch it off again
204 SetOwnerDrawn(false);
205
206 // switch ownerdraw back on if using a non default margin
207 if ( !IsSeparator() )
208 SetMarginWidth(GetMarginWidth());
209
210 #endif // wxUSE_OWNER_DRAWN
211 }
212
213 wxMenuItem::~wxMenuItem()
214 {
215 }
216
217 // misc
218 // ----
219
220 // return the id for calling Win32 API functions
221 WXWPARAM wxMenuItem::GetMSWId() const
222 {
223 // we must use ids in unsigned short range with Windows functions, if we
224 // pass ids > USHRT_MAX to them they get very confused (e.g. start
225 // generating WM_COMMAND messages with negative high word of wParam), so
226 // use the cast to ensure the id is in range
227 return m_subMenu ? wxPtrToUInt(m_subMenu->GetHMenu())
228 : static_cast<unsigned short>(GetId());
229 }
230
231 // get item state
232 // --------------
233
234 bool wxMenuItem::IsChecked() const
235 {
236 // fix that RTTI is always getting the correct state (separators cannot be
237 // checked, but the Windows call below returns true
238 if ( IsSeparator() )
239 return false;
240
241 // the item might not be attached to a menu yet
242 //
243 // TODO: shouldn't we just always call the base class version? It seems
244 // like it ought to always be in sync
245 if ( !m_parentMenu )
246 return wxMenuItemBase::IsChecked();
247
248 HMENU hmenu = GetHMenuOf(m_parentMenu);
249 int flag = ::GetMenuState(hmenu, GetMSWId(), MF_BYCOMMAND);
250
251 return (flag & MF_CHECKED) != 0;
252 }
253
254 // radio group stuff
255 // -----------------
256
257 void wxMenuItem::SetAsRadioGroupStart()
258 {
259 m_isRadioGroupStart = true;
260 }
261
262 void wxMenuItem::SetRadioGroupStart(int start)
263 {
264 wxASSERT_MSG( !m_isRadioGroupStart,
265 wxT("should only be called for the next radio items") );
266
267 m_radioGroup.start = start;
268 }
269
270 void wxMenuItem::SetRadioGroupEnd(int end)
271 {
272 wxASSERT_MSG( m_isRadioGroupStart,
273 wxT("should only be called for the first radio item") );
274
275 m_radioGroup.end = end;
276 }
277
278 // change item state
279 // -----------------
280
281 void wxMenuItem::Enable(bool enable)
282 {
283 if ( m_isEnabled == enable )
284 return;
285
286 if ( m_parentMenu )
287 {
288 long rc = EnableMenuItem(GetHMenuOf(m_parentMenu),
289 GetMSWId(),
290 MF_BYCOMMAND |
291 (enable ? MF_ENABLED : MF_GRAYED));
292
293 if ( rc == -1 )
294 {
295 wxLogLastError(wxT("EnableMenuItem"));
296 }
297 }
298
299 wxMenuItemBase::Enable(enable);
300 }
301
302 void wxMenuItem::Check(bool check)
303 {
304 wxCHECK_RET( IsCheckable(), wxT("only checkable items may be checked") );
305
306 if ( m_isChecked == check )
307 return;
308
309 if ( m_parentMenu )
310 {
311 int flags = check ? MF_CHECKED : MF_UNCHECKED;
312 HMENU hmenu = GetHMenuOf(m_parentMenu);
313
314 if ( GetKind() == wxITEM_RADIO )
315 {
316 // it doesn't make sense to uncheck a radio item -- what would this
317 // do?
318 if ( !check )
319 return;
320
321 // get the index of this item in the menu
322 const wxMenuItemList& items = m_parentMenu->GetMenuItems();
323 int pos = items.IndexOf(this);
324 wxCHECK_RET( pos != wxNOT_FOUND,
325 wxT("menuitem not found in the menu items list?") );
326
327 // get the radio group range
328 int start,
329 end;
330
331 if ( m_isRadioGroupStart )
332 {
333 // we already have all information we need
334 start = pos;
335 end = m_radioGroup.end;
336 }
337 else // next radio group item
338 {
339 // get the radio group end from the start item
340 start = m_radioGroup.start;
341 end = items.Item(start)->GetData()->m_radioGroup.end;
342 }
343
344 #ifdef __WIN32__
345 // calling CheckMenuRadioItem() with such parameters hangs my system
346 // (NT4 SP6) and I suspect this could happen to the others as well,
347 // so don't do it!
348 wxCHECK_RET( start != -1 && end != -1,
349 wxT("invalid ::CheckMenuRadioItem() parameter(s)") );
350
351 if ( !::CheckMenuRadioItem(hmenu,
352 start, // the first radio group item
353 end, // the last one
354 pos, // the one to check
355 MF_BYPOSITION) )
356 {
357 wxLogLastError(wxT("CheckMenuRadioItem"));
358 }
359 #endif // __WIN32__
360
361 // also uncheck all the other items in this radio group
362 wxMenuItemList::compatibility_iterator node = items.Item(start);
363 for ( int n = start; n <= end && node; n++ )
364 {
365 if ( n != pos )
366 {
367 node->GetData()->m_isChecked = false;
368 }
369
370 node = node->GetNext();
371 }
372 }
373 else // check item
374 {
375 if ( ::CheckMenuItem(hmenu,
376 GetMSWId(),
377 MF_BYCOMMAND | flags) == (DWORD)-1 )
378 {
379 wxFAIL_MSG(wxT("CheckMenuItem() failed, item not in the menu?"));
380 }
381 }
382 }
383
384 wxMenuItemBase::Check(check);
385 }
386
387 void wxMenuItem::SetItemLabel(const wxString& txt)
388 {
389 wxString text = txt;
390
391 // don't do anything if label didn't change
392 if ( m_text == txt )
393 return;
394
395 // wxMenuItemBase will do stock ID checks
396 wxMenuItemBase::SetItemLabel(text);
397
398 // the item can be not attached to any menu yet and SetItemLabel() is still
399 // valid to call in this case and should do nothing else
400 if ( !m_parentMenu )
401 return;
402
403 #if wxUSE_ACCEL
404 m_parentMenu->UpdateAccel(this);
405 #endif // wxUSE_ACCEL
406
407 const UINT id = GetMSWId();
408 HMENU hMenu = GetHMenuOf(m_parentMenu);
409 if ( !hMenu || ::GetMenuState(hMenu, id, MF_BYCOMMAND) == (UINT)-1 )
410 return;
411
412 #if wxUSE_OWNER_DRAWN
413 if ( IsOwnerDrawn() )
414 {
415 // we don't need to do anything for owner drawn items, they will redraw
416 // themselves using the new text the next time they're displayed
417 return;
418 }
419 #endif // owner drawn
420
421 // update the text of the native menu item
422 WinStruct<MENUITEMINFO> info;
423
424 // surprisingly, calling SetMenuItemInfo() with just MIIM_STRING doesn't
425 // work as it resets the menu bitmap, so we need to first get the old item
426 // state and then modify it
427 const bool isLaterThanWin95 = wxGetWinVersion() > wxWinVersion_95;
428 info.fMask = MIIM_STATE |
429 MIIM_ID |
430 MIIM_SUBMENU |
431 MIIM_CHECKMARKS |
432 MIIM_DATA;
433 if ( isLaterThanWin95 )
434 info.fMask |= MIIM_BITMAP | MIIM_FTYPE;
435 else
436 info.fMask |= MIIM_TYPE;
437 if ( !::GetMenuItemInfo(hMenu, id, FALSE, &info) )
438 {
439 wxLogLastError(wxT("GetMenuItemInfo"));
440 return;
441 }
442
443 if ( isLaterThanWin95 )
444 info.fMask |= MIIM_STRING;
445 //else: MIIM_TYPE already specified
446 info.dwTypeData = (LPTSTR)m_text.wx_str();
447 info.cch = m_text.length();
448 if ( !::SetMenuItemInfo(hMenu, id, FALSE, &info) )
449 {
450 wxLogLastError(wxT("SetMenuItemInfo"));
451 }
452 }
453
454 #if wxUSE_OWNER_DRAWN
455
456 wxString wxMenuItem::GetName() const
457 {
458 return GetItemLabelText();
459 }
460
461 bool wxMenuItem::OnMeasureItem(size_t *width, size_t *height)
462 {
463 if ( IsOwnerDrawn() )
464 {
465
466 wxString str = GetName();
467
468 // if we have a valid accel string, then pad out
469 // the menu string so that the menu and accel string are not
470 // placed on top of each other.
471 wxString accel = GetItemLabel().AfterFirst(wxT('\t'));
472 if ( !accel.empty() )
473 {
474 str.Pad(str.length()%8);
475 str += accel;
476 }
477
478 wxMemoryDC dc;
479 wxFont font;
480 GetFontToUse(font);
481 dc.SetFont(font);
482
483 wxCoord w, h;
484 dc.GetTextExtent(str, &w, &h);
485 *width = w;
486 *height = h;
487 }
488 else // don't draw the text, just the bitmap (if any)
489 {
490 *width = 0;
491 *height = 0;
492 }
493
494 // increase size to accommodate bigger bitmaps if necessary
495 if (m_bmpChecked.Ok())
496 {
497 // Is BMP height larger than text height?
498 size_t adjustedHeight = m_bmpChecked.GetHeight();
499 if ( *height < adjustedHeight )
500 *height = adjustedHeight;
501
502 const int widthBmp = m_bmpChecked.GetWidth();
503 if ( IsOwnerDrawn() )
504 {
505 // widen the margin to fit the bitmap if necessary
506 if ( GetMarginWidth() < widthBmp )
507 SetMarginWidth(widthBmp);
508
509 }
510 else // we must allocate enough space for the bitmap
511 {
512 *width += widthBmp;
513 }
514 }
515
516 // add a 4-pixel separator, otherwise menus look cluttered
517 *width += 4;
518
519 // notice that this adjustment must be done after (possibly) changing the
520 // margin width above
521 if ( IsOwnerDrawn() )
522 {
523 // add space at the end of the menu for the submenu expansion arrow
524 // this will also allow offsetting the accel string from the right edge
525 *width += GetMarginWidth() + 16;
526 }
527
528 // make sure that this item is at least as tall as the system menu height
529 if ( *height < ms_systemMenuHeight )
530 *height = ms_systemMenuHeight;
531
532 return true;
533 }
534
535 bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
536 wxODAction WXUNUSED(act), wxODStatus stat)
537 {
538
539 // this flag determines whether or not an edge will
540 // be drawn around the bitmap. In most "windows classic"
541 // applications, a 1-pixel highlight edge is drawn around
542 // the bitmap of an item when it is selected. However,
543 // with the new "luna" theme, no edge is drawn around
544 // the bitmap because the background is white (this applies
545 // only to "non-XP style" menus w/ bitmaps --
546 // see IE 6 menus for an example)
547
548 bool draw_bitmap_edge = true;
549
550 // set the colors
551 // --------------
552 wxColour colText1, colBack1;
553 GetColourToUse(stat, colText1, colBack1);
554
555 DWORD colText = wxColourToPalRGB(colText1);
556 DWORD colBack = wxColourToPalRGB(colBack1);
557
558 if ( IsOwnerDrawn() )
559 {
560 // don't draw an edge around the bitmap, if background is white ...
561 DWORD menu_bg_color = GetSysColor(COLOR_MENU);
562 if ( GetRValue( menu_bg_color ) >= 0xf0 &&
563 GetGValue( menu_bg_color ) >= 0xf0 &&
564 GetBValue( menu_bg_color ) >= 0xf0 )
565 {
566 draw_bitmap_edge = false;
567 }
568 }
569 else // edge doesn't look well with default Windows drawing
570 {
571 draw_bitmap_edge = false;
572 }
573
574
575 wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
576 HDC hdc = GetHdcOf(*impl);
577 COLORREF colOldText = ::SetTextColor(hdc, colText);
578 COLORREF colOldBack = ::SetBkColor(hdc, colBack);
579
580 // *2, as in wxSYS_EDGE_Y
581 int margin = GetMarginWidth() + 2 * wxSystemSettings::GetMetric(wxSYS_EDGE_X);
582
583 // select the font and draw the text
584 // ---------------------------------
585
586
587 // determine where to draw and leave space for a check-mark.
588 // + 1 pixel to separate the edge from the highlight rectangle
589 int xText = rc.x + margin + 1;
590
591
592 // using native API because it recognizes '&'
593 if ( IsOwnerDrawn() )
594 {
595 int prevMode = SetBkMode(hdc, TRANSPARENT);
596 AutoHBRUSH hbr(colBack);
597 SelectInHDC selBrush(hdc, hbr);
598
599 RECT rectFill;
600 wxCopyRectToRECT(rc, rectFill);
601
602 if ( (stat & wxODSelected) && m_bmpChecked.Ok() && draw_bitmap_edge )
603 {
604 // only draw the highlight under the text, not under
605 // the bitmap or checkmark
606 rectFill.left = xText;
607 }
608
609 ::FillRect(hdc, &rectFill, hbr);
610
611 // use default font if no font set
612 wxFont font;
613 GetFontToUse(font);
614 SelectInHDC selFont(hdc, GetHfontOf(font));
615
616 // item text name with menemonic
617 wxString text = GetItemLabel().BeforeFirst('\t');
618
619 xText += 3; // separate text from the highlight rectangle
620
621 SIZE textRect;
622 ::GetTextExtentPoint32(hdc, text.c_str(), text.length(), &textRect);
623
624 int flags = DST_PREFIXTEXT;
625 if ( (stat & wxODDisabled) && !(stat & wxODSelected) )
626 flags |= DSS_DISABLED;
627
628 if ( (stat & wxODHidePrefix) && !ms_alwaysShowCues )
629 flags |= DSS_HIDEPREFIX;
630
631 int x = xText;
632 int y = rc.y + (rc.GetHeight() - textRect.cy) / 2;
633 int cx = rc.GetWidth() - GetMarginWidth();
634 int cy = textRect.cy;
635
636 ::DrawState(hdc, NULL, NULL, (LPARAM)text.wx_str(),
637 text.length(), x, y, cx, cy, flags);
638
639 // ::SetTextAlign(hdc, TA_RIGHT) doesn't work with DSS_DISABLED or DSS_MONO
640 // as the last parameter in DrawState() (at least with Windows98). So we have
641 // to take care of right alignment ourselves.
642 wxString accel = GetItemLabel().AfterFirst(wxT('\t'));
643 if ( !accel.empty() )
644 {
645 SIZE accelRect;
646 ::GetTextExtentPoint32(hdc, accel.c_str(), accel.length(), &accelRect);
647
648 int flags = DST_TEXT;
649 if ( (stat & wxODDisabled) && !(stat & wxODSelected) )
650 flags |= DSS_DISABLED;
651
652 // right align accel string with right edge of menu
653 // (offset by the margin width)
654
655 int x = rc.GetWidth() - 16 - accelRect.cx;
656 int y = rc.y + (rc.GetHeight() - accelRect.cy) / 2;
657 ::DrawState(hdc, NULL, NULL, (LPARAM)accel.wx_str(),
658 accel.length(), x, y, 0, 0, flags);
659 }
660
661 ::SetBkMode(hdc, prevMode);
662 }
663
664
665 // draw the bitmap
666 // ---------------
667 if ( IsCheckable() && !m_bmpChecked.Ok() )
668 {
669 if ( stat & wxODChecked )
670 {
671 // what goes on: DrawFrameControl creates a b/w mask,
672 // then we copy it to screen to have right colors
673
674 // first create a monochrome bitmap in a memory DC
675 HDC hdcMem = CreateCompatibleDC(hdc);
676 HBITMAP hbmpCheck = CreateBitmap(margin, rc.GetHeight(), 1, 1, 0);
677 SelectObject(hdcMem, hbmpCheck);
678
679 // then draw a check mark into it
680 RECT rect = { 0, 0, margin, rc.GetHeight() };
681 if ( rc.GetHeight() > 0 )
682 {
683 ::DrawFrameControl(hdcMem, &rect, DFC_MENU, DFCS_MENUCHECK);
684 }
685
686 // finally copy it to screen DC and clean up
687 BitBlt(hdc, rc.x, rc.y, margin, rc.GetHeight(), hdcMem, 0, 0, SRCCOPY);
688
689 DeleteDC(hdcMem);
690 DeleteObject(hbmpCheck);
691 }
692 }
693 else
694 {
695 wxBitmap bmp;
696
697 if ( stat & wxODDisabled )
698 {
699 bmp = GetDisabledBitmap();
700 }
701
702 if ( !bmp.Ok() )
703 {
704 // for not checkable bitmaps we should always use unchecked one
705 // because their checked bitmap is not set
706 bmp = GetBitmap(!IsCheckable() || (stat & wxODChecked));
707
708 #if wxUSE_IMAGE
709 if ( bmp.Ok() && stat & wxODDisabled )
710 {
711 // we need to grey out the bitmap as we don't have any specific
712 // disabled bitmap
713 wxImage imgGrey = bmp.ConvertToImage().ConvertToGreyscale();
714 if ( imgGrey.Ok() )
715 bmp = wxBitmap(imgGrey);
716 }
717 #endif // wxUSE_IMAGE
718 }
719
720 if ( bmp.Ok() )
721 {
722 wxMemoryDC dcMem(&dc);
723 dcMem.SelectObjectAsSource(bmp);
724
725 // center bitmap
726 int nBmpWidth = bmp.GetWidth(),
727 nBmpHeight = bmp.GetHeight();
728
729 // there should be enough space!
730 wxASSERT((nBmpWidth <= rc.GetWidth()) && (nBmpHeight <= rc.GetHeight()));
731
732 int heightDiff = rc.GetHeight() - nBmpHeight;
733 dc.Blit(rc.x + (margin - nBmpWidth) / 2,
734 rc.y + heightDiff / 2,
735 nBmpWidth, nBmpHeight,
736 &dcMem, 0, 0, wxCOPY, true /* use mask */);
737
738 if ( ( stat & wxODSelected ) && !( stat & wxODDisabled ) && draw_bitmap_edge )
739 {
740 RECT rectBmp = { rc.GetLeft(), rc.GetTop(),
741 rc.GetLeft() + margin,
742 rc.GetTop() + rc.GetHeight() };
743 SetBkColor(hdc, colBack);
744
745 DrawEdge(hdc, &rectBmp, BDR_RAISEDINNER, BF_RECT);
746 }
747 }
748 }
749
750 ::SetTextColor(hdc, colOldText);
751 ::SetBkColor(hdc, colOldBack);
752
753 return true;
754
755 }
756
757 void wxMenuItem::GetFontToUse(wxFont& font) const
758 {
759 font = GetFont();
760 if ( !font.IsOk() )
761 font = ms_systemMenuFont;
762 }
763
764 #endif // wxUSE_OWNER_DRAWN
765
766 // ----------------------------------------------------------------------------
767 // wxMenuItemBase
768 // ----------------------------------------------------------------------------
769
770 wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
771 int id,
772 const wxString& name,
773 const wxString& help,
774 wxItemKind kind,
775 wxMenu *subMenu)
776 {
777 return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
778 }
779
780 #endif // wxUSE_MENUS