]> git.saurik.com Git - wxWidgets.git/blobdiff - src/msw/mdi.cpp
Fix restoring the status bar help message after closing MSW menu from keyboard.
[wxWidgets.git] / src / msw / mdi.cpp
index 2d6109b3db36ed209e034581874dfb6c8ca587f9..309002bb01d46b464ee98c41839c9452cb9e32bf 100644 (file)
@@ -55,13 +55,27 @@ extern wxMenu *wxCurrentPopupMenu;
 
 extern void wxRemoveHandleAssociation(wxWindow *win);
 
+namespace
+{
+
 // ---------------------------------------------------------------------------
 // constants
 // ---------------------------------------------------------------------------
 
-// This range gives a maximum of 500 MDI children. Should be enough :-)
-static const int wxFIRST_MDI_CHILD = 4100;
-static const int wxLAST_MDI_CHILD = 4600;
+// First ID for the MDI child menu item in the "Window" menu.
+const int wxFIRST_MDI_CHILD = 4100;
+
+// There can be no more than 9 children in the "Window" menu as beginning with
+// the tenth one they're not shown and "More windows..." menu item is used
+// instead.
+const int wxLAST_MDI_CHILD = wxFIRST_MDI_CHILD + 8;
+
+// The ID of the "More windows..." menu item is the next one after the last
+// child.
+const int wxID_MDI_MORE_WINDOWS = wxLAST_MDI_CHILD + 1;
+
+// The MDI "Window" menu label
+const char *WINDOW_MENU_LABEL = gettext_noop("&Window");
 
 // ---------------------------------------------------------------------------
 // private functions
@@ -69,29 +83,31 @@ static const int wxLAST_MDI_CHILD = 4600;
 
 // set the MDI menus (by sending the WM_MDISETMENU message) and update the menu
 // of the parent of win (which is supposed to be the MDI client window)
-static void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow);
+void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow);
 
 // insert the window menu (subMenu) into menu just before "Help" submenu or at
 // the very end if not found
-static void MDIInsertWindowMenu(wxWindow *win, WXHMENU menu, HMENU subMenu);
+void MDIInsertWindowMenu(wxWindow *win, WXHMENU hMenu, HMENU subMenu);
 
 // Remove the window menu
-static void MDIRemoveWindowMenu(wxWindow *win, WXHMENU menu);
+void MDIRemoveWindowMenu(wxWindow *win, WXHMENU hMenu);
 
 // unpack the parameters of WM_MDIACTIVATE message
-static void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
-                              WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact);
+void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
+                       WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact);
 
 // return the HMENU of the MDI menu
 //
 // this function works correctly even when we don't have a window menu and just
 // returns 0 then
-static inline HMENU GetMDIWindowMenu(wxMDIParentFrame *frame)
+inline HMENU GetMDIWindowMenu(wxMDIParentFrame *frame)
 {
     wxMenu *menu = frame->GetWindowMenu();
     return menu ? GetHmenuOf(menu) : 0;
 }
 
+} // anonymous namespace
+
 // ===========================================================================
 // implementation
 // ===========================================================================
@@ -130,6 +146,14 @@ END_EVENT_TABLE()
 // the children
 // ===========================================================================
 
+void wxMDIParentFrame::Init()
+{
+#if wxUSE_MENUS && wxUSE_ACCEL
+  // the default menu doesn't have any accelerators (even if we have it)
+  m_accelWindowMenu = NULL;
+#endif // wxUSE_MENUS && wxUSE_ACCEL
+}
+
 bool wxMDIParentFrame::Create(wxWindow *parent,
                               wxWindowID id,
                               const wxString& title,
@@ -154,11 +178,6 @@ bool wxMDIParentFrame::Create(wxWindow *parent,
       m_windowMenu->Append(wxID_MDI_WINDOW_PREV, _("&Previous"));
   }
 
-#if wxUSE_MENUS && wxUSE_ACCEL
-  // the default menu doesn't have any accelerators (even if we have it)
-  m_accelWindowMenu = NULL;
-#endif // wxUSE_MENUS && wxUSE_ACCEL
-
   if (!parent)
     wxTopLevelWindows.Append(this);
 
@@ -178,8 +197,8 @@ bool wxMDIParentFrame::Create(wxWindow *parent,
   msflags &= ~WS_VSCROLL;
   msflags &= ~WS_HSCROLL;
 
-  if ( !wxWindow::MSWCreate(wxApp::GetRegisteredClassName(_T("wxMDIFrame")),
-                            title.wx_str(),
+  if ( !wxWindow::MSWCreate(wxApp::GetRegisteredClassName(wxT("wxMDIFrame")),
+                            title.t_str(),
                             pos, size,
                             msflags,
                             exflags) )
@@ -306,13 +325,23 @@ void wxMDIParentFrame::RemoveMDIChild(wxMDIChildFrame * WXUNUSED(child))
 void wxMDIParentFrame::AddWindowMenu()
 {
     if ( m_windowMenu )
+    {
+        // For correct handling of the events from this menu we also must
+        // attach it to the menu bar.
+        m_windowMenu->Attach(GetMenuBar());
+
         MDIInsertWindowMenu(GetClientWindow(), m_hMenu, GetMDIWindowMenu(this));
+    }
 }
 
 void wxMDIParentFrame::RemoveWindowMenu()
 {
     if ( m_windowMenu )
+    {
         MDIRemoveWindowMenu(GetClientWindow(), m_hMenu);
+
+        m_windowMenu->Detach();
+    }
 }
 
 void wxMDIParentFrame::UpdateWindowMenu(bool enable)
@@ -358,8 +387,7 @@ void wxMDIParentFrame::SetWindowMenu(wxMenu* menu)
     }
 
 #if wxUSE_ACCEL
-    delete m_accelWindowMenu;
-    m_accelWindowMenu = NULL;
+    wxDELETE(m_accelWindowMenu);
 
     if ( menu && menu->HasAccels() )
         m_accelWindowMenu = menu->CreateAccelTable();
@@ -398,13 +426,19 @@ void wxMDIParentFrame::DoMenuUpdates(wxMenu* menu)
     }
 }
 
-const wxMenuItem *wxMDIParentFrame::FindItemInMenuBar(int menuId) const
+wxMenuItem *wxMDIParentFrame::FindItemInMenuBar(int menuId) const
 {
-    const wxMenuItem *item = wxFrame::FindItemInMenuBar(menuId);
-    if ( !item && GetActiveChild() )
-    {
-        item = GetActiveChild()->FindItemInMenuBar(menuId);
-    }
+    // We must look in the child menu first: if it has an item with the same ID
+    // as in our own menu bar, the child item should be used to determine
+    // whether it's currently enabled.
+    wxMenuItem *item = GetActiveChild()
+                            ? GetActiveChild()->FindItemInMenuBar(menuId)
+                            : NULL;
+    if ( !item )
+        item = wxFrame::FindItemInMenuBar(menuId);
+
+    if ( !item && m_windowMenu )
+        item = m_windowMenu->FindItem(menuId);
 
     return item;
 }
@@ -484,7 +518,7 @@ void wxMDIParentFrame::Cascade()
 void wxMDIParentFrame::Tile(wxOrientation orient)
 {
     wxASSERT_MSG( orient == wxHORIZONTAL || orient == wxVERTICAL,
-                  _T("invalid orientation value") );
+                  wxT("invalid orientation value") );
 
     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDITILE,
                   orient == wxHORIZONTAL ? MDITILE_HORIZONTAL
@@ -511,8 +545,8 @@ void wxMDIParentFrame::ActivatePrevious()
 // ---------------------------------------------------------------------------
 
 WXLRESULT wxMDIParentFrame::MSWWindowProc(WXUINT message,
-                                     WXWPARAM wParam,
-                                     WXLPARAM lParam)
+                                          WXWPARAM wParam,
+                                          WXLPARAM lParam)
 {
     WXLRESULT rc = 0;
     bool processed = false;
@@ -530,22 +564,23 @@ WXLRESULT wxMDIParentFrame::MSWWindowProc(WXUINT message,
             break;
 
         case WM_COMMAND:
+            // system messages such as SC_CLOSE are sent as WM_COMMANDs to the
+            // parent MDI frame and we must let the DefFrameProc() have them
+            // for these commands to work (without it, closing the maximized
+            // MDI children doesn't work, for example)
             {
                 WXWORD id, cmd;
                 WXHWND hwnd;
                 UnpackCommand(wParam, lParam, &id, &hwnd, &cmd);
 
-                (void)HandleCommand(id, cmd, hwnd);
-
-                // even if the frame didn't process it, there is no need to try it
-                // once again (i.e. call wxFrame::HandleCommand()) - we just did it,
-                // so pretend we processed the message anyhow
-                processed = true;
+                if ( id == wxID_MDI_MORE_WINDOWS ||
+                     (cmd == 0 /* menu */ &&
+                        id >= SC_SIZE /* first system menu command */) )
+                {
+                    MSWDefWindowProc(message, wParam, lParam);
+                    processed = true;
+                }
             }
-
-            // always pass this message DefFrameProc(), otherwise MDI menu
-            // commands (and sys commands - more surprisingly!) won't work
-            MSWDefWindowProc(message, wParam, lParam);
             break;
 
         case WM_CREATE:
@@ -560,18 +595,6 @@ WXLRESULT wxMDIParentFrame::MSWWindowProc(WXUINT message,
 
             processed = true;
             break;
-
-        case WM_ERASEBKGND:
-            processed = true;
-
-            // we erase background ourselves
-            rc = true;
-            break;
-
-        case WM_SIZE:
-            // though we don't (usually) resize the MDI client to exactly fit the
-            // client area we need to pass this one to DefFrameProc to allow the children to show
-            break;
     }
 
     if ( !processed )
@@ -617,9 +640,7 @@ void wxMDIParentFrame::OnMDIChild(wxCommandEvent& event)
             int childId = wxGetWindowId(child->GetHWND());
             if ( childId == event.GetId() )
             {
-                ::SendMessage( GetWinHwnd(GetClientWindow()),
-                        WM_MDIACTIVATE,
-                        (WPARAM)child->GetHWND(), 0);
+                wxStaticCast(child, wxMDIChildFrame)->Activate();
                 return;
             }
         }
@@ -675,26 +696,8 @@ void wxMDIParentFrame::OnMDICommand(wxCommandEvent& event)
     ::SendMessage(GetWinHwnd(GetClientWindow()), msg, wParam, lParam);
 }
 
-wxMenuItem *wxMDIParentFrame::MSWFindMenuBarItem(WXWORD id)
-{
-    wxMenuItem *mitem = wxFrame::MSWFindMenuBarItem(id);
-    if ( !mitem && m_windowMenu )
-        mitem = m_windowMenu->FindItem((signed short)id);
-
-    return mitem;
-}
-
 #endif // wxUSE_MENUS
 
-bool wxMDIParentFrame::HandleCommand(WXWORD id, WXWORD cmd, WXHWND hwnd)
-{
-    wxMDIChildFrame * const child = GetActiveChild();
-    if ( child && child->HandleCommand(id, cmd, hwnd) )
-        return true;
-
-    return wxFrame::HandleCommand(id, cmd, hwnd);
-}
-
 WXLRESULT wxMDIParentFrame::MSWDefWindowProc(WXUINT message,
                                         WXWPARAM wParam,
                                         WXLPARAM lParam)
@@ -730,7 +733,7 @@ bool wxMDIParentFrame::MSWTranslateMessage(WXMSG* msg)
     // but it doesn't check for the (custom) accelerators of the window menu
     // items as it's not part of the menu bar as it's handled by Windows itself
     // so we need to do this explicitly
-    if ( m_accelWindowMenu->Translate(this, msg) )
+    if ( m_accelWindowMenu && m_accelWindowMenu->Translate(this, msg) )
         return true;
 #endif // wxUSE_MENUS && wxUSE_ACCEL
 
@@ -784,12 +787,12 @@ bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
   MDICREATESTRUCT mcs;
 
   wxString className =
-      wxApp::GetRegisteredClassName(_T("wxMDIChildFrame"), COLOR_WINDOW);
+      wxApp::GetRegisteredClassName(wxT("wxMDIChildFrame"), COLOR_WINDOW);
   if ( !(style & wxFULL_REPAINT_ON_RESIZE) )
       className += wxApp::GetNoRedrawClassSuffix();
 
-  mcs.szClass = className.wx_str();
-  mcs.szTitle = title.wx_str();
+  mcs.szClass = className.t_str();
+  mcs.szTitle = title.t_str();
   mcs.hOwner = wxGetInstance();
   if (x != wxDefaultCoord)
       mcs.x = x;
@@ -838,7 +841,7 @@ bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
 
   if ( !m_hWnd )
   {
-      wxLogLastError(_T("WM_MDICREATE"));
+      wxLogLastError(wxT("WM_MDICREATE"));
       return false;
   }
 
@@ -894,6 +897,14 @@ bool wxMDIChildFrame::Show(bool show)
     return true;
 }
 
+void
+wxMDIChildFrame::DoSetSize(int x, int y, int width, int height, int sizeFlags)
+{
+    // we need to disable client area origin adjustments used for the child
+    // windows for the frame itself
+    wxMDIChildFrameBase::DoSetSize(x, y, width, height, sizeFlags);
+}
+
 // Set the client size (i.e. leave the calculation of borders etc.
 // to wxWidgets)
 void wxMDIChildFrame::DoSetClientSize(int width, int height)
@@ -1022,6 +1033,11 @@ void wxMDIChildFrame::Activate()
     wxMDIParentFrame * const parent = GetMDIParent();
     if ( parent && parent->GetClientWindow() )
     {
+        // Activating an iconized MDI frame doesn't do anything, so restore it
+        // first to really present it to the user.
+        if ( IsIconized() )
+            Restore();
+
         ::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIACTIVATE,
                       (WPARAM) GetHwnd(), 0);
     }
@@ -1032,8 +1048,8 @@ void wxMDIChildFrame::Activate()
 // ---------------------------------------------------------------------------
 
 WXLRESULT wxMDIChildFrame::MSWWindowProc(WXUINT message,
-                                    WXWPARAM wParam,
-                                    WXLPARAM lParam)
+                                         WXWPARAM wParam,
+                                         WXLPARAM lParam)
 {
     WXLRESULT rc = 0;
     bool processed = false;
@@ -1066,11 +1082,6 @@ WXLRESULT wxMDIChildFrame::MSWWindowProc(WXUINT message,
             MSWDefWindowProc(message, wParam, lParam);
             break;
 
-        case WM_SYSCOMMAND:
-            // DefMDIChildProc handles SC_{NEXT/PREV}WINDOW here, so pass it
-            // the message (the base class version does not)
-            return MSWDefWindowProc(message, wParam, lParam);
-
         case WM_WINDOWPOSCHANGING:
             processed = HandleWindowPosChanging((LPWINDOWPOS)lParam);
             break;
@@ -1111,7 +1122,7 @@ bool wxMDIChildFrame::HandleMDIActivate(long WXUNUSED(activate),
 
         WXHMENU hMenuParent = parent->m_hMenu;
 
-        // activate the the parent menu only when there is no other child
+        // activate the parent menu only when there is no other child
         // that has been activated
         if ( hMenuParent && !hwndAct )
             hMenuToSet = hMenuParent;
@@ -1364,7 +1375,7 @@ void wxMDIClientWindow::DoSetSize(int x, int y, int width, int height, int sizeF
             while (node)
             {
                 wxWindow *child = node->GetData();
-                if (child->IsKindOf(CLASSINFO(wxMDIChildFrame)))
+                if (wxDynamicCast(child, wxMDIChildFrame))
                 {
                    ::RedrawWindow(GetHwndOf(child),
                                   NULL,
@@ -1406,10 +1417,13 @@ void wxMDIChildFrame::OnIdle(wxIdleEvent& event)
 }
 
 // ---------------------------------------------------------------------------
-// non member functions
+// private helper functions
 // ---------------------------------------------------------------------------
 
-static void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow)
+namespace
+{
+
+void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow)
 {
     if ( hmenuFrame || hmenuWindow )
     {
@@ -1418,11 +1432,11 @@ static void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow)
                             (WPARAM)hmenuFrame,
                             (LPARAM)hmenuWindow) )
         {
-#ifdef __WXDEBUG__
             DWORD err = ::GetLastError();
             if ( err )
-                wxLogApiError(_T("SendMessage(WM_MDISETMENU)"), err);
-#endif // __WXDEBUG__
+            {
+                wxLogApiError(wxT("SendMessage(WM_MDISETMENU)"), err);
+            }
         }
     }
 
@@ -1435,61 +1449,62 @@ static void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow)
     ::DrawMenuBar(GetWinHwnd(parent));
 }
 
-static void MDIInsertWindowMenu(wxWindow *win, WXHMENU menu, HMENU subMenu)
+void MDIInsertWindowMenu(wxWindow *win, WXHMENU hMenu, HMENU menuWin)
 {
-    // Try to insert Window menu in front of Help, otherwise append it.
-    HMENU hmenu = (HMENU)menu;
+    HMENU hmenu = (HMENU)hMenu;
 
-    if (subMenu)
+    if ( menuWin )
     {
+        // Try to insert Window menu in front of Help, otherwise append it.
         int N = GetMenuItemCount(hmenu);
-        bool success = false;
+        bool inserted = false;
         for ( int i = 0; i < N; i++ )
         {
             wxChar buf[256];
-            int chars = GetMenuString(hmenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION);
-            if ( chars == 0 )
+            if ( !::GetMenuString(hmenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION) )
             {
                 wxLogLastError(wxT("GetMenuString"));
 
                 continue;
             }
 
-            wxString strBuf(buf);
-            if ( wxStripMenuCodes(strBuf) == wxGetStockLabel(wxID_HELP,false) )
+            const wxString label = wxStripMenuCodes(buf);
+            if ( label == wxGetStockLabel(wxID_HELP, wxSTOCK_NOFLAGS) )
             {
-                success = true;
+                inserted = true;
                 ::InsertMenu(hmenu, i, MF_BYPOSITION | MF_POPUP | MF_STRING,
-                             (UINT_PTR)subMenu, _("&Window").wx_str());
+                             (UINT_PTR)menuWin,
+                             wxString(wxGetTranslation(WINDOW_MENU_LABEL)).t_str());
                 break;
             }
         }
 
-        if ( !success )
+        if ( !inserted )
         {
             ::AppendMenu(hmenu, MF_POPUP,
-                         (UINT_PTR)subMenu, _("&Window").wx_str());
+                         (UINT_PTR)menuWin,
+                         wxString(wxGetTranslation(WINDOW_MENU_LABEL)).t_str());
         }
     }
 
-    MDISetMenu(win, hmenu, subMenu);
+    MDISetMenu(win, hmenu, menuWin);
 }
 
-static void MDIRemoveWindowMenu(wxWindow *win, WXHMENU menu)
+void MDIRemoveWindowMenu(wxWindow *win, WXHMENU hMenu)
 {
-    HMENU hMenu = (HMENU)menu;
+    HMENU hmenu = (HMENU)hMenu;
 
-    if ( hMenu )
+    if ( hmenu )
     {
         wxChar buf[1024];
 
-        int N = ::GetMenuItemCount(hMenu);
+        int N = ::GetMenuItemCount(hmenu);
         for ( int i = 0; i < N; i++ )
         {
-            if ( !::GetMenuString(hMenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION) )
+            if ( !::GetMenuString(hmenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION) )
             {
                 // Ignore successful read of menu string with length 0 which
-                // occurs, for example, for a maximized MDI childs system menu
+                // occurs, for example, for a maximized MDI child system menu
                 if ( ::GetLastError() != 0 )
                 {
                     wxLogLastError(wxT("GetMenuString"));
@@ -1498,9 +1513,9 @@ static void MDIRemoveWindowMenu(wxWindow *win, WXHMENU menu)
                 continue;
             }
 
-            if ( wxStrcmp(buf, _("&Window")) == 0 )
+            if ( wxStrcmp(buf, wxGetTranslation(WINDOW_MENU_LABEL)) == 0 )
             {
-                if ( !::RemoveMenu(hMenu, i, MF_BYPOSITION) )
+                if ( !::RemoveMenu(hmenu, i, MF_BYPOSITION) )
                 {
                     wxLogLastError(wxT("RemoveMenu"));
                 }
@@ -1513,11 +1528,11 @@ static void MDIRemoveWindowMenu(wxWindow *win, WXHMENU menu)
     if ( win )
     {
         // we don't change the windows menu, but we update the main one
-        MDISetMenu(win, hMenu, NULL);
+        MDISetMenu(win, hmenu, NULL);
     }
 }
 
-static void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
+void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
                               WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact)
 {
     *activate = true;
@@ -1525,4 +1540,6 @@ static void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
     *hwndDeact = (WXHWND)wParam;
 }
 
+} // anonymous namespace
+
 #endif // wxUSE_MDI && !defined(__WXUNIVERSAL__)