]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/menu.cpp
Implement undo and redo for the ie and gtk webkit backends. Extend the sample to...
[wxWidgets.git] / src / msw / menu.cpp
index a549d8b8204d0e7bf3bfbdb67afa296e6790a08a..4769e1b00b9a57002c063b4cc14b4746a786821c 100644 (file)
@@ -82,7 +82,7 @@
 // ----------------------------------------------------------------------------
 
 // the (popup) menu title has this special id
-static const UINT idMenuTitle = (UINT)-3;
+static const int idMenuTitle = wxID_NONE;
 
 // ----------------------------------------------------------------------------
 // private functions
@@ -112,7 +112,8 @@ void SetDefaultMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
 // make the given menu item owner-drawn
 void SetOwnerDrawnMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
                            UINT WXUNUSED_IN_WINCE(id),
-                           ULONG_PTR WXUNUSED_IN_WINCE(data))
+                           ULONG_PTR WXUNUSED_IN_WINCE(data),
+                           BOOL WXUNUSED_IN_WINCE(byPositon = FALSE))
 {
 #ifndef __WXWINCE__
     MENUITEMINFO mii;
@@ -122,7 +123,10 @@ void SetOwnerDrawnMenuItem(HMENU WXUNUSED_IN_WINCE(hmenu),
     mii.fType = MFT_OWNERDRAW;
     mii.dwItemData = data;
 
-    if ( !::SetMenuItemInfo(hmenu, id, FALSE, &mii) )
+    if ( reinterpret_cast<wxMenuItem*>(data)->IsSeparator() )
+        mii.fType |= MFT_SEPARATOR;
+
+    if ( !::SetMenuItemInfo(hmenu, id, byPositon, &mii) )
     {
         wxLogLastError(wxT("SetMenuItemInfo"));
     }
@@ -145,13 +149,10 @@ UINT GetMenuState(HMENU hMenu, UINT id, UINT flags)
 }
 #endif // __WXWINCE__
 
-inline bool IsLessThanStdSize(const wxBitmap& bmp)
+inline bool IsGreaterThanStdSize(const wxBitmap& bmp)
 {
-    // FIXME: these +4 are chosen so that 16*16 bitmaps pass this test with
-    //        default SM_CXMENUCHECK value but I have no idea what do we really
-    //        need to use here
-    return bmp.GetWidth() < ::GetSystemMetrics(SM_CXMENUCHECK) + 4 &&
-            bmp.GetHeight() < ::GetSystemMetrics(SM_CYMENUCHECK) + 4;
+    return bmp.GetWidth() > ::GetSystemMetrics(SM_CXMENUCHECK) ||
+            bmp.GetHeight() > ::GetSystemMetrics(SM_CYMENUCHECK);
 }
 
 } // anonymous namespace
@@ -160,100 +161,6 @@ inline bool IsLessThanStdSize(const wxBitmap& bmp)
 // implementation
 // ============================================================================
 
-#include "wx/listimpl.cpp"
-
-WX_DEFINE_LIST( wxMenuInfoList )
-
-#if wxUSE_EXTENDED_RTTI
-
-WX_DEFINE_FLAGS( wxMenuStyle )
-
-wxBEGIN_FLAGS( wxMenuStyle )
-    wxFLAGS_MEMBER(wxMENU_TEAROFF)
-wxEND_FLAGS( wxMenuStyle )
-
-IMPLEMENT_DYNAMIC_CLASS_XTI(wxMenu, wxEvtHandler,"wx/menu.h")
-
-wxCOLLECTION_TYPE_INFO( wxMenuItem * , wxMenuItemList ) ;
-
-template<> void wxCollectionToVariantArray( wxMenuItemList const &theList, wxxVariantArray &value)
-{
-    wxListCollectionToVariantArray<wxMenuItemList::compatibility_iterator>( theList , value ) ;
-}
-
-wxBEGIN_PROPERTIES_TABLE(wxMenu)
-    wxEVENT_PROPERTY( Select , wxEVT_COMMAND_MENU_SELECTED , wxCommandEvent)
-    wxPROPERTY( Title, wxString , SetTitle, GetTitle, wxString(), 0 /*flags*/ , wxT("Helpstring") , wxT("group") )
-    wxREADONLY_PROPERTY_FLAGS( MenuStyle , wxMenuStyle , long , GetStyle , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
-    wxPROPERTY_COLLECTION( MenuItems , wxMenuItemList , wxMenuItem* , Append , GetMenuItems , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
-wxEND_PROPERTIES_TABLE()
-
-wxBEGIN_HANDLERS_TABLE(wxMenu)
-wxEND_HANDLERS_TABLE()
-
-wxDIRECT_CONSTRUCTOR_2( wxMenu , wxString , Title , long , MenuStyle  )
-
-WX_DEFINE_FLAGS( wxMenuBarStyle )
-
-wxBEGIN_FLAGS( wxMenuBarStyle )
-    wxFLAGS_MEMBER(wxMB_DOCKABLE)
-wxEND_FLAGS( wxMenuBarStyle )
-
-// the negative id would lead the window (its superclass !) to vetoe streaming out otherwise
-bool wxMenuBarStreamingCallback( const wxObject *WXUNUSED(object), wxWriter * , wxPersister * , wxxVariantArray & )
-{
-    return true ;
-}
-
-IMPLEMENT_DYNAMIC_CLASS_XTI_CALLBACK(wxMenuBar, wxWindow ,"wx/menu.h",wxMenuBarStreamingCallback)
-
-IMPLEMENT_DYNAMIC_CLASS_XTI(wxMenuInfo, wxObject , "wx/menu.h" )
-
-wxBEGIN_PROPERTIES_TABLE(wxMenuInfo)
-    wxREADONLY_PROPERTY( Menu , wxMenu* , GetMenu , EMPTY_MACROVALUE , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
-    wxREADONLY_PROPERTY( Title , wxString , GetTitle , wxString() , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
-wxEND_PROPERTIES_TABLE()
-
-wxBEGIN_HANDLERS_TABLE(wxMenuInfo)
-wxEND_HANDLERS_TABLE()
-
-wxCONSTRUCTOR_2( wxMenuInfo , wxMenu* , Menu , wxString , Title )
-
-wxCOLLECTION_TYPE_INFO( wxMenuInfo * , wxMenuInfoList ) ;
-
-template<> void wxCollectionToVariantArray( wxMenuInfoList const &theList, wxxVariantArray &value)
-{
-    wxListCollectionToVariantArray<wxMenuInfoList::compatibility_iterator>( theList , value ) ;
-}
-
-wxBEGIN_PROPERTIES_TABLE(wxMenuBar)
-    wxPROPERTY_COLLECTION( MenuInfos , wxMenuInfoList , wxMenuInfo* , Append , GetMenuInfos , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
-wxEND_PROPERTIES_TABLE()
-
-wxBEGIN_HANDLERS_TABLE(wxMenuBar)
-wxEND_HANDLERS_TABLE()
-
-wxCONSTRUCTOR_DUMMY( wxMenuBar )
-
-#else
-IMPLEMENT_DYNAMIC_CLASS(wxMenu, wxEvtHandler)
-IMPLEMENT_DYNAMIC_CLASS(wxMenuBar, wxWindow)
-IMPLEMENT_DYNAMIC_CLASS(wxMenuInfo, wxObject)
-#endif
-
-const wxMenuInfoList& wxMenuBar::GetMenuInfos() const
-{
-    wxMenuInfoList* list = const_cast< wxMenuInfoList* >( &m_menuInfos ) ;
-    WX_CLEAR_LIST( wxMenuInfoList , *list ) ;
-    for( size_t i = 0 ; i < GetMenuCount() ; ++i )
-    {
-        wxMenuInfo* info = new wxMenuInfo() ;
-        info->Create( const_cast<wxMenuBar*>(this)->GetMenu(i) , GetMenuLabel(i) ) ;
-        list->Append( info ) ;
-    }
-    return m_menuInfos ;
-}
-
 // ---------------------------------------------------------------------------
 // wxMenu construction, adding and removing menu items
 // ---------------------------------------------------------------------------
@@ -267,6 +174,7 @@ void wxMenu::Init()
 #if wxUSE_OWNER_DRAWN
     m_ownerDrawn = false;
     m_maxBitmapWidth = 0;
+    m_maxAccelWidth = -1;
 #endif // wxUSE_OWNER_DRAWN
 
     // create the menu
@@ -279,8 +187,9 @@ void wxMenu::Init()
     // if we have a title, insert it in the beginning of the menu
     if ( !m_title.empty() )
     {
-        Append(idMenuTitle, m_title);
-        AppendSeparator();
+        const wxString title = m_title;
+        m_title.clear(); // so that SetTitle() knows there was no title before
+        SetTitle(title);
     }
 }
 
@@ -385,6 +294,8 @@ void wxMenu::UpdateAccel(wxMenuItem *item)
         {
             GetMenuBar()->RebuildAccelTable();
         }
+
+        ResetMaxAccelWidth();
     }
     //else: it is a separator, they can't have accels, nothing to do
 }
@@ -486,8 +397,8 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
         pos = GetMenuItemCount() - 1;
     }
 
-    // adjust position to account for the title, if any
-    if ( !m_title.empty() )
+    // adjust position to account for the title of a popup menu, if any
+    if ( !GetMenuBar() && !m_title.empty() )
         pos += 2; // for the title itself and its separator
 
     BOOL ok = false;
@@ -496,7 +407,7 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
     // Under older systems mixing owner-drawn and non-owner-drawn items results
     // in inconsistent margins, so we force this one to be owner-drawn if any
     // other items already are.
-    if ( m_ownerDrawn && !pItem->IsSeparator() )
+    if ( m_ownerDrawn )
         pItem->SetOwnerDrawn(true);
 #endif // wxUSE_OWNER_DRAWN
 
@@ -506,7 +417,7 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
     {
 #ifndef __DMC__
 
-        if ( !m_ownerDrawn )
+        if ( !m_ownerDrawn && !pItem->IsSeparator() )
         {
             // MIIM_BITMAP only works under WinME/2000+ so we always use owner
             // drawn item under the previous versions and we also have to use
@@ -522,8 +433,8 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
                 const wxBitmap& bmpUnchecked = pItem->GetBitmap(false),
                                 bmpChecked   = pItem->GetBitmap(true);
 
-                if ( (bmpUnchecked.Ok() && !IsLessThanStdSize(bmpUnchecked)) ||
-                     (bmpChecked.Ok()   && !IsLessThanStdSize(bmpChecked)) )
+                if ( (bmpUnchecked.Ok() && IsGreaterThanStdSize(bmpUnchecked)) ||
+                     (bmpChecked.Ok()   && IsGreaterThanStdSize(bmpChecked)) )
                 {
                     mustUseOwnerDrawn = true;
                 }
@@ -631,27 +542,31 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
             // make other item ownerdrawn and update margin width for equals alignment
             if ( !m_ownerDrawn || updateAllMargins )
             {
+                // we must use position in SetOwnerDrawnMenuItem because
+                // all separators have the same id
+                int pos = 0;
                 wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
                 while (node)
                 {
                     wxMenuItem* item = node->GetData();
 
-                    if ( !item->IsSeparator() )
+                    if ( !item->IsOwnerDrawn())
                     {
-                        if ( !item->IsOwnerDrawn() )
-                        {
-                            item->SetOwnerDrawn(true);
-                            SetOwnerDrawnMenuItem(GetHmenu(), item->GetMSWId(),
-                                                  reinterpret_cast<ULONG_PTR>(item));
-                        }
-                        item->SetMarginWidth(m_maxBitmapWidth);
+                        item->SetOwnerDrawn(true);
+                        SetOwnerDrawnMenuItem(GetHmenu(), pos,
+                                              reinterpret_cast<ULONG_PTR>(item), TRUE);
                     }
 
+                    item->SetMarginWidth(m_maxBitmapWidth);
+
                     node = node->GetNext();
+                    pos++;
                 }
 
                 // set menu as ownerdrawn
                 m_ownerDrawn = true;
+
+                ResetMaxAccelWidth();
             }
             // only update our margin for equals alignment to other item
             else if ( !updateAllMargins )
@@ -686,7 +601,7 @@ bool wxMenu::DoInsertOrAppend(wxMenuItem *pItem, size_t pos)
 
 
     // if we just appended the title, highlight it
-    if ( id == idMenuTitle )
+    if ( id == (UINT_PTR)idMenuTitle )
     {
         // visually select the menu title
         SetDefaultMenuItem(GetHmenu(), id);
@@ -796,6 +711,8 @@ wxMenuItem *wxMenu::DoRemove(wxMenuItem *item)
         delete m_accels[n];
 
         m_accels.RemoveAt(n);
+
+        ResetMaxAccelWidth();
     }
     //else: this item doesn't have an accel, nothing to do
 #endif // wxUSE_ACCEL
@@ -846,6 +763,34 @@ wxAcceleratorTable *wxMenu::CreateAccelTable() const
 
 #endif // wxUSE_ACCEL
 
+// ---------------------------------------------------------------------------
+// ownerdrawn helpers
+// ---------------------------------------------------------------------------
+
+#if wxUSE_OWNER_DRAWN
+
+void wxMenu::CalculateMaxAccelWidth()
+{
+    wxASSERT_MSG( m_maxAccelWidth == -1, wxT("it's really needed?") );
+
+    wxMenuItemList::compatibility_iterator node = GetMenuItems().GetFirst();
+    while (node)
+    {
+        wxMenuItem* item = node->GetData();
+
+        if ( item->IsOwnerDrawn() )
+        {
+            int width = item->MeasureAccelWidth();
+            if (width > m_maxAccelWidth )
+                m_maxAccelWidth = width;
+        }
+
+        node = node->GetNext();
+    }
+}
+
+#endif // wxUSE_OWNER_DRAWN
+
 // ---------------------------------------------------------------------------
 // set wxMenu title
 // ---------------------------------------------------------------------------
@@ -862,7 +807,7 @@ void wxMenu::SetTitle(const wxString& label)
         if ( !label.empty() )
         {
             if ( !::InsertMenu(hMenu, 0u, MF_BYPOSITION | MF_STRING,
-                               idMenuTitle, m_title.wx_str()) ||
+                               (UINT_PTR)idMenuTitle, m_title.wx_str()) ||
                  !::InsertMenu(hMenu, 1u, MF_BYPOSITION, (unsigned)-1, NULL) )
             {
                 wxLogLastError(wxT("InsertMenu"));
@@ -898,7 +843,7 @@ void wxMenu::SetTitle(const wxString& label)
 #else
             if ( !ModifyMenu(hMenu, 0u,
                              MF_BYPOSITION | MF_STRING,
-                             idMenuTitle, m_title.wx_str()) )
+                             (UINT_PTR)idMenuTitle, m_title.wx_str()) )
             {
                 wxLogLastError(wxT("ModifyMenu"));
             }
@@ -910,7 +855,7 @@ void wxMenu::SetTitle(const wxString& label)
     // put the title string in bold face
     if ( !m_title.empty() )
     {
-        SetDefaultMenuItem(GetHmenu(), idMenuTitle);
+        SetDefaultMenuItem(GetHmenu(), (UINT_PTR)idMenuTitle);
     }
 #endif // Win32
 }
@@ -924,7 +869,7 @@ bool wxMenu::MSWCommand(WXUINT WXUNUSED(param), WXWORD id_)
     const int id = (signed short)id_;
 
     // ignore commands from the menu title
-    if ( id != (int)idMenuTitle )
+    if ( id != idMenuTitle )
     {
         // update the check item when it's clicked
         wxMenuItem * const item = FindItem(id);
@@ -940,17 +885,27 @@ bool wxMenu::MSWCommand(WXUINT WXUNUSED(param), WXWORD id_)
     return true;
 }
 
-// ---------------------------------------------------------------------------
-// other
-// ---------------------------------------------------------------------------
-
-wxWindow *wxMenu::GetWindow() const
+// get the menu with given handle (recursively)
+wxMenu* wxMenu::MSWGetMenu(WXHMENU hMenu)
 {
-    if ( m_invokingWindow != NULL )
-        return m_invokingWindow;
-    else if ( GetMenuBar() != NULL)
-        return GetMenuBar()->GetFrame();
+    // check self
+    if ( GetHMenu() == hMenu )
+        return this;
 
+    // recursively query submenus
+    for ( size_t n = 0 ; n < GetMenuItemCount(); ++n )
+    {
+        wxMenuItem* item = FindItemByPosition(n);
+        wxMenu* submenu = item->GetSubMenu();
+        if ( submenu )
+        {
+            submenu = submenu->MSWGetMenu(hMenu);
+            if (submenu)
+                return submenu;
+        }
+    }
+
+    // unknown hMenu
     return NULL;
 }
 
@@ -988,12 +943,13 @@ wxMenuBar::wxMenuBar(size_t count, wxMenu *menus[], const wxString titles[], lon
 {
     Init();
 
-    m_titles.Alloc(count);
-
     for ( size_t i = 0; i < count; i++ )
     {
+        // We just want to store the menu title in the menu itself, not to
+        // show it as a dummy item in the menu itself as we do with the popup
+        // menu titles in overridden wxMenu::SetTitle().
+        menus[i]->wxMenuBase::SetTitle(titles[i]);
         m_menus.Append(menus[i]);
-        m_titles.Add(titles[i]);
 
         menus[i]->Attach(this);
     }
@@ -1106,13 +1062,13 @@ WXHMENU wxMenuBar::Create()
     }
     else
     {
-        size_t count = GetMenuCount(), i;
-        wxMenuList::iterator it;
-        for ( i = 0, it = m_menus.begin(); i < count; i++, it++ )
+        for ( wxMenuList::iterator it = m_menus.begin();
+              it != m_menus.end();
+              ++it )
         {
             if ( !::AppendMenu((HMENU)m_hMenu, MF_POPUP | MF_STRING,
                                (UINT_PTR)(*it)->GetHMenu(),
-                               m_titles[i].wx_str()) )
+                               (*it)->GetTitle().wx_str()) )
             {
                 wxLogLastError(wxT("AppendMenu"));
             }
@@ -1173,7 +1129,7 @@ void wxMenuBar::SetMenuLabel(size_t pos, const wxString& label)
 {
     wxCHECK_RET( pos < GetMenuCount(), wxT("invalid menu index") );
 
-    m_titles[pos] = label;
+    m_menus[pos]->wxMenuBase::SetTitle(label);
 
     if ( !IsAttached() )
     {
@@ -1232,7 +1188,7 @@ wxString wxMenuBar::GetMenuLabel(size_t pos) const
     wxCHECK_MSG( pos < GetMenuCount(), wxEmptyString,
                  wxT("invalid menu index in wxMenuBar::GetMenuLabel") );
 
-    return m_titles[pos];
+    return m_menus[pos]->GetTitle();
 }
 
 // ---------------------------------------------------------------------------
@@ -1245,7 +1201,7 @@ wxMenu *wxMenuBar::Replace(size_t pos, wxMenu *menu, const wxString& title)
     if ( !menuOld )
         return NULL;
 
-    m_titles[pos] = title;
+    menu->wxMenuBase::SetTitle(title);
 
 #if defined(WINCE_WITHOUT_COMMANDBAR)
     if (IsAttached())
@@ -1302,7 +1258,7 @@ bool wxMenuBar::Insert(size_t pos, wxMenu *menu, const wxString& title)
     if ( !wxMenuBarBase::Insert(pos, menu, title) )
         return false;
 
-    m_titles.Insert(title, pos);
+    menu->wxMenuBase::SetTitle(title);
 
     if ( isAttached )
     {
@@ -1358,7 +1314,7 @@ bool wxMenuBar::Append(wxMenu *menu, const wxString& title)
     if ( !wxMenuBarBase::Append(menu, title) )
         return false;
 
-    m_titles.Add(title);
+    menu->wxMenuBase::SetTitle(title);
 
 #if defined(WINCE_WITHOUT_COMMANDBAR)
     if (IsAttached())
@@ -1449,8 +1405,6 @@ wxMenu *wxMenuBar::Remove(size_t pos)
             Refresh();
     }
 
-    m_titles.RemoveAt(pos);
-
     return menu;
 }
 
@@ -1537,4 +1491,22 @@ void wxMenuBar::Detach()
     wxMenuBarBase::Detach();
 }
 
+// get the menu with given handle (recursively)
+wxMenu* wxMenuBar::MSWGetMenu(WXHMENU hMenu)
+{
+    wxCHECK_MSG( GetHMenu() != hMenu, NULL,
+                 wxT("wxMenuBar::MSWGetMenu(): menu handle is wxMenuBar, not wxMenu") );
+
+    // query all menus
+    for ( size_t n = 0 ; n < GetMenuCount(); ++n )
+    {
+        wxMenu* menu = GetMenu(n)->MSWGetMenu(hMenu);
+        if ( menu )
+            return menu;
+    }
+
+    // unknown hMenu
+    return NULL;
+}
+
 #endif // wxUSE_MENUS