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