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