1///////////////////////////////////////////////////////////////////////////////
2// Name:        src/mac/carbon/menuitem.cpp
3// Purpose:     wxMenuItem implementation
4// Author:      Stefan Csomor
5// Modified by:
6// Created:     1998-01-01
7// RCS-ID:      $Id: menuitem.cpp 48053 2007-08-13 17:07:01Z JS $
8// Copyright:   (c) Stefan Csomor
9// Licence:     wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12#include "wx/wxprec.h"
13
14#include "wx/menuitem.h"
15#include "wx/stockitem.h"
16
17#ifndef WX_PRECOMP
18    #include "wx/app.h"
19    #include "wx/menu.h"
20#endif // WX_PRECOMP
21
22#include "wx/mac/uma.h"
23
24IMPLEMENT_DYNAMIC_CLASS(wxMenuItem, wxObject)
25
26
27wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
28                       int id,
29                       const wxString& text,
30                       const wxString& strHelp,
31                       wxItemKind kind,
32                       wxMenu *pSubMenu)
33           :wxMenuItemBase(pParentMenu, id, text, strHelp, kind, pSubMenu)
34{
35    wxASSERT_MSG( id != 0 || pSubMenu != NULL , wxT("A MenuItem ID of Zero does not work under Mac") ) ;
36
37    // In other languages there is no difference in naming the Exit/Quit menu item between MacOS and Windows guidelines
38    // therefore these item must not be translated
39    if ( wxStripMenuCodes(m_text).Upper() == wxT("EXIT") )
40        m_text = wxT("Quit\tCtrl+Q") ;
41
42    m_radioGroup.start = -1;
43    m_isRadioGroupStart = false;
44}
45
46wxMenuItem::~wxMenuItem()
47{
48}
49
50// change item state
51// -----------------
52
53void wxMenuItem::SetBitmap(const wxBitmap& bitmap)
54{
55      m_bitmap = bitmap;
56      UpdateItemBitmap();
57}
58
59void wxMenuItem::UpdateItemBitmap()
60{
61    if ( !m_parentMenu )
62        return ;
63
64    MenuHandle mhandle = MAC_WXHMENU(m_parentMenu->GetHMenu()) ;
65    MenuItemIndex index = m_parentMenu->MacGetIndexFromItem( this ) ;
66    DoUpdateItemBitmap( mhandle, index );
67}
68
69void wxMenuItem::DoUpdateItemBitmap( WXHMENU menu, wxUint16 index)
70{
71    MenuHandle mhandle = (MenuHandle) menu;
72
73    if ( mhandle == NULL || index == 0)
74        return ;
75
76    if ( m_bitmap.Ok() )
77    {
78#if wxUSE_BMPBUTTON
79        ControlButtonContentInfo info ;
80        wxMacCreateBitmapButton( &info , m_bitmap ) ;
81        if ( info.contentType != kControlNoContent )
82        {
83            if ( info.contentType == kControlContentIconRef )
84                SetMenuItemIconHandle( mhandle , index ,
85                    kMenuIconRefType , (Handle) info.u.iconRef ) ;
86            else if ( info.contentType == kControlContentCGImageRef )
87               SetMenuItemIconHandle( mhandle , index ,
88                    kMenuCGImageRefType , (Handle) info.u.imageRef ) ;
89        }
90        wxMacReleaseBitmapButton( &info ) ;
91#endif
92    }
93}
94
95void wxMenuItem::UpdateItemStatus()
96{
97    if ( !m_parentMenu )
98        return ;
99
100    if ( IsSeparator() )
101        return ;
102
103#if TARGET_CARBON
104    if ( UMAGetSystemVersion() >= 0x1000 && GetId() == wxApp::s_macPreferencesMenuItemId)
105    {
106        if ( !IsEnabled() )
107            DisableMenuCommand( NULL , kHICommandPreferences ) ;
108        else
109            EnableMenuCommand( NULL , kHICommandPreferences ) ;
110    }
111
112    if ( UMAGetSystemVersion() >= 0x1000 && GetId() == wxApp::s_macExitMenuItemId)
113    {
114        if ( !IsEnabled() )
115            DisableMenuCommand( NULL , kHICommandQuit ) ;
116        else
117            EnableMenuCommand( NULL , kHICommandQuit ) ;
118    }
119#endif
120
121    {
122        MenuHandle mhandle = MAC_WXHMENU(m_parentMenu->GetHMenu()) ;
123        MenuItemIndex index = m_parentMenu->MacGetIndexFromItem( this ) ;
124        if ( mhandle == NULL || index == 0)
125            return ;
126
127        UMAEnableMenuItem( mhandle , index , m_isEnabled ) ;
128        if ( IsCheckable() && IsChecked() )
129            ::SetItemMark( mhandle , index , 0x12 ) ; // checkmark
130        else
131            ::SetItemMark( mhandle , index , 0 ) ; // no mark
132
133        UMASetMenuItemText( mhandle , index , wxStripMenuCodes(m_text) , wxFont::GetDefaultEncoding() ) ;
134        wxAcceleratorEntry *entry = wxAcceleratorEntry::Create( m_text ) ;
135        UMASetMenuItemShortcut( mhandle , index , entry ) ;
136        delete entry ;
137    }
138}
139
140void wxMenuItem::UpdateItemText()
141{
142    if ( !m_parentMenu )
143        return ;
144
145    MenuHandle mhandle = MAC_WXHMENU(m_parentMenu->GetHMenu()) ;
146    MenuItemIndex index = m_parentMenu->MacGetIndexFromItem( this ) ;
147    if (mhandle == NULL || index == 0)
148        return ;
149
150    wxString text = m_text;
151    if (text.IsEmpty() && !IsSeparator())
152    {
153        wxASSERT_MSG(wxIsStockID(GetId()), wxT("A non-stock menu item with an empty label?"));
154        text = wxGetStockLabel(GetId(), wxSTOCK_WITH_ACCELERATOR|wxSTOCK_WITH_MNEMONIC);
155    }
156
157    UMASetMenuItemText( mhandle , index , wxStripMenuCodes(text) , wxFont::GetDefaultEncoding() ) ;
158    wxAcceleratorEntry *entry = wxAcceleratorEntry::Create( text ) ;
159    UMASetMenuItemShortcut( mhandle , index , entry ) ;
160    delete entry ;
161}
162
163void wxMenuItem::Enable(bool bDoEnable)
164{
165    if (( m_isEnabled != bDoEnable
166#if TARGET_CARBON
167      // avoid changing menuitem state when menu is disabled
168      // eg. BeginAppModalStateForWindow() will disable menus and ignore this change
169      // which in turn causes m_isEnabled to become out of sync with real menuitem state
170         && !(m_parentMenu && !IsMenuItemEnabled(MAC_WXHMENU(m_parentMenu->GetHMenu()), 0)) )
171      // always update builtin menuitems
172         || (   GetId() == wxApp::s_macPreferencesMenuItemId
173             || GetId() == wxApp::s_macExitMenuItemId
174             || GetId() == wxApp::s_macAboutMenuItemId
175#endif
176         ))
177    {
178        wxMenuItemBase::Enable( bDoEnable ) ;
179        UpdateItemStatus() ;
180    }
181}
182
183void wxMenuItem::UncheckRadio()
184{
185    if ( m_isChecked )
186    {
187        wxMenuItemBase::Check( false ) ;
188        UpdateItemStatus() ;
189    }
190}
191
192void wxMenuItem::Check(bool bDoCheck)
193{
194    wxCHECK_RET( IsCheckable() && !IsSeparator(), wxT("only checkable items may be checked") );
195
196    if ( m_isChecked != bDoCheck )
197    {
198        if ( GetKind() == wxITEM_RADIO )
199        {
200            if ( bDoCheck )
201            {
202                wxMenuItemBase::Check( bDoCheck ) ;
203                UpdateItemStatus() ;
204
205                // get the index of this item in the menu
206                const wxMenuItemList& items = m_parentMenu->GetMenuItems();
207                int pos = items.IndexOf(this);
208                wxCHECK_RET( pos != wxNOT_FOUND,
209                             _T("menuitem not found in the menu items list?") );
210
211                // get the radio group range
212                int start, end;
213
214                if ( m_isRadioGroupStart )
215                {
216                    // we already have all information we need
217                    start = pos;
218                    end = m_radioGroup.end;
219                }
220                else // next radio group item
221                {
222                    // get the radio group end from the start item
223                    start = m_radioGroup.start;
224                    end = items.Item(start)->GetData()->m_radioGroup.end;
225                }
226
227                // also uncheck all the other items in this radio group
228                wxMenuItemList::compatibility_iterator node = items.Item(start);
229                for ( int n = start; n <= end && node; n++ )
230                {
231                    if ( n != pos )
232                        ((wxMenuItem*)node->GetData())->UncheckRadio();
233
234                    node = node->GetNext();
235                }
236            }
237        }
238        else
239        {
240            wxMenuItemBase::Check( bDoCheck ) ;
241            UpdateItemStatus() ;
242        }
243    }
244}
245
246void wxMenuItem::SetText(const wxString& text)
247{
248    // don't do anything if label didn't change
249    if ( m_text == text )
250        return;
251
252    wxMenuItemBase::SetText(text);
253
254    UpdateItemText() ;
255}
256
257// radio group stuff
258// -----------------
259
260void wxMenuItem::SetAsRadioGroupStart()
261{
262    m_isRadioGroupStart = true;
263}
264
265void wxMenuItem::SetRadioGroupStart(int start)
266{
267    wxASSERT_MSG( !m_isRadioGroupStart,
268                  wxT("should only be called for the next radio items") );
269
270    m_radioGroup.start = start;
271}
272
273void wxMenuItem::SetRadioGroupEnd(int end)
274{
275    wxASSERT_MSG( m_isRadioGroupStart,
276                  wxT("should only be called for the first radio item") );
277
278    m_radioGroup.end = end;
279}
280
281// ----------------------------------------------------------------------------
282// wxMenuItemBase
283// ----------------------------------------------------------------------------
284
285/* static */
286wxString wxMenuItemBase::GetLabelFromText(const wxString& text)
287{
288    return wxStripMenuCodes(text);
289}
290
291wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
292                                int id,
293                                const wxString& name,
294                                const wxString& help,
295                                wxItemKind kind,
296                                wxMenu *subMenu)
297{
298    return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
299}
300