📄 mdi.c
字号:
/* MDI.C
*
* Copyright 1994, Bob Amstadt
* 1995,1996 Alex Korobka
*
* This file contains routines to support MDI (Multiple Document
* Interface) features .
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* Notes: Fairly complete implementation.
* Also, Excel and WinWord do _not_ use MDI so if you're trying
* to fix them look elsewhere.
*
* Notes on how the "More Windows..." is implemented:
*
* When we have more than 9 opened windows, a "More Windows..."
* option appears in the "Windows" menu. Each child window has
* a WND* associated with it, accesible via the children list of
* the parent window. This WND* has a wIDmenu member, which reflects
* the position of the child in the window list. For example, with
* 9 child windows, we could have the following pattern:
*
*
*
* Name of the child window pWndChild->wIDmenu
* Doc1 5000
* Doc2 5001
* Doc3 5002
* Doc4 5003
* Doc5 5004
* Doc6 5005
* Doc7 5006
* Doc8 5007
* Doc9 5008
*
*
* The "Windows" menu, as the "More windows..." dialog, are constructed
* in this order. If we add a child, we would have the following list:
*
*
* Name of the child window pWndChild->wIDmenu
* Doc1 5000
* Doc2 5001
* Doc3 5002
* Doc4 5003
* Doc5 5004
* Doc6 5005
* Doc7 5006
* Doc8 5007
* Doc9 5008
* Doc10 5009
*
* But only 5000 to 5008 would be displayed in the "Windows" menu. We want
* the last created child to be in the menu, so we swap the last child with
* the 9th... Doc9 will be accessible via the "More Windows..." option.
*
* Doc1 5000
* Doc2 5001
* Doc3 5002
* Doc4 5003
* Doc5 5004
* Doc6 5005
* Doc7 5006
* Doc8 5007
* Doc9 5009
* Doc10 5008
*
*/
#include <user32.h>
#include <wine/debug.h>
WINE_DEFAULT_DEBUG_CHANNEL(mdi);
#define MDI_MAXTITLELENGTH 0xa1
#define WM_MDICALCCHILDSCROLL 0x10ac /* this is exactly what Windows uses */
/* "More Windows..." definitions */
#define MDI_MOREWINDOWSLIMIT 9 /* after this number of windows, a "More Windows..."
option will appear under the Windows menu */
#define MDI_IDC_LISTBOX 100
#define IDS_MDI_MOREWINDOWS 13
#define MDIF_NEEDUPDATE 0x0001
typedef struct
{
UINT nActiveChildren;
HWND hwndActiveChild;
HWND *child; /* array of tracked children */
HMENU hFrameMenu;
HMENU hWindowMenu;
UINT idFirstChild;
LPWSTR frameTitle;
UINT nTotalCreated;
UINT mdiFlags;
UINT sbRecalc; /* SB_xxx flags for scrollbar fixup */
HBITMAP hBmpClose; /* ReactOS modification */
} MDICLIENTINFO;
//static HBITMAP hBmpClose = 0;
/* ----------------- declarations ----------------- */
static void MDI_UpdateFrameText( HWND, HWND, LPCWSTR);
static BOOL MDI_AugmentFrameMenu( HWND, HWND );
static BOOL MDI_RestoreFrameMenu( HWND, HWND, HBITMAP );
static LONG MDI_ChildActivate( HWND, HWND );
static LRESULT MDI_RefreshMenu(MDICLIENTINFO *);
static HWND MDI_MoreWindowsDialog(HWND);
static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
static
HWND* WIN_ListChildren (HWND hWndparent)
{
DWORD dwCount = 0;
HWND* pHwnd = NULL;
HANDLE hHeap;
SetLastError(0);
dwCount = NtUserBuildHwndList ( NULL, hWndparent, FALSE, 0, 0, NULL, 0 );
if ( !dwCount || GetLastError() )
return 0;
/* allocate buffer to receive HWND handles */
hHeap = GetProcessHeap();
pHwnd = HeapAlloc ( hHeap, 0, sizeof(HWND)*(dwCount+1) );
if ( !pHwnd )
{
SetLastError ( ERROR_NOT_ENOUGH_MEMORY );
return 0;
}
/* now call kernel again to fill the buffer this time */
dwCount = NtUserBuildHwndList (NULL, hWndparent, FALSE, 0, 0, pHwnd, dwCount );
if ( !dwCount || GetLastError() )
{
if ( pHwnd )
HeapFree ( hHeap, 0, pHwnd );
return 0;
}
return pHwnd;
}
#ifdef __REACTOS__
void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void WINAPI CalcChildScroll(HWND hwnd, INT scroll);
#endif
/* -------- Miscellaneous service functions ----------
*
* MDI_GetChildByID
*/
static HWND MDI_GetChildByID(HWND hwnd, UINT id, MDICLIENTINFO *ci)
{
int i;
for (i = 0; ci->nActiveChildren; i++)
{
if (GetWindowLongPtrW( ci->child[i], GWLP_ID ) == id)
return ci->child[i];
}
return 0;
}
static void MDI_PostUpdate(HWND hwnd, MDICLIENTINFO* ci, WORD recalc)
{
if( !(ci->mdiFlags & MDIF_NEEDUPDATE) )
{
ci->mdiFlags |= MDIF_NEEDUPDATE;
PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0);
}
ci->sbRecalc = recalc;
}
/*********************************************************************
* MDIClient class descriptor
*/
const struct builtin_class_descr MDICLIENT_builtin_class =
{
L"MDIClient", /* name */
0, /* style */
MDIClientWndProcW, /* procW */
MDIClientWndProcA, /* procA */
sizeof(MDICLIENTINFO), /* extra */
IDC_ARROW, /* cursor */
(HBRUSH)(COLOR_APPWORKSPACE+1) /* brush */
};
static MDICLIENTINFO *get_client_info( HWND client )
{
#ifdef __REACTOS__
return (MDICLIENTINFO *)GetWindowLongPtr(client, 0);
#else
MDICLIENTINFO *ret = NULL;
WND *win = WIN_GetPtr( client );
if (win)
{
if (win == WND_OTHER_PROCESS)
{
if (IsWindow(client)) ERR( "client %p belongs to other process\n", client );
return NULL;
}
if (win->cbWndExtra < sizeof(MDICLIENTINFO)) WARN( "%p is not an MDI client\n", client );
else ret = (MDICLIENTINFO *)win->wExtra;
WIN_ReleasePtr( win );
}
return ret;
#endif
}
static BOOL is_close_enabled(HWND hwnd, HMENU hSysMenu)
{
if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return FALSE;
if (!hSysMenu) hSysMenu = GetSystemMenu(hwnd, FALSE);
if (hSysMenu)
{
UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
return FALSE;
}
return TRUE;
}
/**********************************************************************
* MDI_GetWindow
*
* returns "activateable" child different from the current or zero
*/
static HWND MDI_GetWindow(MDICLIENTINFO *clientInfo, HWND hWnd, BOOL bNext,
DWORD dwStyleMask )
{
int i;
HWND *list;
HWND last = 0;
dwStyleMask |= WS_DISABLED | WS_VISIBLE;
if( !hWnd ) hWnd = clientInfo->hwndActiveChild;
if (!(list = WIN_ListChildren( GetParent(hWnd) ))) return 0;
i = 0;
/* start from next after hWnd */
while (list[i] && list[i] != hWnd) i++;
if (list[i]) i++;
for ( ; list[i]; i++)
{
if (GetWindow( list[i], GW_OWNER )) continue;
if ((GetWindowLongW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
last = list[i];
if (bNext) goto found;
}
/* now restart from the beginning */
for (i = 0; list[i] && list[i] != hWnd; i++)
{
if (GetWindow( list[i], GW_OWNER )) continue;
if ((GetWindowLongW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
last = list[i];
if (bNext) goto found;
}
found:
HeapFree( GetProcessHeap(), 0, list );
return last;
}
/**********************************************************************
* MDI_CalcDefaultChildPos
*
* It seems that the default height is about 2/3 of the client rect
*/
void MDI_CalcDefaultChildPos( HWND hwndClient, INT total, LPPOINT lpPos, INT delta, UINT *id )
{
INT nstagger;
RECT rect;
INT spacing = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME) - 1;
if (total < 0) /* we are called from CreateWindow */
{
MDICLIENTINFO *ci = get_client_info(hwndClient);
total = ci ? ci->nTotalCreated : 0;
*id = ci->idFirstChild + ci->nActiveChildren;
TRACE("MDI child id %04x\n", *id);
}
GetClientRect( hwndClient, &rect );
if( rect.bottom - rect.top - delta >= spacing )
rect.bottom -= delta;
nstagger = (rect.bottom - rect.top)/(3 * spacing);
lpPos[1].x = (rect.right - rect.left - nstagger * spacing);
lpPos[1].y = (rect.bottom - rect.top - nstagger * spacing);
lpPos[0].x = lpPos[0].y = spacing * (total%(nstagger+1));
}
/**********************************************************************
* MDISetMenu
*/
static LRESULT MDISetMenu( HWND hwnd, HMENU hmenuFrame,
HMENU hmenuWindow)
{
MDICLIENTINFO *ci;
HWND hwndFrame = GetParent(hwnd);
TRACE("%p %p %p\n", hwnd, hmenuFrame, hmenuWindow);
if (hmenuFrame && !IsMenu(hmenuFrame))
{
WARN("hmenuFrame is not a menu handle\n");
return 0L;
}
if (hmenuWindow && !IsMenu(hmenuWindow))
{
WARN("hmenuWindow is not a menu handle\n");
return 0L;
}
if (!(ci = get_client_info( hwnd ))) return 0;
if (hmenuFrame)
{
if (hmenuFrame == ci->hFrameMenu) return (LRESULT)hmenuFrame;
if (IsZoomed(ci->hwndActiveChild))
MDI_RestoreFrameMenu( hwndFrame, ci->hwndActiveChild, ci->hBmpClose );
}
if( hmenuWindow && hmenuWindow != ci->hWindowMenu )
{
/* delete menu items from ci->hWindowMenu
* and add them to hmenuWindow */
/* Agent newsreader calls this function with ci->hWindowMenu == NULL */
if( ci->hWindowMenu && ci->nActiveChildren )
{
UINT nActiveChildren_old = ci->nActiveChildren;
/* Remove all items from old Window menu */
ci->nActiveChildren = 0;
MDI_RefreshMenu(ci);
ci->hWindowMenu = hmenuWindow;
/* Add items to the new Window menu */
ci->nActiveChildren = nActiveChildren_old;
MDI_RefreshMenu(ci);
}
else
ci->hWindowMenu = hmenuWindow;
}
if (hmenuFrame)
{
SetMenu(hwndFrame, hmenuFrame);
if( hmenuFrame != ci->hFrameMenu )
{
HMENU oldFrameMenu = ci->hFrameMenu;
ci->hFrameMenu = hmenuFrame;
if (IsZoomed(ci->hwndActiveChild) && (GetWindowLongW(ci->hwndActiveChild, GWL_STYLE) & WS_VISIBLE))
MDI_AugmentFrameMenu( hwndFrame, ci->hwndActiveChild );
return (LRESULT)oldFrameMenu;
}
}
else
{
/* SetMenu() may already have been called, meaning that this window
* already has its menu. But they may have done a SetMenu() on
* an MDI window, and called MDISetMenu() after the fact, meaning
* that the "if" to this "else" wouldn't catch the need to
* augment the frame menu.
*/
if( IsZoomed(ci->hwndActiveChild) )
MDI_AugmentFrameMenu( hwndFrame, ci->hwndActiveChild );
}
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -