]> git.saurik.com Git - wxWidgets.git/blobdiff - src/motif/menu.cpp
More Motif additions: mdi and sashtest samples now just about work!
[wxWidgets.git] / src / motif / menu.cpp
index 468ed194e416ea665b4c7704656550596b87d95b..dcaaa94df45a58774ce28fc4eb732a34b7fd1cf4 100644 (file)
@@ -26,6 +26,8 @@
 #include "wx/menuitem.h"
 #include "wx/log.h"
 #include "wx/utils.h"
+#include "wx/app.h"
+#include "wx/frame.h"
 
 #include <Xm/Label.h>
 #include <Xm/LabelG.h>
 #include <Xm/ToggleBG.h>
 #include <Xm/RowColumn.h>
 
+#include "wx/motif/private.h"
+
 // other standard headers
 // ----------------------
 #include <string.h>
 
-void wxMenuItemCallback (Widget w, XtPointer clientData,
-                        XtPointer ptr);
-void wxMenuItemArmCallback (Widget w, XtPointer clientData,
-                           XtPointer ptr);
-void wxMenuItemDisarmCallback (Widget w, XtPointer clientData,
-                              XtPointer ptr);
-
 #if !USE_SHARED_LIBRARY
 IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
@@ -74,36 +71,46 @@ wxMenu::wxMenu(const wxString& title, const wxFunction func)
     m_popupShell = (WXWidget) NULL;
     m_buttonWidget = (WXWidget) NULL;
     m_menuId = 0;
-    m_topMenu  = (wxMenu*) NULL;
+    m_topLevelMenu  = (wxMenu*) NULL;
     m_ownedByMenuBar = FALSE;
     m_menuParent = (wxMenu*) NULL;
 
     if (m_title != "")
     {
-        Append(-2, m_title) ;
+        Append(ID_SEPARATOR, m_title) ;
         AppendSeparator() ;
     }
 
     Callback(func);
-
-    // TODO create menu
 }
 
 // The wxWindow destructor will take care of deleting the submenus.
 wxMenu::~wxMenu()
 {
-    // TODO destroy menu and children
+    if (m_menuWidget)
+    {
+      if (m_menuParent)
+        DestroyMenu(TRUE);
+      else
+        DestroyMenu(FALSE);
+    }
+
+    // Not sure if this is right
+    if (m_menuParent && m_menuBar)
+    {
+      m_menuParent = NULL;
+      //      m_menuBar = NULL;
+    }
 
     wxNode *node = m_menuItems.First();
     while (node)
     {
         wxMenuItem *item = (wxMenuItem *)node->Data();
 
-        // Delete child menus.
-        // Beware: they must not be appended to children list!!!
-        // (because order of delete is significant)
+       /*
         if (item->GetSubMenu())
             item->DeleteSubMenu();
+        */
 
         wxNode *next = node->Next();
         delete item;
@@ -114,40 +121,47 @@ wxMenu::~wxMenu()
 
 void wxMenu::Break()
 {
-    // TODO
+    m_numColumns ++;
 }
 
 // function appends a new item or submenu to the menu
 void wxMenu::Append(wxMenuItem *pItem)
 {
-    // TODO
-
     wxCHECK_RET( pItem != NULL, "can't append NULL item to the menu" );
 
     m_menuItems.Append(pItem);
 
+    if (m_menuWidget)
+      pItem->CreateItem (m_menuWidget, m_menuBar, m_topLevelMenu);     // this is a dynamic Append
+
     m_noItems++;
 }
 
 void wxMenu::AppendSeparator()
 {
-    // TODO
     Append(new wxMenuItem(this, ID_SEPARATOR));
 }
 
 // Pullright item
-void wxMenu::Append(int Id, const wxString& label, wxMenu *SubMenu, 
+// N.B.: difference between old and new code.
+// Old code stores subMenu in 'children' for later deletion,
+// as well as in m_menuItems, whereas we only store it in
+// m_menuItems here. What implications does this have?
+
+void wxMenu::Append(int id, const wxString& label, wxMenu *subMenu, 
                     const wxString& helpString)
 {
-    Append(new wxMenuItem(this, Id, label, helpString, FALSE, SubMenu));
+    Append(new wxMenuItem(this, id, label, helpString, FALSE, subMenu));
+
+    subMenu->m_topLevelMenu = m_topLevelMenu;
 }
 
 // Ordinary menu item
-void wxMenu::Append(int Id, const wxString& label, 
+void wxMenu::Append(int id, const wxString& label, 
                     const wxString& helpString, bool checkable)
 {
   // 'checkable' parameter is useless for Windows.
-    Append(new wxMenuItem(this, Id, label, helpString, checkable));
+    Append(new wxMenuItem(this, id, label, helpString, checkable));
 }
 
 void wxMenu::Delete(int id)
@@ -156,7 +170,8 @@ void wxMenu::Delete(int id)
     wxMenuItem *item;
     int pos;
 
-    for (pos = 0, node = m_menuItems.First(); node; node = node->Next(), pos++) {
+    for (pos = 0, node = m_menuItems.First(); node; node = node->Next(), pos++) 
+    {
         item = (wxMenuItem *)node->Data();
         if (item->GetId() == id)
                break;
@@ -165,18 +180,27 @@ void wxMenu::Delete(int id)
     if (!node)
        return;
 
+    item->DestroyItem(TRUE);
+
+    // See also old code - don't know if this is needed (seems redundant).
+    /*
+  if (item->GetSubMenu()) {
+    item->subMenu->top_level_menu = item->GetSubMenu();
+    item->subMenu->window_parent = NULL;
+    children->DeleteObject(item->GetSubMenu());
+  }
+  */
+
     m_menuItems.DeleteNode(node);
     delete item;
-
-    // TODO
 }
 
-void wxMenu::Enable(int Id, bool Flag)
+void wxMenu::Enable(int id, bool flag)
 {
-    wxMenuItem *item = FindItemForId(Id);
+    wxMenuItem *item = FindItemForId(id);
     wxCHECK_RET( item != NULL, "can't enable non-existing menu item" );
 
-    item->Enable(Flag);
+    item->Enable(flag);
 }
 
 bool wxMenu::Enabled(int Id) const
@@ -195,9 +219,9 @@ void wxMenu::Check(int Id, bool Flag)
     item->Check(Flag);
 }
 
-bool wxMenu::Checked(int Id) const
+bool wxMenu::Checked(int id) const
 {
-    wxMenuItem *item = FindItemForId(Id);
+    wxMenuItem *item = FindItemForId(id);
     wxCHECK( item != NULL, FALSE );
 
     return item->IsChecked();
@@ -206,7 +230,21 @@ bool wxMenu::Checked(int Id) const
 void wxMenu::SetTitle(const wxString& label)
 {
     m_title = label ;
-    // TODO
+
+    wxNode *node = m_menuItems.First ();
+    if (!node)
+      return;
+
+    wxMenuItem *item = (wxMenuItem *) node->Data ();
+    Widget widget = (Widget) item->GetButtonWidget();
+    if (!widget)
+      return;
+
+    XmString title_str = XmStringCreateSimple ((char*) (const char*) label);
+    XtVaSetValues (widget,
+                   XmNlabelString, title_str,
+                   NULL);
+    // TODO: should we delete title_str now?
 }
 
 const wxString wxMenu::GetTitle() const
@@ -216,25 +254,39 @@ const wxString wxMenu::GetTitle() const
 
 void wxMenu::SetLabel(int id, const wxString& label)
 {
-    wxMenuItem *item = FindItemForId(id) ;
-    if (item==NULL)
-        return;
+    wxMenuItem *item = FindItemForId(id);
+    if (item == (wxMenuItem*) NULL)
+      return;
 
-    if (item->GetSubMenu()==NULL)
-    {
-        // TODO
-    }
-    else
-    {
-        // TODO
-    }
-    item->SetName(label);
+    item->SetLabel(label);
 }
 
-wxString wxMenu::GetLabel(int Id) const
+wxString wxMenu::GetLabel(int id) const
 {
-    // TODO
-    return wxString("") ;
+  wxMenuItem *it = NULL;
+  WXWidget w = FindMenuItem (id, &it);
+  if (w)
+    {
+      XmString text;
+      char *s;
+      XtVaGetValues ((Widget) w,
+                    XmNlabelString, &text,
+                    NULL);
+
+      if (XmStringGetLtoR (text, XmSTRING_DEFAULT_CHARSET, &s))
+       {
+         wxString str(s);
+         XtFree (s);
+         return str;
+       }
+      else
+       {
+         XmStringFree (text);
+         return wxEmptyString;
+       }
+    }
+  else
+    return wxEmptyString;
 }
 
 // Finds the item id matching the given string, -1 if not found.
@@ -332,8 +384,66 @@ void wxMenu::ProcessCommand(wxCommandEvent & event)
 
 bool wxWindow::PopupMenu(wxMenu *menu, int x, int y)
 {
-    // TODO
+  Widget widget = (Widget) GetMainWidget();
+
+  /* The menuId field seems to be usused, so we'll use it to
+     indicate whether a menu is popped up or not:
+        0: Not currently created as a popup
+       -1: Created as a popup, but not active
+        1: Active popup.
+   */
+
+  if (menu->GetParent() && (menu->GetId() != -1))
     return FALSE;
+
+  if (menu->GetMainWidget()) {
+    menu->DestroyMenu(TRUE);
+  }
+
+  wxWindow *parent = this;
+
+  menu->SetId(1); /* Mark as popped-up */
+  menu->CreateMenu(NULL, widget, menu);
+  //  menu->SetParent(parent);
+  //  parent->children->Append(menu);  // Store menu for later deletion
+
+  Widget menuWidget = (Widget) menu->GetMainWidget();
+
+  int rootX = 0;
+  int rootY = 0;
+
+  int deviceX = x;
+  int deviceY = y;
+  /*
+  if (this->IsKindOf(CLASSINFO(wxCanvas)))
+  {
+    wxCanvas *canvas = (wxCanvas *) this;
+    deviceX = canvas->GetDC ()->LogicalToDeviceX (x);
+    deviceY = canvas->GetDC ()->LogicalToDeviceY (y);
+  }
+  */
+
+  Display *display = XtDisplay (widget);
+  Window rootWindow = RootWindowOfScreen (XtScreen((Widget)widget));
+  Window thisWindow = XtWindow (widget);
+  Window childWindow;
+  XTranslateCoordinates (display, thisWindow, rootWindow, (int) deviceX, (int) deviceY,
+                        &rootX, &rootY, &childWindow);
+
+  XButtonPressedEvent event;
+  event.type = ButtonPress;
+  event.button = 1;
+
+  event.x = deviceX;
+  event.y = deviceY;
+
+  event.x_root = rootX;
+  event.y_root = rootY;
+
+  XmMenuPosition (menuWidget, &event);
+  XtManageChild (menuWidget);
+
+  return TRUE;
 }
 
 // Menu Bar
@@ -344,8 +454,6 @@ wxMenuBar::wxMenuBar()
     m_menus = NULL;
     m_titles = NULL;
     m_menuBarFrame = NULL;
-
-    // TODO
 }
 
 wxMenuBar::wxMenuBar(int n, wxMenu *menus[], const wxString titles[])
@@ -358,8 +466,6 @@ wxMenuBar::wxMenuBar(int n, wxMenu *menus[], const wxString titles[])
     for ( i = 0; i < n; i++ )
        m_titles[i] = titles[i];
     m_menuBarFrame = NULL;
-
-    // TODO
 }
 
 wxMenuBar::~wxMenuBar()
@@ -371,8 +477,6 @@ wxMenuBar::~wxMenuBar()
     }
     delete[] m_menus;
     delete[] m_titles;
-
-    // TODO
 }
 
 // Must only be used AFTER menu has been attached to frame,
@@ -383,8 +487,7 @@ void wxMenuBar::Enable(int id, bool flag)
     wxMenuItem *item = FindItemForId(id, &itemMenu) ;
     if (!item)
         return;
-
-    // TODO
+    item->Enable(flag);
 }
 
 void wxMenuBar::EnableTop(int pos, bool flag)
@@ -404,7 +507,7 @@ void wxMenuBar::Check(int id, bool flag)
     if (!item->IsCheckable())
         return ;
 
-    // TODO
+    item->Check(flag);
 }
 
 bool wxMenuBar::Checked(int id) const
@@ -414,8 +517,7 @@ bool wxMenuBar::Checked(int id) const
     if (!item)
         return FALSE;
 
-    // TODO
-    return FALSE;
+    return item->IsChecked();
 }
 
 bool wxMenuBar::Enabled(int id) const
@@ -425,11 +527,9 @@ bool wxMenuBar::Enabled(int id) const
     if (!item)
         return FALSE;
 
-    // TODO
-    return FALSE ;
+    return item->IsEnabled();
 }
 
-
 void wxMenuBar::SetLabel(int id, const wxString& label)
 {
     wxMenu *itemMenu = NULL;
@@ -438,7 +538,7 @@ void wxMenuBar::SetLabel(int id, const wxString& label)
     if (!item)
         return;
 
-    // TODO
+    item->SetLabel(label);
 }
 
 wxString wxMenuBar::GetLabel(int id) const
@@ -449,31 +549,81 @@ wxString wxMenuBar::GetLabel(int id) const
     if (!item)
         return wxString("");
 
-    // TODO
-    return wxString("") ;
+    return item->GetLabel();
 }
 
 void wxMenuBar::SetLabelTop(int pos, const wxString& label)
 {
-    // TODO
+  wxASSERT( (pos < m_menuCount) );
+
+  Widget w = (Widget) m_menus[pos]->GetButtonWidget();
+  if (w)
+    {
+      XmString label_str = XmStringCreateSimple ((char*) (const char*) label);
+      XtVaSetValues (w,
+                    XmNlabelString, label_str,
+                    NULL);
+      XmStringFree (label_str);
+    }
 }
 
 wxString wxMenuBar::GetLabelTop(int pos) const
 {
-    // TODO
-    return wxString("");
+  wxASSERT( (pos < m_menuCount) );
+
+  Widget w = (Widget) m_menus[pos]->GetButtonWidget();
+  if (w)
+    {
+      XmString text;
+      char *s;
+      XtVaGetValues (w,
+                    XmNlabelString, &text,
+                    NULL);
+
+      if (XmStringGetLtoR (text, XmSTRING_DEFAULT_CHARSET, &s))
+       {
+         wxString str(s);
+         XtFree (s);
+         return str;
+       }
+      else
+       {
+         return wxEmptyString;
+       }
+    }
+  else
+    return wxEmptyString;
+
 }
 
-bool wxMenuBar::OnDelete(wxMenu *a_menu, int pos)
+bool wxMenuBar::OnDelete(wxMenu *menu, int pos)
 {
-    // TODO
-    return FALSE;
+    // Only applies to dynamic deletion (when set in frame)
+    if (!m_menuBarFrame)
+        return TRUE;
+
+    menu->DestroyMenu(TRUE);
+    return TRUE;
 }
 
-bool wxMenuBar::OnAppend(wxMenu *a_menu, const char *title)
+bool wxMenuBar::OnAppend(wxMenu *menu, const char *title)
 {
-    // TODO
-    return FALSE;
+    // Only applies to dynamic append (when set in frame)
+    if (!m_menuBarFrame)
+        return TRUE;
+
+    // Probably should be an assert here
+    if (menu->GetParent())
+        return FALSE;
+
+    // Has already been appended
+    if (menu->GetButtonWidget())
+        return FALSE;
+
+    WXWidget w = menu->CreateMenu(this, GetMainWidget(), menu, title, TRUE);
+    menu->SetButtonWidget(w);
+
+    return TRUE;
 }
 
 void wxMenuBar::Append (wxMenu * menu, const wxString& title)
@@ -504,7 +654,8 @@ void wxMenuBar::Append (wxMenu * menu, const wxString& title)
     m_menus[m_menuCount - 1] = (wxMenu *)menu;
     m_titles[m_menuCount - 1] = title;
 
-    // TODO
+    menu->SetMenuBar(this);
+    menu->SetParent(this);
 }
 
 void wxMenuBar::Delete(wxMenu * menu, int i)
@@ -531,7 +682,7 @@ void wxMenuBar::Delete(wxMenu * menu, int i)
     if (!OnDelete(menu, ii))
         return;
 
-    menu->SetParent(NULL);
+    menu->SetParent((wxEvtHandler*) NULL);
 
     -- m_menuCount;
     for (j = ii; j < m_menuCount; j++)
@@ -558,7 +709,7 @@ int wxMenuBar::FindMenuItem (const wxString& menuString, const wxString& itemStr
     return -1;
 }
 
-wxMenuItem *wxMenuBar::FindItemForId (int Id, wxMenu ** itemMenu) const
+wxMenuItem *wxMenuBar::FindItemForId (int id, wxMenu ** itemMenu) const
 {
     if (itemMenu)
         *itemMenu = NULL;
@@ -566,33 +717,199 @@ wxMenuItem *wxMenuBar::FindItemForId (int Id, wxMenu ** itemMenu) const
     wxMenuItem *item = NULL;
     int i;
     for (i = 0; i < m_menuCount; i++)
-        if ((item = m_menus[i]->FindItemForId (Id, itemMenu)))
+        if ((item = m_menus[i]->FindItemForId (id, itemMenu)))
             return item;
     return NULL;
 }
 
-void wxMenuBar::SetHelpString (int Id, const wxString& helpString)
+void wxMenuBar::SetHelpString (int id, const wxString& helpString)
 {
     int i;
     for (i = 0; i < m_menuCount; i++)
     {
-        if (m_menus[i]->FindItemForId (Id))
+        if (m_menus[i]->FindItemForId (id))
         {
-            m_menus[i]->SetHelpString (Id, helpString);
+            m_menus[i]->SetHelpString (id, helpString);
             return;
         }
     }
 }
 
-wxString wxMenuBar::GetHelpString (int Id) const
+wxString wxMenuBar::GetHelpString (int id) const
 {
     int i;
     for (i = 0; i < m_menuCount; i++)
     {
-        if (m_menus[i]->FindItemForId (Id))
-            return wxString(m_menus[i]->GetHelpString (Id));
+        if (m_menus[i]->FindItemForId (id))
+            return wxString(m_menus[i]->GetHelpString (id));
     }
     return wxString("");
 }
 
+//// Motif-specific
+
+extern wxApp *wxTheApp;
+static XtWorkProcId WorkProcMenuId;
+
+/* Since PopupMenu under Motif stills grab right mouse button events
+ * after it was closed, we need to delete the associated widgets to
+ * allow next PopUpMenu to appear...
+ */
+
+int PostDeletionOfMenu( XtPointer* clientData )
+{
+  XtRemoveWorkProc(WorkProcMenuId);
+  wxMenu *menu = (wxMenu *)clientData;
+
+  if (menu->GetMainWidget()) {
+    wxList& list = menu->GetParent()->GetItems();
+    list.DeleteObject(menu);
+    menu->DestroyMenu(TRUE);
+  }
+  /* Mark as no longer popped up */
+  menu->m_menuId = -1;
+  return TRUE;
+}
+
+void 
+wxMenuPopdownCallback(Widget w, XtPointer clientData,
+                     XtPointer ptr)
+{
+  wxMenu *menu = (wxMenu *)clientData;
+
+  // Added by JOREL Jean-Charles <jjorel@silr.ireste.fr>
+  /* Since Callbacks of MenuItems are not yet processed, we put a
+   * background job which will be done when system will be idle.
+   * What awful hack!! :(
+   */
+
+  WorkProcMenuId = XtAppAddWorkProc( 
+               (XtAppContext) wxTheApp->GetAppContext(), 
+               (XtWorkProc) PostDeletionOfMenu,
+               (XtPointer) menu );
+  // Apparently not found in Motif headers
+  //  XtVaSetValues( w, XmNpopupEnabled, XmPOPUP_DISABLED, NULL );
+}
+
+/*
+ * Create a popup or pulldown menu.
+ * Submenus of a popup will be pulldown.
+ *
+ */
+
+WXWidget wxMenu::CreateMenu (wxMenuBar * menuBar, WXWidget parent, wxMenu * topMenu, const wxString& title, bool pullDown)
+{
+  Widget menu = (Widget) 0;
+  Widget buttonWidget = (Widget) 0;
+  Arg args[5];
+  XtSetArg (args[0], XmNnumColumns, m_numColumns);
+  XtSetArg (args[1], XmNpacking, XmPACK_COLUMN);
+
+  if (!pullDown)
+    {
+      menu = XmCreatePopupMenu ((Widget) parent, "popup", args, 2);
+      XtAddCallback(menu,
+                   XmNunmapCallback, 
+                   (XtCallbackProc)wxMenuPopdownCallback,
+                   (XtPointer)this);
+    }
+  else
+    {
+      char mnem = wxFindMnemonic (title);
+      wxStripMenuCodes ((char*) (const char*) title, wxBuffer);
+
+      menu = XmCreatePulldownMenu ((Widget) parent, "pulldown", args, 2);
+
+      XmString label_str = XmStringCreateSimple (wxBuffer);
+      buttonWidget = XtVaCreateManagedWidget (wxBuffer,
+#if wxUSE_GADGETS
+                                        xmCascadeButtonGadgetClass, (Widget) parent,
+#else
+                                        xmCascadeButtonWidgetClass, (Widget) parent,
+#endif
+                                             XmNlabelString, label_str,
+                                             XmNsubMenuId, menu,
+                                             NULL);
+
+      if (mnem != 0)
+       XtVaSetValues (buttonWidget, XmNmnemonic, mnem, NULL);
+
+      XmStringFree (label_str);
+    }
+
+  m_menuWidget = (WXWidget) menu;
+
+  m_menuBar = menuBar;
+  m_topLevelMenu = topMenu;
+
+  for (wxNode * node = m_menuItems.First (); node; node = node->Next ())
+    {
+      wxMenuItem *item = (wxMenuItem *) node->Data ();
+      item->CreateItem (menu, menuBar, topMenu);
+    }
+
+  return buttonWidget;
+}
+
+// Destroys the Motif implementation of the menu,
+// but maintains the wxWindows data structures so we can
+// do a CreateMenu again. 
+void wxMenu::DestroyMenu (bool full)
+{
+  for (wxNode * node = m_menuItems.First (); node; node = node->Next ())
+    {
+      wxMenuItem *item = (wxMenuItem *) node->Data ();
+      item->SetMenuBar((wxMenuBar*) NULL);
+
+      item->DestroyItem(full);
+    }                          // for()
+
+  if (m_buttonWidget)
+    {
+      if (full)
+      {
+        XtVaSetValues((Widget) m_buttonWidget, XmNsubMenuId, NULL, NULL);
+        XtDestroyWidget ((Widget) m_buttonWidget);
+        m_buttonWidget = (WXWidget) 0;
+      }
+    }
+  if (m_menuWidget && full)
+    {
+      XtDestroyWidget((Widget) m_menuWidget);
+      m_menuWidget = (WXWidget) NULL;
+    }
+}
+
+WXWidget wxMenu::FindMenuItem (int id, wxMenuItem ** it) const
+{
+  if (id == m_menuId)
+    {
+      if (it)
+       *it = (wxMenuItem*) NULL;
+      return m_buttonWidget;
+    }
 
+  for (wxNode * node = m_menuItems.First (); node; node = node->Next ())
+    {
+      wxMenuItem *item = (wxMenuItem *) node->Data ();
+      if (item->GetId() == id)
+       {
+         if (it)
+           *it = item;
+         return item->GetButtonWidget();
+       }
+
+      if (item->GetSubMenu())
+       {
+         WXWidget w = item->GetSubMenu()->FindMenuItem (id, it);
+         if (w)
+           {
+             return w;
+           }
+       }
+    }                          // for()
+
+  if (it)
+    *it = (wxMenuItem*) NULL;
+  return (WXWidget) NULL;
+}