1 // This is a part of the Microsoft Foundation Classes C++ library.
2 // Copyright (C) Microsoft Corporation
3 // All rights reserved.
5 // This source code is only intended as a supplement to the
6 // Microsoft Foundation Classes Reference and related
7 // electronic documentation provided with the library.
8 // See these sources for detailed information regarding the
9 // Microsoft Foundation Classes product.
12 //#include "afxmdichildwndex.h"
13 //#include "afxmdiframewndex.h"
16 /////////////////////////////////////////////////////////////////////////////
19 BEGIN_MESSAGE_MAP(CMDIFrameWnd
, CFrameWnd
)
20 //{{AFX_MSG_MAP(CMDIFrameWnd)
21 ON_MESSAGE_VOID(WM_IDLEUPDATECMDUI
, CMDIFrameWnd::OnIdleUpdateCmdUI
)
22 ON_UPDATE_COMMAND_UI(ID_WINDOW_ARRANGE
, &CMDIFrameWnd::OnUpdateMDIWindowCmd
)
23 ON_UPDATE_COMMAND_UI(ID_WINDOW_CASCADE
, &CMDIFrameWnd::OnUpdateMDIWindowCmd
)
24 ON_UPDATE_COMMAND_UI(ID_WINDOW_TILE_HORZ
, &CMDIFrameWnd::OnUpdateMDIWindowCmd
)
25 ON_UPDATE_COMMAND_UI(ID_WINDOW_TILE_VERT
, &CMDIFrameWnd::OnUpdateMDIWindowCmd
)
27 ON_COMMAND_EX(ID_WINDOW_ARRANGE
, &CMDIFrameWnd::OnMDIWindowCmd
)
28 ON_COMMAND_EX(ID_WINDOW_CASCADE
, &CMDIFrameWnd::OnMDIWindowCmd
)
29 ON_COMMAND_EX(ID_WINDOW_TILE_HORZ
, &CMDIFrameWnd::OnMDIWindowCmd
)
30 ON_COMMAND_EX(ID_WINDOW_TILE_VERT
, &CMDIFrameWnd::OnMDIWindowCmd
)
31 ON_UPDATE_COMMAND_UI(ID_WINDOW_NEW
, &CMDIFrameWnd::OnUpdateMDIWindowCmd
)
32 ON_COMMAND(ID_WINDOW_NEW
, &CMDIFrameWnd::OnWindowNew
)
34 ON_MESSAGE(WM_COMMANDHELP
, &CMDIFrameWnd::OnCommandHelp
)
39 CMDIFrameWnd::CMDIFrameWnd()
41 m_hWndMDIClient
= NULL
;
44 BOOL
CMDIFrameWnd::OnCommand(WPARAM wParam
, LPARAM lParam
)
46 // send to MDI child first - will be re-sent through OnCmdMsg later
47 CMDIChildWnd
* pActiveChild
= MDIGetActive();
48 if (pActiveChild
!= NULL
&& AfxCallWndProc(pActiveChild
,
49 pActiveChild
->m_hWnd
, WM_COMMAND
, wParam
, lParam
) != 0)
50 return TRUE
; // handled by child
52 if (CFrameWnd::OnCommand(wParam
, lParam
))
53 return TRUE
; // handled through normal mechanism (MDI child or frame)
55 HWND hWndCtrl
= (HWND
)lParam
;
57 ASSERT(AFX_IDM_FIRST_MDICHILD
== 0xFF00);
58 if (hWndCtrl
== NULL
&& (LOWORD(wParam
) & 0xf000) == 0xf000)
60 // menu or accelerator within range of MDI children
61 // default frame proc will handle it
62 DefWindowProc(WM_COMMAND
, wParam
, lParam
);
66 return FALSE
; // not handled
69 BOOL
CMDIFrameWnd::OnCmdMsg(UINT nID
, int nCode
, void* pExtra
,
70 AFX_CMDHANDLERINFO
* pHandlerInfo
)
72 CMDIChildWnd
* pActiveChild
= MDIGetActive();
73 // pump through active child FIRST
74 if (pActiveChild
!= NULL
)
76 CPushRoutingFrame
push(this);
77 if (pActiveChild
->OnCmdMsg(nID
, nCode
, pExtra
, pHandlerInfo
))
81 // then pump through normal frame
82 return CFrameWnd::OnCmdMsg(nID
, nCode
, pExtra
, pHandlerInfo
);
85 LRESULT
CMDIFrameWnd::OnCommandHelp(WPARAM wParam
, LPARAM lParam
)
87 if (lParam
== 0 && IsTracking())
88 lParam
= HID_BASE_COMMAND
+m_nIDTracking
;
90 CMDIChildWnd
* pActiveChild
= MDIGetActive();
91 if (pActiveChild
!= NULL
&& AfxCallWndProc(pActiveChild
,
92 pActiveChild
->m_hWnd
, WM_COMMANDHELP
, wParam
, lParam
) != 0)
98 if (CFrameWnd::OnCommandHelp(wParam
, lParam
))
100 // handled by our base
106 CWinApp
* pApp
= AfxGetApp();
109 AfxGetApp()->WinHelpInternal(lParam
);
116 BOOL
CMDIFrameWnd::OnCreateClient(LPCREATESTRUCT lpcs
, CCreateContext
*)
119 if (m_hMenuDefault
== NULL
)
121 // default implementation for MFC V1 backward compatibility
123 ASSERT(pMenu
!= NULL
);
124 // This is attempting to guess which sub-menu is the Window menu.
125 // The Windows user interface guidelines say that the right-most
126 // menu on the menu bar should be Help and Window should be one
127 // to the left of that.
128 int iMenu
= pMenu
->GetMenuItemCount() - 2;
130 // If this assertion fails, your menu bar does not follow the guidelines
131 // so you will have to override this function and call CreateClient
132 // appropriately or use the MFC V2 MDI functionality.
134 pMenu
= pMenu
->GetSubMenu(iMenu
);
135 ASSERT(pMenu
!= NULL
);
138 return CreateClient(lpcs
, pMenu
);
141 BOOL
CMDIFrameWnd::CreateClient(LPCREATESTRUCT lpCreateStruct
,
144 ASSERT(m_hWnd
!= NULL
);
145 ASSERT(m_hWndMDIClient
== NULL
);
146 DWORD dwStyle
= WS_VISIBLE
| WS_CHILD
| WS_BORDER
|
147 WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
|
148 MDIS_ALLCHILDSTYLES
; // allow children to be created invisible
150 // will be inset by the frame
152 // special styles for 3d effect on Win4
153 dwStyle
&= ~WS_BORDER
;
154 dwExStyle
= WS_EX_CLIENTEDGE
;
156 CLIENTCREATESTRUCT ccs
;
157 ccs
.hWindowMenu
= pWindowMenu
->GetSafeHmenu();
158 // set hWindowMenu for MFC V1 backward compatibility
159 // for MFC V2, window menu will be set in OnMDIActivate
160 ccs
.idFirstChild
= AFX_IDM_FIRST_MDICHILD
;
162 if (lpCreateStruct
->style
& (WS_HSCROLL
|WS_VSCROLL
))
164 // parent MDIFrame's scroll styles move to the MDICLIENT
165 dwStyle
|= (lpCreateStruct
->style
& (WS_HSCROLL
|WS_VSCROLL
));
167 // fast way to turn off the scrollbar bits (without a resize)
168 ModifyStyle(WS_HSCROLL
|WS_VSCROLL
, 0, SWP_NOREDRAW
|SWP_FRAMECHANGED
);
171 // Create MDICLIENT control with special IDC
172 if ((m_hWndMDIClient
= ::AfxCtxCreateWindowEx(dwExStyle
, _T("mdiclient"), NULL
,
173 dwStyle
, 0, 0, 0, 0, m_hWnd
, (HMENU
)AFX_IDW_PANE_FIRST
,
174 AfxGetInstanceHandle(), (LPVOID
)&ccs
)) == NULL
)
176 TRACE(traceAppMsg
, 0, _T("Warning: CMDIFrameWnd::OnCreateClient: failed to create MDICLIENT.")
177 _T(" GetLastError returns 0x%8.8X\n"), ::GetLastError());
180 // Move it to the top of z-order
181 ::BringWindowToTop(m_hWndMDIClient
);
186 LRESULT
CMDIFrameWnd::DefWindowProc(UINT nMsg
, WPARAM wParam
, LPARAM lParam
)
188 return ::DefFrameProc(m_hWnd
, m_hWndMDIClient
, nMsg
, wParam
, lParam
);
191 BOOL
CMDIFrameWnd::PreTranslateMessage(MSG
* pMsg
)
193 // check for special cancel modes for ComboBoxes
194 if (pMsg
->message
== WM_LBUTTONDOWN
|| pMsg
->message
== WM_NCLBUTTONDOWN
)
195 AfxCancelModes(pMsg
->hwnd
); // filter clicks
197 // allow tooltip messages to be filtered
198 if (CWnd::PreTranslateMessage(pMsg
))
201 #ifndef _AFX_NO_OLE_SUPPORT
202 // allow hook to consume message
203 if (m_pNotifyHook
!= NULL
&& m_pNotifyHook
->OnPreTranslateMessage(pMsg
))
207 CMDIChildWnd
* pActiveChild
= MDIGetActive();
209 // current active child gets first crack at it
210 if (pActiveChild
!= NULL
&& pActiveChild
->PreTranslateMessage(pMsg
))
213 if (pMsg
->message
>= WM_KEYFIRST
&& pMsg
->message
<= WM_KEYLAST
)
215 // translate accelerators for frame and any children
216 if (m_hAccelTable
!= NULL
&&
217 ::TranslateAccelerator(m_hWnd
, m_hAccelTable
, pMsg
))
222 // special processing for MDI accelerators last
223 // and only if it is not in SDI mode (print preview)
224 if (GetActiveView() == NULL
)
226 if (pMsg
->message
== WM_KEYDOWN
|| pMsg
->message
== WM_SYSKEYDOWN
)
228 // the MDICLIENT window may translate it
229 if (::TranslateMDISysAccel(m_hWndMDIClient
, pMsg
))
238 void CMDIFrameWnd::DelayUpdateFrameMenu(HMENU hMenuAlt
)
240 OnUpdateFrameMenu(hMenuAlt
);
242 m_nIdleFlags
|= idleMenu
;
245 void CMDIFrameWnd::OnIdleUpdateCmdUI()
247 if (m_nIdleFlags
& idleMenu
)
250 m_nIdleFlags
&= ~idleMenu
;
252 CFrameWnd::OnIdleUpdateCmdUI();
255 CFrameWnd
* CMDIFrameWnd::GetActiveFrame()
257 CMDIChildWnd
* pActiveChild
= MDIGetActive();
258 if (pActiveChild
== NULL
)
263 BOOL
CMDIFrameWnd::PreCreateWindow(CREATESTRUCT
& cs
)
265 if (cs
.lpszClass
== NULL
)
267 VERIFY(AfxDeferRegisterClass(AFX_WNDMDIFRAME_REG
));
268 cs
.lpszClass
= _afxWndMDIFrame
;
273 BOOL
CMDIFrameWnd::LoadFrame(UINT nIDResource
, DWORD dwDefaultStyle
,
274 CWnd
* pParentWnd
, CCreateContext
* pContext
)
276 if (!CFrameWnd::LoadFrame(nIDResource
, dwDefaultStyle
,
277 pParentWnd
, pContext
))
280 // save menu to use when no active MDI child window is present
281 ASSERT(m_hWnd
!= NULL
);
282 m_hMenuDefault
= ::GetMenu(m_hWnd
);
286 void CMDIFrameWnd::OnDestroy()
288 CFrameWnd::OnDestroy(); // exit and misc cleanup
290 // owned menu stored in shared slot for MDIFRAME
291 if (m_hMenuDefault
!= NULL
&& ::GetMenu(m_hWnd
) != m_hMenuDefault
)
293 // must go through MDI client to get rid of MDI menu additions
294 ::SendMessage(m_hWndMDIClient
, WM_MDISETMENU
,
295 (WPARAM
)m_hMenuDefault
, NULL
);
296 ASSERT(::GetMenu(m_hWnd
) == m_hMenuDefault
);
300 void CMDIFrameWnd::OnSize(UINT nType
, int, int)
302 // do not call default - it will reposition the MDICLIENT
303 if (nType
!= SIZE_MINIMIZED
)
307 LRESULT
CMDIFrameWnd::OnMenuChar(UINT nChar
, UINT
, CMenu
*)
309 // do not call Default() for Alt+(-) when in print preview mode
310 if (m_lpfnCloseProc
!= NULL
&& nChar
== (UINT
)'-')
316 CMDIChildWnd
* CMDIFrameWnd::MDIGetActive(BOOL
* pbMaximized
) const
318 // check first for MDI client window not created
319 if (m_hWndMDIClient
== NULL
)
321 if (pbMaximized
!= NULL
)
322 *pbMaximized
= FALSE
;
326 // MDI client has been created, get active MDI child
327 HWND hWnd
= (HWND
)::SendMessage(m_hWndMDIClient
, WM_MDIGETACTIVE
, 0,
328 (LPARAM
)pbMaximized
);
329 CMDIChildWnd
* pWnd
= (CMDIChildWnd
*)CWnd::FromHandlePermanent(hWnd
);
330 ASSERT(pWnd
== NULL
|| pWnd
->IsKindOf(RUNTIME_CLASS(CMDIChildWnd
)));
332 // check for special pseudo-inactive state
333 if (pWnd
!= NULL
&& pWnd
->m_bPseudoInactive
&&
334 (pWnd
->GetStyle() & WS_VISIBLE
) == 0)
336 // Window is hidden, active, but m_bPseudoInactive -- return NULL
338 // Ignore maximized flag if pseudo-inactive and maximized
339 if (pbMaximized
!= NULL
)
340 *pbMaximized
= FALSE
;
346 CMDIChildWnd
* CMDIFrameWnd::CreateNewChild(CRuntimeClass
* pClass
,
347 UINT nResources
, HMENU hMenu
/* = NULL */, HACCEL hAccel
/* = NULL */)
349 ASSERT(pClass
!= NULL
);
350 CMDIChildWnd
* pFrame
= (CMDIChildWnd
*) pClass
->CreateObject();
351 ASSERT_KINDOF(CMDIChildWnd
, pFrame
);
354 CCreateContext context
;
355 context
.m_pCurrentFrame
= this;
357 pFrame
->SetHandles(hMenu
, hAccel
);
358 if (!pFrame
->LoadFrame(nResources
,
359 WS_OVERLAPPEDWINDOW
| FWS_ADDTOTITLE
, NULL
, &context
))
361 TRACE(traceAppMsg
, 0, "Couldn't load frame window.\n");
365 CString strFullString
, strTitle
;
366 if (strFullString
.LoadString(nResources
))
367 AfxExtractSubString(strTitle
, strFullString
, CDocTemplate::docName
);
369 // redraw the frame and parent
370 pFrame
->SetTitle(strTitle
);
371 pFrame
->InitialUpdateFrame(NULL
, TRUE
);
376 /////////////////////////////////////////////////////////////////////////////
377 // CMDIFrameWnd Diagnostics
380 void CMDIFrameWnd::AssertValid() const
382 CFrameWnd::AssertValid();
383 ASSERT(m_hWndMDIClient
== NULL
|| ::IsWindow(m_hWndMDIClient
));
384 ASSERT(m_hMenuDefault
== NULL
|| ::IsMenu(m_hMenuDefault
));
387 void CMDIFrameWnd::Dump(CDumpContext
& dc
) const
391 dc
<< "m_hWndMDIClient = " << (void*)m_hWndMDIClient
;
392 dc
<< "\nm_hMenuDefault = " << (void*)m_hMenuDefault
;
398 /////////////////////////////////////////////////////////////////////////////
401 BEGIN_MESSAGE_MAP(CMDIChildWnd
, CFrameWnd
)
402 //{{AFX_MSG_MAP(CMDIChildWnd)
403 ON_WM_MOUSEACTIVATE()
407 ON_WM_WINDOWPOSCHANGING()
411 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW
, 0, 0xFFFF, &CMDIChildWnd::OnToolTipText
)
412 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA
, 0, 0xFFFF, &CMDIChildWnd::OnToolTipText
)
416 CMDIChildWnd::CMDIChildWnd()
418 m_hMenuShared
= NULL
;
419 m_bPseudoInactive
= FALSE
;
422 /////////////////////////////////////////////////////////////////////////////
423 // CMDIChildWnd special processing
425 LRESULT
CMDIChildWnd::DefWindowProc(UINT nMsg
, WPARAM wParam
, LPARAM lParam
)
427 return ::DefMDIChildProc(m_hWnd
, nMsg
, wParam
, lParam
);
430 BOOL
CMDIChildWnd::DestroyWindow()
435 // avoid changing the caption during the destroy message(s)
436 CMDIFrameWnd
* pFrameWnd
= GetMDIFrame();
437 HWND hWndFrame
= pFrameWnd
->m_hWnd
;
438 ASSERT(::IsWindow(hWndFrame
));
439 DWORD dwStyle
= SetWindowLong(hWndFrame
, GWL_STYLE
,
440 GetWindowLong(hWndFrame
, GWL_STYLE
) & ~FWS_ADDTOTITLE
);
444 if (::IsWindow(hWndFrame
))
446 ASSERT(hWndFrame
== pFrameWnd
->m_hWnd
);
447 SetWindowLong(hWndFrame
, GWL_STYLE
, dwStyle
);
448 pFrameWnd
->OnUpdateFrameTitle(TRUE
);
454 BOOL
CMDIChildWnd::PreTranslateMessage(MSG
* pMsg
)
456 // check for special cancel modes for combo boxes
457 if (pMsg
->message
== WM_LBUTTONDOWN
|| pMsg
->message
== WM_NCLBUTTONDOWN
)
458 AfxCancelModes(pMsg
->hwnd
); // filter clicks
460 // allow tooltip messages to be filtered
461 if (CWnd::PreTranslateMessage(pMsg
))
464 // we can't call 'CFrameWnd::PreTranslate' since it will translate
465 // accelerators in the context of the MDI Child - but since MDI Child
466 // windows don't have menus this doesn't work properly. MDI Child
467 // accelerators must be translated in context of their MDI Frame.
469 if (pMsg
->message
>= WM_KEYFIRST
&& pMsg
->message
<= WM_KEYLAST
)
471 // use document specific accelerator table over m_hAccelTable
472 HACCEL hAccel
= GetDefaultAccelerator();
473 return hAccel
!= NULL
&&
474 ::TranslateAccelerator(GetMDIFrame()->m_hWnd
, hAccel
, pMsg
);
479 BOOL
CMDIChildWnd::PreCreateWindow(CREATESTRUCT
& cs
)
481 ASSERT(cs
.style
& WS_CHILD
);
482 // MFC V2 requires that MDI Children are created with proper styles,
483 // usually: WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW.
484 // See Technical note TN019 for more details on MFC V1->V2 migration.
486 return CFrameWnd::PreCreateWindow(cs
);
489 BOOL
CMDIChildWnd::Create(LPCTSTR lpszClassName
,
490 LPCTSTR lpszWindowName
, DWORD dwStyle
,
491 const RECT
& rect
, CMDIFrameWnd
* pParentWnd
,
492 CCreateContext
* pContext
)
494 if (pParentWnd
== NULL
)
496 CWinThread
*pThread
= AfxGetThread();
497 ENSURE_VALID(pThread
);
498 CWnd
* pMainWnd
= pThread
->m_pMainWnd
;
499 ENSURE_VALID(pMainWnd
);
500 ASSERT_KINDOF(CMDIFrameWnd
, pMainWnd
);
501 pParentWnd
= (CMDIFrameWnd
*)pMainWnd
;
503 ASSERT(::IsWindow(pParentWnd
->m_hWndMDIClient
));
505 // insure correct window positioning
506 pParentWnd
->RecalcLayout();
508 // first copy into a CREATESTRUCT for PreCreate
511 cs
.lpszClass
= lpszClassName
;
512 cs
.lpszName
= lpszWindowName
;
516 cs
.cx
= rect
.right
- rect
.left
;
517 cs
.cy
= rect
.bottom
- rect
.top
;
518 cs
.hwndParent
= pParentWnd
->m_hWnd
;
520 cs
.hInstance
= AfxGetInstanceHandle();
521 cs
.lpCreateParams
= (LPVOID
)pContext
;
523 if (!PreCreateWindow(cs
))
528 // extended style must be zero for MDI Children (except under Win4)
529 ASSERT(cs
.hwndParent
== pParentWnd
->m_hWnd
); // must not change
531 // now copy into a MDICREATESTRUCT for real create
533 mcs
.szClass
= cs
.lpszClass
;
534 mcs
.szTitle
= cs
.lpszName
;
535 mcs
.hOwner
= cs
.hInstance
;
540 mcs
.style
= cs
.style
& ~(WS_MAXIMIZE
| WS_VISIBLE
);
541 mcs
.lParam
= (LPARAM
)cs
.lpCreateParams
;
543 // create the window through the MDICLIENT window
544 AfxHookWindowCreate(this);
545 HWND hWnd
= (HWND
)::SendMessage(pParentWnd
->m_hWndMDIClient
,
546 WM_MDICREATE
, 0, (LPARAM
)&mcs
);
547 if (!AfxUnhookWindowCreate())
548 PostNcDestroy(); // cleanup if MDICREATE fails too soon
553 // special handling of visibility (always created invisible)
554 if (cs
.style
& WS_VISIBLE
)
556 // place the window on top in z-order before showing it
557 ::BringWindowToTop(hWnd
);
559 // show it as specified
560 if (cs
.style
& WS_MINIMIZE
)
561 ShowWindow(SW_SHOWMINIMIZED
);
562 else if (cs
.style
& WS_MAXIMIZE
)
563 ShowWindow(SW_SHOWMAXIMIZED
);
565 ShowWindow(SW_SHOWNORMAL
);
567 // make sure it is active (visibility == activation)
568 pParentWnd
->MDIActivate(this);
570 // refresh MDI Window menu
571 ::SendMessage(pParentWnd
->m_hWndMDIClient
, WM_MDIREFRESHMENU
, 0, 0);
574 ASSERT(hWnd
== m_hWnd
);
578 BOOL
CMDIChildWnd::LoadFrame(UINT nIDResource
, DWORD dwDefaultStyle
,
579 CWnd
* pParentWnd
, CCreateContext
* pContext
)
582 ASSERT_VALID_IDR(nIDResource
);
583 ASSERT(m_nIDHelp
== 0 || m_nIDHelp
== nIDResource
);
585 m_nIDHelp
= nIDResource
; // ID for help context (+HID_BASE_RESOURCE)
587 // parent must be MDI Frame (or NULL for default)
588 ASSERT(pParentWnd
== NULL
|| pParentWnd
->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd
)));
589 // will be a child of MDIClient
590 ASSERT(!(dwDefaultStyle
& WS_POPUP
));
591 dwDefaultStyle
|= WS_CHILD
;
593 // if available - get MDI child menus from doc template
594 CMultiDocTemplate
* pTemplate
;
595 if (pContext
!= NULL
&&
596 (pTemplate
= (CMultiDocTemplate
*)pContext
->m_pNewDocTemplate
) != NULL
)
598 ASSERT_KINDOF(CMultiDocTemplate
, pTemplate
);
599 // get shared menu from doc template
600 m_hMenuShared
= pTemplate
->m_hMenuShared
;
601 m_hAccelTable
= pTemplate
->m_hAccelTable
;
605 TRACE(traceAppMsg
, 0, "Warning: no shared menu/acceltable for MDI Child window.\n");
606 // if this happens, programmer must load these manually
609 CString strFullString
, strTitle
;
610 if (strFullString
.LoadString(nIDResource
))
611 AfxExtractSubString(strTitle
, strFullString
, 0); // first sub-string
613 ASSERT(m_hWnd
== NULL
);
614 if (!Create(GetIconWndClass(dwDefaultStyle
, nIDResource
),
615 strTitle
, dwDefaultStyle
, rectDefault
,
616 (CMDIFrameWnd
*)pParentWnd
, pContext
))
618 return FALSE
; // will self destruct on failure normally
625 void CMDIChildWnd::OnSize(UINT nType
, int cx
, int cy
)
627 CFrameWnd::OnSize(nType
, cx
, cy
);
629 // update our parent frame - in case we are now maximized or not
630 GetMDIFrame()->OnUpdateFrameTitle(TRUE
);
633 BOOL
CMDIChildWnd::UpdateClientEdge(LPRECT lpRect
)
635 // only adjust for active MDI child window
636 CMDIFrameWnd
* pFrameWnd
= GetMDIFrame();
637 CMDIChildWnd
* pChild
= pFrameWnd
->MDIGetActive();
639 // Only adjust for regular MDI child windows, not tabbed windows. Attempting to set WS_EX_CLIENTEDGE on the tabbed
640 // MDI client area window is subverted by CMDIClientAreaWnd::OnStyleChanging, so we always try to reset the style and
641 // always repaint, none of which is necessary since the tabbed MDI children never change from maximized to restored.
642 //CMDIChildWndEx* pChildEx = (pChild == NULL) ? NULL : DYNAMIC_DOWNCAST(CMDIChildWndEx, pChild);
643 //BOOL bIsTabbedMDIChild = (pChildEx == NULL) ? FALSE : pChildEx->GetMDIFrameWndEx() != NULL && pChildEx->GetMDIFrameWndEx()->AreMDITabs();
644 if ((pChild
== NULL
|| pChild
== this) /*&& !bIsTabbedMDIChild*/)
646 // need to adjust the client edge style as max/restore happens
647 DWORD dwStyle
= ::GetWindowLong(pFrameWnd
->m_hWndMDIClient
, GWL_EXSTYLE
);
648 DWORD dwNewStyle
= dwStyle
;
649 if (pChild
!= NULL
&& !(GetExStyle() & WS_EX_CLIENTEDGE
) && (GetStyle() & WS_MAXIMIZE
))
651 dwNewStyle
&= ~(WS_EX_CLIENTEDGE
);
655 dwNewStyle
|= WS_EX_CLIENTEDGE
;
658 if (dwStyle
!= dwNewStyle
)
660 // SetWindowPos will not move invalid bits
661 ::RedrawWindow(pFrameWnd
->m_hWndMDIClient
, NULL
, NULL
, RDW_INVALIDATE
| RDW_ALLCHILDREN
);
663 // remove/add WS_EX_CLIENTEDGE to MDI client area
664 ::SetWindowLong(pFrameWnd
->m_hWndMDIClient
, GWL_EXSTYLE
, dwNewStyle
);
665 ::SetWindowPos(pFrameWnd
->m_hWndMDIClient
, NULL
, 0, 0, 0, 0, SWP_FRAMECHANGED
| SWP_NOACTIVATE
| SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOCOPYBITS
);
667 // return new client area
670 ::GetClientRect(pFrameWnd
->m_hWndMDIClient
, lpRect
);
680 void CMDIChildWnd::OnWindowPosChanging(LPWINDOWPOS lpWndPos
)
682 if (!(lpWndPos
->flags
& SWP_NOSIZE
))
685 if (UpdateClientEdge(rectClient
) && (GetStyle() & WS_MAXIMIZE
))
687 // adjust maximized window size and position based on new
688 // size/position of the MDI client area.
689 ::AdjustWindowRectEx(rectClient
, GetStyle(), FALSE
, GetExStyle());
690 lpWndPos
->x
= rectClient
.left
;
691 lpWndPos
->y
= rectClient
.top
;
692 lpWndPos
->cx
= rectClient
.Width();
693 lpWndPos
->cy
= rectClient
.Height();
697 CFrameWnd::OnWindowPosChanging(lpWndPos
);
700 void CMDIChildWnd::OnDestroy()
704 CFrameWnd::OnDestroy();
707 BOOL
CMDIChildWnd::OnNcActivate(BOOL bActive
)
709 // bypass CFrameWnd::OnNcActivate()
710 return CWnd::OnNcActivate(bActive
);
713 int CMDIChildWnd::OnMouseActivate(CWnd
* pDesktopWnd
, UINT nHitTest
, UINT message
)
715 int nResult
= CFrameWnd::OnMouseActivate(pDesktopWnd
, nHitTest
, message
);
716 if (nResult
== MA_NOACTIVATE
|| nResult
== MA_NOACTIVATEANDEAT
)
717 return nResult
; // frame does not want to activate
719 // activate this window if necessary
720 CMDIFrameWnd
* pFrameWnd
= GetMDIFrame();
721 ENSURE_VALID(pFrameWnd
);
722 CMDIChildWnd
* pActive
= pFrameWnd
->MDIGetActive();
729 BOOL
CMDIChildWnd::OnToolTipText(UINT msg
, NMHDR
* pNMHDR
, LRESULT
* pResult
)
731 ASSERT(pNMHDR
->code
== TTN_NEEDTEXTA
|| pNMHDR
->code
== TTN_NEEDTEXTW
);
734 // check to see if the message is going directly to this window or not
735 const MSG
* pMsg
= GetCurrentMessage();
736 if (pMsg
->hwnd
!= m_hWnd
)
738 // let top level frame handle this for us
742 // otherwise, handle it ourselves
743 return CFrameWnd::OnToolTipText(msg
, pNMHDR
, pResult
);
746 void CMDIChildWnd::ActivateFrame(int nCmdShow
)
748 BOOL bVisibleThen
= (GetStyle() & WS_VISIBLE
) != 0;
749 CMDIFrameWnd
* pFrameWnd
= GetMDIFrame();
750 ASSERT_VALID(pFrameWnd
);
752 // determine default show command
755 // get maximized state of frame window (previously active child)
757 pFrameWnd
->MDIGetActive(&bMaximized
);
759 // convert show command based on current style
760 DWORD dwStyle
= GetStyle();
761 if (bMaximized
|| (dwStyle
& WS_MAXIMIZE
))
762 nCmdShow
= SW_SHOWMAXIMIZED
;
763 else if (dwStyle
& WS_MINIMIZE
)
764 nCmdShow
= SW_SHOWMINIMIZED
;
767 // finally, show the window
768 CFrameWnd::ActivateFrame(nCmdShow
);
770 // update the Window menu to reflect new child window
771 CMDIFrameWnd
* pFrame
= GetMDIFrame();
772 ::SendMessage(pFrame
->m_hWndMDIClient
, WM_MDIREFRESHMENU
, 0, 0);
774 // Note: Update the m_bPseudoInactive flag. This is used to handle the
775 // last MDI child getting hidden. Windows provides no way to deactivate
776 // an MDI child window.
778 BOOL bVisibleNow
= (GetStyle() & WS_VISIBLE
) != 0;
779 if (bVisibleNow
== bVisibleThen
)
784 // get current active window according to Windows MDI
785 HWND hWnd
= (HWND
)::SendMessage(pFrameWnd
->m_hWndMDIClient
,
786 WM_MDIGETACTIVE
, 0, 0);
789 // not active any more -- window must have been deactivated
790 ASSERT(!m_bPseudoInactive
);
795 ASSERT(hWnd
!= NULL
);
796 pFrameWnd
->MDINext();
798 // see if it has been deactivated now...
799 hWnd
= (HWND
)::SendMessage(pFrameWnd
->m_hWndMDIClient
,
800 WM_MDIGETACTIVE
, 0, 0);
803 // still active -- fake deactivate it
804 ASSERT(hWnd
!= NULL
);
805 ::SendMessage(pFrameWnd
->m_hWndMDIClient
, WM_MDIACTIVATE
, (WPARAM
)m_hWnd
, NULL
);
806 m_bPseudoInactive
= TRUE
; // so MDIGetActive returns NULL
809 else if (m_bPseudoInactive
)
811 // if state transitioned from not visible to visible, but
812 // was pseudo deactivated -- send activate notify now
813 ::SendMessage(pFrameWnd
->m_hWndMDIClient
, WM_MDIACTIVATE
, NULL
, (LPARAM
)m_hWnd
);
814 ASSERT(!m_bPseudoInactive
); // should get set in OnMDIActivate!
818 void CMDIChildWnd::SetHandles(HMENU hMenu
, HACCEL hAccel
)
820 m_hMenuShared
= hMenu
;
821 m_hAccelTable
= hAccel
;
824 /////////////////////////////////////////////////////////////////////////////
825 // CMDIChildWnd Diagnostics
828 void CMDIChildWnd::AssertValid() const
830 CFrameWnd::AssertValid();
831 ASSERT(m_hMenuShared
== NULL
|| ::IsMenu(m_hMenuShared
));
834 void CMDIChildWnd::Dump(CDumpContext
& dc
) const
838 dc
<< "m_hMenuShared = " << (void*)m_hMenuShared
;
843 /////////////////////////////////////////////////////////////////////////////
844 // Smarts for the "Window" menu
846 HMENU
CMDIFrameWnd::GetWindowMenuPopup(HMENU hMenuBar
)
847 // find which popup is the "Window" menu
849 if (hMenuBar
== NULL
)
852 ASSERT(::IsMenu(hMenuBar
));
854 int iItem
= ::GetMenuItemCount(hMenuBar
);
857 HMENU hMenuPop
= ::GetSubMenu(hMenuBar
, iItem
);
858 if (hMenuPop
!= NULL
)
860 int iItemMax
= ::GetMenuItemCount(hMenuPop
);
861 for (int iItemPop
= 0; iItemPop
< iItemMax
; iItemPop
++)
863 UINT nID
= GetMenuItemID(hMenuPop
, iItemPop
);
864 if (nID
>= AFX_IDM_WINDOW_FIRST
&& nID
<= AFX_IDM_WINDOW_LAST
)
870 // no default menu found
871 TRACE(traceAppMsg
, 0, "Warning: GetWindowMenuPopup failed!\n");
875 /////////////////////////////////////////////////////////////////////////////
876 // Smarts for updating the window menu based on the current child
878 void CMDIFrameWnd::OnUpdateFrameMenu(HMENU hMenuAlt
)
880 CMDIChildWnd
* pActiveWnd
= MDIGetActive();
881 if (pActiveWnd
!= NULL
)
883 // let child update the menu bar
884 pActiveWnd
->OnUpdateFrameMenu(TRUE
, pActiveWnd
, hMenuAlt
);
888 // no child active, so have to update it ourselves
889 // (we can't send it to a child window, since pActiveWnd is NULL)
890 if (hMenuAlt
== NULL
)
891 hMenuAlt
= m_hMenuDefault
; // use default
892 ::SendMessage(m_hWndMDIClient
, WM_MDISETMENU
, (WPARAM
)hMenuAlt
, NULL
);
896 /////////////////////////////////////////////////////////////////////////////
897 // MDI Child Extensions
899 // walk up two parents for MDIFrame that owns MDIChild (skip MDIClient)
900 CMDIFrameWnd
* CMDIChildWnd::GetMDIFrame()
902 ASSERT_KINDOF(CMDIChildWnd
, this);
903 ASSERT(m_hWnd
!= NULL
);
904 HWND hWndMDIClient
= ::GetParent(m_hWnd
);
905 ASSERT(hWndMDIClient
!= NULL
);
907 CMDIFrameWnd
* pMDIFrame
;
908 pMDIFrame
= (CMDIFrameWnd
*)CWnd::FromHandle(::GetParent(hWndMDIClient
));
909 ASSERT(pMDIFrame
!= NULL
);
910 ASSERT_KINDOF(CMDIFrameWnd
, pMDIFrame
);
911 ASSERT(pMDIFrame
->m_hWndMDIClient
== hWndMDIClient
);
912 ASSERT_VALID(pMDIFrame
);
916 CWnd
* CMDIChildWnd::GetMessageBar()
918 // status bar/message bar owned by parent MDI frame
919 return GetMDIFrame()->GetMessageBar();
922 void CMDIChildWnd::OnUpdateFrameTitle(BOOL bAddToTitle
)
924 // update our parent window first
925 GetMDIFrame()->OnUpdateFrameTitle(bAddToTitle
);
927 if ((GetStyle() & FWS_ADDTOTITLE
) == 0)
928 return; // leave child window alone!
930 CDocument
* pDocument
= GetActiveDocument();
933 TCHAR szText
[256+_MAX_PATH
];
934 if (pDocument
== NULL
)
935 Checked::tcsncpy_s(szText
, _countof(szText
), m_strTitle
, _TRUNCATE
);
937 Checked::tcsncpy_s(szText
, _countof(szText
), pDocument
->GetTitle(), _TRUNCATE
);
940 TCHAR szWinNumber
[16+1];
941 _stprintf_s(szWinNumber
, _countof(szWinNumber
), _T(":%d"), m_nWindow
);
943 if( lstrlen(szText
) + lstrlen(szWinNumber
) < _countof(szText
) )
945 Checked::tcscat_s( szText
, _countof(szText
), szWinNumber
);
949 // set title if changed, but don't remove completely
950 AfxSetWindowText(m_hWnd
, szText
);
954 void CMDIChildWnd::OnMDIActivate(BOOL bActivate
, CWnd
* pActivateWnd
, CWnd
*)
956 m_bPseudoInactive
= FALSE
; // must be happening for real
958 // make sure MDI client window has correct client edge
961 // send deactivate notification to active view
962 CView
* pActiveView
= GetActiveView();
963 if (!bActivate
&& pActiveView
!= NULL
)
964 pActiveView
->OnActivateView(FALSE
, pActiveView
, pActiveView
);
966 // allow hook to short circuit normal activation
967 BOOL bHooked
= FALSE
;
968 #ifndef _AFX_NO_OLE_SUPPORT
969 if (m_pNotifyHook
!= NULL
&& m_pNotifyHook
->OnDocActivate(bActivate
))
973 // update titles (don't AddToTitle if deactivate last)
975 OnUpdateFrameTitle(bActivate
|| (pActivateWnd
!= NULL
));
977 // re-activate the appropriate view
980 if (pActiveView
!= NULL
&& GetMDIFrame() == GetActiveWindow())
981 pActiveView
->OnActivateView(TRUE
, pActiveView
, pActiveView
);
987 OnUpdateFrameMenu(bActivate
, pActivateWnd
, NULL
);
988 GetMDIFrame()->DrawMenuBar();
992 void CMDIChildWnd::OnUpdateFrameMenu(BOOL bActivate
, CWnd
* pActivateWnd
,
995 CMDIFrameWnd
* pFrame
= GetMDIFrame();
997 if (hMenuAlt
== NULL
&& bActivate
)
999 // attempt to get default menu from document
1000 CDocument
* pDoc
= GetActiveDocument();
1002 hMenuAlt
= pDoc
->GetDefaultMenu();
1005 // use default menu stored in frame if none from document
1006 if (hMenuAlt
== NULL
)
1007 hMenuAlt
= m_hMenuShared
;
1009 if (hMenuAlt
!= NULL
&& bActivate
)
1011 ASSERT(pActivateWnd
== this);
1013 // activating child, set parent menu
1014 ::SendMessage(pFrame
->m_hWndMDIClient
, WM_MDISETMENU
,
1015 (WPARAM
)hMenuAlt
, (LPARAM
)pFrame
->GetWindowMenuPopup(hMenuAlt
));
1017 else if (hMenuAlt
!= NULL
&& !bActivate
&& pActivateWnd
== NULL
)
1019 // destroying last child
1020 HMENU hMenuLast
= NULL
;
1021 ::SendMessage(pFrame
->m_hWndMDIClient
, WM_MDISETMENU
,
1022 (WPARAM
)pFrame
->m_hMenuDefault
, (LPARAM
)hMenuLast
);
1026 // refresh MDI Window menu (even if non-shared menu)
1027 ::SendMessage(pFrame
->m_hWndMDIClient
, WM_MDIREFRESHMENU
, 0, 0);
1031 BOOL
CMDIChildWnd::OnNcCreate(LPCREATESTRUCT lpCreateStruct
)
1033 if (!CFrameWnd::OnNcCreate(lpCreateStruct
))
1036 // handle extended styles under Win4
1037 // call PreCreateWindow again just to get dwExStyle
1038 VERIFY(PreCreateWindow(*lpCreateStruct
));
1039 SetWindowLong(m_hWnd
, GWL_EXSTYLE
, lpCreateStruct
->dwExStyle
);
1044 int CMDIChildWnd::OnCreate(LPCREATESTRUCT lpCreateStruct
)
1046 // call base class with lParam context (not MDI one)
1047 MDICREATESTRUCT
* lpmcs
;
1048 lpmcs
= (MDICREATESTRUCT
*)lpCreateStruct
->lpCreateParams
;
1049 CCreateContext
* pContext
= (CCreateContext
*)lpmcs
->lParam
;
1051 return OnCreateHelper(lpCreateStruct
, pContext
);
1054 /////////////////////////////////////////////////////////////////////////////
1055 // Special UI processing depending on current active child
1057 void CMDIFrameWnd::OnUpdateFrameTitle(BOOL bAddToTitle
)
1059 if ((GetStyle() & FWS_ADDTOTITLE
) == 0)
1060 return; // leave it alone!
1062 #ifndef _AFX_NO_OLE_SUPPORT
1063 // allow hook to set the title (used for OLE support)
1064 if (m_pNotifyHook
!= NULL
&& m_pNotifyHook
->OnUpdateFrameTitle())
1068 CMDIChildWnd
* pActiveChild
= NULL
;
1069 CDocument
* pDocument
= GetActiveDocument();
1071 (pActiveChild
= MDIGetActive()) != NULL
&&
1072 (pActiveChild
->GetStyle() & WS_MAXIMIZE
) == 0 &&
1073 (pDocument
!= NULL
||
1074 (pDocument
= pActiveChild
->GetActiveDocument()) != NULL
))
1075 UpdateFrameTitleForDocument(pDocument
->GetTitle());
1078 LPCTSTR lpstrTitle
= NULL
;
1081 if (pActiveChild
!= NULL
&&
1082 (pActiveChild
->GetStyle() & WS_MAXIMIZE
) == 0)
1084 strTitle
= pActiveChild
->GetTitle();
1085 if (!strTitle
.IsEmpty())
1086 lpstrTitle
= strTitle
;
1088 UpdateFrameTitleForDocument(lpstrTitle
);
1092 /////////////////////////////////////////////////////////////////////////////
1093 // Standard MDI Commands
1095 // Two function for all standard MDI "Window" commands
1096 void CMDIFrameWnd::OnUpdateMDIWindowCmd(CCmdUI
* pCmdUI
)
1098 ASSERT(m_hWndMDIClient
!= NULL
);
1099 pCmdUI
->Enable(MDIGetActive() != NULL
);
1102 BOOL
CMDIFrameWnd::OnMDIWindowCmd(UINT nID
)
1104 ASSERT(m_hWndMDIClient
!= NULL
);
1111 return FALSE
; // not for us
1112 case ID_WINDOW_ARRANGE
:
1113 msg
= WM_MDIICONARRANGE
;
1115 case ID_WINDOW_CASCADE
:
1116 msg
= WM_MDICASCADE
;
1118 case ID_WINDOW_TILE_HORZ
:
1119 wParam
= MDITILE_HORIZONTAL
;
1121 case ID_WINDOW_TILE_VERT
:
1122 ASSERT(MDITILE_VERTICAL
== 0);
1127 ::SendMessage(m_hWndMDIClient
, msg
, wParam
, 0);
1131 void CMDIFrameWnd::OnWindowNew()
1133 CMDIChildWnd
* pActiveChild
= MDIGetActive();
1134 CDocument
* pDocument
;
1135 if (pActiveChild
== NULL
||
1136 (pDocument
= pActiveChild
->GetActiveDocument()) == NULL
)
1138 TRACE(traceAppMsg
, 0, "Warning: No active document for WindowNew command.\n");
1139 AfxMessageBox(AFX_IDP_COMMAND_FAILURE
);
1140 return; // command failed
1143 // otherwise we have a new frame !
1144 CDocTemplate
* pTemplate
= pDocument
->GetDocTemplate();
1145 ASSERT_VALID(pTemplate
);
1146 CFrameWnd
* pFrame
= pTemplate
->CreateNewFrame(pDocument
, pActiveChild
);
1149 TRACE(traceAppMsg
, 0, "Warning: failed to create new frame.\n");
1150 return; // command failed
1153 pTemplate
->InitialUpdateFrame(pFrame
, pDocument
);
1156 void CMDIFrameWnd::SetMenuBarVisibility(DWORD dwStyle
)
1158 ENSURE_ARG(dwStyle
== AFX_MBV_KEEPVISIBLE
);
1159 ASSERT(m_dwMenuBarVisibility
== AFX_MBV_KEEPVISIBLE
);
1162 BOOL
CMDIFrameWnd::SetMenuBarState(DWORD dwState
)
1164 return m_dwMenuBarState
== AFX_MBS_HIDDEN
? FALSE
: CFrameWnd::SetMenuBarState(dwState
);
1167 IMPLEMENT_DYNCREATE(CMDIFrameWnd
, CFrameWnd
)
1168 IMPLEMENT_DYNCREATE(CMDIChildWnd
, CFrameWnd
)
1170 ////////////////////////////////////////////////////////////////////////////