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
6 // Created:     04/01/98
7 // Copyright:   (c) 1998 Julian Smart
8 //              (c) 2008-2009 Vadim Zeitlin
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15 
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19 
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22 
23 #ifdef __BORLANDC__
24     #pragma hdrstop
25 #endif
26 
27 #if wxUSE_MDI && !defined(__WXUNIVERSAL__)
28 
29 #include "wx/mdi.h"
30 
31 #ifndef WX_PRECOMP
32     #include "wx/frame.h"
33     #include "wx/menu.h"
34     #include "wx/app.h"
35     #include "wx/utils.h"
36     #include "wx/dialog.h"
37     #include "wx/statusbr.h"
38     #include "wx/settings.h"
39     #include "wx/intl.h"
40     #include "wx/log.h"
41     #include "wx/toolbar.h"
42 #endif
43 
44 #include "wx/stockitem.h"
45 #include "wx/msw/private.h"
46 
47 #include <string.h>
48 
49 // ---------------------------------------------------------------------------
50 // global variables
51 // ---------------------------------------------------------------------------
52 
53 extern wxMenu *wxCurrentPopupMenu;
54 
55 extern void wxRemoveHandleAssociation(wxWindow *win);
56 
57 namespace
58 {
59 
60 // ---------------------------------------------------------------------------
61 // constants
62 // ---------------------------------------------------------------------------
63 
64 // First ID for the MDI child menu item in the "Window" menu.
65 const int wxFIRST_MDI_CHILD = 4100;
66 
67 // There can be no more than 9 children in the "Window" menu as beginning with
68 // the tenth one they're not shown and "More windows..." menu item is used
69 // instead.
70 const int wxLAST_MDI_CHILD = wxFIRST_MDI_CHILD + 8;
71 
72 // The ID of the "More windows..." menu item is the next one after the last
73 // child.
74 const int wxID_MDI_MORE_WINDOWS = wxLAST_MDI_CHILD + 1;
75 
76 // The MDI "Window" menu label
77 const char *WINDOW_MENU_LABEL = gettext_noop("&Window");
78 
79 // ---------------------------------------------------------------------------
80 // private functions
81 // ---------------------------------------------------------------------------
82 
83 // set the MDI menus (by sending the WM_MDISETMENU message) and update the menu
84 // of the parent of win (which is supposed to be the MDI client window)
85 void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow);
86 
87 // insert the window menu (subMenu) into menu just before "Help" submenu or at
88 // the very end if not found
89 void MDIInsertWindowMenu(wxWindow *win, WXHMENU hMenu, HMENU subMenu);
90 
91 // Remove the window menu
92 void MDIRemoveWindowMenu(wxWindow *win, WXHMENU hMenu);
93 
94 // unpack the parameters of WM_MDIACTIVATE message
95 void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
96                        WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact);
97 
98 // return the HMENU of the MDI menu
99 //
100 // this function works correctly even when we don't have a window menu and just
101 // returns 0 then
GetMDIWindowMenu(wxMDIParentFrame * frame)102 inline HMENU GetMDIWindowMenu(wxMDIParentFrame *frame)
103 {
104     wxMenu *menu = frame->GetWindowMenu();
105     return menu ? GetHmenuOf(menu) : 0;
106 }
107 
108 } // anonymous namespace
109 
110 // ===========================================================================
111 // implementation
112 // ===========================================================================
113 
114 // ---------------------------------------------------------------------------
115 // wxWin macros
116 // ---------------------------------------------------------------------------
117 
IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame,wxFrame)118 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame, wxFrame)
119 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame, wxFrame)
120 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow, wxWindow)
121 
122 BEGIN_EVENT_TABLE(wxMDIParentFrame, wxFrame)
123     EVT_SIZE(wxMDIParentFrame::OnSize)
124     EVT_ICONIZE(wxMDIParentFrame::OnIconized)
125     EVT_SYS_COLOUR_CHANGED(wxMDIParentFrame::OnSysColourChanged)
126 
127 #if wxUSE_MENUS
128     EVT_MENU_RANGE(wxFIRST_MDI_CHILD, wxLAST_MDI_CHILD,
129                    wxMDIParentFrame::OnMDIChild)
130     EVT_MENU_RANGE(wxID_MDI_WINDOW_FIRST, wxID_MDI_WINDOW_LAST,
131                    wxMDIParentFrame::OnMDICommand)
132 #endif // wxUSE_MENUS
133 END_EVENT_TABLE()
134 
135 BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
136     EVT_IDLE(wxMDIChildFrame::OnIdle)
137 END_EVENT_TABLE()
138 
139 BEGIN_EVENT_TABLE(wxMDIClientWindow, wxWindow)
140     EVT_SCROLL(wxMDIClientWindow::OnScroll)
141 END_EVENT_TABLE()
142 
143 // ===========================================================================
144 // wxMDIParentFrame: the frame which contains the client window which manages
145 // the children
146 // ===========================================================================
147 
148 void wxMDIParentFrame::Init()
149 {
150 #if wxUSE_MENUS && wxUSE_ACCEL
151   // the default menu doesn't have any accelerators (even if we have it)
152   m_accelWindowMenu = NULL;
153 #endif // wxUSE_MENUS && wxUSE_ACCEL
154 }
155 
Create(wxWindow * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & size,long style,const wxString & name)156 bool wxMDIParentFrame::Create(wxWindow *parent,
157                               wxWindowID id,
158                               const wxString& title,
159                               const wxPoint& pos,
160                               const wxSize& size,
161                               long style,
162                               const wxString& name)
163 {
164   // this style can be used to prevent a window from having the standard MDI
165   // "Window" menu
166   if ( !(style & wxFRAME_NO_WINDOW_MENU) )
167   {
168       // normal case: we have the window menu, so construct it
169       m_windowMenu = new wxMenu;
170 
171       m_windowMenu->Append(wxID_MDI_WINDOW_CASCADE, _("&Cascade"));
172       m_windowMenu->Append(wxID_MDI_WINDOW_TILE_HORZ, _("Tile &Horizontally"));
173       m_windowMenu->Append(wxID_MDI_WINDOW_TILE_VERT, _("Tile &Vertically"));
174       m_windowMenu->AppendSeparator();
175       m_windowMenu->Append(wxID_MDI_WINDOW_ARRANGE_ICONS, _("&Arrange Icons"));
176       m_windowMenu->Append(wxID_MDI_WINDOW_NEXT, _("&Next"));
177       m_windowMenu->Append(wxID_MDI_WINDOW_PREV, _("&Previous"));
178   }
179 
180   if (!parent)
181     wxTopLevelWindows.Append(this);
182 
183   SetName(name);
184   m_windowStyle = style;
185 
186   if ( parent )
187       parent->AddChild(this);
188 
189   if ( id != wxID_ANY )
190     m_windowId = id;
191   else
192     m_windowId = NewControlId();
193 
194   WXDWORD exflags;
195   WXDWORD msflags = MSWGetCreateWindowFlags(&exflags);
196   msflags &= ~WS_VSCROLL;
197   msflags &= ~WS_HSCROLL;
198 
199   if ( !wxWindow::MSWCreate(wxApp::GetRegisteredClassName(wxT("wxMDIFrame")),
200                             title.t_str(),
201                             pos, size,
202                             msflags,
203                             exflags) )
204   {
205       return false;
206   }
207 
208   SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE));
209 
210   // unlike (almost?) all other windows, frames are created hidden
211   m_isShown = false;
212 
213   return true;
214 }
215 
~wxMDIParentFrame()216 wxMDIParentFrame::~wxMDIParentFrame()
217 {
218     // see comment in ~wxMDIChildFrame
219 #if wxUSE_TOOLBAR
220     m_frameToolBar = NULL;
221 #endif
222 #if wxUSE_STATUSBAR
223     m_frameStatusBar = NULL;
224 #endif // wxUSE_STATUSBAR
225 
226 #if wxUSE_MENUS && wxUSE_ACCEL
227     delete m_accelWindowMenu;
228 #endif // wxUSE_MENUS && wxUSE_ACCEL
229 
230     DestroyChildren();
231 
232     // the MDI frame menubar is not automatically deleted by Windows unlike for
233     // the normal frames
234     if ( m_hMenu )
235         ::DestroyMenu((HMENU)m_hMenu);
236 
237     if ( m_clientWindow )
238     {
239         if ( m_clientWindow->MSWGetOldWndProc() )
240             m_clientWindow->UnsubclassWin();
241 
242         m_clientWindow->SetHWND(0);
243         delete m_clientWindow;
244     }
245 }
246 
247 // ----------------------------------------------------------------------------
248 // wxMDIParentFrame child management
249 // ----------------------------------------------------------------------------
250 
GetActiveChild() const251 wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
252 {
253     HWND hWnd = (HWND)::SendMessage(GetWinHwnd(GetClientWindow()),
254                                     WM_MDIGETACTIVE, 0, 0L);
255     if ( !hWnd )
256         return NULL;
257 
258     return static_cast<wxMDIChildFrame *>(wxFindWinFromHandle(hWnd));
259 }
260 
GetChildFramesCount() const261 int wxMDIParentFrame::GetChildFramesCount() const
262 {
263     int count = 0;
264     for ( wxWindowList::const_iterator i = GetChildren().begin();
265           i != GetChildren().end();
266           ++i )
267     {
268         if ( wxDynamicCast(*i, wxMDIChildFrame) )
269             count++;
270     }
271 
272     return count;
273 }
274 
275 #if wxUSE_MENUS
276 
AddMDIChild(wxMDIChildFrame * WXUNUSED (child))277 void wxMDIParentFrame::AddMDIChild(wxMDIChildFrame * WXUNUSED(child))
278 {
279     switch ( GetChildFramesCount() )
280     {
281         case 1:
282             // first MDI child was just added, we need to insert the window
283             // menu now if we have it
284             AddWindowMenu();
285 
286             // and disable the items which can't be used until we have more
287             // than one child
288             UpdateWindowMenu(false);
289             break;
290 
291         case 2:
292             // second MDI child was added, enable the menu items which were
293             // disabled because they didn't make sense for a single window
294             UpdateWindowMenu(true);
295             break;
296     }
297 }
298 
RemoveMDIChild(wxMDIChildFrame * WXUNUSED (child))299 void wxMDIParentFrame::RemoveMDIChild(wxMDIChildFrame * WXUNUSED(child))
300 {
301     switch ( GetChildFramesCount() )
302     {
303         case 1:
304             // last MDI child is being removed, remove the now unnecessary
305             // window menu too
306             RemoveWindowMenu();
307 
308             // there is no need to call UpdateWindowMenu(true) here so this is
309             // not quite symmetric to AddMDIChild() above
310             break;
311 
312         case 2:
313             // only one MDI child is going to remain, disable the menu commands
314             // which don't make sense for a single child window
315             UpdateWindowMenu(false);
316             break;
317     }
318 }
319 
320 // ----------------------------------------------------------------------------
321 // wxMDIParentFrame window menu handling
322 // ----------------------------------------------------------------------------
323 
AddWindowMenu()324 void wxMDIParentFrame::AddWindowMenu()
325 {
326     if ( m_windowMenu )
327     {
328         // For correct handling of the events from this menu we also must
329         // attach it to the menu bar.
330         m_windowMenu->Attach(GetMenuBar());
331 
332         MDIInsertWindowMenu(GetClientWindow(), m_hMenu, GetMDIWindowMenu(this));
333     }
334 }
335 
RemoveWindowMenu()336 void wxMDIParentFrame::RemoveWindowMenu()
337 {
338     if ( m_windowMenu )
339     {
340         MDIRemoveWindowMenu(GetClientWindow(), m_hMenu);
341 
342         m_windowMenu->Detach();
343     }
344 }
345 
UpdateWindowMenu(bool enable)346 void wxMDIParentFrame::UpdateWindowMenu(bool enable)
347 {
348     if ( m_windowMenu )
349     {
350         m_windowMenu->Enable(wxID_MDI_WINDOW_NEXT, enable);
351         m_windowMenu->Enable(wxID_MDI_WINDOW_PREV, enable);
352     }
353 }
354 
355 #if wxUSE_MENUS_NATIVE
356 
InternalSetMenuBar()357 void wxMDIParentFrame::InternalSetMenuBar()
358 {
359     if ( GetActiveChild() )
360     {
361         AddWindowMenu();
362     }
363     else // we don't have any MDI children yet
364     {
365         // wait until we do to add the window menu but do set the main menu for
366         // now (this is done by AddWindowMenu() as a side effect)
367         MDISetMenu(GetClientWindow(), (HMENU)m_hMenu, NULL);
368     }
369 }
370 
371 #endif // wxUSE_MENUS_NATIVE
372 
SetWindowMenu(wxMenu * menu)373 void wxMDIParentFrame::SetWindowMenu(wxMenu* menu)
374 {
375     if ( menu != m_windowMenu )
376     {
377         // We may not be showing the window menu currently if we don't have any
378         // children, and in this case we shouldn't remove/add it back right now.
379         const bool hasWindowMenu = GetActiveChild() != NULL;
380 
381         if ( hasWindowMenu )
382             RemoveWindowMenu();
383 
384         delete m_windowMenu;
385 
386         m_windowMenu = menu;
387 
388         if ( hasWindowMenu )
389             AddWindowMenu();
390     }
391 
392 #if wxUSE_ACCEL
393     wxDELETE(m_accelWindowMenu);
394 
395     if ( menu && menu->HasAccels() )
396         m_accelWindowMenu = menu->CreateAccelTable();
397 #endif // wxUSE_ACCEL
398 }
399 
400 // ----------------------------------------------------------------------------
401 // wxMDIParentFrame other menu-related stuff
402 // ----------------------------------------------------------------------------
403 
DoMenuUpdates(wxMenu * menu)404 void wxMDIParentFrame::DoMenuUpdates(wxMenu* menu)
405 {
406     wxMDIChildFrame *child = GetActiveChild();
407     if ( child )
408     {
409         wxEvtHandler* source = child->GetEventHandler();
410         wxMenuBar* bar = child->GetMenuBar();
411 
412         if (menu)
413         {
414             menu->UpdateUI(source);
415         }
416         else
417         {
418             if ( bar != NULL )
419             {
420                 int nCount = bar->GetMenuCount();
421                 for (int n = 0; n < nCount; n++)
422                     bar->GetMenu(n)->UpdateUI(source);
423             }
424         }
425     }
426     else
427     {
428         wxFrameBase::DoMenuUpdates(menu);
429     }
430 }
431 
FindItemInMenuBar(int menuId) const432 wxMenuItem *wxMDIParentFrame::FindItemInMenuBar(int menuId) const
433 {
434     // We must look in the child menu first: if it has an item with the same ID
435     // as in our own menu bar, the child item should be used to determine
436     // whether it's currently enabled.
437     wxMenuItem *item = GetActiveChild()
438                             ? GetActiveChild()->FindItemInMenuBar(menuId)
439                             : NULL;
440     if ( !item )
441         item = wxFrame::FindItemInMenuBar(menuId);
442 
443     if ( !item && m_windowMenu )
444         item = m_windowMenu->FindItem(menuId);
445 
446     return item;
447 }
448 
MSWGetActiveMenu() const449 WXHMENU wxMDIParentFrame::MSWGetActiveMenu() const
450 {
451     wxMDIChildFrame * const child  = GetActiveChild();
452     if ( child )
453     {
454         const WXHMENU hmenu = child->MSWGetActiveMenu();
455         if ( hmenu )
456             return hmenu;
457     }
458 
459     return wxFrame::MSWGetActiveMenu();
460 }
461 
462 #endif // wxUSE_MENUS
463 
464 // ----------------------------------------------------------------------------
465 // wxMDIParentFrame event handling
466 // ----------------------------------------------------------------------------
467 
UpdateClientSize()468 void wxMDIParentFrame::UpdateClientSize()
469 {
470     if ( GetClientWindow() )
471     {
472         int width, height;
473         GetClientSize(&width, &height);
474 
475         GetClientWindow()->SetSize(0, 0, width, height);
476     }
477 }
478 
OnSize(wxSizeEvent & WXUNUSED (event))479 void wxMDIParentFrame::OnSize(wxSizeEvent& WXUNUSED(event))
480 {
481     UpdateClientSize();
482 
483     // do not call event.Skip() here, it somehow messes up MDI client window
484 }
485 
OnIconized(wxIconizeEvent & event)486 void wxMDIParentFrame::OnIconized(wxIconizeEvent& event)
487 {
488     event.Skip();
489 
490     if ( !event.IsIconized() )
491         UpdateClientSize();
492 }
493 
494 // Responds to colour changes, and passes event on to children.
OnSysColourChanged(wxSysColourChangedEvent & event)495 void wxMDIParentFrame::OnSysColourChanged(wxSysColourChangedEvent& event)
496 {
497     if ( m_clientWindow )
498     {
499         m_clientWindow->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE));
500         m_clientWindow->Refresh();
501     }
502 
503     event.Skip();
504 }
505 
GetDefaultIcon() const506 WXHICON wxMDIParentFrame::GetDefaultIcon() const
507 {
508     // we don't have any standard icons (any more)
509     return (WXHICON)0;
510 }
511 
512 // ---------------------------------------------------------------------------
513 // MDI operations
514 // ---------------------------------------------------------------------------
515 
Cascade()516 void wxMDIParentFrame::Cascade()
517 {
518     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDICASCADE, 0, 0);
519 }
520 
Tile(wxOrientation orient)521 void wxMDIParentFrame::Tile(wxOrientation orient)
522 {
523     wxASSERT_MSG( orient == wxHORIZONTAL || orient == wxVERTICAL,
524                   wxT("invalid orientation value") );
525 
526     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDITILE,
527                   orient == wxHORIZONTAL ? MDITILE_HORIZONTAL
528                                          : MDITILE_VERTICAL, 0);
529 }
530 
ArrangeIcons()531 void wxMDIParentFrame::ArrangeIcons()
532 {
533     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDIICONARRANGE, 0, 0);
534 }
535 
ActivateNext()536 void wxMDIParentFrame::ActivateNext()
537 {
538     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT, 0, 0);
539 }
540 
ActivatePrevious()541 void wxMDIParentFrame::ActivatePrevious()
542 {
543     ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT, 0, 1);
544 }
545 
546 // ---------------------------------------------------------------------------
547 // the MDI parent frame window proc
548 // ---------------------------------------------------------------------------
549 
MSWWindowProc(WXUINT message,WXWPARAM wParam,WXLPARAM lParam)550 WXLRESULT wxMDIParentFrame::MSWWindowProc(WXUINT message,
551                                           WXWPARAM wParam,
552                                           WXLPARAM lParam)
553 {
554     WXLRESULT rc = 0;
555     bool processed = false;
556 
557     switch ( message )
558     {
559         case WM_ACTIVATE:
560             {
561                 WXWORD state, minimized;
562                 WXHWND hwnd;
563                 UnpackActivate(wParam, lParam, &state, &minimized, &hwnd);
564 
565                 processed = HandleActivate(state, minimized != 0, hwnd);
566             }
567             break;
568 
569         case WM_COMMAND:
570             // system messages such as SC_CLOSE are sent as WM_COMMANDs to the
571             // parent MDI frame and we must let the DefFrameProc() have them
572             // for these commands to work (without it, closing the maximized
573             // MDI children doesn't work, for example)
574             {
575                 WXWORD id, cmd;
576                 WXHWND hwnd;
577                 UnpackCommand(wParam, lParam, &id, &hwnd, &cmd);
578 
579                 if ( id == wxID_MDI_MORE_WINDOWS ||
580                      (cmd == 0 /* menu */ &&
581                         id >= SC_SIZE /* first system menu command */) )
582                 {
583                     MSWDefWindowProc(message, wParam, lParam);
584                     processed = true;
585                 }
586             }
587             break;
588 
589         case WM_CREATE:
590             m_clientWindow = OnCreateClient();
591             // Uses own style for client style
592             if ( !m_clientWindow->CreateClient(this, GetWindowStyleFlag()) )
593             {
594                 wxLogMessage(_("Failed to create MDI parent frame."));
595 
596                 rc = -1;
597             }
598 
599             processed = true;
600             break;
601     }
602 
603     if ( !processed )
604         rc = wxFrame::MSWWindowProc(message, wParam, lParam);
605 
606     return rc;
607 }
608 
HandleActivate(int state,bool minimized,WXHWND activate)609 bool wxMDIParentFrame::HandleActivate(int state, bool minimized, WXHWND activate)
610 {
611     bool processed = false;
612 
613     if ( wxWindow::HandleActivate(state, minimized, activate) )
614     {
615         // already processed
616         processed = true;
617     }
618 
619     // If this window is an MDI parent, we must also send an OnActivate message
620     // to the current child.
621     if ( GetActiveChild() &&
622          ((state == WA_ACTIVE) || (state == WA_CLICKACTIVE)) )
623     {
624         wxActivateEvent event(wxEVT_ACTIVATE, true, GetActiveChild()->GetId());
625         event.SetEventObject( GetActiveChild() );
626         if ( GetActiveChild()->HandleWindowEvent(event) )
627             processed = true;
628     }
629 
630     return processed;
631 }
632 
633 #if wxUSE_MENUS
634 
OnMDIChild(wxCommandEvent & event)635 void wxMDIParentFrame::OnMDIChild(wxCommandEvent& event)
636 {
637     wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
638     while ( node )
639     {
640         wxWindow *child = node->GetData();
641         if ( child->GetHWND() )
642         {
643             int childId = wxGetWindowId(child->GetHWND());
644             if ( childId == event.GetId() )
645             {
646                 wxStaticCast(child, wxMDIChildFrame)->Activate();
647                 return;
648             }
649         }
650 
651         node = node->GetNext();
652     }
653 
654     wxFAIL_MSG( "unknown MDI child selected?" );
655 }
656 
OnMDICommand(wxCommandEvent & event)657 void wxMDIParentFrame::OnMDICommand(wxCommandEvent& event)
658 {
659     WXWPARAM wParam = 0;
660     WXLPARAM lParam = 0;
661     int msg;
662     switch ( event.GetId() )
663     {
664         case wxID_MDI_WINDOW_CASCADE:
665             msg = WM_MDICASCADE;
666             wParam = MDITILE_SKIPDISABLED;
667             break;
668 
669         case wxID_MDI_WINDOW_TILE_HORZ:
670             wParam |= MDITILE_HORIZONTAL;
671             // fall through
672 
673         case wxID_MDI_WINDOW_TILE_VERT:
674             if ( !wParam )
675                 wParam = MDITILE_VERTICAL;
676             msg = WM_MDITILE;
677             wParam |= MDITILE_SKIPDISABLED;
678             break;
679 
680         case wxID_MDI_WINDOW_ARRANGE_ICONS:
681             msg = WM_MDIICONARRANGE;
682             break;
683 
684         case wxID_MDI_WINDOW_NEXT:
685             msg = WM_MDINEXT;
686             lParam = 0;         // next child
687             break;
688 
689         case wxID_MDI_WINDOW_PREV:
690             msg = WM_MDINEXT;
691             lParam = 1;         // previous child
692             break;
693 
694         default:
695             wxFAIL_MSG( "unknown MDI command" );
696             return;
697     }
698 
699     ::SendMessage(GetWinHwnd(GetClientWindow()), msg, wParam, lParam);
700 }
701 
702 #endif // wxUSE_MENUS
703 
MSWDefWindowProc(WXUINT message,WXWPARAM wParam,WXLPARAM lParam)704 WXLRESULT wxMDIParentFrame::MSWDefWindowProc(WXUINT message,
705                                         WXWPARAM wParam,
706                                         WXLPARAM lParam)
707 {
708     WXHWND clientWnd;
709     if ( GetClientWindow() )
710         clientWnd = GetClientWindow()->GetHWND();
711     else
712         clientWnd = 0;
713 
714     return DefFrameProc(GetHwnd(), (HWND)clientWnd, message, wParam, lParam);
715 }
716 
MSWTranslateMessage(WXMSG * msg)717 bool wxMDIParentFrame::MSWTranslateMessage(WXMSG* msg)
718 {
719     MSG *pMsg = (MSG *)msg;
720 
721     // first let the current child get it
722     wxMDIChildFrame * const child = GetActiveChild();
723     if ( child && child->MSWTranslateMessage(msg) )
724     {
725         return true;
726     }
727 
728     // then try out accelerator table (will also check the accelerators for the
729     // normal menu items)
730     if ( wxFrame::MSWTranslateMessage(msg) )
731     {
732         return true;
733     }
734 
735 #if wxUSE_MENUS && wxUSE_ACCEL
736     // but it doesn't check for the (custom) accelerators of the window menu
737     // items as it's not part of the menu bar as it's handled by Windows itself
738     // so we need to do this explicitly
739     if ( m_accelWindowMenu && m_accelWindowMenu->Translate(this, msg) )
740         return true;
741 #endif // wxUSE_MENUS && wxUSE_ACCEL
742 
743     // finally, check for MDI specific built-in accelerators
744     if ( pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN )
745     {
746         if ( ::TranslateMDISysAccel(GetWinHwnd(GetClientWindow()), pMsg))
747             return true;
748     }
749 
750     return false;
751 }
752 
753 // ===========================================================================
754 // wxMDIChildFrame
755 // ===========================================================================
756 
Init()757 void wxMDIChildFrame::Init()
758 {
759     m_needsResize = true;
760     m_needsInitialShow = true;
761 }
762 
Create(wxMDIParentFrame * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & size,long style,const wxString & name)763 bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
764                              wxWindowID id,
765                              const wxString& title,
766                              const wxPoint& pos,
767                              const wxSize& size,
768                              long style,
769                              const wxString& name)
770 {
771     m_mdiParent = parent;
772 
773   SetName(name);
774 
775   if ( id != wxID_ANY )
776     m_windowId = id;
777   else
778     m_windowId = NewControlId();
779 
780   if ( parent )
781   {
782       parent->AddChild(this);
783   }
784 
785   int x = pos.x;
786   int y = pos.y;
787   int width = size.x;
788   int height = size.y;
789 
790   MDICREATESTRUCT mcs;
791 
792   wxString className =
793       wxApp::GetRegisteredClassName(wxT("wxMDIChildFrame"), COLOR_WINDOW);
794   if ( !(style & wxFULL_REPAINT_ON_RESIZE) )
795       className += wxApp::GetNoRedrawClassSuffix();
796 
797   mcs.szClass = className.t_str();
798   mcs.szTitle = title.t_str();
799   mcs.hOwner = wxGetInstance();
800   if (x != wxDefaultCoord)
801       mcs.x = x;
802   else
803       mcs.x = CW_USEDEFAULT;
804 
805   if (y != wxDefaultCoord)
806       mcs.y = y;
807   else
808       mcs.y = CW_USEDEFAULT;
809 
810   if (width != wxDefaultCoord)
811       mcs.cx = width;
812   else
813       mcs.cx = CW_USEDEFAULT;
814 
815   if (height != wxDefaultCoord)
816       mcs.cy = height;
817   else
818       mcs.cy = CW_USEDEFAULT;
819 
820   DWORD msflags = WS_OVERLAPPED | WS_CLIPCHILDREN;
821   if (style & wxMINIMIZE_BOX)
822     msflags |= WS_MINIMIZEBOX;
823   if (style & wxMAXIMIZE_BOX)
824     msflags |= WS_MAXIMIZEBOX;
825   if (style & wxRESIZE_BORDER)
826     msflags |= WS_THICKFRAME;
827   if (style & wxSYSTEM_MENU)
828     msflags |= WS_SYSMENU;
829   if ((style & wxMINIMIZE) || (style & wxICONIZE))
830     msflags |= WS_MINIMIZE;
831   if (style & wxMAXIMIZE)
832     msflags |= WS_MAXIMIZE;
833   if (style & wxCAPTION)
834     msflags |= WS_CAPTION;
835 
836   mcs.style = msflags;
837 
838   mcs.lParam = 0;
839 
840   wxWindowCreationHook hook(this);
841 
842   m_hWnd = (WXHWND)::SendMessage(GetWinHwnd(parent->GetClientWindow()),
843                                  WM_MDICREATE, 0, (LPARAM)&mcs);
844 
845   if ( !m_hWnd )
846   {
847       wxLogLastError(wxT("WM_MDICREATE"));
848       return false;
849   }
850 
851   SubclassWin(m_hWnd);
852 
853   parent->AddMDIChild(this);
854 
855   return true;
856 }
857 
~wxMDIChildFrame()858 wxMDIChildFrame::~wxMDIChildFrame()
859 {
860     // if we hadn't been created, there is nothing to destroy
861     if ( !m_hWnd )
862         return;
863 
864     GetMDIParent()->RemoveMDIChild(this);
865 
866     // will be destroyed by DestroyChildren() but reset them before calling it
867     // to avoid using dangling pointers if a callback comes in the meanwhile
868 #if wxUSE_TOOLBAR
869     m_frameToolBar = NULL;
870 #endif
871 #if wxUSE_STATUSBAR
872     m_frameStatusBar = NULL;
873 #endif // wxUSE_STATUSBAR
874 
875     DestroyChildren();
876 
877     MDIRemoveWindowMenu(NULL, m_hMenu);
878 
879     MSWDestroyWindow();
880 }
881 
Show(bool show)882 bool wxMDIChildFrame::Show(bool show)
883 {
884     m_needsInitialShow = false;
885 
886     if (!wxFrame::Show(show))
887         return false;
888 
889     // KH: Without this call, new MDI children do not become active.
890     // This was added here after the same BringWindowToTop call was
891     // removed from wxTopLevelWindow::Show (November 2005)
892     if ( show )
893         ::BringWindowToTop(GetHwnd());
894 
895     // we need to refresh the MDI frame window menu to include (or exclude if
896     // we've been hidden) this frame
897     wxMDIParentFrame * const parent = GetMDIParent();
898     MDISetMenu(parent->GetClientWindow(), NULL, NULL);
899 
900     return true;
901 }
902 
903 void
DoSetSize(int x,int y,int width,int height,int sizeFlags)904 wxMDIChildFrame::DoSetSize(int x, int y, int width, int height, int sizeFlags)
905 {
906     // we need to disable client area origin adjustments used for the child
907     // windows for the frame itself
908     wxMDIChildFrameBase::DoSetSize(x, y, width, height, sizeFlags);
909 }
910 
911 // Set the client size (i.e. leave the calculation of borders etc.
912 // to wxWidgets)
DoSetClientSize(int width,int height)913 void wxMDIChildFrame::DoSetClientSize(int width, int height)
914 {
915   HWND hWnd = GetHwnd();
916 
917   RECT rect;
918   ::GetClientRect(hWnd, &rect);
919 
920   RECT rect2;
921   GetWindowRect(hWnd, &rect2);
922 
923   // Find the difference between the entire window (title bar and all)
924   // and the client area; add this to the new client size to move the
925   // window
926   int actual_width = rect2.right - rect2.left - rect.right + width;
927   int actual_height = rect2.bottom - rect2.top - rect.bottom + height;
928 
929 #if wxUSE_STATUSBAR
930   if (GetStatusBar() && GetStatusBar()->IsShown())
931   {
932     int sx, sy;
933     GetStatusBar()->GetSize(&sx, &sy);
934     actual_height += sy;
935   }
936 #endif // wxUSE_STATUSBAR
937 
938   POINT point;
939   point.x = rect2.left;
940   point.y = rect2.top;
941 
942   // If there's an MDI parent, must subtract the parent's top left corner
943   // since MoveWindow moves relative to the parent
944   wxMDIParentFrame * const mdiParent = GetMDIParent();
945   ::ScreenToClient(GetHwndOf(mdiParent->GetClientWindow()), &point);
946 
947   MoveWindow(hWnd, point.x, point.y, actual_width, actual_height, (BOOL)true);
948 
949   wxSize size(width, height);
950   wxSizeEvent event(size, m_windowId);
951   event.SetEventObject( this );
952   HandleWindowEvent(event);
953 }
954 
955 // Unlike other wxTopLevelWindowBase, the mdi child's "GetPosition" is not the
956 //  same as its GetScreenPosition
DoGetScreenPosition(int * x,int * y) const957 void wxMDIChildFrame::DoGetScreenPosition(int *x, int *y) const
958 {
959   HWND hWnd = GetHwnd();
960 
961   RECT rect;
962   ::GetWindowRect(hWnd, &rect);
963   if (x)
964      *x = rect.left;
965   if (y)
966      *y = rect.top;
967 }
968 
969 
DoGetPosition(int * x,int * y) const970 void wxMDIChildFrame::DoGetPosition(int *x, int *y) const
971 {
972   RECT rect;
973   GetWindowRect(GetHwnd(), &rect);
974   POINT point;
975   point.x = rect.left;
976   point.y = rect.top;
977 
978   // Since we now have the absolute screen coords,
979   // if there's a parent we must subtract its top left corner
980   wxMDIParentFrame * const mdiParent = GetMDIParent();
981   ::ScreenToClient(GetHwndOf(mdiParent->GetClientWindow()), &point);
982 
983   if (x)
984       *x = point.x;
985   if (y)
986       *y = point.y;
987 }
988 
InternalSetMenuBar()989 void wxMDIChildFrame::InternalSetMenuBar()
990 {
991     wxMDIParentFrame * const parent = GetMDIParent();
992 
993     MDIInsertWindowMenu(parent->GetClientWindow(),
994                      m_hMenu, GetMDIWindowMenu(parent));
995 }
996 
DetachMenuBar()997 void wxMDIChildFrame::DetachMenuBar()
998 {
999     MDIRemoveWindowMenu(NULL, m_hMenu);
1000     wxFrame::DetachMenuBar();
1001 }
1002 
GetDefaultIcon() const1003 WXHICON wxMDIChildFrame::GetDefaultIcon() const
1004 {
1005     // we don't have any standard icons (any more)
1006     return (WXHICON)0;
1007 }
1008 
1009 // ---------------------------------------------------------------------------
1010 // MDI operations
1011 // ---------------------------------------------------------------------------
1012 
Maximize(bool maximize)1013 void wxMDIChildFrame::Maximize(bool maximize)
1014 {
1015     wxMDIParentFrame * const parent = GetMDIParent();
1016     if ( parent && parent->GetClientWindow() )
1017     {
1018         ::SendMessage(GetWinHwnd(parent->GetClientWindow()),
1019                       maximize ? WM_MDIMAXIMIZE : WM_MDIRESTORE,
1020                       (WPARAM)GetHwnd(), 0);
1021     }
1022 }
1023 
Restore()1024 void wxMDIChildFrame::Restore()
1025 {
1026     wxMDIParentFrame * const parent = GetMDIParent();
1027     if ( parent && parent->GetClientWindow() )
1028     {
1029         ::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIRESTORE,
1030                       (WPARAM) GetHwnd(), 0);
1031     }
1032 }
1033 
Activate()1034 void wxMDIChildFrame::Activate()
1035 {
1036     wxMDIParentFrame * const parent = GetMDIParent();
1037     if ( parent && parent->GetClientWindow() )
1038     {
1039         // Activating an iconized MDI frame doesn't do anything, so restore it
1040         // first to really present it to the user.
1041         if ( IsIconized() )
1042             Restore();
1043 
1044         ::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIACTIVATE,
1045                       (WPARAM) GetHwnd(), 0);
1046     }
1047 }
1048 
1049 // ---------------------------------------------------------------------------
1050 // MDI window proc and message handlers
1051 // ---------------------------------------------------------------------------
1052 
MSWWindowProc(WXUINT message,WXWPARAM wParam,WXLPARAM lParam)1053 WXLRESULT wxMDIChildFrame::MSWWindowProc(WXUINT message,
1054                                          WXWPARAM wParam,
1055                                          WXLPARAM lParam)
1056 {
1057     WXLRESULT rc = 0;
1058     bool processed = false;
1059 
1060     switch ( message )
1061     {
1062         case WM_GETMINMAXINFO:
1063             processed = HandleGetMinMaxInfo((MINMAXINFO *)lParam);
1064             break;
1065 
1066         case WM_MDIACTIVATE:
1067             {
1068                 WXWORD act;
1069                 WXHWND hwndAct, hwndDeact;
1070                 UnpackMDIActivate(wParam, lParam, &act, &hwndAct, &hwndDeact);
1071 
1072                 processed = HandleMDIActivate(act, hwndAct, hwndDeact);
1073             }
1074             // fall through
1075 
1076         case WM_MOVE:
1077             // must pass WM_MOVE to DefMDIChildProc() to recalculate MDI client
1078             // scrollbars if necessary
1079 
1080             // fall through
1081 
1082         case WM_SIZE:
1083             // must pass WM_SIZE to DefMDIChildProc(), otherwise many weird
1084             // things happen
1085             MSWDefWindowProc(message, wParam, lParam);
1086             break;
1087 
1088         case WM_WINDOWPOSCHANGING:
1089             processed = HandleWindowPosChanging((LPWINDOWPOS)lParam);
1090             break;
1091     }
1092 
1093     if ( !processed )
1094         rc = wxFrame::MSWWindowProc(message, wParam, lParam);
1095 
1096     return rc;
1097 }
1098 
HandleMDIActivate(long WXUNUSED (activate),WXHWND hwndAct,WXHWND hwndDeact)1099 bool wxMDIChildFrame::HandleMDIActivate(long WXUNUSED(activate),
1100                                         WXHWND hwndAct,
1101                                         WXHWND hwndDeact)
1102 {
1103     wxMDIParentFrame * const parent = GetMDIParent();
1104 
1105     WXHMENU hMenuToSet = 0;
1106 
1107     bool activated;
1108 
1109     if ( m_hWnd == hwndAct )
1110     {
1111         activated = true;
1112         parent->SetActiveChild(this);
1113 
1114         WXHMENU hMenuChild = m_hMenu;
1115         if ( hMenuChild )
1116             hMenuToSet = hMenuChild;
1117     }
1118     else if ( m_hWnd == hwndDeact )
1119     {
1120         wxASSERT_MSG( parent->GetActiveChild() == this,
1121                       wxT("can't deactivate MDI child which wasn't active!") );
1122 
1123         activated = false;
1124         parent->SetActiveChild(NULL);
1125 
1126         WXHMENU hMenuParent = parent->m_hMenu;
1127 
1128         // activate the parent menu only when there is no other child
1129         // that has been activated
1130         if ( hMenuParent && !hwndAct )
1131             hMenuToSet = hMenuParent;
1132     }
1133     else
1134     {
1135         // we have nothing to do with it
1136         return false;
1137     }
1138 
1139     if ( hMenuToSet )
1140     {
1141         MDISetMenu(parent->GetClientWindow(),
1142                    (HMENU)hMenuToSet, GetMDIWindowMenu(parent));
1143     }
1144 
1145     wxActivateEvent event(wxEVT_ACTIVATE, activated, m_windowId);
1146     event.SetEventObject( this );
1147 
1148     ResetWindowStyle(NULL);
1149 
1150     return HandleWindowEvent(event);
1151 }
1152 
HandleWindowPosChanging(void * pos)1153 bool wxMDIChildFrame::HandleWindowPosChanging(void *pos)
1154 {
1155     WINDOWPOS *lpPos = (WINDOWPOS *)pos;
1156 
1157     if (!(lpPos->flags & SWP_NOSIZE))
1158     {
1159         RECT rectClient;
1160         DWORD dwExStyle = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE);
1161         DWORD dwStyle = ::GetWindowLong(GetHwnd(), GWL_STYLE);
1162         if (ResetWindowStyle((void *) & rectClient) && (dwStyle & WS_MAXIMIZE))
1163         {
1164             ::AdjustWindowRectEx(&rectClient, dwStyle, false, dwExStyle);
1165             lpPos->x = rectClient.left;
1166             lpPos->y = rectClient.top;
1167             lpPos->cx = rectClient.right - rectClient.left;
1168             lpPos->cy = rectClient.bottom - rectClient.top;
1169         }
1170     }
1171 
1172     return false;
1173 }
1174 
HandleGetMinMaxInfo(void * mmInfo)1175 bool wxMDIChildFrame::HandleGetMinMaxInfo(void *mmInfo)
1176 {
1177     MINMAXINFO *info = (MINMAXINFO *)mmInfo;
1178 
1179     // let the default window proc calculate the size of MDI children
1180     // frames because it is based on the size of the MDI client window,
1181     // not on the values specified in wxWindow m_max variables
1182     bool processed = MSWDefWindowProc(WM_GETMINMAXINFO, 0, (LPARAM)mmInfo) != 0;
1183 
1184     int minWidth = GetMinWidth(),
1185         minHeight = GetMinHeight();
1186 
1187     // but allow GetSizeHints() to set the min size
1188     if ( minWidth != wxDefaultCoord )
1189     {
1190         info->ptMinTrackSize.x = minWidth;
1191 
1192         processed = true;
1193     }
1194 
1195     if ( minHeight != wxDefaultCoord )
1196     {
1197         info->ptMinTrackSize.y = minHeight;
1198 
1199         processed = true;
1200     }
1201 
1202     return processed;
1203 }
1204 
1205 // ---------------------------------------------------------------------------
1206 // MDI specific message translation/preprocessing
1207 // ---------------------------------------------------------------------------
1208 
MSWDefWindowProc(WXUINT message,WXWPARAM wParam,WXLPARAM lParam)1209 WXLRESULT wxMDIChildFrame::MSWDefWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
1210 {
1211     return DefMDIChildProc(GetHwnd(),
1212                            (UINT)message, (WPARAM)wParam, (LPARAM)lParam);
1213 }
1214 
MSWTranslateMessage(WXMSG * msg)1215 bool wxMDIChildFrame::MSWTranslateMessage(WXMSG* msg)
1216 {
1217     // we must pass the parent frame to ::TranslateAccelerator(), otherwise it
1218     // doesn't do its job correctly for MDI child menus
1219     return MSWDoTranslateMessage(GetMDIParent(), msg);
1220 }
1221 
1222 // ---------------------------------------------------------------------------
1223 // misc
1224 // ---------------------------------------------------------------------------
1225 
MSWDestroyWindow()1226 void wxMDIChildFrame::MSWDestroyWindow()
1227 {
1228     wxMDIParentFrame * const parent = GetMDIParent();
1229 
1230     // Must make sure this handle is invalidated (set to NULL) since all sorts
1231     // of things could happen after the child client is destroyed, but before
1232     // the wxFrame is destroyed.
1233 
1234     HWND oldHandle = (HWND)GetHWND();
1235     SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIDESTROY,
1236                 (WPARAM)oldHandle, 0);
1237 
1238     if (parent->GetActiveChild() == NULL)
1239         ResetWindowStyle(NULL);
1240 
1241     if (m_hMenu)
1242     {
1243         ::DestroyMenu((HMENU) m_hMenu);
1244         m_hMenu = 0;
1245     }
1246     wxRemoveHandleAssociation(this);
1247     m_hWnd = 0;
1248 }
1249 
1250 // Change the client window's extended style so we don't get a client edge
1251 // style when a child is maximised (a double border looks silly.)
ResetWindowStyle(void * vrect)1252 bool wxMDIChildFrame::ResetWindowStyle(void *vrect)
1253 {
1254     RECT *rect = (RECT *)vrect;
1255     wxMDIParentFrame * const pFrameWnd = GetMDIParent();
1256     wxMDIChildFrame* pChild = pFrameWnd->GetActiveChild();
1257 
1258     if (!pChild || (pChild == this))
1259     {
1260         HWND hwndClient = GetWinHwnd(pFrameWnd->GetClientWindow());
1261         DWORD dwStyle = ::GetWindowLong(hwndClient, GWL_EXSTYLE);
1262 
1263         // we want to test whether there is a maximized child, so just set
1264         // dwThisStyle to 0 if there is no child at all
1265         DWORD dwThisStyle = pChild
1266             ? ::GetWindowLong(GetWinHwnd(pChild), GWL_STYLE) : 0;
1267         DWORD dwNewStyle = dwStyle;
1268         if ( dwThisStyle & WS_MAXIMIZE )
1269             dwNewStyle &= ~(WS_EX_CLIENTEDGE);
1270         else
1271             dwNewStyle |= WS_EX_CLIENTEDGE;
1272 
1273         if (dwStyle != dwNewStyle)
1274         {
1275             // force update of everything
1276             ::RedrawWindow(hwndClient, NULL, NULL,
1277                            RDW_INVALIDATE | RDW_ALLCHILDREN);
1278             ::SetWindowLong(hwndClient, GWL_EXSTYLE, dwNewStyle);
1279             ::SetWindowPos(hwndClient, NULL, 0, 0, 0, 0,
1280                            SWP_FRAMECHANGED | SWP_NOACTIVATE |
1281                            SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1282                            SWP_NOCOPYBITS);
1283             if (rect)
1284                 ::GetClientRect(hwndClient, rect);
1285 
1286             return true;
1287         }
1288     }
1289 
1290     return false;
1291 }
1292 
1293 // ===========================================================================
1294 // wxMDIClientWindow: the window of predefined (by Windows) class which
1295 // contains the child frames
1296 // ===========================================================================
1297 
CreateClient(wxMDIParentFrame * parent,long style)1298 bool wxMDIClientWindow::CreateClient(wxMDIParentFrame *parent, long style)
1299 {
1300     m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE);
1301 
1302     CLIENTCREATESTRUCT ccs;
1303     m_windowStyle = style;
1304     m_parent = parent;
1305 
1306     ccs.hWindowMenu = GetMDIWindowMenu(parent);
1307     ccs.idFirstChild = wxFIRST_MDI_CHILD;
1308 
1309     DWORD msStyle = MDIS_ALLCHILDSTYLES | WS_VISIBLE | WS_CHILD |
1310                     WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
1311 
1312     if ( style & wxHSCROLL )
1313         msStyle |= WS_HSCROLL;
1314     if ( style & wxVSCROLL )
1315         msStyle |= WS_VSCROLL;
1316 
1317     DWORD exStyle = WS_EX_CLIENTEDGE;
1318 
1319     wxWindowCreationHook hook(this);
1320     m_hWnd = (WXHWND)::CreateWindowEx
1321                        (
1322                         exStyle,
1323                         wxT("MDICLIENT"),
1324                         NULL,
1325                         msStyle,
1326                         0, 0, 0, 0,
1327                         GetWinHwnd(parent),
1328                         NULL,
1329                         wxGetInstance(),
1330                         (LPSTR)(LPCLIENTCREATESTRUCT)&ccs);
1331     if ( !m_hWnd )
1332     {
1333         wxLogLastError(wxT("CreateWindowEx(MDI client)"));
1334 
1335         return false;
1336     }
1337 
1338     SubclassWin(m_hWnd);
1339 
1340     return true;
1341 }
1342 
1343 // Explicitly call default scroll behaviour
OnScroll(wxScrollEvent & event)1344 void wxMDIClientWindow::OnScroll(wxScrollEvent& event)
1345 {
1346     // Note: for client windows, the scroll position is not set in
1347     // WM_HSCROLL, WM_VSCROLL, so we can't easily determine what
1348     // scroll position we're at.
1349     // This makes it hard to paint patterns or bitmaps in the background,
1350     // and have the client area scrollable as well.
1351 
1352     if ( event.GetOrientation() == wxHORIZONTAL )
1353         m_scrollX = event.GetPosition(); // Always returns zero!
1354     else
1355         m_scrollY = event.GetPosition(); // Always returns zero!
1356 
1357     event.Skip();
1358 }
1359 
DoSetSize(int x,int y,int width,int height,int sizeFlags)1360 void wxMDIClientWindow::DoSetSize(int x, int y, int width, int height, int sizeFlags)
1361 {
1362     // Try to fix a problem whereby if you show an MDI child frame, then reposition the
1363     // client area, you can end up with a non-refreshed portion in the client window
1364     // (see OGL studio sample). So check if the position is changed and if so,
1365     // redraw the MDI child frames.
1366 
1367     const wxPoint oldPos = GetPosition();
1368 
1369     wxWindow::DoSetSize(x, y, width, height, sizeFlags | wxSIZE_FORCE);
1370 
1371     const wxPoint newPos = GetPosition();
1372 
1373     if ((newPos.x != oldPos.x) || (newPos.y != oldPos.y))
1374     {
1375         if (GetParent())
1376         {
1377             wxWindowList::compatibility_iterator node = GetParent()->GetChildren().GetFirst();
1378             while (node)
1379             {
1380                 wxWindow *child = node->GetData();
1381                 if (wxDynamicCast(child, wxMDIChildFrame))
1382                 {
1383                    ::RedrawWindow(GetHwndOf(child),
1384                                   NULL,
1385                                   NULL,
1386                                   RDW_FRAME |
1387                                   RDW_ALLCHILDREN |
1388                                   RDW_INVALIDATE);
1389                 }
1390                 node = node->GetNext();
1391             }
1392         }
1393     }
1394 }
1395 
OnIdle(wxIdleEvent & event)1396 void wxMDIChildFrame::OnIdle(wxIdleEvent& event)
1397 {
1398     // wxMSW prior to 2.5.3 created MDI child frames as visible, which resulted
1399     // in flicker e.g. when the frame contained controls with non-trivial
1400     // layout. Since 2.5.3, the frame is created hidden as all other top level
1401     // windows. In order to maintain backward compatibility, the frame is shown
1402     // in OnIdle, unless Show(false) was called by the programmer before.
1403     if ( m_needsInitialShow )
1404     {
1405         Show(true);
1406     }
1407 
1408     // MDI child frames get their WM_SIZE when they're constructed but at this
1409     // moment they don't have any children yet so all child windows will be
1410     // positioned incorrectly when they are added later - to fix this, we
1411     // generate an artificial size event here
1412     if ( m_needsResize )
1413     {
1414         m_needsResize = false; // avoid any possibility of recursion
1415 
1416         SendSizeEvent();
1417     }
1418 
1419     event.Skip();
1420 }
1421 
1422 // ---------------------------------------------------------------------------
1423 // private helper functions
1424 // ---------------------------------------------------------------------------
1425 
1426 namespace
1427 {
1428 
MDISetMenu(wxWindow * win,HMENU hmenuFrame,HMENU hmenuWindow)1429 void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow)
1430 {
1431     if ( hmenuFrame || hmenuWindow )
1432     {
1433         if ( !::SendMessage(GetWinHwnd(win),
1434                             WM_MDISETMENU,
1435                             (WPARAM)hmenuFrame,
1436                             (LPARAM)hmenuWindow) )
1437         {
1438             DWORD err = ::GetLastError();
1439             if ( err )
1440             {
1441                 wxLogApiError(wxT("SendMessage(WM_MDISETMENU)"), err);
1442             }
1443         }
1444     }
1445 
1446     // update menu bar of the parent window
1447     wxWindow *parent = win->GetParent();
1448     wxCHECK_RET( parent, wxT("MDI client without parent frame? weird...") );
1449 
1450     ::SendMessage(GetWinHwnd(win), WM_MDIREFRESHMENU, 0, 0L);
1451 
1452     ::DrawMenuBar(GetWinHwnd(parent));
1453 }
1454 
MDIInsertWindowMenu(wxWindow * win,WXHMENU hMenu,HMENU menuWin)1455 void MDIInsertWindowMenu(wxWindow *win, WXHMENU hMenu, HMENU menuWin)
1456 {
1457     HMENU hmenu = (HMENU)hMenu;
1458 
1459     if ( menuWin )
1460     {
1461         // Try to insert Window menu in front of Help, otherwise append it.
1462         int N = GetMenuItemCount(hmenu);
1463         bool inserted = false;
1464         for ( int i = 0; i < N; i++ )
1465         {
1466             wxChar buf[256];
1467             if ( !::GetMenuString(hmenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION) )
1468             {
1469                 wxLogLastError(wxT("GetMenuString"));
1470 
1471                 continue;
1472             }
1473 
1474             const wxString label = wxStripMenuCodes(buf);
1475             if ( label == wxGetStockLabel(wxID_HELP, wxSTOCK_NOFLAGS) )
1476             {
1477                 inserted = true;
1478                 ::InsertMenu(hmenu, i, MF_BYPOSITION | MF_POPUP | MF_STRING,
1479                              (UINT_PTR)menuWin,
1480                              wxString(wxGetTranslation(WINDOW_MENU_LABEL)).t_str());
1481                 break;
1482             }
1483         }
1484 
1485         if ( !inserted )
1486         {
1487             ::AppendMenu(hmenu, MF_POPUP,
1488                          (UINT_PTR)menuWin,
1489                          wxString(wxGetTranslation(WINDOW_MENU_LABEL)).t_str());
1490         }
1491     }
1492 
1493     MDISetMenu(win, hmenu, menuWin);
1494 }
1495 
MDIRemoveWindowMenu(wxWindow * win,WXHMENU hMenu)1496 void MDIRemoveWindowMenu(wxWindow *win, WXHMENU hMenu)
1497 {
1498     HMENU hmenu = (HMENU)hMenu;
1499 
1500     if ( hmenu )
1501     {
1502         wxChar buf[1024];
1503 
1504         int N = ::GetMenuItemCount(hmenu);
1505         for ( int i = 0; i < N; i++ )
1506         {
1507             if ( !::GetMenuString(hmenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION) )
1508             {
1509                 // Ignore successful read of menu string with length 0 which
1510                 // occurs, for example, for a maximized MDI child system menu
1511                 if ( ::GetLastError() != 0 )
1512                 {
1513                     wxLogLastError(wxT("GetMenuString"));
1514                 }
1515 
1516                 continue;
1517             }
1518 
1519             if ( wxStrcmp(buf, wxGetTranslation(WINDOW_MENU_LABEL)) == 0 )
1520             {
1521                 if ( !::RemoveMenu(hmenu, i, MF_BYPOSITION) )
1522                 {
1523                     wxLogLastError(wxT("RemoveMenu"));
1524                 }
1525 
1526                 break;
1527             }
1528         }
1529     }
1530 
1531     if ( win )
1532     {
1533         // we don't change the windows menu, but we update the main one
1534         MDISetMenu(win, hmenu, NULL);
1535     }
1536 }
1537 
UnpackMDIActivate(WXWPARAM wParam,WXLPARAM lParam,WXWORD * activate,WXHWND * hwndAct,WXHWND * hwndDeact)1538 void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
1539                               WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact)
1540 {
1541     *activate = true;
1542     *hwndAct = (WXHWND)lParam;
1543     *hwndDeact = (WXHWND)wParam;
1544 }
1545 
1546 } // anonymous namespace
1547 
1548 #endif // wxUSE_MDI && !defined(__WXUNIVERSAL__)
1549