]> git.saurik.com Git - wxWidgets.git/blob - src/msw/mdi.cpp
f91d0a6cb364de385d96a88fa4395e3721650929
[wxWidgets.git] / src / msw / mdi.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: mdi.cpp
3 // Purpose: MDI classes
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ===========================================================================
13 // declarations
14 // ===========================================================================
15
16 // ---------------------------------------------------------------------------
17 // headers
18 // ---------------------------------------------------------------------------
19
20 #ifdef __GNUG__
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 #ifndef WX_PRECOMP
32 #include "wx/setup.h"
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 #if wxUSE_STATUSBAR
39 #include "wx/statusbr.h"
40 #endif
41 #include "wx/settings.h"
42 #include "wx/intl.h"
43 #include "wx/log.h"
44 #endif
45
46 #include "wx/mdi.h"
47 #include "wx/msw/private.h"
48
49 #if wxUSE_STATUSBAR && wxUSE_NATIVE_STATUSBAR
50 #include "wx/msw/statbr95.h"
51 #endif
52
53 #if wxUSE_TOOLBAR
54 #include "wx/toolbar.h"
55 #endif // wxUSE_TOOLBAR
56
57 #include <string.h>
58
59 // ---------------------------------------------------------------------------
60 // global variables
61 // ---------------------------------------------------------------------------
62
63 extern wxWindowList wxModelessWindows; // from dialog.cpp
64 extern wxMenu *wxCurrentPopupMenu;
65
66 extern wxChar wxMDIFrameClassName[];
67 extern wxChar wxMDIChildFrameClassName[];
68 extern wxWindow *wxWndHook; // from window.cpp
69
70 extern void wxAssociateWinWithHandle(HWND hWnd, wxWindow *win);
71
72 static HWND invalidHandle = 0;
73
74 // ---------------------------------------------------------------------------
75 // constants
76 // ---------------------------------------------------------------------------
77
78 static const int IDM_WINDOWTILE = 4001;
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
85 // This range gives a maximum of 500 MDI children. Should be enough :-)
86 static const int wxFIRST_MDI_CHILD = 4100;
87 static const int wxLAST_MDI_CHILD = 4600;
88
89 // Status border dimensions
90 static const int wxTHICK_LINE_BORDER = 3;
91 static const int wxTHICK_LINE_WIDTH = 1;
92
93 // ---------------------------------------------------------------------------
94 // private functions
95 // ---------------------------------------------------------------------------
96
97 // set the MDI menus (by sending the WM_MDISETMENU message) and update the menu
98 // of the parent of win (which is supposed to be the MDI client window)
99 static void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow);
100
101 // insert the window menu (subMenu) into menu just before "Help" submenu or at
102 // the very end if not found
103 static void InsertWindowMenu(wxWindow *win, WXHMENU menu, HMENU subMenu);
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 static void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
112 WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact);
113
114 // ===========================================================================
115 // implementation
116 // ===========================================================================
117
118 // ---------------------------------------------------------------------------
119 // wxWin macros
120 // ---------------------------------------------------------------------------
121
122 #if !USE_SHARED_LIBRARY
123 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame, wxFrame)
124 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame, wxFrame)
125 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow, wxWindow)
126 #endif // USE_SHARED_LIBRARY
127
128 BEGIN_EVENT_TABLE(wxMDIParentFrame, wxFrame)
129 EVT_SIZE(wxMDIParentFrame::OnSize)
130 EVT_SYS_COLOUR_CHANGED(wxMDIParentFrame::OnSysColourChanged)
131 END_EVENT_TABLE()
132
133 BEGIN_EVENT_TABLE(wxMDIClientWindow, wxWindow)
134 EVT_SCROLL(wxMDIClientWindow::OnScroll)
135 END_EVENT_TABLE()
136
137 // ===========================================================================
138 // wxMDIParentFrame: the frame which contains the client window which manages
139 // the children
140 // ===========================================================================
141
142 wxMDIParentFrame::wxMDIParentFrame()
143 {
144 m_clientWindow = NULL;
145 m_currentChild = NULL;
146 m_windowMenu = 0;
147 m_parentFrameActive = TRUE;
148 }
149
150 bool wxMDIParentFrame::Create(wxWindow *parent,
151 wxWindowID id,
152 const wxString& title,
153 const wxPoint& pos,
154 const wxSize& size,
155 long style,
156 const wxString& name)
157 {
158 m_defaultIcon = (WXHICON) (wxSTD_MDIPARENTFRAME_ICON ? wxSTD_MDIPARENTFRAME_ICON : wxDEFAULT_MDIPARENTFRAME_ICON);
159
160 m_clientWindow = NULL;
161 m_currentChild = NULL;
162 m_windowMenu = 0;
163 m_parentFrameActive = TRUE;
164
165 if (!parent)
166 wxTopLevelWindows.Append(this);
167
168 SetName(name);
169 m_windowStyle = style;
170
171 if (parent) parent->AddChild(this);
172
173 if ( id > -1 )
174 m_windowId = id;
175 else
176 m_windowId = (int)NewControlId();
177
178 int x = pos.x;
179 int y = pos.y;
180 int width = size.x;
181 int height = size.y;
182
183 m_windowMenu = (WXHMENU) ::LoadMenu(wxGetInstance(), wxT("wxWindowMenu"));
184
185 DWORD msflags = WS_OVERLAPPED;
186 if (style & wxMINIMIZE_BOX)
187 msflags |= WS_MINIMIZEBOX;
188 if (style & wxMAXIMIZE_BOX)
189 msflags |= WS_MAXIMIZEBOX;
190 if (style & wxTHICK_FRAME)
191 msflags |= WS_THICKFRAME;
192 if (style & wxSYSTEM_MENU)
193 msflags |= WS_SYSMENU;
194 if ((style & wxMINIMIZE) || (style & wxICONIZE))
195 msflags |= WS_MINIMIZE;
196 if (style & wxMAXIMIZE)
197 msflags |= WS_MAXIMIZE;
198 if (style & wxCAPTION)
199 msflags |= WS_CAPTION;
200
201 if (style & wxCLIP_CHILDREN)
202 msflags |= WS_CLIPCHILDREN;
203
204 wxWindow::MSWCreate(m_windowId, parent, wxMDIFrameClassName, this, title, x, y, width, height,
205 msflags);
206
207 wxModelessWindows.Append(this);
208
209 return TRUE;
210 }
211
212 wxMDIParentFrame::~wxMDIParentFrame()
213 {
214 DestroyChildren();
215
216 // already delete by DestroyChildren()
217 m_frameToolBar = NULL;
218
219 ::DestroyMenu((HMENU)m_windowMenu);
220 m_windowMenu = 0;
221
222 if ( m_clientWindow )
223 {
224 if ( m_clientWindow->MSWGetOldWndProc() )
225 m_clientWindow->UnsubclassWin();
226
227 m_clientWindow->SetHWND(0);
228 delete m_clientWindow;
229 }
230 }
231
232 void wxMDIParentFrame::InternalSetMenuBar()
233 {
234 HMENU subMenu = GetSubMenu((HMENU) m_windowMenu, 0);
235
236 m_parentFrameActive = TRUE;
237
238 InsertWindowMenu(GetClientWindow(), m_hMenu, subMenu);
239 }
240
241 void wxMDIParentFrame::OnSize(wxSizeEvent& event)
242 {
243 if ( GetClientWindow() )
244 {
245 int width, height;
246 GetClientSize(&width, &height);
247
248 GetClientWindow()->SetSize(0, 0, width, height);
249 }
250 }
251
252 // Returns the active MDI child window
253 wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
254 {
255 HWND hWnd = (HWND)::SendMessage(GetWinHwnd(GetClientWindow()),
256 WM_MDIGETACTIVE, 0, 0L);
257 if ( hWnd == 0 )
258 return NULL;
259 else
260 return (wxMDIChildFrame *)wxFindWinFromHandle((WXHWND) hWnd);
261 }
262
263 // Create the client window class (don't Create the window, just return a new
264 // class)
265 wxMDIClientWindow *wxMDIParentFrame::OnCreateClient()
266 {
267 return new wxMDIClientWindow;
268 }
269
270 // Responds to colour changes, and passes event on to children.
271 void wxMDIParentFrame::OnSysColourChanged(wxSysColourChangedEvent& event)
272 {
273 if ( m_clientWindow )
274 {
275 m_clientWindow->SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_APPWORKSPACE));
276 m_clientWindow->Refresh();
277 }
278
279 event.Skip();
280 }
281
282 // ---------------------------------------------------------------------------
283 // MDI operations
284 // ---------------------------------------------------------------------------
285
286 void wxMDIParentFrame::Cascade()
287 {
288 ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDICASCADE, 0, 0);
289 }
290
291 // TODO: add a direction argument (hor/vert)
292 void wxMDIParentFrame::Tile()
293 {
294 ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDITILE, MDITILE_HORIZONTAL, 0);
295 }
296
297 void wxMDIParentFrame::ArrangeIcons()
298 {
299 ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDIICONARRANGE, 0, 0);
300 }
301
302 void wxMDIParentFrame::ActivateNext()
303 {
304 ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT, 0, 0);
305 }
306
307 void wxMDIParentFrame::ActivatePrevious()
308 {
309 ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT, 0, 1);
310 }
311
312 // ---------------------------------------------------------------------------
313 // the MDI parent frame window proc
314 // ---------------------------------------------------------------------------
315
316 long wxMDIParentFrame::MSWWindowProc(WXUINT message,
317 WXWPARAM wParam,
318 WXLPARAM lParam)
319 {
320 long rc = 0;
321 bool processed = FALSE;
322
323 switch ( message )
324 {
325 case WM_ACTIVATE:
326 {
327 WXWORD state, minimized;
328 WXHWND hwnd;
329 UnpackActivate(wParam, lParam, &state, &minimized, &hwnd);
330
331 processed = HandleActivate(state, minimized != 0, hwnd);
332 }
333 break;
334
335 case WM_COMMAND:
336 {
337 WXWORD id, cmd;
338 WXHWND hwnd;
339 UnpackCommand(wParam, lParam, &id, &hwnd, &cmd);
340
341 (void)HandleCommand(id, cmd, hwnd);
342
343 // even if the frame didn't process it, there is no need to try it
344 // once again (i.e. call wxFrame::HandleCommand()) - we just dud it,
345 // so pretend we processed the message anyhow
346 processed = TRUE;
347 }
348
349 // always pass this message DefFrameProc(), otherwise MDI menu
350 // commands (and sys commands - more surprizingly!) won't work
351 MSWDefWindowProc(message, wParam, lParam);
352 break;
353
354 case WM_CREATE:
355 m_clientWindow = OnCreateClient();
356 // Uses own style for client style
357 if ( !m_clientWindow->CreateClient(this, GetWindowStyleFlag()) )
358 {
359 wxLogMessage(_("Failed to create MDI parent frame."));
360
361 rc = -1;
362 }
363
364 processed = TRUE;
365 break;
366
367 case WM_ERASEBKGND:
368 processed = TRUE;
369
370 // we erase background ourselves
371 rc = TRUE;
372 break;
373
374 case WM_MENUSELECT:
375 {
376 WXWORD item, flags;
377 WXHMENU hmenu;
378 UnpackMenuSelect(wParam, lParam, &item, &flags, &hmenu);
379
380 if ( m_parentFrameActive )
381 {
382 processed = HandleMenuSelect(item, flags, hmenu);
383 }
384 else if (m_currentChild)
385 {
386 processed = m_currentChild->
387 HandleMenuSelect(item, flags, hmenu);
388 }
389 }
390 break;
391
392 case WM_SIZE:
393 // as we don't (usually) resize the MDI client to exactly fit the
394 // client area (we put it below the toolbar, above statusbar &c),
395 // we should not pass this one to DefFrameProc
396 break;
397 }
398
399 if ( !processed )
400 rc = wxFrame::MSWWindowProc(message, wParam, lParam);
401
402 return rc;
403 }
404
405 bool wxMDIParentFrame::HandleActivate(int state, bool minimized, WXHWND activate)
406 {
407 bool processed = FALSE;
408
409 if ( wxWindow::HandleActivate(state, minimized, activate) )
410 {
411 // already processed
412 processed = TRUE;
413 }
414
415 // If this window is an MDI parent, we must also send an OnActivate message
416 // to the current child.
417 if ( (m_currentChild != NULL) &&
418 ((state == WA_ACTIVE) || (state == WA_CLICKACTIVE)) )
419 {
420 wxActivateEvent event(wxEVT_ACTIVATE, TRUE, m_currentChild->GetId());
421 event.SetEventObject( m_currentChild );
422 if ( m_currentChild->GetEventHandler()->ProcessEvent(event) )
423 processed = TRUE;
424 }
425
426 return processed;
427 }
428
429 bool wxMDIParentFrame::HandleCommand(WXWORD id, WXWORD cmd, WXHWND hwnd)
430 {
431 // In case it's e.g. a toolbar.
432 if ( hwnd )
433 {
434 wxWindow *win = wxFindWinFromHandle(hwnd);
435 if ( win )
436 return win->MSWCommand(cmd, id);
437 }
438
439 // is it one of standard MDI commands?
440 WXWPARAM wParam = 0;
441 int msg;
442 switch ( id )
443 {
444 case IDM_WINDOWCASCADE:
445 msg = WM_MDICASCADE;
446 wParam = MDITILE_SKIPDISABLED;
447 break;
448
449 case IDM_WINDOWTILEHOR:
450 wParam |= MDITILE_HORIZONTAL;
451 // fall through
452
453 case IDM_WINDOWTILEVERT:
454 if ( !wParam )
455 wParam = MDITILE_VERTICAL;
456 msg = WM_MDITILE;
457 wParam |= MDITILE_SKIPDISABLED;
458 break;
459
460 case IDM_WINDOWICONS:
461 msg = WM_MDIICONARRANGE;
462 break;
463
464 case IDM_WINDOWNEXT:
465 msg = WM_MDINEXT;
466 break;
467
468 default:
469 msg = 0;
470 }
471
472 if ( msg )
473 {
474 ::SendMessage(GetWinHwnd(GetClientWindow()), msg, wParam, 0);
475
476 return TRUE;
477 }
478
479 // FIXME VZ: what does this test do??
480 if (id >= 0xF000)
481 {
482 return FALSE; // Get WndProc to call default proc
483 }
484
485 if ( IsMdiCommandId(id) )
486 {
487 wxWindowList::Node* node = GetChildren().GetFirst();
488 while ( node )
489 {
490 wxWindow* child = node->GetData();
491 if ( child->GetHWND() )
492 {
493 long childId = wxGetWindowId(child->GetHWND());
494 if (childId == (long)id)
495 {
496 ::SendMessage( GetWinHwnd(GetClientWindow()),
497 WM_MDIACTIVATE,
498 (WPARAM)child->GetHWND(), 0);
499 return TRUE;
500 }
501 }
502 node = node->GetNext();
503 }
504 }
505 else if ( m_parentFrameActive )
506 {
507 return ProcessCommand(id);
508 }
509 else if ( m_currentChild )
510 {
511 return m_currentChild->HandleCommand(id, cmd, hwnd);
512 }
513 else
514 {
515 // this shouldn't happen because it means that our messages are being
516 // lost (they're not sent to the parent frame nor to the children)
517 wxFAIL_MSG(wxT("MDI parent frame is not active, "
518 "yet there is no active MDI child?"));
519 }
520
521 return FALSE;
522 }
523
524 long wxMDIParentFrame::MSWDefWindowProc(WXUINT message,
525 WXWPARAM wParam,
526 WXLPARAM lParam)
527 {
528 WXHWND clientWnd;
529 if ( GetClientWindow() )
530 clientWnd = GetClientWindow()->GetHWND();
531 else
532 clientWnd = 0;
533
534 return DefFrameProc(GetHwnd(), (HWND)clientWnd, message, wParam, lParam);
535 }
536
537 bool wxMDIParentFrame::MSWTranslateMessage(WXMSG* msg)
538 {
539 MSG *pMsg = (MSG *)msg;
540
541 if ( m_currentChild && m_currentChild->GetHWND() &&
542 m_currentChild->MSWTranslateMessage(msg) )
543 {
544 return TRUE;
545 }
546
547 if ( m_acceleratorTable.Translate(this, msg) )
548 {
549 return TRUE;
550 }
551
552 if ( pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN )
553 {
554 if ( ::TranslateMDISysAccel(GetWinHwnd(GetClientWindow()), pMsg))
555 return TRUE;
556 }
557
558 return FALSE;
559 }
560
561 // ===========================================================================
562 // wxMDIChildFrame
563 // ===========================================================================
564
565 wxMDIChildFrame::wxMDIChildFrame()
566 {
567 }
568
569 bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
570 wxWindowID id,
571 const wxString& title,
572 const wxPoint& pos,
573 const wxSize& size,
574 long style,
575 const wxString& name)
576 {
577 m_defaultIcon = (WXHICON)(wxSTD_MDICHILDFRAME_ICON ? wxSTD_MDICHILDFRAME_ICON
578 : wxDEFAULT_MDICHILDFRAME_ICON);
579
580 SetName(name);
581
582 if ( id > -1 )
583 m_windowId = id;
584 else
585 m_windowId = (int)NewControlId();
586
587 if ( parent )
588 {
589 parent->AddChild(this);
590 }
591
592 wxWndHook = this;
593
594 int x = pos.x;
595 int y = pos.y;
596 int width = size.x;
597 int height = size.y;
598
599 MDICREATESTRUCT mcs;
600
601 mcs.szClass = wxMDIChildFrameClassName;
602 mcs.szTitle = title;
603 mcs.hOwner = wxGetInstance();
604 if (x > -1)
605 mcs.x = x;
606 else
607 mcs.x = CW_USEDEFAULT;
608
609 if (y > -1)
610 mcs.y = y;
611 else
612 mcs.y = CW_USEDEFAULT;
613
614 if (width > -1)
615 mcs.cx = width;
616 else
617 mcs.cx = CW_USEDEFAULT;
618
619 if (height > -1)
620 mcs.cy = height;
621 else
622 mcs.cy = CW_USEDEFAULT;
623
624 DWORD msflags = WS_OVERLAPPED | WS_CLIPCHILDREN;
625 if (style & wxMINIMIZE_BOX)
626 msflags |= WS_MINIMIZEBOX;
627 if (style & wxMAXIMIZE_BOX)
628 msflags |= WS_MAXIMIZEBOX;
629 if (style & wxTHICK_FRAME)
630 msflags |= WS_THICKFRAME;
631 if (style & wxSYSTEM_MENU)
632 msflags |= WS_SYSMENU;
633 if ((style & wxMINIMIZE) || (style & wxICONIZE))
634 msflags |= WS_MINIMIZE;
635 if (style & wxMAXIMIZE)
636 msflags |= WS_MAXIMIZE;
637 if (style & wxCAPTION)
638 msflags |= WS_CAPTION;
639
640 mcs.style = msflags;
641
642 mcs.lParam = 0;
643
644 DWORD Return = SendMessage(GetWinHwnd(parent->GetClientWindow()),
645 WM_MDICREATE, 0, (LONG)(LPSTR)&mcs);
646
647 //handle = (HWND)LOWORD(Return);
648 // Must be the DWORRD for WIN32. And in 16 bits, HIWORD=0 (says Microsoft)
649 m_hWnd = (WXHWND)Return;
650
651 wxWndHook = NULL;
652 wxAssociateWinWithHandle((HWND) GetHWND(), this);
653
654 // VZ: what's this? an act of piracy?
655 //SetWindowLong(GetHwnd(), 0, (long)this);
656
657 wxModelessWindows.Append(this);
658 return TRUE;
659 }
660
661 wxMDIChildFrame::~wxMDIChildFrame()
662 {
663 MSWDestroyWindow();
664 }
665
666 // Set the client size (i.e. leave the calculation of borders etc.
667 // to wxWindows)
668 void wxMDIChildFrame::DoSetClientSize(int width, int height)
669 {
670 HWND hWnd = GetHwnd();
671
672 RECT rect;
673 ::GetClientRect(hWnd, &rect);
674
675 RECT rect2;
676 GetWindowRect(hWnd, &rect2);
677
678 // Find the difference between the entire window (title bar and all)
679 // and the client area; add this to the new client size to move the
680 // window
681 int actual_width = rect2.right - rect2.left - rect.right + width;
682 int actual_height = rect2.bottom - rect2.top - rect.bottom + height;
683
684 if (GetStatusBar())
685 {
686 int sx, sy;
687 GetStatusBar()->GetSize(&sx, &sy);
688 actual_height += sy;
689 }
690
691 POINT point;
692 point.x = rect2.left;
693 point.y = rect2.top;
694
695 // If there's an MDI parent, must subtract the parent's top left corner
696 // since MoveWindow moves relative to the parent
697 wxMDIParentFrame *mdiParent = (wxMDIParentFrame *)GetParent();
698 ::ScreenToClient((HWND) mdiParent->GetClientWindow()->GetHWND(), &point);
699
700 MoveWindow(hWnd, point.x, point.y, actual_width, actual_height, (BOOL)TRUE);
701
702 wxSizeEvent event(wxSize(width, height), m_windowId);
703 event.SetEventObject( this );
704 GetEventHandler()->ProcessEvent(event);
705 }
706
707 void wxMDIChildFrame::DoGetPosition(int *x, int *y) const
708 {
709 RECT rect;
710 GetWindowRect(GetHwnd(), &rect);
711 POINT point;
712 point.x = rect.left;
713 point.y = rect.top;
714
715 // Since we now have the absolute screen coords,
716 // if there's a parent we must subtract its top left corner
717 wxMDIParentFrame *mdiParent = (wxMDIParentFrame *)GetParent();
718 ::ScreenToClient((HWND) mdiParent->GetClientWindow()->GetHWND(), &point);
719
720 *x = point.x;
721 *y = point.y;
722 }
723
724 void wxMDIChildFrame::InternalSetMenuBar()
725 {
726 wxMDIParentFrame *parent = (wxMDIParentFrame *)GetParent();
727
728 HMENU subMenu = GetSubMenu((HMENU)parent->GetWindowMenu(), 0);
729
730 InsertWindowMenu(parent->GetClientWindow(), m_hMenu, subMenu);
731
732 parent->m_parentFrameActive = FALSE;
733 }
734
735 // ---------------------------------------------------------------------------
736 // MDI operations
737 // ---------------------------------------------------------------------------
738
739 void wxMDIChildFrame::Maximize(bool maximize)
740 {
741 wxMDIParentFrame *parent = (wxMDIParentFrame *)GetParent();
742 if ( parent && parent->GetClientWindow() )
743 {
744 ::SendMessage(GetWinHwnd(parent->GetClientWindow()),
745 maximize ? WM_MDIMAXIMIZE : WM_MDIRESTORE,
746 (WPARAM)GetHwnd(), 0);
747 }
748 }
749
750 void wxMDIChildFrame::Restore()
751 {
752 wxMDIParentFrame *parent = (wxMDIParentFrame *)GetParent();
753 if ( parent && parent->GetClientWindow() )
754 {
755 ::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIRESTORE,
756 (WPARAM) GetHwnd(), 0);
757 }
758 }
759
760 void wxMDIChildFrame::Activate()
761 {
762 wxMDIParentFrame *parent = (wxMDIParentFrame *)GetParent();
763 if ( parent && parent->GetClientWindow() )
764 {
765 ::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIACTIVATE,
766 (WPARAM) GetHwnd(), 0);
767 }
768 }
769
770 // ---------------------------------------------------------------------------
771 // MDI window proc and message handlers
772 // ---------------------------------------------------------------------------
773
774 long wxMDIChildFrame::MSWWindowProc(WXUINT message,
775 WXWPARAM wParam,
776 WXLPARAM lParam)
777 {
778 long rc = 0;
779 bool processed = FALSE;
780
781 switch ( message )
782 {
783 case WM_COMMAND:
784 {
785 WORD id, cmd;
786 WXHWND hwnd;
787 UnpackCommand((WXWPARAM)wParam, (WXLPARAM)lParam,
788 &id, &hwnd, &cmd);
789
790 processed = HandleCommand(id, cmd, (WXHWND)hwnd);
791 }
792 break;
793
794 case WM_GETMINMAXINFO:
795 // let the default window proc calculate the size of MDI children
796 // frames because it is based on the size of the MDI client window,
797 // not on the values specified in wxWindow m_min/max variables
798 return MSWDefWindowProc(message, wParam, lParam);
799
800 case WM_MDIACTIVATE:
801 {
802 WXWORD act;
803 WXHWND hwndAct, hwndDeact;
804 UnpackMDIActivate(wParam, lParam, &act, &hwndAct, &hwndDeact);
805
806 processed = HandleMDIActivate(act, hwndAct, hwndDeact);
807 }
808 // fall through
809
810 case WM_MOVE:
811 // must pass WM_MOVE to DefMDIChildProc() to recalculate MDI client
812 // scrollbars if necessary
813
814 // fall through
815
816 case WM_SIZE:
817 // must pass WM_SIZE to DefMDIChildProc(), otherwise many weird
818 // things happen
819 MSWDefWindowProc(message, wParam, lParam);
820 break;
821
822 case WM_SYSCOMMAND:
823 // DefMDIChildProc handles SC_{NEXT/PREV}WINDOW here, so pass it
824 // the message (the base class version does not)
825 return MSWDefWindowProc(message, wParam, lParam);
826
827 case WM_WINDOWPOSCHANGING:
828 processed = HandleWindowPosChanging((LPWINDOWPOS)lParam);
829 break;
830 }
831
832 if ( !processed )
833 rc = wxFrame::MSWWindowProc(message, wParam, lParam);
834
835 return rc;
836 }
837
838 bool wxMDIChildFrame::HandleSize(int x, int y, WXUINT id)
839 {
840 HWND hwnd = GetHwnd();
841
842 if ( !hwnd || hwnd == invalidHandle )
843 {
844 return FALSE;
845 }
846
847 switch (id)
848 {
849 case SIZEFULLSCREEN:
850 case SIZENORMAL:
851 m_iconized = FALSE;
852 break;
853
854 case SIZEICONIC:
855 m_iconized = TRUE;
856 break;
857 }
858
859 if ( !m_iconized )
860 {
861 // forward WM_SIZE to status bar control
862 #if wxUSE_NATIVE_STATUSBAR
863 if (m_frameStatusBar && m_frameStatusBar->IsKindOf(CLASSINFO(wxStatusBar95)))
864 {
865 wxSizeEvent event(wxSize(x, y), m_frameStatusBar->GetId());
866 event.SetEventObject( m_frameStatusBar );
867
868 ((wxStatusBar95 *)m_frameStatusBar)->OnSize(event);
869 }
870 #endif // wxUSE_NATIVE_STATUSBAR
871
872 PositionStatusBar();
873 PositionToolBar();
874
875 return wxWindow::HandleSize(x, y, id);
876 }
877 else
878 {
879 return FALSE;
880 }
881 }
882
883 bool wxMDIChildFrame::HandleCommand(WXWORD id, WXWORD cmd, WXHWND hwnd)
884 {
885 // In case it's e.g. a toolbar.
886 if ( hwnd )
887 {
888 wxWindow *win = wxFindWinFromHandle(hwnd);
889 if (win)
890 return win->MSWCommand(cmd, id);
891 }
892
893 if (wxCurrentPopupMenu)
894 {
895 wxMenu *popupMenu = wxCurrentPopupMenu;
896 wxCurrentPopupMenu = NULL;
897 if (popupMenu->MSWCommand(cmd, id))
898 return TRUE;
899 }
900
901 if (GetMenuBar() && GetMenuBar()->FindItem(id))
902 {
903 ProcessCommand(id);
904 return TRUE;
905 }
906 else
907 return FALSE;
908
909 return TRUE;
910 }
911
912 bool wxMDIChildFrame::HandleMDIActivate(long WXUNUSED(activate),
913 WXHWND hwndAct,
914 WXHWND hwndDeact)
915 {
916 wxMDIParentFrame *parent = (wxMDIParentFrame *)GetParent();
917
918 HMENU menuToSet = 0;
919
920 bool activated;
921
922 if ( m_hWnd == hwndAct )
923 {
924 activated = TRUE;
925 parent->m_currentChild = this;
926
927 HMENU child_menu = (HMENU)GetWinMenu();
928 if ( child_menu )
929 {
930 parent->m_parentFrameActive = FALSE;
931
932 menuToSet = child_menu;
933 }
934 }
935 else if ( m_hWnd == hwndDeact )
936 {
937 wxASSERT_MSG( parent->m_currentChild == this,
938 wxT("can't deactivate MDI child which wasn't active!") );
939
940 activated = FALSE;
941 parent->m_currentChild = NULL;
942
943 HMENU parent_menu = (HMENU)parent->GetWinMenu();
944 if ( parent_menu )
945 {
946 parent->m_parentFrameActive = TRUE;
947
948 menuToSet = parent_menu;
949 }
950 }
951 else
952 {
953 // we have nothing to with it
954 return FALSE;
955 }
956
957 if ( menuToSet )
958 {
959 HMENU subMenu = GetSubMenu((HMENU) parent->GetWindowMenu(), 0);
960
961 MDISetMenu(parent->GetClientWindow(), menuToSet, subMenu);
962 }
963
964 wxActivateEvent event(wxEVT_ACTIVATE, activated, m_windowId);
965 event.SetEventObject( this );
966
967 return GetEventHandler()->ProcessEvent(event);
968 }
969
970 bool wxMDIChildFrame::HandleWindowPosChanging(void *pos)
971 {
972 WINDOWPOS *lpPos = (WINDOWPOS *)pos;
973 #if defined(__WIN95__)
974 if (!(lpPos->flags & SWP_NOSIZE))
975 {
976 RECT rectClient;
977 DWORD dwExStyle = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE);
978 DWORD dwStyle = ::GetWindowLong(GetHwnd(), GWL_STYLE);
979 if (ResetWindowStyle((void *) & rectClient) && (dwStyle & WS_MAXIMIZE))
980 {
981 ::AdjustWindowRectEx(&rectClient, dwStyle, FALSE, dwExStyle);
982 lpPos->x = rectClient.left;
983 lpPos->y = rectClient.top;
984 lpPos->cx = rectClient.right - rectClient.left;
985 lpPos->cy = rectClient.bottom - rectClient.top;
986 }
987 wxMDIParentFrame* pFrameWnd = (wxMDIParentFrame *)GetParent();
988 if (pFrameWnd && pFrameWnd->GetToolBar())
989 {
990 pFrameWnd->GetToolBar()->Refresh();
991 }
992 }
993 #endif // Win95
994
995 return FALSE;
996 }
997
998 // ---------------------------------------------------------------------------
999 // MDI specific message translation/preprocessing
1000 // ---------------------------------------------------------------------------
1001
1002 long wxMDIChildFrame::MSWDefWindowProc(WXUINT message, WXUINT wParam, WXLPARAM lParam)
1003 {
1004 return DefMDIChildProc(GetHwnd(),
1005 (UINT)message, (WPARAM)wParam, (LPARAM)lParam);
1006 }
1007
1008 bool wxMDIChildFrame::MSWTranslateMessage(WXMSG* msg)
1009 {
1010 return m_acceleratorTable.Translate(GetParent(), msg);
1011 }
1012
1013 // ---------------------------------------------------------------------------
1014 // misc
1015 // ---------------------------------------------------------------------------
1016
1017 void wxMDIChildFrame::MSWDestroyWindow()
1018 {
1019 MSWDetachWindowMenu();
1020 invalidHandle = GetHwnd();
1021
1022 wxMDIParentFrame *parent = (wxMDIParentFrame *)GetParent();
1023
1024 // Must make sure this handle is invalidated (set to NULL) since all sorts
1025 // of things could happen after the child client is destroyed, but before
1026 // the wxFrame is destroyed.
1027
1028 HWND oldHandle = (HWND)GetHWND();
1029 SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIDESTROY,
1030 (WPARAM)oldHandle, 0);
1031 invalidHandle = 0;
1032
1033 if (m_hMenu)
1034 {
1035 ::DestroyMenu((HMENU) m_hMenu);
1036 m_hMenu = 0;
1037 }
1038 m_hWnd = 0;
1039 }
1040
1041 // Change the client window's extended style so we don't get a client edge
1042 // style when a child is maximised (a double border looks silly.)
1043 bool wxMDIChildFrame::ResetWindowStyle(void *vrect)
1044 {
1045 #if defined(__WIN95__)
1046 RECT *rect = (RECT *)vrect;
1047 wxMDIParentFrame* pFrameWnd = (wxMDIParentFrame *)GetParent();
1048 wxMDIChildFrame* pChild = pFrameWnd->GetActiveChild();
1049 if (!pChild || (pChild == this))
1050 {
1051 DWORD dwStyle = ::GetWindowLong(GetWinHwnd(pFrameWnd->GetClientWindow()), GWL_EXSTYLE);
1052 DWORD dwThisStyle = ::GetWindowLong(GetHwnd(), GWL_STYLE);
1053 DWORD dwNewStyle = dwStyle;
1054 if (pChild != NULL && (dwThisStyle & WS_MAXIMIZE))
1055 dwNewStyle &= ~(WS_EX_CLIENTEDGE);
1056 else
1057 dwNewStyle |= WS_EX_CLIENTEDGE;
1058
1059 if (dwStyle != dwNewStyle)
1060 {
1061 HWND hwnd = GetWinHwnd(pFrameWnd->GetClientWindow());
1062 ::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN);
1063 ::SetWindowLong(hwnd, GWL_EXSTYLE, dwNewStyle);
1064 ::SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
1065 SWP_FRAMECHANGED | SWP_NOACTIVATE |
1066 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1067 SWP_NOCOPYBITS);
1068 if (rect)
1069 ::GetClientRect(hwnd, rect);
1070
1071 return TRUE;
1072 }
1073 }
1074 #endif // Win95
1075
1076 return FALSE;
1077 }
1078
1079 // ===========================================================================
1080 // wxMDIClientWindow: the window of predefined (by Windows) class which
1081 // contains the child frames
1082 // ===========================================================================
1083
1084 bool wxMDIClientWindow::CreateClient(wxMDIParentFrame *parent, long style)
1085 {
1086 m_backgroundColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_APPWORKSPACE);
1087
1088 CLIENTCREATESTRUCT ccs;
1089 m_windowStyle = style;
1090 m_parent = parent;
1091
1092 ccs.hWindowMenu = (HMENU)parent->GetWindowMenu();
1093 ccs.idFirstChild = wxFIRST_MDI_CHILD;
1094
1095 DWORD msStyle = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN;
1096 if ( style & wxHSCROLL )
1097 msStyle |= WS_HSCROLL;
1098 if ( style & wxVSCROLL )
1099 msStyle |= WS_VSCROLL;
1100
1101 #if defined(__WIN95__)
1102 DWORD exStyle = WS_EX_CLIENTEDGE;
1103 #else
1104 DWORD exStyle = 0;
1105 #endif
1106
1107 wxWndHook = this;
1108 m_hWnd = (WXHWND)::CreateWindowEx
1109 (
1110 exStyle,
1111 wxT("MDICLIENT"),
1112 NULL,
1113 msStyle,
1114 0, 0, 0, 0,
1115 GetWinHwnd(parent),
1116 NULL,
1117 wxGetInstance(),
1118 (LPSTR)(LPCLIENTCREATESTRUCT)&ccs);
1119 if ( !m_hWnd )
1120 {
1121 wxLogLastError("CreateWindowEx(MDI client)");
1122
1123 return FALSE;
1124 }
1125
1126 SubclassWin(m_hWnd);
1127 wxWndHook = NULL;
1128
1129 return TRUE;
1130 }
1131
1132 // Explicitly call default scroll behaviour
1133 void wxMDIClientWindow::OnScroll(wxScrollEvent& event)
1134 {
1135 // Note: for client windows, the scroll position is not set in
1136 // WM_HSCROLL, WM_VSCROLL, so we can't easily determine what
1137 // scroll position we're at.
1138 // This makes it hard to paint patterns or bitmaps in the background,
1139 // and have the client area scrollable as well.
1140
1141 if ( event.GetOrientation() == wxHORIZONTAL )
1142 m_scrollX = event.GetPosition(); // Always returns zero!
1143 else
1144 m_scrollY = event.GetPosition(); // Always returns zero!
1145
1146 event.Skip();
1147 }
1148
1149 // ---------------------------------------------------------------------------
1150 // non member functions
1151 // ---------------------------------------------------------------------------
1152
1153 static void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow)
1154 {
1155 ::SendMessage(GetWinHwnd(win), WM_MDISETMENU,
1156 #ifdef __WIN32__
1157 (WPARAM)hmenuFrame, (LPARAM)hmenuWindow);
1158 #else
1159 0, MAKELPARAM(hmenuFrame, hmenuWindow));
1160 #endif
1161
1162 // update menu bar of the parent window
1163 wxWindow *parent = win->GetParent();
1164 wxCHECK_RET( parent, wxT("MDI client without parent frame? weird...") );
1165
1166 ::DrawMenuBar(GetWinHwnd(parent));
1167 }
1168
1169 static void InsertWindowMenu(wxWindow *win, WXHMENU menu, HMENU subMenu)
1170 {
1171 // Try to insert Window menu in front of Help, otherwise append it.
1172 HMENU hmenu = (HMENU)menu;
1173 int N = GetMenuItemCount(hmenu);
1174 bool success = FALSE;
1175 for ( int i = 0; i < N; i++ )
1176 {
1177 wxChar buf[256];
1178 int chars = GetMenuString(hmenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION);
1179 if ( chars == 0 )
1180 {
1181 wxLogLastError(wxT("GetMenuString"));
1182
1183 continue;
1184 }
1185
1186 if ( wxStripMenuCodes(wxString(buf)).IsSameAs(wxT("Help")) )
1187 {
1188 success = TRUE;
1189 ::InsertMenu(hmenu, i, MF_BYPOSITION | MF_POPUP | MF_STRING,
1190 (UINT)subMenu, wxT("&Window"));
1191 break;
1192 }
1193 }
1194
1195 if ( !success )
1196 {
1197 ::AppendMenu(hmenu, MF_POPUP, (UINT)subMenu, wxT("&Window"));
1198 }
1199
1200 MDISetMenu(win, hmenu, subMenu);
1201 }
1202
1203 static void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
1204 WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact)
1205 {
1206 #ifdef __WIN32__
1207 *activate = TRUE;
1208 *hwndAct = (WXHWND)lParam;
1209 *hwndDeact = (WXHWND)wParam;
1210 #else // Win16
1211 *activate = (WXWORD)wParam;
1212 *hwndAct = (WXHWND)LOWORD(lParam);
1213 *hwndDeact = (WXHWND)HIWORD(lParam);
1214 #endif // Win32/Win16
1215 }