/* $Id: win32wbasepos.cpp,v 1.31 2003-05-15 12:40:20 sandervl Exp $ */ /* * Win32 Window Base Class for OS/2 (nonclient/position methods) * * Copyright 1998-1999 Sander van Leeuwen (sandervl@xs4all.nl) * Copyright 1999 Daniela Engert (dani@ngrt.de) * * Parts based on Wine Windows code (windows\win.c, windows\nonclient.c) * * Copyright 1993, 1994 Alexandre Julliard * * TODO: Not thread/process safe * * Wine code based on build 990815 * * Project Odin Software License can be found in LICENSE.TXT * */ #include #include #include #include #include #include #include #include "win32wbase.h" #include #include "wndmsg.h" #include "oslibwin.h" #include "oslibutil.h" #include "oslibgdi.h" #include "oslibres.h" #include "oslibdos.h" #include "syscolor.h" #include "win32wndhandle.h" #include "dc.h" #include "win32wdesktop.h" #include #define DBG_LOCALLOG DBG_win32wbasepos #include "dbglocal.h" /******************************************************************* * GetMinMaxInfo * * Get the minimized and maximized information for a window. */ void Win32BaseWindow::GetMinMaxInfo(POINT *maxSize, POINT *maxPos, POINT *minTrack, POINT *maxTrack ) { MINMAXINFO MinMax; INT xinc, yinc; /* Compute default values */ MinMax.ptMaxPosition.x = 0; MinMax.ptMaxPosition.y = 0; if(!(getStyle() & (WS_POPUP | WS_CHILD))) { RECT rect; OSLibWinGetMaxPosition(getOS2FrameWindowHandle(), &rect); MinMax.ptMaxPosition.x = rect.left; MinMax.ptMaxPosition.y = rect.top; MinMax.ptMaxSize.x = rect.right - rect.left; MinMax.ptMaxSize.y = rect.bottom - rect.top; MinMax.ptMinTrackSize.x = GetSystemMetrics(SM_CXMINTRACK); MinMax.ptMinTrackSize.y = GetSystemMetrics(SM_CYMINTRACK); MinMax.ptMaxTrackSize.x = GetSystemMetrics(SM_CXSCREEN); MinMax.ptMaxTrackSize.y = GetSystemMetrics(SM_CYSCREEN); } else { MinMax.ptMaxSize.x = GetSystemMetrics(SM_CXSCREEN); MinMax.ptMaxSize.y = GetSystemMetrics(SM_CYSCREEN); MinMax.ptMinTrackSize.x = GetSystemMetrics(SM_CXMINTRACK); MinMax.ptMinTrackSize.y = GetSystemMetrics(SM_CYMINTRACK); MinMax.ptMaxTrackSize.x = GetSystemMetrics(SM_CXSCREEN); MinMax.ptMaxTrackSize.y = GetSystemMetrics(SM_CYSCREEN); } if (HAS_DLGFRAME( dwStyle, dwExStyle )) { xinc = GetSystemMetrics(SM_CXDLGFRAME); yinc = GetSystemMetrics(SM_CYDLGFRAME); } else { xinc = yinc = 0; if (HAS_THICKFRAME(dwStyle, dwExStyle)) { xinc += GetSystemMetrics(SM_CXFRAME); yinc += GetSystemMetrics(SM_CYFRAME); } //SvL: Wine has no 'else', but I'm seeing different behaviour in NT // and it doesn't make much sense either as a window can have // only one kind of border (see drawing code) else if (dwStyle & WS_BORDER) { xinc += GetSystemMetrics(SM_CXBORDER); yinc += GetSystemMetrics(SM_CYBORDER); } } MinMax.ptMaxSize.x += 2 * xinc; MinMax.ptMaxSize.y += 2 * yinc; #if 0 lpPos = (LPINTERNALPOS)GetPropA( hwndSelf, atomInternalPos ); if( lpPos && !EMPTYPOINT(lpPos->ptMaxPos) ) CONV_POINT16TO32( &lpPos->ptMaxPos, &MinMax.ptMaxPosition ); else { #endif MinMax.ptMaxPosition.x -= xinc; MinMax.ptMaxPosition.y -= yinc; // } SendMessageA(getWindowHandle(), WM_GETMINMAXINFO, 0, (LPARAM)&MinMax ); /* Some sanity checks */ dprintf(("GetMinMaxInfo: %ld %ld / %ld %ld / %ld %ld / %ld %ld\n", MinMax.ptMaxSize.x, MinMax.ptMaxSize.y, MinMax.ptMaxPosition.x, MinMax.ptMaxPosition.y, MinMax.ptMaxTrackSize.x, MinMax.ptMaxTrackSize.y, MinMax.ptMinTrackSize.x, MinMax.ptMinTrackSize.y)); MinMax.ptMaxTrackSize.x = MAX( MinMax.ptMaxTrackSize.x, MinMax.ptMinTrackSize.x ); MinMax.ptMaxTrackSize.y = MAX( MinMax.ptMaxTrackSize.y, MinMax.ptMinTrackSize.y ); if (maxSize) *maxSize = MinMax.ptMaxSize; if (maxPos) *maxPos = MinMax.ptMaxPosition; if (minTrack) *minTrack = MinMax.ptMinTrackSize; if (maxTrack) *maxTrack = MinMax.ptMaxTrackSize; } /*********************************************************************** * WINPOS_SendNCCalcSize * * Send a WM_NCCALCSIZE message to a window. * All parameters are read-only except newClientRect. * oldWindowRect, oldClientRect and winpos must be non-NULL only * when calcValidRect is TRUE. */ LONG Win32BaseWindow::SendNCCalcSize(BOOL calcValidRect, RECT *newWindowRect, RECT *oldWindowRect, RECT *oldClientRect, WINDOWPOS *winpos, RECT *newClientRect ) { NCCALCSIZE_PARAMS params; WINDOWPOS winposCopy; LONG result = 0; /* Send WM_NCCALCSIZE message to get new client area */ params.rgrc[0] = *newWindowRect; if(calcValidRect) { winposCopy = *winpos; params.rgrc[1] = *oldWindowRect; params.rgrc[2] = *oldClientRect; //client rectangel must be in parent coordinates OffsetRect(¶ms.rgrc[2], rectWindow.left, rectWindow.top); params.lppos = &winposCopy; } result = SendMessageA(getWindowHandle(), WM_NCCALCSIZE, calcValidRect, (LPARAM)¶ms ); /* If the application send back garbage, ignore it */ if(params.rgrc[0].left <= params.rgrc[0].right && params.rgrc[0].top <= params.rgrc[0].bottom) { *newClientRect = params.rgrc[0]; //client rectangle now in parent coordinates; convert to 'frame' coordinates OffsetRect(newClientRect, -rectWindow.left, -rectWindow.top); } /* FIXME: WVR_ALIGNxxx */ if(newClientRect->left != rectClient.left || newClientRect->top != rectClient.top) winpos->flags &= ~SWP_NOCLIENTMOVE; if((newClientRect->right - newClientRect->left != rectClient.right - rectClient.left) || (newClientRect->bottom - newClientRect->top != rectClient.bottom - rectClient.top)) winpos->flags &= ~SWP_NOCLIENTSIZE; return result; } /*********************************************************************** * WINPOS_HandleWindowPosChanging * * Default handling for a WM_WINDOWPOSCHANGING. Called from DefWindowProc(). */ LONG Win32BaseWindow::HandleWindowPosChanging(WINDOWPOS *winpos) { POINT maxSize, maxTrack, minTrack; if (winpos->flags & SWP_NOSIZE) return 0; if ((dwStyle & WS_THICKFRAME) || ((dwStyle & (WS_POPUP | WS_CHILD)) == 0)) { GetMinMaxInfo( &maxSize, NULL, &minTrack, &maxTrack ); winpos->cx = MIN( winpos->cx, MAX( maxSize.x, maxTrack.x) ); winpos->cy = MIN( winpos->cy, MAX( maxSize.y, maxTrack.y) ); if (!(dwStyle & WS_MINIMIZE)) { if (winpos->cx < minTrack.x ) winpos->cx = minTrack.x; if (winpos->cy < minTrack.y ) winpos->cy = minTrack.y; } } return 0; } /*********************************************************************** * WINPOS_FindIconPos * * Find a suitable place for an iconic window. */ static void WINPOS_FindIconPos( HWND hwnd, POINT &pt ) { RECT rectParent; int x, y, xspacing, yspacing; HWND hwndChild, hwndParent; hwndParent = GetParent(hwnd); if(hwndParent == 0) { dprintf(("WINPOS_FindIconPos: no parent found for window %x", hwnd)); return; } GetClientRect(hwndParent, &rectParent ); if ((pt.x >= rectParent.left) && (pt.x + GetSystemMetrics(SM_CXICON) < rectParent.right) && (pt.y >= rectParent.top) && (pt.y + GetSystemMetrics(SM_CYICON) < rectParent.bottom)) return; /* The icon already has a suitable position */ xspacing = GetSystemMetrics(SM_CXICONSPACING); yspacing = GetSystemMetrics(SM_CYICONSPACING); y = rectParent.bottom; for (;;) { x = rectParent.left; do { /* Check if another icon already occupies this spot */ hwndChild = GetWindow(hwndParent, GW_CHILD); while(hwndChild) { Win32BaseWindow *child = NULL; RECT *pRectWindow; child = Win32BaseWindow::GetWindowFromHandle(hwndChild); if(!child) { dprintf(("ERROR: WINPOS_FindIconPos, child %x not found", hwndChild)); return; } if ((child->getStyle() & WS_MINIMIZE) && (child->getWindowHandle() != hwnd)) { pRectWindow = child->getWindowRect(); if ((pRectWindow->left < x + xspacing) && (pRectWindow->right >= x) && (pRectWindow->top <= y) && (pRectWindow->bottom > y - yspacing)) break; /* There's a window in there */ } RELEASE_WNDOBJ(child); hwndChild = GetWindow(hwndChild, GW_HWNDNEXT); } if (!hwndChild) /* No window was found, so it's OK for us */ { pt.x = x + (xspacing - GetSystemMetrics(SM_CXICON)) / 2; pt.y = y - (yspacing + GetSystemMetrics(SM_CYICON)) / 2; return; } x += xspacing; } while(x <= rectParent.right-xspacing); y -= yspacing; } } /****************************************************************************** * WINPOS_MinMaximize * * Fill in lpRect and return additional flags to be used with SetWindowPos(). * This function assumes that 'cmd' is different from the current window * state. */ UINT Win32BaseWindow::MinMaximize(UINT cmd, LPRECT lpRect) { UINT swpFlags = 0; POINT size, iconPos; size.x = rectWindow.left; size.y = rectWindow.top; if(IsRectEmpty(&windowpos.rcNormalPosition)) { CopyRect(&windowpos.rcNormalPosition, &rectWindow); } if(!HOOK_CallHooksA(WH_CBT, HCBT_MINMAX, getWindowHandle(), cmd)) { if(getStyle() & WS_MINIMIZE ) { if(!SendMessageA(getWindowHandle(), WM_QUERYOPEN, 0, 0L)) return (SWP_NOSIZE | SWP_NOMOVE); } switch( cmd ) { case SW_MINIMIZE: if( getStyle() & WS_MAXIMIZE) { setFlags(getFlags() | WIN_RESTORE_MAX); setStyle(getStyle() & ~WS_MAXIMIZE); } else setFlags(getFlags() & ~WIN_RESTORE_MAX); setStyle(getStyle() | WS_MINIMIZE); if(getParent() == NULL) { // @@PF : for now disable windows style - it messes with WV minimize - will fix it soon // SetRect(lpRect, -32000, -32000, getWindowWidth(), getWindowHeight()); // OSLibSetWindowStyle(getOS2FrameWindowHandle(), getOS2WindowHandle(), getStyle(), getExStyle()); } else { iconPos.x = windowpos.ptMinPosition.x; iconPos.y = windowpos.ptMinPosition.y; WINPOS_FindIconPos(getWindowHandle(), iconPos); SetRect(lpRect, iconPos.x, iconPos.y, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON) ); } break; case SW_MAXIMIZE: GetMinMaxInfo(&size, &windowpos.ptMaxPosition, NULL, NULL ); if(getStyle() & WS_MINIMIZE ) { setStyle(getStyle() & ~WS_MINIMIZE); OSLibSetWindowStyle(getOS2FrameWindowHandle(), getOS2WindowHandle(), getStyle(), getExStyle(), getStyle()); } setStyle(getStyle() | WS_MAXIMIZE); SetRect(lpRect, windowpos.ptMaxPosition.x, windowpos.ptMaxPosition.y, size.x, size.y ); break; case SW_RESTORE: if(getStyle() & WS_MINIMIZE) { setStyle(getStyle() & ~WS_MINIMIZE); OSLibSetWindowStyle(getOS2FrameWindowHandle(), getOS2WindowHandle(), getStyle(), getExStyle(), getStyle()); if( getFlags() & WIN_RESTORE_MAX) { /* Restore to maximized position */ GetMinMaxInfo(&size, &windowpos.ptMaxPosition, NULL, NULL); setStyle(getStyle() | WS_MAXIMIZE); SetRect(lpRect, windowpos.ptMaxPosition.x, windowpos.ptMaxPosition.y, size.x, size.y); break; } } else if( !(getStyle() & WS_MAXIMIZE) ) return 0; else setStyle(getStyle() & ~WS_MAXIMIZE); /* Restore to normal position */ *lpRect = windowpos.rcNormalPosition; lpRect->right -= lpRect->left; lpRect->bottom -= lpRect->top; break; } } else swpFlags |= SWP_NOSIZE | SWP_NOMOVE; return swpFlags; } /*********************************************************************** * WIN_FixCoordinates * * Fix the coordinates - Helper for WIN_CreateWindowEx. * returns default show mode in sw. * Note: the feature presented as undocumented *is* in the MSDN since 1993. */ void Win32BaseWindow::FixCoordinates( CREATESTRUCTA *cs, INT *sw) { if (cs->x == CW_USEDEFAULT || cs->x == CW_USEDEFAULT16 || cs->cx == CW_USEDEFAULT || cs->cx == CW_USEDEFAULT16) { if (cs->style & (WS_CHILD | WS_POPUP)) { if (cs->x == CW_USEDEFAULT || cs->x == CW_USEDEFAULT16) cs->x = cs->y = 0; if (cs->cx == CW_USEDEFAULT || cs->cx == CW_USEDEFAULT16) cs->cx = cs->cy = 0; } else /* overlapped window */ { STARTUPINFOA info; GetStartupInfoA( &info ); if (cs->x == CW_USEDEFAULT || cs->x == CW_USEDEFAULT16) { /* Never believe Microsoft's documentation... CreateWindowEx doc says * that if an overlapped window is created with WS_VISIBLE style bit * set and the x parameter is set to CW_USEDEFAULT, the system ignores * the y parameter. However, disassembling NT implementation (WIN32K.SYS) * reveals that * * 1) not only it checks for CW_USEDEFAULT but also for CW_USEDEFAULT16 * 2) it does not ignore the y parameter as the docs claim; instead, it * uses it as second parameter to ShowWindow() unless y is either * CW_USEDEFAULT or CW_USEDEFAULT16. * * The fact that we didn't do 2) caused bogus windows pop up when wine * was running apps that were using this obscure feature. Example - * calc.exe that comes with Win98 (only Win98, it's different from * the one that comes with Win95 and NT) */ if (cs->y != CW_USEDEFAULT && cs->y != CW_USEDEFAULT16) *sw = cs->y; cs->x = (info.dwFlags & STARTF_USEPOSITION) ? info.dwX : 0; cs->y = (info.dwFlags & STARTF_USEPOSITION) ? info.dwY : 0; } if (cs->cx == CW_USEDEFAULT || cs->cx == CW_USEDEFAULT16) { if (info.dwFlags & STARTF_USESIZE) { cs->cx = info.dwXSize; cs->cy = info.dwYSize; } else /* if no other hint from the app, pick 3/4 of the screen real estate */ { RECT r; SystemParametersInfoA( SPI_GETWORKAREA, 0, &r, 0); cs->cx = (((r.right - r.left) * 3) / 4) - cs->x; cs->cy = (((r.bottom - r.top) * 3) / 4) - cs->y; } } } } else { /* neither x nor cx are default. Check the y values . * In the trace we see Outlook and Outlook Express using * cy set to CW_USEDEFAULT when opening the address book. */ if (cs->cy == CW_USEDEFAULT || cs->cy == CW_USEDEFAULT16) { RECT r; dprintf(("Strange use of CW_USEDEFAULT in nHeight\n")); SystemParametersInfoA( SPI_GETWORKAREA, 0, &r, 0); cs->cy = (((r.bottom - r.top) * 3) / 4) - cs->y; } } } //****************************************************************************** //******************************************************************************