]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/motif/menu.cpp
fix building with WXWIN_COMPATIBILITY_2_8 == 0
[wxWidgets.git] / src / motif / menu.cpp
... / ...
CommitLineData
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)
63void 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.
82wxMenu::~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
100void wxMenu::Break()
101{
102 m_numColumns++;
103}
104
105// function appends a new item or submenu to the menu
106wxMenuItem* wxMenu::DoAppend(wxMenuItem *pItem)
107{
108 return DoInsert(GetMenuItemCount(), pItem);
109}
110
111wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
112{
113 item->DestroyItem(true);
114
115 return wxMenuBase::DoRemove(item);
116}
117
118wxMenuItem* 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
138void 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
157bool 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
175void wxMenuBar::Init()
176{
177 m_eventHandler = this;
178 m_menuBarFrame = NULL;
179 m_mainWidget = (WXWidget) NULL;
180}
181
182wxMenuBar::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
193wxMenuBar::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
204wxMenuBar::~wxMenuBar()
205{
206 // nothing to do: wxMenuBarBase will delete the menus
207}
208
209void wxMenuBar::EnableTop(size_t WXUNUSED(pos), bool WXUNUSED(flag))
210{
211 // wxFAIL_MSG("TODO");
212// wxLogWarning("wxMenuBar::EnableTop not yet implemented.");
213}
214
215void 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
233wxString 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
240bool wxMenuBar::Append(wxMenu * menu, const wxString& title)
241{
242 return Insert(GetMenuCount(), menu, title);
243}
244
245bool 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
265wxMenu *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
275wxMenu *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.
293int 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
306wxMenuItem *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
322bool 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.
378bool 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...
407void 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
442WXWidget 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.
521void 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
549WXWidget 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
585void 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
610void 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
635void 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
673void wxMenu::SetFont(const wxFont& font)
674{
675 m_font = font;
676 ChangeFont();
677}
678
679bool 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
695bool 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
711void wxMenuBar::ChangeFont(bool WXUNUSED(keepOriginalSize))
712{
713 // Nothing to do for menubar, fonts are kept in wxMenus
714}
715
716bool 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}