1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/motif/menuitem.cpp
3 // Purpose:     wxMenuItem implementation
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     17/09/98
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 ///////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21 
22 #include "wx/menuitem.h"
23 #include "wx/stockitem.h"
24 
25 #ifndef WX_PRECOMP
26     #include "wx/utils.h"
27     #include "wx/frame.h"
28     #include "wx/menu.h"
29 #endif
30 
31 #ifdef __VMS__
32 #pragma message disable nosimpint
33 #endif
34 #include <Xm/Label.h>
35 #include <Xm/LabelG.h>
36 #include <Xm/CascadeBG.h>
37 #include <Xm/CascadeB.h>
38 #include <Xm/SeparatoG.h>
39 #include <Xm/PushBG.h>
40 #include <Xm/ToggleB.h>
41 #include <Xm/ToggleBG.h>
42 #include <Xm/RowColumn.h>
43 #ifdef __VMS__
44 #pragma message enable nosimpint
45 #endif
46 
47 #include "wx/motif/private.h"
48 
49 // ----------------------------------------------------------------------------
50 // functions prototypes
51 // ----------------------------------------------------------------------------
52 
53 static void wxMenuItemCallback(Widget w, XtPointer clientData, XtPointer ptr);
54 static void wxMenuItemArmCallback(Widget w, XtPointer clientData, XtPointer ptr);
55 static void wxMenuItemDisarmCallback(Widget w, XtPointer clientData, XtPointer ptr);
56 
57 // ============================================================================
58 // implementation
59 // ============================================================================
60 
61 // ----------------------------------------------------------------------------
62 // dynamic classes implementation
63 // ----------------------------------------------------------------------------
64 
65 // ----------------------------------------------------------------------------
66 // wxMenuItem
67 // ----------------------------------------------------------------------------
68 
69 // ctor & dtor
70 // -----------
71 
wxMenuItem(wxMenu * pParentMenu,int id,const wxString & strName,const wxString & strHelp,wxItemKind kind,wxMenu * pSubMenu)72 wxMenuItem::wxMenuItem(wxMenu *pParentMenu,
73                        int id,
74                        const wxString& strName,
75                        const wxString& strHelp,
76                        wxItemKind kind,
77                        wxMenu *pSubMenu)
78           : wxMenuItemBase(pParentMenu, id, strName, strHelp, kind, pSubMenu)
79 {
80     // Motif-specific
81     m_menuBar      = NULL;
82     m_buttonWidget = (WXWidget) NULL;
83     m_topMenu      = NULL;
84 }
85 
~wxMenuItem()86 wxMenuItem::~wxMenuItem()
87 {
88 }
89 
90 // change item state
91 // -----------------
92 
Enable(bool bDoEnable)93 void wxMenuItem::Enable(bool bDoEnable)
94 {
95     if ( m_isEnabled != bDoEnable )
96     {
97         if ( !IsSubMenu() )
98         {
99             // normal menu item
100             if (m_buttonWidget)
101                 XtSetSensitive( (Widget) m_buttonWidget, (Boolean) bDoEnable);
102         }
103         else                            // submenu
104         {
105             // Maybe we should apply this to all items in the submenu?
106             // Or perhaps it works anyway.
107             if (m_buttonWidget)
108                 XtSetSensitive( (Widget) m_buttonWidget, (Boolean) bDoEnable);
109         }
110 
111         wxMenuItemBase::Enable(bDoEnable);
112     }
113 }
114 
Check(bool bDoCheck)115 void wxMenuItem::Check(bool bDoCheck)
116 {
117     wxCHECK_RET( IsCheckable(), "only checkable items may be checked" );
118 
119     if ( m_isChecked != bDoCheck )
120     {
121         if ( m_buttonWidget )
122         {
123             wxASSERT_MSG( XtIsSubclass((Widget)m_buttonWidget,
124                                        xmToggleButtonGadgetClass),
125                           wxT("checkable menu item must be a toggle button") );
126 
127             XtVaSetValues((Widget)m_buttonWidget,
128                           XmNset, (Boolean)bDoCheck,
129                           NULL);
130         }
131 
132         wxMenuItemBase::Check(bDoCheck);
133     }
134 }
135 
136 // ----------------------------------------------------------------------------
137 // wxMenuItemBase
138 // ----------------------------------------------------------------------------
139 
New(wxMenu * parentMenu,int id,const wxString & name,const wxString & help,wxItemKind kind,wxMenu * subMenu)140 wxMenuItem *wxMenuItemBase::New(wxMenu *parentMenu,
141                                 int id,
142                                 const wxString& name,
143                                 const wxString& help,
144                                 wxItemKind kind,
145                                 wxMenu *subMenu)
146 {
147     return new wxMenuItem(parentMenu, id, name, help, kind, subMenu);
148 }
149 
150 // ----------------------------------------------------------------------------
151 // Motif-specific
152 // ----------------------------------------------------------------------------
153 
CreateItem(WXWidget menu,wxMenuBar * menuBar,wxMenu * topMenu,size_t index)154 void wxMenuItem::CreateItem (WXWidget menu, wxMenuBar * menuBar,
155                              wxMenu * topMenu, size_t index)
156 {
157     m_menuBar = menuBar;
158     m_topMenu = topMenu;
159 
160     if (GetId() == -3)
161     {
162         // Id=-3 identifies a Title item.
163         m_buttonWidget = (WXWidget) XtVaCreateManagedWidget
164             (wxStripMenuCodes(m_text),
165             xmLabelGadgetClass, (Widget) menu, NULL);
166     }
167     else if (!IsSeparator() && !m_subMenu)
168     {
169         wxString txt = m_text;
170 
171         if (m_text.IsEmpty())
172         {
173             wxASSERT_MSG(wxIsStockID(GetId()), wxT("A non-stock menu item with an empty label?"));
174             txt = wxGetStockLabel(GetId(), wxSTOCK_WITH_ACCELERATOR|wxSTOCK_WITH_MNEMONIC);
175         }
176 
177         wxString strName = wxStripMenuCodes(txt);
178         if (IsCheckable())
179         {
180             m_buttonWidget = (WXWidget) XtVaCreateManagedWidget (strName,
181                 xmToggleButtonGadgetClass, (Widget) menu,
182 #ifdef XmNpositionIndex
183                 XmNpositionIndex, index,
184 #endif
185                 NULL);
186             XtVaSetValues ((Widget) m_buttonWidget, XmNset, (Boolean) IsChecked(), NULL);
187         }
188         else
189             m_buttonWidget = (WXWidget) XtVaCreateManagedWidget (strName,
190             xmPushButtonGadgetClass, (Widget) menu,
191 #ifdef XmNpositionIndex
192             XmNpositionIndex, index,
193 #endif
194             NULL);
195         char mnem = wxFindMnemonic (m_text);
196         if (mnem != 0)
197             XtVaSetValues ((Widget) m_buttonWidget, XmNmnemonic, mnem, NULL);
198 
199         //// TODO: proper accelerator treatment. What does wxFindAccelerator
200         //// look for?
201         strName = m_text;
202         char *accel = wxFindAccelerator (strName);
203         if (accel)
204             XtVaSetValues ((Widget) m_buttonWidget, XmNaccelerator, accel, NULL);
205 
206         // TODO: What does this do?
207         XmString accel_str = wxFindAcceleratorText (strName);
208         if (accel_str)
209         {
210             XtVaSetValues ((Widget) m_buttonWidget, XmNacceleratorText, accel_str, NULL);
211             XmStringFree (accel_str);
212         }
213 
214         if (IsCheckable())
215             XtAddCallback ((Widget) m_buttonWidget,
216             XmNvalueChangedCallback,
217             (XtCallbackProc) wxMenuItemCallback,
218             (XtPointer) this);
219         else
220             XtAddCallback ((Widget) m_buttonWidget,
221             XmNactivateCallback,
222             (XtCallbackProc) wxMenuItemCallback,
223             (XtPointer) this);
224         XtAddCallback ((Widget) m_buttonWidget,
225             XmNarmCallback,
226             (XtCallbackProc) wxMenuItemArmCallback,
227             (XtPointer) this);
228         XtAddCallback ((Widget) m_buttonWidget,
229             XmNdisarmCallback,
230             (XtCallbackProc) wxMenuItemDisarmCallback,
231             (XtPointer) this);
232     }
233     else if (IsSeparator())
234     {
235         m_buttonWidget = (WXWidget) XtVaCreateManagedWidget ("separator",
236             xmSeparatorGadgetClass, (Widget) menu,
237 #ifndef XmNpositionIndex
238             XmNpositionIndex, index,
239 #endif
240             NULL);
241     }
242     else if (m_subMenu)
243     {
244         m_buttonWidget = m_subMenu->CreateMenu (menuBar, menu, topMenu, index, m_text, true);
245         m_subMenu->SetButtonWidget(m_buttonWidget);
246         XtAddCallback ((Widget) m_buttonWidget,
247             XmNcascadingCallback,
248             (XtCallbackProc) wxMenuItemArmCallback,
249             (XtPointer) this);
250     }
251     if (m_buttonWidget)
252         XtSetSensitive ((Widget) m_buttonWidget, (Boolean) IsEnabled());
253 }
254 
DestroyItem(bool full)255 void wxMenuItem::DestroyItem(bool full)
256 {
257     if (GetId() == -3)
258     {
259         ;      // Nothing
260 
261     }
262     else if (!m_text.empty() && !m_subMenu)
263     {
264         if (m_buttonWidget)
265         {
266             if (IsCheckable())
267                 XtRemoveCallback ((Widget) m_buttonWidget, XmNvalueChangedCallback,
268                 wxMenuItemCallback, (XtPointer) this);
269             else
270                 XtRemoveCallback ((Widget) m_buttonWidget, XmNactivateCallback,
271                 wxMenuItemCallback, (XtPointer) this);
272             XtRemoveCallback ((Widget) m_buttonWidget, XmNarmCallback,
273                 wxMenuItemArmCallback, (XtPointer) this);
274             XtRemoveCallback ((Widget) m_buttonWidget, XmNdisarmCallback,
275                 wxMenuItemDisarmCallback, (XtPointer) this);
276         }
277     }
278     else if (IsSeparator())
279     {
280         ;      // Nothing
281 
282     }
283     else if (GetSubMenu())
284     {
285         if (m_buttonWidget)
286         {
287             XtRemoveCallback ((Widget) m_buttonWidget, XmNcascadingCallback,
288                 wxMenuItemArmCallback, (XtPointer) this);
289         }
290         m_subMenu->DestroyMenu(full);
291         if (full)
292             m_buttonWidget = NULL;
293     }
294 
295     if (m_buttonWidget && full)
296     {
297         XtDestroyWidget ((Widget) m_buttonWidget);
298         m_buttonWidget = (WXWidget) 0;
299     }
300 }
301 
SetItemLabel(const wxString & label)302 void wxMenuItem::SetItemLabel(const wxString& label)
303 {
304     char mnem = wxFindMnemonic (label);
305     wxString label2 = wxStripMenuCodes(label);
306 
307     m_text = label;
308 
309     if (m_buttonWidget)
310     {
311         wxXmString label_str(label2);
312         XtVaSetValues ((Widget) m_buttonWidget,
313             XmNlabelString, label_str(),
314             NULL);
315         if (mnem != 0)
316             XtVaSetValues ((Widget) m_buttonWidget, XmNmnemonic, mnem, NULL);
317         char *accel = wxFindAccelerator (label2);
318         if (accel)
319             XtVaSetValues ((Widget) m_buttonWidget, XmNaccelerator, accel, NULL);
320 
321         XmString accel_str = wxFindAcceleratorText (label2);
322         if (accel_str)
323         {
324             XtVaSetValues ((Widget) m_buttonWidget, XmNacceleratorText, accel_str, NULL);
325             XmStringFree (accel_str);
326         }
327     }
328 }
329 
330 // ----------------------------------------------------------------------------
331 // Motif callbacks
332 // ----------------------------------------------------------------------------
333 
wxMenuItemCallback(Widget WXUNUSED (w),XtPointer clientData,XtPointer WXUNUSED (ptr))334 void wxMenuItemCallback (Widget WXUNUSED(w), XtPointer clientData,
335                          XtPointer WXUNUSED(ptr))
336 {
337     wxMenuItem *item = (wxMenuItem *) clientData;
338     if (item)
339     {
340         wxCommandEvent event(wxEVT_MENU, item->GetId());
341         event.SetInt( item->GetId() );
342 
343         if (item->IsCheckable())
344         {
345             Boolean isChecked = false;
346             XtVaGetValues ((Widget) item->GetButtonWidget(),
347                            XmNset, & isChecked,
348                            NULL);
349 
350             // only set the flag, don't actually check anything
351             item->wxMenuItemBase::Check(isChecked);
352             event.SetInt(isChecked);
353         }
354 
355         if (item->GetMenuBar() && item->GetMenuBar()->GetMenuBarFrame())
356         {
357             event.SetEventObject(item->GetMenuBar()->GetMenuBarFrame());
358 
359             item->GetMenuBar()->GetMenuBarFrame()
360                 ->HandleWindowEvent(event);
361         }
362         // this is the child of a popup menu
363         else if (item->GetTopMenu())
364         {
365             event.SetEventObject(item->GetTopMenu());
366 
367             item->GetTopMenu()->ProcessCommand (event);
368 
369             // Since PopupMenu under Motif still grab right mouse
370             // button events after it was closed, we need to delete
371             // the associated widgets to allow next PopUpMenu to
372             // appear; this needs to be done there because doing it in
373             // a WorkProc as before may cause crashes if a menu item causes
374             // the parent window of the menu to be destroyed
375             item->GetTopMenu()->DestroyWidgetAndDetach();
376         }
377     }
378 }
379 
wxMenuItemArmCallback(Widget WXUNUSED (w),XtPointer clientData,XtPointer WXUNUSED (ptr))380 void wxMenuItemArmCallback (Widget WXUNUSED(w), XtPointer clientData,
381                        XtPointer WXUNUSED(ptr))
382 {
383     wxMenuItem *item = (wxMenuItem *) clientData;
384     if (item)
385     {
386         if (item->GetMenuBar() && item->GetMenuBar()->GetMenuBarFrame())
387         {
388             wxMenuEvent menuEvent(wxEVT_MENU_HIGHLIGHT, item->GetId());
389             menuEvent.SetEventObject(item->GetMenuBar()->GetMenuBarFrame());
390 
391             item->GetMenuBar()->GetMenuBarFrame()
392                 ->HandleWindowEvent(menuEvent);
393         }
394     }
395 }
396 
397 void
wxMenuItemDisarmCallback(Widget WXUNUSED (w),XtPointer clientData,XtPointer WXUNUSED (ptr))398 wxMenuItemDisarmCallback (Widget WXUNUSED(w), XtPointer clientData,
399                           XtPointer WXUNUSED(ptr))
400 {
401     wxMenuItem *item = (wxMenuItem *) clientData;
402     if (item)
403     {
404         if (item->GetMenuBar() && item->GetMenuBar()->GetMenuBarFrame())
405         {
406             // TODO: not sure this is correct, since -1 means something
407             // special to event system
408             wxMenuEvent menuEvent(wxEVT_MENU_HIGHLIGHT, -1);
409             menuEvent.SetEventObject(item->GetMenuBar()->GetMenuBarFrame());
410 
411             item->GetMenuBar()->GetMenuBarFrame()
412                 ->HandleWindowEvent(menuEvent);
413         }
414     }
415 }
416