/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// If called with UpdateFrameGeometry, it must be called after it, as UpdateFrameGeometry // updates the geometry depending on the old state! void WinSalFrame::UpdateFrameState()
{ // don't overwrite restore state in fullscreen mode if (isFullScreen()) return;
// if pParentRect is set, the workarea of the monitor that contains pParentRect is returned void ImplSalGetWorkArea( HWND hWnd, RECT *pRect, const RECT *pParentRect )
{ if (Application::IsHeadlessModeEnabled()) {
pRect->left = 0;
pRect->top = 0;
pRect->right = VIRTUAL_DESKTOP_WIDTH;
pRect->bottom = VIRTUAL_DESKTOP_HEIGHT; return;
} // check if we or our parent is fullscreen, then the taskbar should be ignored bool bIgnoreTaskbar = false;
WinSalFrame* pFrame = GetWindowPtr( hWnd ); if( pFrame )
{
vcl::Window *pWin = pFrame->GetWindow(); while( pWin )
{
WorkWindow *pWorkWin = (pWin->GetType() == WindowType::WORKWINDOW) ? static_cast<WorkWindow *>(pWin) : nullptr; if( pWorkWin && pWorkWin->ImplGetWindowImpl()->mbReallyVisible && pWorkWin->IsFullScreenMode() )
{
bIgnoreTaskbar = true; break;
} else
pWin = pWin->ImplGetWindowImpl()->mpParent;
}
}
// calculates the work area taking multiple monitors into account staticint nMonitors = GetSystemMetrics( SM_CMONITORS ); if( nMonitors == 1 )
{ if( bIgnoreTaskbar )
{
pRect->left = pRect->top = 0;
pRect->right = GetSystemMetrics( SM_CXSCREEN );
pRect->bottom = GetSystemMetrics( SM_CYSCREEN );
} else
SystemParametersInfoW( SPI_GETWORKAREA, 0, pRect, 0 );
} else
{ if( pParentRect != nullptr )
{ // return the size of the monitor where pParentRect lives
HMONITOR hMonitor;
MONITORINFO mi;
// get the nearest monitor to the passed rect.
hMonitor = MonitorFromRect(pParentRect, MONITOR_DEFAULTTONEAREST);
// get the work area or entire monitor rect.
mi.cbSize = sizeof(mi);
GetMonitorInfo(hMonitor, &mi); if( !bIgnoreTaskbar )
*pRect = mi.rcWork; else
*pRect = mi.rcMonitor;
} else
{ // return the union of all monitors
pRect->left = GetSystemMetrics( SM_XVIRTUALSCREEN );
pRect->top = GetSystemMetrics( SM_YVIRTUALSCREEN );
pRect->right = pRect->left + GetSystemMetrics( SM_CXVIRTUALSCREEN );
pRect->bottom = pRect->top + GetSystemMetrics( SM_CYVIRTUALSCREEN );
// virtualscreen does not take taskbar into account, so use the corresponding // diffs between screen and workarea from the default screen // however, this is still not perfect: the taskbar might not be on the primary screen if( !bIgnoreTaskbar )
{
RECT wRect, scrRect;
SystemParametersInfoW( SPI_GETWORKAREA, 0, &wRect, 0 );
scrRect.left = 0;
scrRect.top = 0;
scrRect.right = GetSystemMetrics( SM_CXSCREEN );
scrRect.bottom = GetSystemMetrics( SM_CYSCREEN );
// determine creation data if ( nSalFrameStyle & (SalFrameStyleFlags::PLUG | SalFrameStyleFlags::SYSTEMCHILD) )
{
nSysStyle |= WS_CHILD; if( nSalFrameStyle & SalFrameStyleFlags::SYSTEMCHILD )
nSysStyle |= WS_CLIPSIBLINGS;
} else
{ // #i87402# commenting out WS_CLIPCHILDREN // this breaks SalFrameStyleFlags::SYSTEMCHILD handling, which is not // used currently. Probably SalFrameStyleFlags::SYSTEMCHILD should be // removed again.
// nSysStyle |= WS_CLIPCHILDREN; if ( hWndParent )
{
nSysStyle |= WS_POPUP;
bSubFrame = true;
pFrame->mbNoIcon = true;
} else
{ // Only with WS_OVERLAPPED we get a useful default position/size if ( (nSalFrameStyle & (SalFrameStyleFlags::SIZEABLE | SalFrameStyleFlags::MOVEABLE)) ==
(SalFrameStyleFlags::SIZEABLE | SalFrameStyleFlags::MOVEABLE) )
nSysStyle |= WS_OVERLAPPED; else
{
nSysStyle |= WS_POPUP; if ( !(nSalFrameStyle & SalFrameStyleFlags::MOVEABLE) )
nExSysStyle |= WS_EX_TOOLWINDOW; // avoid taskbar appearance, for eg splash screen
}
}
if ( nSalFrameStyle & SalFrameStyleFlags::DEFAULT )
nExSysStyle |= WS_EX_APPWINDOW;
} if( nSalFrameStyle & SalFrameStyleFlags::TOOLWINDOW // #100656# toolwindows lead to bad alt-tab behaviour, if they have the focus // you must press it twice to leave the application // so toolwindows are only used for non sizeable windows // which are typically small, so a small caption makes sense
if( pFrame->mnShowState == SW_SHOWMAXIMIZED )
{ // #96084 set a useful internal window size because // the window will not be maximized (and the size updated) before show()
pFrame->SetMaximizedFrameGeometry(hWnd);
}
return pFrame;
}
// helper that only creates the HWND // to allow for easy reparenting of system windows, (i.e. destroy and create new)
HWND ImplSalReCreateHWND( HWND hWndParent, HWND oldhWnd, bool bAsChild )
{
HINSTANCE hInstance = GetSalData()->mhInst;
sal_uLong nSysStyle = GetWindowLongW( oldhWnd, GWL_STYLE );
sal_uLong nExSysStyle = GetWindowLongW( oldhWnd, GWL_EXSTYLE );
staticvoid ImplSalCalcFullScreenSize( const WinSalFrame* pFrame, int& rX, int& rY, int& rDX, int& rDY )
{ // set window to screen size int nFrameX; int nFrameY; int nCaptionY; int nScreenX = 0; int nScreenY = 0; int nScreenDX = 0; int nScreenDY = 0;
// get data, when making 1st frame if ( !pSalData->mpFirstFrame )
{ if ( !aSalShlData.mnWheelScrollLines )
aSalShlData.mnWheelScrollLines = ImplSalGetWheelScrollLines(); if ( !aSalShlData.mnWheelScrollChars )
aSalShlData.mnWheelScrollChars = ImplSalGetWheelScrollChars();
}
// Other threads get an own DC, because Windows modify in the // other case our DC (changing clip region), when they send a // WM_ERASEBACKGROUND message if ( !pSalData->mpInstance->IsMainThread() )
{
HDC hDC = reinterpret_cast<HDC>(static_cast<sal_IntPtr>(pSalData->mpInstance->SendComWndMessage(
SAL_MSG_GETCACHEDDC, reinterpret_cast<WPARAM>(mhWnd), 0 ))); if ( !hDC ) return nullptr;
if ( !mpThreadGraphics )
mpThreadGraphics = new WinSalGraphics(WinSalGraphics::WINDOW, true, mhWnd, this);
assert(!mpThreadGraphics->getHDC() && "this is supposed to be zeroed when ReleaseGraphics is called");
mpThreadGraphics->setHDC( hDC );
void WinSalFrame::SetIcon( sal_uInt16 nIcon )
{ // If we have a window without an Icon (for example a dialog), ignore this call if ( mbNoIcon ) return;
// 0 means default (class) icon
HICON hIcon = nullptr, hSmIcon = nullptr; if ( !nIcon )
nIcon = 1;
ImplLoadSalIcon( nIcon, hIcon, hSmIcon );
SAL_WARN_IF( !hIcon , "vcl", "WinSalFrame::SetIcon(): Could not load large icon !" );
SAL_WARN_IF( !hSmIcon , "vcl", "WinSalFrame::SetIcon(): Could not load small icon !");
if (pFrame->mbFloatWin && !(pFrame->mnStyle & SalFrameStyleFlags::NOSHADOW))
{ // erase the window immediately to improve XP shadow effect // otherwise the shadow may appears long time before the rest of the window // especially when accessibility is on
HDC dc = GetDC( hWnd );
RECT aRect;
GetClientRect( hWnd, &aRect );
FillRect( dc, &aRect, reinterpret_cast<HBRUSH>(COLOR_MENU+1) ); // choose the menucolor, because its mostly noticeable for menus
ReleaseDC( hWnd, dc );
}
// Direct Paint only, if we get the SolarMutex if ( ImplSalYieldMutexTryToAcquire() )
{
UpdateWindow( hWnd );
ImplSalYieldMutexRelease();
}
} else
{
ShowWindow( hWnd, SW_HIDE );
}
}
void WinSalFrame::Show( bool bVisible, bool bNoActivate )
{ // Post this Message to the window, because this only works // in the thread of the window, which has create this window. // We post this message to avoid deadlocks if ( GetSalData()->mnAppThreadId != GetCurrentThreadId() )
{ boolconst ret = PostMessageW(mhWnd, SAL_MSG_SHOW, WPARAM(bVisible), LPARAM(bNoActivate));
SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
} else
ImplSalShow( mhWnd, bVisible, bNoActivate );
}
HWND hWndParent = ImplGetParentHwnd(mhWnd); // For dialogs (WS_POPUP && WS_DLGFRAME), we need to find the "real" parent, // in case multiple dialogs are stacked on each other // (we don't want to position the second dialog relative to the first one, but relative to the main window) if ( (GetWindowStyle( mhWnd ) & WS_POPUP) && (GetWindowStyle( mhWnd ) & WS_DLGFRAME) ) // mhWnd is a dialog
{ while ( hWndParent && (GetWindowStyle( hWndParent ) & WS_POPUP) && (GetWindowStyle( hWndParent ) & WS_DLGFRAME) )
{
hWndParent = ::ImplGetParentHwnd( hWndParent );
}
}
//#110386#, do not transform coordinates for system child windows if( !(GetWindowStyle( mhWnd ) & WS_CHILD) )
{
POINT aPt;
aPt.x = nX;
aPt.y = nY;
WinSalFrame* pParentFrame = GetWindowPtr( hWndParent ); if ( pParentFrame && pParentFrame->mnShowState == SW_SHOWMAXIMIZED )
{ // #i42485#: parent will be shown maximized in which case // a ClientToScreen uses the wrong coordinates (i.e. those from the restore pos) // so use the (already updated) frame geometry for the transformation
aPt.x += pParentFrame->maGeometry.x();
aPt.y += pParentFrame->maGeometry.y();
} else
ClientToScreen( hWndParent, &aPt );
nX = aPt.x;
nY = aPt.y;
// the position is set
mbDefPos = false;
}
}
// #i3338# to be conformant to UNIX we must position the client window, ie without the decoration // #i43250# if the position was read from the system (GetWindowRect(), see above), it must not be modified if ( nFlags & SAL_FRAME_POSSIZE_X )
nX += aWinRect.left; if ( nFlags & SAL_FRAME_POSSIZE_Y )
nY += aWinRect.top;
int nScreenX; int nScreenY; int nScreenWidth; int nScreenHeight;
if ( mbDefPos && (nPosSize & SWP_NOMOVE)) // we got no positioning request, so choose default position
{ // center window
HWND hWndParent2 = ::GetParent( mhWnd ); // Search for TopLevel Frame while ( hWndParent2 && (GetWindowStyle( hWndParent2 ) & WS_CHILD) )
hWndParent2 = ::GetParent( hWndParent2 ); // if the Window has a Parent, then center the window to // the parent, in the other case to the screen if ( hWndParent2 && !IsIconic( hWndParent2 ) &&
(GetWindowStyle( hWndParent2 ) & WS_VISIBLE) )
{
RECT aParentRect;
GetWindowRect( hWndParent2, &aParentRect ); int nParentWidth = aParentRect.right-aParentRect.left; int nParentHeight = aParentRect.bottom-aParentRect.top;
// We don't center, when Parent is smaller than our window if ( (nParentWidth-GetSystemMetrics( SM_CXFIXEDFRAME ) <= nWidth) &&
(nParentHeight-GetSystemMetrics( SM_CYFIXEDFRAME ) <= nHeight) )
{ int nOff = GetSystemMetrics( SM_CYSIZEFRAME ) + GetSystemMetrics( SM_CYCAPTION );
nX = aParentRect.left+nOff;
nY = aParentRect.top+nOff;
} else
{
nX = (nParentWidth-nWidth)/2 + aParentRect.left;
nY = (nParentHeight-nHeight)/2 + aParentRect.top;
}
} else
{
POINT pt;
GetCursorPos( &pt );
RECT aRect2;
aRect2.left = pt.x;
aRect2.top = pt.y;
aRect2.right = pt.x+2;
aRect2.bottom = pt.y+2;
// dualmonitor support: // Get screensize of the monitor with the mouse pointer
ImplSalGetWorkArea( mhWnd, &aRect2, &aRect2 );
mbDefPos = false; // center only once
nPosSize &= ~SWP_NOMOVE; // activate positioning
nEvent = SalEvent::MoveResize;
}
// Adjust Window in the screen bool bCheckOffScreen = true;
// but don't do this for floaters or ownerdraw windows that are currently moved interactively if( (mnStyle & SalFrameStyleFlags::FLOAT) && !(mnStyle & SalFrameStyleFlags::OWNERDRAWDECORATION) )
bCheckOffScreen = false;
if( mnStyle & SalFrameStyleFlags::OWNERDRAWDECORATION )
{ // may be the window is currently being moved (mouse is captured), then no check is required if( mhWnd == ::GetCapture() )
bCheckOffScreen = false; else
bCheckOffScreen = true;
}
if( bCheckOffScreen )
{ if ( nX+nWidth > nScreenX+nScreenWidth )
nX = (nScreenX+nScreenWidth) - nWidth; if ( nY+nHeight > nScreenY+nScreenHeight )
nY = (nScreenY+nScreenHeight) - nHeight; if ( nX < nScreenX )
nX = nScreenX; if ( nY < nScreenY )
nY = nScreenY;
}
UINT nPosFlags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | nPosSize; // bring floating windows always to top if( !(mnStyle & SalFrameStyleFlags::FLOAT) )
nPosFlags |= SWP_NOZORDER; // do not change z-order
// release the local DC if ( mpLocalGraphics )
{
bHadLocalGraphics = true;
HDC hDC = mpLocalGraphics->getHDC();
mpLocalGraphics->setHDC(nullptr);
ReleaseDC(mhWnd, hDC);
}
// create a new hwnd with the same styles
HWND hWndParent = hNewParentWnd; // forward to main thread
HWND hWnd = reinterpret_cast<HWND>(static_cast<sal_IntPtr>(pSalData->mpInstance->SendComWndMessage(
bAsChild ? SAL_MSG_RECREATECHILDHWND : SAL_MSG_RECREATEHWND, reinterpret_cast<WPARAM>(hWndParent), reinterpret_cast<LPARAM>(mhWnd) )));
// re-create local DC if( bHadLocalGraphics )
{
mpLocalGraphics->setHWND( hWnd );
HDC hDC = GetDC( hWnd ); if (hDC)
mpLocalGraphics->setHDC( hDC );
}
// TODO: add SetParent() call for SalObjects
SAL_WARN_IF( !systemChildren.empty(), "vcl", "WinSalFrame::SetParent() parent of living system child window will be destroyed!");
// reparent children before old parent is destroyed for (auto & child : children)
child->ImplSetParentFrame( hWnd, false );
children.clear();
systemChildren.clear();
// Now destroy original HWND in the thread where it was created.
pSalData->mpInstance->SendComWndMessage(
SAL_MSG_DESTROYHWND, WPARAM(0), reinterpret_cast<LPARAM>(hWndOld));
}
void WinSalFrame::GetWorkArea( AbsoluteScreenPixelRectangle &rRect )
{
RECT aRect;
// pass cursor's position to ImplSalGetWorkArea to determine work area on // multi monitor setups correctly.
POINT aPoint;
GetCursorPos(&aPoint);
RECT aRect2{ aPoint.x, aPoint.y, aPoint.x + 2, aPoint.y + 2 };
void WinSalFrame::SetWindowState(const vcl::WindowData* pState)
{ // Check if the window fits into the screen, in case the screen // resolution changed int nX; int nY; int nWidth; int nHeight; int nScreenX; int nScreenY; int nScreenWidth; int nScreenHeight;
RECT aRect;
ImplSalGetWorkArea( mhWnd, &aRect, nullptr ); // #102500# allow some overlap, the window could have been made a little larger than the physical screen
nScreenX = aRect.left-10;
nScreenY = aRect.top-10;
nScreenWidth = aRect.right-aRect.left+20;
nScreenHeight = aRect.bottom-aRect.top+20;
UINT nPosSize = 0;
RECT aWinRect;
GetWindowRect( mhWnd, &aWinRect );
// to be consistent with Unix, the frame state is without(!) decoration // ->add the decoration
RECT aRect2 = aWinRect;
AdjustWindowRectEx( &aRect2, GetWindowStyle( mhWnd ), FALSE, GetWindowExStyle( mhWnd ) );
tools::Long nTopDeco = abs( aWinRect.top - aRect2.top );
tools::Long nLeftDeco = abs( aWinRect.left - aRect2.left );
tools::Long nBottomDeco = abs( aWinRect.bottom - aRect2.bottom );
tools::Long nRightDeco = abs( aWinRect.right - aRect2.right );
// adjust window position/size to fit the screen if ( !(pState->mask() & vcl::WindowDataMask::Pos) )
nPosSize |= SWP_NOMOVE; if ( !(pState->mask() & vcl::WindowDataMask::Size) )
nPosSize |= SWP_NOSIZE; if ( pState->mask() & vcl::WindowDataMask::X )
nX = static_cast<int>(pState->x()) - nLeftDeco; else
nX = aWinRect.left; if ( pState->mask() & vcl::WindowDataMask::Y )
nY = static_cast<int>(pState->y()) - nTopDeco; else
nY = aWinRect.top; if ( pState->mask() & vcl::WindowDataMask::Width )
nWidth = static_cast<int>(pState->width()) + nLeftDeco + nRightDeco; else
nWidth = aWinRect.right-aWinRect.left; if ( pState->mask() & vcl::WindowDataMask::Height )
nHeight = static_cast<int>(pState->height()) + nTopDeco + nBottomDeco; else
nHeight = aWinRect.bottom-aWinRect.top;
// Adjust Window in the screen: // if it does not fit into the screen do nothing, ie default pos/size will be used // if there is an overlap with the screen border move the window while keeping its size
// if a window is neither minimized nor maximized or need not be // positioned visibly (that is in visible state), do not use // SetWindowPlacement since it calculates including the TaskBar if (!bIsMinimized && !bIsMaximized && (!bVisible || (aPlacement.showCmd == SW_RESTORE)))
{ if( bUpdateHiddenFramePos )
{
RECT aStateRect;
aStateRect.left = nX;
aStateRect.top = nY;
aStateRect.right = nX+nWidth;
aStateRect.bottom = nY+nHeight; // #96084 set a useful internal window size because // the window will not be maximized (and the size updated) before show()
SetMaximizedFrameGeometry(mhWnd, &aStateRect);
SetWindowPos( mhWnd, nullptr,
maGeometry.x(), maGeometry.y(), maGeometry.width(), maGeometry.height(),
SWP_NOZORDER | SWP_NOACTIVATE | nPosSize );
} else
SetWindowPos( mhWnd, nullptr,
nX, nY, nWidth, nHeight,
SWP_NOZORDER | SWP_NOACTIVATE | nPosSize );
} else
{ if( !(nPosSize & (SWP_NOMOVE|SWP_NOSIZE)) )
{
aPlacement.rcNormalPosition.left = nX-nScreenX;
aPlacement.rcNormalPosition.top = nY-nScreenY;
aPlacement.rcNormalPosition.right = nX+nWidth-nScreenX;
aPlacement.rcNormalPosition.bottom = nY+nHeight-nScreenY;
}
SetWindowPlacement( mhWnd, &aPlacement );
}