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
= 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(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(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
,
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
, menuIndex
,
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(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
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
);
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
);