Document domain parameter of wxTranslations::GetTranslatedString().
[wxWidgets.git] / src / motif / menu.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/motif/menu.cpp
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 17/09/98
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #include "wx/menu.h"
23
24 #ifndef WX_PRECOMP
25 #include "wx/log.h"
26 #include "wx/app.h"
27 #include "wx/utils.h"
28 #include "wx/frame.h"
29 #include "wx/settings.h"
30 #include "wx/menuitem.h"
31 #endif
32
33 #ifdef __VMS__
34 #pragma message disable nosimpint
35 #endif
36 #include <Xm/Label.h>
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>
45 #ifdef __VMS__
46 #pragma message enable nosimpint
47 #endif
48
49 #include "wx/motif/private.h"
50
51 // other standard headers
52 #include <string.h>
53
54 // ============================================================================
55 // implementation
56 // ============================================================================
57
58 // ----------------------------------------------------------------------------
59 // Menus
60 // ----------------------------------------------------------------------------
61
62 // Construct a menu with optional title (then use append)
63 void wxMenu::Init()
64 {
65 // Motif-specific members
66 m_numColumns = 1;
67 m_menuWidget = (WXWidget) NULL;
68 m_popupShell = (WXWidget) NULL;
69 m_buttonWidget = (WXWidget) NULL;
70 m_menuId = 0;
71 m_topLevelMenu = NULL;
72 m_ownedByMenuBar = false;
73
74 if ( !m_title.empty() )
75 {
76 Append(-3, m_title) ;
77 AppendSeparator() ;
78 }
79 }
80
81 // The wxWindow destructor will take care of deleting the submenus.
82 wxMenu::~wxMenu()
83 {
84 if (m_menuWidget)
85 {
86 if (m_menuParent)
87 DestroyMenu(true);
88 else
89 DestroyMenu(false);
90 }
91
92 // Not sure if this is right
93 if (m_menuParent && m_menuBar)
94 {
95 m_menuParent = NULL;
96 // m_menuBar = NULL;
97 }
98 }
99
100 void wxMenu::Break()
101 {
102 m_numColumns++;
103 }
104
105 // function appends a new item or submenu to the menu
106 wxMenuItem* wxMenu::DoAppend(wxMenuItem *pItem)
107 {
108 return DoInsert(GetMenuItemCount(), pItem);
109 }
110
111 wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
112 {
113 item->DestroyItem(true);
114
115 return wxMenuBase::DoRemove(item);
116 }
117
118 wxMenuItem* wxMenu::DoInsert(size_t pos, wxMenuItem *item)
119 {
120 if (m_menuWidget)
121 {
122 // this is a dynamic Append
123 #ifndef XmNpositionIndex
124 wxCHECK_MSG( pos == GetMenuItemCount(), -1, wxT("insert not implemented"));
125 #endif
126 item->CreateItem(m_menuWidget, GetMenuBar(), m_topLevelMenu, pos);
127 }
128
129 if ( item->IsSubMenu() )
130 {
131 item->GetSubMenu()->m_topLevelMenu = m_topLevelMenu;
132 }
133
134 return pos == GetMenuItemCount() ? wxMenuBase::DoAppend(item) :
135 wxMenuBase::DoInsert(pos, item);
136 }
137
138 void wxMenu::SetTitle(const wxString& label)
139 {
140 m_title = label;
141
142 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
143 if ( !node )
144 return;
145
146 wxMenuItem *item = node->GetData ();
147 Widget widget = (Widget) item->GetButtonWidget();
148 if ( !widget )
149 return;
150
151 wxXmString title_str(label);
152 XtVaSetValues(widget,
153 XmNlabelString, title_str(),
154 NULL);
155 }
156
157 bool wxMenu::ProcessCommand(wxCommandEvent & event)
158 {
159 // Try the menu's event handler first
160 wxEvtHandler * const handler = GetEventHandler();
161 bool processed = handler ? handler->SafelyProcessEvent(event) : false;
162
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);
167
168 return processed;
169 }
170
171 // ----------------------------------------------------------------------------
172 // Menu Bar
173 // ----------------------------------------------------------------------------
174
175 void wxMenuBar::Init()
176 {
177 m_eventHandler = this;
178 m_menuBarFrame = NULL;
179 m_mainWidget = (WXWidget) NULL;
180 }
181
182 wxMenuBar::wxMenuBar(size_t n, wxMenu *menus[], const wxArrayString& titles, long WXUNUSED(style))
183 {
184 wxASSERT( n == titles.GetCount() );
185
186 Init();
187
188 m_titles = titles;
189 for ( size_t i = 0; i < n; i++ )
190 m_menus.Append(menus[i]);
191 }
192
193 wxMenuBar::wxMenuBar(size_t n, wxMenu *menus[], const wxString titles[], long WXUNUSED(style))
194 {
195 Init();
196
197 for ( size_t i = 0; i < n; i++ )
198 {
199 m_menus.Append(menus[i]);
200 m_titles.Add(titles[i]);
201 }
202 }
203
204 wxMenuBar::~wxMenuBar()
205 {
206 // nothing to do: wxMenuBarBase will delete the menus
207 }
208
209 void wxMenuBar::EnableTop(size_t WXUNUSED(pos), bool WXUNUSED(flag))
210 {
211 // wxFAIL_MSG("TODO");
212 // wxLogWarning("wxMenuBar::EnableTop not yet implemented.");
213 }
214
215 void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
216 {
217 wxMenu *menu = GetMenu(pos);
218 if ( !menu )
219 return;
220
221 Widget w = (Widget)menu->GetButtonWidget();
222 if (w)
223 {
224 wxXmString label_str(label);
225
226 XtVaSetValues(w,
227 XmNlabelString, label_str(),
228 NULL);
229 }
230 m_titles[pos] = label;
231 }
232
233 wxString wxMenuBar::GetMenuLabel(size_t pos) const
234 {
235 wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
236 wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
237 return m_titles[pos];
238 }
239
240 bool wxMenuBar::Append(wxMenu * menu, const wxString& title)
241 {
242 return Insert(GetMenuCount(), menu, title);
243 }
244
245 bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
246 {
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") );
251
252 if ( m_menuBarFrame )
253 {
254 WXWidget w = menu->CreateMenu(this, GetMainWidget(), menu,
255 pos, title, true);
256 wxCHECK_MSG( w, false, wxT("failed to create menu") );
257 menu->SetButtonWidget(w);
258 }
259
260 m_titles.Insert(title, pos);
261
262 return wxMenuBarBase::Insert(pos, menu, title);
263 }
264
265 wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
266 {
267 if ( !wxMenuBarBase::Replace(pos, menu, title) )
268 return NULL;
269
270 wxFAIL_MSG(wxT("TODO"));
271
272 return NULL;
273 }
274
275 wxMenu *wxMenuBar::Remove(size_t pos)
276 {
277 wxMenu *menu = wxMenuBarBase::Remove(pos);
278 if ( !menu )
279 return NULL;
280
281 if ( m_menuBarFrame )
282 menu->DestroyMenu(true);
283
284 menu->SetMenuBar(NULL);
285
286 m_titles.RemoveAt(pos);
287
288 return menu;
289 }
290
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
294 {
295 const wxString stripped = wxStripMenuCodes(menuString);
296
297 size_t menuCount = GetMenuCount();
298 for (size_t i = 0; i < menuCount; i++)
299 {
300 if ( wxStripMenuCodes(m_titles[i]) == stripped )
301 return m_menus.Item(i)->GetData()->FindItem (itemString);
302 }
303 return wxNOT_FOUND;
304 }
305
306 wxMenuItem *wxMenuBar::FindItem(int id, wxMenu ** itemMenu) const
307 {
308 if (itemMenu)
309 *itemMenu = NULL;
310
311 size_t menuCount = GetMenuCount();
312 for (size_t i = 0; i < menuCount; i++)
313 {
314 wxMenuItem *item = m_menus.Item(i)->GetData()->FindItem(id, itemMenu);
315 if (item) return item;
316 }
317
318 return NULL;
319 }
320
321 // Create menubar
322 bool wxMenuBar::CreateMenuBar(wxFrame* parent)
323 {
324 m_parent = parent; // bleach... override it!
325 PreCreation();
326 m_parent = NULL;
327
328 if (m_mainWidget)
329 {
330 XtVaSetValues((Widget) parent->GetMainWidget(), XmNmenuBar, (Widget) m_mainWidget, NULL);
331 /*
332 if (!XtIsManaged((Widget) m_mainWidget))
333 XtManageChild((Widget) m_mainWidget);
334 */
335 XtMapWidget((Widget) m_mainWidget);
336 return true;
337 }
338
339 Widget menuBarW = XmCreateMenuBar ((Widget) parent->GetMainWidget(),
340 wxMOTIF_STR("MenuBar"), NULL, 0);
341 m_mainWidget = (WXWidget) menuBarW;
342
343 size_t menuCount = GetMenuCount();
344 for (size_t i = 0; i < menuCount; i++)
345 {
346 wxMenu *menu = GetMenu(i);
347 wxString title(m_titles[i]);
348 menu->SetButtonWidget(menu->CreateMenu (this, menuBarW, menu, i, title, true));
349
350 if (strcmp (wxStripMenuCodes(title), "Help") == 0)
351 XtVaSetValues ((Widget) menuBarW, XmNmenuHelpWidget, (Widget) menu->GetButtonWidget(), NULL);
352
353 // tear off menu support
354 #if (XmVersion >= 1002)
355 if ( menu->IsTearOff() )
356 {
357 XtVaSetValues(GetWidget(menu),
358 XmNtearOffModel, XmTEAR_OFF_ENABLED,
359 NULL);
360 Widget tearOff = XmGetTearOffControl(GetWidget(menu));
361 wxDoChangeForegroundColour((Widget) tearOff, m_foregroundColour);
362 wxDoChangeBackgroundColour((Widget) tearOff, m_backgroundColour, true);
363 }
364 #endif
365 }
366
367 PostCreation();
368
369 XtVaSetValues((Widget) parent->GetMainWidget(), XmNmenuBar, (Widget) m_mainWidget, NULL);
370 XtRealizeWidget ((Widget) menuBarW);
371 XtManageChild ((Widget) menuBarW);
372 SetMenuBarFrame(parent);
373
374 return true;
375 }
376
377 // Destroy menubar, but keep data structures intact so we can recreate it.
378 bool wxMenuBar::DestroyMenuBar()
379 {
380 if (!m_mainWidget)
381 {
382 SetMenuBarFrame(NULL);
383 return false;
384 }
385
386 XtUnmanageChild ((Widget) m_mainWidget);
387 XtUnrealizeWidget ((Widget) m_mainWidget);
388
389 size_t menuCount = GetMenuCount();
390 for (size_t i = 0; i < menuCount; i++)
391 {
392 wxMenu *menu = GetMenu(i);
393 menu->DestroyMenu(true);
394
395 }
396 XtDestroyWidget((Widget) m_mainWidget);
397 m_mainWidget = (WXWidget) 0;
398
399 SetMenuBarFrame(NULL);
400
401 return true;
402 }
403
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()
408 {
409 if (GetMainWidget())
410 {
411 wxMenu *menuParent = GetParent();
412 if ( menuParent )
413 {
414 wxMenuItemList::compatibility_iterator node = menuParent->GetMenuItems().GetFirst();
415 while ( node )
416 {
417 if ( node->GetData()->GetSubMenu() == this )
418 {
419 delete node->GetData();
420 menuParent->GetMenuItems().Erase(node);
421
422 break;
423 }
424
425 node = node->GetNext();
426 }
427 }
428
429 DestroyMenu(true);
430 }
431
432 // Mark as no longer popped up
433 m_menuId = -1;
434 }
435
436 /*
437 * Create a popup or pulldown menu.
438 * Submenus of a popup will be pulldown.
439 *
440 */
441
442 WXWidget wxMenu::CreateMenu (wxMenuBar * menuBar,
443 WXWidget parent,
444 wxMenu * topMenu,
445 size_t menuIndex,
446 const wxString& title,
447 bool pullDown)
448 {
449 Widget menu = (Widget) 0;
450 Widget buttonWidget = (Widget) 0;
451 Display* dpy = XtDisplay((Widget)parent);
452 Arg args[5];
453 XtSetArg (args[0], XmNnumColumns, m_numColumns);
454 XtSetArg (args[1], XmNpacking, (m_numColumns > 1) ? XmPACK_COLUMN : XmPACK_TIGHT);
455
456 if ( !m_font.IsOk() )
457 {
458 if ( menuBar )
459 m_font = menuBar->GetFont();
460 else if ( GetInvokingWindow() )
461 m_font = GetInvokingWindow()->GetFont();
462 }
463
464 XtSetArg (args[2], (String)wxFont::GetFontTag(), m_font.GetFontTypeC(dpy) );
465
466 if (!pullDown)
467 {
468 menu = XmCreatePopupMenu ((Widget) parent, wxMOTIF_STR("popup"), args, 3);
469 #if 0
470 XtAddCallback(menu,
471 XmNunmapCallback,
472 (XtCallbackProc)wxMenuPopdownCallback,
473 (XtPointer)this);
474 #endif
475 }
476 else
477 {
478 char mnem = wxFindMnemonic (title);
479 menu = XmCreatePulldownMenu ((Widget) parent, wxMOTIF_STR("pulldown"), args, 3);
480
481 wxString title2(wxStripMenuCodes(title));
482 wxXmString label_str(title2);
483 buttonWidget = XtVaCreateManagedWidget(title2,
484 #if wxUSE_GADGETS
485 xmCascadeButtonGadgetClass, (Widget) parent,
486 #else
487 xmCascadeButtonWidgetClass, (Widget) parent,
488 #endif
489 XmNlabelString, label_str(),
490 XmNsubMenuId, menu,
491 (String)wxFont::GetFontTag(), m_font.GetFontTypeC(dpy),
492 XmNpositionIndex, menuIndex,
493 NULL);
494
495 if (mnem != 0)
496 XtVaSetValues (buttonWidget, XmNmnemonic, mnem, NULL);
497 }
498
499 m_menuWidget = (WXWidget) menu;
500
501 m_topLevelMenu = topMenu;
502
503 size_t i = 0;
504 for ( wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
505 node;
506 node = node->GetNext(), ++i )
507 {
508 wxMenuItem *item = node->GetData();
509
510 item->CreateItem(menu, menuBar, topMenu, i);
511 }
512
513 ChangeFont();
514
515 return buttonWidget;
516 }
517
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)
522 {
523 for ( wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
524 node;
525 node = node->GetNext() )
526 {
527 wxMenuItem *item = node->GetData();
528 item->SetMenuBar(NULL);
529
530 item->DestroyItem(full);
531 }
532
533 if (m_buttonWidget)
534 {
535 if (full)
536 {
537 XtVaSetValues((Widget) m_buttonWidget, XmNsubMenuId, NULL, NULL);
538 XtDestroyWidget ((Widget) m_buttonWidget);
539 m_buttonWidget = (WXWidget) 0;
540 }
541 }
542 if (m_menuWidget && full)
543 {
544 XtDestroyWidget((Widget) m_menuWidget);
545 m_menuWidget = (WXWidget) NULL;
546 }
547 }
548
549 WXWidget wxMenu::FindMenuItem (int id, wxMenuItem ** it) const
550 {
551 if (id == m_menuId)
552 {
553 if (it)
554 *it = NULL;
555 return m_buttonWidget;
556 }
557
558 for ( wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
559 node;
560 node = node->GetNext() )
561 {
562 wxMenuItem *item = node->GetData ();
563 if (item->GetId() == id)
564 {
565 if (it)
566 *it = item;
567 return item->GetButtonWidget();
568 }
569
570 if (item->GetSubMenu())
571 {
572 WXWidget w = item->GetSubMenu()->FindMenuItem (id, it);
573 if (w)
574 {
575 return w;
576 }
577 }
578 }
579
580 if (it)
581 *it = NULL;
582 return (WXWidget) NULL;
583 }
584
585 void wxMenu::SetBackgroundColour(const wxColour& col)
586 {
587 m_backgroundColour = col;
588 if (!col.IsOk())
589 return;
590 if (m_menuWidget)
591 wxDoChangeBackgroundColour(m_menuWidget, (wxColour&) col);
592 if (m_buttonWidget)
593 wxDoChangeBackgroundColour(m_buttonWidget, (wxColour&) col, true);
594
595 for ( wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
596 node;
597 node = node->GetNext() )
598 {
599 wxMenuItem* item = node->GetData();
600 if (item->GetButtonWidget())
601 {
602 // This crashes because it uses gadgets
603 // wxDoChangeBackgroundColour(item->GetButtonWidget(), (wxColour&) col, true);
604 }
605 if (item->GetSubMenu())
606 item->GetSubMenu()->SetBackgroundColour((wxColour&) col);
607 }
608 }
609
610 void wxMenu::SetForegroundColour(const wxColour& col)
611 {
612 m_foregroundColour = col;
613 if (!col.IsOk())
614 return;
615 if (m_menuWidget)
616 wxDoChangeForegroundColour(m_menuWidget, (wxColour&) col);
617 if (m_buttonWidget)
618 wxDoChangeForegroundColour(m_buttonWidget, (wxColour&) col);
619
620 for ( wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
621 node;
622 node = node->GetNext() )
623 {
624 wxMenuItem* item = node->GetData();
625 if (item->GetButtonWidget())
626 {
627 // This crashes because it uses gadgets
628 // wxDoChangeForegroundColour(item->GetButtonWidget(), (wxColour&) col);
629 }
630 if (item->GetSubMenu())
631 item->GetSubMenu()->SetForegroundColour((wxColour&) col);
632 }
633 }
634
635 void wxMenu::ChangeFont(bool keepOriginalSize)
636 {
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)
640 return;
641
642 Display* dpy = XtDisplay((Widget) m_menuWidget);
643
644 XtVaSetValues ((Widget) m_menuWidget,
645 wxFont::GetFontTag(), m_font.GetFontTypeC(dpy),
646 NULL);
647 if (m_buttonWidget)
648 {
649 XtVaSetValues ((Widget) m_buttonWidget,
650 wxFont::GetFontTag(), m_font.GetFontTypeC(dpy),
651 NULL);
652 }
653
654 for ( wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
655 node;
656 node = node->GetNext() )
657 {
658 wxMenuItem* item = node->GetData();
659 if (m_menuWidget && item->GetButtonWidget() && m_font.IsOk())
660 {
661 XtVaSetValues ((Widget) item->GetButtonWidget(),
662 wxFont::GetFontTag(), m_font.GetFontTypeC(dpy),
663 NULL);
664 }
665 if (item->GetSubMenu())
666 item->GetSubMenu()->ChangeFont(keepOriginalSize);
667 }
668 #else
669 wxUnusedVar(keepOriginalSize);
670 #endif
671 }
672
673 void wxMenu::SetFont(const wxFont& font)
674 {
675 m_font = font;
676 ChangeFont();
677 }
678
679 bool wxMenuBar::SetBackgroundColour(const wxColour& col)
680 {
681 if (!wxWindowBase::SetBackgroundColour(col))
682 return false;
683 if (!col.IsOk())
684 return false;
685 if (m_mainWidget)
686 wxDoChangeBackgroundColour(m_mainWidget, (wxColour&) col);
687
688 size_t menuCount = GetMenuCount();
689 for (size_t i = 0; i < menuCount; i++)
690 m_menus.Item(i)->GetData()->SetBackgroundColour((wxColour&) col);
691
692 return true;
693 }
694
695 bool wxMenuBar::SetForegroundColour(const wxColour& col)
696 {
697 if (!wxWindowBase::SetForegroundColour(col))
698 return false;
699 if (!col.IsOk())
700 return false;
701 if (m_mainWidget)
702 wxDoChangeForegroundColour(m_mainWidget, (wxColour&) col);
703
704 size_t menuCount = GetMenuCount();
705 for (size_t i = 0; i < menuCount; i++)
706 m_menus.Item(i)->GetData()->SetForegroundColour((wxColour&) col);
707
708 return true;
709 }
710
711 void wxMenuBar::ChangeFont(bool WXUNUSED(keepOriginalSize))
712 {
713 // Nothing to do for menubar, fonts are kept in wxMenus
714 }
715
716 bool wxMenuBar::SetFont(const wxFont& font)
717 {
718 m_font = font;
719 ChangeFont();
720
721 size_t menuCount = GetMenuCount();
722 for (size_t i = 0; i < menuCount; i++)
723 m_menus.Item(i)->GetData()->SetFont(font);
724
725 return true;
726 }