1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 // ============================================================================
14 // headers & declarations
15 // ============================================================================
21 #pragma implementation "menu.h"
22 #pragma implementation "menuitem.h"
26 #include "wx/menuitem.h"
33 #include <Xm/LabelG.h>
34 #include <Xm/CascadeBG.h>
35 #include <Xm/CascadeB.h>
36 #include <Xm/SeparatoG.h>
37 #include <Xm/PushBG.h>
38 #include <Xm/ToggleB.h>
39 #include <Xm/ToggleBG.h>
40 #include <Xm/RowColumn.h>
42 #include "wx/motif/private.h"
44 // other standard headers
45 // ----------------------
48 #if !USE_SHARED_LIBRARY
49 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
50 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxEvtHandler
)
53 // ============================================================================
55 // ============================================================================
59 // Construct a menu with optional title (then use append)
60 wxMenu::wxMenu(const wxString
& title
, const wxFunction func
)
63 m_parent
= (wxEvtHandler
*) NULL
;
64 m_eventHandler
= this;
68 //// Motif-specific members
70 m_menuWidget
= (WXWidget
) NULL
;
71 m_popupShell
= (WXWidget
) NULL
;
72 m_buttonWidget
= (WXWidget
) NULL
;
74 m_topLevelMenu
= (wxMenu
*) NULL
;
75 m_ownedByMenuBar
= FALSE
;
76 m_menuParent
= (wxMenu
*) NULL
;
77 m_clientData
= (void*) NULL
;
81 Append(ID_SEPARATOR
, m_title
) ;
88 // The wxWindow destructor will take care of deleting the submenus.
99 // Not sure if this is right
100 if (m_menuParent
&& m_menuBar
)
106 wxNode
*node
= m_menuItems
.First();
109 wxMenuItem
*item
= (wxMenuItem
*)node
->Data();
112 if (item->GetSubMenu())
113 item->DeleteSubMenu();
116 wxNode
*next
= node
->Next();
128 // function appends a new item or submenu to the menu
129 void wxMenu::Append(wxMenuItem
*pItem
)
131 wxCHECK_RET( pItem
!= NULL
, "can't append NULL item to the menu" );
133 m_menuItems
.Append(pItem
);
136 pItem
->CreateItem (m_menuWidget
, m_menuBar
, m_topLevelMenu
); // this is a dynamic Append
141 void wxMenu::AppendSeparator()
143 Append(new wxMenuItem(this, ID_SEPARATOR
));
147 // N.B.: difference between old and new code.
148 // Old code stores subMenu in 'children' for later deletion,
149 // as well as in m_menuItems, whereas we only store it in
150 // m_menuItems here. What implications does this have?
152 void wxMenu::Append(int id
, const wxString
& label
, wxMenu
*subMenu
,
153 const wxString
& helpString
)
155 Append(new wxMenuItem(this, id
, label
, helpString
, FALSE
, subMenu
));
157 subMenu
->m_topLevelMenu
= m_topLevelMenu
;
160 // Ordinary menu item
161 void wxMenu::Append(int id
, const wxString
& label
,
162 const wxString
& helpString
, bool checkable
)
164 // 'checkable' parameter is useless for Windows.
165 Append(new wxMenuItem(this, id
, label
, helpString
, checkable
));
168 void wxMenu::Delete(int id
)
174 for (pos
= 0, node
= m_menuItems
.First(); node
; node
= node
->Next(), pos
++)
176 item
= (wxMenuItem
*)node
->Data();
177 if (item
->GetId() == id
)
184 item
->DestroyItem(TRUE
);
186 // See also old code - don't know if this is needed (seems redundant).
188 if (item->GetSubMenu()) {
189 item->subMenu->top_level_menu = item->GetSubMenu();
190 item->subMenu->window_parent = NULL;
191 children->DeleteObject(item->GetSubMenu());
195 m_menuItems
.DeleteNode(node
);
199 void wxMenu::Enable(int id
, bool flag
)
201 wxMenuItem
*item
= FindItemForId(id
);
202 wxCHECK_RET( item
!= NULL
, "can't enable non-existing menu item" );
207 bool wxMenu::Enabled(int Id
) const
209 wxMenuItem
*item
= FindItemForId(Id
);
210 wxCHECK( item
!= NULL
, FALSE
);
212 return item
->IsEnabled();
215 void wxMenu::Check(int Id
, bool Flag
)
217 wxMenuItem
*item
= FindItemForId(Id
);
218 wxCHECK_RET( item
!= NULL
, "can't get status of non-existing menu item" );
223 bool wxMenu::Checked(int id
) const
225 wxMenuItem
*item
= FindItemForId(id
);
226 wxCHECK( item
!= NULL
, FALSE
);
228 return item
->IsChecked();
231 void wxMenu::SetTitle(const wxString
& label
)
235 wxNode
*node
= m_menuItems
.First ();
239 wxMenuItem
*item
= (wxMenuItem
*) node
->Data ();
240 Widget widget
= (Widget
) item
->GetButtonWidget();
244 XmString title_str
= XmStringCreateSimple ((char*) (const char*) label
);
245 XtVaSetValues (widget
,
246 XmNlabelString
, title_str
,
248 // TODO: should we delete title_str now?
251 const wxString
wxMenu::GetTitle() const
256 void wxMenu::SetLabel(int id
, const wxString
& label
)
258 wxMenuItem
*item
= FindItemForId(id
);
259 if (item
== (wxMenuItem
*) NULL
)
262 item
->SetLabel(label
);
265 wxString
wxMenu::GetLabel(int id
) const
267 wxMenuItem
*it
= NULL
;
268 WXWidget w
= FindMenuItem (id
, &it
);
273 XtVaGetValues ((Widget
) w
,
274 XmNlabelString
, &text
,
277 if (XmStringGetLtoR (text
, XmSTRING_DEFAULT_CHARSET
, &s
))
286 return wxEmptyString
;
290 return wxEmptyString
;
293 // Finds the item id matching the given string, -1 if not found.
294 int wxMenu::FindItem (const wxString
& itemString
) const
298 wxStripMenuCodes ((char *)(const char *)itemString
, buf1
);
300 for (wxNode
* node
= m_menuItems
.First (); node
; node
= node
->Next ())
302 wxMenuItem
*item
= (wxMenuItem
*) node
->Data ();
303 if (item
->GetSubMenu())
305 int ans
= item
->GetSubMenu()->FindItem(itemString
);
309 if ( !item
->IsSeparator() )
311 wxStripMenuCodes((char *)item
->GetName().c_str(), buf2
);
312 if (strcmp(buf1
, buf2
) == 0)
313 return item
->GetId();
320 wxMenuItem
*wxMenu::FindItemForId(int itemId
, wxMenu
** itemMenu
) const
324 for (wxNode
* node
= m_menuItems
.First (); node
; node
= node
->Next ())
326 wxMenuItem
*item
= (wxMenuItem
*) node
->Data ();
328 if (item
->GetId() == itemId
)
331 *itemMenu
= (wxMenu
*) this;
335 if (item
->GetSubMenu())
337 wxMenuItem
*ans
= item
->GetSubMenu()->FindItemForId (itemId
, itemMenu
);
348 void wxMenu::SetHelpString(int itemId
, const wxString
& helpString
)
350 wxMenuItem
*item
= FindItemForId (itemId
);
352 item
->SetHelp(helpString
);
355 wxString
wxMenu::GetHelpString (int itemId
) const
357 wxMenuItem
*item
= FindItemForId (itemId
);
359 return (item
== NULL
) ? str
: item
->GetHelp();
362 void wxMenu::ProcessCommand(wxCommandEvent
& event
)
364 bool processed
= FALSE
;
369 (void) (*(m_callback
)) (*this, event
);
373 // Try the menu's event handler
374 if ( !processed
&& GetEventHandler())
376 processed
= GetEventHandler()->ProcessEvent(event
);
379 // Try the window the menu was popped up from (and up
380 // through the hierarchy)
381 if ( !processed && GetInvokingWindow())
382 processed = GetInvokingWindow()->ProcessEvent(event);
386 bool wxWindow::PopupMenu(wxMenu
*menu
, int x
, int y
)
388 Widget widget
= (Widget
) GetMainWidget();
390 /* The menuId field seems to be usused, so we'll use it to
391 indicate whether a menu is popped up or not:
392 0: Not currently created as a popup
393 -1: Created as a popup, but not active
397 if (menu
->GetParent() && (menu
->GetId() != -1))
400 if (menu
->GetMainWidget()) {
401 menu
->DestroyMenu(TRUE
);
404 wxWindow
*parent
= this;
406 menu
->SetId(1); /* Mark as popped-up */
407 menu
->CreateMenu(NULL
, widget
, menu
);
408 // menu->SetParent(parent);
409 // parent->children->Append(menu); // Store menu for later deletion
411 Widget menuWidget
= (Widget
) menu
->GetMainWidget();
419 if (this->IsKindOf(CLASSINFO(wxCanvas)))
421 wxCanvas *canvas = (wxCanvas *) this;
422 deviceX = canvas->GetDC ()->LogicalToDeviceX (x);
423 deviceY = canvas->GetDC ()->LogicalToDeviceY (y);
427 Display
*display
= XtDisplay (widget
);
428 Window rootWindow
= RootWindowOfScreen (XtScreen((Widget
)widget
));
429 Window thisWindow
= XtWindow (widget
);
431 XTranslateCoordinates (display
, thisWindow
, rootWindow
, (int) deviceX
, (int) deviceY
,
432 &rootX
, &rootY
, &childWindow
);
434 XButtonPressedEvent event
;
435 event
.type
= ButtonPress
;
441 event
.x_root
= rootX
;
442 event
.y_root
= rootY
;
444 XmMenuPosition (menuWidget
, &event
);
445 XtManageChild (menuWidget
);
451 wxMenuBar::wxMenuBar()
453 m_eventHandler
= this;
457 m_menuBarFrame
= NULL
;
458 m_mainWidget
= (WXWidget
) NULL
;
461 wxMenuBar::wxMenuBar(int n
, wxMenu
*menus
[], const wxString titles
[])
463 m_eventHandler
= this;
466 m_titles
= new wxString
[n
];
468 for ( i
= 0; i
< n
; i
++ )
469 m_titles
[i
] = titles
[i
];
470 m_menuBarFrame
= NULL
;
473 wxMenuBar::~wxMenuBar()
476 for (i
= 0; i
< m_menuCount
; i
++)
484 // Must only be used AFTER menu has been attached to frame,
485 // otherwise use individual menus to enable/disable items
486 void wxMenuBar::Enable(int id
, bool flag
)
488 wxMenu
*itemMenu
= NULL
;
489 wxMenuItem
*item
= FindItemForId(id
, &itemMenu
) ;
495 void wxMenuBar::EnableTop(int pos
, bool flag
)
500 // Must only be used AFTER menu has been attached to frame,
501 // otherwise use individual menus
502 void wxMenuBar::Check(int id
, bool flag
)
504 wxMenu
*itemMenu
= NULL
;
505 wxMenuItem
*item
= FindItemForId(id
, &itemMenu
) ;
509 if (!item
->IsCheckable())
515 bool wxMenuBar::Checked(int id
) const
517 wxMenu
*itemMenu
= NULL
;
518 wxMenuItem
*item
= FindItemForId(id
, &itemMenu
) ;
522 return item
->IsChecked();
525 bool wxMenuBar::Enabled(int id
) const
527 wxMenu
*itemMenu
= NULL
;
528 wxMenuItem
*item
= FindItemForId(id
, &itemMenu
) ;
532 return item
->IsEnabled();
535 void wxMenuBar::SetLabel(int id
, const wxString
& label
)
537 wxMenu
*itemMenu
= NULL
;
538 wxMenuItem
*item
= FindItemForId(id
, &itemMenu
) ;
543 item
->SetLabel(label
);
546 wxString
wxMenuBar::GetLabel(int id
) const
548 wxMenu
*itemMenu
= NULL
;
549 wxMenuItem
*item
= FindItemForId(id
, &itemMenu
) ;
554 return item
->GetLabel();
557 void wxMenuBar::SetLabelTop(int pos
, const wxString
& label
)
559 wxASSERT( (pos
< m_menuCount
) );
561 Widget w
= (Widget
) m_menus
[pos
]->GetButtonWidget();
564 XmString label_str
= XmStringCreateSimple ((char*) (const char*) label
);
566 XmNlabelString
, label_str
,
568 XmStringFree (label_str
);
572 wxString
wxMenuBar::GetLabelTop(int pos
) const
574 wxASSERT( (pos
< m_menuCount
) );
576 Widget w
= (Widget
) m_menus
[pos
]->GetButtonWidget();
582 XmNlabelString
, &text
,
585 if (XmStringGetLtoR (text
, XmSTRING_DEFAULT_CHARSET
, &s
))
593 return wxEmptyString
;
597 return wxEmptyString
;
601 bool wxMenuBar::OnDelete(wxMenu
*menu
, int pos
)
603 // Only applies to dynamic deletion (when set in frame)
607 menu
->DestroyMenu(TRUE
);
611 bool wxMenuBar::OnAppend(wxMenu
*menu
, const char *title
)
613 // Only applies to dynamic append (when set in frame)
617 // Probably should be an assert here
618 if (menu
->GetParent())
621 // Has already been appended
622 if (menu
->GetButtonWidget())
625 WXWidget w
= menu
->CreateMenu(this, GetMainWidget(), menu
, title
, TRUE
);
626 menu
->SetButtonWidget(w
);
631 void wxMenuBar::Append (wxMenu
* menu
, const wxString
& title
)
633 if (!OnAppend(menu
, title
))
637 wxMenu
**new_menus
= new wxMenu
*[m_menuCount
];
638 wxString
*new_titles
= new wxString
[m_menuCount
];
641 for (i
= 0; i
< m_menuCount
- 1; i
++)
643 new_menus
[i
] = m_menus
[i
];
645 new_titles
[i
] = m_titles
[i
];
654 m_titles
= new_titles
;
656 m_menus
[m_menuCount
- 1] = (wxMenu
*)menu
;
657 m_titles
[m_menuCount
- 1] = title
;
659 menu
->SetMenuBar(this);
660 menu
->SetParent(this);
663 void wxMenuBar::Delete(wxMenu
* menu
, int i
)
670 for (ii
= 0; ii
< m_menuCount
; ii
++)
672 if (m_menus
[ii
] == menu
)
675 if (ii
>= m_menuCount
)
679 if (ii
< 0 || ii
>= m_menuCount
)
684 if (!OnDelete(menu
, ii
))
687 menu
->SetParent((wxEvtHandler
*) NULL
);
690 for (j
= ii
; j
< m_menuCount
; j
++)
692 m_menus
[j
] = m_menus
[j
+ 1];
693 m_titles
[j
] = m_titles
[j
+ 1];
697 // Find the menu menuString, item itemString, and return the item id.
698 // Returns -1 if none found.
699 int wxMenuBar::FindMenuItem (const wxString
& menuString
, const wxString
& itemString
) const
703 wxStripMenuCodes ((char *)(const char *)menuString
, buf1
);
705 for (i
= 0; i
< m_menuCount
; i
++)
707 wxStripMenuCodes ((char *)(const char *)m_titles
[i
], buf2
);
708 if (strcmp (buf1
, buf2
) == 0)
709 return m_menus
[i
]->FindItem (itemString
);
714 wxMenuItem
*wxMenuBar::FindItemForId (int id
, wxMenu
** itemMenu
) const
719 wxMenuItem
*item
= NULL
;
721 for (i
= 0; i
< m_menuCount
; i
++)
722 if ((item
= m_menus
[i
]->FindItemForId (id
, itemMenu
)))
727 void wxMenuBar::SetHelpString (int id
, const wxString
& helpString
)
730 for (i
= 0; i
< m_menuCount
; i
++)
732 if (m_menus
[i
]->FindItemForId (id
))
734 m_menus
[i
]->SetHelpString (id
, helpString
);
740 wxString
wxMenuBar::GetHelpString (int id
) const
743 for (i
= 0; i
< m_menuCount
; i
++)
745 if (m_menus
[i
]->FindItemForId (id
))
746 return wxString(m_menus
[i
]->GetHelpString (id
));
752 bool wxMenuBar::CreateMenuBar(wxFrame
* parent
)
756 XtVaSetValues((Widget
) parent
->GetMainWindowWidget(), XmNmenuBar
, (Widget
) m_mainWidget
, NULL
);
758 if (!XtIsManaged((Widget) m_mainWidget))
759 XtManageChild((Widget) m_mainWidget);
761 XtMapWidget((Widget
) m_mainWidget
);
765 Widget menuBarW
= XmCreateMenuBar ((Widget
) parent
->GetMainWindowWidget(), "MenuBar", NULL
, 0);
766 m_mainWidget
= (WXWidget
) menuBarW
;
769 for (i
= 0; i
< GetMenuCount(); i
++)
771 wxMenu
*menu
= GetMenu(i
);
772 wxString
title(m_titles
[i
]);
773 menu
->SetButtonWidget(menu
->CreateMenu (this, menuBarW
, menu
, title
, TRUE
));
776 * COMMENT THIS OUT IF YOU DON'T LIKE A RIGHT-JUSTIFIED HELP MENU
778 wxStripMenuCodes ((char*) (const char*) title
, wxBuffer
);
780 if (strcmp (wxBuffer
, "Help") == 0)
781 XtVaSetValues ((Widget
) menuBarW
, XmNmenuHelpWidget
, (Widget
) menu
->GetButtonWidget(), NULL
);
784 XtVaSetValues((Widget
) parent
->GetMainWindowWidget(), XmNmenuBar
, (Widget
) m_mainWidget
, NULL
);
785 XtRealizeWidget ((Widget
) menuBarW
);
786 XtManageChild ((Widget
) menuBarW
);
787 SetMenuBarFrame(parent
);
792 // Destroy menubar, but keep data structures intact so we can recreate it.
793 bool wxMenuBar::DestroyMenuBar()
797 SetMenuBarFrame((wxFrame
*) NULL
);
801 XtUnmanageChild ((Widget
) m_mainWidget
);
802 XtUnrealizeWidget ((Widget
) m_mainWidget
);
805 for (i
= 0; i
< GetMenuCount(); i
++)
807 wxMenu
*menu
= GetMenu(i
);
808 menu
->DestroyMenu(TRUE
);
811 XtDestroyWidget((Widget
) m_mainWidget
);
812 m_mainWidget
= (WXWidget
) 0;
814 SetMenuBarFrame((wxFrame
*) NULL
);
821 extern wxApp
*wxTheApp
;
822 static XtWorkProcId WorkProcMenuId
;
824 /* Since PopupMenu under Motif stills grab right mouse button events
825 * after it was closed, we need to delete the associated widgets to
826 * allow next PopUpMenu to appear...
829 int PostDeletionOfMenu( XtPointer
* clientData
)
831 XtRemoveWorkProc(WorkProcMenuId
);
832 wxMenu
*menu
= (wxMenu
*)clientData
;
834 if (menu
->GetMainWidget()) {
835 if (menu
->GetParent())
837 wxList
& list
= menu
->GetParent()->GetItems();
838 list
.DeleteObject(menu
);
840 menu
->DestroyMenu(TRUE
);
842 /* Mark as no longer popped up */
848 wxMenuPopdownCallback(Widget w
, XtPointer clientData
,
851 wxMenu
*menu
= (wxMenu
*)clientData
;
853 // Added by JOREL Jean-Charles <jjorel@silr.ireste.fr>
854 /* Since Callbacks of MenuItems are not yet processed, we put a
855 * background job which will be done when system will be idle.
856 * What awful hack!! :(
859 WorkProcMenuId
= XtAppAddWorkProc(
860 (XtAppContext
) wxTheApp
->GetAppContext(),
861 (XtWorkProc
) PostDeletionOfMenu
,
863 // Apparently not found in Motif headers
864 // XtVaSetValues( w, XmNpopupEnabled, XmPOPUP_DISABLED, NULL );
868 * Create a popup or pulldown menu.
869 * Submenus of a popup will be pulldown.
873 WXWidget
wxMenu::CreateMenu (wxMenuBar
* menuBar
, WXWidget parent
, wxMenu
* topMenu
, const wxString
& title
, bool pullDown
)
875 Widget menu
= (Widget
) 0;
876 Widget buttonWidget
= (Widget
) 0;
878 XtSetArg (args
[0], XmNnumColumns
, m_numColumns
);
879 XtSetArg (args
[1], XmNpacking
, XmPACK_COLUMN
);
883 menu
= XmCreatePopupMenu ((Widget
) parent
, "popup", args
, 2);
886 (XtCallbackProc
)wxMenuPopdownCallback
,
891 char mnem
= wxFindMnemonic (title
);
892 wxStripMenuCodes ((char*) (const char*) title
, wxBuffer
);
894 menu
= XmCreatePulldownMenu ((Widget
) parent
, "pulldown", args
, 2);
896 XmString label_str
= XmStringCreateSimple (wxBuffer
);
897 buttonWidget
= XtVaCreateManagedWidget (wxBuffer
,
899 xmCascadeButtonGadgetClass
, (Widget
) parent
,
901 xmCascadeButtonWidgetClass
, (Widget
) parent
,
903 XmNlabelString
, label_str
,
908 XtVaSetValues (buttonWidget
, XmNmnemonic
, mnem
, NULL
);
910 XmStringFree (label_str
);
913 m_menuWidget
= (WXWidget
) menu
;
916 m_topLevelMenu
= topMenu
;
918 for (wxNode
* node
= m_menuItems
.First (); node
; node
= node
->Next ())
920 wxMenuItem
*item
= (wxMenuItem
*) node
->Data ();
921 item
->CreateItem (menu
, menuBar
, topMenu
);
927 // Destroys the Motif implementation of the menu,
928 // but maintains the wxWindows data structures so we can
929 // do a CreateMenu again.
930 void wxMenu::DestroyMenu (bool full
)
932 for (wxNode
* node
= m_menuItems
.First (); node
; node
= node
->Next ())
934 wxMenuItem
*item
= (wxMenuItem
*) node
->Data ();
935 item
->SetMenuBar((wxMenuBar
*) NULL
);
937 item
->DestroyItem(full
);
944 XtVaSetValues((Widget
) m_buttonWidget
, XmNsubMenuId
, NULL
, NULL
);
945 XtDestroyWidget ((Widget
) m_buttonWidget
);
946 m_buttonWidget
= (WXWidget
) 0;
949 if (m_menuWidget
&& full
)
951 XtDestroyWidget((Widget
) m_menuWidget
);
952 m_menuWidget
= (WXWidget
) NULL
;
956 WXWidget
wxMenu::FindMenuItem (int id
, wxMenuItem
** it
) const
961 *it
= (wxMenuItem
*) NULL
;
962 return m_buttonWidget
;
965 for (wxNode
* node
= m_menuItems
.First (); node
; node
= node
->Next ())
967 wxMenuItem
*item
= (wxMenuItem
*) node
->Data ();
968 if (item
->GetId() == id
)
972 return item
->GetButtonWidget();
975 if (item
->GetSubMenu())
977 WXWidget w
= item
->GetSubMenu()->FindMenuItem (id
, it
);
986 *it
= (wxMenuItem
*) NULL
;
987 return (WXWidget
) NULL
;