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