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