1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/motif/menu.cpp 
   3 // Purpose:     wxMenu, wxMenuBar, wxMenuItem 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  30     #include "wx/settings.h" 
  31     #include "wx/menuitem.h" 
  35 #pragma message disable nosimpint 
  38 #include <Xm/LabelG.h> 
  39 #include <Xm/CascadeBG.h> 
  40 #include <Xm/CascadeB.h> 
  41 #include <Xm/SeparatoG.h> 
  42 #include <Xm/PushBG.h> 
  43 #include <Xm/ToggleB.h> 
  44 #include <Xm/ToggleBG.h> 
  45 #include <Xm/RowColumn.h> 
  47 #pragma message enable nosimpint 
  50 #include "wx/motif/private.h" 
  52 // other standard headers 
  55 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
) 
  56 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxEvtHandler
) 
  58 // ============================================================================ 
  60 // ============================================================================ 
  62 // ---------------------------------------------------------------------------- 
  64 // ---------------------------------------------------------------------------- 
  66 // Construct a menu with optional title (then use append) 
  69     // Motif-specific members 
  71     m_menuWidget 
= (WXWidget
) NULL
; 
  72     m_popupShell 
= (WXWidget
) NULL
; 
  73     m_buttonWidget 
= (WXWidget
) NULL
; 
  75     m_topLevelMenu  
= (wxMenu
*) NULL
; 
  76     m_ownedByMenuBar 
= false; 
  78     if ( !m_title
.empty() ) 
  85 // The wxWindow destructor will take care of deleting the submenus. 
  96     // Not sure if this is right 
  97     if (m_menuParent 
&& m_menuBar
) 
 109 // function appends a new item or submenu to the menu 
 110 wxMenuItem
* wxMenu::DoAppend(wxMenuItem 
*pItem
) 
 112     return DoInsert(GetMenuItemCount(), pItem
); 
 115 wxMenuItem 
*wxMenu::DoRemove(wxMenuItem 
*item
) 
 117     item
->DestroyItem(true); 
 119     return wxMenuBase::DoRemove(item
); 
 122 wxMenuItem
* wxMenu::DoInsert(size_t pos
, wxMenuItem 
*item
) 
 126         // this is a dynamic Append 
 127 #ifndef XmNpositionIndex 
 128     wxCHECK_MSG( pos 
== GetMenuItemCount(), -1, wxT("insert not implemented")); 
 130         item
->CreateItem(m_menuWidget
, GetMenuBar(), m_topLevelMenu
, pos
); 
 133     if ( item
->IsSubMenu() ) 
 135         item
->GetSubMenu()->m_topLevelMenu 
= m_topLevelMenu
; 
 138     return pos 
== GetMenuItemCount() ? wxMenuBase::DoAppend(item
) : 
 139                                        wxMenuBase::DoInsert(pos
, item
); 
 142 void wxMenu::SetTitle(const wxString
& label
) 
 146     wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
 150     wxMenuItem 
*item 
= node
->GetData (); 
 151     Widget widget 
= (Widget
) item
->GetButtonWidget(); 
 155     wxXmString 
title_str(label
); 
 156     XtVaSetValues(widget
, 
 157                   XmNlabelString
, title_str(), 
 161 bool wxMenu::ProcessCommand(wxCommandEvent 
& event
) 
 163     // Try the menu's event handler first 
 164     wxEvtHandler 
* const handler 
= GetEventHandler(); 
 165     bool processed 
= handler 
? handler
->SafelyProcessEvent(event
) : false; 
 167     // Try the window the menu was popped up from (and up 
 168     // through the hierarchy) 
 169     if ( !processed 
&& GetInvokingWindow()) 
 170         processed 
= GetInvokingWindow()->HandleWindowEvent(event
); 
 175 // ---------------------------------------------------------------------------- 
 177 // ---------------------------------------------------------------------------- 
 179 void wxMenuBar::Init() 
 181     m_eventHandler 
= this; 
 182     m_menuBarFrame 
= NULL
; 
 183     m_mainWidget 
= (WXWidget
) NULL
; 
 186 wxMenuBar::wxMenuBar(size_t n
, wxMenu 
*menus
[], const wxArrayString
& titles
, long WXUNUSED(style
)) 
 188     wxASSERT( n 
== titles
.GetCount() ); 
 193     for ( size_t i 
= 0; i 
< n
; i
++ ) 
 194         m_menus
.Append(menus
[i
]); 
 197 wxMenuBar::wxMenuBar(size_t n
, wxMenu 
*menus
[], const wxString titles
[], long WXUNUSED(style
)) 
 201     for ( size_t i 
= 0; i 
< n
; i
++ ) 
 203         m_menus
.Append(menus
[i
]); 
 204         m_titles
.Add(titles
[i
]); 
 208 wxMenuBar::~wxMenuBar() 
 210     // nothing to do: wxMenuBarBase will delete the menus 
 213 void wxMenuBar::EnableTop(size_t WXUNUSED(pos
), bool WXUNUSED(flag
)) 
 215   //    wxFAIL_MSG("TODO"); 
 216 //  wxLogWarning("wxMenuBar::EnableTop not yet implemented."); 
 219 void wxMenuBar::SetMenuLabel(size_t pos
, const wxString
& label
) 
 221     wxMenu 
*menu 
= GetMenu(pos
); 
 225     Widget w 
= (Widget
)menu
->GetButtonWidget(); 
 228         wxXmString 
label_str(label
); 
 231                       XmNlabelString
, label_str(), 
 234     m_titles
[pos
] = label
; 
 237 wxString 
wxMenuBar::GetMenuLabel(size_t pos
) const 
 239     wxCHECK_MSG( pos 
< GetMenuCount(), wxEmptyString
, 
 240                  wxT("invalid menu index in wxMenuBar::GetMenuLabel") ); 
 241     return m_titles
[pos
]; 
 244 bool wxMenuBar::Append(wxMenu 
* menu
, const wxString
& title
) 
 246     return Insert(GetMenuCount(), menu
, title
); 
 249 bool wxMenuBar::Insert(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 251     wxCHECK_MSG( pos 
<= GetMenuCount(), false, wxT("invalid position") ); 
 252     wxCHECK_MSG( menu
, false, wxT("invalid menu") ); 
 253     wxCHECK_MSG( !menu
->GetParent() && !menu
->GetButtonWidget(), false, 
 254                  wxT("menu already appended") ); 
 256     if ( m_menuBarFrame 
) 
 258         WXWidget w 
= menu
->CreateMenu(this, GetMainWidget(), menu
, 
 260         wxCHECK_MSG( w
, false, wxT("failed to create menu") ); 
 261         menu
->SetButtonWidget(w
); 
 264     m_titles
.Insert(title
, pos
); 
 266     return wxMenuBarBase::Insert(pos
, menu
, title
); 
 269 wxMenu 
*wxMenuBar::Replace(size_t pos
, wxMenu 
*menu
, const wxString
& title
) 
 271     if ( !wxMenuBarBase::Replace(pos
, menu
, title
) ) 
 274     wxFAIL_MSG(wxT("TODO")); 
 279 wxMenu 
*wxMenuBar::Remove(size_t pos
) 
 281     wxMenu 
*menu 
= wxMenuBarBase::Remove(pos
); 
 285     if ( m_menuBarFrame 
) 
 286         menu
->DestroyMenu(true); 
 288     menu
->SetMenuBar(NULL
); 
 290     m_titles
.RemoveAt(pos
); 
 295 // Find the menu menuString, item itemString, and return the item id. 
 296 // Returns -1 if none found. 
 297 int wxMenuBar::FindMenuItem(const wxString
& menuString
, const wxString
& itemString
) const 
 299     const wxString stripped 
= wxStripMenuCodes(menuString
); 
 301     size_t menuCount 
= GetMenuCount(); 
 302     for (size_t i 
= 0; i 
< menuCount
; i
++) 
 304         if ( wxStripMenuCodes(m_titles
[i
]) == stripped 
) 
 305             return m_menus
.Item(i
)->GetData()->FindItem (itemString
); 
 310 wxMenuItem 
*wxMenuBar::FindItem(int id
, wxMenu 
** itemMenu
) const 
 315     size_t menuCount 
= GetMenuCount(); 
 316     for (size_t i 
= 0; i 
< menuCount
; i
++) 
 318         wxMenuItem 
*item 
= m_menus
.Item(i
)->GetData()->FindItem(id
, itemMenu
); 
 319         if (item
) return item
; 
 326 bool wxMenuBar::CreateMenuBar(wxFrame
* parent
) 
 328     m_parent 
= parent
; // bleach... override it! 
 334         XtVaSetValues((Widget
) parent
->GetMainWidget(), XmNmenuBar
, (Widget
) m_mainWidget
, NULL
); 
 336         if (!XtIsManaged((Widget) m_mainWidget)) 
 337         XtManageChild((Widget) m_mainWidget); 
 339         XtMapWidget((Widget
) m_mainWidget
); 
 343     Widget menuBarW 
= XmCreateMenuBar ((Widget
) parent
->GetMainWidget(), 
 344                                        wxMOTIF_STR("MenuBar"), NULL
, 0); 
 345     m_mainWidget 
= (WXWidget
) menuBarW
; 
 347     size_t menuCount 
= GetMenuCount(); 
 348     for (size_t i 
= 0; i 
< menuCount
; i
++) 
 350         wxMenu 
*menu 
= GetMenu(i
); 
 351         wxString 
title(m_titles
[i
]); 
 352         menu
->SetButtonWidget(menu
->CreateMenu (this, menuBarW
, menu
, i
, title
, true)); 
 354         if (strcmp (wxStripMenuCodes(title
), "Help") == 0) 
 355             XtVaSetValues ((Widget
) menuBarW
, XmNmenuHelpWidget
, (Widget
) menu
->GetButtonWidget(), NULL
); 
 357         // tear off menu support 
 358 #if (XmVersion >= 1002) 
 359         if ( menu
->IsTearOff() ) 
 361             XtVaSetValues(GetWidget(menu
), 
 362                           XmNtearOffModel
, XmTEAR_OFF_ENABLED
, 
 364             Widget tearOff 
= XmGetTearOffControl(GetWidget(menu
)); 
 365             wxDoChangeForegroundColour((Widget
) tearOff
, m_foregroundColour
); 
 366             wxDoChangeBackgroundColour((Widget
) tearOff
, m_backgroundColour
, true); 
 373     XtVaSetValues((Widget
) parent
->GetMainWidget(), XmNmenuBar
, (Widget
) m_mainWidget
, NULL
); 
 374     XtRealizeWidget ((Widget
) menuBarW
); 
 375     XtManageChild ((Widget
) menuBarW
); 
 376     SetMenuBarFrame(parent
); 
 381 // Destroy menubar, but keep data structures intact so we can recreate it. 
 382 bool wxMenuBar::DestroyMenuBar() 
 386         SetMenuBarFrame((wxFrame
*) NULL
); 
 390     XtUnmanageChild ((Widget
) m_mainWidget
); 
 391     XtUnrealizeWidget ((Widget
) m_mainWidget
); 
 393     size_t menuCount 
= GetMenuCount(); 
 394     for (size_t i 
= 0; i 
< menuCount
; i
++) 
 396         wxMenu 
*menu 
= GetMenu(i
); 
 397         menu
->DestroyMenu(true); 
 400     XtDestroyWidget((Widget
) m_mainWidget
); 
 401     m_mainWidget 
= (WXWidget
) 0; 
 403     SetMenuBarFrame((wxFrame
*) NULL
); 
 408 // Since PopupMenu under Motif stills grab right mouse button events 
 409 // after it was closed, we need to delete the associated widgets to 
 410 // allow next PopUpMenu to appear... 
 411 void wxMenu::DestroyWidgetAndDetach() 
 415         wxMenu 
*menuParent 
= GetParent(); 
 418             wxMenuItemList::compatibility_iterator node 
= menuParent
->GetMenuItems().GetFirst(); 
 421                 if ( node
->GetData()->GetSubMenu() == this ) 
 423                     delete node
->GetData(); 
 424                     menuParent
->GetMenuItems().Erase(node
); 
 429                 node 
= node
->GetNext(); 
 436     // Mark as no longer popped up 
 441 * Create a popup or pulldown menu. 
 442 * Submenus of a popup will be pulldown. 
 446 WXWidget 
wxMenu::CreateMenu (wxMenuBar 
* menuBar
, 
 449                              size_t WXUNUSED(index
), 
 450                              const wxString
& title
, 
 453     Widget menu 
= (Widget
) 0; 
 454     Widget buttonWidget 
= (Widget
) 0; 
 455     Display
* dpy 
= XtDisplay((Widget
)parent
); 
 457     XtSetArg (args
[0], XmNnumColumns
, m_numColumns
); 
 458     XtSetArg (args
[1], XmNpacking
, (m_numColumns 
> 1) ? XmPACK_COLUMN 
: XmPACK_TIGHT
); 
 463             m_font 
= menuBar
->GetFont(); 
 464         else if ( GetInvokingWindow() ) 
 465             m_font 
= GetInvokingWindow()->GetFont(); 
 468     XtSetArg (args
[2], (String
)wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
) ); 
 472         menu 
= XmCreatePopupMenu ((Widget
) parent
, wxMOTIF_STR("popup"), args
, 3); 
 476             (XtCallbackProc
)wxMenuPopdownCallback
, 
 482         char mnem 
= wxFindMnemonic (title
); 
 483         menu 
= XmCreatePulldownMenu ((Widget
) parent
, wxMOTIF_STR("pulldown"), args
, 3); 
 485         wxString 
title2(wxStripMenuCodes(title
)); 
 486         wxXmString 
label_str(title2
); 
 487         buttonWidget 
= XtVaCreateManagedWidget(title2
, 
 489             xmCascadeButtonGadgetClass
, (Widget
) parent
, 
 491             xmCascadeButtonWidgetClass
, (Widget
) parent
, 
 493             XmNlabelString
, label_str(), 
 495             (String
)wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
), 
 496             XmNpositionIndex
, index
, 
 500             XtVaSetValues (buttonWidget
, XmNmnemonic
, mnem
, NULL
); 
 503     m_menuWidget 
= (WXWidget
) menu
; 
 505     m_topLevelMenu 
= topMenu
; 
 508     for ( wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
 510           node 
= node
->GetNext(), ++i 
) 
 512         wxMenuItem 
*item 
= node
->GetData(); 
 514         item
->CreateItem(menu
, menuBar
, topMenu
, i
); 
 522 // Destroys the Motif implementation of the menu, 
 523 // but maintains the wxWidgets data structures so we can 
 524 // do a CreateMenu again. 
 525 void wxMenu::DestroyMenu (bool full
) 
 527     for ( wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
 529           node 
= node
->GetNext() ) 
 531         wxMenuItem 
*item 
= node
->GetData(); 
 532         item
->SetMenuBar((wxMenuBar
*) NULL
); 
 534         item
->DestroyItem(full
); 
 541             XtVaSetValues((Widget
) m_buttonWidget
, XmNsubMenuId
, NULL
, NULL
); 
 542             XtDestroyWidget ((Widget
) m_buttonWidget
); 
 543             m_buttonWidget 
= (WXWidget
) 0; 
 546     if (m_menuWidget 
&& full
) 
 548         XtDestroyWidget((Widget
) m_menuWidget
); 
 549         m_menuWidget 
= (WXWidget
) NULL
; 
 553 WXWidget 
wxMenu::FindMenuItem (int id
, wxMenuItem 
** it
) const 
 558             *it 
= (wxMenuItem
*) NULL
; 
 559         return m_buttonWidget
; 
 562     for ( wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
 564           node 
= node
->GetNext() ) 
 566         wxMenuItem 
*item 
= node
->GetData (); 
 567         if (item
->GetId() == id
) 
 571             return item
->GetButtonWidget(); 
 574         if (item
->GetSubMenu()) 
 576             WXWidget w 
= item
->GetSubMenu()->FindMenuItem (id
, it
); 
 585         *it 
= (wxMenuItem
*) NULL
; 
 586     return (WXWidget
) NULL
; 
 589 void wxMenu::SetBackgroundColour(const wxColour
& col
) 
 591     m_backgroundColour 
= col
; 
 595         wxDoChangeBackgroundColour(m_menuWidget
, (wxColour
&) col
); 
 597         wxDoChangeBackgroundColour(m_buttonWidget
, (wxColour
&) col
, true); 
 599     for ( wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
 601           node 
= node
->GetNext() ) 
 603         wxMenuItem
* item 
= node
->GetData(); 
 604         if (item
->GetButtonWidget()) 
 606             // This crashes because it uses gadgets 
 607             //            wxDoChangeBackgroundColour(item->GetButtonWidget(), (wxColour&) col, true); 
 609         if (item
->GetSubMenu()) 
 610             item
->GetSubMenu()->SetBackgroundColour((wxColour
&) col
); 
 614 void wxMenu::SetForegroundColour(const wxColour
& col
) 
 616     m_foregroundColour 
= col
; 
 620         wxDoChangeForegroundColour(m_menuWidget
, (wxColour
&) col
); 
 622         wxDoChangeForegroundColour(m_buttonWidget
, (wxColour
&) col
); 
 624     for ( wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
 626           node 
= node
->GetNext() ) 
 628         wxMenuItem
* item 
= node
->GetData(); 
 629         if (item
->GetButtonWidget()) 
 631             // This crashes because it uses gadgets 
 632             //            wxDoChangeForegroundColour(item->GetButtonWidget(), (wxColour&) col); 
 634         if (item
->GetSubMenu()) 
 635             item
->GetSubMenu()->SetForegroundColour((wxColour
&) col
); 
 639 void wxMenu::ChangeFont(bool keepOriginalSize
) 
 641     // Lesstif 0.87 hangs here, but 0.93 does not; MBN: sometimes it does 
 642 #if !wxCHECK_LESSTIF() // || wxCHECK_LESSTIF_VERSION( 0, 93 ) 
 643     if (!m_font
.Ok() || !m_menuWidget
) 
 646     Display
* dpy 
= XtDisplay((Widget
) m_menuWidget
); 
 648     XtVaSetValues ((Widget
) m_menuWidget
, 
 649                    wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
), 
 653         XtVaSetValues ((Widget
) m_buttonWidget
, 
 654                        wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
), 
 658     for ( wxMenuItemList::compatibility_iterator node 
= GetMenuItems().GetFirst(); 
 660           node 
= node
->GetNext() ) 
 662         wxMenuItem
* item 
= node
->GetData(); 
 663         if (m_menuWidget 
&& item
->GetButtonWidget() && m_font
.Ok()) 
 665             XtVaSetValues ((Widget
) item
->GetButtonWidget(), 
 666                            wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
), 
 669         if (item
->GetSubMenu()) 
 670             item
->GetSubMenu()->ChangeFont(keepOriginalSize
); 
 673     wxUnusedVar(keepOriginalSize
); 
 677 void wxMenu::SetFont(const wxFont
& font
) 
 683 bool wxMenuBar::SetBackgroundColour(const wxColour
& col
) 
 685     if (!wxWindowBase::SetBackgroundColour(col
)) 
 690         wxDoChangeBackgroundColour(m_mainWidget
, (wxColour
&) col
); 
 692     size_t menuCount 
= GetMenuCount(); 
 693     for (size_t i 
= 0; i 
< menuCount
; i
++) 
 694         m_menus
.Item(i
)->GetData()->SetBackgroundColour((wxColour
&) col
); 
 699 bool wxMenuBar::SetForegroundColour(const wxColour
& col
) 
 701     if (!wxWindowBase::SetForegroundColour(col
)) 
 706         wxDoChangeForegroundColour(m_mainWidget
, (wxColour
&) col
); 
 708     size_t menuCount 
= GetMenuCount(); 
 709     for (size_t i 
= 0; i 
< menuCount
; i
++) 
 710         m_menus
.Item(i
)->GetData()->SetForegroundColour((wxColour
&) col
); 
 715 void wxMenuBar::ChangeFont(bool WXUNUSED(keepOriginalSize
)) 
 717     // Nothing to do for menubar, fonts are kept in wxMenus 
 720 bool wxMenuBar::SetFont(const wxFont
& font
) 
 725     size_t menuCount 
= GetMenuCount(); 
 726     for (size_t i 
= 0; i 
< menuCount
; i
++) 
 727         m_menus
.Item(i
)->GetData()->SetFont(font
);