]> git.saurik.com Git - wxWidgets.git/blobdiff - src/os2/menu.cpp
Correction to accelerator bug fix: add FVIRTKEY if ctrl, alt or shift
[wxWidgets.git] / src / os2 / menu.cpp
index 8ff449298fbe16a47a29ad05915dcbe3eac60995..e307253b9130661983ea2edc3274112a4e6e6cb9 100644 (file)
@@ -17,6 +17,7 @@
 #include "wx/wxprec.h"
 
 #ifndef WX_PRECOMP
+    #include "wx/app.h"
     #include "wx/frame.h"
     #include "wx/menu.h"
     #include "wx/utils.h"
@@ -48,6 +49,15 @@ extern wxMenu*                      wxCurrentPopupMenu;
 //
 static const int                    idMenuTitle = -2;
 
+//
+// The unique ID for Menus
+//
+#ifdef __VISAGECPP__
+USHORT                              wxMenu::m_nextMenuId = 0;
+#else
+static USHORT                       wxMenu::m_nextMenuId = 0;
+#endif
+
 // ----------------------------------------------------------------------------
 // macros
 // ----------------------------------------------------------------------------
@@ -55,6 +65,44 @@ static const int                    idMenuTitle = -2;
     IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
     IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxEvtHandler)
 
+// ----------------------------------------------------------------------------
+// static function for translating menu labels
+// ----------------------------------------------------------------------------
+
+static wxString TextToLabel(const wxString& rTitle)
+{
+    wxString Title;
+    const wxChar *pc;
+    for (pc = rTitle; *pc != wxT('\0'); pc++ )
+    {
+        if (*pc == wxT('&') )
+        {
+            if (*(pc+1) == wxT('&'))
+            {
+                pc++;
+                Title << wxT('&');
+            }
+            else
+                Title << wxT('~');
+        }
+//         else if (*pc == wxT('/'))
+//         {
+//             Title << wxT('\\');
+//         }
+        else
+        {
+            if ( *pc == wxT('~') )
+            {
+                // tildes must be doubled to prevent them from being
+                // interpreted as accelerator character prefix by PM ???
+                Title << *pc;
+            }
+            Title << *pc;
+        }
+    }
+    return Title;
+}
+
 // ============================================================================
 // implementation
 // ============================================================================
@@ -74,7 +122,7 @@ void wxMenu::Init()
     // Create the menu (to be used as a submenu or a popup)
     //
     if ((m_hMenu =  ::WinCreateWindow( HWND_DESKTOP
-                                      ,(const wxChar*)WC_MENU
+                                      ,WC_MENU
                                       ,"Menu"
                                       ,0L
                                       ,0L
@@ -86,10 +134,16 @@ void wxMenu::Init()
                                       ,0L
                                       ,NULL
                                       ,NULL
-                                     )) != 0)
+                                     )) == 0)
     {
         wxLogLastError("WinLoadMenu");
     }
+    m_vMenuData.iPosition   = 0;
+    m_vMenuData.afStyle     = MIS_SUBMENU | MIS_TEXT;
+    m_vMenuData.afAttribute = (USHORT)0;
+    m_vMenuData.id          = m_nextMenuId++;
+    m_vMenuData.hwndSubMenu = m_hMenu;
+    m_vMenuData.hItem       = NULLHANDLE;
 
     //
     // If we have a title, insert it in the beginning of the menu
@@ -124,7 +178,9 @@ wxMenu::~wxMenu()
     //
     // Delete accels
     //
+#if (!(defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 )))
     WX_CLEAR_ARRAY(m_vAccels);
+#endif
 #endif // wxUSE_ACCEL
 } // end of wxMenu::~wxMenu
 
@@ -207,44 +263,60 @@ bool wxMenu::DoInsertOrAppend(
 , size_t                            nPos
 )
 {
+    ERRORID                         vError;
+    wxString                        sError;
+    char                            zMsg[128];
 #if wxUSE_ACCEL
     UpdateAccel(pItem);
 #endif // wxUSE_ACCEL
 
+    //
+    // rItem is the member MENUITEM for the  menu items and the submenu's
+    // MENUITEM for submenus as required by ::MM_INSERTITEM message API
+    //
+
+    wxMenu*                         pSubmenu = pItem->GetSubMenu();
+    MENUITEM&                       rItem = (pSubmenu != NULL)?pSubmenu->m_vMenuData:
+                                            pItem->m_vMenuData;
+    if(pSubmenu != NULL)
+    {
+        wxASSERT_MSG(pSubmenu->GetHMenu(), wxT("invalid submenu"));
+        pSubmenu->SetParent(this);
+        rItem.afStyle |= MIS_SUBMENU | MIS_TEXT;
+    }
+
     //
     // If "Break" has just been called, insert a menu break before this item
     // (and don't forget to reset the flag)
     //
     if (m_bDoBreak)
     {
-        m_vMenuData.afStyle |= MIS_BREAK;
+        rItem.afStyle |= MIS_BREAK;
         m_bDoBreak = FALSE;
     }
 
     if (pItem->IsSeparator())
     {
-        m_vMenuData.afStyle |= MIS_SEPARATOR;
+        rItem.afStyle |= MIS_SEPARATOR;
     }
 
     //
     // Id is the numeric id for normal menu items and HMENU for submenus as
-    // required by ::WinInsertMenu() API
+    // required by ::MM_INSERTITEM message API
     //
 
-    wxMenu*                         pSubmenu = pItem->GetSubMenu();
-
     if (pSubmenu != NULL)
     {
         wxASSERT_MSG(pSubmenu->GetHMenu(), wxT("invalid submenu"));
         pSubmenu->SetParent(this);
 
-        m_vMenuData.iPosition = 0; // submenus have a 0 position
-        m_vMenuData.id = (USHORT)pSubmenu->GetHMenu();
-        m_vMenuData.afStyle |= MIS_SUBMENU;
+        rItem.iPosition = 0; // submenus have a 0 position
+        rItem.id = (USHORT)pSubmenu->GetHMenu();
+        rItem.afStyle |= MIS_SUBMENU | MIS_TEXT;
     }
     else
     {
-        m_vMenuData.id = pItem->GetId();
+        rItem.id = pItem->GetId();
     }
 
     BYTE*                           pData;
@@ -254,11 +326,14 @@ bool wxMenu::DoInsertOrAppend(
     {
         //
         // Want to get {Measure|Draw}Item messages?
-        // item draws itself, pass pointer to it in data parameter
-        // Will eventually need to set the image handle somewhere into m_vMenuData.hItem
+        // item draws itself, passing pointer to data doesn't work in OS/2
+        // Will eventually need to set the image handle somewhere into vItem.hItem
         //
-        m_vMenuData.afStyle |= MIS_OWNERDRAW;
-        pData = (BYTE*)pItem;
+        rItem.afStyle |= MIS_OWNERDRAW;
+        pData = (BYTE*)NULL;
+        rItem.hItem = (HBITMAP)pItem->GetBitmap().GetHBITMAP();
+        pItem->m_vMenuData.afStyle = rItem.afStyle;
+        pItem->m_vMenuData.hItem = rItem.hItem;
     }
     else
 #endif
@@ -266,46 +341,46 @@ bool wxMenu::DoInsertOrAppend(
         //
         // Menu is just a normal string (passed in data parameter)
         //
-        m_vMenuData.afStyle |= MIS_TEXT;
+        rItem.afStyle |= MIS_TEXT;
         pData = (char*)pItem->GetText().c_str();
     }
 
-    BOOL                            bOk;
-
-    //
-    // -1 means this is a sub menu not a menuitem.  We must create a window for it.
-    // Submenus are also attached to a menubar so its parent and owner should be the handle of the menubar.
-    //
     if (nPos == (size_t)-1)
     {
-        HWND                        hSubMenu = ::WinCreateWindow( GetWinHwnd(m_menuBar) // parent
-                                                                 ,WC_MENU               // type
-                                                                 ,"Menu"                // a generic name
-                                                                 ,0L                    // no style flag
-                                                                 ,0L,0L,0L,0L           // no position
-                                                                 ,GetWinHwnd(m_menuBar) // no owner
-                                                                 ,HWND_TOP              // always on top
-                                                                 ,0L                    // no ID needed for dynamic creation
-                                                                 ,NULL                  // no control data
-                                                                 ,NULL                  // no presentation params
-                                                                );
-
-        m_vMenuData.iPosition   = 0;
-        m_vMenuData.hwndSubMenu = hSubMenu;
-        m_vMenuData.hItem       = NULLHANDLE;
-
-        bOk = (bool)::WinSendMsg(GetHmenu(), MM_INSERTITEM, (MPARAM)&m_vMenuData, (MPARAM)pItem->GetText().c_str());
+        rItem.iPosition = MIT_END;
     }
     else
     {
-        m_vMenuData.iPosition   = nPos;
-        m_vMenuData.hwndSubMenu = NULLHANDLE;
-        m_vMenuData.hItem       = NULLHANDLE;
-        bOk = (bool)::WinSendMsg(GetHmenu(), MM_INSERTITEM, (MPARAM)&m_vMenuData, (MPARAM)pItem->GetText().c_str());
+        rItem.iPosition = nPos;
     }
 
-    if (!bOk)
+    APIRET                          rc;
+
+    rc = (APIRET)::WinSendMsg( GetHmenu()
+                              ,MM_INSERTITEM
+                              ,(MPARAM)&rItem
+                              ,(MPARAM)pData
+                             );
+#if wxUSE_OWNER_DRAWN
+    if (pItem->IsOwnerDrawn())
     {
+        BOOL                       rc;
+        MENUITEM                   vMenuItem;
+
+        ::WinSendMsg( GetHmenu()
+                     ,MM_QUERYITEM
+                     ,MPFROM2SHORT( (USHORT)pItem->GetId()
+                                   ,(USHORT)(FALSE)
+                                  )
+                     ,&vMenuItem
+                    );
+    }
+#endif
+    if (rc == MIT_MEMERROR || rc == MIT_ERROR)
+    {
+        vError = ::WinGetLastError(vHabmain);
+        sError = wxPMErrorToStr(vError);
+        wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError);
         wxLogLastError("Insert or AppendMenu");
         return FALSE;
     }
@@ -314,7 +389,7 @@ bool wxMenu::DoInsertOrAppend(
         //
         // If we're already attached to the menubar, we must update it
         //
-        if (IsAttached())
+        if (IsAttached() && m_menuBar->IsAttached())
         {
             m_menuBar->Refresh();
         }
@@ -385,7 +460,7 @@ wxMenuItem* wxMenu::DoRemove(
                  ,MPFROM2SHORT(pItem->GetId(), TRUE)
                  ,(MPARAM)0
                 );
-    if (IsAttached())
+    if (IsAttached() && m_menuBar->IsAttached())
     {
         //
         // Otherwise, the chane won't be visible
@@ -561,6 +636,42 @@ wxWindow* wxMenu::GetWindow() const
     return NULL;
 } // end of wxMenu::GetWindow
 
+// recursive search for item by id
+wxMenuItem* wxMenu::FindItem(
+  int                               nItemId
+, ULONG                             hItem
+, wxMenu**                          ppItemMenu
+) const
+{
+    if ( ppItemMenu )
+        *ppItemMenu = NULL;
+
+    wxMenuItem*                     pItem = NULL;
+
+    for ( wxMenuItemList::Node *node = m_items.GetFirst();
+          node && !pItem;
+          node = node->GetNext() )
+    {
+        pItem = node->GetData();
+
+        if ( pItem->GetId() == nItemId && pItem->m_vMenuData.hItem == hItem)
+        {
+            if ( ppItemMenu )
+                *ppItemMenu = (wxMenu *)this;
+        }
+        else if ( pItem->IsSubMenu() )
+        {
+            pItem = pItem->GetSubMenu()->FindItem(nItemId, hItem, ppItemMenu);
+        }
+        else
+        {
+            // don't exit the loop
+            pItem = NULL;
+        }
+    }
+    return pItem;
+} // end of wxMenu::FindItem
+
 // ---------------------------------------------------------------------------
 // Menu Bar
 // ---------------------------------------------------------------------------
@@ -636,22 +747,22 @@ WXHMENU wxMenuBar::Create()
     //
     // Create an empty menu and then fill it with insertions
     //
-    if (!wxWindow::OS2Create( hFrame
-                             ,WC_MENU
-                             ,"Menu"
-                             ,MS_ACTIONBAR | WS_SYNCPAINT | WS_VISIBLE
-                             ,0L
-                             ,0L
-                             ,0L
-                             ,0L
-                             ,hFrame
-                             ,HWND_TOP
-                             ,FID_MENU
-                             ,(PVOID)NULL
-                             ,(PVOID)NULL
-                            ))
-    {
-        wxLogLastError("CreateMenu");
+    if ((m_hMenu =  ::WinCreateWindow( hFrame
+                                      ,WC_MENU
+                                      ,(PSZ)NULL
+                                      ,MS_ACTIONBAR | WS_SYNCPAINT | WS_VISIBLE
+                                      ,0L
+                                      ,0L
+                                      ,0L
+                                      ,0L
+                                      ,hFrame
+                                      ,HWND_TOP
+                                      ,FID_MENU
+                                      ,NULL
+                                      ,NULL
+                                     )) == 0)
+    {
+        wxLogLastError("WinLoadMenu");
     }
     else
     {
@@ -659,14 +770,41 @@ WXHMENU wxMenuBar::Create()
 
         for (size_t i = 0; i < nCount; i++)
         {
-            vItem.iPosition   = 0;
-            vItem.afStyle     = MIS_SUBMENU | MIS_TEXT;
-            vItem.afAttribute = (USHORT)0;
-            vItem.id          = (USHORT)0;
-            vItem.hwndSubMenu = m_menus[i]->GetHMenu();
-            vItem.hItem       = NULLHANDLE;
-
-            ::WinSendMsg(GetHmenu(), MM_INSERTITEM, (MPARAM)&vItem, (MPARAM)m_titles[i].c_str());
+            APIRET                  rc;
+            ERRORID                 vError;
+            wxString                sError;
+            HWND                    hSubMenu;
+
+            //
+            // Set the parent and owner of the submenues to be the menubar, not the desktop
+            //
+            hSubMenu = m_menus[i]->m_vMenuData.hwndSubMenu;
+            if (!::WinSetParent(m_menus[i]->m_vMenuData.hwndSubMenu, m_hMenu, FALSE))
+            {
+                vError = ::WinGetLastError(vHabmain);
+                sError = wxPMErrorToStr(vError);
+                wxLogError("Error setting parent for submenu. Error: %s\n", sError);
+                return NULLHANDLE;
+            }
+
+            if (!::WinSetOwner(m_menus[i]->m_vMenuData.hwndSubMenu, m_hMenu))
+            {
+                vError = ::WinGetLastError(vHabmain);
+                sError = wxPMErrorToStr(vError);
+                wxLogError("Error setting parent for submenu. Error: %s\n", sError);
+                return NULLHANDLE;
+            }
+
+            m_menus[i]->m_vMenuData.iPosition = i;
+
+            rc = (APIRET)::WinSendMsg(m_hMenu, MM_INSERTITEM, (MPARAM)&m_menus[i]->m_vMenuData, (MPARAM)m_titles[i].c_str());
+            if (rc == MIT_MEMERROR || rc == MIT_ERROR)
+            {
+                vError = ::WinGetLastError(vHabmain);
+                sError = wxPMErrorToStr(vError);
+                wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError);
+                return NULLHANDLE;
+            }
         }
     }
     return m_hMenu;
@@ -698,7 +836,7 @@ void wxMenuBar::EnableTop(
         wxLogLastError("LogLastError");
         return;
     }
-    ::WinSendMsg((HWND)m_hMenu, MM_SETITEMATTR, MPFROM2SHORT(nId, TRUE), MPFROM2SHORT(uFlag, uFlag));
+    ::WinSendMsg((HWND)m_hMenu, MM_SETITEMATTR, MPFROM2SHORT(nId, TRUE), MPFROM2SHORT(MIA_DISABLED, uFlag));
     Refresh();
 } // end of wxMenuBar::EnableTop
 
@@ -761,9 +899,10 @@ wxMenu* wxMenuBar::Replace(
 )
 {
     SHORT                            nId;
+    wxString                         Title = TextToLabel(rTitle);
     wxMenu*                          pMenuOld = wxMenuBarBase::Replace( nPos
                                                                        ,pMenu
-                                                                       ,rTitle
+                                                                       ,Title
                                                                       );
 
 
@@ -775,11 +914,11 @@ wxMenu* wxMenuBar::Replace(
     }
     if (!pMenuOld)
         return FALSE;
-    m_titles[nPos] = rTitle;
+    m_titles[nPos] = Title;
     if (IsAttached())
     {
-        ::WinSendMsg((HWND)m_hMenu, MM_DELETEITEM, MPFROM2SHORT(nId, TRUE), (MPARAM)0);
-        ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)rTitle.c_str());
+        ::WinSendMsg((HWND)m_hMenu, MM_REMOVEITEM, MPFROM2SHORT(nId, TRUE), (MPARAM)0);
+        ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)Title.c_str());
 
 #if wxUSE_ACCEL
         if (pMenuOld->HasAccels() || pMenu->HasAccels())
@@ -801,13 +940,14 @@ bool wxMenuBar::Insert(
 , const wxString&                   rTitle
 )
 {
+    wxString Title = TextToLabel(rTitle);
     if (!wxMenuBarBase::Insert( nPos
                                ,pMenu
-                               ,rTitle
+                               ,Title
                               ))
         return FALSE;
 
-    m_titles.Insert( rTitle
+    m_titles.Insert( Title
                     ,nPos
                    );
 
@@ -815,7 +955,7 @@ bool wxMenuBar::Insert(
 
     if (IsAttached())
     {
-        ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)rTitle.c_str());
+        ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)Title.c_str());
 #if wxUSE_ACCEL
         if (pMenu->HasAccels())
         {
@@ -837,16 +977,17 @@ bool wxMenuBar::Append(
 
     wxCHECK_MSG(hSubmenu, FALSE, wxT("can't append invalid menu to menubar"));
 
-    if (!wxMenuBarBase::Append(pMenu, rTitle))
+    wxString Title = TextToLabel(rTitle);
+    if (!wxMenuBarBase::Append(pMenu, Title))
         return FALSE;
 
     pMenu->Attach(this);
-    m_titles.Add(rTitle);
+    m_titles.Add(Title);
 
     if ( IsAttached() )
     {
         pMenu->m_vMenuData.iPosition = MIT_END;
-        ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)rTitle.c_str());
+        ::WinSendMsg((HWND)m_hMenu, MM_INSERTITEM, (MPARAM)&pMenu->m_vMenuData, (MPARAM)Title.c_str());
 #if wxUSE_ACCEL
         if (pMenu->HasAccels())
         {
@@ -879,7 +1020,7 @@ wxMenu* wxMenuBar::Remove(
     }
     if (IsAttached())
     {
-        ::WinSendMsg((HWND)GetHmenu(), MM_DELETEITEM, MPFROM2SHORT(nId, TRUE), (MPARAM)0);
+        ::WinSendMsg((HWND)GetHmenu(), MM_REMOVEITEM, MPFROM2SHORT(nId, TRUE), (MPARAM)0);
         pMenu->Detach();
 
 #if wxUSE_ACCEL
@@ -940,6 +1081,14 @@ void wxMenuBar::Attach(
 
 #if wxUSE_ACCEL
     RebuildAccelTable();
+    //
+    // Ensure the accelerator table is set to the frame (not the client!)
+    //
+    if (!::WinSetAccelTable( vHabmain
+                            ,(HWND)pFrame->GetHWND()
+                            ,m_vAccelTable.GetHACCEL()
+                           ))
+        wxLogLastError("WinSetAccelTable");
 #endif // wxUSE_ACCEL
 } // end of wxMenuBar::Attach
 
@@ -995,3 +1144,25 @@ wxMenuItem* wxMenuBar::FindItem(
     return pItem;
 } // end of wxMenuBar::FindItem
 
+wxMenuItem* wxMenuBar::FindItem(
+  int                               nId
+, ULONG                             hItem
+, wxMenu**                          ppItemMenu
+) const
+{
+    if (ppItemMenu)
+        *ppItemMenu = NULL;
+
+    wxMenuItem*                     pItem = NULL;
+    size_t                          nCount = GetMenuCount();
+
+    for (size_t i = 0; !pItem && (i < nCount); i++)
+    {
+        pItem = m_menus[i]->FindItem( nId
+                                     ,hItem
+                                     ,ppItemMenu
+                                    );
+    }
+    return pItem;
+} // end of wxMenuBar::FindItem
+