1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/motif/menu.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Julian Smart
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
29 #include "wx/settings.h"
30 #include "wx/menuitem.h"
34 #pragma message disable nosimpint
37 #include <Xm/LabelG.h>
38 #include <Xm/CascadeBG.h>
39 #include <Xm/CascadeB.h>
40 #include <Xm/SeparatoG.h>
41 #include <Xm/PushBG.h>
42 #include <Xm/ToggleB.h>
43 #include <Xm/ToggleBG.h>
44 #include <Xm/RowColumn.h>
46 #pragma message enable nosimpint
49 #include "wx/motif/private.h"
51 // other standard headers
54 // ============================================================================
56 // ============================================================================
58 // ----------------------------------------------------------------------------
60 // ----------------------------------------------------------------------------
62 // Construct a menu with optional title (then use append)
65 // Motif-specific members
67 m_menuWidget
= (WXWidget
) NULL
;
68 m_popupShell
= (WXWidget
) NULL
;
69 m_buttonWidget
= (WXWidget
) NULL
;
71 m_topLevelMenu
= NULL
;
72 m_ownedByMenuBar
= false;
74 if ( !m_title
.empty() )
81 // The wxWindow destructor will take care of deleting the submenus.
92 // Not sure if this is right
93 if (m_menuParent
&& m_menuBar
)
105 // function appends a new item or submenu to the menu
106 wxMenuItem
* wxMenu::DoAppend(wxMenuItem
*pItem
)
108 return DoInsert(GetMenuItemCount(), pItem
);
111 wxMenuItem
*wxMenu::DoRemove(wxMenuItem
*item
)
113 item
->DestroyItem(true);
115 return wxMenuBase::DoRemove(item
);
118 wxMenuItem
* wxMenu::DoInsert(size_t pos
, wxMenuItem
*item
)
122 // this is a dynamic Append
123 #ifndef XmNpositionIndex
124 wxCHECK_MSG( pos
== GetMenuItemCount(), -1, wxT("insert not implemented"));
126 item
->CreateItem(m_menuWidget
, GetMenuBar(), m_topLevelMenu
, pos
);
129 if ( item
->IsSubMenu() )
131 item
->GetSubMenu()->m_topLevelMenu
= m_topLevelMenu
;
134 return pos
== GetMenuItemCount() ? wxMenuBase::DoAppend(item
) :
135 wxMenuBase::DoInsert(pos
, item
);
138 void wxMenu::SetTitle(const wxString
& label
)
142 wxMenuItemList::compatibility_iterator node
= GetMenuItems().GetFirst();
146 wxMenuItem
*item
= node
->GetData ();
147 Widget widget
= (Widget
) item
->GetButtonWidget();
151 wxXmString
title_str(label
);
152 XtVaSetValues(widget
,
153 XmNlabelString
, title_str(),
157 bool wxMenu::ProcessCommand(wxCommandEvent
& event
)
159 // Try the menu's event handler first
160 wxEvtHandler
* const handler
= GetEventHandler();
161 bool processed
= handler
? handler
->SafelyProcessEvent(event
) : false;
163 // Try the window the menu was popped up from (and up
164 // through the hierarchy)
165 if ( !processed
&& GetInvokingWindow())
166 processed
= GetInvokingWindow()->HandleWindowEvent(event
);
171 // ----------------------------------------------------------------------------
173 // ----------------------------------------------------------------------------
175 void wxMenuBar::Init()
177 m_eventHandler
= this;
178 m_menuBarFrame
= NULL
;
179 m_mainWidget
= (WXWidget
) NULL
;
182 wxMenuBar::wxMenuBar(size_t n
, wxMenu
*menus
[], const wxArrayString
& titles
, long WXUNUSED(style
))
184 wxASSERT( n
== titles
.GetCount() );
189 for ( size_t i
= 0; i
< n
; i
++ )
190 m_menus
.Append(menus
[i
]);
193 wxMenuBar::wxMenuBar(size_t n
, wxMenu
*menus
[], const wxString titles
[], long WXUNUSED(style
))
197 for ( size_t i
= 0; i
< n
; i
++ )
199 m_menus
.Append(menus
[i
]);
200 m_titles
.Add(titles
[i
]);
204 wxMenuBar::~wxMenuBar()
206 // nothing to do: wxMenuBarBase will delete the menus
209 void wxMenuBar::EnableTop(size_t WXUNUSED(pos
), bool WXUNUSED(flag
))
211 // wxFAIL_MSG("TODO");
212 // wxLogWarning("wxMenuBar::EnableTop not yet implemented.");
215 void wxMenuBar::SetMenuLabel(size_t pos
, const wxString
& label
)
217 wxMenu
*menu
= GetMenu(pos
);
221 Widget w
= (Widget
)menu
->GetButtonWidget();
224 wxXmString
label_str(label
);
227 XmNlabelString
, label_str(),
230 m_titles
[pos
] = label
;
233 wxString
wxMenuBar::GetMenuLabel(size_t pos
) const
235 wxCHECK_MSG( pos
< GetMenuCount(), wxEmptyString
,
236 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
237 return m_titles
[pos
];
240 bool wxMenuBar::Append(wxMenu
* menu
, const wxString
& title
)
242 return Insert(GetMenuCount(), menu
, title
);
245 bool wxMenuBar::Insert(size_t pos
, wxMenu
*menu
, const wxString
& title
)
247 wxCHECK_MSG( pos
<= GetMenuCount(), false, wxT("invalid position") );
248 wxCHECK_MSG( menu
, false, wxT("invalid menu") );
249 wxCHECK_MSG( !menu
->GetParent() && !menu
->GetButtonWidget(), false,
250 wxT("menu already appended") );
252 if ( m_menuBarFrame
)
254 WXWidget w
= menu
->CreateMenu(this, GetMainWidget(), menu
,
256 wxCHECK_MSG( w
, false, wxT("failed to create menu") );
257 menu
->SetButtonWidget(w
);
260 m_titles
.Insert(title
, pos
);
262 return wxMenuBarBase::Insert(pos
, menu
, title
);
265 wxMenu
*wxMenuBar::Replace(size_t pos
, wxMenu
*menu
, const wxString
& title
)
267 if ( !wxMenuBarBase::Replace(pos
, menu
, title
) )
270 wxFAIL_MSG(wxT("TODO"));
275 wxMenu
*wxMenuBar::Remove(size_t pos
)
277 wxMenu
*menu
= wxMenuBarBase::Remove(pos
);
281 if ( m_menuBarFrame
)
282 menu
->DestroyMenu(true);
284 menu
->SetMenuBar(NULL
);
286 m_titles
.RemoveAt(pos
);
291 // Find the menu menuString, item itemString, and return the item id.
292 // Returns -1 if none found.
293 int wxMenuBar::FindMenuItem(const wxString
& menuString
, const wxString
& itemString
) const
295 const wxString stripped
= wxStripMenuCodes(menuString
);
297 size_t menuCount
= GetMenuCount();
298 for (size_t i
= 0; i
< menuCount
; i
++)
300 if ( wxStripMenuCodes(m_titles
[i
]) == stripped
)
301 return m_menus
.Item(i
)->GetData()->FindItem (itemString
);
306 wxMenuItem
*wxMenuBar::FindItem(int id
, wxMenu
** itemMenu
) const
311 size_t menuCount
= GetMenuCount();
312 for (size_t i
= 0; i
< menuCount
; i
++)
314 wxMenuItem
*item
= m_menus
.Item(i
)->GetData()->FindItem(id
, itemMenu
);
315 if (item
) return item
;
322 bool wxMenuBar::CreateMenuBar(wxFrame
* parent
)
324 m_parent
= parent
; // bleach... override it!
330 XtVaSetValues((Widget
) parent
->GetMainWidget(), XmNmenuBar
, (Widget
) m_mainWidget
, NULL
);
332 if (!XtIsManaged((Widget) m_mainWidget))
333 XtManageChild((Widget) m_mainWidget);
335 XtMapWidget((Widget
) m_mainWidget
);
339 Widget menuBarW
= XmCreateMenuBar ((Widget
) parent
->GetMainWidget(),
340 wxMOTIF_STR("MenuBar"), NULL
, 0);
341 m_mainWidget
= (WXWidget
) menuBarW
;
343 size_t menuCount
= GetMenuCount();
344 for (size_t i
= 0; i
< menuCount
; i
++)
346 wxMenu
*menu
= GetMenu(i
);
347 wxString
title(m_titles
[i
]);
348 menu
->SetButtonWidget(menu
->CreateMenu (this, menuBarW
, menu
, i
, title
, true));
350 if (strcmp (wxStripMenuCodes(title
), "Help") == 0)
351 XtVaSetValues ((Widget
) menuBarW
, XmNmenuHelpWidget
, (Widget
) menu
->GetButtonWidget(), NULL
);
353 // tear off menu support
354 #if (XmVersion >= 1002)
355 if ( menu
->IsTearOff() )
357 XtVaSetValues(GetWidget(menu
),
358 XmNtearOffModel
, XmTEAR_OFF_ENABLED
,
360 Widget tearOff
= XmGetTearOffControl(GetWidget(menu
));
361 wxDoChangeForegroundColour((Widget
) tearOff
, m_foregroundColour
);
362 wxDoChangeBackgroundColour((Widget
) tearOff
, m_backgroundColour
, true);
369 XtVaSetValues((Widget
) parent
->GetMainWidget(), XmNmenuBar
, (Widget
) m_mainWidget
, NULL
);
370 XtRealizeWidget ((Widget
) menuBarW
);
371 XtManageChild ((Widget
) menuBarW
);
372 SetMenuBarFrame(parent
);
377 // Destroy menubar, but keep data structures intact so we can recreate it.
378 bool wxMenuBar::DestroyMenuBar()
382 SetMenuBarFrame(NULL
);
386 XtUnmanageChild ((Widget
) m_mainWidget
);
387 XtUnrealizeWidget ((Widget
) m_mainWidget
);
389 size_t menuCount
= GetMenuCount();
390 for (size_t i
= 0; i
< menuCount
; i
++)
392 wxMenu
*menu
= GetMenu(i
);
393 menu
->DestroyMenu(true);
396 XtDestroyWidget((Widget
) m_mainWidget
);
397 m_mainWidget
= (WXWidget
) 0;
399 SetMenuBarFrame(NULL
);
404 // Since PopupMenu under Motif stills grab right mouse button events
405 // after it was closed, we need to delete the associated widgets to
406 // allow next PopUpMenu to appear...
407 void wxMenu::DestroyWidgetAndDetach()
411 wxMenu
*menuParent
= GetParent();
414 wxMenuItemList::compatibility_iterator node
= menuParent
->GetMenuItems().GetFirst();
417 if ( node
->GetData()->GetSubMenu() == this )
419 delete node
->GetData();
420 menuParent
->GetMenuItems().Erase(node
);
425 node
= node
->GetNext();
432 // Mark as no longer popped up
437 * Create a popup or pulldown menu.
438 * Submenus of a popup will be pulldown.
442 WXWidget
wxMenu::CreateMenu (wxMenuBar
* menuBar
,
446 const wxString
& title
,
449 Widget menu
= (Widget
) 0;
450 Widget buttonWidget
= (Widget
) 0;
451 Display
* dpy
= XtDisplay((Widget
)parent
);
453 XtSetArg (args
[0], XmNnumColumns
, m_numColumns
);
454 XtSetArg (args
[1], XmNpacking
, (m_numColumns
> 1) ? XmPACK_COLUMN
: XmPACK_TIGHT
);
456 if ( !m_font
.IsOk() )
459 m_font
= menuBar
->GetFont();
460 else if ( GetInvokingWindow() )
461 m_font
= GetInvokingWindow()->GetFont();
464 XtSetArg (args
[2], (String
)wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
) );
468 menu
= XmCreatePopupMenu ((Widget
) parent
, wxMOTIF_STR("popup"), args
, 3);
472 (XtCallbackProc
)wxMenuPopdownCallback
,
478 char mnem
= wxFindMnemonic (title
);
479 menu
= XmCreatePulldownMenu ((Widget
) parent
, wxMOTIF_STR("pulldown"), args
, 3);
481 wxString
title2(wxStripMenuCodes(title
));
482 wxXmString
label_str(title2
);
483 buttonWidget
= XtVaCreateManagedWidget(title2
,
485 xmCascadeButtonGadgetClass
, (Widget
) parent
,
487 xmCascadeButtonWidgetClass
, (Widget
) parent
,
489 XmNlabelString
, label_str(),
491 (String
)wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
),
492 XmNpositionIndex
, menuIndex
,
496 XtVaSetValues (buttonWidget
, XmNmnemonic
, mnem
, NULL
);
499 m_menuWidget
= (WXWidget
) menu
;
501 m_topLevelMenu
= topMenu
;
504 for ( wxMenuItemList::compatibility_iterator node
= GetMenuItems().GetFirst();
506 node
= node
->GetNext(), ++i
)
508 wxMenuItem
*item
= node
->GetData();
510 item
->CreateItem(menu
, menuBar
, topMenu
, i
);
518 // Destroys the Motif implementation of the menu,
519 // but maintains the wxWidgets data structures so we can
520 // do a CreateMenu again.
521 void wxMenu::DestroyMenu (bool full
)
523 for ( wxMenuItemList::compatibility_iterator node
= GetMenuItems().GetFirst();
525 node
= node
->GetNext() )
527 wxMenuItem
*item
= node
->GetData();
528 item
->SetMenuBar(NULL
);
530 item
->DestroyItem(full
);
537 XtVaSetValues((Widget
) m_buttonWidget
, XmNsubMenuId
, NULL
, NULL
);
538 XtDestroyWidget ((Widget
) m_buttonWidget
);
539 m_buttonWidget
= (WXWidget
) 0;
542 if (m_menuWidget
&& full
)
544 XtDestroyWidget((Widget
) m_menuWidget
);
545 m_menuWidget
= (WXWidget
) NULL
;
549 WXWidget
wxMenu::FindMenuItem (int id
, wxMenuItem
** it
) const
555 return m_buttonWidget
;
558 for ( wxMenuItemList::compatibility_iterator node
= GetMenuItems().GetFirst();
560 node
= node
->GetNext() )
562 wxMenuItem
*item
= node
->GetData ();
563 if (item
->GetId() == id
)
567 return item
->GetButtonWidget();
570 if (item
->GetSubMenu())
572 WXWidget w
= item
->GetSubMenu()->FindMenuItem (id
, it
);
582 return (WXWidget
) NULL
;
585 void wxMenu::SetBackgroundColour(const wxColour
& col
)
587 m_backgroundColour
= col
;
591 wxDoChangeBackgroundColour(m_menuWidget
, (wxColour
&) col
);
593 wxDoChangeBackgroundColour(m_buttonWidget
, (wxColour
&) col
, true);
595 for ( wxMenuItemList::compatibility_iterator node
= GetMenuItems().GetFirst();
597 node
= node
->GetNext() )
599 wxMenuItem
* item
= node
->GetData();
600 if (item
->GetButtonWidget())
602 // This crashes because it uses gadgets
603 // wxDoChangeBackgroundColour(item->GetButtonWidget(), (wxColour&) col, true);
605 if (item
->GetSubMenu())
606 item
->GetSubMenu()->SetBackgroundColour((wxColour
&) col
);
610 void wxMenu::SetForegroundColour(const wxColour
& col
)
612 m_foregroundColour
= col
;
616 wxDoChangeForegroundColour(m_menuWidget
, (wxColour
&) col
);
618 wxDoChangeForegroundColour(m_buttonWidget
, (wxColour
&) col
);
620 for ( wxMenuItemList::compatibility_iterator node
= GetMenuItems().GetFirst();
622 node
= node
->GetNext() )
624 wxMenuItem
* item
= node
->GetData();
625 if (item
->GetButtonWidget())
627 // This crashes because it uses gadgets
628 // wxDoChangeForegroundColour(item->GetButtonWidget(), (wxColour&) col);
630 if (item
->GetSubMenu())
631 item
->GetSubMenu()->SetForegroundColour((wxColour
&) col
);
635 void wxMenu::ChangeFont(bool keepOriginalSize
)
637 // Lesstif 0.87 hangs here, but 0.93 does not; MBN: sometimes it does
638 #if !wxCHECK_LESSTIF() // || wxCHECK_LESSTIF_VERSION( 0, 93 )
639 if (!m_font
.IsOk() || !m_menuWidget
)
642 Display
* dpy
= XtDisplay((Widget
) m_menuWidget
);
644 XtVaSetValues ((Widget
) m_menuWidget
,
645 wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
),
649 XtVaSetValues ((Widget
) m_buttonWidget
,
650 wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
),
654 for ( wxMenuItemList::compatibility_iterator node
= GetMenuItems().GetFirst();
656 node
= node
->GetNext() )
658 wxMenuItem
* item
= node
->GetData();
659 if (m_menuWidget
&& item
->GetButtonWidget() && m_font
.IsOk())
661 XtVaSetValues ((Widget
) item
->GetButtonWidget(),
662 wxFont::GetFontTag(), m_font
.GetFontTypeC(dpy
),
665 if (item
->GetSubMenu())
666 item
->GetSubMenu()->ChangeFont(keepOriginalSize
);
669 wxUnusedVar(keepOriginalSize
);
673 void wxMenu::SetFont(const wxFont
& font
)
679 bool wxMenuBar::SetBackgroundColour(const wxColour
& col
)
681 if (!wxWindowBase::SetBackgroundColour(col
))
686 wxDoChangeBackgroundColour(m_mainWidget
, (wxColour
&) col
);
688 size_t menuCount
= GetMenuCount();
689 for (size_t i
= 0; i
< menuCount
; i
++)
690 m_menus
.Item(i
)->GetData()->SetBackgroundColour((wxColour
&) col
);
695 bool wxMenuBar::SetForegroundColour(const wxColour
& col
)
697 if (!wxWindowBase::SetForegroundColour(col
))
702 wxDoChangeForegroundColour(m_mainWidget
, (wxColour
&) col
);
704 size_t menuCount
= GetMenuCount();
705 for (size_t i
= 0; i
< menuCount
; i
++)
706 m_menus
.Item(i
)->GetData()->SetForegroundColour((wxColour
&) col
);
711 void wxMenuBar::ChangeFont(bool WXUNUSED(keepOriginalSize
))
713 // Nothing to do for menubar, fonts are kept in wxMenus
716 bool wxMenuBar::SetFont(const wxFont
& font
)
721 size_t menuCount
= GetMenuCount();
722 for (size_t i
= 0; i
< menuCount
; i
++)
723 m_menus
.Item(i
)->GetData()->SetFont(font
);