/* -*- 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 );
}
IPropertyStore *pps;
HRESULT hr = SHGetPropertyStoreForWindow(mhWnd, IID_PPV_ARGS(&pps)); if (SUCCEEDED(hr))
{
PROPVARIANT pv; if (!rApplicationID.isEmpty())
{
hr = InitPropVariantFromString(o3tl::toW(rApplicationID.getStr()), &pv);
mbPropertiesStored = true;
} else // if rApplicationID we remove the property from the window, if present
PropVariantInit(&pv);
// set window to screen size
ImplSalFrameFullScreenPos( this, true );
} else
{
m_eState &= ~vcl::WindowState::FullScreen; // when the ShowState has to be reset, hide the window first to // reduce flicker bool bVisible = (GetWindowStyle( mhWnd ) & WS_VISIBLE) != 0; if ( bVisible && (mnShowState != mnFullScreenShowState) )
ShowWindow( mhWnd, SW_HIDE );
if ( bStart )
{ // turn off screen-saver / power saving when in Presentation mode
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
} else
{ // turn on screen-saver / power saving back
SetThreadExecutionState(ES_CONTINUOUS);
}
}
if ( nFlags & SalFrameToTop::ForegroundTask )
{ // LO used to always call AttachThreadInput here, which resulted in deadlocks // in some installations for unknown reasons! if (bEnableAttachThreadInputHack)
{ // This magic code is necessary to connect the input focus of the // current window thread and the thread which owns the window that // should be the new foreground window.
HWND hCurrWnd = GetForegroundWindow();
DWORD myThreadID = GetCurrentThreadId();
DWORD currThreadID = GetWindowThreadProcessId(hCurrWnd,nullptr);
AttachThreadInput(myThreadID, currThreadID, TRUE);
SetForegroundWindow_Impl(hWnd);
AttachThreadInput(myThreadID, currThreadID, FALSE);
} else
SetForegroundWindow_Impl(hWnd);
}
// Windows sometimes incorrectly reports to have the focus; // thus make sure to really get the focus if ( ::GetFocus() == hWnd )
SetForegroundWindow_Impl( hWnd );
}
}
void WinSalFrame::ToTop( SalFrameToTop nFlags )
{
nFlags &= ~SalFrameToTop::GrabFocus; // this flag is not needed on win32 // 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_TOTOP, static_cast<WPARAM>(nFlags), 0 );
SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
} else
ImplSalToTop( mhWnd, nFlags );
}
// change the mouse pointer if different if ( mhCursor != aImplPtrTab[ePointerStyle].mhCursor )
{
mhCursor = aImplPtrTab[ePointerStyle].mhCursor;
SetCursor( mhCursor );
}
}
void WinSalFrame::CaptureMouse( bool bCapture )
{ // Send this Message to the window, because CaptureMouse() only work // in the thread of the window, which has create this window int nMsg; if ( bCapture )
nMsg = SAL_MSG_CAPTUREMOUSE; else
nMsg = SAL_MSG_RELEASEMOUSE;
WinSalInstance::SendWndMessage(mhWnd, nMsg, 0, 0);
}
// When the application can't handle IME messages, then the // System should handle the IME handling if ( !(pContext->mnOptions & InputContextFlags::ExtText) )
pFrame->mbHandleIME = false;
// Set the Font for IME Handling if ( pContext->mpFont )
{
HIMC hIMC = ImmGetContext( pFrame->mhWnd ); if ( hIMC )
{
LOGFONTW aLogFont;
ImplGetLogFontFromFontSelect(pContext->mpFont->GetFontSelectPattern(),
nullptr, aLogFont, true);
// tdf#147299: To enable vertical input mode, Windows IMEs check the face // name string for a leading '@'.
SalExtTextInputPosEvent aPosEvt;
pFrame->CallCallback(SalEvent::ExtTextInputPos, &aPosEvt); if (aPosEvt.mbVertical)
{
std::array<WCHAR, LF_FACESIZE> aTmpFaceName;
std::copy(aLogFont.lfFaceName, aLogFont.lfFaceName + LF_FACESIZE,
aTmpFaceName.begin());
aLogFont.lfFaceName[0] = L'@';
std::copy(aTmpFaceName.begin(), aTmpFaceName.end() - 1,
aLogFont.lfFaceName + 1);
aLogFont.lfFaceName[LF_FACESIZE - 1] = L'\0';
}
void WinSalFrame::SetInputContext( SalInputContext* pContext )
{ // Must be called in the main thread!
WinSalInstance::SendWndMessage(mhWnd, SAL_MSG_SETINPUTCONTEXT, 0, reinterpret_cast<LPARAM>(pContext));
}
void WinSalFrame::EndExtTextInput( EndExtTextInputFlags nFlags )
{ // Must be called in the main thread!
WinSalInstance::SendWndMessage(mhWnd, SAL_MSG_ENDEXTTEXTINPUT, static_cast<WPARAM>(nFlags), 0);
}
staticvoid ImplGetKeyNameText(UINT lParam, OUStringBuffer& rBuf, constchar* pReplace)
{
static_assert( sizeof( WCHAR ) == sizeof( sal_Unicode ), "must be the same size" );
staticconstint nMaxKeyLen = 350;
WCHAR aKeyBuf[ nMaxKeyLen ]; int nKeyLen = 0; if ( lParam )
{
OUString aLang = Application::GetSettings().GetUILanguageTag().getLanguage();
OUString aRet = vcl_sal::getKeysReplacementName(aLang, lParam); if( aRet.isEmpty() )
{
nKeyLen = GetKeyNameTextW( lParam, aKeyBuf, nMaxKeyLen );
SAL_WARN_IF( nKeyLen > nMaxKeyLen, "vcl", "Invalid key name length!" ); if( nKeyLen > nMaxKeyLen )
nKeyLen = 0; elseif( nKeyLen > 0 )
{ // Capitalize just the first letter of key names
CharLowerBuffW( aKeyBuf, nKeyLen );
// On Windows 9x, Windows NT we get sometimes very small sizes // (for example for the small Caption height). // So if it is MS Sans Serif, a none scalable font we use // 8 Point as the minimum control height, in all other cases // 6 Point is the smallest one if ( rFont.GetFontHeight() < 8 )
{ if ( rtl_ustr_compareIgnoreAsciiCase( o3tl::toU(rLogFont.lfFaceName), o3tl::toU(L"MS Sans Serif") ) == 0 )
rFont.SetFontHeight( 8 ); elseif ( rFont.GetFontHeight() < 6 )
rFont.SetFontHeight( 6 );
}
}
static tools::Long ImplW2I( constwchar_t* pStr )
{
tools::Long n = 0; int nSign = 1;
if ( *pStr == '-' )
{
nSign = -1;
pStr++;
}
while( (*pStr >= 48) && (*pStr <= 57) )
{
n *= 10;
n += ((*pStr) - 48);
pStr++;
}
Color aControlTextColor;
Color aMenuBarTextColor;
Color aMenuBarRolloverTextColor;
Color aHighlightTextColor = ImplWinColorToSal(GetSysColor(COLOR_HIGHLIGHTTEXT));
BOOL bFlatMenus = FALSE;
SystemParametersInfoW( SPI_GETFLATMENU, 0, &bFlatMenus, 0); if( bFlatMenus )
{
aStyleSettings.SetUseFlatMenus( true ); // flat borders for our controls etc. as well in this mode (ie, no 3d borders) // this is not active in the classic style appearance
aStyleSettings.SetUseFlatBorders( true );
} else
{
aStyleSettings.SetUseFlatMenus( false );
aStyleSettings.SetUseFlatBorders( false );
}
// tdf#156040 in the absence of a better idea, do like // StyleSettings::Set3DColors does
Color aLightColor(ImplWinColorToSal(color));
aLightColor.DecreaseLuminance(64);
aStyleSettings.SetLightColor(aLightColor);
GetThemeColor(hTheme, 0, 0, TMT_TEXTCOLOR, &color);
aStyleSettings.SetWindowTextColor( ImplWinColorToSal( color ) );
aStyleSettings.SetToolTextColor( ImplWinColorToSal( color ) );
GetThemeColor(hTheme, 0, 0, TMT_SHADOWCOLOR, &color);
aStyleSettings.SetShadowColor( ImplWinColorToSal( color ) );
GetThemeColor(hTheme, 0, 0, TMT_DKSHADOW3D, &color);
aStyleSettings.SetDarkShadowColor( ImplWinColorToSal( color ) );
CloseThemeData(hTheme);
Color aHighlightButtonTextColor = aStyleSettings.GetHighContrastMode() ?
aHighlightTextColor : aControlTextColor;
if (aStyleSettings.GetHighContrastMode())
{
Color aLinkColor(ImplWinColorToSal(GetSysColor(COLOR_HOTLIGHT)));
aStyleSettings.SetLinkColor(aLinkColor);
aStyleSettings.SetVisitedLinkColor(aLinkColor);
}
// otherwise, menu shows up as white in dark mode
aStyleSettings.SetMenuColor(aStyleSettings.GetWindowColor()); if (ThemeColors::VclPluginCanUseThemeColors())
lcl_LoadColorsFromTheme(aStyleSettings);
aStyleSettings.SetSystemColorsLoaded(true);
case WM_NCMOUSEMOVE: case SAL_MSG_MOUSELEAVE:
{
SalData* pSalData = GetSalData(); if ( pSalData->mhWantLeaveMsg == hWnd )
{ // Mouse-Coordinates are relative to the screen
POINT aPt;
aPt.x = static_cast<short>(LOWORD(lParam));
aPt.y = static_cast<short>(HIWORD(lParam));
ScreenToClient(hWnd, &aPt); if (constauto& pHelpWin = ImplGetSVHelpData().mpHelpWin)
{ const tools::Rectangle& rHelpRect = pHelpWin->GetHelpArea(); if (rHelpRect.Contains(Point(aPt.x, aPt.y)))
{ // We have entered a tooltip (help window). Don't call the handler here; it // would launch the sequence "Mouse leaves the Control->Control redraws-> // Help window gets destroyed->Mouse enters the Control->Control redraws", // which takes CPU and may flicker. Just destroy the help window and pretend // we are still over the original window.
ImplDestroyHelpWindow(true);
bCall = false; break;
}
}
pSalData->mhWantLeaveMsg = nullptr;
aMouseEvt.mnX = aPt.x;
aMouseEvt.mnY = aPt.y;
aMouseEvt.mnButton = 0;
nEvent = SalEvent::MouseLeave;
} else
bCall = false;
} break;
case WM_LBUTTONDOWN:
aMouseEvt.mnButton = MOUSE_LEFT;
nEvent = SalEvent::MouseButtonDown; break;
case WM_MBUTTONDOWN:
aMouseEvt.mnButton = MOUSE_MIDDLE;
nEvent = SalEvent::MouseButtonDown; break;
case WM_RBUTTONDOWN:
aMouseEvt.mnButton = MOUSE_RIGHT;
nEvent = SalEvent::MouseButtonDown; break;
case WM_LBUTTONUP:
aMouseEvt.mnButton = MOUSE_LEFT;
nEvent = SalEvent::MouseButtonUp; break;
case WM_MBUTTONUP:
aMouseEvt.mnButton = MOUSE_MIDDLE;
nEvent = SalEvent::MouseButtonUp; break;
case WM_RBUTTONUP:
aMouseEvt.mnButton = MOUSE_RIGHT;
nEvent = SalEvent::MouseButtonUp; break;
}
// check if this window was destroyed - this might happen if we are the help window // and sent a mouse leave message to the application which killed the help window, ie ourselves if( !IsWindow( hWnd ) ) returnfalse;
if ( bCall )
{ if ( nEvent == SalEvent::MouseButtonDown )
UpdateWindow( hWnd );
SalWheelMouseEvent aWheelEvt;
aWheelEvt.mnTime = GetMessageTime();
aWheelEvt.mnX = aWinPt.x;
aWheelEvt.mnY = aWinPt.y;
aWheelEvt.mnCode = 0;
aWheelEvt.mnDelta = GET_WHEEL_DELTA_WPARAM( wParam ); // Distance scrolled passed in message param
aWheelEvt.mnNotchDelta = aWheelEvt.mnDelta/WHEEL_DELTA; // Number of mouse notches/detents scrolled if( aWheelEvt.mnNotchDelta == 0 )
{ // Keep mnNotchDelta nonzero unless distance scrolled was exactly zero. // Many places use its sign to indicate direction scrolled. if( aWheelEvt.mnDelta > 0 )
aWheelEvt.mnNotchDelta = 1; elseif( aWheelEvt.mnDelta < 0 )
aWheelEvt.mnNotchDelta = -1;
}
if( nMsg == WM_MOUSEWHEEL ) // Vertical scroll
{ if ( aSalShlData.mnWheelScrollLines == WHEEL_PAGESCROLL ) // Mouse wheel set to "One screen at a time"
{ // Note: mnDelta may hit a multiple of WHEEL_DELTA via touchpad scrolling. That's the tradeoff to keep // smooth touchpad scrolling with mouse wheel set to screen.
if ( (aWheelEvt.mnDelta % WHEEL_DELTA) == 0 ) // Mouse wheel sends WHEEL_DELTA (or multiple of it)
aWheelEvt.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL; // "Magic" page scroll value else// Touchpad can send smaller values. Use default 3 lines to scroll at a time.
aWheelEvt.mnScrollLines = aWheelEvt.mnDelta / double(WHEEL_DELTA) * 3.0; // Calculate actual lines using distance
} else// Mouse wheel set to "Multiple lines at a time"
{ // Windows legacy touchpad support sends touchpad scroll gesture as multiple mouse wheel messages. // Calculate number of mouse notches scrolled using distance from Windows.
aWheelEvt.mnScrollLines = aWheelEvt.mnDelta / double(WHEEL_DELTA); // Multiply by user setting for number of lines to scroll at a time.
aWheelEvt.mnScrollLines *= aSalShlData.mnWheelScrollLines;
}
aWheelEvt.mbHorz = false;
} else// Horizontal scroll
{ // Windows legacy touchpad support sends touchpad scroll gesture as multiple mouse wheel messages. // Calculate number of mouse notches scrolled using distance from Windows.
aWheelEvt.mnScrollLines = aWheelEvt.mnDelta / double(WHEEL_DELTA); // Multiply by user setting for number of characters to scroll at a time.
aWheelEvt.mnScrollLines *= aSalShlData.mnWheelScrollChars;
aWheelEvt.mbHorz = true;
// fdo#36380 - seems horiz scrolling has swapped direction
aWheelEvt.mnDelta *= -1;
aWheelEvt.mnNotchDelta *= -1;
aWheelEvt.mnScrollLines *= -1.0;
}
// Do not change magic value for page scrolling if (aWheelEvt.mnScrollLines != SAL_WHEELMOUSE_EVENT_PAGESCROLL)
{ // Scrolling code multiplies (scroll lines * number of notches), so pull # notches out to prevent double multiply. if (aWheelEvt.mnNotchDelta != 0) // No divide by zero!
aWheelEvt.mnScrollLines /= aWheelEvt.mnNotchDelta; else
aWheelEvt.mnScrollLines = abs(aWheelEvt.mnScrollLines); // Just ensure (+) value
}
if ( nWinModCode & MK_SHIFT )
aWheelEvt.mnCode |= KEY_SHIFT; if ( nWinModCode & MK_CONTROL )
aWheelEvt.mnCode |= KEY_MOD1; if ( GetKeyState( VK_MENU ) & 0x8000 )
aWheelEvt.mnCode |= KEY_MOD2;
// We are on Windows NT so we use Unicode FrameProcs and we // get Unicode charcodes directly from Windows returnstatic_cast<sal_Unicode>(nCharCode);
}
LanguageType WinSalFrame::GetInputLanguage()
{ if( !mnInputLang )
ImplUpdateInputLang( this );
bool WinSalFrame::MapUnicodeToKeyCode( sal_Unicode aUnicode, LanguageType aLangType, vcl::KeyCode& rKeyCode )
{ bool bRet = false;
sal_IntPtr nLangType = static_cast<sal_uInt16>(aLangType); // just use the passed language identifier, do not try to load additional keyboard support
HKL hkl = reinterpret_cast<HKL>(nLangType);
if( hkl )
{ SHORT scan = VkKeyScanExW( aUnicode, hkl ); if( LOWORD(scan) == 0xFFFF ) // keyboard not loaded or key cannot be mapped
bRet = false; else
{
BYTE vkeycode = LOBYTE(scan);
BYTE shiftstate = HIBYTE(scan);
// Last argument is set to false, because there's no decision made // yet which key should be assigned to MOD3 modifier on Windows. // Windows key - user's can be confused, because it should display // Windows menu (applies to both left/right key) // Menu key - this key is used to display context menu // AltGr key - probably it has no sense
rKeyCode = vcl::KeyCode( ImplSalGetKeyCode( vkeycode ),
(shiftstate & 0x01) != 0, // shift
(shiftstate & 0x02) != 0, // ctrl
(shiftstate & 0x04) != 0, // alt false );
bRet = true;
}
}
return bRet;
}
staticvoid UnsetAltIfAltGr(SalKeyEvent& rKeyEvt, sal_uInt16 nModCode)
{ if ((nModCode & (KEY_MOD1 | KEY_MOD2)) == (KEY_MOD1 | KEY_MOD2) &&
rKeyEvt.mnCharCode)
{ // this is actually AltGr and should not be handled as Alt
rKeyEvt.mnCode &= ~(KEY_MOD1 | KEY_MOD2);
}
}
// tdf#152404 Commit uncommitted text before dispatching key shortcuts. In // certain cases such as pressing Control-Alt-C in a Writer document while // there is uncommitted text will call WinSalFrame::EndExtTextInput() which // will dispatch a SalEvent::EndExtTextInput event. Writer's handler for that // event will delete the uncommitted text and then insert the committed text // but LibreOffice will crash when deleting the uncommitted text because // deletion of the text also removes and deletes the newly inserted comment. staticvoid FlushIMBeforeShortCut(WinSalFrame* pFrame, SalEvent nEvent, sal_uInt16 nModCode)
{ if (pFrame->mbCandidateMode && nEvent == SalEvent::KeyInput
&& (nModCode & (KEY_MOD1 | KEY_MOD2)))
{
pFrame->EndExtTextInput(EndExtTextInputFlags::Complete);
}
}
// When Num Lock is off, the key codes from NumPag come as arrows, PgUp/PgDn, etc. static WORD NumPadFromArrows(WORD vk)
{ switch (vk)
{ case VK_CLEAR: return VK_NUMPAD5; case VK_PRIOR: return VK_NUMPAD9; case VK_NEXT: return VK_NUMPAD3; case VK_END: return VK_NUMPAD1; case VK_HOME: return VK_NUMPAD7; case VK_LEFT: return VK_NUMPAD4; case VK_UP: return VK_NUMPAD8; case VK_RIGHT: return VK_NUMPAD6; case VK_DOWN: return VK_NUMPAD2; case VK_INSERT: return VK_NUMPAD0; default: return vk;
}
}
WORD vk = LOWORD(wParam);
WORD keyFlags = HIWORD(lParam);
switch (nMsg)
{ case WM_CHAR: if (state.wait_WM_CHAR && MapVirtualKeyW(LOBYTE(keyFlags), MAPVK_VSC_TO_VK) == VK_MENU)
{
state.clear(); // Ignore it - it is synthetized (incorrect, truncated) character from system returntrue;
}
break;
case WM_SYSKEYDOWN: if (vk == VK_MENU)
{ if (!(keyFlags & KF_REPEAT))
state.clear();
state.started = true; returnfalse; // This must be processed further - e.g., to show accelerators
}
if (!state.started) break;
if (keyFlags & KF_EXTENDED) break; // NUMPAD numeric keys are *not* considered extended
// reset the background mode for each text input, // as some tools such as RichWin may have changed it if ( pFrame->mpLocalGraphics &&
pFrame->mpLocalGraphics->getHDC() )
SetBkMode( pFrame->mpLocalGraphics->getHDC(), TRANSPARENT );
if ( bIgnoreCharMsg )
{
bIgnoreCharMsg = false; // #101635# if zero is returned here for WM_SYSCHAR (ALT+<key>) Windows will beep // because this 'hotkey' was not processed -> better return 1 // except for Alt-SPACE which should always open the sysmenu (#104616#)
// also return zero if a system menubar is available that might process this hotkey // this also applies to the OLE inplace embedding where we are a child window if( (GetWindowStyle( hWnd ) & WS_CHILD) || GetMenu( hWnd ) || (wParam == 0x20) ) returnfalse; else returntrue;
}
// ignore backspace as a single key, so that // we do not get problems for combinations w/ a DeadKey if ( wParam == 0x08 ) // BACKSPACE returnfalse;
// only "free flying" WM_CHAR messages arrive here, that are // created by typing an ALT-NUMPAD combination
SalKeyEvent aKeyEvt;
bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt ); return nRet;
} // #i11583#, MCD, 2003-01-13, Support for WM_UNICHAR & Keyman 6.0; addition begins elseif( nMsg == WM_UNICHAR )
{ // If Windows is asking if we accept WM_UNICHAR, return TRUE if(wParam == UNICODE_NOCHAR)
{
rResult = TRUE; // ssa: this will actually return TRUE to windows returntrue; // ...but this will only avoid calling the defwindowproc
}
if (!rtl::isUnicodeCodePoint(wParam)) returnfalse;
SalKeyEvent aKeyEvt;
aKeyEvt.mnCode = nModCode; // Or should it be 0? - as this is always a character returned
aKeyEvt.mnRepeat = 0;
if( wParam >= Uni_SupplementaryPlanesStart )
{ // character is supplementary char in UTF-32 format - must be converted to UTF-16 supplementary pair
aKeyEvt.mnCharCode = rtl::getHighSurrogate(wParam);
nLastChar = 0;
nLastVKChar = 0;
pFrame->CallCallback(SalEvent::KeyInput, &aKeyEvt);
pFrame->CallCallback(SalEvent::KeyUp, &aKeyEvt);
wParam = rtl::getLowSurrogate(wParam);
}
comphelper::ScopeGuard aEndModKeyCodes(
[nModCode, pFrame, listener = vcl::DeletionListener(pFrame)]
{ if (listener.isDeleted()) return; // Send SalEvent::KeyModChange, to make sure that this window ends special mode // (e.g., hides mnemonics if auto-accelerator feature is active)
SalKeyModEvent aModEvt;
aModEvt.mbDown = false;
aModEvt.mnCode = nModCode;
aModEvt.mnModKeyCode = nLastModKeyCode;
pFrame->CallCallback(SalEvent::KeyModChange, &aModEvt);
}); if (nLastModKeyCode == ModKeyFlags::NONE)
aEndModKeyCodes.dismiss();
nLastModKeyCode = ModKeyFlags::NONE; // make sure no modkey messages are sent if they belong to a hotkey (see above)
aKeyEvt.mnCharCode = 0;
aKeyEvt.mnCode = ImplSalGetKeyCode( wParam ); if ( !bKeyUp )
{ // check for charcode // Get the related WM_CHAR message using PeekMessage, if available. // The WM_CHAR message is always at the beginning of the // message queue. Also it is made certain that there is always only // one WM_CHAR message in the queue.
bCharPeek = PeekMessageW( &aCharMsg, hWnd,
WM_CHAR, WM_CHAR, PM_NOREMOVE | PM_NOYIELD ); if ( bCharPeek && (nDeadChar == aCharMsg.wParam) )
{
bCharPeek = false;
nDeadChar = 0;
bIgnoreCharMsg = bCharPeek; bool nRet = pFrame->CallCallback( nEvent, &aKeyEvt ); // independent part only reacts on keyup but Windows does not send // keyup for VK_HANJA if( aKeyEvt.mnCode == KEY_HANGUL_HANJA )
nRet = pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
bIgnoreCharMsg = false;
// char-message, then remove or ignore if ( bCharPeek )
{
nDeadChar = 0; if ( nRet )
{
PeekMessageW( &aCharMsg, hWnd,
nCharMsg, nCharMsg, PM_REMOVE | PM_NOYIELD );
} else
bIgnoreCharMsg = true;
}
// even without the Yield mutex, we can still change the clip region, // because other threads don't use the Yield mutex // --> see AcquireGraphics()
WinSalFrame* pFrame = GetWindowPtr( hWnd ); if ( pFrame )
{ // clip region must be set, as we don't get a proper // bounding rectangle otherwise
WinSalGraphics *pGraphics = pFrame->mpLocalGraphics; bool bHasClipRegion = pGraphics &&
pGraphics->getHDC() && pGraphics->getRegion(); if ( bHasClipRegion )
SelectClipRgn( pGraphics->getHDC(), nullptr );
// according to Windows documentation one shall check first if // there really is a paint-region
RECT aUpdateRect;
PAINTSTRUCT aPs; bool bHasPaintRegion = GetUpdateRect( hWnd, nullptr, FALSE ); if ( bHasPaintRegion )
{ // call BeginPaint/EndPaint to query the paint rect and use // this information in the (deferred) paint
BeginPaint( hWnd, &aPs );
CopyRect( &aUpdateRect, &aPs.rcPaint );
}
// reset clip region if ( bHasClipRegion )
SelectClipRgn( pGraphics->getHDC(), pGraphics->getRegion() );
// try painting if ( bHasPaintRegion )
{
bPaintSuccessful = ImplHandlePostPaintMsg(
hWnd, &aUpdateRect, PostedState::IsInitial );
EndPaint( hWnd, &aPs );
} else// if there is nothing to paint, the paint is successful
bPaintSuccessful = true;
}
return bPaintSuccessful;
}
void WinSalFrame::SetMaximizedFrameGeometry(HWND hWnd, RECT* pParentRect )
{ // calculate and set frame geometry of a maximized window - useful if the window is still hidden
// dualmonitor support: // Get screensize of the monitor with the mouse pointer
RECT aInnerRect;
GetClientRect(mhWnd, &aInnerRect); if( aInnerRect.right )
{ // improve right decoration
aPt.x=aInnerRect.right;
aPt.y=aInnerRect.top;
ClientToScreen(mhWnd, &aPt);
maGeometry.setRightDecoration(aRect.right - aPt.x);
} if( aInnerRect.bottom ) // may be zero if window was not shown yet
maGeometry.setBottomDecoration(aRect.bottom - aPt.y - aInnerRect.bottom); else // bottom border is typically the same as left/right
maGeometry.setBottomDecoration(maGeometry.leftDecoration());
if (GetWindowStyle(hWnd) & WS_VISIBLE)
pFrame->mbDefPos = false;
// protect against recursion if (!pFrame->mbInMoveMsg)
{ // adjust window again for FullScreenMode
pFrame->mbInMoveMsg = true; if (pFrame->isFullScreen())
ImplSalFrameFullScreenPos(pFrame);
pFrame->mbInMoveMsg = false;
}
pFrame->CallCallback(SalEvent::Resize, nullptr); // to avoid double Paints by VCL and SAL if (IsWindowVisible(hWnd) && !pFrame->mbInShow)
UpdateWindow(hWnd);
if ( pFrame->mnMinWidth || pFrame->mnMinHeight )
{ int nWidth = pFrame->mnMinWidth; int nHeight = pFrame->mnMinHeight;
ImplSalAddBorder( pFrame, nWidth, nHeight );
if ( pMinMax->ptMinTrackSize.x < nWidth )
pMinMax->ptMinTrackSize.x = nWidth; if ( pMinMax->ptMinTrackSize.y < nHeight )
pMinMax->ptMinTrackSize.y = nHeight;
}
if ( pFrame->mnMaxWidth || pFrame->mnMaxHeight )
{ int nWidth = pFrame->mnMaxWidth; int nHeight = pFrame->mnMaxHeight;
ImplSalAddBorder( pFrame, nWidth, nHeight );
if( nWidth > 0 && nHeight > 0 ) // protect against int overflow due to INT_MAX initialisation
{ if ( pMinMax->ptMaxTrackSize.x > nWidth )
pMinMax->ptMaxTrackSize.x = nWidth; if ( pMinMax->ptMaxTrackSize.y > nHeight )
pMinMax->ptMaxTrackSize.y = nHeight;
}
}
}
ImplSalYieldMutexRelease();
}
return bRet;
}
// retrieves the SalMenuItem pointer from a hMenu // the pointer is stored in every item, so if no position // is specified we just use the first item (ie, pos=0) // if bByPosition is false then nPos denotes a menu id instead of a position static WinSalMenuItem* ImplGetSalMenuItem( HMENU hMenu, UINT nPos, bool bByPosition=true)
{
MENUITEMINFOW mi = {};
mi.cbSize = sizeof( mi );
mi.fMask = MIIM_DATA; if( !GetMenuItemInfoW( hMenu, nPos, bByPosition, &mi) )
SAL_WARN("vcl", "GetMenuItemInfoW failed: " << comphelper::WindowsErrorString(GetLastError()));
// Send activate and deactivate together, so we have not keep track of opened menus // this will be enough to have the menus updated correctly
SalMenuEvent aMenuEvt;
WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( hMenu, 0 ); if( pSalMenuItem )
aMenuEvt.mpMenu = pSalMenuItem->mpMenu; else
aMenuEvt.mpMenu = nullptr;
bool nRet = false; if ( hMenu && !pFrame->mLastActivatedhMenu )
{ // we never activated a menu (ie, no WM_INITMENUPOPUP has occurred yet) // which means this must be the menubar -> send activation/deactivation
SalMenuEvent aMenuEvt;
WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( hMenu, nId, bByPosition ); if( pSalMenuItem )
aMenuEvt.mpMenu = pSalMenuItem->mpMenu; else
aMenuEvt.mpMenu = nullptr;
if( !hMenu && nFlags == 0xFFFF )
{ // all menus are closed, reset activation logic
pFrame->mLastActivatedhMenu = nullptr;
}
if( hMenu )
{ // hMenu must be saved, as it is not passed in WM_COMMAND which always occurs after a selection // if a menu is closed due to a command selection then hMenu is NULL, but WM_COMMAND comes later // so we must not overwrite it in this case
pFrame->mSelectedhMenu = hMenu;
// send highlight event if( nFlags & MF_POPUP )
{ // submenu selected // wParam now carries an index instead of an id -> retrieve id
MENUITEMINFOW mi = {};
mi.cbSize = sizeof( mi );
mi.fMask = MIIM_ID; if( GetMenuItemInfoW( hMenu, LOWORD(wParam), TRUE, &mi) )
nId = sal::static_int_cast<WORD>(mi.wID);
}
if ( nCommand == SC_KEYMENU )
{ // do not process SC_KEYMENU if we have a native menu // Windows should handle this if( GetMenu( hWnd ) ) returnfalse;
// Process here KeyMenu events only for Alt to activate the MenuBar, // or if a SysChild window is in focus, as Alt-key-combinations are // only processed via this event if ( !LOWORD( lParam ) )
{ // Only trigger if no other key is pressed. // Contrary to Docu the CharCode is delivered with the x-coordinate // that is pressed in addition. // Also 32 for space, 99 for c, 100 for d, ... // As this is not documented, we check the state of the space-bar if ( GetKeyState( VK_SPACE ) & 0x8000 ) returnfalse;
// to avoid activating the MenuBar for Alt+MouseKey if ( (GetKeyState( VK_LBUTTON ) & 0x8000) ||
(GetKeyState( VK_RBUTTON ) & 0x8000) ||
(GetKeyState( VK_MBUTTON ) & 0x8000) ||
(GetKeyState( VK_SHIFT ) & 0x8000) ) returntrue;
SalKeyEvent aKeyEvt;
aKeyEvt.mnCode = KEY_MENU;
aKeyEvt.mnCharCode = 0;
aKeyEvt.mnRepeat = 0; bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt ); return nRet;
} else
{ // check if a SysChild is in focus
HWND hFocusWnd = ::GetFocus(); if ( hFocusWnd && ImplFindSalObject( hFocusWnd ) )
{ char cKeyCode = static_cast<char>(static_cast<unsignedchar>(LOWORD( lParam ))); // LowerCase if ( (cKeyCode >= 65) && (cKeyCode <= 90) )
cKeyCode += 32; // We only accept 0-9 and A-Z; all other keys have to be // processed by the SalObj hook if ( ((cKeyCode >= 48) && (cKeyCode <= 57)) ||
((cKeyCode >= 97) && (cKeyCode <= 122)) )
{
sal_uInt16 nModCode = 0; if ( GetKeyState( VK_SHIFT ) & 0x8000 )
nModCode |= KEY_SHIFT; if ( GetKeyState( VK_CONTROL ) & 0x8000 )
nModCode |= KEY_MOD1;
nModCode |= KEY_MOD2;
// get cursor position and from it calculate default position // for the composition window
SalExtTextInputPosEvent aPosEvt;
pFrame->CallCallback( SalEvent::ExtTextInputPos, &aPosEvt ); if ( (aPosEvt.mnX == -1) && (aPosEvt.mnY == -1) )
aForm.dwStyle |= CFS_DEFAULT; else
{
aForm.dwStyle |= CFS_POINT;
aForm.ptCurrentPos.x = aPosEvt.mnX;
aForm.ptCurrentPos.y = aPosEvt.mnY;
}
ImmSetCompositionWindow( hIMC, &aForm );
// Because not all IME's use this values, we create // a Windows caret to force the Position from the IME if ( GetFocus() == pFrame->mhWnd )
{
CreateCaret( pFrame->mhWnd, nullptr,
aPosEvt.mnWidth, aPosEvt.mnHeight );
SetCaretPos( aPosEvt.mnX, aPosEvt.mnY );
}
}
// If the IME doesn't support OnSpot input, then there is nothing to do if ( !pFrame->mbAtCursorIME ) return !bDef;
// If we get new Composition data, then we handle this new input if ( (lParam & (GCS_COMPSTR | GCS_COMPATTR)) ||
((lParam & GCS_CURSORPOS) && !(lParam & GCS_RESULTSTR)) )
{
bDef = false;
// Only when we get new composition data, we must send this event if ( (nTextLen > 0) || !(lParam & GCS_RESULTSTR) )
{ // End the mode, if the last character is deleted if ( !nTextLen )
{
pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt );
pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
} else
{ // Because Cursor-Position and DeltaStart never updated // from the korean input engine, we must handle this here if ( lParam & CS_INSERTCHAR )
{
aEvt.mnCursorPos = nTextLen; if ( aEvt.mnCursorPos && (lParam & CS_NOMOVECARET) )
aEvt.mnCursorPos--;
} else
aEvt.mnCursorPos = LOWORD( ImmGetCompositionStringW( hIMC, GCS_CURSORPOS, nullptr, 0 ) );
if ( pFrame->mbCandidateMode )
aEvt.mnCursorFlags |= EXTTEXTINPUT_CURSOR_INVISIBLE; if ( lParam & CS_NOMOVECARET )
aEvt.mnCursorFlags |= EXTTEXTINPUT_CURSOR_OVERWRITE;
WinSalFrame* pFrame = GetWindowPtr( hWnd ); if ( pFrame && (!lParam || (lParam & GCS_RESULTSTR)) )
{ // reset the background mode for each text input, // as some tools such as RichWin may have changed it if ( pFrame->mpLocalGraphics &&
pFrame->mpLocalGraphics->getHDC() )
SetBkMode( pFrame->mpLocalGraphics->getHDC(), TRANSPARENT );
}
// tdf#155158: Windows IMEs do not necessarily send a composition message if they are // dismissed during composition (for example, by an input method/language change). // tdf#167740: Also clear the candidate text when the IME is dismissed.
SalExtTextInputEvent aEvt;
aEvt.mpTextAttr = nullptr;
aEvt.mnCursorPos = 0;
aEvt.mnCursorFlags = 0;
pFrame->CallCallback(SalEvent::ExtTextInput, &aEvt);
pFrame->CallCallback(SalEvent::EndExtTextInput, nullptr);
}
ImplSalYieldMutexRelease();
return bDef;
}
staticbool ImplHandleAppCommand( HWND hWnd, LPARAM lParam, LRESULT & nRet )
{
MediaCommand nCommand; switch( GET_APPCOMMAND_LPARAM(lParam) )
{ case APPCOMMAND_MEDIA_CHANNEL_DOWN: nCommand = MediaCommand::ChannelDown; break; case APPCOMMAND_MEDIA_CHANNEL_UP: nCommand = MediaCommand::ChannelUp; break; case APPCOMMAND_MEDIA_NEXTTRACK: nCommand = MediaCommand::NextTrack; break; case APPCOMMAND_MEDIA_PAUSE: nCommand = MediaCommand::Pause; break; case APPCOMMAND_MEDIA_PLAY: nCommand = MediaCommand::Play; break; case APPCOMMAND_MEDIA_PLAY_PAUSE: nCommand = MediaCommand::PlayPause; break; case APPCOMMAND_MEDIA_PREVIOUSTRACK: nCommand = MediaCommand::PreviousTrack; break; case APPCOMMAND_MEDIA_RECORD: nCommand = MediaCommand::Record; break; case APPCOMMAND_MEDIA_REWIND: nCommand = MediaCommand::Rewind; break; case APPCOMMAND_MEDIA_STOP: nCommand = MediaCommand::Stop; break; case APPCOMMAND_MIC_ON_OFF_TOGGLE: nCommand = MediaCommand::MicOnOffToggle; break; case APPCOMMAND_MICROPHONE_VOLUME_DOWN: nCommand = MediaCommand::MicrophoneVolumeDown; break; case APPCOMMAND_MICROPHONE_VOLUME_MUTE: nCommand = MediaCommand::MicrophoneVolumeMute; break; case APPCOMMAND_MICROPHONE_VOLUME_UP: nCommand = MediaCommand::MicrophoneVolumeUp; break; case APPCOMMAND_VOLUME_DOWN: nCommand = MediaCommand::VolumeDown; break; case APPCOMMAND_VOLUME_MUTE: nCommand = MediaCommand::VolumeMute; break; case APPCOMMAND_VOLUME_UP: nCommand = MediaCommand::VolumeUp; break; default: returnfalse;
}
// Make sure to launch Accessibility only the following criteria are satisfied // to avoid RFT interrupts regular accessibility processing if ( !pSVData->mxAccessBridge.is() )
{
css::uno::Reference<XComponentContext> xContext(comphelper::getProcessComponentContext()); try
{
pSVData->mxAccessBridge = css::accessibility::MSAAService::create(xContext);
SAL_INFO("vcl", "got IAccessible2 bridge");
} catch (css::uno::DeploymentException&)
{
TOOLS_WARN_EXCEPTION("vcl", "got no IAccessible2 bridge");
assert(false && "failed to create IAccessible2 bridge");
}
}
xMSAA.set(pSVData->mxAccessBridge, uno::UNO_QUERY);
ImplSalYieldMutexRelease();
} else
{ // tdf#155794: access without locking: hopefully this should be fine // as WM_GETOBJECT is received only on the main thread and by the time in // VCL shutdown when ImplSvData dies there should not be Windows any // more that could receive messages.
xMSAA.set(ImplGetSVData()->mxAccessBridge, uno::UNO_QUERY);
}
// mhOnSetTitleWnd not set to reasonable value anywhere... if ( lParam32 == OBJID_CLIENT )
{
nRet = xMSAA->getAccObjectPtr( reinterpret_cast<sal_Int64>(hWnd), lParam32, wParam32); if (nRet != 0) returntrue;
}
} returnfalse;
}
if ( aEvt.mbVertical )
{ // For vertical writing, the base line is left edge of the rectangle // and the target position is top-right corner.
pQueryCharPosition->pt.x = aEvt.maCursorBound.getX() + aEvt.maCursorBound.GetWidth();
pQueryCharPosition->pt.y = aEvt.maCursorBound.getY();
pQueryCharPosition->cLineHeight = aEvt.maCursorBound.GetWidth();
} else
{ // For horizontal writing, the base line is the bottom edge of the rectangle. // and the target position is top-left corner.
pQueryCharPosition->pt.x = aEvt.maCursorBound.getX();
pQueryCharPosition->pt.y = aEvt.maCursorBound.getY();
pQueryCharPosition->cLineHeight = aEvt.maCursorBound.GetHeight();
}
// Currently not supported but many IMEs usually ignore them.
pQueryCharPosition->rcDocument.left = 0;
pQueryCharPosition->rcDocument.top = 0;
pQueryCharPosition->rcDocument.right = 0;
pQueryCharPosition->rcDocument.bottom = 0;
if ( pSalData->mhWantLeaveMsg && !::GetCapture() )
{
POINT aPt;
GetCursorPos( &aPt );
// a one item cache, because this function is sometimes hot - if the cursor has not moved, then // no need to call WindowFromPoint static POINT cachedPoint; if (cachedPoint.x == aPt.x && cachedPoint.y == aPt.y) return;
cachedPoint = aPt;
// By WM_CREATE we connect the frame with the window handle if ( nMsg == WM_CREATE )
{ // Save Window-Instance in Windowhandle // Can also be used for the A-Version, because the struct // to access lpCreateParams is the same structure
CREATESTRUCTW* pStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
WinSalFrame* pFrame = static_cast<WinSalFrame*>(pStruct->lpCreateParams); if ( pFrame != nullptr )
{
SetWindowPtr( hWnd, pFrame );
UpdateAutoAccel();
UpdateDarkMode(hWnd);
// Set HWND already here, as data might be used already // when messages are being sent by CreateWindow()
pFrame->mhWnd = hWnd;
pFrame->maSysData.hWnd = hWnd;
ImplSVData* pSVData = ImplGetSVData(); // #i72707# TODO: the mbDeInit check will not be needed // once all windows that are not properly closed on exit got fixed if( pSVData->mbDeInit ) return 0;
case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: case WM_NCMOUSEMOVE: case SAL_MSG_MOUSELEAVE:
ImplSalYieldMutexAcquireWithWait();
rDef = !ImplHandleMouseMsg( hWnd, nMsg, wParam, lParam );
ImplSalYieldMutexRelease(); break;
case WM_NCLBUTTONDOWN: case WM_NCMBUTTONDOWN: case WM_NCRBUTTONDOWN:
ImplSalYieldMutexAcquireWithWait();
ImplCallClosePopupsHdl( hWnd ); // close popups...
ImplSalYieldMutexRelease(); break;
case WM_KEYDOWN: case WM_KEYUP: case WM_DEADCHAR: case WM_CHAR: case WM_UNICHAR: // MCD, 2003-01-13, Support for WM_UNICHAR & Keyman 6.0 case WM_SYSKEYDOWN: case WM_SYSKEYUP: case WM_SYSCHAR:
ImplSalYieldMutexAcquireWithWait();
rDef = !ImplHandleKeyMsg( hWnd, nMsg, wParam, lParam, nRet );
ImplSalYieldMutexRelease(); break;
case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: // protect against recursion, in case the message is returned // by IE or the external window if ( !bInWheelMsg )
{
bInWheelMsg = true;
rDef = !ImplHandleWheelMsg( hWnd, nMsg, wParam, lParam ); // If we did not process the message, re-check if there is a // connected (?) window that we have to notify. if ( rDef )
rDef = ImplSalWheelMousePos( hWnd, nMsg, wParam, lParam, nRet );
bInWheelMsg = false;
} break;
case WM_KILLFOCUS:
DestroyCaret();
[[fallthrough]]; case WM_SETFOCUS: case SAL_MSG_POSTFOCUS:
ImplHandleFocusMsg( hWnd );
rDef = false; break;
case WM_CLOSE:
ImplHandleCloseMsg( hWnd );
rDef = false; break;
case WM_QUERYENDSESSION: if( !bInQueryEnd )
{ // handle queryendsession only once
bInQueryEnd = true;
nRet = LRESULT(!ImplHandleShutDownMsg( hWnd ));
rDef = false;
// Issue #16314#: ImplHandleShutDownMsg causes a PostMessage in case of allowing shutdown. // This posted message was never processed and cause Windows XP to hang after log off // if there are multiple sessions and the current session wasn't the first one started. // So if shutdown is allowed we assume that a post message was done and retrieve all // messages in the message queue and dispatch them before we return control to the system.
case WM_ENDSESSION: if( !wParam )
bInQueryEnd = false; // no shutdown: allow query again
nRet = FALSE;
rDef = false; break;
case WM_DISPLAYCHANGE: case WM_SETTINGCHANGE: case WM_DEVMODECHANGE: case WM_FONTCHANGE: case WM_SYSCOLORCHANGE: case WM_TIMECHANGE:
ImplHandleSettingsChangeMsg( hWnd, nMsg, wParam, lParam ); break;
case WM_THEMECHANGED:
UpdateDarkMode(hWnd);
GetSalData()->mbThemeChanged = true; break;
case SAL_MSG_USEREVENT:
ImplHandleUserEvent( hWnd, lParam );
rDef = false; break;
case SAL_MSG_CAPTUREMOUSE:
SetCapture( hWnd );
rDef = false; break; case SAL_MSG_RELEASEMOUSE: if ( ::GetCapture() == hWnd )
ReleaseCapture();
rDef = false; break; case SAL_MSG_TOTOP:
ImplSalToTop( hWnd, static_cast<SalFrameToTop>(wParam) );
rDef = false; break; case SAL_MSG_SHOW:
ImplSalShow( hWnd, static_cast<bool>(wParam), static_cast<bool>(lParam) );
rDef = false; break; case SAL_MSG_SETINPUTCONTEXT:
ImplSalFrameSetInputContext( hWnd, reinterpret_cast<const SalInputContext*>(lParam) );
rDef = false; break; case SAL_MSG_ENDEXTTEXTINPUT:
ImplSalFrameEndExtTextInput( hWnd, static_cast<EndExtTextInputFlags>(wParam) );
rDef = false; break;
case WM_INPUTLANGCHANGE:
ImplHandleInputLangChange( hWnd, wParam, lParam ); break;
case WM_IME_CHAR: // #103487#, some IMEs (eg, those that do not work onspot) // may send WM_IME_CHAR instead of WM_IME_COMPOSITION // we just handle it like a WM_CHAR message - seems to work fine
ImplSalYieldMutexAcquireWithWait();
rDef = !ImplHandleKeyMsg( hWnd, WM_CHAR, wParam, lParam, nRet );
ImplSalYieldMutexRelease(); break;
case WM_IME_STARTCOMPOSITION:
rDef = ImplHandleIMEStartComposition( hWnd ); break;
case WM_IME_COMPOSITION:
rDef = ImplHandleIMEComposition( hWnd, lParam ); break;
case WM_IME_ENDCOMPOSITION:
rDef = ImplHandleIMEEndComposition( hWnd ); break;
case WM_IME_NOTIFY:
ImplHandleIMENotify( hWnd, wParam ); break;
case WM_GETOBJECT: // tdf#155794: this must complete without taking SolarMutex if ( ImplHandleGetObject( hWnd, lParam, wParam, nRet ) )
{
rDef = false;
} break;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.