1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/mdi.cpp 
   3 // Purpose:     MDI classes for wxMSW 
   4 // Author:      Julian Smart 
   5 // Modified by: Vadim Zeitlin on 2008-11-04 to use the base classes 
   8 // Copyright:   (c) 1998 Julian Smart 
   9 //              (c) 2008-2009 Vadim Zeitlin 
  10 // Licence:     wxWindows licence 
  11 ///////////////////////////////////////////////////////////////////////////// 
  13 // =========================================================================== 
  15 // =========================================================================== 
  17 // --------------------------------------------------------------------------- 
  19 // --------------------------------------------------------------------------- 
  21 // For compilers that support precompilation, includes "wx.h". 
  22 #include "wx/wxprec.h" 
  28 #if wxUSE_MDI && !defined(__WXUNIVERSAL__) 
  37     #include "wx/dialog.h" 
  38     #include "wx/statusbr.h" 
  39     #include "wx/settings.h" 
  42     #include "wx/toolbar.h" 
  45 #include "wx/stockitem.h" 
  46 #include "wx/msw/private.h" 
  50 // --------------------------------------------------------------------------- 
  52 // --------------------------------------------------------------------------- 
  54 extern wxMenu 
*wxCurrentPopupMenu
; 
  56 extern void wxRemoveHandleAssociation(wxWindow 
*win
); 
  61 // --------------------------------------------------------------------------- 
  63 // --------------------------------------------------------------------------- 
  65 // This range gives a maximum of 500 MDI children. Should be enough :-) 
  66 const int wxFIRST_MDI_CHILD 
= 4100; 
  67 const int wxLAST_MDI_CHILD 
= 4600; 
  69 // The MDI "Window" menu label 
  70 const char *WINDOW_MENU_LABEL 
= gettext_noop("&Window"); 
  72 // --------------------------------------------------------------------------- 
  74 // --------------------------------------------------------------------------- 
  76 // set the MDI menus (by sending the WM_MDISETMENU message) and update the menu 
  77 // of the parent of win (which is supposed to be the MDI client window) 
  78 void MDISetMenu(wxWindow 
*win
, HMENU hmenuFrame
, HMENU hmenuWindow
); 
  80 // insert the window menu (subMenu) into menu just before "Help" submenu or at 
  81 // the very end if not found 
  82 void MDIInsertWindowMenu(wxWindow 
*win
, WXHMENU hMenu
, HMENU subMenu
); 
  84 // Remove the window menu 
  85 void MDIRemoveWindowMenu(wxWindow 
*win
, WXHMENU hMenu
); 
  87 // unpack the parameters of WM_MDIACTIVATE message 
  88 void UnpackMDIActivate(WXWPARAM wParam
, WXLPARAM lParam
, 
  89                        WXWORD 
*activate
, WXHWND 
*hwndAct
, WXHWND 
*hwndDeact
); 
  91 // return the HMENU of the MDI menu 
  93 // this function works correctly even when we don't have a window menu and just 
  95 inline HMENU 
GetMDIWindowMenu(wxMDIParentFrame 
*frame
) 
  97     wxMenu 
*menu 
= frame
->GetWindowMenu(); 
  98     return menu 
? GetHmenuOf(menu
) : 0; 
 101 } // anonymous namespace 
 103 // =========================================================================== 
 105 // =========================================================================== 
 107 // --------------------------------------------------------------------------- 
 109 // --------------------------------------------------------------------------- 
 111 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame
, wxFrame
) 
 112 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame
, wxFrame
) 
 113 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow
, wxWindow
) 
 115 BEGIN_EVENT_TABLE(wxMDIParentFrame
, wxFrame
) 
 116     EVT_SIZE(wxMDIParentFrame::OnSize
) 
 117     EVT_ICONIZE(wxMDIParentFrame::OnIconized
) 
 118     EVT_SYS_COLOUR_CHANGED(wxMDIParentFrame::OnSysColourChanged
) 
 121     EVT_MENU_RANGE(wxFIRST_MDI_CHILD
, wxLAST_MDI_CHILD
, 
 122                    wxMDIParentFrame::OnMDIChild
) 
 123     EVT_MENU_RANGE(wxID_MDI_WINDOW_FIRST
, wxID_MDI_WINDOW_LAST
, 
 124                    wxMDIParentFrame::OnMDICommand
) 
 125 #endif // wxUSE_MENUS 
 128 BEGIN_EVENT_TABLE(wxMDIChildFrame
, wxFrame
) 
 129     EVT_IDLE(wxMDIChildFrame::OnIdle
) 
 132 BEGIN_EVENT_TABLE(wxMDIClientWindow
, wxWindow
) 
 133     EVT_SCROLL(wxMDIClientWindow::OnScroll
) 
 136 // =========================================================================== 
 137 // wxMDIParentFrame: the frame which contains the client window which manages 
 139 // =========================================================================== 
 141 void wxMDIParentFrame::Init() 
 143 #if wxUSE_MENUS && wxUSE_ACCEL 
 144   // the default menu doesn't have any accelerators (even if we have it) 
 145   m_accelWindowMenu 
= NULL
; 
 146 #endif // wxUSE_MENUS && wxUSE_ACCEL 
 149 bool wxMDIParentFrame::Create(wxWindow 
*parent
, 
 151                               const wxString
& title
, 
 155                               const wxString
& name
) 
 157   // this style can be used to prevent a window from having the standard MDI 
 159   if ( !(style 
& wxFRAME_NO_WINDOW_MENU
) ) 
 161       // normal case: we have the window menu, so construct it 
 162       m_windowMenu 
= new wxMenu
; 
 164       m_windowMenu
->Append(wxID_MDI_WINDOW_CASCADE
, _("&Cascade")); 
 165       m_windowMenu
->Append(wxID_MDI_WINDOW_TILE_HORZ
, _("Tile &Horizontally")); 
 166       m_windowMenu
->Append(wxID_MDI_WINDOW_TILE_VERT
, _("Tile &Vertically")); 
 167       m_windowMenu
->AppendSeparator(); 
 168       m_windowMenu
->Append(wxID_MDI_WINDOW_ARRANGE_ICONS
, _("&Arrange Icons")); 
 169       m_windowMenu
->Append(wxID_MDI_WINDOW_NEXT
, _("&Next")); 
 170       m_windowMenu
->Append(wxID_MDI_WINDOW_PREV
, _("&Previous")); 
 174     wxTopLevelWindows
.Append(this); 
 177   m_windowStyle 
= style
; 
 180       parent
->AddChild(this); 
 182   if ( id 
!= wxID_ANY 
) 
 185     m_windowId 
= NewControlId(); 
 188   WXDWORD msflags 
= MSWGetCreateWindowFlags(&exflags
); 
 189   msflags 
&= ~WS_VSCROLL
; 
 190   msflags 
&= ~WS_HSCROLL
; 
 192   if ( !wxWindow::MSWCreate(wxApp::GetRegisteredClassName(wxT("wxMDIFrame")), 
 201   SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE
)); 
 203   // unlike (almost?) all other windows, frames are created hidden 
 209 wxMDIParentFrame::~wxMDIParentFrame() 
 211     // see comment in ~wxMDIChildFrame 
 213     m_frameToolBar 
= NULL
; 
 216     m_frameStatusBar 
= NULL
; 
 217 #endif // wxUSE_STATUSBAR 
 219 #if wxUSE_MENUS && wxUSE_ACCEL 
 220     delete m_accelWindowMenu
; 
 221 #endif // wxUSE_MENUS && wxUSE_ACCEL 
 225     // the MDI frame menubar is not automatically deleted by Windows unlike for 
 228         ::DestroyMenu((HMENU
)m_hMenu
); 
 230     if ( m_clientWindow 
) 
 232         if ( m_clientWindow
->MSWGetOldWndProc() ) 
 233             m_clientWindow
->UnsubclassWin(); 
 235         m_clientWindow
->SetHWND(0); 
 236         delete m_clientWindow
; 
 240 // ---------------------------------------------------------------------------- 
 241 // wxMDIParentFrame child management 
 242 // ---------------------------------------------------------------------------- 
 244 wxMDIChildFrame 
*wxMDIParentFrame::GetActiveChild() const 
 246     HWND hWnd 
= (HWND
)::SendMessage(GetWinHwnd(GetClientWindow()), 
 247                                     WM_MDIGETACTIVE
, 0, 0L); 
 251     return static_cast<wxMDIChildFrame 
*>(wxFindWinFromHandle(hWnd
)); 
 254 int wxMDIParentFrame::GetChildFramesCount() const 
 257     for ( wxWindowList::const_iterator i 
= GetChildren().begin(); 
 258           i 
!= GetChildren().end(); 
 261         if ( wxDynamicCast(*i
, wxMDIChildFrame
) ) 
 270 void wxMDIParentFrame::AddMDIChild(wxMDIChildFrame 
* WXUNUSED(child
)) 
 272     switch ( GetChildFramesCount() ) 
 275             // first MDI child was just added, we need to insert the window 
 276             // menu now if we have it 
 279             // and disable the items which can't be used until we have more 
 281             UpdateWindowMenu(false); 
 285             // second MDI child was added, enable the menu items which were 
 286             // disabled because they didn't make sense for a single window 
 287             UpdateWindowMenu(true); 
 292 void wxMDIParentFrame::RemoveMDIChild(wxMDIChildFrame 
* WXUNUSED(child
)) 
 294     switch ( GetChildFramesCount() ) 
 297             // last MDI child is being removed, remove the now unnecessary 
 301             // there is no need to call UpdateWindowMenu(true) here so this is 
 302             // not quite symmetric to AddMDIChild() above 
 306             // only one MDI child is going to remain, disable the menu commands 
 307             // which don't make sense for a single child window 
 308             UpdateWindowMenu(false); 
 313 // ---------------------------------------------------------------------------- 
 314 // wxMDIParentFrame window menu handling 
 315 // ---------------------------------------------------------------------------- 
 317 void wxMDIParentFrame::AddWindowMenu() 
 321         // For correct handling of the events from this menu we also must 
 322         // attach it to the menu bar. 
 323         m_windowMenu
->Attach(GetMenuBar()); 
 325         MDIInsertWindowMenu(GetClientWindow(), m_hMenu
, GetMDIWindowMenu(this)); 
 329 void wxMDIParentFrame::RemoveWindowMenu() 
 333         MDIRemoveWindowMenu(GetClientWindow(), m_hMenu
); 
 335         m_windowMenu
->Detach(); 
 339 void wxMDIParentFrame::UpdateWindowMenu(bool enable
) 
 343         m_windowMenu
->Enable(wxID_MDI_WINDOW_NEXT
, enable
); 
 344         m_windowMenu
->Enable(wxID_MDI_WINDOW_PREV
, enable
); 
 348 #if wxUSE_MENUS_NATIVE 
 350 void wxMDIParentFrame::InternalSetMenuBar() 
 352     if ( GetActiveChild() ) 
 356     else // we don't have any MDI children yet 
 358         // wait until we do to add the window menu but do set the main menu for 
 359         // now (this is done by AddWindowMenu() as a side effect) 
 360         MDISetMenu(GetClientWindow(), (HMENU
)m_hMenu
, NULL
); 
 364 #endif // wxUSE_MENUS_NATIVE 
 366 void wxMDIParentFrame::SetWindowMenu(wxMenu
* menu
) 
 368     if ( menu 
!= m_windowMenu 
) 
 370         // notice that Remove/AddWindowMenu() are safe to call even when 
 371         // m_windowMenu is NULL 
 382     wxDELETE(m_accelWindowMenu
); 
 384     if ( menu 
&& menu
->HasAccels() ) 
 385         m_accelWindowMenu 
= menu
->CreateAccelTable(); 
 386 #endif // wxUSE_ACCEL 
 389 // ---------------------------------------------------------------------------- 
 390 // wxMDIParentFrame other menu-related stuff 
 391 // ---------------------------------------------------------------------------- 
 393 void wxMDIParentFrame::DoMenuUpdates(wxMenu
* menu
) 
 395     wxMDIChildFrame 
*child 
= GetActiveChild(); 
 398         wxEvtHandler
* source 
= child
->GetEventHandler(); 
 399         wxMenuBar
* bar 
= child
->GetMenuBar(); 
 403             menu
->UpdateUI(source
); 
 409                 int nCount 
= bar
->GetMenuCount(); 
 410                 for (int n 
= 0; n 
< nCount
; n
++) 
 411                     bar
->GetMenu(n
)->UpdateUI(source
); 
 417         wxFrameBase::DoMenuUpdates(menu
); 
 421 wxMenuItem 
*wxMDIParentFrame::FindItemInMenuBar(int menuId
) const 
 423     wxMenuItem 
*item 
= wxFrame::FindItemInMenuBar(menuId
); 
 424     if ( !item 
&& GetActiveChild() ) 
 426         item 
= GetActiveChild()->FindItemInMenuBar(menuId
); 
 429     if ( !item 
&& m_windowMenu 
) 
 430         item 
= m_windowMenu
->FindItem(menuId
); 
 435 WXHMENU 
wxMDIParentFrame::MSWGetActiveMenu() const 
 437     wxMDIChildFrame 
* const child  
= GetActiveChild(); 
 440         const WXHMENU hmenu 
= child
->MSWGetActiveMenu(); 
 445     return wxFrame::MSWGetActiveMenu(); 
 448 #endif // wxUSE_MENUS 
 450 // ---------------------------------------------------------------------------- 
 451 // wxMDIParentFrame event handling 
 452 // ---------------------------------------------------------------------------- 
 454 void wxMDIParentFrame::UpdateClientSize() 
 456     if ( GetClientWindow() ) 
 459         GetClientSize(&width
, &height
); 
 461         GetClientWindow()->SetSize(0, 0, width
, height
); 
 465 void wxMDIParentFrame::OnSize(wxSizeEvent
& WXUNUSED(event
)) 
 469     // do not call event.Skip() here, it somehow messes up MDI client window 
 472 void wxMDIParentFrame::OnIconized(wxIconizeEvent
& event
) 
 476     if ( !event
.IsIconized() ) 
 480 // Responds to colour changes, and passes event on to children. 
 481 void wxMDIParentFrame::OnSysColourChanged(wxSysColourChangedEvent
& event
) 
 483     if ( m_clientWindow 
) 
 485         m_clientWindow
->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE
)); 
 486         m_clientWindow
->Refresh(); 
 492 WXHICON 
wxMDIParentFrame::GetDefaultIcon() const 
 494     // we don't have any standard icons (any more) 
 498 // --------------------------------------------------------------------------- 
 500 // --------------------------------------------------------------------------- 
 502 void wxMDIParentFrame::Cascade() 
 504     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDICASCADE
, 0, 0); 
 507 void wxMDIParentFrame::Tile(wxOrientation orient
) 
 509     wxASSERT_MSG( orient 
== wxHORIZONTAL 
|| orient 
== wxVERTICAL
, 
 510                   wxT("invalid orientation value") ); 
 512     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDITILE
, 
 513                   orient 
== wxHORIZONTAL 
? MDITILE_HORIZONTAL
 
 514                                          : MDITILE_VERTICAL
, 0); 
 517 void wxMDIParentFrame::ArrangeIcons() 
 519     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDIICONARRANGE
, 0, 0); 
 522 void wxMDIParentFrame::ActivateNext() 
 524     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT
, 0, 0); 
 527 void wxMDIParentFrame::ActivatePrevious() 
 529     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT
, 0, 1); 
 532 // --------------------------------------------------------------------------- 
 533 // the MDI parent frame window proc 
 534 // --------------------------------------------------------------------------- 
 536 WXLRESULT 
wxMDIParentFrame::MSWWindowProc(WXUINT message
, 
 541     bool processed 
= false; 
 547                 WXWORD state
, minimized
; 
 549                 UnpackActivate(wParam
, lParam
, &state
, &minimized
, &hwnd
); 
 551                 processed 
= HandleActivate(state
, minimized 
!= 0, hwnd
); 
 556             // system messages such as SC_CLOSE are sent as WM_COMMANDs to the 
 557             // parent MDI frame and we must let the DefFrameProc() have them 
 558             // for these commands to work (without it, closing the maximized 
 559             // MDI children doesn't work, for example) 
 563                 UnpackCommand(wParam
, lParam
, &id
, &hwnd
, &cmd
); 
 565                 if ( cmd 
== 0 /* menu */ && 
 566                         id 
>= SC_SIZE 
/* first system menu command */ ) 
 568                     MSWDefWindowProc(message
, wParam
, lParam
); 
 575             m_clientWindow 
= OnCreateClient(); 
 576             // Uses own style for client style 
 577             if ( !m_clientWindow
->CreateClient(this, GetWindowStyleFlag()) ) 
 579                 wxLogMessage(_("Failed to create MDI parent frame.")); 
 589         rc 
= wxFrame::MSWWindowProc(message
, wParam
, lParam
); 
 594 bool wxMDIParentFrame::HandleActivate(int state
, bool minimized
, WXHWND activate
) 
 596     bool processed 
= false; 
 598     if ( wxWindow::HandleActivate(state
, minimized
, activate
) ) 
 604     // If this window is an MDI parent, we must also send an OnActivate message 
 605     // to the current child. 
 606     if ( GetActiveChild() && 
 607          ((state 
== WA_ACTIVE
) || (state 
== WA_CLICKACTIVE
)) ) 
 609         wxActivateEvent 
event(wxEVT_ACTIVATE
, true, GetActiveChild()->GetId()); 
 610         event
.SetEventObject( GetActiveChild() ); 
 611         if ( GetActiveChild()->HandleWindowEvent(event
) ) 
 620 void wxMDIParentFrame::OnMDIChild(wxCommandEvent
& event
) 
 622     wxWindowList::compatibility_iterator node 
= GetChildren().GetFirst(); 
 625         wxWindow 
*child 
= node
->GetData(); 
 626         if ( child
->GetHWND() ) 
 628             int childId 
= wxGetWindowId(child
->GetHWND()); 
 629             if ( childId 
== event
.GetId() ) 
 631                 ::SendMessage( GetWinHwnd(GetClientWindow()), 
 633                         (WPARAM
)child
->GetHWND(), 0); 
 638         node 
= node
->GetNext(); 
 641     wxFAIL_MSG( "unknown MDI child selected?" ); 
 644 void wxMDIParentFrame::OnMDICommand(wxCommandEvent
& event
) 
 649     switch ( event
.GetId() ) 
 651         case wxID_MDI_WINDOW_CASCADE
: 
 653             wParam 
= MDITILE_SKIPDISABLED
; 
 656         case wxID_MDI_WINDOW_TILE_HORZ
: 
 657             wParam 
|= MDITILE_HORIZONTAL
; 
 660         case wxID_MDI_WINDOW_TILE_VERT
: 
 662                 wParam 
= MDITILE_VERTICAL
; 
 664             wParam 
|= MDITILE_SKIPDISABLED
; 
 667         case wxID_MDI_WINDOW_ARRANGE_ICONS
: 
 668             msg 
= WM_MDIICONARRANGE
; 
 671         case wxID_MDI_WINDOW_NEXT
: 
 673             lParam 
= 0;         // next child 
 676         case wxID_MDI_WINDOW_PREV
: 
 678             lParam 
= 1;         // previous child 
 682             wxFAIL_MSG( "unknown MDI command" ); 
 686     ::SendMessage(GetWinHwnd(GetClientWindow()), msg
, wParam
, lParam
); 
 689 #endif // wxUSE_MENUS 
 691 bool wxMDIParentFrame::TryBefore(wxEvent
& event
) 
 693     // menu (and toolbar) events should be sent to the active child frame 
 695     if ( event
.GetEventType() == wxEVT_COMMAND_MENU_SELECTED 
) 
 697         wxMDIChildFrame 
* const child 
= GetActiveChild(); 
 698         if ( child 
&& child
->ProcessWindowEventLocally(event
) ) 
 702     return wxMDIParentFrameBase::TryBefore(event
); 
 705 WXLRESULT 
wxMDIParentFrame::MSWDefWindowProc(WXUINT message
, 
 710     if ( GetClientWindow() ) 
 711         clientWnd 
= GetClientWindow()->GetHWND(); 
 715     return DefFrameProc(GetHwnd(), (HWND
)clientWnd
, message
, wParam
, lParam
); 
 718 bool wxMDIParentFrame::MSWTranslateMessage(WXMSG
* msg
) 
 720     MSG 
*pMsg 
= (MSG 
*)msg
; 
 722     // first let the current child get it 
 723     wxMDIChildFrame 
* const child 
= GetActiveChild(); 
 724     if ( child 
&& child
->MSWTranslateMessage(msg
) ) 
 729     // then try out accelerator table (will also check the accelerators for the 
 730     // normal menu items) 
 731     if ( wxFrame::MSWTranslateMessage(msg
) ) 
 736 #if wxUSE_MENUS && wxUSE_ACCEL 
 737     // but it doesn't check for the (custom) accelerators of the window menu 
 738     // items as it's not part of the menu bar as it's handled by Windows itself 
 739     // so we need to do this explicitly 
 740     if ( m_accelWindowMenu 
&& m_accelWindowMenu
->Translate(this, msg
) ) 
 742 #endif // wxUSE_MENUS && wxUSE_ACCEL 
 744     // finally, check for MDI specific built-in accelerators 
 745     if ( pMsg
->message 
== WM_KEYDOWN 
|| pMsg
->message 
== WM_SYSKEYDOWN 
) 
 747         if ( ::TranslateMDISysAccel(GetWinHwnd(GetClientWindow()), pMsg
)) 
 754 // =========================================================================== 
 756 // =========================================================================== 
 758 void wxMDIChildFrame::Init() 
 760     m_needsResize 
= true; 
 761     m_needsInitialShow 
= true; 
 764 bool wxMDIChildFrame::Create(wxMDIParentFrame 
*parent
, 
 766                              const wxString
& title
, 
 770                              const wxString
& name
) 
 772     m_mdiParent 
= parent
; 
 776   if ( id 
!= wxID_ANY 
) 
 779     m_windowId 
= NewControlId(); 
 783       parent
->AddChild(this); 
 794       wxApp::GetRegisteredClassName(wxT("wxMDIChildFrame"), COLOR_WINDOW
); 
 795   if ( !(style 
& wxFULL_REPAINT_ON_RESIZE
) ) 
 796       className 
+= wxApp::GetNoRedrawClassSuffix(); 
 798   mcs
.szClass 
= className
.wx_str(); 
 799   mcs
.szTitle 
= title
.wx_str(); 
 800   mcs
.hOwner 
= wxGetInstance(); 
 801   if (x 
!= wxDefaultCoord
) 
 804       mcs
.x 
= CW_USEDEFAULT
; 
 806   if (y 
!= wxDefaultCoord
) 
 809       mcs
.y 
= CW_USEDEFAULT
; 
 811   if (width 
!= wxDefaultCoord
) 
 814       mcs
.cx 
= CW_USEDEFAULT
; 
 816   if (height 
!= wxDefaultCoord
) 
 819       mcs
.cy 
= CW_USEDEFAULT
; 
 821   DWORD msflags 
= WS_OVERLAPPED 
| WS_CLIPCHILDREN
; 
 822   if (style 
& wxMINIMIZE_BOX
) 
 823     msflags 
|= WS_MINIMIZEBOX
; 
 824   if (style 
& wxMAXIMIZE_BOX
) 
 825     msflags 
|= WS_MAXIMIZEBOX
; 
 826   if (style 
& wxRESIZE_BORDER
) 
 827     msflags 
|= WS_THICKFRAME
; 
 828   if (style 
& wxSYSTEM_MENU
) 
 829     msflags 
|= WS_SYSMENU
; 
 830   if ((style 
& wxMINIMIZE
) || (style 
& wxICONIZE
)) 
 831     msflags 
|= WS_MINIMIZE
; 
 832   if (style 
& wxMAXIMIZE
) 
 833     msflags 
|= WS_MAXIMIZE
; 
 834   if (style 
& wxCAPTION
) 
 835     msflags 
|= WS_CAPTION
; 
 841   wxWindowCreationHook 
hook(this); 
 843   m_hWnd 
= (WXHWND
)::SendMessage(GetWinHwnd(parent
->GetClientWindow()), 
 844                                  WM_MDICREATE
, 0, (LPARAM
)&mcs
); 
 848       wxLogLastError(wxT("WM_MDICREATE")); 
 854   parent
->AddMDIChild(this); 
 859 wxMDIChildFrame::~wxMDIChildFrame() 
 861     // if we hadn't been created, there is nothing to destroy 
 865     GetMDIParent()->RemoveMDIChild(this); 
 867     // will be destroyed by DestroyChildren() but reset them before calling it 
 868     // to avoid using dangling pointers if a callback comes in the meanwhile 
 870     m_frameToolBar 
= NULL
; 
 873     m_frameStatusBar 
= NULL
; 
 874 #endif // wxUSE_STATUSBAR 
 878     MDIRemoveWindowMenu(NULL
, m_hMenu
); 
 883 bool wxMDIChildFrame::Show(bool show
) 
 885     m_needsInitialShow 
= false; 
 887     if (!wxFrame::Show(show
)) 
 890     // KH: Without this call, new MDI children do not become active. 
 891     // This was added here after the same BringWindowToTop call was 
 892     // removed from wxTopLevelWindow::Show (November 2005) 
 894         ::BringWindowToTop(GetHwnd()); 
 896     // we need to refresh the MDI frame window menu to include (or exclude if 
 897     // we've been hidden) this frame 
 898     wxMDIParentFrame 
* const parent 
= GetMDIParent(); 
 899     MDISetMenu(parent
->GetClientWindow(), NULL
, NULL
); 
 905 wxMDIChildFrame::DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
) 
 907     // we need to disable client area origin adjustments used for the child 
 908     // windows for the frame itself 
 909     wxMDIChildFrameBase::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
 912 // Set the client size (i.e. leave the calculation of borders etc. 
 914 void wxMDIChildFrame::DoSetClientSize(int width
, int height
) 
 916   HWND hWnd 
= GetHwnd(); 
 919   ::GetClientRect(hWnd
, &rect
); 
 922   GetWindowRect(hWnd
, &rect2
); 
 924   // Find the difference between the entire window (title bar and all) 
 925   // and the client area; add this to the new client size to move the 
 927   int actual_width 
= rect2
.right 
- rect2
.left 
- rect
.right 
+ width
; 
 928   int actual_height 
= rect2
.bottom 
- rect2
.top 
- rect
.bottom 
+ height
; 
 931   if (GetStatusBar() && GetStatusBar()->IsShown()) 
 934     GetStatusBar()->GetSize(&sx
, &sy
); 
 937 #endif // wxUSE_STATUSBAR 
 940   point
.x 
= rect2
.left
; 
 943   // If there's an MDI parent, must subtract the parent's top left corner 
 944   // since MoveWindow moves relative to the parent 
 945   wxMDIParentFrame 
* const mdiParent 
= GetMDIParent(); 
 946   ::ScreenToClient(GetHwndOf(mdiParent
->GetClientWindow()), &point
); 
 948   MoveWindow(hWnd
, point
.x
, point
.y
, actual_width
, actual_height
, (BOOL
)true); 
 950   wxSize 
size(width
, height
); 
 951   wxSizeEvent 
event(size
, m_windowId
); 
 952   event
.SetEventObject( this ); 
 953   HandleWindowEvent(event
); 
 956 // Unlike other wxTopLevelWindowBase, the mdi child's "GetPosition" is not the 
 957 //  same as its GetScreenPosition 
 958 void wxMDIChildFrame::DoGetScreenPosition(int *x
, int *y
) const 
 960   HWND hWnd 
= GetHwnd(); 
 963   ::GetWindowRect(hWnd
, &rect
); 
 971 void wxMDIChildFrame::DoGetPosition(int *x
, int *y
) const 
 974   GetWindowRect(GetHwnd(), &rect
); 
 979   // Since we now have the absolute screen coords, 
 980   // if there's a parent we must subtract its top left corner 
 981   wxMDIParentFrame 
* const mdiParent 
= GetMDIParent(); 
 982   ::ScreenToClient(GetHwndOf(mdiParent
->GetClientWindow()), &point
); 
 990 void wxMDIChildFrame::InternalSetMenuBar() 
 992     wxMDIParentFrame 
* const parent 
= GetMDIParent(); 
 994     MDIInsertWindowMenu(parent
->GetClientWindow(), 
 995                      m_hMenu
, GetMDIWindowMenu(parent
)); 
 998 void wxMDIChildFrame::DetachMenuBar() 
1000     MDIRemoveWindowMenu(NULL
, m_hMenu
); 
1001     wxFrame::DetachMenuBar(); 
1004 WXHICON 
wxMDIChildFrame::GetDefaultIcon() const 
1006     // we don't have any standard icons (any more) 
1010 // --------------------------------------------------------------------------- 
1012 // --------------------------------------------------------------------------- 
1014 void wxMDIChildFrame::Maximize(bool maximize
) 
1016     wxMDIParentFrame 
* const parent 
= GetMDIParent(); 
1017     if ( parent 
&& parent
->GetClientWindow() ) 
1019         ::SendMessage(GetWinHwnd(parent
->GetClientWindow()), 
1020                       maximize 
? WM_MDIMAXIMIZE 
: WM_MDIRESTORE
, 
1021                       (WPARAM
)GetHwnd(), 0); 
1025 void wxMDIChildFrame::Restore() 
1027     wxMDIParentFrame 
* const parent 
= GetMDIParent(); 
1028     if ( parent 
&& parent
->GetClientWindow() ) 
1030         ::SendMessage(GetWinHwnd(parent
->GetClientWindow()), WM_MDIRESTORE
, 
1031                       (WPARAM
) GetHwnd(), 0); 
1035 void wxMDIChildFrame::Activate() 
1037     wxMDIParentFrame 
* const parent 
= GetMDIParent(); 
1038     if ( parent 
&& parent
->GetClientWindow() ) 
1040         ::SendMessage(GetWinHwnd(parent
->GetClientWindow()), WM_MDIACTIVATE
, 
1041                       (WPARAM
) GetHwnd(), 0); 
1045 // --------------------------------------------------------------------------- 
1046 // MDI window proc and message handlers 
1047 // --------------------------------------------------------------------------- 
1049 WXLRESULT 
wxMDIChildFrame::MSWWindowProc(WXUINT message
, 
1054     bool processed 
= false; 
1058         case WM_GETMINMAXINFO
: 
1059             processed 
= HandleGetMinMaxInfo((MINMAXINFO 
*)lParam
); 
1062         case WM_MDIACTIVATE
: 
1065                 WXHWND hwndAct
, hwndDeact
; 
1066                 UnpackMDIActivate(wParam
, lParam
, &act
, &hwndAct
, &hwndDeact
); 
1068                 processed 
= HandleMDIActivate(act
, hwndAct
, hwndDeact
); 
1073             // must pass WM_MOVE to DefMDIChildProc() to recalculate MDI client 
1074             // scrollbars if necessary 
1079             // must pass WM_SIZE to DefMDIChildProc(), otherwise many weird 
1081             MSWDefWindowProc(message
, wParam
, lParam
); 
1084         case WM_WINDOWPOSCHANGING
: 
1085             processed 
= HandleWindowPosChanging((LPWINDOWPOS
)lParam
); 
1090         rc 
= wxFrame::MSWWindowProc(message
, wParam
, lParam
); 
1095 bool wxMDIChildFrame::HandleMDIActivate(long WXUNUSED(activate
), 
1099     wxMDIParentFrame 
* const parent 
= GetMDIParent(); 
1101     WXHMENU hMenuToSet 
= 0; 
1105     if ( m_hWnd 
== hwndAct 
) 
1108         parent
->SetActiveChild(this); 
1110         WXHMENU hMenuChild 
= m_hMenu
; 
1112             hMenuToSet 
= hMenuChild
; 
1114     else if ( m_hWnd 
== hwndDeact 
) 
1116         wxASSERT_MSG( parent
->GetActiveChild() == this, 
1117                       wxT("can't deactivate MDI child which wasn't active!") ); 
1120         parent
->SetActiveChild(NULL
); 
1122         WXHMENU hMenuParent 
= parent
->m_hMenu
; 
1124         // activate the parent menu only when there is no other child 
1125         // that has been activated 
1126         if ( hMenuParent 
&& !hwndAct 
) 
1127             hMenuToSet 
= hMenuParent
; 
1131         // we have nothing to do with it 
1137         MDISetMenu(parent
->GetClientWindow(), 
1138                    (HMENU
)hMenuToSet
, GetMDIWindowMenu(parent
)); 
1141     wxActivateEvent 
event(wxEVT_ACTIVATE
, activated
, m_windowId
); 
1142     event
.SetEventObject( this ); 
1144     ResetWindowStyle(NULL
); 
1146     return HandleWindowEvent(event
); 
1149 bool wxMDIChildFrame::HandleWindowPosChanging(void *pos
) 
1151     WINDOWPOS 
*lpPos 
= (WINDOWPOS 
*)pos
; 
1153     if (!(lpPos
->flags 
& SWP_NOSIZE
)) 
1156         DWORD dwExStyle 
= ::GetWindowLong(GetHwnd(), GWL_EXSTYLE
); 
1157         DWORD dwStyle 
= ::GetWindowLong(GetHwnd(), GWL_STYLE
); 
1158         if (ResetWindowStyle((void *) & rectClient
) && (dwStyle 
& WS_MAXIMIZE
)) 
1160             ::AdjustWindowRectEx(&rectClient
, dwStyle
, false, dwExStyle
); 
1161             lpPos
->x 
= rectClient
.left
; 
1162             lpPos
->y 
= rectClient
.top
; 
1163             lpPos
->cx 
= rectClient
.right 
- rectClient
.left
; 
1164             lpPos
->cy 
= rectClient
.bottom 
- rectClient
.top
; 
1171 bool wxMDIChildFrame::HandleGetMinMaxInfo(void *mmInfo
) 
1173     MINMAXINFO 
*info 
= (MINMAXINFO 
*)mmInfo
; 
1175     // let the default window proc calculate the size of MDI children 
1176     // frames because it is based on the size of the MDI client window, 
1177     // not on the values specified in wxWindow m_max variables 
1178     bool processed 
= MSWDefWindowProc(WM_GETMINMAXINFO
, 0, (LPARAM
)mmInfo
) != 0; 
1180     int minWidth 
= GetMinWidth(), 
1181         minHeight 
= GetMinHeight(); 
1183     // but allow GetSizeHints() to set the min size 
1184     if ( minWidth 
!= wxDefaultCoord 
) 
1186         info
->ptMinTrackSize
.x 
= minWidth
; 
1191     if ( minHeight 
!= wxDefaultCoord 
) 
1193         info
->ptMinTrackSize
.y 
= minHeight
; 
1201 // --------------------------------------------------------------------------- 
1202 // MDI specific message translation/preprocessing 
1203 // --------------------------------------------------------------------------- 
1205 WXLRESULT 
wxMDIChildFrame::MSWDefWindowProc(WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
) 
1207     return DefMDIChildProc(GetHwnd(), 
1208                            (UINT
)message
, (WPARAM
)wParam
, (LPARAM
)lParam
); 
1211 bool wxMDIChildFrame::MSWTranslateMessage(WXMSG
* msg
) 
1213     // we must pass the parent frame to ::TranslateAccelerator(), otherwise it 
1214     // doesn't do its job correctly for MDI child menus 
1215     return MSWDoTranslateMessage(GetMDIParent(), msg
); 
1218 // --------------------------------------------------------------------------- 
1220 // --------------------------------------------------------------------------- 
1222 void wxMDIChildFrame::MSWDestroyWindow() 
1224     wxMDIParentFrame 
* const parent 
= GetMDIParent(); 
1226     // Must make sure this handle is invalidated (set to NULL) since all sorts 
1227     // of things could happen after the child client is destroyed, but before 
1228     // the wxFrame is destroyed. 
1230     HWND oldHandle 
= (HWND
)GetHWND(); 
1231     SendMessage(GetWinHwnd(parent
->GetClientWindow()), WM_MDIDESTROY
, 
1232                 (WPARAM
)oldHandle
, 0); 
1234     if (parent
->GetActiveChild() == NULL
) 
1235         ResetWindowStyle(NULL
); 
1239         ::DestroyMenu((HMENU
) m_hMenu
); 
1242     wxRemoveHandleAssociation(this); 
1246 // Change the client window's extended style so we don't get a client edge 
1247 // style when a child is maximised (a double border looks silly.) 
1248 bool wxMDIChildFrame::ResetWindowStyle(void *vrect
) 
1250     RECT 
*rect 
= (RECT 
*)vrect
; 
1251     wxMDIParentFrame 
* const pFrameWnd 
= GetMDIParent(); 
1252     wxMDIChildFrame
* pChild 
= pFrameWnd
->GetActiveChild(); 
1254     if (!pChild 
|| (pChild 
== this)) 
1256         HWND hwndClient 
= GetWinHwnd(pFrameWnd
->GetClientWindow()); 
1257         DWORD dwStyle 
= ::GetWindowLong(hwndClient
, GWL_EXSTYLE
); 
1259         // we want to test whether there is a maximized child, so just set 
1260         // dwThisStyle to 0 if there is no child at all 
1261         DWORD dwThisStyle 
= pChild
 
1262             ? ::GetWindowLong(GetWinHwnd(pChild
), GWL_STYLE
) : 0; 
1263         DWORD dwNewStyle 
= dwStyle
; 
1264         if ( dwThisStyle 
& WS_MAXIMIZE 
) 
1265             dwNewStyle 
&= ~(WS_EX_CLIENTEDGE
); 
1267             dwNewStyle 
|= WS_EX_CLIENTEDGE
; 
1269         if (dwStyle 
!= dwNewStyle
) 
1271             // force update of everything 
1272             ::RedrawWindow(hwndClient
, NULL
, NULL
, 
1273                            RDW_INVALIDATE 
| RDW_ALLCHILDREN
); 
1274             ::SetWindowLong(hwndClient
, GWL_EXSTYLE
, dwNewStyle
); 
1275             ::SetWindowPos(hwndClient
, NULL
, 0, 0, 0, 0, 
1276                            SWP_FRAMECHANGED 
| SWP_NOACTIVATE 
| 
1277                            SWP_NOMOVE 
| SWP_NOSIZE 
| SWP_NOZORDER 
| 
1280                 ::GetClientRect(hwndClient
, rect
); 
1289 // =========================================================================== 
1290 // wxMDIClientWindow: the window of predefined (by Windows) class which 
1291 // contains the child frames 
1292 // =========================================================================== 
1294 bool wxMDIClientWindow::CreateClient(wxMDIParentFrame 
*parent
, long style
) 
1296     m_backgroundColour 
= wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE
); 
1298     CLIENTCREATESTRUCT ccs
; 
1299     m_windowStyle 
= style
; 
1302     ccs
.hWindowMenu 
= GetMDIWindowMenu(parent
); 
1303     ccs
.idFirstChild 
= wxFIRST_MDI_CHILD
; 
1305     DWORD msStyle 
= MDIS_ALLCHILDSTYLES 
| WS_VISIBLE 
| WS_CHILD 
| 
1306                     WS_CLIPCHILDREN 
| WS_CLIPSIBLINGS
; 
1308     if ( style 
& wxHSCROLL 
) 
1309         msStyle 
|= WS_HSCROLL
; 
1310     if ( style 
& wxVSCROLL 
) 
1311         msStyle 
|= WS_VSCROLL
; 
1313     DWORD exStyle 
= WS_EX_CLIENTEDGE
; 
1315     wxWindowCreationHook 
hook(this); 
1316     m_hWnd 
= (WXHWND
)::CreateWindowEx
 
1326                         (LPSTR
)(LPCLIENTCREATESTRUCT
)&ccs
); 
1329         wxLogLastError(wxT("CreateWindowEx(MDI client)")); 
1334     SubclassWin(m_hWnd
); 
1339 // Explicitly call default scroll behaviour 
1340 void wxMDIClientWindow::OnScroll(wxScrollEvent
& event
) 
1342     // Note: for client windows, the scroll position is not set in 
1343     // WM_HSCROLL, WM_VSCROLL, so we can't easily determine what 
1344     // scroll position we're at. 
1345     // This makes it hard to paint patterns or bitmaps in the background, 
1346     // and have the client area scrollable as well. 
1348     if ( event
.GetOrientation() == wxHORIZONTAL 
) 
1349         m_scrollX 
= event
.GetPosition(); // Always returns zero! 
1351         m_scrollY 
= event
.GetPosition(); // Always returns zero! 
1356 void wxMDIClientWindow::DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
) 
1358     // Try to fix a problem whereby if you show an MDI child frame, then reposition the 
1359     // client area, you can end up with a non-refreshed portion in the client window 
1360     // (see OGL studio sample). So check if the position is changed and if so, 
1361     // redraw the MDI child frames. 
1363     const wxPoint oldPos 
= GetPosition(); 
1365     wxWindow::DoSetSize(x
, y
, width
, height
, sizeFlags 
| wxSIZE_FORCE
); 
1367     const wxPoint newPos 
= GetPosition(); 
1369     if ((newPos
.x 
!= oldPos
.x
) || (newPos
.y 
!= oldPos
.y
)) 
1373             wxWindowList::compatibility_iterator node 
= GetParent()->GetChildren().GetFirst(); 
1376                 wxWindow 
*child 
= node
->GetData(); 
1377                 if (child
->IsKindOf(CLASSINFO(wxMDIChildFrame
))) 
1379                    ::RedrawWindow(GetHwndOf(child
), 
1386                 node 
= node
->GetNext(); 
1392 void wxMDIChildFrame::OnIdle(wxIdleEvent
& event
) 
1394     // wxMSW prior to 2.5.3 created MDI child frames as visible, which resulted 
1395     // in flicker e.g. when the frame contained controls with non-trivial 
1396     // layout. Since 2.5.3, the frame is created hidden as all other top level 
1397     // windows. In order to maintain backward compatibility, the frame is shown 
1398     // in OnIdle, unless Show(false) was called by the programmer before. 
1399     if ( m_needsInitialShow 
) 
1404     // MDI child frames get their WM_SIZE when they're constructed but at this 
1405     // moment they don't have any children yet so all child windows will be 
1406     // positioned incorrectly when they are added later - to fix this, we 
1407     // generate an artificial size event here 
1408     if ( m_needsResize 
) 
1410         m_needsResize 
= false; // avoid any possibility of recursion 
1418 // --------------------------------------------------------------------------- 
1419 // private helper functions 
1420 // --------------------------------------------------------------------------- 
1425 void MDISetMenu(wxWindow 
*win
, HMENU hmenuFrame
, HMENU hmenuWindow
) 
1427     if ( hmenuFrame 
|| hmenuWindow 
) 
1429         if ( !::SendMessage(GetWinHwnd(win
), 
1432                             (LPARAM
)hmenuWindow
) ) 
1434             DWORD err 
= ::GetLastError(); 
1437                 wxLogApiError(wxT("SendMessage(WM_MDISETMENU)"), err
); 
1442     // update menu bar of the parent window 
1443     wxWindow 
*parent 
= win
->GetParent(); 
1444     wxCHECK_RET( parent
, wxT("MDI client without parent frame? weird...") ); 
1446     ::SendMessage(GetWinHwnd(win
), WM_MDIREFRESHMENU
, 0, 0L); 
1448     ::DrawMenuBar(GetWinHwnd(parent
)); 
1451 void MDIInsertWindowMenu(wxWindow 
*win
, WXHMENU hMenu
, HMENU menuWin
) 
1453     HMENU hmenu 
= (HMENU
)hMenu
; 
1457         // Try to insert Window menu in front of Help, otherwise append it. 
1458         int N 
= GetMenuItemCount(hmenu
); 
1459         bool inserted 
= false; 
1460         for ( int i 
= 0; i 
< N
; i
++ ) 
1463             if ( !::GetMenuString(hmenu
, i
, buf
, WXSIZEOF(buf
), MF_BYPOSITION
) ) 
1465                 wxLogLastError(wxT("GetMenuString")); 
1470             const wxString label 
= wxStripMenuCodes(buf
); 
1471             if ( label 
== wxGetStockLabel(wxID_HELP
, wxSTOCK_NOFLAGS
) ) 
1474                 ::InsertMenu(hmenu
, i
, MF_BYPOSITION 
| MF_POPUP 
| MF_STRING
, 
1476                              wxString(wxGetTranslation(WINDOW_MENU_LABEL
)).wx_str()); 
1483             ::AppendMenu(hmenu
, MF_POPUP
, 
1485                          wxString(wxGetTranslation(WINDOW_MENU_LABEL
)).wx_str()); 
1489     MDISetMenu(win
, hmenu
, menuWin
); 
1492 void MDIRemoveWindowMenu(wxWindow 
*win
, WXHMENU hMenu
) 
1494     HMENU hmenu 
= (HMENU
)hMenu
; 
1500         int N 
= ::GetMenuItemCount(hmenu
); 
1501         for ( int i 
= 0; i 
< N
; i
++ ) 
1503             if ( !::GetMenuString(hmenu
, i
, buf
, WXSIZEOF(buf
), MF_BYPOSITION
) ) 
1505                 // Ignore successful read of menu string with length 0 which 
1506                 // occurs, for example, for a maximized MDI child system menu 
1507                 if ( ::GetLastError() != 0 ) 
1509                     wxLogLastError(wxT("GetMenuString")); 
1515             if ( wxStrcmp(buf
, wxGetTranslation(WINDOW_MENU_LABEL
)) == 0 ) 
1517                 if ( !::RemoveMenu(hmenu
, i
, MF_BYPOSITION
) ) 
1519                     wxLogLastError(wxT("RemoveMenu")); 
1529         // we don't change the windows menu, but we update the main one 
1530         MDISetMenu(win
, hmenu
, NULL
); 
1534 void UnpackMDIActivate(WXWPARAM wParam
, WXLPARAM lParam
, 
1535                               WXWORD 
*activate
, WXHWND 
*hwndAct
, WXHWND 
*hwndDeact
) 
1538     *hwndAct 
= (WXHWND
)lParam
; 
1539     *hwndDeact 
= (WXHWND
)wParam
; 
1542 } // anonymous namespace 
1544 #endif // wxUSE_MDI && !defined(__WXUNIVERSAL__)