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