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