1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/msw/mdi.cpp 
   3 // Purpose:     MDI classes for wxMSW 
   4 // Author:      Julian Smart 
   8 // Copyright:   (c) Julian Smart 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // =========================================================================== 
  14 // =========================================================================== 
  16 // --------------------------------------------------------------------------- 
  18 // --------------------------------------------------------------------------- 
  20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  21     #pragma implementation "mdi.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  31 #if wxUSE_MDI_ARCHITECTURE && !defined(__WXUNIVERSAL__) 
  39     #include "wx/dialog.h" 
  41         #include "wx/statusbr.h" 
  43     #include "wx/settings.h" 
  49 #include "wx/msw/private.h" 
  51 #if wxUSE_STATUSBAR && wxUSE_NATIVE_STATUSBAR 
  52     #include "wx/msw/statbr95.h" 
  56     #include "wx/toolbar.h" 
  57 #endif // wxUSE_TOOLBAR 
  61 // --------------------------------------------------------------------------- 
  63 // --------------------------------------------------------------------------- 
  65 extern wxWindowList wxModelessWindows
;      // from dialog.cpp 
  66 extern wxMenu 
*wxCurrentPopupMenu
; 
  68 extern const wxChar 
*wxMDIFrameClassName
;   // from app.cpp 
  69 extern const wxChar 
*wxMDIChildFrameClassName
; 
  70 extern const wxChar 
*wxMDIChildFrameClassNameNoRedraw
; 
  71 extern void wxAssociateWinWithHandle(HWND hWnd
, wxWindow 
*win
); 
  72 extern void wxRemoveHandleAssociation(wxWindow 
*win
); 
  74 static HWND invalidHandle 
= 0; 
  76 // --------------------------------------------------------------------------- 
  78 // --------------------------------------------------------------------------- 
  80 static const int IDM_WINDOWTILE  
= 4001; 
  81 static const int IDM_WINDOWTILEHOR  
= 4001; 
  82 static const int IDM_WINDOWCASCADE 
= 4002; 
  83 static const int IDM_WINDOWICONS 
= 4003; 
  84 static const int IDM_WINDOWNEXT 
= 4004; 
  85 static const int IDM_WINDOWTILEVERT 
= 4005; 
  86 static const int IDM_WINDOWPREV 
= 4006; 
  88 // This range gives a maximum of 500 MDI children. Should be enough :-) 
  89 static const int wxFIRST_MDI_CHILD 
= 4100; 
  90 static const int wxLAST_MDI_CHILD 
= 4600; 
  92 // Status border dimensions 
  93 static const int wxTHICK_LINE_BORDER 
= 3; 
  94 static const int wxTHICK_LINE_WIDTH  
= 1; 
  96 // --------------------------------------------------------------------------- 
  98 // --------------------------------------------------------------------------- 
 100 // set the MDI menus (by sending the WM_MDISETMENU message) and update the menu 
 101 // of the parent of win (which is supposed to be the MDI client window) 
 102 static void MDISetMenu(wxWindow 
*win
, HMENU hmenuFrame
, HMENU hmenuWindow
); 
 104 // insert the window menu (subMenu) into menu just before "Help" submenu or at 
 105 // the very end if not found 
 106 static void InsertWindowMenu(wxWindow 
*win
, WXHMENU menu
, HMENU subMenu
); 
 108 // Remove the window menu 
 109 static void RemoveWindowMenu(wxWindow 
*win
, WXHMENU menu
); 
 111 // is this an id of an MDI child? 
 112 inline bool IsMdiCommandId(int id
) 
 114     return (id 
>= wxFIRST_MDI_CHILD
) && (id 
<= wxLAST_MDI_CHILD
); 
 117 // unpack the parameters of WM_MDIACTIVATE message 
 118 static void UnpackMDIActivate(WXWPARAM wParam
, WXLPARAM lParam
, 
 119                               WXWORD 
*activate
, WXHWND 
*hwndAct
, WXHWND 
*hwndDeact
); 
 121 // return the HMENU of the MDI menu 
 122 static inline HMENU 
GetMDIWindowMenu(wxMDIParentFrame 
*frame
) 
 124     wxMenu 
*menu 
= frame
->GetWindowMenu(); 
 125     return menu 
? GetHmenuOf(menu
) : 0; 
 128 // =========================================================================== 
 130 // =========================================================================== 
 132 // --------------------------------------------------------------------------- 
 134 // --------------------------------------------------------------------------- 
 136 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame
, wxFrame
) 
 137 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame
, wxFrame
) 
 138 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow
, wxWindow
) 
 140 BEGIN_EVENT_TABLE(wxMDIParentFrame
, wxFrame
) 
 141     EVT_SIZE(wxMDIParentFrame::OnSize
) 
 142     EVT_SYS_COLOUR_CHANGED(wxMDIParentFrame::OnSysColourChanged
) 
 145 BEGIN_EVENT_TABLE(wxMDIChildFrame
, wxFrame
) 
 146     EVT_IDLE(wxMDIChildFrame::OnIdle
) 
 149 BEGIN_EVENT_TABLE(wxMDIClientWindow
, wxWindow
) 
 150     EVT_SCROLL(wxMDIClientWindow::OnScroll
) 
 153 // =========================================================================== 
 154 // wxMDIParentFrame: the frame which contains the client window which manages 
 156 // =========================================================================== 
 158 wxMDIParentFrame::wxMDIParentFrame() 
 160     m_clientWindow 
= NULL
; 
 161     m_currentChild 
= NULL
; 
 162     m_windowMenu 
= (wxMenu
*) NULL
; 
 163     m_parentFrameActive 
= true; 
 166 bool wxMDIParentFrame::Create(wxWindow 
*parent
, 
 168                               const wxString
& title
, 
 172                               const wxString
& name
) 
 174   m_clientWindow 
= NULL
; 
 175   m_currentChild 
= NULL
; 
 177   // this style can be used to prevent a window from having the standard MDI 
 179   if ( style 
& wxFRAME_NO_WINDOW_MENU 
) 
 181       m_windowMenu 
= (wxMenu 
*)NULL
; 
 183   else // normal case: we have the window menu, so construct it 
 185       m_windowMenu 
= new wxMenu
; 
 187       m_windowMenu
->Append(IDM_WINDOWCASCADE
, _("&Cascade")); 
 188       m_windowMenu
->Append(IDM_WINDOWTILEHOR
, _("Tile &Horizontally")); 
 189       m_windowMenu
->Append(IDM_WINDOWTILEVERT
, _("Tile &Vertically")); 
 190       m_windowMenu
->AppendSeparator(); 
 191       m_windowMenu
->Append(IDM_WINDOWICONS
, _("&Arrange Icons")); 
 192       m_windowMenu
->Append(IDM_WINDOWNEXT
, _("&Next")); 
 193       m_windowMenu
->Append(IDM_WINDOWPREV
, _("&Previous")); 
 196   m_parentFrameActive 
= true; 
 199     wxTopLevelWindows
.Append(this); 
 202   m_windowStyle 
= style
; 
 205       parent
->AddChild(this); 
 210     m_windowId 
= NewControlId(); 
 213   WXDWORD msflags 
= MSWGetCreateWindowFlags(&exflags
); 
 214   msflags 
&= ~WS_VSCROLL
; 
 215   msflags 
&= ~WS_HSCROLL
; 
 217   if ( !wxWindow::MSWCreate(wxMDIFrameClassName
, 
 226   wxModelessWindows
.Append(this); 
 228   // unlike (almost?) all other windows, frames are created hidden 
 234 wxMDIParentFrame::~wxMDIParentFrame() 
 236     // see comment in ~wxMDIChildFrame 
 238     m_frameToolBar 
= NULL
; 
 240     m_frameStatusBar 
= NULL
; 
 247         m_windowMenu 
= (wxMenu
*) NULL
; 
 250     // the MDI frame menubar is not automatically deleted by Windows unlike for 
 254         ::DestroyMenu((HMENU
)m_hMenu
); 
 255         m_hMenu 
= (WXHMENU
)NULL
; 
 258     if ( m_clientWindow 
) 
 260         if ( m_clientWindow
->MSWGetOldWndProc() ) 
 261             m_clientWindow
->UnsubclassWin(); 
 263         m_clientWindow
->SetHWND(0); 
 264         delete m_clientWindow
; 
 268 #if wxUSE_MENUS_NATIVE 
 270 void wxMDIParentFrame::InternalSetMenuBar() 
 272     m_parentFrameActive 
= true; 
 274     InsertWindowMenu(GetClientWindow(), m_hMenu
, GetMDIWindowMenu(this)); 
 277 #endif // wxUSE_MENUS_NATIVE 
 279 void wxMDIParentFrame::SetWindowMenu(wxMenu
* menu
) 
 285             // Remove old window menu 
 286             RemoveWindowMenu(GetClientWindow(), m_hMenu
); 
 290         m_windowMenu 
= (wxMenu
*) NULL
; 
 298             InsertWindowMenu(GetClientWindow(), m_hMenu
, 
 299                              GetHmenuOf(m_windowMenu
)); 
 304 void wxMDIParentFrame::OnSize(wxSizeEvent
&) 
 306     if ( GetClientWindow() ) 
 309         GetClientSize(&width
, &height
); 
 311         GetClientWindow()->SetSize(0, 0, width
, height
); 
 315 // Returns the active MDI child window 
 316 wxMDIChildFrame 
*wxMDIParentFrame::GetActiveChild() const 
 318     HWND hWnd 
= (HWND
)::SendMessage(GetWinHwnd(GetClientWindow()), 
 319                                     WM_MDIGETACTIVE
, 0, 0L); 
 323         return (wxMDIChildFrame 
*)wxFindWinFromHandle((WXHWND
) hWnd
); 
 326 // Create the client window class (don't Create the window, just return a new 
 328 wxMDIClientWindow 
*wxMDIParentFrame::OnCreateClient() 
 330     return new wxMDIClientWindow
; 
 333 // Responds to colour changes, and passes event on to children. 
 334 void wxMDIParentFrame::OnSysColourChanged(wxSysColourChangedEvent
& event
) 
 336     if ( m_clientWindow 
) 
 338         m_clientWindow
->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE
)); 
 339         m_clientWindow
->Refresh(); 
 345 WXHICON 
wxMDIParentFrame::GetDefaultIcon() const 
 347     // we don't have any standard icons (any more) 
 351 // --------------------------------------------------------------------------- 
 353 // --------------------------------------------------------------------------- 
 355 void wxMDIParentFrame::Cascade() 
 357     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDICASCADE
, 0, 0); 
 360 // TODO: add a direction argument (hor/vert) 
 361 void wxMDIParentFrame::Tile() 
 363     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDITILE
, MDITILE_HORIZONTAL
, 0); 
 366 void wxMDIParentFrame::ArrangeIcons() 
 368     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDIICONARRANGE
, 0, 0); 
 371 void wxMDIParentFrame::ActivateNext() 
 373     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT
, 0, 0); 
 376 void wxMDIParentFrame::ActivatePrevious() 
 378     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT
, 0, 1); 
 381 // --------------------------------------------------------------------------- 
 382 // the MDI parent frame window proc 
 383 // --------------------------------------------------------------------------- 
 385 WXLRESULT 
wxMDIParentFrame::MSWWindowProc(WXUINT message
, 
 390     bool processed 
= false; 
 396                 WXWORD state
, minimized
; 
 398                 UnpackActivate(wParam
, lParam
, &state
, &minimized
, &hwnd
); 
 400                 processed 
= HandleActivate(state
, minimized 
!= 0, hwnd
); 
 408                 UnpackCommand(wParam
, lParam
, &id
, &hwnd
, &cmd
); 
 410                 (void)HandleCommand(id
, cmd
, hwnd
); 
 412                 // even if the frame didn't process it, there is no need to try it 
 413                 // once again (i.e. call wxFrame::HandleCommand()) - we just did it, 
 414                 // so pretend we processed the message anyhow 
 418             // always pass this message DefFrameProc(), otherwise MDI menu 
 419             // commands (and sys commands - more surprisingly!) won't work 
 420             MSWDefWindowProc(message
, wParam
, lParam
); 
 424             m_clientWindow 
= OnCreateClient(); 
 425             // Uses own style for client style 
 426             if ( !m_clientWindow
->CreateClient(this, GetWindowStyleFlag()) ) 
 428                 wxLogMessage(_("Failed to create MDI parent frame.")); 
 439             // we erase background ourselves 
 447                 UnpackMenuSelect(wParam
, lParam
, &item
, &flags
, &hmenu
); 
 449                 if ( m_parentFrameActive 
) 
 451                     processed 
= HandleMenuSelect(item
, flags
, hmenu
); 
 453                 else if (m_currentChild
) 
 455                     processed 
= m_currentChild
-> 
 456                         HandleMenuSelect(item
, flags
, hmenu
); 
 462             // as we don't (usually) resize the MDI client to exactly fit the 
 463             // client area (we put it below the toolbar, above statusbar &c), 
 464             // we should not pass this one to DefFrameProc 
 469         rc 
= wxFrame::MSWWindowProc(message
, wParam
, lParam
); 
 474 bool wxMDIParentFrame::HandleActivate(int state
, bool minimized
, WXHWND activate
) 
 476     bool processed 
= false; 
 478     if ( wxWindow::HandleActivate(state
, minimized
, activate
) ) 
 484     // If this window is an MDI parent, we must also send an OnActivate message 
 485     // to the current child. 
 486     if ( (m_currentChild 
!= NULL
) && 
 487          ((state 
== WA_ACTIVE
) || (state 
== WA_CLICKACTIVE
)) ) 
 489         wxActivateEvent 
event(wxEVT_ACTIVATE
, true, m_currentChild
->GetId()); 
 490         event
.SetEventObject( m_currentChild 
); 
 491         if ( m_currentChild
->GetEventHandler()->ProcessEvent(event
) ) 
 498 bool wxMDIParentFrame::HandleCommand(WXWORD id
, WXWORD cmd
, WXHWND hwnd
) 
 500     // In case it's e.g. a toolbar. 
 503         wxWindow 
*win 
= wxFindWinFromHandle(hwnd
); 
 505             return win
->MSWCommand(cmd
, id
); 
 508     // is it one of standard MDI commands? 
 514         case IDM_WINDOWCASCADE
: 
 516             wParam 
= MDITILE_SKIPDISABLED
; 
 519         case IDM_WINDOWTILEHOR
: 
 520             wParam 
|= MDITILE_HORIZONTAL
; 
 523         case IDM_WINDOWTILEVERT
: 
 525                 wParam 
= MDITILE_VERTICAL
; 
 527             wParam 
|= MDITILE_SKIPDISABLED
; 
 530         case IDM_WINDOWICONS
: 
 531             msg 
= WM_MDIICONARRANGE
; 
 536             lParam 
= 0;         // next child 
 541             lParam 
= 1;         // previous child 
 550         ::SendMessage(GetWinHwnd(GetClientWindow()), msg
, wParam
, lParam
); 
 555     // FIXME VZ: what does this test do?? 
 558         return false; // Get WndProc to call default proc 
 561     if ( IsMdiCommandId(id
) ) 
 563         wxWindowList::compatibility_iterator node 
= GetChildren().GetFirst(); 
 566             wxWindow 
*child 
= node
->GetData(); 
 567             if ( child
->GetHWND() ) 
 569                 long childId 
= wxGetWindowId(child
->GetHWND()); 
 570                 if (childId 
== (long)id
) 
 572                     ::SendMessage( GetWinHwnd(GetClientWindow()), 
 574                                    (WPARAM
)child
->GetHWND(), 0); 
 578             node 
= node
->GetNext(); 
 581     else if ( m_parentFrameActive 
) 
 583         return ProcessCommand(id
); 
 585     else if ( m_currentChild 
) 
 587         return m_currentChild
->HandleCommand(id
, cmd
, hwnd
); 
 591         // this shouldn't happen because it means that our messages are being 
 592         // lost (they're not sent to the parent frame nor to the children) 
 593         wxFAIL_MSG(wxT("MDI parent frame is not active, yet there is no active MDI child?")); 
 599 WXLRESULT 
wxMDIParentFrame::MSWDefWindowProc(WXUINT message
, 
 604     if ( GetClientWindow() ) 
 605         clientWnd 
= GetClientWindow()->GetHWND(); 
 609     return DefFrameProc(GetHwnd(), (HWND
)clientWnd
, message
, wParam
, lParam
); 
 612 bool wxMDIParentFrame::MSWTranslateMessage(WXMSG
* msg
) 
 614     MSG 
*pMsg 
= (MSG 
*)msg
; 
 616     // first let the current child get it 
 617     if ( m_currentChild 
&& m_currentChild
->GetHWND() && 
 618          m_currentChild
->MSWTranslateMessage(msg
) ) 
 623     // then try out accel table (will also check the menu accels) 
 624     if ( wxFrame::MSWTranslateMessage(msg
) ) 
 629     // finally, check for MDI specific built in accel keys 
 630     if ( pMsg
->message 
== WM_KEYDOWN 
|| pMsg
->message 
== WM_SYSKEYDOWN 
) 
 632         if ( ::TranslateMDISysAccel(GetWinHwnd(GetClientWindow()), pMsg
)) 
 639 // =========================================================================== 
 641 // =========================================================================== 
 643 void wxMDIChildFrame::Init() 
 645     m_needsResize 
= true; 
 648 bool wxMDIChildFrame::Create(wxMDIParentFrame 
*parent
, 
 650                              const wxString
& title
, 
 654                              const wxString
& name
) 
 657   wxWindowBase::Show(true); // MDI child frame starts off shown 
 662     m_windowId 
= (int)NewControlId(); 
 666       parent
->AddChild(this); 
 676   mcs
.szClass 
= style 
& wxFULL_REPAINT_ON_RESIZE
 
 677                     ? wxMDIChildFrameClassName
 
 678                     : wxMDIChildFrameClassNameNoRedraw
; 
 680   mcs
.hOwner 
= wxGetInstance(); 
 684       mcs
.x 
= CW_USEDEFAULT
; 
 689       mcs
.y 
= CW_USEDEFAULT
; 
 694       mcs
.cx 
= CW_USEDEFAULT
; 
 699       mcs
.cy 
= CW_USEDEFAULT
; 
 701   DWORD msflags 
= WS_OVERLAPPED 
| WS_CLIPCHILDREN 
| WS_VISIBLE 
; 
 702   if (style 
& wxMINIMIZE_BOX
) 
 703     msflags 
|= WS_MINIMIZEBOX
; 
 704   if (style 
& wxMAXIMIZE_BOX
) 
 705     msflags 
|= WS_MAXIMIZEBOX
; 
 706   if (style 
& wxTHICK_FRAME
) 
 707     msflags 
|= WS_THICKFRAME
; 
 708   if (style 
& wxSYSTEM_MENU
) 
 709     msflags 
|= WS_SYSMENU
; 
 710   if ((style 
& wxMINIMIZE
) || (style 
& wxICONIZE
)) 
 711     msflags 
|= WS_MINIMIZE
; 
 712   if (style 
& wxMAXIMIZE
) 
 713     msflags 
|= WS_MAXIMIZE
; 
 714   if (style 
& wxCAPTION
) 
 715     msflags 
|= WS_CAPTION
; 
 721   wxWindowCreationHook 
hook(this); 
 723   m_hWnd 
= (WXHWND
)::SendMessage(GetWinHwnd(parent
->GetClientWindow()), 
 724                                  WM_MDICREATE
, 0, (LONG
)(LPSTR
)&mcs
); 
 726   wxAssociateWinWithHandle((HWND
) GetHWND(), this); 
 728   wxModelessWindows
.Append(this); 
 733 wxMDIChildFrame::~wxMDIChildFrame() 
 735     // will be destroyed by DestroyChildren() but reset them before calling it 
 736     // to avoid using dangling pointers if a callback comes in the meanwhile 
 738     m_frameToolBar 
= NULL
; 
 740     m_frameStatusBar 
= NULL
; 
 744     RemoveWindowMenu(NULL
, m_hMenu
); 
 749 // Set the client size (i.e. leave the calculation of borders etc. 
 751 void wxMDIChildFrame::DoSetClientSize(int width
, int height
) 
 753   HWND hWnd 
= GetHwnd(); 
 756   ::GetClientRect(hWnd
, &rect
); 
 759   GetWindowRect(hWnd
, &rect2
); 
 761   // Find the difference between the entire window (title bar and all) 
 762   // and the client area; add this to the new client size to move the 
 764   int actual_width 
= rect2
.right 
- rect2
.left 
- rect
.right 
+ width
; 
 765   int actual_height 
= rect2
.bottom 
- rect2
.top 
- rect
.bottom 
+ height
; 
 767   if (GetStatusBar() && GetStatusBar()->IsShown()) 
 770     GetStatusBar()->GetSize(&sx
, &sy
); 
 775   point
.x 
= rect2
.left
; 
 778   // If there's an MDI parent, must subtract the parent's top left corner 
 779   // since MoveWindow moves relative to the parent 
 780   wxMDIParentFrame 
*mdiParent 
= (wxMDIParentFrame 
*)GetParent(); 
 781   ::ScreenToClient((HWND
) mdiParent
->GetClientWindow()->GetHWND(), &point
); 
 783   MoveWindow(hWnd
, point
.x
, point
.y
, actual_width
, actual_height
, (BOOL
)true); 
 785   wxSizeEvent 
event(wxSize(width
, height
), m_windowId
); 
 786   event
.SetEventObject( this ); 
 787   GetEventHandler()->ProcessEvent(event
); 
 790 void wxMDIChildFrame::DoGetPosition(int *x
, int *y
) const 
 793   GetWindowRect(GetHwnd(), &rect
); 
 798   // Since we now have the absolute screen coords, 
 799   // if there's a parent we must subtract its top left corner 
 800   wxMDIParentFrame 
*mdiParent 
= (wxMDIParentFrame 
*)GetParent(); 
 801   ::ScreenToClient((HWND
) mdiParent
->GetClientWindow()->GetHWND(), &point
); 
 807 void wxMDIChildFrame::InternalSetMenuBar() 
 809     wxMDIParentFrame 
*parent 
= (wxMDIParentFrame 
*)GetParent(); 
 811     InsertWindowMenu(parent
->GetClientWindow(), 
 812                      m_hMenu
, GetMDIWindowMenu(parent
)); 
 814     parent
->m_parentFrameActive 
= false; 
 817 WXHICON 
wxMDIChildFrame::GetDefaultIcon() const 
 819     // we don't have any standard icons (any more) 
 823 // --------------------------------------------------------------------------- 
 825 // --------------------------------------------------------------------------- 
 827 void wxMDIChildFrame::Maximize(bool maximize
) 
 829     wxMDIParentFrame 
*parent 
= (wxMDIParentFrame 
*)GetParent(); 
 830     if ( parent 
&& parent
->GetClientWindow() ) 
 832         ::SendMessage(GetWinHwnd(parent
->GetClientWindow()), 
 833                       maximize 
? WM_MDIMAXIMIZE 
: WM_MDIRESTORE
, 
 834                       (WPARAM
)GetHwnd(), 0); 
 838 void wxMDIChildFrame::Restore() 
 840     wxMDIParentFrame 
*parent 
= (wxMDIParentFrame 
*)GetParent(); 
 841     if ( parent 
&& parent
->GetClientWindow() ) 
 843         ::SendMessage(GetWinHwnd(parent
->GetClientWindow()), WM_MDIRESTORE
, 
 844                       (WPARAM
) GetHwnd(), 0); 
 848 void wxMDIChildFrame::Activate() 
 850     wxMDIParentFrame 
*parent 
= (wxMDIParentFrame 
*)GetParent(); 
 851     if ( parent 
&& parent
->GetClientWindow() ) 
 853         ::SendMessage(GetWinHwnd(parent
->GetClientWindow()), WM_MDIACTIVATE
, 
 854                       (WPARAM
) GetHwnd(), 0); 
 858 // --------------------------------------------------------------------------- 
 859 // MDI window proc and message handlers 
 860 // --------------------------------------------------------------------------- 
 862 WXLRESULT 
wxMDIChildFrame::MSWWindowProc(WXUINT message
, 
 867     bool processed 
= false; 
 875                 UnpackCommand((WXWPARAM
)wParam
, (WXLPARAM
)lParam
, 
 878                 processed 
= HandleCommand(id
, cmd
, (WXHWND
)hwnd
); 
 882         case WM_GETMINMAXINFO
: 
 883             processed 
= HandleGetMinMaxInfo((MINMAXINFO 
*)lParam
); 
 889                 WXHWND hwndAct
, hwndDeact
; 
 890                 UnpackMDIActivate(wParam
, lParam
, &act
, &hwndAct
, &hwndDeact
); 
 892                 processed 
= HandleMDIActivate(act
, hwndAct
, hwndDeact
); 
 897             // must pass WM_MOVE to DefMDIChildProc() to recalculate MDI client 
 898             // scrollbars if necessary 
 903             // must pass WM_SIZE to DefMDIChildProc(), otherwise many weird 
 905             MSWDefWindowProc(message
, wParam
, lParam
); 
 909             // DefMDIChildProc handles SC_{NEXT/PREV}WINDOW here, so pass it 
 910             // the message (the base class version does not) 
 911             return MSWDefWindowProc(message
, wParam
, lParam
); 
 913         case WM_WINDOWPOSCHANGING
: 
 914             processed 
= HandleWindowPosChanging((LPWINDOWPOS
)lParam
); 
 919         rc 
= wxFrame::MSWWindowProc(message
, wParam
, lParam
); 
 924 bool wxMDIChildFrame::HandleCommand(WXWORD id
, WXWORD cmd
, WXHWND hwnd
) 
 926     // In case it's e.g. a toolbar. 
 929         wxWindow 
*win 
= wxFindWinFromHandle(hwnd
); 
 931             return win
->MSWCommand(cmd
, id
); 
 934     if (wxCurrentPopupMenu
) 
 936         wxMenu 
*popupMenu 
= wxCurrentPopupMenu
; 
 937         wxCurrentPopupMenu 
= NULL
; 
 938         if (popupMenu
->MSWCommand(cmd
, id
)) 
 943     if (GetMenuBar() && GetMenuBar()->FindItem(id
)) 
 945         processed 
= ProcessCommand(id
); 
 955 bool wxMDIChildFrame::HandleMDIActivate(long WXUNUSED(activate
), 
 959     wxMDIParentFrame 
*parent 
= (wxMDIParentFrame 
*)GetParent(); 
 965     if ( m_hWnd 
== hwndAct 
) 
 968         parent
->m_currentChild 
= this; 
 970         HMENU child_menu 
= (HMENU
)GetWinMenu(); 
 973             parent
->m_parentFrameActive 
= false; 
 975             menuToSet 
= child_menu
; 
 978     else if ( m_hWnd 
== hwndDeact 
) 
 980         wxASSERT_MSG( parent
->m_currentChild 
== this, 
 981                       wxT("can't deactivate MDI child which wasn't active!") ); 
 984         parent
->m_currentChild 
= NULL
; 
 986         HMENU parent_menu 
= (HMENU
)parent
->GetWinMenu(); 
 988         // activate the the parent menu only when there is no other child 
 989         // that has been activated 
 990         if ( parent_menu 
&& !hwndAct 
) 
 992             parent
->m_parentFrameActive 
= true; 
 994             menuToSet 
= parent_menu
; 
 999         // we have nothing to do with it 
1005         MDISetMenu(parent
->GetClientWindow(), 
1006                    menuToSet
, GetMDIWindowMenu(parent
)); 
1009     wxActivateEvent 
event(wxEVT_ACTIVATE
, activated
, m_windowId
); 
1010     event
.SetEventObject( this ); 
1012     ResetWindowStyle((void *)NULL
); 
1014     return GetEventHandler()->ProcessEvent(event
); 
1017 bool wxMDIChildFrame::HandleWindowPosChanging(void *pos
) 
1019     WINDOWPOS 
*lpPos 
= (WINDOWPOS 
*)pos
; 
1020 #if defined(__WIN95__) 
1021     if (!(lpPos
->flags 
& SWP_NOSIZE
)) 
1024         DWORD dwExStyle 
= ::GetWindowLong(GetHwnd(), GWL_EXSTYLE
); 
1025         DWORD dwStyle 
= ::GetWindowLong(GetHwnd(), GWL_STYLE
); 
1026         if (ResetWindowStyle((void *) & rectClient
) && (dwStyle 
& WS_MAXIMIZE
)) 
1028             ::AdjustWindowRectEx(&rectClient
, dwStyle
, false, dwExStyle
); 
1029             lpPos
->x 
= rectClient
.left
; 
1030             lpPos
->y 
= rectClient
.top
; 
1031             lpPos
->cx 
= rectClient
.right 
- rectClient
.left
; 
1032             lpPos
->cy 
= rectClient
.bottom 
- rectClient
.top
; 
1035         wxMDIParentFrame
* pFrameWnd 
= (wxMDIParentFrame 
*)GetParent(); 
1036         if (pFrameWnd 
&& pFrameWnd
->GetToolBar() && pFrameWnd
->GetToolBar()->IsShown()) 
1038             pFrameWnd
->GetToolBar()->Refresh(); 
1047 bool wxMDIChildFrame::HandleGetMinMaxInfo(void *mmInfo
) 
1049     MINMAXINFO 
*info 
= (MINMAXINFO 
*)mmInfo
; 
1051     // let the default window proc calculate the size of MDI children 
1052     // frames because it is based on the size of the MDI client window, 
1053     // not on the values specified in wxWindow m_max variables 
1054     bool processed 
= MSWDefWindowProc(WM_GETMINMAXINFO
, 0, (LPARAM
)mmInfo
) != 0; 
1056     int minWidth 
= GetMinWidth(), 
1057         minHeight 
= GetMinHeight(); 
1059     // but allow GetSizeHints() to set the min size 
1060     if ( minWidth 
!= -1 ) 
1062         info
->ptMinTrackSize
.x 
= minWidth
; 
1067     if ( minHeight 
!= -1 ) 
1069         info
->ptMinTrackSize
.y 
= minHeight
; 
1077 // --------------------------------------------------------------------------- 
1078 // MDI specific message translation/preprocessing 
1079 // --------------------------------------------------------------------------- 
1081 WXLRESULT 
wxMDIChildFrame::MSWDefWindowProc(WXUINT message
, WXWPARAM wParam
, WXLPARAM lParam
) 
1083     return DefMDIChildProc(GetHwnd(), 
1084                            (UINT
)message
, (WPARAM
)wParam
, (LPARAM
)lParam
); 
1087 bool wxMDIChildFrame::MSWTranslateMessage(WXMSG
* msg
) 
1089     return wxFrame::MSWTranslateMessage(msg
); 
1092 // --------------------------------------------------------------------------- 
1094 // --------------------------------------------------------------------------- 
1096 void wxMDIChildFrame::MSWDestroyWindow() 
1098     invalidHandle 
= GetHwnd(); 
1100     wxMDIParentFrame 
*parent 
= (wxMDIParentFrame 
*)GetParent(); 
1102     // Must make sure this handle is invalidated (set to NULL) since all sorts 
1103     // of things could happen after the child client is destroyed, but before 
1104     // the wxFrame is destroyed. 
1106     HWND oldHandle 
= (HWND
)GetHWND(); 
1107     SendMessage(GetWinHwnd(parent
->GetClientWindow()), WM_MDIDESTROY
, 
1108                 (WPARAM
)oldHandle
, 0); 
1110     if (parent
->GetActiveChild() == (wxMDIChildFrame
*) NULL
) 
1111         ResetWindowStyle((void*) NULL
); 
1117         ::DestroyMenu((HMENU
) m_hMenu
); 
1120     wxRemoveHandleAssociation(this); 
1124 // Change the client window's extended style so we don't get a client edge 
1125 // style when a child is maximised (a double border looks silly.) 
1126 bool wxMDIChildFrame::ResetWindowStyle(void *vrect
) 
1128 #if defined(__WIN95__) 
1129     RECT 
*rect 
= (RECT 
*)vrect
; 
1130     wxMDIParentFrame
* pFrameWnd 
= (wxMDIParentFrame 
*)GetParent(); 
1131     wxMDIChildFrame
* pChild 
= pFrameWnd
->GetActiveChild(); 
1132     if (!pChild 
|| (pChild 
== this)) 
1134         HWND hwndClient 
= GetWinHwnd(pFrameWnd
->GetClientWindow()); 
1135         DWORD dwStyle 
= ::GetWindowLong(hwndClient
, GWL_EXSTYLE
); 
1137         // we want to test whether there is a maximized child, so just set 
1138         // dwThisStyle to 0 if there is no child at all 
1139         DWORD dwThisStyle 
= pChild
 
1140             ? ::GetWindowLong(GetWinHwnd(pChild
), GWL_STYLE
) : 0; 
1141         DWORD dwNewStyle 
= dwStyle
; 
1142         if ( dwThisStyle 
& WS_MAXIMIZE 
) 
1143             dwNewStyle 
&= ~(WS_EX_CLIENTEDGE
); 
1145             dwNewStyle 
|= WS_EX_CLIENTEDGE
; 
1147         if (dwStyle 
!= dwNewStyle
) 
1149             // force update of everything 
1150             ::RedrawWindow(hwndClient
, NULL
, NULL
, 
1151                            RDW_INVALIDATE 
| RDW_ALLCHILDREN
); 
1152             ::SetWindowLong(hwndClient
, GWL_EXSTYLE
, dwNewStyle
); 
1153             ::SetWindowPos(hwndClient
, NULL
, 0, 0, 0, 0, 
1154                            SWP_FRAMECHANGED 
| SWP_NOACTIVATE 
| 
1155                            SWP_NOMOVE 
| SWP_NOSIZE 
| SWP_NOZORDER 
| 
1158                 ::GetClientRect(hwndClient
, rect
); 
1168 // =========================================================================== 
1169 // wxMDIClientWindow: the window of predefined (by Windows) class which 
1170 // contains the child frames 
1171 // =========================================================================== 
1173 bool wxMDIClientWindow::CreateClient(wxMDIParentFrame 
*parent
, long style
) 
1175     m_backgroundColour 
= wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE
); 
1177     CLIENTCREATESTRUCT ccs
; 
1178     m_windowStyle 
= style
; 
1181     ccs
.hWindowMenu 
= GetMDIWindowMenu(parent
); 
1182     ccs
.idFirstChild 
= wxFIRST_MDI_CHILD
; 
1184     DWORD msStyle 
= MDIS_ALLCHILDSTYLES 
| WS_VISIBLE 
| WS_CHILD 
| 
1185                     WS_CLIPCHILDREN 
| WS_CLIPSIBLINGS
; 
1187     if ( style 
& wxHSCROLL 
) 
1188         msStyle 
|= WS_HSCROLL
; 
1189     if ( style 
& wxVSCROLL 
) 
1190         msStyle 
|= WS_VSCROLL
; 
1192 #if defined(__WIN95__) 
1193     DWORD exStyle 
= WS_EX_CLIENTEDGE
; 
1198     wxWindowCreationHook 
hook(this); 
1199     m_hWnd 
= (WXHWND
)::CreateWindowEx
 
1209                         (LPSTR
)(LPCLIENTCREATESTRUCT
)&ccs
); 
1212         wxLogLastError(wxT("CreateWindowEx(MDI client)")); 
1217     SubclassWin(m_hWnd
); 
1222 // Explicitly call default scroll behaviour 
1223 void wxMDIClientWindow::OnScroll(wxScrollEvent
& event
) 
1225     // Note: for client windows, the scroll position is not set in 
1226     // WM_HSCROLL, WM_VSCROLL, so we can't easily determine what 
1227     // scroll position we're at. 
1228     // This makes it hard to paint patterns or bitmaps in the background, 
1229     // and have the client area scrollable as well. 
1231     if ( event
.GetOrientation() == wxHORIZONTAL 
) 
1232         m_scrollX 
= event
.GetPosition(); // Always returns zero! 
1234         m_scrollY 
= event
.GetPosition(); // Always returns zero! 
1239 void wxMDIClientWindow::DoSetSize(int x
, int y
, int width
, int height
, int sizeFlags
) 
1241     // Try to fix a problem whereby if you show an MDI child frame, then reposition the 
1242     // client area, you can end up with a non-refreshed portion in the client window 
1243     // (see OGL studio sample). So check if the position is changed and if so, 
1244     // redraw the MDI child frames. 
1246     wxPoint oldPos 
= GetPosition(); 
1248     wxWindow::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
1250     wxPoint newPos 
= GetPosition(); 
1252     if ((newPos
.x 
!= oldPos
.x
) || (newPos
.y 
!= oldPos
.y
)) 
1256             wxWindowList::compatibility_iterator node 
= GetParent()->GetChildren().GetFirst(); 
1259                 wxWindow 
*child 
= node
->GetData(); 
1260                 if (child
->IsKindOf(CLASSINFO(wxMDIChildFrame
))) 
1262                    ::RedrawWindow(GetHwndOf(child
), 
1269                 node 
= node
->GetNext(); 
1275 void wxMDIChildFrame::OnIdle(wxIdleEvent
& event
) 
1277     // MDI child frames get their WM_SIZE when they're constructed but at this 
1278     // moment they don't have any children yet so all child windows will be 
1279     // positioned incorrectly when they are added later - to fix this, we 
1280     // generate an artificial size event here 
1281     if ( m_needsResize 
) 
1283         m_needsResize 
= false; // avoid any possibility of recursion 
1291 // --------------------------------------------------------------------------- 
1292 // non member functions 
1293 // --------------------------------------------------------------------------- 
1295 static void MDISetMenu(wxWindow 
*win
, HMENU hmenuFrame
, HMENU hmenuWindow
) 
1297     ::SendMessage(GetWinHwnd(win
), WM_MDISETMENU
, 
1299                   (WPARAM
)hmenuFrame
, (LPARAM
)hmenuWindow
 
1301                   0, MAKELPARAM(hmenuFrame
, hmenuWindow
) 
1305     // update menu bar of the parent window 
1306     wxWindow 
*parent 
= win
->GetParent(); 
1307     wxCHECK_RET( parent
, wxT("MDI client without parent frame? weird...") ); 
1309     ::SendMessage(GetWinHwnd(win
), WM_MDIREFRESHMENU
, 0, 0L); 
1311     ::DrawMenuBar(GetWinHwnd(parent
)); 
1314 static void InsertWindowMenu(wxWindow 
*win
, WXHMENU menu
, HMENU subMenu
) 
1316     // Try to insert Window menu in front of Help, otherwise append it. 
1317     HMENU hmenu 
= (HMENU
)menu
; 
1321         int N 
= GetMenuItemCount(hmenu
); 
1322         bool success 
= false; 
1323         for ( int i 
= 0; i 
< N
; i
++ ) 
1326             int chars 
= GetMenuString(hmenu
, i
, buf
, WXSIZEOF(buf
), MF_BYPOSITION
); 
1329                 wxLogLastError(wxT("GetMenuString")); 
1334             if ( wxStripMenuCodes(wxString(buf
)).IsSameAs(_("Help")) ) 
1337                 ::InsertMenu(hmenu
, i
, MF_BYPOSITION 
| MF_POPUP 
| MF_STRING
, 
1338                              (UINT
)subMenu
, _("&Window")); 
1345             ::AppendMenu(hmenu
, MF_POPUP
, (UINT
)subMenu
, _("&Window")); 
1349     MDISetMenu(win
, hmenu
, subMenu
); 
1352 static void RemoveWindowMenu(wxWindow 
*win
, WXHMENU menu
) 
1354     HMENU hMenu 
= (HMENU
)menu
; 
1360         int N 
= ::GetMenuItemCount(hMenu
); 
1361         for ( int i 
= 0; i 
< N
; i
++ ) 
1363             if ( !::GetMenuString(hMenu
, i
, buf
, WXSIZEOF(buf
), MF_BYPOSITION
) ) 
1365                 // Ignore successful read of menu string with length 0 which 
1366                 // occurs, for example, for a maximized MDI childs system menu 
1367                 if ( ::GetLastError() != 0 ) 
1369                     wxLogLastError(wxT("GetMenuString")); 
1375             if ( wxStrcmp(buf
, _("&Window")) == 0 ) 
1377                 if ( !::RemoveMenu(hMenu
, i
, MF_BYPOSITION
) ) 
1379                     wxLogLastError(wxT("RemoveMenu")); 
1389         // we don't change the windows menu, but we update the main one 
1390         MDISetMenu(win
, hMenu
, NULL
); 
1394 static void UnpackMDIActivate(WXWPARAM wParam
, WXLPARAM lParam
, 
1395                               WXWORD 
*activate
, WXHWND 
*hwndAct
, WXHWND 
*hwndDeact
) 
1398     *hwndAct 
= (WXHWND
)lParam
; 
1399     *hwndDeact 
= (WXHWND
)wParam
; 
1403 // wxUSE_MDI_ARCHITECTURE && !defined(__WXUNIVERSAL__)