diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /vcl/win/window/salframe.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/win/window/salframe.cxx')
-rw-r--r-- | vcl/win/window/salframe.cxx | 6027 |
1 files changed, 6027 insertions, 0 deletions
diff --git a/vcl/win/window/salframe.cxx b/vcl/win/window/salframe.cxx new file mode 100644 index 000000000..a271c4e5e --- /dev/null +++ b/vcl/win/window/salframe.cxx @@ -0,0 +1,6027 @@ +/* -*- 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 . + */ + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/Rectangle.hpp> + +#include <officecfg/Office/Common.hxx> + +#include <memory> +#include <string.h> +#include <limits.h> + +#include <svsys.h> + +#include <comphelper/windowserrorstring.hxx> + +#include <fstream> +#include <boost/property_tree/ptree.hpp> +#include <boost/property_tree/ini_parser.hpp> +#include <osl/file.hxx> +#include <osl/process.h> + +#include <rtl/character.hxx> +#include <rtl/string.h> +#include <rtl/ustring.h> +#include <sal/log.hxx> + +#include <osl/module.h> + +#include <tools/debug.hxx> +#include <o3tl/enumarray.hxx> +#include <o3tl/char16_t2wchar_t.hxx> + +#include <vcl/event.hxx> +#include <vcl/sysdata.hxx> +#include <vcl/timer.hxx> +#include <vcl/settings.hxx> +#include <vcl/keycodes.hxx> +#include <vcl/window.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/svapp.hxx> +#include <vcl/ptrstyle.hxx> + +#include <win/wincomp.hxx> +#include <win/salids.hrc> +#include <win/saldata.hxx> +#include <win/salinst.h> +#include <win/salbmp.h> +#include <win/salgdi.h> +#include <win/salsys.h> +#include <win/salframe.h> +#include <win/salvd.h> +#include <win/salmenu.h> +#include <win/salobj.h> +#include <win/saltimer.h> + +#include <helpwin.hxx> +#include <window.h> +#include <sallayout.hxx> + +#define COMPILE_MULTIMON_STUBS +#pragma warning(push) +#pragma warning(disable:4996) // 'GetVersionExA': was declared deprecated +#include <multimon.h> +#pragma warning(pop) +#include <vector> + +#include <com/sun/star/uno/Exception.hpp> + +#include <oleacc.h> +#include <com/sun/star/accessibility/XMSAAService.hpp> + +#include <time.h> + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <dwmapi.h> +#include <shobjidl.h> +#include <propkey.h> +#include <propvarutil.h> +#include <shellapi.h> +#include <uxtheme.h> +#include <Vssym32.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; + +#ifndef SPI_GETWHEELSCROLLCHARS +# define SPI_GETWHEELSCROLLCHARS 0x006C +#endif +#ifndef SPI_SETWHEELSCROLLCHARS +# define SPI_SETWHEELSCROLLCHARS 0x006D +#endif +#ifndef WM_MOUSEHWHEEL +# define WM_MOUSEHWHEEL 0x020E +#endif +#ifndef IDC_PEN +# define IDC_PEN MAKEINTRESOURCE(32631) +#endif + +const unsigned int WM_USER_SYSTEM_WINDOW_ACTIVATED = RegisterWindowMessageW(L"SYSTEM_WINDOW_ACTIVATED"); + +bool WinSalFrame::mbInReparent = false; + +// Macros for support of WM_UNICHAR & Keyman 6.0 +#define Uni_SupplementaryPlanesStart 0x10000 + +static void UpdateFrameGeometry( HWND hWnd, WinSalFrame* pFrame ); +static void SetMaximizedFrameGeometry( HWND hWnd, WinSalFrame* pFrame, RECT* pParentRect = nullptr ); + +static void UpdateGeometry(WinSalFrame* pFrame, RECT& aRect) +{ + RECT aRect2 = aRect; + AdjustWindowRectEx(&aRect2, GetWindowStyle(pFrame->mhWnd), + FALSE, GetWindowExStyle(pFrame->mhWnd)); + tools::Long nTopDeco = abs(aRect.top - aRect2.top); + tools::Long nLeftDeco = abs(aRect.left - aRect2.left); + tools::Long nBottomDeco = abs(aRect.bottom - aRect2.bottom); + tools::Long nRightDeco = abs(aRect.right - aRect2.right); + + pFrame->maState.mnX = aRect.left + nLeftDeco; + pFrame->maState.mnY = aRect.top + nTopDeco; + pFrame->maState.mnWidth = aRect.right - aRect.left - nLeftDeco - nRightDeco; + pFrame->maState.mnHeight = aRect.bottom - aRect.top - nTopDeco - nBottomDeco; +} + +static void ImplSaveFrameState( WinSalFrame* pFrame ) +{ + // save position, size and state for GetWindowState() + if ( !pFrame->mbFullScreen ) + { + bool bVisible = (GetWindowStyle( pFrame->mhWnd ) & WS_VISIBLE) != 0; + if ( IsIconic( pFrame->mhWnd ) ) + { + pFrame->maState.mnState |= WindowStateState::Minimized; + if ( bVisible ) + pFrame->mnShowState = SW_SHOWMAXIMIZED; + } + else if ( IsZoomed( pFrame->mhWnd ) ) + { + pFrame->maState.mnState &= ~WindowStateState::Minimized; + pFrame->maState.mnState |= WindowStateState::Maximized; + if ( bVisible ) + pFrame->mnShowState = SW_SHOWMAXIMIZED; + pFrame->mbRestoreMaximize = true; + + WINDOWPLACEMENT aPlacement; + aPlacement.length = sizeof(aPlacement); + if( GetWindowPlacement( pFrame->mhWnd, &aPlacement ) ) + UpdateGeometry(pFrame, aPlacement.rcNormalPosition); + } + else + { + RECT aRect; + GetWindowRect( pFrame->mhWnd, &aRect ); + UpdateGeometry(pFrame, aRect); + + pFrame->maState.mnState &= ~WindowStateState(WindowStateState::Minimized | WindowStateState::Maximized); + if ( bVisible ) + pFrame->mnShowState = SW_SHOWNORMAL; + pFrame->mbRestoreMaximize = false; + } + } +} + +// if pParentRect is set, the workarea of the monitor that contains pParentRect is returned +void ImplSalGetWorkArea( HWND hWnd, RECT *pRect, const RECT *pParentRect ) +{ + // 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 + static int 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 ); + + pRect->left += wRect.left; + pRect->top += wRect.top; + pRect->right -= scrRect.right - wRect.right; + pRect->bottom -= scrRect.bottom - wRect.bottom; + } + } + } +} + +static void UpdateDarkMode(HWND hWnd) +{ + static bool bExperimental = officecfg::Office::Common::Misc::ExperimentalMode::get(); + if (!bExperimental) + return; + static bool bOSSupportsDarkMode = OSSupportsDarkMode(); + if (!bOSSupportsDarkMode) + return; + + HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (!hUxthemeLib) + return; + + typedef void(WINAPI* AllowDarkModeForWindow_t)(HWND, BOOL); + if (auto AllowDarkModeForWindow = reinterpret_cast<AllowDarkModeForWindow_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(133)))) + AllowDarkModeForWindow(hWnd, TRUE); + + typedef bool(WINAPI* ShouldAppsUseDarkMode_t)(); + if (auto ShouldAppsUseDarkMode = reinterpret_cast<ShouldAppsUseDarkMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(132)))) + { + BOOL bDarkMode = ShouldAppsUseDarkMode(); + DwmSetWindowAttribute(hWnd, 20, &bDarkMode, sizeof(bDarkMode)); + } + + FreeLibrary(hUxthemeLib); +} + +SalFrame* ImplSalCreateFrame( WinSalInstance* pInst, + HWND hWndParent, SalFrameStyleFlags nSalFrameStyle ) +{ + WinSalFrame* pFrame = new WinSalFrame; + HWND hWnd; + DWORD nSysStyle = 0; + DWORD nExSysStyle = 0; + bool bSubFrame = false; + + static const char* pEnvSynchronize = getenv("SAL_SYNCHRONIZE"); + if ( pEnvSynchronize ) // no buffering of drawing commands + GdiSetBatchLimit( 1 ); + + static const char* pEnvTransparentFloats = getenv("SAL_TRANSPARENT_FLOATS" ); + + // 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::MOVEABLE ) + { + pFrame->mbCaption = true; + nSysStyle |= WS_SYSMENU | WS_CAPTION; + if ( !hWndParent ) + nSysStyle |= WS_SYSMENU | WS_MINIMIZEBOX; + else + nExSysStyle |= WS_EX_DLGMODALFRAME; + + if ( nSalFrameStyle & SalFrameStyleFlags::SIZEABLE ) + { + pFrame->mbSizeBorder = true; + nSysStyle |= WS_THICKFRAME; + if ( !hWndParent ) + nSysStyle |= WS_MAXIMIZEBOX; + } + else + pFrame->mbFixBorder = true; + + 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 + + // #103578# looked too bad - above changes reverted + /* && !(nSalFrameStyle & SalFrameStyleFlags::SIZEABLE) */ ) + { + pFrame->mbNoIcon = true; + nExSysStyle |= WS_EX_TOOLWINDOW; + if ( pEnvTransparentFloats /*&& !(nSalFrameStyle & SalFrameStyleFlags::MOVEABLE) */) + nExSysStyle |= WS_EX_LAYERED; + } + } + if ( nSalFrameStyle & SalFrameStyleFlags::FLOAT ) + { + nExSysStyle |= WS_EX_TOOLWINDOW; + pFrame->mbFloatWin = true; + + if (pEnvTransparentFloats) + nExSysStyle |= WS_EX_LAYERED; + + } + if (nSalFrameStyle & SalFrameStyleFlags::TOOLTIP) + nExSysStyle |= WS_EX_TOPMOST; + + // init frame data + pFrame->mnStyle = nSalFrameStyle; + + // determine show style + pFrame->mnShowState = SW_SHOWNORMAL; + if ( (nSysStyle & (WS_POPUP | WS_MAXIMIZEBOX | WS_THICKFRAME)) == (WS_MAXIMIZEBOX | WS_THICKFRAME) ) + { + if ( GetSystemMetrics( SM_CXSCREEN ) <= 1024 ) + pFrame->mnShowState = SW_SHOWMAXIMIZED; + else + { + if ( nSalFrameStyle & SalFrameStyleFlags::DEFAULT ) + { + SalData* pSalData = GetSalData(); + pFrame->mnShowState = pSalData->mnCmdShow; + if ( (pFrame->mnShowState != SW_SHOWMINIMIZED) && + (pFrame->mnShowState != SW_MINIMIZE) && + (pFrame->mnShowState != SW_SHOWMINNOACTIVE) ) + { + if ( (pFrame->mnShowState == SW_SHOWMAXIMIZED) || + (pFrame->mnShowState == SW_MAXIMIZE) ) + pFrame->mbOverwriteState = false; + pFrame->mnShowState = SW_SHOWMAXIMIZED; + } + else + pFrame->mbOverwriteState = false; + } + else + { + // Document Windows are also maximized, if the current Document Window + // is also maximized + HWND hWnd2 = GetForegroundWindow(); + if ( hWnd2 && IsMaximized( hWnd2 ) && + (GetWindowInstance( hWnd2 ) == pInst->mhInst) && + ((GetWindowStyle( hWnd2 ) & (WS_POPUP | WS_MAXIMIZEBOX | WS_THICKFRAME)) == (WS_MAXIMIZEBOX | WS_THICKFRAME)) ) + pFrame->mnShowState = SW_SHOWMAXIMIZED; + } + } + } + + // create frame + LPCWSTR pClassName; + if ( bSubFrame ) + { + if ( nSalFrameStyle & (SalFrameStyleFlags::MOVEABLE|SalFrameStyleFlags::NOSHADOW) ) // check if shadow not wanted + pClassName = SAL_SUBFRAME_CLASSNAMEW; + else + pClassName = SAL_TMPSUBFRAME_CLASSNAMEW; // undecorated floaters will get shadow on XP + } + else + { + if ( nSalFrameStyle & SalFrameStyleFlags::MOVEABLE ) + pClassName = SAL_FRAME_CLASSNAMEW; + else + pClassName = SAL_TMPSUBFRAME_CLASSNAMEW; + } + hWnd = CreateWindowExW( nExSysStyle, pClassName, L"", nSysStyle, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, + hWndParent, nullptr, pInst->mhInst, pFrame ); + SAL_WARN_IF(!hWnd, "vcl", "CreateWindowExW failed: " << WindowsErrorString(GetLastError())); + +#if OSL_DEBUG_LEVEL > 1 + // set transparency value + if( GetWindowExStyle( hWnd ) & WS_EX_LAYERED ) + SetLayeredWindowAttributes( hWnd, 0, 230, 0x00000002 /*LWA_ALPHA*/ ); +#endif + if ( !hWnd ) + { + delete pFrame; + return nullptr; + } + + // If we have a Window with a Caption Bar and without + // a MaximizeBox, we change the SystemMenu + if ( (nSysStyle & (WS_CAPTION | WS_MAXIMIZEBOX)) == (WS_CAPTION) ) + { + HMENU hSysMenu = GetSystemMenu( hWnd, FALSE ); + if ( hSysMenu ) + { + if ( !(nSysStyle & (WS_MINIMIZEBOX | WS_MAXIMIZEBOX)) ) + DeleteMenu( hSysMenu, SC_RESTORE, MF_BYCOMMAND ); + else + EnableMenuItem( hSysMenu, SC_RESTORE, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED ); + if ( !(nSysStyle & WS_MINIMIZEBOX) ) + DeleteMenu( hSysMenu, SC_MINIMIZE, MF_BYCOMMAND ); + if ( !(nSysStyle & WS_MAXIMIZEBOX) ) + DeleteMenu( hSysMenu, SC_MAXIMIZE, MF_BYCOMMAND ); + if ( !(nSysStyle & WS_THICKFRAME) ) + DeleteMenu( hSysMenu, SC_SIZE, MF_BYCOMMAND ); + } + } + if ( (nSysStyle & WS_SYSMENU) && !(nSalFrameStyle & SalFrameStyleFlags::CLOSEABLE) ) + { + HMENU hSysMenu = GetSystemMenu( hWnd, FALSE ); + if ( hSysMenu ) + EnableMenuItem( hSysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED | MF_DISABLED ); + } + + // reset input context + pFrame->mhDefIMEContext = ImmAssociateContext( hWnd, nullptr ); + + // determine output size and state + RECT aRect; + GetClientRect( hWnd, &aRect ); + pFrame->mnWidth = aRect.right; + pFrame->mnHeight = aRect.bottom; + ImplSaveFrameState( pFrame ); + pFrame->mbDefPos = true; + + UpdateFrameGeometry( hWnd, pFrame ); + + 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() + + SetMaximizedFrameGeometry( hWnd, pFrame ); + } + + 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 ); + + if( bAsChild ) + { + nSysStyle = WS_CHILD; + nExSysStyle = 0; + } + + LPCWSTR pClassName = SAL_SUBFRAME_CLASSNAMEW; + return CreateWindowExW( nExSysStyle, pClassName, L"", nSysStyle, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, + hWndParent, nullptr, hInstance, GetWindowPtr( oldhWnd ) ); +} + +// translation table from System keycodes into StartView keycodes +#define KEY_TAB_SIZE 146 + +const sal_uInt16 aImplTranslateKeyTab[KEY_TAB_SIZE] = +{ + // StarView-Code System-Code Index + 0, // 0 + 0, // VK_LBUTTON 1 + 0, // VK_RBUTTON 2 + 0, // VK_CANCEL 3 + 0, // VK_MBUTTON 4 + 0, // 5 + 0, // 6 + 0, // 7 + KEY_BACKSPACE, // VK_BACK 8 + KEY_TAB, // VK_TAB 9 + 0, // 10 + 0, // 11 + 0, // VK_CLEAR 12 + KEY_RETURN, // VK_RETURN 13 + 0, // 14 + 0, // 15 + 0, // VK_SHIFT 16 + 0, // VK_CONTROL 17 + 0, // VK_MENU 18 + 0, // VK_PAUSE 19 + 0, // VK_CAPITAL 20 + 0, // VK_HANGUL 21 + 0, // 22 + 0, // 23 + 0, // 24 + KEY_HANGUL_HANJA, // VK_HANJA 25 + 0, // 26 + KEY_ESCAPE, // VK_ESCAPE 27 + 0, // 28 + 0, // 29 + 0, // 30 + 0, // 31 + KEY_SPACE, // VK_SPACE 32 + KEY_PAGEUP, // VK_PRIOR 33 + KEY_PAGEDOWN, // VK_NEXT 34 + KEY_END, // VK_END 35 + KEY_HOME, // VK_HOME 36 + KEY_LEFT, // VK_LEFT 37 + KEY_UP, // VK_UP 38 + KEY_RIGHT, // VK_RIGHT 39 + KEY_DOWN, // VK_DOWN 40 + 0, // VK_SELECT 41 + 0, // VK_PRINT 42 + 0, // VK_EXECUTE 43 + 0, // VK_SNAPSHOT 44 + KEY_INSERT, // VK_INSERT 45 + KEY_DELETE, // VK_DELETE 46 + KEY_HELP, // VK_HELP 47 + KEY_0, // 48 + KEY_1, // 49 + KEY_2, // 50 + KEY_3, // 51 + KEY_4, // 52 + KEY_5, // 53 + KEY_6, // 54 + KEY_7, // 55 + KEY_8, // 56 + KEY_9, // 57 + 0, // 58 + 0, // 59 + 0, // 60 + 0, // 61 + 0, // 62 + 0, // 63 + 0, // 64 + KEY_A, // 65 + KEY_B, // 66 + KEY_C, // 67 + KEY_D, // 68 + KEY_E, // 69 + KEY_F, // 70 + KEY_G, // 71 + KEY_H, // 72 + KEY_I, // 73 + KEY_J, // 74 + KEY_K, // 75 + KEY_L, // 76 + KEY_M, // 77 + KEY_N, // 78 + KEY_O, // 79 + KEY_P, // 80 + KEY_Q, // 81 + KEY_R, // 82 + KEY_S, // 83 + KEY_T, // 84 + KEY_U, // 85 + KEY_V, // 86 + KEY_W, // 87 + KEY_X, // 88 + KEY_Y, // 89 + KEY_Z, // 90 + 0, // VK_LWIN 91 + 0, // VK_RWIN 92 + KEY_CONTEXTMENU, // VK_APPS 93 + 0, // 94 + 0, // 95 + KEY_0, // VK_NUMPAD0 96 + KEY_1, // VK_NUMPAD1 97 + KEY_2, // VK_NUMPAD2 98 + KEY_3, // VK_NUMPAD3 99 + KEY_4, // VK_NUMPAD4 100 + KEY_5, // VK_NUMPAD5 101 + KEY_6, // VK_NUMPAD6 102 + KEY_7, // VK_NUMPAD7 103 + KEY_8, // VK_NUMPAD8 104 + KEY_9, // VK_NUMPAD9 105 + KEY_MULTIPLY, // VK_MULTIPLY 106 + KEY_ADD, // VK_ADD 107 + KEY_DECIMAL, // VK_SEPARATOR 108 + KEY_SUBTRACT, // VK_SUBTRACT 109 + KEY_DECIMAL, // VK_DECIMAL 110 + KEY_DIVIDE, // VK_DIVIDE 111 + KEY_F1, // VK_F1 112 + KEY_F2, // VK_F2 113 + KEY_F3, // VK_F3 114 + KEY_F4, // VK_F4 115 + KEY_F5, // VK_F5 116 + KEY_F6, // VK_F6 117 + KEY_F7, // VK_F7 118 + KEY_F8, // VK_F8 119 + KEY_F9, // VK_F9 120 + KEY_F10, // VK_F10 121 + KEY_F11, // VK_F11 122 + KEY_F12, // VK_F12 123 + KEY_F13, // VK_F13 124 + KEY_F14, // VK_F14 125 + KEY_F15, // VK_F15 126 + KEY_F16, // VK_F16 127 + KEY_F17, // VK_F17 128 + KEY_F18, // VK_F18 129 + KEY_F19, // VK_F19 130 + KEY_F20, // VK_F20 131 + KEY_F21, // VK_F21 132 + KEY_F22, // VK_F22 133 + KEY_F23, // VK_F23 134 + KEY_F24, // VK_F24 135 + 0, // 136 + 0, // 137 + 0, // 138 + 0, // 139 + 0, // 140 + 0, // 141 + 0, // 142 + 0, // 143 + 0, // NUMLOCK 144 + 0 // SCROLLLOCK 145 +}; + +static UINT ImplSalGetWheelScrollLines() +{ + UINT nScrLines = 0; + HWND hWndMsWheel = FindWindowW( MSH_WHEELMODULE_CLASS, MSH_WHEELMODULE_TITLE ); + if ( hWndMsWheel ) + { + UINT nGetScrollLinesMsgId = RegisterWindowMessageW( MSH_SCROLL_LINES ); + nScrLines = static_cast<UINT>(SendMessageW( hWndMsWheel, nGetScrollLinesMsgId, 0, 0 )); + } + + if ( !nScrLines ) + if( !SystemParametersInfoW( SPI_GETWHEELSCROLLLINES, 0, &nScrLines, 0 ) ) + nScrLines = 0 ; + + if ( !nScrLines ) + nScrLines = 3; + + return nScrLines; +} + +static UINT ImplSalGetWheelScrollChars() +{ + UINT nScrChars = 0; + if( !SystemParametersInfoW( SPI_GETWHEELSCROLLCHARS, 0, &nScrChars, 0 ) ) + { + return 3; + } + + // system settings successfully read + return nScrChars; +} + +static void ImplSalAddBorder( const WinSalFrame* pFrame, int& width, int& height ) +{ + // transform client size into window size + RECT aWinRect; + aWinRect.left = 0; + aWinRect.right = width-1; + aWinRect.top = 0; + aWinRect.bottom = height-1; + AdjustWindowRectEx( &aWinRect, GetWindowStyle( pFrame->mhWnd ), + FALSE, GetWindowExStyle( pFrame->mhWnd ) ); + width = aWinRect.right - aWinRect.left + 1; + height = aWinRect.bottom - aWinRect.top + 1; +} + +static void 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; + + if ( pFrame->mbSizeBorder ) + { + nFrameX = GetSystemMetrics( SM_CXSIZEFRAME ); + nFrameY = GetSystemMetrics( SM_CYSIZEFRAME ); + } + else if ( pFrame->mbFixBorder ) + { + nFrameX = GetSystemMetrics( SM_CXFIXEDFRAME ); + nFrameY = GetSystemMetrics( SM_CYFIXEDFRAME ); + } + else if ( pFrame->mbBorder ) + { + nFrameX = GetSystemMetrics( SM_CXBORDER ); + nFrameY = GetSystemMetrics( SM_CYBORDER ); + } + else + { + nFrameX = 0; + nFrameY = 0; + } + if ( pFrame->mbCaption ) + nCaptionY = GetSystemMetrics( SM_CYCAPTION ); + else + nCaptionY = 0; + + try + { + sal_Int32 nMonitors = Application::GetScreenCount(); + if( (pFrame->mnDisplay >= 0) && (pFrame->mnDisplay < nMonitors) ) + { + tools::Rectangle aRect = Application::GetScreenPosSizePixel( pFrame->mnDisplay ); + nScreenX = aRect.Left(); + nScreenY = aRect.Top(); + nScreenDX = aRect.GetWidth(); + nScreenDY = aRect.GetHeight(); + } + else + { + tools::Rectangle aCombined = Application::GetScreenPosSizePixel( 0 ); + for( sal_Int32 i = 1 ; i < nMonitors ; i++ ) + { + aCombined.Union( Application::GetScreenPosSizePixel( i ) ); + } + nScreenX = aCombined.Left(); + nScreenY = aCombined.Top(); + nScreenDX = aCombined.GetWidth(); + nScreenDY = aCombined.GetHeight(); + } + } + catch( Exception& ) + { + } + + if( !nScreenDX || !nScreenDY ) + { + nScreenDX = GetSystemMetrics( SM_CXSCREEN ); + nScreenDY = GetSystemMetrics( SM_CYSCREEN ); + } + + rX = nScreenX -nFrameX; + rY = nScreenY -(nFrameY+nCaptionY); + rDX = nScreenDX+(nFrameX*2); + rDY = nScreenDY+(nFrameY*2)+nCaptionY; +} + +static void ImplSalFrameFullScreenPos( WinSalFrame* pFrame, bool bAlways = false ) +{ + if ( bAlways || !IsIconic( pFrame->mhWnd ) ) + { + // set window to screen size + int nX; + int nY; + int nWidth; + int nHeight; + ImplSalCalcFullScreenSize( pFrame, nX, nY, nWidth, nHeight ); + SetWindowPos( pFrame->mhWnd, nullptr, + nX, nY, nWidth, nHeight, + SWP_NOZORDER | SWP_NOACTIVATE ); + } +} + +namespace { + +void SetForegroundWindow_Impl(HWND hwnd) +{ + if (!Application::IsHeadlessModeEnabled()) + SetForegroundWindow(hwnd); +} + +} + +WinSalFrame::WinSalFrame() +{ + SalData* pSalData = GetSalData(); + + mhWnd = nullptr; + mhCursor = LoadCursor( nullptr, IDC_ARROW ); + mhDefIMEContext = nullptr; + mpLocalGraphics = nullptr; + mpThreadGraphics = nullptr; + mnShowState = SW_SHOWNORMAL; + mnWidth = 0; + mnHeight = 0; + mnMinWidth = 0; + mnMinHeight = 0; + mnMaxWidth = SHRT_MAX; + mnMaxHeight = SHRT_MAX; + mnInputLang = 0; + mnInputCodePage = 0; + mbGraphics = false; + mbCaption = false; + mbBorder = false; + mbFixBorder = false; + mbSizeBorder = false; + mbFullScreenCaption = false; + mbFullScreen = false; + mbPresentation = false; + mbInShow = false; + mbRestoreMaximize = false; + mbInMoveMsg = false; + mbInSizeMsg = false; + mbFullScreenToolWin = false; + mbDefPos = true; + mbOverwriteState = true; + mbIME = false; + mbHandleIME = false; + mbSpezIME = false; + mbAtCursorIME = false; + mbCandidateMode = false; + mbFloatWin = false; + mbNoIcon = false; + mSelectedhMenu = nullptr; + mLastActivatedhMenu = nullptr; + mpClipRgnData = nullptr; + mbFirstClipRect = true; + mpNextClipRect = nullptr; + mnDisplay = 0; + mbPropertiesStored = false; + + // get data, when making 1st frame + if ( !pSalData->mpFirstFrame ) + { + if ( !aSalShlData.mnWheelScrollLines ) + aSalShlData.mnWheelScrollLines = ImplSalGetWheelScrollLines(); + if ( !aSalShlData.mnWheelScrollChars ) + aSalShlData.mnWheelScrollChars = ImplSalGetWheelScrollChars(); + } + + // insert frame in framelist + mpNextFrame = pSalData->mpFirstFrame; + pSalData->mpFirstFrame = this; +} + +void WinSalFrame::updateScreenNumber() +{ + if( mnDisplay == -1 ) // spans all monitors + return; + WinSalSystem* pSys = static_cast<WinSalSystem*>(ImplGetSalSystem()); + if( pSys ) + { + const std::vector<WinSalSystem::DisplayMonitor>& rMonitors = + pSys->getMonitors(); + Point aPoint( maGeometry.nX, maGeometry.nY ); + size_t nMon = rMonitors.size(); + for( size_t i = 0; i < nMon; i++ ) + { + if( rMonitors[i].m_aArea.Contains( aPoint ) ) + { + mnDisplay = static_cast<sal_Int32>(i); + maGeometry.nDisplayScreenNumber = static_cast<unsigned int>(i); + } + } + } +} + +bool WinSalFrame::ReleaseFrameGraphicsDC( WinSalGraphics* pGraphics ) +{ + assert( pGraphics ); + SalData* pSalData = GetSalData(); + HDC hDC = pGraphics->getHDC(); + if ( !hDC ) + return false; + pGraphics->setHDC(nullptr); + SendMessageW( pSalData->mpInstance->mhComWnd, SAL_MSG_RELEASEDC, + reinterpret_cast<WPARAM>(mhWnd), reinterpret_cast<LPARAM>(hDC) ); + if ( pGraphics == mpThreadGraphics ) + pSalData->mnCacheDCInUse--; + return true; +} + +WinSalFrame::~WinSalFrame() +{ + SalData* pSalData = GetSalData(); + + if( mpClipRgnData ) + delete [] reinterpret_cast<BYTE*>(mpClipRgnData); + + // remove frame from framelist + WinSalFrame** ppFrame = &pSalData->mpFirstFrame; + for(; (*ppFrame != this) && *ppFrame; ppFrame = &(*ppFrame)->mpNextFrame ); + if( *ppFrame ) + *ppFrame = mpNextFrame; + mpNextFrame = nullptr; + + // destroy the thread SalGraphics + if ( mpThreadGraphics ) + { + ReleaseFrameGraphicsDC( mpThreadGraphics ); + delete mpThreadGraphics; + mpThreadGraphics = nullptr; + } + + // destroy the local SalGraphics + if ( mpLocalGraphics ) + { + ReleaseFrameGraphicsDC( mpLocalGraphics ); + delete mpLocalGraphics; + mpLocalGraphics = nullptr; + } + + if ( mhWnd ) + { + // reset mouse leave data + if ( pSalData->mhWantLeaveMsg == mhWnd ) + { + pSalData->mhWantLeaveMsg = nullptr; + if ( pSalData->mpMouseLeaveTimer ) + { + delete pSalData->mpMouseLeaveTimer; + pSalData->mpMouseLeaveTimer = nullptr; + } + } + + // remove windows properties + if ( mbPropertiesStored ) + SetApplicationID( OUString() ); + + // destroy system frame + if ( !DestroyWindow( mhWnd ) ) + SetWindowPtr( mhWnd, nullptr ); + + mhWnd = nullptr; + } +} + +bool WinSalFrame::InitFrameGraphicsDC( WinSalGraphics *pGraphics, HDC hDC, HWND hWnd ) +{ + SalData* pSalData = GetSalData(); + assert( pGraphics ); + pGraphics->setHWND( hWnd ); + + HDC hCurrentDC = pGraphics->getHDC(); + assert( !hCurrentDC || (hCurrentDC == hDC) ); + if ( hCurrentDC ) + return true; + pGraphics->setHDC( hDC ); + + if ( !hDC ) + return false; + + if ( pSalData->mhDitherPal ) + pGraphics->setPalette(pSalData->mhDitherPal); + + if ( pGraphics == mpThreadGraphics ) + pSalData->mnCacheDCInUse++; + return true; +} + +SalGraphics* WinSalFrame::AcquireGraphics() +{ + if ( mbGraphics || !mhWnd ) + return nullptr; + + SalData* pSalData = GetSalData(); + WinSalGraphics *pGraphics = nullptr; + HDC hDC = nullptr; + + // 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() ) + { + // We use only three CacheDC's for all threads, because W9x is limited + // to max. 5 Cache DC's per thread + if ( pSalData->mnCacheDCInUse >= 3 ) + return nullptr; + + if ( !mpThreadGraphics ) + mpThreadGraphics = new WinSalGraphics(WinSalGraphics::WINDOW, true, mhWnd, this); + pGraphics = mpThreadGraphics; + assert( !pGraphics->getHDC() ); + hDC = reinterpret_cast<HDC>(static_cast<sal_IntPtr>(SendMessageW( pSalData->mpInstance->mhComWnd, + SAL_MSG_GETCACHEDDC, reinterpret_cast<WPARAM>(mhWnd), 0 ))); + } + else + { + if ( !mpLocalGraphics ) + mpLocalGraphics = new WinSalGraphics(WinSalGraphics::WINDOW, true, mhWnd, this); + pGraphics = mpLocalGraphics; + hDC = pGraphics->getHDC(); + if ( !hDC ) + hDC = GetDC( mhWnd ); + } + + mbGraphics = InitFrameGraphicsDC( pGraphics, hDC, mhWnd ); + return mbGraphics ? pGraphics : nullptr; +} + +void WinSalFrame::ReleaseGraphics( SalGraphics* pGraphics ) +{ + if ( mpThreadGraphics == pGraphics ) + ReleaseFrameGraphicsDC( mpThreadGraphics ); + mbGraphics = false; +} + +bool WinSalFrame::PostEvent(std::unique_ptr<ImplSVEvent> pData) +{ + bool const ret = PostMessageW(mhWnd, SAL_MSG_USEREVENT, 0, reinterpret_cast<LPARAM>(pData.release())); + SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!"); + return ret; +} + +void WinSalFrame::SetTitle( const OUString& rTitle ) +{ + static_assert( sizeof( WCHAR ) == sizeof( sal_Unicode ), "must be the same size" ); + + SetWindowTextW( mhWnd, o3tl::toW(rTitle.getStr()) ); +} + +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 !" ); + + SendMessageW( mhWnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon) ); + SendMessageW( mhWnd, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hSmIcon) ); +} + +void WinSalFrame::SetMenu( SalMenu* pSalMenu ) +{ + WinSalMenu* pWMenu = static_cast<WinSalMenu*>(pSalMenu); + if( pSalMenu && pWMenu->mbMenuBar ) + ::SetMenu( mhWnd, pWMenu->mhMenu ); +} + +void WinSalFrame::DrawMenuBar() +{ + ::DrawMenuBar( mhWnd ); +} + +static HWND ImplGetParentHwnd( HWND hWnd ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if( !pFrame || !pFrame->GetWindow()) + return ::GetParent( hWnd ); + vcl::Window *pRealParent = pFrame->GetWindow()->ImplGetWindowImpl()->mpRealParent; + if( pRealParent ) + return static_cast<WinSalFrame*>(pRealParent->ImplGetWindowImpl()->mpFrame)->mhWnd; + else + return ::GetParent( hWnd ); + +} + +SalFrame* WinSalFrame::GetParent() const +{ + return GetWindowPtr( ImplGetParentHwnd( mhWnd ) ); +} + +static void ImplSalShow( HWND hWnd, bool bVisible, bool bNoActivate ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( !pFrame ) + return; + + if ( bVisible ) + { + pFrame->mbDefPos = false; + pFrame->mbOverwriteState = true; + pFrame->mbInShow = true; + + // #i4715, save position + RECT aRectPreMatrox, aRectPostMatrox; + GetWindowRect( hWnd, &aRectPreMatrox ); + + vcl::DeletionListener aDogTag( pFrame ); + if( bNoActivate ) + ShowWindow( hWnd, SW_SHOWNOACTIVATE ); + else + ShowWindow( hWnd, pFrame->mnShowState ); + if( aDogTag.isDeleted() ) + return; + + 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 ); + } + + // #i4715, matrox centerpopup might have changed our position + // reposition popups without caption (menus, dropdowns, tooltips) + GetWindowRect( hWnd, &aRectPostMatrox ); + if( (GetWindowStyle( hWnd ) & WS_POPUP) && + !pFrame->mbCaption && + (aRectPreMatrox.left != aRectPostMatrox.left || aRectPreMatrox.top != aRectPostMatrox.top) ) + SetWindowPos( hWnd, nullptr, aRectPreMatrox.left, aRectPreMatrox.top, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE ); + + if( aDogTag.isDeleted() ) + return; + vcl::Window *pClientWin = pFrame->GetWindow()->ImplGetClientWindow(); + if ( pFrame->mbFloatWin || ( pClientWin && (pClientWin->GetStyle() & WB_SYSTEMFLOATWIN) ) ) + pFrame->mnShowState = SW_SHOWNOACTIVATE; + else + pFrame->mnShowState = SW_SHOW; + // hide toolbar for W98 + if ( pFrame->mbPresentation ) + { + HWND hWndParent = ::GetParent( hWnd ); + if ( hWndParent ) + SetForegroundWindow_Impl( hWndParent ); + SetForegroundWindow_Impl( hWnd ); + } + + pFrame->mbInShow = false; + pFrame->updateScreenNumber(); + + // Direct Paint only, if we get the SolarMutex + if ( ImplSalYieldMutexTryToAcquire() ) + { + UpdateWindow( hWnd ); + ImplSalYieldMutexRelease(); + } + } + else + { + ShowWindow( hWnd, SW_HIDE ); + } +} + +void WinSalFrame::SetExtendedFrameStyle( SalExtStyle ) +{ +} + +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() ) + { + bool const ret = PostMessageW(mhWnd, SAL_MSG_SHOW, WPARAM(bVisible), LPARAM(bNoActivate)); + SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!"); + } + else + ImplSalShow( mhWnd, bVisible, bNoActivate ); +} + +void WinSalFrame::SetMinClientSize( tools::Long nWidth, tools::Long nHeight ) +{ + mnMinWidth = nWidth; + mnMinHeight = nHeight; +} + +void WinSalFrame::SetMaxClientSize( tools::Long nWidth, tools::Long nHeight ) +{ + mnMaxWidth = nWidth; + mnMaxHeight = nHeight; +} + +void WinSalFrame::SetPosSize( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, + sal_uInt16 nFlags ) +{ + bool bVisible = (GetWindowStyle( mhWnd ) & WS_VISIBLE) != 0; + if ( !bVisible ) + { + vcl::Window *pClientWin = GetWindow()->ImplGetClientWindow(); + if ( mbFloatWin || ( pClientWin && (pClientWin->GetStyle() & WB_SYSTEMFLOATWIN) ) ) + mnShowState = SW_SHOWNOACTIVATE; + else + mnShowState = SW_SHOWNORMAL; + } + else + { + if ( IsIconic( mhWnd ) || IsZoomed( mhWnd ) ) + ShowWindow( mhWnd, SW_RESTORE ); + } + + SalEvent nEvent = SalEvent::NONE; + UINT nPosSize = 0; + RECT aClientRect, aWindowRect; + GetClientRect( mhWnd, &aClientRect ); // x,y always 0,0, but width and height without border + GetWindowRect( mhWnd, &aWindowRect ); // x,y in screen coordinates, width and height with border + + if ( !(nFlags & (SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y)) ) + nPosSize |= SWP_NOMOVE; + else + { + //SAL_WARN_IF( !nX || !nY, "vcl", " Windowposition of (0,0) requested!" ); + nEvent = SalEvent::Move; + } + if ( !(nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT)) ) + nPosSize |= SWP_NOSIZE; + else + nEvent = (nEvent == SalEvent::Move) ? SalEvent::MoveResize : SalEvent::Resize; + + if ( !(nFlags & SAL_FRAME_POSSIZE_X) ) + nX = aWindowRect.left; + if ( !(nFlags & SAL_FRAME_POSSIZE_Y) ) + nY = aWindowRect.top; + if ( !(nFlags & SAL_FRAME_POSSIZE_WIDTH) ) + nWidth = aClientRect.right-aClientRect.left; + if ( !(nFlags & SAL_FRAME_POSSIZE_HEIGHT) ) + nHeight = aClientRect.bottom-aClientRect.top; + + // Calculate window size including the border + RECT aWinRect; + aWinRect.left = 0; + aWinRect.right = static_cast<int>(nWidth)-1; + aWinRect.top = 0; + aWinRect.bottom = static_cast<int>(nHeight)-1; + AdjustWindowRectEx( &aWinRect, GetWindowStyle( mhWnd ), + FALSE, GetWindowExStyle( mhWnd ) ); + nWidth = aWinRect.right - aWinRect.left + 1; + nHeight = aWinRect.bottom - aWinRect.top + 1; + + if ( !(nPosSize & SWP_NOMOVE) && ::GetParent( mhWnd ) ) + { + RECT aParentRect; + GetClientRect( ImplGetParentHwnd( mhWnd ), &aParentRect ); + if( AllSettings::GetLayoutRTL() ) + nX = (aParentRect.right - aParentRect.left) - nWidth-1 - nX; + + //#110386#, do not transform coordinates for system child windows + if( !(GetWindowStyle( mhWnd ) & WS_CHILD) ) + { + POINT aPt; + aPt.x = nX; + aPt.y = nY; + + HWND parentHwnd = ImplGetParentHwnd( mhWnd ); + WinSalFrame* pParentFrame = GetWindowPtr( parentHwnd ); + 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.nX; + aPt.y += pParentFrame->maGeometry.nY; + } + else + ClientToScreen( parentHwnd, &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; + + RECT aRect; + ImplSalGetWorkArea( mhWnd, &aRect, nullptr ); + nScreenX = aRect.left; + nScreenY = aRect.top; + nScreenWidth = aRect.right-aRect.left; + nScreenHeight = aRect.bottom-aRect.top; + + if ( mbDefPos && (nPosSize & SWP_NOMOVE)) // we got no positioning request, so choose default position + { + // center window + + HWND hWndParent = ::GetParent( mhWnd ); + // Search for TopLevel Frame + while ( hWndParent && (GetWindowStyle( hWndParent ) & WS_CHILD) ) + hWndParent = ::GetParent( hWndParent ); + // if the Window has a Parent, then center the window to + // the parent, in the other case to the screen + if ( hWndParent && !IsIconic( hWndParent ) && + (GetWindowStyle( hWndParent ) & WS_VISIBLE) ) + { + RECT aParentRect; + GetWindowRect( hWndParent, &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 ); + + nX = ((aRect2.right-aRect2.left)-nWidth)/2 + aRect2.left; + nY = ((aRect2.bottom-aRect2.top)-nHeight)/2 + aRect2.top; + } + + //if ( bVisible ) + // mbDefPos = FALSE; + + 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 + + SetWindowPos( mhWnd, HWND_TOP, nX, nY, static_cast<int>(nWidth), static_cast<int>(nHeight), nPosFlags ); + + UpdateFrameGeometry( mhWnd, this ); + + // Notification -- really ??? + if( nEvent != SalEvent::NONE ) + CallCallback( nEvent, nullptr ); +} + +void WinSalFrame::ImplSetParentFrame( HWND hNewParentWnd, bool bAsChild ) +{ + // save hwnd, will be overwritten in WM_CREATE during createwindow + HWND hWndOld = mhWnd; + HWND hWndOldParent = ::GetParent( hWndOld ); + SalData* pSalData = GetSalData(); + + if( hNewParentWnd == hWndOldParent ) + return; + + ::std::vector< WinSalFrame* > children; + ::std::vector< WinSalObject* > systemChildren; + + // search child windows + WinSalFrame *pFrame = pSalData->mpFirstFrame; + while( pFrame ) + { + HWND hWndParent = ::GetParent( pFrame->mhWnd ); + if( mhWnd == hWndParent ) + children.push_back( pFrame ); + pFrame = pFrame->mpNextFrame; + } + + // search system child windows (plugins etc.) + WinSalObject *pObject = pSalData->mpFirstObject; + while( pObject ) + { + HWND hWndParent = ::GetParent( pObject->mhWnd ); + if( mhWnd == hWndParent ) + systemChildren.push_back( pObject ); + pObject = pObject->mpNextObject; + } + + // to recreate the DCs, if they were destroyed + bool bHadLocalGraphics = false, bHadThreadGraphics = false; + + HFONT hFont = nullptr; + HPEN hPen = nullptr; + HBRUSH hBrush = nullptr; + + int oldCount = pSalData->mnCacheDCInUse; + + // release the thread DC + if ( mpThreadGraphics ) + { + // save current gdi objects before hdc is gone + HDC hDC = mpThreadGraphics->getHDC(); + if ( hDC ) + { + hFont = static_cast<HFONT>(GetCurrentObject( hDC, OBJ_FONT )); + hPen = static_cast<HPEN>(GetCurrentObject( hDC, OBJ_PEN )); + hBrush = static_cast<HBRUSH>(GetCurrentObject( hDC, OBJ_BRUSH )); + } + + bHadThreadGraphics = ReleaseFrameGraphicsDC( mpThreadGraphics ); + assert( (bHadThreadGraphics && hDC) || (!bHadThreadGraphics && !hDC) ); + } + + // release the local DC + if ( mpLocalGraphics ) + bHadLocalGraphics = ReleaseFrameGraphicsDC( mpLocalGraphics ); + + // create a new hwnd with the same styles + HWND hWndParent = hNewParentWnd; + // forward to main thread + HWND hWnd = reinterpret_cast<HWND>(static_cast<sal_IntPtr>(SendMessageW( pSalData->mpInstance->mhComWnd, + bAsChild ? SAL_MSG_RECREATECHILDHWND : SAL_MSG_RECREATEHWND, + reinterpret_cast<WPARAM>(hWndParent), reinterpret_cast<LPARAM>(mhWnd) ))); + + // succeeded ? + SAL_WARN_IF( !IsWindow( hWnd ), "vcl", "WinSalFrame::SetParent not successful"); + + // re-create thread DC + if( bHadThreadGraphics ) + { + HDC hDC = reinterpret_cast<HDC>(static_cast<sal_IntPtr>( + SendMessageW( pSalData->mpInstance->mhComWnd, + SAL_MSG_GETCACHEDDC, reinterpret_cast<WPARAM>(hWnd), 0 ))); + InitFrameGraphicsDC( mpThreadGraphics, hDC, hWnd ); + if ( hDC ) + { + // re-select saved gdi objects + if( hFont ) + SelectObject( hDC, hFont ); + if( hPen ) + SelectObject( hDC, hPen ); + if( hBrush ) + SelectObject( hDC, hBrush ); + + SAL_WARN_IF( oldCount != pSalData->mnCacheDCInUse, "vcl", "WinSalFrame::SetParent() hDC count corrupted"); + } + } + + // re-create local DC + if( bHadLocalGraphics ) + InitFrameGraphicsDC( mpLocalGraphics, GetDC( hWnd ), hWnd ); + + // 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. + SendMessageW( GetSalData()->mpInstance->mhComWnd, + SAL_MSG_DESTROYHWND, WPARAM(0), reinterpret_cast<LPARAM>(hWndOld)); +} + +void WinSalFrame::SetParent( SalFrame* pNewParent ) +{ + WinSalFrame::mbInReparent = true; + ImplSetParentFrame( static_cast<WinSalFrame*>(pNewParent)->mhWnd, false ); + WinSalFrame::mbInReparent = false; +} + +void WinSalFrame::SetPluginParent( SystemParentData* pNewParent ) +{ + if ( pNewParent->hWnd == nullptr ) + { + pNewParent->hWnd = GetDesktopWindow(); + } + + WinSalFrame::mbInReparent = true; + ImplSetParentFrame( pNewParent->hWnd, true ); + WinSalFrame::mbInReparent = false; +} + +void WinSalFrame::GetWorkArea( tools::Rectangle &rRect ) +{ + RECT aRect; + ImplSalGetWorkArea( mhWnd, &aRect, nullptr ); + rRect.SetLeft( aRect.left ); + rRect.SetRight( aRect.right-1 ); + rRect.SetTop( aRect.top ); + rRect.SetBottom( aRect.bottom-1 ); +} + +void WinSalFrame::GetClientSize( tools::Long& rWidth, tools::Long& rHeight ) +{ + rWidth = maGeometry.nWidth; + rHeight = maGeometry.nHeight; +} + +void WinSalFrame::SetWindowState( const SalFrameState* 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->mnMask & (WindowStateMask::X | WindowStateMask::Y)) ) + nPosSize |= SWP_NOMOVE; + if ( !(pState->mnMask & (WindowStateMask::Width | WindowStateMask::Height)) ) + nPosSize |= SWP_NOSIZE; + if ( pState->mnMask & WindowStateMask::X ) + nX = static_cast<int>(pState->mnX) - nLeftDeco; + else + nX = aWinRect.left; + if ( pState->mnMask & WindowStateMask::Y ) + nY = static_cast<int>(pState->mnY) - nTopDeco; + else + nY = aWinRect.top; + if ( pState->mnMask & WindowStateMask::Width ) + nWidth = static_cast<int>(pState->mnWidth) + nLeftDeco + nRightDeco; + else + nWidth = aWinRect.right-aWinRect.left; + if ( pState->mnMask & WindowStateMask::Height ) + nHeight = static_cast<int>(pState->mnHeight) + 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( nWidth > nScreenWidth || nHeight > nScreenHeight ) + nPosSize |= (SWP_NOMOVE | SWP_NOSIZE); + + 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; + + // set Restore-Position + WINDOWPLACEMENT aPlacement; + aPlacement.length = sizeof( aPlacement ); + GetWindowPlacement( mhWnd, &aPlacement ); + + // set State + bool bVisible = (GetWindowStyle( mhWnd ) & WS_VISIBLE) != 0; + bool bUpdateHiddenFramePos = false; + if ( !bVisible ) + { + aPlacement.showCmd = SW_HIDE; + + if ( mbOverwriteState ) + { + if ( pState->mnMask & WindowStateMask::State ) + { + if ( pState->mnState & WindowStateState::Minimized ) + mnShowState = SW_SHOWMINIMIZED; + else if ( pState->mnState & WindowStateState::Maximized ) + { + mnShowState = SW_SHOWMAXIMIZED; + bUpdateHiddenFramePos = true; + } + else if ( pState->mnState & WindowStateState::Normal ) + mnShowState = SW_SHOWNORMAL; + } + } + } + else + { + if ( pState->mnMask & WindowStateMask::State ) + { + if ( pState->mnState & WindowStateState::Minimized ) + { + if ( pState->mnState & WindowStateState::Maximized ) + aPlacement.flags |= WPF_RESTORETOMAXIMIZED; + aPlacement.showCmd = SW_SHOWMINIMIZED; + } + else if ( pState->mnState & WindowStateState::Maximized ) + aPlacement.showCmd = SW_SHOWMAXIMIZED; + else if ( pState->mnState & WindowStateState::Normal ) + aPlacement.showCmd = SW_RESTORE; + } + } + + // 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 ( !IsIconic( mhWnd ) && !IsZoomed( mhWnd ) && + (!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, this, &aStateRect ); + SetWindowPos( mhWnd, nullptr, + maGeometry.nX, maGeometry.nY, maGeometry.nWidth, maGeometry.nHeight, + 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 ); + } + + if( !(nPosSize & SWP_NOMOVE) ) + mbDefPos = false; // window was positioned +} + +bool WinSalFrame::GetWindowState( SalFrameState* pState ) +{ + if ( maState.mnWidth && maState.mnHeight ) + { + *pState = maState; + // #94144# allow Minimize again, should be masked out when read from configuration + // 91625 - Don't save minimize + //if ( !(pState->mnState & WindowStateState::Maximized) ) + if ( !(pState->mnState & (WindowStateState::Minimized | WindowStateState::Maximized)) ) + pState->mnState |= WindowStateState::Normal; + return true; + } + + return false; +} + +void WinSalFrame::SetScreenNumber( unsigned int nNewScreen ) +{ + WinSalSystem* pSys = static_cast<WinSalSystem*>(ImplGetSalSystem()); + if( pSys ) + { + const std::vector<WinSalSystem::DisplayMonitor>& rMonitors = + pSys->getMonitors(); + size_t nMon = rMonitors.size(); + if( nNewScreen < nMon ) + { + Point aOldMonPos, aNewMonPos( rMonitors[nNewScreen].m_aArea.TopLeft() ); + Point aCurPos( maGeometry.nX, maGeometry.nY ); + for( size_t i = 0; i < nMon; i++ ) + { + if( rMonitors[i].m_aArea.Contains( aCurPos ) ) + { + aOldMonPos = rMonitors[i].m_aArea.TopLeft(); + break; + } + } + mnDisplay = nNewScreen; + maGeometry.nDisplayScreenNumber = nNewScreen; + SetPosSize( aNewMonPos.X() + (maGeometry.nX - aOldMonPos.X()), + aNewMonPos.Y() + (maGeometry.nY - aOldMonPos.Y()), + 0, 0, + SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y ); + } + } +} + +void WinSalFrame::SetApplicationID( const OUString &rApplicationID ) +{ + // http://msdn.microsoft.com/en-us/library/windows/desktop/dd378430(v=vs.85).aspx + // A window's properties must be removed before the window is closed. + + 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); + + if (SUCCEEDED(hr)) + { + hr = pps->SetValue(PKEY_AppUserModel_ID, pv); + PropVariantClear(&pv); + } + pps->Release(); + } +} + +void WinSalFrame::ShowFullScreen( bool bFullScreen, sal_Int32 nDisplay ) +{ + if ( (mbFullScreen == bFullScreen) && (!bFullScreen || (mnDisplay == nDisplay)) ) + return; + + mbFullScreen = bFullScreen; + mnDisplay = nDisplay; + + if ( bFullScreen ) + { + // to hide the Windows taskbar + DWORD nExStyle = GetWindowExStyle( mhWnd ); + if ( nExStyle & WS_EX_TOOLWINDOW ) + { + mbFullScreenToolWin = true; + nExStyle &= ~WS_EX_TOOLWINDOW; + SetWindowExStyle( mhWnd, nExStyle ); + } + // save old position + GetWindowRect( mhWnd, &maFullScreenRect ); + + // save show state + mnFullScreenShowState = mnShowState; + if ( !(GetWindowStyle( mhWnd ) & WS_VISIBLE) ) + mnShowState = SW_SHOW; + + // Save caption state. + mbFullScreenCaption = mbCaption; + if (mbCaption) + { + DWORD nStyle = GetWindowStyle(mhWnd); + SetWindowStyle(mhWnd, nStyle & ~WS_CAPTION); + mbCaption = false; + } + + // set window to screen size + ImplSalFrameFullScreenPos( this, true ); + } + else + { + // 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 ( mbFullScreenToolWin ) + SetWindowExStyle( mhWnd, GetWindowExStyle( mhWnd ) | WS_EX_TOOLWINDOW ); + mbFullScreenToolWin = false; + + // Restore caption state. + if (mbFullScreenCaption) + { + DWORD nStyle = GetWindowStyle(mhWnd); + SetWindowStyle(mhWnd, nStyle | WS_CAPTION); + } + mbCaption = mbFullScreenCaption; + + SetWindowPos( mhWnd, nullptr, + maFullScreenRect.left, + maFullScreenRect.top, + maFullScreenRect.right-maFullScreenRect.left, + maFullScreenRect.bottom-maFullScreenRect.top, + SWP_NOZORDER | SWP_NOACTIVATE ); + + // restore show state + if ( mnShowState != mnFullScreenShowState ) + { + mnShowState = mnFullScreenShowState; + if ( bVisible ) + { + mbInShow = true; + ShowWindow( mhWnd, mnShowState ); + mbInShow = false; + UpdateWindow( mhWnd ); + } + } + } +} + +void WinSalFrame::StartPresentation( bool bStart ) +{ + if ( mbPresentation == bStart ) + return; + + mbPresentation = bStart; + + SalData* pSalData = GetSalData(); + if ( bStart ) + { + // turn off screen-saver when in Presentation mode + SystemParametersInfoW( SPI_GETSCREENSAVEACTIVE, 0, + &(pSalData->mbScrSvrEnabled), 0 ); + if ( pSalData->mbScrSvrEnabled ) + SystemParametersInfoW( SPI_SETSCREENSAVEACTIVE, FALSE, nullptr, 0 ); + } + else + { + // turn on screen-saver + if ( pSalData->mbScrSvrEnabled ) + SystemParametersInfoW( SPI_SETSCREENSAVEACTIVE, pSalData->mbScrSvrEnabled, nullptr, 0 ); + } +} + +void WinSalFrame::SetAlwaysOnTop( bool bOnTop ) +{ + HWND hWnd; + if ( bOnTop ) + hWnd = HWND_TOPMOST; + else + hWnd = HWND_NOTOPMOST; + SetWindowPos( mhWnd, hWnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE ); +} + +static bool EnableAttachThreadInputHack() +{ + OUString aBootstrapUri; + if (osl_getProcessWorkingDir(&aBootstrapUri.pData) != osl_Process_E_None) + return false; + aBootstrapUri += "/bootstrap.ini"; + + OUString aSystemFileName; + if (osl::FileBase::getSystemPathFromFileURL(aBootstrapUri, aSystemFileName) != osl::FileBase::E_None) + return false; + if (aSystemFileName.getLength() > MAX_PATH) + return false; + + // this uses the Boost ini parser, instead of tools::Config, as we already use it to read other + // values from bootstrap.ini in desktop/win32/source/loader.cxx, because that watchdog process + // can't access LO libs. This way the handling is consistent. + try + { + boost::property_tree::ptree pt; + std::ifstream aFile(o3tl::toW(aSystemFileName.getStr())); + boost::property_tree::ini_parser::read_ini(aFile, pt); + const bool bEnabled = pt.get("Win32.EnableAttachThreadInputHack", false); + SAL_WARN_IF(bEnabled, "vcl", "AttachThreadInput hack is enabled. Watch out for deadlocks!"); + return bEnabled; + } + catch (...) + { + return false; + } +} + +static void ImplSalToTop( HWND hWnd, SalFrameToTop nFlags ) +{ + static const bool bEnableAttachThreadInputHack = EnableAttachThreadInputHack(); + + WinSalFrame* pToTopFrame = GetWindowPtr( hWnd ); + if( pToTopFrame && (pToTopFrame->mnStyle & SalFrameStyleFlags::SYSTEMCHILD) ) + BringWindowToTop( hWnd ); + + 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); + } + + if ( nFlags & SalFrameToTop::RestoreWhenMin ) + { + HWND hIconicWnd = hWnd; + while ( hIconicWnd ) + { + if ( IsIconic( hIconicWnd ) ) + { + WinSalFrame* pFrame = GetWindowPtr( hIconicWnd ); + if ( pFrame ) + { + if ( GetWindowPtr( hWnd )->mbRestoreMaximize ) + ShowWindow( hIconicWnd, SW_MAXIMIZE ); + else + ShowWindow( hIconicWnd, SW_RESTORE ); + } + else + ShowWindow( hIconicWnd, SW_RESTORE ); + } + + hIconicWnd = ::GetParent( hIconicWnd ); + } + } + + if ( !IsIconic( hWnd ) && IsWindowVisible( hWnd ) ) + { + SetFocus( 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() ) + { + bool const ret = PostMessageW( mhWnd, SAL_MSG_TOTOP, static_cast<WPARAM>(nFlags), 0 ); + SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!"); + } + else + ImplSalToTop( mhWnd, nFlags ); +} + +void WinSalFrame::SetPointer( PointerStyle ePointerStyle ) +{ + struct ImplPtrData + { + HCURSOR mhCursor; + LPCTSTR mnSysId; + UINT mnOwnId; + }; + + static o3tl::enumarray<PointerStyle, ImplPtrData> aImplPtrTab = + { + ImplPtrData{ nullptr, IDC_ARROW, 0 }, // POINTER_ARROW + { nullptr, nullptr, SAL_RESID_POINTER_NULL }, // POINTER_NULL + { nullptr, IDC_WAIT, 0 }, // POINTER_WAIT + { nullptr, IDC_IBEAM, 0 }, // POINTER_TEXT + { nullptr, IDC_HELP, 0 }, // POINTER_HELP + { nullptr, IDC_CROSS, 0 }, // POINTER_CROSS + { nullptr, IDC_SIZEALL, 0 }, // POINTER_MOVE + { nullptr, IDC_SIZENS, 0 }, // POINTER_NSIZE + { nullptr, IDC_SIZENS, 0 }, // POINTER_SSIZE + { nullptr, IDC_SIZEWE, 0 }, // POINTER_WSIZE + { nullptr, IDC_SIZEWE, 0 }, // POINTER_ESIZE + { nullptr, IDC_SIZENWSE, 0 }, // POINTER_NWSIZE + { nullptr, IDC_SIZENESW, 0 }, // POINTER_NESIZE + { nullptr, IDC_SIZENESW, 0 }, // POINTER_SWSIZE + { nullptr, IDC_SIZENWSE, 0 }, // POINTER_SESIZE + { nullptr, IDC_SIZENS, 0 }, // POINTER_WINDOW_NSIZE + { nullptr, IDC_SIZENS, 0 }, // POINTER_WINDOW_SSIZE + { nullptr, IDC_SIZEWE, 0 }, // POINTER_WINDOW_WSIZE + { nullptr, IDC_SIZEWE, 0 }, // POINTER_WINDOW_ESIZE + { nullptr, IDC_SIZENWSE, 0 }, // POINTER_WINDOW_NWSIZE + { nullptr, IDC_SIZENESW, 0 }, // POINTER_WINDOW_NESIZE + { nullptr, IDC_SIZENESW, 0 }, // POINTER_WINDOW_SWSIZE + { nullptr, IDC_SIZENWSE, 0 }, // POINTER_WINDOW_SESIZE + { nullptr, IDC_SIZEWE, 0 }, // POINTER_HSPLIT + { nullptr, IDC_SIZENS, 0 }, // POINTER_VSPLIT + { nullptr, IDC_SIZEWE, 0 }, // POINTER_HSIZEBAR + { nullptr, IDC_SIZENS, 0 }, // POINTER_VSIZEBAR + { nullptr, IDC_HAND, 0 }, // POINTER_HAND + { nullptr, IDC_HAND, 0 }, // POINTER_REFHAND + { nullptr, IDC_PEN, 0 }, // POINTER_PEN + { nullptr, nullptr, SAL_RESID_POINTER_MAGNIFY }, // POINTER_MAGNIFY + { nullptr, nullptr, SAL_RESID_POINTER_FILL }, // POINTER_FILL + { nullptr, nullptr, SAL_RESID_POINTER_ROTATE }, // POINTER_ROTATE + { nullptr, nullptr, SAL_RESID_POINTER_HSHEAR }, // POINTER_HSHEAR + { nullptr, nullptr, SAL_RESID_POINTER_VSHEAR }, // POINTER_VSHEAR + { nullptr, nullptr, SAL_RESID_POINTER_MIRROR }, // POINTER_MIRROR + { nullptr, nullptr, SAL_RESID_POINTER_CROOK }, // POINTER_CROOK + { nullptr, nullptr, SAL_RESID_POINTER_CROP }, // POINTER_CROP + { nullptr, nullptr, SAL_RESID_POINTER_MOVEPOINT }, // POINTER_MOVEPOINT + { nullptr, nullptr, SAL_RESID_POINTER_MOVEBEZIERWEIGHT }, // POINTER_MOVEBEZIERWEIGHT + { nullptr, nullptr, SAL_RESID_POINTER_MOVEDATA }, // POINTER_MOVEDATA + { nullptr, nullptr, SAL_RESID_POINTER_COPYDATA }, // POINTER_COPYDATA + { nullptr, nullptr, SAL_RESID_POINTER_LINKDATA }, // POINTER_LINKDATA + { nullptr, nullptr, SAL_RESID_POINTER_MOVEDATALINK }, // POINTER_MOVEDATALINK + { nullptr, nullptr, SAL_RESID_POINTER_COPYDATALINK }, // POINTER_COPYDATALINK + { nullptr, nullptr, SAL_RESID_POINTER_MOVEFILE }, // POINTER_MOVEFILE + { nullptr, nullptr, SAL_RESID_POINTER_COPYFILE }, // POINTER_COPYFILE + { nullptr, nullptr, SAL_RESID_POINTER_LINKFILE }, // POINTER_LINKFILE + { nullptr, nullptr, SAL_RESID_POINTER_MOVEFILELINK }, // POINTER_MOVEFILELINK + { nullptr, nullptr, SAL_RESID_POINTER_COPYFILELINK }, // POINTER_COPYFILELINK + { nullptr, nullptr, SAL_RESID_POINTER_MOVEFILES }, // POINTER_MOVEFILES + { nullptr, nullptr, SAL_RESID_POINTER_COPYFILES }, // POINTER_COPYFILES + { nullptr, IDC_NO, 0 }, // POINTER_NOTALLOWED + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_LINE }, // POINTER_DRAW_LINE + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_RECT }, // POINTER_DRAW_RECT + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_POLYGON }, // POINTER_DRAW_POLYGON + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_BEZIER }, // POINTER_DRAW_BEZIER + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_ARC }, // POINTER_DRAW_ARC + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_PIE }, // POINTER_DRAW_PIE + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_CIRCLECUT }, // POINTER_DRAW_CIRCLECUT + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_ELLIPSE }, // POINTER_DRAW_ELLIPSE + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_FREEHAND }, // POINTER_DRAW_FREEHAND + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_CONNECT }, // POINTER_DRAW_CONNECT + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_TEXT }, // POINTER_DRAW_TEXT + { nullptr, nullptr, SAL_RESID_POINTER_DRAW_CAPTION }, // POINTER_DRAW_CAPTION + { nullptr, nullptr, SAL_RESID_POINTER_CHART }, // POINTER_CHART + { nullptr, nullptr, SAL_RESID_POINTER_DETECTIVE }, // POINTER_DETECTIVE + { nullptr, nullptr, SAL_RESID_POINTER_PIVOT_COL }, // POINTER_PIVOT_COL + { nullptr, nullptr, SAL_RESID_POINTER_PIVOT_ROW }, // POINTER_PIVOT_ROW + { nullptr, nullptr, SAL_RESID_POINTER_PIVOT_FIELD }, // POINTER_PIVOT_FIELD + { nullptr, nullptr, SAL_RESID_POINTER_CHAIN }, // POINTER_CHAIN + { nullptr, nullptr, SAL_RESID_POINTER_CHAIN_NOTALLOWED }, // POINTER_CHAIN_NOTALLOWED + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_N }, // POINTER_AUTOSCROLL_N + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_S }, // POINTER_AUTOSCROLL_S + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_W }, // POINTER_AUTOSCROLL_W + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_E }, // POINTER_AUTOSCROLL_E + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NW }, // POINTER_AUTOSCROLL_NW + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NE }, // POINTER_AUTOSCROLL_NE + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_SW }, // POINTER_AUTOSCROLL_SW + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_SE }, // POINTER_AUTOSCROLL_SE + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NS }, // POINTER_AUTOSCROLL_NS + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_WE }, // POINTER_AUTOSCROLL_WE + { nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NSWE }, // POINTER_AUTOSCROLL_NSWE + { nullptr, nullptr, SAL_RESID_POINTER_TEXT_VERTICAL }, // POINTER_TEXT_VERTICAL + { nullptr, nullptr, SAL_RESID_POINTER_PIVOT_DELETE }, // POINTER_PIVOT_DELETE + + // #i32329# + { nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_S }, // POINTER_TAB_SELECT_S + { nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_E }, // POINTER_TAB_SELECT_E + { nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_SE }, // POINTER_TAB_SELECT_SE + { nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_W }, // POINTER_TAB_SELECT_W + { nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_SW }, // POINTER_TAB_SELECT_SW + + { nullptr, nullptr, SAL_RESID_POINTER_HIDEWHITESPACE }, // POINTER_HIDEWHITESPACE + { nullptr, nullptr, SAL_RESID_POINTER_SHOWWHITESPACE }, // POINTER_UNHIDEWHITESPACE + + { nullptr, nullptr, SAL_RESID_POINTER_FATCROSS } // POINTER_FATCROSS + }; + + // Mousepointer loaded ? + if ( !aImplPtrTab[ePointerStyle].mhCursor ) + { + if ( aImplPtrTab[ePointerStyle].mnOwnId ) + aImplPtrTab[ePointerStyle].mhCursor = ImplLoadSalCursor( aImplPtrTab[ePointerStyle].mnOwnId ); + else + aImplPtrTab[ePointerStyle].mhCursor = LoadCursor( nullptr, aImplPtrTab[ePointerStyle].mnSysId ); + } + + // 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; + SendMessageW( mhWnd, nMsg, 0, 0 ); +} + +void WinSalFrame::SetPointerPos( tools::Long nX, tools::Long nY ) +{ + POINT aPt; + aPt.x = static_cast<int>(nX); + aPt.y = static_cast<int>(nY); + ClientToScreen( mhWnd, &aPt ); + SetCursorPos( aPt.x, aPt.y ); +} + +void WinSalFrame::Flush() +{ + if(mpLocalGraphics) + mpLocalGraphics->Flush(); + if(mpThreadGraphics) + mpThreadGraphics->Flush(); + GdiFlush(); +} + +static void ImplSalFrameSetInputContext( HWND hWnd, const SalInputContext* pContext ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + bool bIME(pContext->mnOptions & InputContextFlags::Text); + if ( bIME ) + { + if ( !pFrame->mbIME ) + { + pFrame->mbIME = true; + + if ( pFrame->mhDefIMEContext ) + { + ImmAssociateContext( pFrame->mhWnd, pFrame->mhDefIMEContext ); + UINT nImeProps = ImmGetProperty( GetKeyboardLayout( 0 ), IGP_PROPERTY ); + pFrame->mbSpezIME = (nImeProps & IME_PROP_SPECIAL_UI) != 0; + pFrame->mbAtCursorIME = (nImeProps & IME_PROP_AT_CARET) != 0; + pFrame->mbHandleIME = !pFrame->mbSpezIME; + } + } + + // 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); + ImmSetCompositionFontW( hIMC, &aLogFont ); + ImmReleaseContext( pFrame->mhWnd, hIMC ); + } + } + } + else + { + if ( pFrame->mbIME ) + { + pFrame->mbIME = false; + pFrame->mbHandleIME = false; + ImmAssociateContext( pFrame->mhWnd, nullptr ); + } + } +} + +void WinSalFrame::SetInputContext( SalInputContext* pContext ) +{ + // Must be called in the main thread! + SendMessageW( mhWnd, SAL_MSG_SETINPUTCONTEXT, 0, reinterpret_cast<LPARAM>(pContext) ); +} + +static void ImplSalFrameEndExtTextInput( HWND hWnd, EndExtTextInputFlags nFlags ) +{ + HIMC hIMC = ImmGetContext( hWnd ); + if ( hIMC ) + { + DWORD nIndex; + if ( nFlags & EndExtTextInputFlags::Complete ) + nIndex = CPS_COMPLETE; + else + nIndex = CPS_CANCEL; + + ImmNotifyIME( hIMC, NI_COMPOSITIONSTR, nIndex, 0 ); + ImmReleaseContext( hWnd, hIMC ); + } +} + +void WinSalFrame::EndExtTextInput( EndExtTextInputFlags nFlags ) +{ + // Must be called in the main thread! + SendMessageW( mhWnd, SAL_MSG_ENDEXTTEXTINPUT, static_cast<WPARAM>(nFlags), 0 ); +} + +static void ImplGetKeyNameText( LONG lParam, sal_Unicode* pBuf, + UINT& rCount, UINT nMaxSize, + const char* pReplace ) +{ + static_assert( sizeof( WCHAR ) == sizeof( sal_Unicode ), "must be the same size" ); + + static const int nMaxKeyLen = 350; + WCHAR aKeyBuf[ nMaxKeyLen ]; + int nKeyLen = 0; + if ( lParam ) + { + OUString aLang = Application::GetSettings().GetUILanguageTag().getLanguage(); + OUString aRet; + + 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; + else if( nKeyLen > 0 ) + { + // Capitalize just the first letter of key names + CharLowerBuffW( aKeyBuf, nKeyLen ); + + bool bUpper = true; + for( WCHAR *pW=aKeyBuf, *pE=pW+nKeyLen; pW < pE; ++pW ) + { + if( bUpper ) + CharUpperBuffW( pW, 1 ); + bUpper = (*pW=='+') || (*pW=='-') || (*pW==' ') || (*pW=='.'); + } + } + } + else + { + nKeyLen = aRet.getLength(); + wcscpy( aKeyBuf, o3tl::toW( aRet.getStr() )); + } + } + + if ( (nKeyLen > 0) || pReplace ) + { + if( (rCount > 0) && (rCount < nMaxSize) ) + { + pBuf[rCount] = '+'; + rCount++; + } + + if( nKeyLen > 0 ) + { + WCHAR *pW = aKeyBuf, *pE = aKeyBuf + nKeyLen; + while ((pW < pE) && *pW && (rCount < nMaxSize)) + pBuf[rCount++] = *pW++; + } + else // fall back to provided default name + { + while( *pReplace && (rCount < nMaxSize) ) + { + pBuf[rCount] = *pReplace; + rCount++; + pReplace++; + } + } + } + else + rCount = 0; +} + +OUString WinSalFrame::GetKeyName( sal_uInt16 nKeyCode ) +{ + static const UINT nMaxKeyLen = 350; + sal_Unicode aKeyBuf[ nMaxKeyLen ]; + UINT nKeyBufLen = 0; + UINT nSysCode = 0; + + if ( nKeyCode & KEY_MOD1 ) + { + nSysCode = MapVirtualKeyW( VK_CONTROL, 0 ); + nSysCode = (nSysCode << 16) | ((sal_uLong(1)) << 25); + ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, "Ctrl" ); + } + + if ( nKeyCode & KEY_MOD2 ) + { + nSysCode = MapVirtualKeyW( VK_MENU, 0 ); + nSysCode = (nSysCode << 16) | ((sal_uLong(1)) << 25); + ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, "Alt" ); + } + + if ( nKeyCode & KEY_SHIFT ) + { + nSysCode = MapVirtualKeyW( VK_SHIFT, 0 ); + nSysCode = (nSysCode << 16) | ((sal_uLong(1)) << 25); + ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, "Shift" ); + } + + sal_uInt16 nCode = nKeyCode & 0x0FFF; + sal_uLong nSysCode2 = 0; + const char* pReplace = nullptr; + sal_Unicode cSVCode = 0; + char aFBuf[4]; + nSysCode = 0; + + if ( (nCode >= KEY_0) && (nCode <= KEY_9) ) + cSVCode = '0' + (nCode - KEY_0); + else if ( (nCode >= KEY_A) && (nCode <= KEY_Z) ) + cSVCode = 'A' + (nCode - KEY_A); + else if ( (nCode >= KEY_F1) && (nCode <= KEY_F26) ) + { + nSysCode = VK_F1 + (nCode - KEY_F1); + aFBuf[0] = 'F'; + if (nCode <= KEY_F9) + { + aFBuf[1] = sal::static_int_cast<char>('1' + (nCode - KEY_F1)); + aFBuf[2] = 0; + } + else if (nCode <= KEY_F19) + { + aFBuf[1] = '1'; + aFBuf[2] = sal::static_int_cast<char>('0' + (nCode - KEY_F10)); + aFBuf[3] = 0; + } + else + { + aFBuf[1] = '2'; + aFBuf[2] = sal::static_int_cast<char>('0' + (nCode - KEY_F20)); + aFBuf[3] = 0; + } + pReplace = aFBuf; + } + else + { + switch ( nCode ) + { + case KEY_DOWN: + nSysCode = VK_DOWN; + nSysCode2 = ((sal_uLong(1)) << 24); + pReplace = "Down"; + break; + case KEY_UP: + nSysCode = VK_UP; + nSysCode2 = ((sal_uLong(1)) << 24); + pReplace = "Up"; + break; + case KEY_LEFT: + nSysCode = VK_LEFT; + nSysCode2 = ((sal_uLong(1)) << 24); + pReplace = "Left"; + break; + case KEY_RIGHT: + nSysCode = VK_RIGHT; + nSysCode2 = ((sal_uLong(1)) << 24); + pReplace = "Right"; + break; + case KEY_HOME: + nSysCode = VK_HOME; + nSysCode2 = ((sal_uLong(1)) << 24); + pReplace = "Home"; + break; + case KEY_END: + nSysCode = VK_END; + nSysCode2 = ((sal_uLong(1)) << 24); + pReplace = "End"; + break; + case KEY_PAGEUP: + nSysCode = VK_PRIOR; + nSysCode2 = ((sal_uLong(1)) << 24); + pReplace = "Page Up"; + break; + case KEY_PAGEDOWN: + nSysCode = VK_NEXT; + nSysCode2 = ((sal_uLong(1)) << 24); + pReplace = "Page Down"; + break; + case KEY_RETURN: + nSysCode = VK_RETURN; + pReplace = "Enter"; + break; + case KEY_ESCAPE: + nSysCode = VK_ESCAPE; + pReplace = "Escape"; + break; + case KEY_TAB: + nSysCode = VK_TAB; + pReplace = "Tab"; + break; + case KEY_BACKSPACE: + nSysCode = VK_BACK; + pReplace = "Backspace"; + break; + case KEY_SPACE: + nSysCode = VK_SPACE; + pReplace = "Space"; + break; + case KEY_INSERT: + nSysCode = VK_INSERT; + nSysCode2 = ((sal_uLong(1)) << 24); + pReplace = "Insert"; + break; + case KEY_DELETE: + nSysCode = VK_DELETE; + nSysCode2 = ((sal_uLong(1)) << 24); + pReplace = "Delete"; + break; + + case KEY_ADD: + cSVCode = '+'; + break; + case KEY_SUBTRACT: + cSVCode = '-'; + break; + case KEY_MULTIPLY: + cSVCode = '*'; + break; + case KEY_DIVIDE: + cSVCode = '/'; + break; + case KEY_POINT: + cSVCode = '.'; + break; + case KEY_COMMA: + cSVCode = ','; + break; + case KEY_LESS: + cSVCode = '<'; + break; + case KEY_GREATER: + cSVCode = '>'; + break; + case KEY_EQUAL: + cSVCode = '='; + break; + case KEY_SEMICOLON: + cSVCode = ';'; + break; + case KEY_QUOTERIGHT: + cSVCode = '\''; + break; + case KEY_BRACKETLEFT: + cSVCode = '['; + break; + case KEY_BRACKETRIGHT: + cSVCode = ']'; + break; + case KEY_QUOTELEFT: + cSVCode = '`'; + break; + } + } + + if ( nSysCode ) + { + nSysCode = MapVirtualKeyW( nSysCode, 0 ); + if ( nSysCode ) + nSysCode = (nSysCode << 16) | nSysCode2; + ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, pReplace ); + } + else + { + if ( cSVCode ) + { + if ( nKeyBufLen > 0 ) + aKeyBuf[ nKeyBufLen++ ] = '+'; + if( nKeyBufLen < nMaxKeyLen ) + aKeyBuf[ nKeyBufLen++ ] = cSVCode; + } + } + + if( !nKeyBufLen ) + return OUString(); + + return OUString( aKeyBuf, sal::static_int_cast< sal_uInt16 >(nKeyBufLen) ); +} + +static Color ImplWinColorToSal( COLORREF nColor ) +{ + return Color( GetRValue( nColor ), GetGValue( nColor ), GetBValue( nColor ) ); +} + +static void ImplSalUpdateStyleFontW( HDC hDC, const LOGFONTW& rLogFont, vcl::Font& rFont ) +{ + ImplSalLogFontToFontW( hDC, rLogFont, rFont ); + + // 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 ); + else if ( rFont.GetFontHeight() < 6 ) + rFont.SetFontHeight( 6 ); + } +} + +static tools::Long ImplW2I( const wchar_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++; + } + + n *= nSign; + + return n; +} + +void WinSalFrame::UpdateSettings( AllSettings& rSettings ) +{ + MouseSettings aMouseSettings = rSettings.GetMouseSettings(); + aMouseSettings.SetDoubleClickTime( GetDoubleClickTime() ); + aMouseSettings.SetDoubleClickWidth( GetSystemMetrics( SM_CXDOUBLECLK ) ); + aMouseSettings.SetDoubleClickHeight( GetSystemMetrics( SM_CYDOUBLECLK ) ); + tools::Long nDragWidth = GetSystemMetrics( SM_CXDRAG ); + tools::Long nDragHeight = GetSystemMetrics( SM_CYDRAG ); + if ( nDragWidth ) + aMouseSettings.SetStartDragWidth( nDragWidth ); + if ( nDragHeight ) + aMouseSettings.SetStartDragHeight( nDragHeight ); + HKEY hRegKey; + if ( RegOpenKeyW( HKEY_CURRENT_USER, + L"Control Panel\\Desktop", + &hRegKey ) == ERROR_SUCCESS ) + { + wchar_t aValueBuf[10]; + DWORD nValueSize = sizeof( aValueBuf ); + DWORD nType; + if ( RegQueryValueExW( hRegKey, L"MenuShowDelay", nullptr, + &nType, reinterpret_cast<LPBYTE>(aValueBuf), &nValueSize ) == ERROR_SUCCESS ) + { + if ( nType == REG_SZ ) + aMouseSettings.SetMenuDelay( static_cast<sal_uLong>(ImplW2I( aValueBuf )) ); + } + + RegCloseKey( hRegKey ); + } + + StyleSettings aStyleSettings = rSettings.GetStyleSettings(); + + // High contrast + HIGHCONTRAST hc; + hc.cbSize = sizeof( HIGHCONTRAST ); + if( SystemParametersInfoW( SPI_GETHIGHCONTRAST, hc.cbSize, &hc, 0 ) + && (hc.dwFlags & HCF_HIGHCONTRASTON) ) + aStyleSettings.SetHighContrastMode( true ); + else + aStyleSettings.SetHighContrastMode( false ); + + aStyleSettings.SetScrollBarSize( GetSystemMetrics( SM_CXVSCROLL ) ); + aStyleSettings.SetSpinSize( GetSystemMetrics( SM_CXVSCROLL ) ); + UINT blinkTime = GetCaretBlinkTime(); + aStyleSettings.SetCursorBlinkTime( + blinkTime == 0 || blinkTime == INFINITE // 0 indicates error + ? STYLE_CURSOR_NOBLINKTIME : blinkTime ); + aStyleSettings.SetFloatTitleHeight( GetSystemMetrics( SM_CYSMCAPTION ) ); + aStyleSettings.SetTitleHeight( GetSystemMetrics( SM_CYCAPTION ) ); + aStyleSettings.SetActiveBorderColor( ImplWinColorToSal( GetSysColor( COLOR_ACTIVEBORDER ) ) ); + aStyleSettings.SetDeactiveBorderColor( ImplWinColorToSal( GetSysColor( COLOR_INACTIVEBORDER ) ) ); + aStyleSettings.SetDeactiveColor( ImplWinColorToSal( GetSysColor( COLOR_GRADIENTINACTIVECAPTION ) ) ); + + Color aControlTextColor; + Color aMenuBarTextColor; + Color aMenuBarRolloverTextColor; + Color aHighlightTextColor = ImplWinColorToSal(GetSysColor(COLOR_HIGHLIGHTTEXT)); + + const bool bUseDarkMode(UseDarkMode()); + + OUString sThemeName(!bUseDarkMode ? u"colibre" : u"colibre_dark"); + aStyleSettings.SetPreferredIconTheme(sThemeName, bUseDarkMode); + + if (bUseDarkMode) + { + SetWindowTheme(mhWnd, L"Explorer", nullptr); + + HTHEME hTheme = OpenThemeData(nullptr, L"ItemsView"); + COLORREF color; + GetThemeColor(hTheme, 0, 0, TMT_FILLCOLOR, &color); + aStyleSettings.SetFaceColor( ImplWinColorToSal( color ) ); + aStyleSettings.SetWindowColor( ImplWinColorToSal( color ) ); + 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); + + hTheme = OpenThemeData(mhWnd, L"Button"); + GetThemeColor(hTheme, BP_PUSHBUTTON, MBI_NORMAL, TMT_TEXTCOLOR, &color); + aControlTextColor = ImplWinColorToSal(color); + GetThemeColor(hTheme, BP_CHECKBOX, MBI_NORMAL, TMT_TEXTCOLOR, &color); + aStyleSettings.SetRadioCheckTextColor( ImplWinColorToSal( color ) ); + CloseThemeData(hTheme); + + SetWindowTheme(mhWnd, nullptr, nullptr); + + hTheme = OpenThemeData(mhWnd, L"Menu"); + GetThemeColor(hTheme, MENU_POPUPITEM, MBI_NORMAL, TMT_TEXTCOLOR, &color); + aStyleSettings.SetMenuTextColor( ImplWinColorToSal( color ) ); + aMenuBarTextColor = ImplWinColorToSal( color ); + aMenuBarRolloverTextColor = ImplWinColorToSal( color ); + CloseThemeData(hTheme); + + if (hTheme = OpenThemeData(mhWnd, L"Textstyle")) + { + GetThemeColor(hTheme, TEXT_HYPERLINKTEXT, TS_HYPERLINK_NORMAL, TMT_TEXTCOLOR, &color); + aStyleSettings.SetLinkColor(ImplWinColorToSal(color)); + CloseThemeData(hTheme); + } + + // tdf#148448 pick a warning color more likely to be readable as a + // background in a dark theme + aStyleSettings.SetWarningColor(Color(0xf5, 0x79, 0x00)); + } + else + { + aStyleSettings.SetFaceColor( ImplWinColorToSal( GetSysColor( COLOR_3DFACE ) ) ); + aStyleSettings.SetWindowColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOW ) ) ); + aStyleSettings.SetWindowTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) ); + aStyleSettings.SetToolTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) ); + aStyleSettings.SetShadowColor( ImplWinColorToSal( GetSysColor( COLOR_3DSHADOW ) ) ); + aStyleSettings.SetDarkShadowColor( ImplWinColorToSal( GetSysColor( COLOR_3DDKSHADOW ) ) ); + aControlTextColor = ImplWinColorToSal(GetSysColor(COLOR_BTNTEXT)); + aStyleSettings.SetRadioCheckTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) ); + aStyleSettings.SetMenuTextColor( ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ) ); + aMenuBarTextColor = ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ); + aMenuBarRolloverTextColor = aHighlightTextColor; + } + + if ( std::optional<Color> aColor = aStyleSettings.GetPersonaMenuBarTextColor() ) + { + aMenuBarTextColor = *aColor; + aMenuBarRolloverTextColor = *aColor; + } + + aStyleSettings.SetMenuBarTextColor( aMenuBarTextColor ); + aStyleSettings.SetMenuBarRolloverTextColor( aMenuBarRolloverTextColor ); + + aStyleSettings.SetInactiveTabColor( aStyleSettings.GetFaceColor() ); + aStyleSettings.SetLightColor( ImplWinColorToSal( GetSysColor( COLOR_3DHILIGHT ) ) ); + aStyleSettings.SetLightBorderColor( ImplWinColorToSal( GetSysColor( COLOR_3DLIGHT ) ) ); + aStyleSettings.SetHelpColor( ImplWinColorToSal( GetSysColor( COLOR_INFOBK ) ) ); + aStyleSettings.SetHelpTextColor( ImplWinColorToSal( GetSysColor( COLOR_INFOTEXT ) ) ); + + aStyleSettings.SetWorkspaceColor(aStyleSettings.GetFaceColor()); + aStyleSettings.SetDialogColor(aStyleSettings.GetFaceColor()); + aStyleSettings.SetDialogTextColor(aControlTextColor); + + Color aHighlightButtonTextColor = aStyleSettings.GetHighContrastMode() ? + aHighlightTextColor : aControlTextColor; + + aStyleSettings.SetDefaultButtonTextColor(aHighlightButtonTextColor); + aStyleSettings.SetButtonTextColor(aControlTextColor); + aStyleSettings.SetDefaultActionButtonTextColor(aHighlightButtonTextColor); + aStyleSettings.SetActionButtonTextColor(aControlTextColor); + aStyleSettings.SetFlatButtonTextColor(aControlTextColor); + aStyleSettings.SetDefaultButtonRolloverTextColor(aHighlightButtonTextColor); + aStyleSettings.SetButtonRolloverTextColor(aHighlightButtonTextColor); + aStyleSettings.SetDefaultActionButtonRolloverTextColor(aHighlightButtonTextColor); + aStyleSettings.SetActionButtonRolloverTextColor(aHighlightButtonTextColor); + aStyleSettings.SetFlatButtonRolloverTextColor(aHighlightButtonTextColor); + aStyleSettings.SetDefaultButtonPressedRolloverTextColor(aControlTextColor); + aStyleSettings.SetButtonPressedRolloverTextColor(aControlTextColor); + aStyleSettings.SetDefaultActionButtonPressedRolloverTextColor(aControlTextColor); + aStyleSettings.SetActionButtonPressedRolloverTextColor(aControlTextColor); + aStyleSettings.SetFlatButtonPressedRolloverTextColor(aControlTextColor); + + aStyleSettings.SetTabTextColor(aControlTextColor); + aStyleSettings.SetTabRolloverTextColor(aControlTextColor); + aStyleSettings.SetTabHighlightTextColor(aControlTextColor); + + aStyleSettings.SetGroupTextColor( aStyleSettings.GetRadioCheckTextColor() ); + aStyleSettings.SetLabelTextColor( aStyleSettings.GetRadioCheckTextColor() ); + aStyleSettings.SetActiveTabColor( aStyleSettings.GetWindowColor() ); + aStyleSettings.SetFieldColor( aStyleSettings.GetWindowColor() ); + aStyleSettings.SetFieldTextColor( aStyleSettings.GetWindowTextColor() ); + aStyleSettings.SetFieldRolloverTextColor( aStyleSettings.GetFieldTextColor() ); + aStyleSettings.SetHighlightColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHT ) ) ); + aStyleSettings.SetHighlightTextColor(aHighlightTextColor); + aStyleSettings.SetMenuHighlightColor( aStyleSettings.GetHighlightColor() ); + aStyleSettings.SetMenuHighlightTextColor( aStyleSettings.GetHighlightTextColor() ); + + ImplSVData* pSVData = ImplGetSVData(); + pSVData->maNWFData.mnMenuFormatBorderX = 0; + pSVData->maNWFData.mnMenuFormatBorderY = 0; + pSVData->maNWFData.maMenuBarHighlightTextColor = COL_TRANSPARENT; + GetSalData()->mbThemeMenuSupport = false; + if (officecfg::Office::Common::Accessibility::AutoDetectSystemHC::get()) + { + aStyleSettings.SetShadowColor( ImplWinColorToSal( GetSysColor( COLOR_ACTIVEBORDER ) ) ); + aStyleSettings.SetWorkspaceColor( ImplWinColorToSal( GetSysColor( COLOR_MENU ) ) ); + } + aStyleSettings.SetMenuColor( ImplWinColorToSal( GetSysColor( COLOR_MENU ) ) ); + aStyleSettings.SetMenuBarColor( aStyleSettings.GetMenuColor() ); + aStyleSettings.SetMenuBarRolloverColor( aStyleSettings.GetHighlightColor() ); + aStyleSettings.SetMenuBorderColor( aStyleSettings.GetLightBorderColor() ); // overridden below for flat menus + aStyleSettings.SetUseFlatBorders( false ); + aStyleSettings.SetUseFlatMenus( false ); + aStyleSettings.SetMenuBarHighlightTextColor(aStyleSettings.GetMenuHighlightTextColor()); + aStyleSettings.SetActiveColor( ImplWinColorToSal( GetSysColor( COLOR_ACTIVECAPTION ) ) ); + aStyleSettings.SetActiveTextColor( ImplWinColorToSal( GetSysColor( COLOR_CAPTIONTEXT ) ) ); + aStyleSettings.SetDeactiveColor( ImplWinColorToSal( GetSysColor( COLOR_INACTIVECAPTION ) ) ); + aStyleSettings.SetDeactiveTextColor( ImplWinColorToSal( GetSysColor( COLOR_INACTIVECAPTIONTEXT ) ) ); + BOOL bFlatMenus = FALSE; + SystemParametersInfoW( SPI_GETFLATMENU, 0, &bFlatMenus, 0); + if( bFlatMenus ) + { + aStyleSettings.SetUseFlatMenus( true ); + aStyleSettings.SetMenuBarColor( ImplWinColorToSal( GetSysColor( COLOR_MENUBAR ) ) ); + aStyleSettings.SetMenuHighlightColor( ImplWinColorToSal( GetSysColor( COLOR_MENUHILIGHT ) ) ); + aStyleSettings.SetMenuBarRolloverColor( ImplWinColorToSal( GetSysColor( COLOR_MENUHILIGHT ) ) ); + aStyleSettings.SetMenuBorderColor( ImplWinColorToSal( GetSysColor( COLOR_3DSHADOW ) ) ); + + // 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 ); + } + aStyleSettings.SetCheckedColorSpecialCase( ); + + // caret width + DWORD nCaretWidth = 2; + if( SystemParametersInfoW( SPI_GETCARETWIDTH, 0, &nCaretWidth, 0 ) ) + aStyleSettings.SetCursorSize( nCaretWidth ); + + // Query Fonts + vcl::Font aMenuFont = aStyleSettings.GetMenuFont(); + vcl::Font aTitleFont = aStyleSettings.GetTitleFont(); + vcl::Font aFloatTitleFont = aStyleSettings.GetFloatTitleFont(); + vcl::Font aHelpFont = aStyleSettings.GetHelpFont(); + vcl::Font aAppFont = aStyleSettings.GetAppFont(); + vcl::Font aIconFont = aStyleSettings.GetIconFont(); + HDC hDC = GetDC( nullptr ); + NONCLIENTMETRICSW aNonClientMetrics; + aNonClientMetrics.cbSize = sizeof( aNonClientMetrics ); + if ( SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, sizeof( aNonClientMetrics ), &aNonClientMetrics, 0 ) ) + { + ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfMenuFont, aMenuFont ); + ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfCaptionFont, aTitleFont ); + ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfSmCaptionFont, aFloatTitleFont ); + ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfStatusFont, aHelpFont ); + ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfMessageFont, aAppFont ); + + LOGFONTW aLogFont; + if ( SystemParametersInfoW( SPI_GETICONTITLELOGFONT, 0, &aLogFont, 0 ) ) + ImplSalUpdateStyleFontW( hDC, aLogFont, aIconFont ); + } + + ReleaseDC( nullptr, hDC ); + + aStyleSettings.SetToolbarIconSize(ToolbarIconSize::Large); + + aStyleSettings.BatchSetFonts( aAppFont, aAppFont ); + + aStyleSettings.SetMenuFont( aMenuFont ); + aStyleSettings.SetTitleFont( aTitleFont ); + aStyleSettings.SetFloatTitleFont( aFloatTitleFont ); + aStyleSettings.SetHelpFont( aHelpFont ); + aStyleSettings.SetIconFont( aIconFont ); + + if ( aAppFont.GetWeight() > WEIGHT_NORMAL ) + aAppFont.SetWeight( WEIGHT_NORMAL ); + aStyleSettings.SetToolFont( aAppFont ); + aStyleSettings.SetTabFont( aAppFont ); + + BOOL bDragFull; + if ( SystemParametersInfoW( SPI_GETDRAGFULLWINDOWS, 0, &bDragFull, 0 ) ) + { + DragFullOptions nDragFullOptions = aStyleSettings.GetDragFullOptions(); + if ( bDragFull ) + nDragFullOptions |= DragFullOptions::WindowMove | DragFullOptions::WindowSize | DragFullOptions::Docking | DragFullOptions::Split; + else + nDragFullOptions &= ~DragFullOptions(DragFullOptions::WindowMove | DragFullOptions::WindowSize | DragFullOptions::Docking | DragFullOptions::Split); + aStyleSettings.SetDragFullOptions( nDragFullOptions ); + } + + if ( RegOpenKeyW( HKEY_CURRENT_USER, + L"Control Panel\\International\\Calendars\\TwoDigitYearMax", + &hRegKey ) == ERROR_SUCCESS ) + { + wchar_t aValueBuf[10]; + DWORD nValue; + DWORD nValueSize = sizeof( aValueBuf ); + DWORD nType; + if ( RegQueryValueExW( hRegKey, L"1", nullptr, + &nType, reinterpret_cast<LPBYTE>(aValueBuf), &nValueSize ) == ERROR_SUCCESS ) + { + if ( nType == REG_SZ ) + { + nValue = static_cast<sal_uLong>(ImplW2I( aValueBuf )); + if ( (nValue > 1000) && (nValue < 10000) ) + { + std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::DateFormat::TwoDigitYear::set(static_cast<sal_Int32>(nValue-99), batch); + batch->commit(); + } + } + } + + RegCloseKey( hRegKey ); + } + + rSettings.SetMouseSettings( aMouseSettings ); + rSettings.SetStyleSettings( aStyleSettings ); + + // now apply the values from theming, if available + WinSalGraphics::updateSettingsNative( rSettings ); +} + +const SystemEnvData* WinSalFrame::GetSystemData() const +{ + return &maSysData; +} + +void WinSalFrame::Beep() +{ + // a simple beep + MessageBeep( 0 ); +} + +SalFrame::SalPointerState WinSalFrame::GetPointerState() +{ + SalPointerState aState; + aState.mnState = 0; + + if ( GetKeyState( VK_LBUTTON ) & 0x8000 ) + aState.mnState |= MOUSE_LEFT; + if ( GetKeyState( VK_MBUTTON ) & 0x8000 ) + aState.mnState |= MOUSE_MIDDLE; + if ( GetKeyState( VK_RBUTTON ) & 0x8000 ) + aState.mnState |= MOUSE_RIGHT; + if ( GetKeyState( VK_SHIFT ) & 0x8000 ) + aState.mnState |= KEY_SHIFT; + if ( GetKeyState( VK_CONTROL ) & 0x8000 ) + aState.mnState |= KEY_MOD1; + if ( GetKeyState( VK_MENU ) & 0x8000 ) + aState.mnState |= KEY_MOD2; + + POINT pt; + GetCursorPos( &pt ); + + aState.maPos = Point( pt.x - maGeometry.nX, pt.y - maGeometry.nY ); + return aState; +} + +KeyIndicatorState WinSalFrame::GetIndicatorState() +{ + KeyIndicatorState aState = KeyIndicatorState::NONE; + if (::GetKeyState(VK_CAPITAL)) + aState |= KeyIndicatorState::CAPSLOCK; + + if (::GetKeyState(VK_NUMLOCK)) + aState |= KeyIndicatorState::NUMLOCK; + + if (::GetKeyState(VK_SCROLL)) + aState |= KeyIndicatorState::SCROLLLOCK; + + return aState; +} + +void WinSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode ) +{ + BYTE nVKey = 0; + switch (nKeyCode) + { + case KEY_CAPSLOCK: + nVKey = VK_CAPITAL; + break; + } + + if (nVKey > 0 && nVKey < 255) + { + ::keybd_event(nVKey, 0x45, KEYEVENTF_EXTENDEDKEY, 0); + ::keybd_event(nVKey, 0x45, KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP, 0); + } +} + +void WinSalFrame::ResetClipRegion() +{ + SetWindowRgn( mhWnd, nullptr, TRUE ); +} + +void WinSalFrame::BeginSetClipRegion( sal_uInt32 nRects ) +{ + if( mpClipRgnData ) + delete [] reinterpret_cast<BYTE*>(mpClipRgnData); + sal_uLong nRectBufSize = sizeof(RECT)*nRects; + mpClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+nRectBufSize]); + mpClipRgnData->rdh.dwSize = sizeof( RGNDATAHEADER ); + mpClipRgnData->rdh.iType = RDH_RECTANGLES; + mpClipRgnData->rdh.nCount = nRects; + mpClipRgnData->rdh.nRgnSize = nRectBufSize; + SetRectEmpty( &(mpClipRgnData->rdh.rcBound) ); + mpNextClipRect = reinterpret_cast<RECT*>(&(mpClipRgnData->Buffer)); + mbFirstClipRect = true; +} + +void WinSalFrame::UnionClipRegion( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) +{ + if( ! mpClipRgnData ) + return; + + RECT* pRect = mpNextClipRect; + RECT* pBoundRect = &(mpClipRgnData->rdh.rcBound); + tools::Long nRight = nX + nWidth; + tools::Long nBottom = nY + nHeight; + + if ( mbFirstClipRect ) + { + pBoundRect->left = nX; + pBoundRect->top = nY; + pBoundRect->right = nRight; + pBoundRect->bottom = nBottom; + mbFirstClipRect = false; + } + else + { + if ( nX < pBoundRect->left ) + pBoundRect->left = static_cast<int>(nX); + + if ( nY < pBoundRect->top ) + pBoundRect->top = static_cast<int>(nY); + + if ( nRight > pBoundRect->right ) + pBoundRect->right = static_cast<int>(nRight); + + if ( nBottom > pBoundRect->bottom ) + pBoundRect->bottom = static_cast<int>(nBottom); + } + + pRect->left = static_cast<int>(nX); + pRect->top = static_cast<int>(nY); + pRect->right = static_cast<int>(nRight); + pRect->bottom = static_cast<int>(nBottom); + if( (mpNextClipRect - reinterpret_cast<RECT*>(&mpClipRgnData->Buffer)) < static_cast<int>(mpClipRgnData->rdh.nCount) ) + mpNextClipRect++; +} + +void WinSalFrame::EndSetClipRegion() +{ + if( ! mpClipRgnData ) + return; + + HRGN hRegion; + + // create region from accumulated rectangles + if ( mpClipRgnData->rdh.nCount == 1 ) + { + RECT* pRect = &(mpClipRgnData->rdh.rcBound); + hRegion = CreateRectRgn( pRect->left, pRect->top, + pRect->right, pRect->bottom ); + } + else + { + sal_uLong nSize = mpClipRgnData->rdh.nRgnSize+sizeof(RGNDATAHEADER); + hRegion = ExtCreateRegion( nullptr, nSize, mpClipRgnData ); + } + delete [] reinterpret_cast<BYTE*>(mpClipRgnData); + mpClipRgnData = nullptr; + + SAL_WARN_IF( !hRegion, "vcl", "WinSalFrame::EndSetClipRegion() - Can't create ClipRegion" ); + if( hRegion ) + { + RECT aWindowRect; + GetWindowRect( mhWnd, &aWindowRect ); + POINT aPt; + aPt.x=0; + aPt.y=0; + ClientToScreen( mhWnd, &aPt ); + OffsetRgn( hRegion, aPt.x - aWindowRect.left, aPt.y - aWindowRect.top ); + + if( SetWindowRgn( mhWnd, hRegion, TRUE ) == 0 ) + DeleteObject( hRegion ); + } +} + +static bool ImplHandleMouseMsg( HWND hWnd, UINT nMsg, + WPARAM wParam, LPARAM lParam ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( !pFrame ) + return false; + + if( nMsg == WM_LBUTTONDOWN || nMsg == WM_MBUTTONDOWN || nMsg == WM_RBUTTONDOWN ) + { + // #103168# post again if async focus has not arrived yet + // hopefully we will not receive the corresponding button up before this + // button down arrives again + vcl::Window *pWin = pFrame->GetWindow(); + if( pWin && pWin->ImplGetWindowImpl()->mpFrameData->mnFocusId ) + { + bool const ret = PostMessageW( hWnd, nMsg, wParam, lParam ); + SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!"); + return true; + } + } + SalMouseEvent aMouseEvt; + bool nRet; + SalEvent nEvent = SalEvent::NONE; + bool bCall = true; + + aMouseEvt.mnX = static_cast<short>(LOWORD( lParam )); + aMouseEvt.mnY = static_cast<short>(HIWORD( lParam )); + aMouseEvt.mnCode = 0; + aMouseEvt.mnTime = GetMessageTime(); + + // Use GetKeyState(), as some Logitech mouse drivers do not check + // KeyState when simulating double-click with center mouse button + + if ( GetKeyState( VK_LBUTTON ) & 0x8000 ) + aMouseEvt.mnCode |= MOUSE_LEFT; + if ( GetKeyState( VK_MBUTTON ) & 0x8000 ) + aMouseEvt.mnCode |= MOUSE_MIDDLE; + if ( GetKeyState( VK_RBUTTON ) & 0x8000 ) + aMouseEvt.mnCode |= MOUSE_RIGHT; + if ( GetKeyState( VK_SHIFT ) & 0x8000 ) + aMouseEvt.mnCode |= KEY_SHIFT; + if ( GetKeyState( VK_CONTROL ) & 0x8000 ) + aMouseEvt.mnCode |= KEY_MOD1; + if ( GetKeyState( VK_MENU ) & 0x8000 ) + aMouseEvt.mnCode |= KEY_MOD2; + + switch ( nMsg ) + { + case WM_MOUSEMOVE: + { + // As the mouse events are not collected correctly when + // pressing modifier keys (as interrupted by KeyEvents) + // we do this here ourselves + if ( aMouseEvt.mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2) ) + { + MSG aTempMsg; + if ( PeekMessageW( &aTempMsg, hWnd, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE | PM_NOYIELD ) ) + { + if ( (aTempMsg.message == WM_MOUSEMOVE) && + (aTempMsg.wParam == wParam) ) + return true; + } + } + + SalData* pSalData = GetSalData(); + // Test for MouseLeave + if ( pSalData->mhWantLeaveMsg && (pSalData->mhWantLeaveMsg != hWnd) ) + SendMessageW( pSalData->mhWantLeaveMsg, SAL_MSG_MOUSELEAVE, 0, GetMessagePos() ); + + pSalData->mhWantLeaveMsg = hWnd; + // Start MouseLeave-Timer + if ( !pSalData->mpMouseLeaveTimer ) + { + pSalData->mpMouseLeaveTimer = new AutoTimer( "ImplHandleMouseMsg SalData::mpMouseLeaveTimer" ); + pSalData->mpMouseLeaveTimer->SetTimeout( SAL_MOUSELEAVE_TIMEOUT ); + pSalData->mpMouseLeaveTimer->Start(); + // We don't need to set a timeout handler, because we test + // for mouseleave in the timeout callback + } + aMouseEvt.mnButton = 0; + nEvent = SalEvent::MouseMove; + } + break; + + 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 (const auto& 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; + if ( pSalData->mpMouseLeaveTimer ) + { + delete pSalData->mpMouseLeaveTimer; + pSalData->mpMouseLeaveTimer = 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 ) ) + return false; + + if ( bCall ) + { + if ( nEvent == SalEvent::MouseButtonDown ) + UpdateWindow( hWnd ); + + if( AllSettings::GetLayoutRTL() ) + aMouseEvt.mnX = pFrame->maGeometry.nWidth-1-aMouseEvt.mnX; + + nRet = pFrame->CallCallback( nEvent, &aMouseEvt ); + if ( nMsg == WM_MOUSEMOVE ) + SetCursor( pFrame->mhCursor ); + } + else + nRet = false; + + return nRet; +} + +static bool ImplHandleMouseActivateMsg( HWND hWnd ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( !pFrame ) + return false; + + if ( pFrame->mbFloatWin ) + return true; + + return pFrame->CallCallback( SalEvent::MouseActivate, nullptr ); +} + +static bool ImplHandleWheelMsg( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) +{ + DBG_ASSERT( nMsg == WM_MOUSEWHEEL || + nMsg == WM_MOUSEHWHEEL, + "ImplHandleWheelMsg() called with no wheel mouse event" ); + + ImplSalYieldMutexAcquireWithWait(); + + bool nRet = false; + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( pFrame ) + { + WORD nWinModCode = LOWORD( wParam ); + POINT aWinPt; + aWinPt.x = static_cast<short>(LOWORD( lParam )); + aWinPt.y = static_cast<short>(HIWORD( lParam )); + ScreenToClient( hWnd, &aWinPt ); + + SalWheelMouseEvent aWheelEvt; + aWheelEvt.mnTime = GetMessageTime(); + aWheelEvt.mnX = aWinPt.x; + aWheelEvt.mnY = aWinPt.y; + aWheelEvt.mnCode = 0; + aWheelEvt.mnDelta = static_cast<short>(HIWORD( wParam )); + aWheelEvt.mnNotchDelta = aWheelEvt.mnDelta/WHEEL_DELTA; + if( aWheelEvt.mnNotchDelta == 0 ) + { + if( aWheelEvt.mnDelta > 0 ) + aWheelEvt.mnNotchDelta = 1; + else if( aWheelEvt.mnDelta < 0 ) + aWheelEvt.mnNotchDelta = -1; + } + + if( nMsg == WM_MOUSEWHEEL ) + { + if ( aSalShlData.mnWheelScrollLines == WHEEL_PAGESCROLL ) + aWheelEvt.mnScrollLines = SAL_WHEELMOUSE_EVENT_PAGESCROLL; + else + aWheelEvt.mnScrollLines = aSalShlData.mnWheelScrollLines; + aWheelEvt.mbHorz = false; + } + else + { + aWheelEvt.mnScrollLines = aSalShlData.mnWheelScrollChars; + aWheelEvt.mbHorz = true; + + // fdo#36380 - seems horiz scrolling has swapped direction + aWheelEvt.mnDelta *= -1; + aWheelEvt.mnNotchDelta *= -1; + } + + 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; + + if( AllSettings::GetLayoutRTL() ) + aWheelEvt.mnX = pFrame->maGeometry.nWidth-1-aWheelEvt.mnX; + + nRet = pFrame->CallCallback( SalEvent::WheelMouse, &aWheelEvt ); + } + + ImplSalYieldMutexRelease(); + + return nRet; +} + +static sal_uInt16 ImplSalGetKeyCode( WPARAM wParam ) +{ + sal_uInt16 nKeyCode; + + // convert KeyCode + if ( wParam < KEY_TAB_SIZE ) + nKeyCode = aImplTranslateKeyTab[wParam]; + else + { + SalData* pSalData = GetSalData(); + std::map< UINT, sal_uInt16 >::const_iterator it = pSalData->maVKMap.find( static_cast<UINT>(wParam) ); + if( it != pSalData->maVKMap.end() ) + nKeyCode = it->second; + else + nKeyCode = 0; + } + + return nKeyCode; +} + +static void ImplUpdateInputLang( WinSalFrame* pFrame ) +{ + UINT nLang = LOWORD( GetKeyboardLayout( 0 ) ); + if ( nLang && nLang != pFrame->mnInputLang ) + { + // keep input lang up-to-date + pFrame->mnInputLang = nLang; + } + + // We are on Windows NT so we use Unicode FrameProcs and get + // Unicode charcodes directly from Windows no need to set up a + // code page + return; +} + +static sal_Unicode ImplGetCharCode( WinSalFrame* pFrame, WPARAM nCharCode ) +{ + ImplUpdateInputLang( pFrame ); + + // We are on Windows NT so we use Unicode FrameProcs and we + // get Unicode charcodes directly from Windows + return static_cast<sal_Unicode>(nCharCode); +} + +LanguageType WinSalFrame::GetInputLanguage() +{ + if( !mnInputLang ) + ImplUpdateInputLang( this ); + + if( !mnInputLang ) + return LANGUAGE_DONTKNOW; + else + return LanguageType(mnInputLang); +} + +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; +} + +static void 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 Command-Option-C in a Writer document while +// there is uncommitted text will call AquaSalFrame::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. +static void FlushIMBeforeShortCut(WinSalFrame* pFrame, SalEvent nEvent, sal_uInt16 nModCode) +{ + if (pFrame->mbCandidateMode && nEvent == SalEvent::KeyInput + && (nModCode & (KEY_MOD1 | KEY_MOD2))) + { + pFrame->EndExtTextInput(EndExtTextInputFlags::Complete); + } +} + +static bool ImplHandleKeyMsg( HWND hWnd, UINT nMsg, + WPARAM wParam, LPARAM lParam, LRESULT& rResult ) +{ + static bool bIgnoreCharMsg = false; + static WPARAM nDeadChar = 0; + static WPARAM nLastVKChar = 0; + static sal_uInt16 nLastChar = 0; + static ModKeyFlags nLastModKeyCode = ModKeyFlags::NONE; + static bool bWaitForModKeyRelease = false; + sal_uInt16 nRepeat = LOWORD( lParam )-1; + sal_uInt16 nModCode = 0; + + // this key might have been relayed by SysChild and thus + // may not be processed twice + GetSalData()->mnSalObjWantKeyEvt = 0; + + if ( nMsg == WM_DEADCHAR ) + { + nDeadChar = wParam; + return false; + } + + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( !pFrame ) + return false; + + // 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 ); + + // determine modifiers + if ( GetKeyState( VK_SHIFT ) & 0x8000 ) + nModCode |= KEY_SHIFT; + if ( GetKeyState( VK_CONTROL ) & 0x8000 ) + nModCode |= KEY_MOD1; + if (GetKeyState(VK_MENU) & 0x8000) + nModCode |= KEY_MOD2; + + if ( (nMsg == WM_CHAR) || (nMsg == WM_SYSCHAR) ) + { + nDeadChar = 0; + + 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) ) + return false; + else + return true; + } + + // ignore backspace as a single key, so that + // we do not get problems for combinations w/ a DeadKey + if ( wParam == 0x08 ) // BACKSPACE + return false; + + // only "free flying" WM_CHAR messages arrive here, that are + // created by typing an ALT-NUMPAD combination + SalKeyEvent aKeyEvt; + + if ( (wParam >= '0') && (wParam <= '9') ) + aKeyEvt.mnCode = sal::static_int_cast<sal_uInt16>(KEYGROUP_NUM + wParam - '0'); + else if ( (wParam >= 'A') && (wParam <= 'Z') ) + aKeyEvt.mnCode = sal::static_int_cast<sal_uInt16>(KEYGROUP_ALPHA + wParam - 'A'); + else if ( (wParam >= 'a') && (wParam <= 'z') ) + aKeyEvt.mnCode = sal::static_int_cast<sal_uInt16>(KEYGROUP_ALPHA + wParam - 'a'); + else if ( wParam == 0x0D ) // RETURN + aKeyEvt.mnCode = KEY_RETURN; + else if ( wParam == 0x1B ) // ESCAPE + aKeyEvt.mnCode = KEY_ESCAPE; + else if ( wParam == 0x09 ) // TAB + aKeyEvt.mnCode = KEY_TAB; + else if ( wParam == 0x20 ) // SPACE + aKeyEvt.mnCode = KEY_SPACE; + else + aKeyEvt.mnCode = 0; + + aKeyEvt.mnCode |= nModCode; + aKeyEvt.mnCharCode = ImplGetCharCode( pFrame, wParam ); + aKeyEvt.mnRepeat = nRepeat; + + UnsetAltIfAltGr(aKeyEvt, nModCode); + FlushIMBeforeShortCut(pFrame, SalEvent::KeyInput, nModCode); + + nLastChar = 0; + nLastVKChar = 0; + + 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 + else if( 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 + return true; // ...but this will only avoid calling the defwindowproc + } + + 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 + // sal_Unicode ch = (sal_Unicode) Uni_UTF32ToSurrogate1(wParam); + nLastChar = 0; + nLastVKChar = 0; + pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt ); + pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt ); + wParam = rtl::getLowSurrogate( wParam ); + } + + aKeyEvt.mnCharCode = static_cast<sal_Unicode>(wParam); + + nLastChar = 0; + nLastVKChar = 0; + bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt ); + pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt ); + + return nRet; + } + // MCD, 2003-01-13, Support for WM_UNICHAR & Keyman 6.0; addition ends + else + { + // for shift, control and menu we issue a KeyModChange event + if ( (wParam == VK_SHIFT) || (wParam == VK_CONTROL) || (wParam == VK_MENU) ) + { + SalKeyModEvent aModEvt; + aModEvt.mbDown = false; // auto-accelerator feature not supported here. + aModEvt.mnCode = nModCode; + aModEvt.mnModKeyCode = ModKeyFlags::NONE; // no command events will be sent if this member is 0 + + ModKeyFlags tmpCode = ModKeyFlags::NONE; + if( GetKeyState( VK_LSHIFT ) & 0x8000 ) + tmpCode |= ModKeyFlags::LeftShift; + if( GetKeyState( VK_RSHIFT ) & 0x8000 ) + tmpCode |= ModKeyFlags::RightShift; + if( GetKeyState( VK_LCONTROL ) & 0x8000 ) + tmpCode |= ModKeyFlags::LeftMod1; + if( GetKeyState( VK_RCONTROL ) & 0x8000 ) + tmpCode |= ModKeyFlags::RightMod1; + if( GetKeyState( VK_LMENU ) & 0x8000 ) + tmpCode |= ModKeyFlags::LeftMod2; + if( GetKeyState( VK_RMENU ) & 0x8000 ) + tmpCode |= ModKeyFlags::RightMod2; + + if( tmpCode < nLastModKeyCode ) + { + aModEvt.mnModKeyCode = nLastModKeyCode; + nLastModKeyCode = ModKeyFlags::NONE; + bWaitForModKeyRelease = true; + } + else + { + if( !bWaitForModKeyRelease ) + nLastModKeyCode = tmpCode; + } + + if( tmpCode == ModKeyFlags::NONE ) + bWaitForModKeyRelease = false; + + return pFrame->CallCallback( SalEvent::KeyModChange, &aModEvt ); + } + else + { + SalKeyEvent aKeyEvt; + SalEvent nEvent; + MSG aCharMsg; + bool bCharPeek = false; + UINT nCharMsg = WM_CHAR; + bool bKeyUp = (nMsg == WM_KEYUP) || (nMsg == WM_SYSKEYUP); + + 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; + + if ( wParam == VK_BACK ) + { + PeekMessageW( &aCharMsg, hWnd, + nCharMsg, nCharMsg, PM_REMOVE | PM_NOYIELD ); + return false; + } + } + else + { + if ( !bCharPeek ) + { + bCharPeek = PeekMessageW( &aCharMsg, hWnd, + WM_SYSCHAR, WM_SYSCHAR, PM_NOREMOVE | PM_NOYIELD ); + nCharMsg = WM_SYSCHAR; + } + } + if ( bCharPeek ) + aKeyEvt.mnCharCode = ImplGetCharCode( pFrame, aCharMsg.wParam ); + else + aKeyEvt.mnCharCode = 0; + + nLastChar = aKeyEvt.mnCharCode; + nLastVKChar = wParam; + } + else + { + if ( wParam == nLastVKChar ) + { + aKeyEvt.mnCharCode = nLastChar; + nLastChar = 0; + nLastVKChar = 0; + } + } + + if ( aKeyEvt.mnCode || aKeyEvt.mnCharCode ) + { + if ( bKeyUp ) + nEvent = SalEvent::KeyUp; + else + nEvent = SalEvent::KeyInput; + + aKeyEvt.mnCode |= nModCode; + aKeyEvt.mnRepeat = nRepeat; + + UnsetAltIfAltGr(aKeyEvt, nModCode); + FlushIMBeforeShortCut(pFrame, nEvent, nModCode); + + 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; + } + + return nRet; + } + else + return false; + } + } +} + +bool ImplHandleSalObjKeyMsg( HWND hWnd, UINT nMsg, + WPARAM wParam, LPARAM lParam ) +{ + if ( (nMsg == WM_KEYDOWN) || (nMsg == WM_KEYUP) ) + { + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( !pFrame ) + return false; + + sal_uInt16 nRepeat = LOWORD( lParam )-1; + sal_uInt16 nModCode = 0; + + // determine modifiers + if ( GetKeyState( VK_SHIFT ) & 0x8000 ) + nModCode |= KEY_SHIFT; + if ( GetKeyState( VK_CONTROL ) & 0x8000 ) + nModCode |= KEY_MOD1; + if ( GetKeyState( VK_MENU ) & 0x8000 ) + nModCode |= KEY_MOD2; + + if ( (wParam != VK_SHIFT) && (wParam != VK_CONTROL) && (wParam != VK_MENU) ) + { + SalKeyEvent aKeyEvt; + SalEvent nEvent; + + // convert KeyCode + aKeyEvt.mnCode = ImplSalGetKeyCode( wParam ); + aKeyEvt.mnCharCode = 0; + + if ( aKeyEvt.mnCode ) + { + if (nMsg == WM_KEYUP) + nEvent = SalEvent::KeyUp; + else + nEvent = SalEvent::KeyInput; + + aKeyEvt.mnCode |= nModCode; + aKeyEvt.mnRepeat = nRepeat; + bool nRet = pFrame->CallCallback( nEvent, &aKeyEvt ); + return nRet; + } + else + return false; + } + } + + return false; +} + +bool ImplHandleSalObjSysCharMsg( HWND hWnd, WPARAM wParam, LPARAM lParam ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( !pFrame ) + return false; + + sal_uInt16 nRepeat = LOWORD( lParam )-1; + sal_uInt16 nModCode = 0; + sal_uInt16 cKeyCode = static_cast<sal_uInt16>(wParam); + + // determine modifiers + if ( GetKeyState( VK_SHIFT ) & 0x8000 ) + nModCode |= KEY_SHIFT; + if ( GetKeyState( VK_CONTROL ) & 0x8000 ) + nModCode |= KEY_MOD1; + nModCode |= KEY_MOD2; + + // assemble KeyEvent + SalKeyEvent aKeyEvt; + if ( (cKeyCode >= 48) && (cKeyCode <= 57) ) + aKeyEvt.mnCode = KEY_0+(cKeyCode-48); + else if ( (cKeyCode >= 65) && (cKeyCode <= 90) ) + aKeyEvt.mnCode = KEY_A+(cKeyCode-65); + else if ( (cKeyCode >= 97) && (cKeyCode <= 122) ) + aKeyEvt.mnCode = KEY_A+(cKeyCode-97); + else + aKeyEvt.mnCode = 0; + aKeyEvt.mnCode |= nModCode; + aKeyEvt.mnCharCode = ImplGetCharCode( pFrame, cKeyCode ); + aKeyEvt.mnRepeat = nRepeat; + bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt ); + pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt ); + return nRet; +} + +namespace { + +enum class DeferPolicy +{ + Blocked, + Allowed +}; + +} + +// Remember to release the solar mutex on success! +static WinSalFrame* ProcessOrDeferMessage( HWND hWnd, INT nMsg, WPARAM pWParam = 0, + DeferPolicy eCanDefer = DeferPolicy::Allowed ) +{ + bool bFailedCondition = false, bGotMutex = false; + WinSalFrame* pFrame = nullptr; + + if ( DeferPolicy::Blocked == eCanDefer ) + assert( (DeferPolicy::Blocked == eCanDefer) && (nMsg == 0) && (pWParam == 0) ); + else + assert( (DeferPolicy::Allowed == eCanDefer) && (nMsg != 0) ); + + if ( DeferPolicy::Blocked == eCanDefer ) + { + ImplSalYieldMutexAcquireWithWait(); + bGotMutex = true; + } + else if ( !(bGotMutex = ImplSalYieldMutexTryToAcquire()) ) + bFailedCondition = true; + + if ( !bFailedCondition ) + { + pFrame = GetWindowPtr( hWnd ); + bFailedCondition = pFrame == nullptr; + } + + if ( bFailedCondition ) + { + if ( bGotMutex ) + ImplSalYieldMutexRelease(); + if ( DeferPolicy::Allowed == eCanDefer ) + { + bool const ret = PostMessageW(hWnd, nMsg, pWParam, 0); + SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!"); + } + } + + return pFrame; +} + +namespace { + +enum class PostedState +{ + IsPosted, + IsInitial +}; + +} + +static bool ImplHandlePostPaintMsg( HWND hWnd, RECT* pRect, + PostedState eProcessed = PostedState::IsPosted ) +{ + RECT* pMsgRect; + if ( PostedState::IsInitial == eProcessed ) + { + pMsgRect = new RECT; + CopyRect( pMsgRect, pRect ); + } + else + pMsgRect = pRect; + + WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_POSTPAINT, + reinterpret_cast<WPARAM>(pMsgRect) ); + if ( pFrame ) + { + SalPaintEvent aPEvt( pRect->left, pRect->top, pRect->right-pRect->left, pRect->bottom-pRect->top ); + pFrame->CallCallback( SalEvent::Paint, &aPEvt ); + ImplSalYieldMutexRelease(); + if ( PostedState::IsPosted == eProcessed ) + delete pRect; + } + + return (pFrame != nullptr); +} + +static bool ImplHandlePaintMsg( HWND hWnd ) +{ + bool bPaintSuccessful = false; + + // 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; +} + +static void SetMaximizedFrameGeometry( HWND hWnd, WinSalFrame* pFrame, 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 aRectMouse; + if( ! pParentRect ) + { + POINT pt; + GetCursorPos( &pt ); + aRectMouse.left = pt.x; + aRectMouse.top = pt.y; + aRectMouse.right = pt.x+2; + aRectMouse.bottom = pt.y+2; + pParentRect = &aRectMouse; + } + + RECT aRect; + ImplSalGetWorkArea( hWnd, &aRect, pParentRect ); + + // a maximized window has no other borders than the caption + pFrame->maGeometry.nLeftDecoration = pFrame->maGeometry.nRightDecoration = pFrame->maGeometry.nBottomDecoration = 0; + pFrame->maGeometry.nTopDecoration = pFrame->mbCaption ? GetSystemMetrics( SM_CYCAPTION ) : 0; + + aRect.top += pFrame->maGeometry.nTopDecoration; + pFrame->maGeometry.nX = aRect.left; + pFrame->maGeometry.nY = aRect.top; + pFrame->maGeometry.nWidth = aRect.right - aRect.left; + pFrame->maGeometry.nHeight = aRect.bottom - aRect.top; +} + +static void UpdateFrameGeometry( HWND hWnd, WinSalFrame* pFrame ) +{ + if( !pFrame ) + return; + + RECT aRect; + GetWindowRect( hWnd, &aRect ); + pFrame->maGeometry.nX = 0; + pFrame->maGeometry.nY = 0; + pFrame->maGeometry.nWidth = 0; + pFrame->maGeometry.nHeight = 0; + pFrame->maGeometry.nLeftDecoration = 0; + pFrame->maGeometry.nTopDecoration = 0; + pFrame->maGeometry.nRightDecoration = 0; + pFrame->maGeometry.nBottomDecoration = 0; + pFrame->maGeometry.nDisplayScreenNumber = 0; + + if ( IsIconic( hWnd ) ) + return; + + POINT aPt; + aPt.x=0; + aPt.y=0; + ClientToScreen(hWnd, &aPt); + int cx = aPt.x - aRect.left; + pFrame->maGeometry.nTopDecoration = aPt.y - aRect.top; + + pFrame->maGeometry.nLeftDecoration = cx; + pFrame->maGeometry.nRightDecoration = cx; + + pFrame->maGeometry.nX = aPt.x; + pFrame->maGeometry.nY = aPt.y; + + RECT aInnerRect; + GetClientRect( hWnd, &aInnerRect ); + if( aInnerRect.right ) + { + // improve right decoration + aPt.x=aInnerRect.right; + aPt.y=aInnerRect.top; + ClientToScreen(hWnd, &aPt); + pFrame->maGeometry.nRightDecoration = aRect.right - aPt.x; + } + if( aInnerRect.bottom ) // may be zero if window was not shown yet + pFrame->maGeometry.nBottomDecoration += aRect.bottom - aPt.y - aInnerRect.bottom; + else + // bottom border is typically the same as left/right + pFrame->maGeometry.nBottomDecoration = pFrame->maGeometry.nLeftDecoration; + + int nWidth = aRect.right - aRect.left + - pFrame->maGeometry.nRightDecoration - pFrame->maGeometry.nLeftDecoration; + int nHeight = aRect.bottom - aRect.top + - pFrame->maGeometry.nBottomDecoration - pFrame->maGeometry.nTopDecoration; + // clamp to zero + pFrame->maGeometry.nHeight = nHeight < 0 ? 0 : nHeight; + pFrame->maGeometry.nWidth = nWidth < 0 ? 0 : nWidth; + pFrame->updateScreenNumber(); +} + +static void ImplCallMoveHdl( HWND hWnd ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( pFrame ) + { + pFrame->CallCallback( SalEvent::Move, nullptr ); + // to avoid doing Paint twice by VCL and SAL + //if ( IsWindowVisible( hWnd ) && !pFrame->mbInShow ) + // UpdateWindow( hWnd ); + } +} + +static void ImplCallClosePopupsHdl( HWND hWnd ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( pFrame ) + { + pFrame->CallCallback( SalEvent::ClosePopups, nullptr ); + } +} + +static void ImplHandleMoveMsg( HWND hWnd ) +{ + WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_POSTMOVE ); + if ( pFrame ) + { + UpdateFrameGeometry( hWnd, pFrame ); + + if ( GetWindowStyle( hWnd ) & WS_VISIBLE ) + pFrame->mbDefPos = false; + + // protect against recursion + if ( !pFrame->mbInMoveMsg ) + { + // adjust window again for FullScreenMode + pFrame->mbInMoveMsg = true; + if ( pFrame->mbFullScreen ) + ImplSalFrameFullScreenPos( pFrame ); + pFrame->mbInMoveMsg = false; + } + + // save state + ImplSaveFrameState( pFrame ); + + // Call Hdl + //#93851 if we call this handler, VCL floating windows are not updated correctly + ImplCallMoveHdl( hWnd ); + + ImplSalYieldMutexRelease(); + } +} + +static void ImplCallSizeHdl( HWND hWnd ) +{ + // as Windows can send these messages also, we have to use + // the Solar semaphore + WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_POSTCALLSIZE ); + if ( pFrame ) + { + pFrame->CallCallback( SalEvent::Resize, nullptr ); + // to avoid double Paints by VCL and SAL + if ( IsWindowVisible( hWnd ) && !pFrame->mbInShow ) + UpdateWindow( hWnd ); + + ImplSalYieldMutexRelease(); + } +} + +static void ImplHandleSizeMsg( HWND hWnd, WPARAM wParam, LPARAM lParam ) +{ + if ( (wParam != SIZE_MAXSHOW) && (wParam != SIZE_MAXHIDE) ) + { + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( pFrame ) + { + UpdateFrameGeometry( hWnd, pFrame ); + + pFrame->mnWidth = static_cast<LONG>(LOWORD(lParam)); + pFrame->mnHeight = static_cast<LONG>(HIWORD(lParam)); + // save state + ImplSaveFrameState( pFrame ); + // Call Hdl + ImplCallSizeHdl( hWnd ); + + WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer ); + if ( pTimer ) + pTimer->SetForceRealTimer( true ); + } + } +} + +static void ImplHandleFocusMsg( HWND hWnd ) +{ + WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_POSTFOCUS ); + if ( pFrame ) + { + if ( !WinSalFrame::mbInReparent ) + { + bool bGotFocus = ::GetFocus() == hWnd; + if ( bGotFocus ) + { + if ( IsWindowVisible( hWnd ) && !pFrame->mbInShow ) + UpdateWindow( hWnd ); + + // do we support IME? + if ( pFrame->mbIME && pFrame->mhDefIMEContext ) + { + UINT nImeProps = ImmGetProperty( GetKeyboardLayout( 0 ), IGP_PROPERTY ); + + pFrame->mbSpezIME = (nImeProps & IME_PROP_SPECIAL_UI) != 0; + pFrame->mbAtCursorIME = (nImeProps & IME_PROP_AT_CARET) != 0; + pFrame->mbHandleIME = !pFrame->mbSpezIME; + } + } + pFrame->CallCallback( bGotFocus ? SalEvent::GetFocus : SalEvent::LoseFocus, nullptr ); + } + ImplSalYieldMutexRelease(); + } +} + +static void ImplHandleCloseMsg( HWND hWnd ) +{ + WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, WM_CLOSE ); + if ( pFrame ) + { + pFrame->CallCallback( SalEvent::Close, nullptr ); + ImplSalYieldMutexRelease(); + } +} + +static bool ImplHandleShutDownMsg( HWND hWnd ) +{ + bool nRet = false; + WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, 0, 0, DeferPolicy::Blocked ); + if ( pFrame ) + { + nRet = pFrame->CallCallback( SalEvent::Shutdown, nullptr ); + ImplSalYieldMutexRelease(); + } + return nRet; +} + +static void ImplHandleSettingsChangeMsg( HWND hWnd, UINT nMsg, + WPARAM wParam, LPARAM lParam ) +{ + SalEvent nSalEvent = SalEvent::SettingsChanged; + + if ( nMsg == WM_DEVMODECHANGE ) + nSalEvent = SalEvent::PrinterChanged; + else if ( nMsg == WM_DISPLAYCHANGE ) + nSalEvent = SalEvent::DisplayChanged; + else if ( nMsg == WM_FONTCHANGE ) + nSalEvent = SalEvent::FontChanged; + else if ( nMsg == WM_WININICHANGE ) + { + if ( lParam ) + { + if ( ImplSalWICompareAscii( reinterpret_cast<const wchar_t*>(lParam), "devices" ) == 0 ) + nSalEvent = SalEvent::PrinterChanged; + } + } + + if ( nMsg == WM_SETTINGCHANGE ) + { + if ( wParam == SPI_SETWHEELSCROLLLINES ) + aSalShlData.mnWheelScrollLines = ImplSalGetWheelScrollLines(); + else if( wParam == SPI_SETWHEELSCROLLCHARS ) + aSalShlData.mnWheelScrollChars = ImplSalGetWheelScrollChars(); + UpdateDarkMode(hWnd); + GetSalData()->mbThemeChanged = true; + } + + if ( WM_SYSCOLORCHANGE == nMsg && GetSalData()->mhDitherPal ) + ImplUpdateSysColorEntries(); + + WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, 0, 0, DeferPolicy::Blocked ); + if ( pFrame ) + { + if ( (nMsg == WM_DISPLAYCHANGE) || (nMsg == WM_WININICHANGE) ) + { + if ( pFrame->mbFullScreen ) + ImplSalFrameFullScreenPos( pFrame ); + } + + pFrame->CallCallback( nSalEvent, nullptr ); + ImplSalYieldMutexRelease(); + } +} + +static void ImplHandleUserEvent( HWND hWnd, LPARAM lParam ) +{ + WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, 0, 0, DeferPolicy::Blocked ); + if ( pFrame ) + { + pFrame->CallCallback( SalEvent::UserEvent, reinterpret_cast<void*>(lParam) ); + ImplSalYieldMutexRelease(); + } +} + +static void ImplHandleForcePalette( HWND hWnd ) +{ + SalData* pSalData = GetSalData(); + HPALETTE hPal = pSalData->mhDitherPal; + if ( hPal ) + { + WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_FORCEPALETTE ); + if ( pFrame && pFrame->mpLocalGraphics && pFrame->mpLocalGraphics->getHDC() ) + { + WinSalGraphics* pGraphics = pFrame->mpLocalGraphics; + if (pGraphics->getDefPal()) + { + if (pGraphics->setPalette(hPal, FALSE) != GDI_ERROR) + { + InvalidateRect( hWnd, nullptr, FALSE ); + UpdateWindow( hWnd ); + pFrame->CallCallback( SalEvent::DisplayChanged, nullptr ); + } + } + } + if ( pFrame ) + ImplSalYieldMutexRelease(); + } +} + +static LRESULT ImplHandlePalette( bool bFrame, HWND hWnd, UINT nMsg, + WPARAM wParam, LPARAM lParam, bool& rDef ) +{ + SalData* pSalData = GetSalData(); + HPALETTE hPal = pSalData->mhDitherPal; + if ( !hPal ) + return 0; + + rDef = false; + if ( pSalData->mbInPalChange ) + return 0; + + if ( (nMsg == WM_PALETTECHANGED) || (nMsg == SAL_MSG_POSTPALCHANGED) ) + { + if ( reinterpret_cast<HWND>(wParam) == hWnd ) + return 0; + } + + bool bReleaseMutex = false; + if ( (nMsg == WM_QUERYNEWPALETTE) || (nMsg == WM_PALETTECHANGED) ) + { + // as Windows can send these messages also, we have to use + // the Solar semaphore + if ( ImplSalYieldMutexTryToAcquire() ) + bReleaseMutex = true; + else if ( nMsg == WM_QUERYNEWPALETTE ) + { + bool const ret = PostMessageW(hWnd, SAL_MSG_POSTQUERYNEWPAL, wParam, lParam); + SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!"); + } + else /* ( nMsg == WM_PALETTECHANGED ) */ + { + bool const ret = PostMessageW(hWnd, SAL_MSG_POSTPALCHANGED, wParam, lParam); + SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!"); + } + } + + WinSalVirtualDevice*pTempVD; + WinSalFrame* pTempFrame; + WinSalGraphics* pGraphics; + HDC hDC; + HPALETTE hOldPal = nullptr; + UINT nCols = GDI_ERROR; + bool bUpdate; + + pSalData->mbInPalChange = true; + + // reset all palettes in VirDevs and Frames + pTempVD = pSalData->mpFirstVD; + while ( pTempVD ) + { + pGraphics = pTempVD->getGraphics(); + pGraphics->setPalette(nullptr); + pTempVD = pTempVD->getNext(); + } + pTempFrame = pSalData->mpFirstFrame; + while ( pTempFrame ) + { + pGraphics = pTempFrame->mpLocalGraphics; + pGraphics->setPalette(nullptr); + pTempFrame = pTempFrame->mpNextFrame; + } + + // re-initialize palette + WinSalFrame* pFrame = nullptr; + if ( bFrame ) + pFrame = GetWindowPtr( hWnd ); + + UnrealizeObject(hPal); + const bool bStdDC = pFrame && pFrame->mpLocalGraphics && pFrame->mpLocalGraphics->getHDC(); + if (!bStdDC) + { + hDC = GetDC(hWnd); + hOldPal = SelectPalette(hDC, hPal, TRUE); + if (hOldPal) + nCols = RealizePalette(hDC); + } + else + { + hDC = pFrame->mpLocalGraphics->getHDC(); + nCols = pFrame->mpLocalGraphics->setPalette(hPal); + } + + bUpdate = nCols != 0 && nCols != GDI_ERROR; + + if ( !bStdDC ) + { + if (hOldPal) + SelectPalette(hDC, hOldPal, TRUE); + ReleaseDC( hWnd, hDC ); + } + + // reset all palettes in VirDevs and Frames + pTempVD = pSalData->mpFirstVD; + while ( pTempVD ) + { + pGraphics = pTempVD->getGraphics(); + if ( pGraphics->getDefPal() ) + pGraphics->setPalette(hPal); + pTempVD = pTempVD->getNext(); + } + + pTempFrame = pSalData->mpFirstFrame; + while ( pTempFrame ) + { + if ( pTempFrame != pFrame ) + { + pGraphics = pTempFrame->mpLocalGraphics; + if (pGraphics && pGraphics->getDefPal()) + { + UINT nRes = pGraphics->setPalette(hPal); + if (nRes != 0 && nRes != GDI_ERROR) + bUpdate = true; + } + } + pTempFrame = pTempFrame->mpNextFrame; + } + + // if colors changed, update the window + if ( bUpdate ) + { + pTempFrame = pSalData->mpFirstFrame; + while ( pTempFrame ) + { + pGraphics = pTempFrame->mpLocalGraphics; + if (pGraphics && pGraphics->getDefPal()) + { + InvalidateRect( pTempFrame->mhWnd, nullptr, FALSE ); + UpdateWindow( pTempFrame->mhWnd ); + pTempFrame->CallCallback( SalEvent::DisplayChanged, nullptr ); + } + pTempFrame = pTempFrame->mpNextFrame; + } + } + + pSalData->mbInPalChange = false; + + if ( bReleaseMutex ) + ImplSalYieldMutexRelease(); + + if ( nMsg == WM_PALETTECHANGED ) + return 0; + else + return nCols; +} + +static bool ImplHandleMinMax( HWND hWnd, LPARAM lParam ) +{ + bool bRet = false; + + if ( ImplSalYieldMutexTryToAcquire() ) + { + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( pFrame ) + { + MINMAXINFO* pMinMax = reinterpret_cast<MINMAXINFO*>(lParam); + + if ( pFrame->mbFullScreen ) + { + int nX; + int nY; + int nDX; + int nDY; + ImplSalCalcFullScreenSize( pFrame, nX, nY, nDX, nDY ); + + if ( pMinMax->ptMaxPosition.x > nX ) + pMinMax->ptMaxPosition.x = nX; + if ( pMinMax->ptMaxPosition.y > nY ) + pMinMax->ptMaxPosition.y = nY; + + if ( pMinMax->ptMaxSize.x < nDX ) + pMinMax->ptMaxSize.x = nDX; + if ( pMinMax->ptMaxSize.y < nDY ) + pMinMax->ptMaxSize.y = nDY; + if ( pMinMax->ptMaxTrackSize.x < nDX ) + pMinMax->ptMaxTrackSize.x = nDX; + if ( pMinMax->ptMaxTrackSize.y < nDY ) + pMinMax->ptMaxTrackSize.y = nDY; + + pMinMax->ptMinTrackSize.x = nDX; + pMinMax->ptMinTrackSize.y = nDY; + + bRet = true; + } + + 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: " << WindowsErrorString(GetLastError())); + + return reinterpret_cast<WinSalMenuItem *>(mi.dwItemData); +} + +// returns the index of the currently selected item if any or -1 +static int ImplGetSelectedIndex( HMENU hMenu ) +{ + MENUITEMINFOW mi = {}; + mi.cbSize = sizeof( mi ); + mi.fMask = MIIM_STATE; + int n = GetMenuItemCount( hMenu ); + if( n != -1 ) + { + for(int i=0; i<n; i++ ) + { + if( !GetMenuItemInfoW( hMenu, i, TRUE, &mi) ) + SAL_WARN( "vcl", "GetMenuItemInfoW failed: " << WindowsErrorString( GetLastError() ) ); + else + { + if( mi.fState & MFS_HILITE ) + return i; + } + } + } + return -1; +} + +static LRESULT ImplMenuChar( HWND, WPARAM wParam, LPARAM lParam ) +{ + LRESULT nRet = MNC_IGNORE; + HMENU hMenu = reinterpret_cast<HMENU>(lParam); + OUString aMnemonic( "&" + OUStringChar(static_cast<sal_Unicode>(LOWORD(wParam))) ); + aMnemonic = aMnemonic.toAsciiLowerCase(); // we only have ascii mnemonics + + // search the mnemonic in the current menu + int nItemCount = GetMenuItemCount( hMenu ); + int nFound = 0; + int idxFound = -1; + int idxSelected = ImplGetSelectedIndex( hMenu ); + int idx = idxSelected != -1 ? idxSelected+1 : 0; // if duplicate mnemonics cycle through menu + for( int i=0; i< nItemCount; i++, idx++ ) + { + WinSalMenuItem* pSalMenuItem = ImplGetSalMenuItem( hMenu, idx % nItemCount ); + if( !pSalMenuItem ) + continue; + OUString aStr = pSalMenuItem->mText; + aStr = aStr.toAsciiLowerCase(); + if( aStr.indexOf( aMnemonic ) != -1 ) + { + if( idxFound == -1 ) + idxFound = idx % nItemCount; + if( nFound++ ) + break; // duplicate found + } + } + if( nFound == 1 ) + nRet = MAKELRESULT( idxFound, MNC_EXECUTE ); + else + // duplicate mnemonics, just select the next occurrence + nRet = MAKELRESULT( idxFound, MNC_SELECT ); + + return nRet; +} + +static LRESULT ImplMeasureItem( HWND hWnd, WPARAM wParam, LPARAM lParam ) +{ + LRESULT nRet = 0; + if( !wParam ) + { + // request was sent by a menu + nRet = 1; + MEASUREITEMSTRUCT *pMI = reinterpret_cast<LPMEASUREITEMSTRUCT>(lParam); + if( pMI->CtlType != ODT_MENU ) + return 0; + + WinSalMenuItem *pSalMenuItem = reinterpret_cast<WinSalMenuItem *>(pMI->itemData); + if( !pSalMenuItem ) + return 0; + + HDC hdc = GetDC( hWnd ); + SIZE strSize; + + NONCLIENTMETRICSW ncm = {}; + ncm.cbSize = sizeof( ncm ); + SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); + + // Assume every menu item can be default and printed bold + //ncm.lfMenuFont.lfWeight = FW_BOLD; + + HFONT hfntOld = static_cast<HFONT>(SelectObject(hdc, CreateFontIndirectW( &ncm.lfMenuFont ))); + + // menu text and accelerator + OUString aStr(pSalMenuItem->mText); + if( pSalMenuItem->mAccelText.getLength() ) + { + aStr += " " + pSalMenuItem->mAccelText; + } + GetTextExtentPoint32W( hdc, o3tl::toW(aStr.getStr()), + aStr.getLength(), &strSize ); + + // image + Size bmpSize( 16, 16 ); + //if( pSalMenuItem->maBitmap ) + // bmpSize = pSalMenuItem->maBitmap.GetSizePixel(); + + // checkmark + Size checkSize( GetSystemMetrics( SM_CXMENUCHECK ), GetSystemMetrics( SM_CYMENUCHECK ) ); + + pMI->itemWidth = checkSize.Width() + 3 + bmpSize.Width() + 3 + strSize.cx; + pMI->itemHeight = std::max( std::max( checkSize.Height(), bmpSize.Height() ), tools::Long(strSize.cy) ); + pMI->itemHeight += 4; + + DeleteObject( SelectObject(hdc, hfntOld) ); + ReleaseDC( hWnd, hdc ); + } + + return nRet; +} + +static LRESULT ImplDrawItem(HWND, WPARAM wParam, LPARAM lParam ) +{ + LRESULT nRet = 0; + if( !wParam ) + { + // request was sent by a menu + nRet = 1; + DRAWITEMSTRUCT *pDI = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam); + if( pDI->CtlType != ODT_MENU ) + return 0; + + WinSalMenuItem *pSalMenuItem = reinterpret_cast<WinSalMenuItem *>(pDI->itemData); + if( !pSalMenuItem ) + return 0; + + COLORREF clrPrevText, clrPrevBkgnd; + HFONT hfntOld; + HBRUSH hbrOld; + bool fChecked = (pDI->itemState & ODS_CHECKED); + bool fSelected = (pDI->itemState & ODS_SELECTED); + bool fDisabled = (pDI->itemState & (ODS_DISABLED | ODS_GRAYED)); + + // Set the appropriate foreground and background colors. + RECT aRect = pDI->rcItem; + + if ( fDisabled ) + clrPrevText = SetTextColor( pDI->hDC, GetSysColor( COLOR_GRAYTEXT ) ); + else + clrPrevText = SetTextColor( pDI->hDC, GetSysColor( fSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT ) ); + + DWORD colBackground = GetSysColor( fSelected ? COLOR_HIGHLIGHT : COLOR_MENU ); + clrPrevBkgnd = SetBkColor( pDI->hDC, colBackground ); + + hbrOld = static_cast<HBRUSH>(SelectObject( pDI->hDC, CreateSolidBrush( GetBkColor( pDI->hDC ) ) )); + + // Fill background + if(!PatBlt( pDI->hDC, aRect.left, aRect.top, aRect.right-aRect.left, aRect.bottom-aRect.top, PATCOPY )) + SAL_WARN("vcl", "PatBlt failed: " << WindowsErrorString(GetLastError())); + + int lineHeight = aRect.bottom-aRect.top; + + int x = aRect.left; + int y = aRect.top; + + int checkWidth = GetSystemMetrics( SM_CXMENUCHECK ); + int checkHeight = GetSystemMetrics( SM_CYMENUCHECK ); + if( fChecked ) + { + RECT r; + r.left = 0; + r.top = 0; + r.right = checkWidth; + r.bottom = checkWidth; + HDC memDC = CreateCompatibleDC( pDI->hDC ); + HBITMAP memBmp = CreateCompatibleBitmap( pDI->hDC, checkWidth, checkHeight ); + HBITMAP hOldBmp = static_cast<HBITMAP>(SelectObject( memDC, memBmp )); + DrawFrameControl( memDC, &r, DFC_MENU, DFCS_MENUCHECK ); + BitBlt( pDI->hDC, x, y+(lineHeight-checkHeight)/2, checkWidth, checkHeight, memDC, 0, 0, SRCAND ); + DeleteObject( SelectObject( memDC, hOldBmp ) ); + DeleteDC( memDC ); + } + x += checkWidth+3; + + //Size bmpSize = aBitmap.GetSizePixel(); + Size bmpSize(16, 16); + if( !pSalMenuItem->maBitmap.IsEmpty() ) + { + Bitmap aBitmap( pSalMenuItem->maBitmap ); + + // set transparent pixels to background color + if( fDisabled ) + colBackground = RGB(255,255,255); + aBitmap.Replace( COL_LIGHTMAGENTA, + Color( GetRValue(colBackground),GetGValue(colBackground),GetBValue(colBackground) )); + + WinSalBitmap* pSalBmp = static_cast<WinSalBitmap*>(aBitmap.ImplGetSalBitmap().get()); + HGLOBAL hDrawDIB = pSalBmp->ImplGethDIB(); + + if( hDrawDIB ) + { + PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( hDrawDIB )); + PBYTE pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize + + WinSalBitmap::ImplGetDIBColorCount( hDrawDIB ) * sizeof( RGBQUAD ); + + HBITMAP hBmp = CreateDIBitmap( pDI->hDC, &pBI->bmiHeader, CBM_INIT, pBits, pBI, DIB_RGB_COLORS ); + GlobalUnlock( hDrawDIB ); + + HBRUSH hbrIcon = CreateSolidBrush( GetSysColor( COLOR_GRAYTEXT ) ); + DrawStateW( pDI->hDC, hbrIcon, nullptr, reinterpret_cast<LPARAM>(hBmp), WPARAM(0), + x, y+(lineHeight-bmpSize.Height())/2, bmpSize.Width(), bmpSize.Height(), + DST_BITMAP | (fDisabled ? (fSelected ? DSS_MONO : DSS_DISABLED) : DSS_NORMAL) ); + + DeleteObject( hbrIcon ); + DeleteObject( hBmp ); + } + + } + x += bmpSize.Width() + 3; + aRect.left = x; + + NONCLIENTMETRICSW ncm = {}; + ncm.cbSize = sizeof( ncm ); + SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 ); + + // Print default menu entry with bold font + //if ( pDI->itemState & ODS_DEFAULT ) + // ncm.lfMenuFont.lfWeight = FW_BOLD; + + hfntOld = static_cast<HFONT>(SelectObject(pDI->hDC, CreateFontIndirectW( &ncm.lfMenuFont ))); + + SIZE strSize; + OUString aStr( pSalMenuItem->mText ); + GetTextExtentPoint32W( pDI->hDC, o3tl::toW(aStr.getStr()), + aStr.getLength(), &strSize ); + + if(!DrawStateW( pDI->hDC, nullptr, nullptr, + reinterpret_cast<LPARAM>(aStr.getStr()), + WPARAM(0), aRect.left, aRect.top + (lineHeight - strSize.cy)/2, 0, 0, + DST_PREFIXTEXT | (fDisabled && !fSelected ? DSS_DISABLED : DSS_NORMAL) ) ) + SAL_WARN("vcl", "DrawStateW failed: " << WindowsErrorString(GetLastError())); + + if( pSalMenuItem->mAccelText.getLength() ) + { + SIZE strSizeA; + aStr = pSalMenuItem->mAccelText; + GetTextExtentPoint32W( pDI->hDC, o3tl::toW(aStr.getStr()), + aStr.getLength(), &strSizeA ); + TEXTMETRICW tm; + GetTextMetricsW( pDI->hDC, &tm ); + + // position the accelerator string to the right but leave space for the + // (potential) submenu arrow (tm.tmMaxCharWidth) + if(!DrawStateW( pDI->hDC, nullptr, nullptr, + reinterpret_cast<LPARAM>(aStr.getStr()), + WPARAM(0), aRect.right-strSizeA.cx-tm.tmMaxCharWidth, aRect.top + (lineHeight - strSizeA.cy)/2, 0, 0, + DST_TEXT | (fDisabled && !fSelected ? DSS_DISABLED : DSS_NORMAL) ) ) + SAL_WARN("vcl", "DrawStateW failed: " << WindowsErrorString(GetLastError())); + } + + // Restore the original font and colors. + DeleteObject( SelectObject( pDI->hDC, hbrOld ) ); + DeleteObject( SelectObject( pDI->hDC, hfntOld) ); + SetTextColor(pDI->hDC, clrPrevText); + SetBkColor(pDI->hDC, clrPrevBkgnd); + } + return nRet; +} + +static bool ImplHandleMenuActivate( HWND hWnd, WPARAM wParam, LPARAM ) +{ + // Menu activation + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( !pFrame ) + return false; + + HMENU hMenu = reinterpret_cast<HMENU>(wParam); + // WORD nPos = LOWORD (lParam); + // bool bWindowMenu = (bool) HIWORD(lParam); + + // 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 = pFrame->CallCallback( SalEvent::MenuActivate, &aMenuEvt ); + if( nRet ) + nRet = pFrame->CallCallback( SalEvent::MenuDeactivate, &aMenuEvt ); + if( nRet ) + pFrame->mLastActivatedhMenu = hMenu; + + return nRet; +} + +static bool ImplHandleMenuSelect( HWND hWnd, WPARAM wParam, LPARAM lParam ) +{ + // Menu selection + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( !pFrame ) + return false; + + WORD nId = LOWORD(wParam); // menu item or submenu index + WORD nFlags = HIWORD(wParam); + HMENU hMenu = reinterpret_cast<HMENU>(lParam); + + // check if we have to process the message + if( !GetSalData()->IsKnownMenuHandle( hMenu ) ) + return false; + + bool bByPosition = false; + if( nFlags & MF_POPUP ) + bByPosition = true; + + 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; + + nRet = pFrame->CallCallback( SalEvent::MenuActivate, &aMenuEvt ); + if( nRet ) + nRet = pFrame->CallCallback( SalEvent::MenuDeactivate, &aMenuEvt ); + if( nRet ) + pFrame->mLastActivatedhMenu = hMenu; + } + + 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); + } + + SalMenuEvent aMenuEvt; + aMenuEvt.mnId = nId; + WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( hMenu, nId, false ); + if( pSalMenuItem ) + aMenuEvt.mpMenu = pSalMenuItem->mpMenu; + else + aMenuEvt.mpMenu = nullptr; + + nRet = pFrame->CallCallback( SalEvent::MenuHighlight, &aMenuEvt ); + } + + return nRet; +} + +static bool ImplHandleCommand( HWND hWnd, WPARAM wParam, LPARAM ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( !pFrame ) + return false; + + bool nRet = false; + if( !HIWORD(wParam) ) + { + // Menu command + WORD nId = LOWORD(wParam); + if( nId ) // zero for separators + { + SalMenuEvent aMenuEvt; + aMenuEvt.mnId = nId; + WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( pFrame->mSelectedhMenu, nId, false ); + if( pSalMenuItem ) + aMenuEvt.mpMenu = pSalMenuItem->mpMenu; + else + aMenuEvt.mpMenu = nullptr; + + nRet = pFrame->CallCallback( SalEvent::MenuCommand, &aMenuEvt ); + } + } + return nRet; +} + +static bool ImplHandleSysCommand( HWND hWnd, WPARAM wParam, LPARAM lParam ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( !pFrame ) + return false; + + WPARAM nCommand = wParam & 0xFFF0; + + if ( pFrame->mbFullScreen ) + { + bool bMaximize = IsZoomed( pFrame->mhWnd ); + bool bMinimize = IsIconic( pFrame->mhWnd ); + if ( (nCommand == SC_SIZE) || + (!bMinimize && (nCommand == SC_MOVE)) || + (!bMaximize && (nCommand == SC_MAXIMIZE)) || + (bMaximize && (nCommand == SC_RESTORE)) ) + { + return true; + } + } + + if ( nCommand == SC_MOVE ) + { + WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer ); + if ( pTimer ) + pTimer->SetForceRealTimer( true ); + } + + if ( nCommand == SC_KEYMENU ) + { + // do not process SC_KEYMENU if we have a native menu + // Windows should handle this + if( GetMenu( hWnd ) ) + return false; + + // 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 ) + return false; + + // 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) ) + return true; + + 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<unsigned char>(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; + + SalKeyEvent aKeyEvt; + if ( (cKeyCode >= 48) && (cKeyCode <= 57) ) + aKeyEvt.mnCode = KEY_0+(cKeyCode-48); + else + aKeyEvt.mnCode = KEY_A+(cKeyCode-97); + aKeyEvt.mnCode |= nModCode; + aKeyEvt.mnCharCode = cKeyCode; + aKeyEvt.mnRepeat = 0; + bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt ); + pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt ); + return nRet; + } + } + } + } + + return false; +} + +static void ImplHandleInputLangChange( HWND hWnd, WPARAM, LPARAM lParam ) +{ + ImplSalYieldMutexAcquireWithWait(); + + // check if we support IME + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + + if ( !pFrame ) + return; + + if ( pFrame->mbIME && pFrame->mhDefIMEContext ) + { + HKL hKL = reinterpret_cast<HKL>(lParam); + UINT nImeProps = ImmGetProperty( hKL, IGP_PROPERTY ); + + pFrame->mbSpezIME = (nImeProps & IME_PROP_SPECIAL_UI) != 0; + pFrame->mbAtCursorIME = (nImeProps & IME_PROP_AT_CARET) != 0; + pFrame->mbHandleIME = !pFrame->mbSpezIME; + } + + // trigger input language and codepage update + UINT nLang = pFrame->mnInputLang; + ImplUpdateInputLang( pFrame ); + + // notify change + if( nLang != pFrame->mnInputLang ) + pFrame->CallCallback( SalEvent::InputLanguageChange, nullptr ); + + // reinit spec. keys + GetSalData()->initKeyCodeMap(); + + ImplSalYieldMutexRelease(); +} + +static void ImplUpdateIMECursorPos( WinSalFrame* pFrame, HIMC hIMC ) +{ + COMPOSITIONFORM aForm = {}; + + // 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 ); + } +} + +static bool ImplHandleIMEStartComposition( HWND hWnd ) +{ + bool bDef = true; + + ImplSalYieldMutexAcquireWithWait(); + + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( pFrame ) + { + HIMC hIMC = ImmGetContext( hWnd ); + if ( hIMC ) + { + ImplUpdateIMECursorPos( pFrame, hIMC ); + ImmReleaseContext( hWnd, hIMC ); + } + + if ( pFrame->mbHandleIME ) + { + if ( pFrame->mbAtCursorIME ) + bDef = false; + } + } + + ImplSalYieldMutexRelease(); + + return bDef; +} + +static bool ImplHandleIMECompositionInput( WinSalFrame* pFrame, + HIMC hIMC, LPARAM lParam ) +{ + bool bDef = true; + + // Init Event + SalExtTextInputEvent aEvt; + aEvt.mpTextAttr = nullptr; + aEvt.mnCursorPos = 0; + aEvt.mnCursorFlags = 0; + + // If we get a result string, then we handle this input + if ( lParam & GCS_RESULTSTR ) + { + bDef = false; + + LONG nTextLen = ImmGetCompositionStringW( hIMC, GCS_RESULTSTR, nullptr, 0 ) / sizeof( WCHAR ); + if ( nTextLen >= 0 ) + { + auto pTextBuf = std::make_unique<WCHAR[]>(nTextLen); + ImmGetCompositionStringW( hIMC, GCS_RESULTSTR, pTextBuf.get(), nTextLen*sizeof( WCHAR ) ); + aEvt.maText = OUString( o3tl::toU(pTextBuf.get()), static_cast<sal_Int32>(nTextLen) ); + } + + aEvt.mnCursorPos = aEvt.maText.getLength(); + pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt ); + pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr ); + ImplUpdateIMECursorPos( pFrame, hIMC ); + } + + // 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; + + std::unique_ptr<ExtTextInputAttr[]> pSalAttrAry; + LONG nTextLen = ImmGetCompositionStringW( hIMC, GCS_COMPSTR, nullptr, 0 ) / sizeof( WCHAR ); + if ( nTextLen > 0 ) + { + { + auto pTextBuf = std::make_unique<WCHAR[]>(nTextLen); + ImmGetCompositionStringW( hIMC, GCS_COMPSTR, pTextBuf.get(), nTextLen*sizeof( WCHAR ) ); + aEvt.maText = OUString( o3tl::toU(pTextBuf.get()), static_cast<sal_Int32>(nTextLen) ); + } + + std::unique_ptr<BYTE[]> pAttrBuf; + LONG nAttrLen = ImmGetCompositionStringW( hIMC, GCS_COMPATTR, nullptr, 0 ); + if ( nAttrLen > 0 ) + { + pAttrBuf.reset(new BYTE[nAttrLen]); + ImmGetCompositionStringW( hIMC, GCS_COMPATTR, pAttrBuf.get(), nAttrLen ); + } + + if ( pAttrBuf ) + { + sal_Int32 nTextLen2 = aEvt.maText.getLength(); + pSalAttrAry.reset(new ExtTextInputAttr[nTextLen2]); + memset( pSalAttrAry.get(), 0, nTextLen2*sizeof( sal_uInt16 ) ); + for( sal_Int32 i = 0; (i < nTextLen2) && (i < nAttrLen); i++ ) + { + BYTE nWinAttr = pAttrBuf.get()[i]; + ExtTextInputAttr nSalAttr; + if ( nWinAttr == ATTR_TARGET_CONVERTED ) + { + nSalAttr = ExtTextInputAttr::BoldUnderline; + aEvt.mnCursorFlags |= EXTTEXTINPUT_CURSOR_INVISIBLE; + } + else if ( nWinAttr == ATTR_CONVERTED ) + nSalAttr = ExtTextInputAttr::DashDotUnderline; + else if ( nWinAttr == ATTR_TARGET_NOTCONVERTED ) + nSalAttr = ExtTextInputAttr::Highlight; + else if ( nWinAttr == ATTR_INPUT_ERROR ) + nSalAttr = ExtTextInputAttr::RedText | ExtTextInputAttr::DottedUnderline; + else /* ( nWinAttr == ATTR_INPUT ) */ + nSalAttr = ExtTextInputAttr::DottedUnderline; + pSalAttrAry[i] = nSalAttr; + } + + aEvt.mpTextAttr = pSalAttrAry.get(); + } + } + + // 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; + + pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt ); + } + ImplUpdateIMECursorPos( pFrame, hIMC ); + } + } + + return !bDef; +} + +static bool ImplHandleIMEComposition( HWND hWnd, LPARAM lParam ) +{ + bool bDef = true; + ImplSalYieldMutexAcquireWithWait(); + + 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 ); + } + + if ( pFrame && pFrame->mbHandleIME ) + { + if ( !lParam ) + { + SalExtTextInputEvent aEvt; + aEvt.mpTextAttr = nullptr; + aEvt.mnCursorPos = 0; + aEvt.mnCursorFlags = 0; + pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt ); + pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr ); + } + else if ( lParam & (GCS_RESULTSTR | GCS_COMPSTR | GCS_COMPATTR | GCS_CURSORPOS) ) + { + HIMC hIMC = ImmGetContext( hWnd ); + if ( hIMC ) + { + if ( ImplHandleIMECompositionInput( pFrame, hIMC, lParam ) ) + bDef = false; + + ImmReleaseContext( hWnd, hIMC ); + } + } + } + + ImplSalYieldMutexRelease(); + return bDef; +} + +static bool ImplHandleIMEEndComposition( HWND hWnd ) +{ + bool bDef = true; + + ImplSalYieldMutexAcquireWithWait(); + + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( pFrame && pFrame->mbHandleIME ) + { + if ( pFrame->mbAtCursorIME ) + { + pFrame->mbCandidateMode = false; + bDef = false; + } + } + + ImplSalYieldMutexRelease(); + + return bDef; +} + +static bool 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: + return false; + } + + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + vcl::Window *pWindow = pFrame ? pFrame->GetWindow() : nullptr; + + if( pWindow ) + { + const Point aPoint; + CommandMediaData aMediaData(nCommand); + CommandEvent aCEvt( aPoint, CommandEventId::Media, false, &aMediaData ); + NotifyEvent aNCmdEvt( MouseNotifyEvent::COMMAND, pWindow, &aCEvt ); + + if ( !ImplCallPreNotify( aNCmdEvt ) ) + { + pWindow->Command( aCEvt ); + nRet = 1; + return !aMediaData.GetPassThroughToOS(); + } + } + + return false; +} + +static void ImplHandleIMENotify( HWND hWnd, WPARAM wParam ) +{ + if ( wParam == WPARAM(IMN_OPENCANDIDATE) ) + { + ImplSalYieldMutexAcquireWithWait(); + + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( pFrame && pFrame->mbHandleIME && + pFrame->mbAtCursorIME ) + { + // we want to hide the cursor + pFrame->mbCandidateMode = true; + ImplHandleIMEComposition( hWnd, GCS_CURSORPOS ); + + HWND hWnd2 = pFrame->mhWnd; + HIMC hIMC = ImmGetContext( hWnd2 ); + if ( hIMC ) + { + LONG nBufLen = ImmGetCompositionStringW( hIMC, GCS_COMPSTR, nullptr, 0 ); + if ( nBufLen >= 1 ) + { + SalExtTextInputPosEvent aPosEvt; + pFrame->CallCallback( SalEvent::ExtTextInputPos, &aPosEvt ); + + // Vertical !!! + CANDIDATEFORM aForm; + aForm.dwIndex = 0; + aForm.dwStyle = CFS_EXCLUDE; + aForm.ptCurrentPos.x = aPosEvt.mnX; + aForm.ptCurrentPos.y = aPosEvt.mnY+1; + aForm.rcArea.left = aPosEvt.mnX; + aForm.rcArea.top = aPosEvt.mnY; + aForm.rcArea.right = aForm.rcArea.left+aPosEvt.mnExtWidth+1; + aForm.rcArea.bottom = aForm.rcArea.top+aPosEvt.mnHeight+1; + ImmSetCandidateWindow( hIMC, &aForm ); + } + + ImmReleaseContext( hWnd2, hIMC ); + } + } + + ImplSalYieldMutexRelease(); + } + else if ( wParam == WPARAM(IMN_CLOSECANDIDATE) ) + { + ImplSalYieldMutexAcquireWithWait(); + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + if ( pFrame ) + pFrame->mbCandidateMode = false; + ImplSalYieldMutexRelease(); + } +} + +static bool +ImplHandleGetObject(HWND hWnd, LPARAM lParam, WPARAM wParam, LRESULT & nRet) +{ + if (!Application::GetSettings().GetMiscSettings().GetEnableATToolSupport()) + { + // IA2 should be enabled automatically + AllSettings aSettings = Application::GetSettings(); + MiscSettings aMisc = aSettings.GetMiscSettings(); + aMisc.SetEnableATToolSupport(true); + // The above is enough, since aMisc changes the same shared ImplMiscData as used in global + // settings, so no need to call aSettings.SetMiscSettings and Application::SetSettings + + if (!Application::GetSettings().GetMiscSettings().GetEnableATToolSupport()) + return false; // locked down somehow ? + } + + ImplSVData* pSVData = ImplGetSVData(); + + // Make sure to launch Accessibility only the following criteria are satisfied + // to avoid RFT interrupts regular accessibility processing + if ( !pSVData->mxAccessBridge.is() ) + { + if( !InitAccessBridge() ) + return false; + } + + uno::Reference< accessibility::XMSAAService > xMSAA( pSVData->mxAccessBridge, uno::UNO_QUERY ); + if ( xMSAA.is() ) + { + sal_Int32 lParam32 = static_cast<sal_Int32>(lParam); + sal_uInt32 wParam32 = static_cast<sal_uInt32>(wParam); + + // mhOnSetTitleWnd not set to reasonable value anywhere... + if ( lParam32 == OBJID_CLIENT ) + { + nRet = xMSAA->getAccObjectPtr( + reinterpret_cast<sal_Int64>(hWnd), lParam32, wParam32); + if (nRet != 0) + return true; + } + } + return false; +} + +static LRESULT ImplHandleIMEReconvertString( HWND hWnd, LPARAM lParam ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + LPRECONVERTSTRING pReconvertString = reinterpret_cast<LPRECONVERTSTRING>(lParam); + LRESULT nRet = 0; + SalSurroundingTextRequestEvent aEvt; + aEvt.maText.clear(); + aEvt.mnStart = aEvt.mnEnd = 0; + + UINT nImeProps = ImmGetProperty( GetKeyboardLayout( 0 ), IGP_SETCOMPSTR ); + if( (nImeProps & SCS_CAP_SETRECONVERTSTRING) == 0 ) + { + // This IME does not support reconversion. + return 0; + } + + if( !pReconvertString ) + { + // The first call for reconversion. + pFrame->CallCallback( SalEvent::StartReconversion, nullptr ); + + // Retrieve the surrounding text from the focused control. + pFrame->CallCallback( SalEvent::SurroundingTextRequest, &aEvt ); + + if( aEvt.maText.isEmpty()) + { + return 0; + } + + nRet = sizeof(RECONVERTSTRING) + (aEvt.maText.getLength() + 1) * sizeof(WCHAR); + } + else + { + // The second call for reconversion. + + // Retrieve the surrounding text from the focused control. + pFrame->CallCallback( SalEvent::SurroundingTextRequest, &aEvt ); + nRet = sizeof(RECONVERTSTRING) + (aEvt.maText.getLength() + 1) * sizeof(WCHAR); + + pReconvertString->dwStrOffset = sizeof(RECONVERTSTRING); + pReconvertString->dwStrLen = aEvt.maText.getLength(); + pReconvertString->dwCompStrOffset = aEvt.mnStart * sizeof(WCHAR); + pReconvertString->dwCompStrLen = aEvt.mnEnd - aEvt.mnStart; + pReconvertString->dwTargetStrOffset = pReconvertString->dwCompStrOffset; + pReconvertString->dwTargetStrLen = pReconvertString->dwCompStrLen; + + memcpy( pReconvertString + 1, aEvt.maText.getStr(), (aEvt.maText.getLength() + 1) * sizeof(WCHAR) ); + } + + // just return the required size of buffer to reconvert. + return nRet; +} + +static LRESULT ImplHandleIMEConfirmReconvertString( HWND hWnd, LPARAM lParam ) +{ + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + LPRECONVERTSTRING pReconvertString = reinterpret_cast<LPRECONVERTSTRING>(lParam); + SalSurroundingTextRequestEvent aEvt; + aEvt.maText.clear(); + aEvt.mnStart = aEvt.mnEnd = 0; + + pFrame->CallCallback( SalEvent::SurroundingTextRequest, &aEvt ); + + sal_uLong nTmpStart = pReconvertString->dwCompStrOffset / sizeof(WCHAR); + sal_uLong nTmpEnd = nTmpStart + pReconvertString->dwCompStrLen; + + if( nTmpStart != aEvt.mnStart || nTmpEnd != aEvt.mnEnd ) + { + SalSurroundingTextSelectionChangeEvent aSelEvt { nTmpStart, nTmpEnd }; + pFrame->CallCallback( SalEvent::SurroundingTextSelectionChange, &aSelEvt ); + } + + return TRUE; +} + +static LRESULT ImplHandleIMEQueryCharPosition( HWND hWnd, LPARAM lParam ) { + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + PIMECHARPOSITION pQueryCharPosition = reinterpret_cast<PIMECHARPOSITION>(lParam); + if ( pQueryCharPosition->dwSize < sizeof(IMECHARPOSITION) ) + return FALSE; + + SalQueryCharPositionEvent aEvt; + aEvt.mbValid = false; + aEvt.mnCharPos = pQueryCharPosition->dwCharPos; + + pFrame->CallCallback( SalEvent::QueryCharPosition, &aEvt ); + + if ( !aEvt.mbValid ) + return FALSE; + + 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.mnCursorBoundX + aEvt.mnCursorBoundWidth; + pQueryCharPosition->pt.y = aEvt.mnCursorBoundY; + pQueryCharPosition->cLineHeight = aEvt.mnCursorBoundWidth; + } + 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.mnCursorBoundX; + pQueryCharPosition->pt.y = aEvt.mnCursorBoundY; + pQueryCharPosition->cLineHeight = aEvt.mnCursorBoundHeight; + } + + // 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; + + return TRUE; +} + +void SalTestMouseLeave() +{ + SalData* pSalData = GetSalData(); + + 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; + + if ( pSalData->mhWantLeaveMsg != WindowFromPoint( aPt ) ) + SendMessageW( pSalData->mhWantLeaveMsg, SAL_MSG_MOUSELEAVE, 0, MAKELPARAM( aPt.x, aPt.y ) ); + } +} + +static bool ImplSalWheelMousePos( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam , + LRESULT& rResult ) +{ + POINT aPt; + POINT aScreenPt; + aScreenPt.x = static_cast<short>(LOWORD( lParam )); + aScreenPt.y = static_cast<short>(HIWORD( lParam )); + // find child window that is at this position + HWND hChildWnd; + HWND hWheelWnd = hWnd; + do + { + hChildWnd = hWheelWnd; + aPt = aScreenPt; + ScreenToClient( hChildWnd, &aPt ); + hWheelWnd = ChildWindowFromPointEx( hChildWnd, aPt, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT ); + } + while ( hWheelWnd && (hWheelWnd != hChildWnd) ); + if ( hWheelWnd && (hWheelWnd != hWnd) && + (hWheelWnd != ::GetFocus()) && IsWindowEnabled( hWheelWnd ) ) + { + rResult = SendMessageW( hWheelWnd, nMsg, wParam, lParam ); + return false; + } + + return true; +} + +static LRESULT CALLBACK SalFrameWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, bool& rDef ) +{ + LRESULT nRet = 0; + static bool bInWheelMsg = false; + static bool bInQueryEnd = false; + + SAL_INFO("vcl.gdi.wndproc", "SalFrameWndProc(nMsg=" << nMsg << ", wParam=" << wParam << ", lParam=" << lParam << ")"); + + // 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 ); + + 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; + } + return 0; + } + + 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; + + if ( WM_USER_SYSTEM_WINDOW_ACTIVATED == nMsg ) + { + ImplHideSplash(); + return 0; + } + + switch( nMsg ) + { + 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_MOUSEACTIVATE: + if ( LOWORD( lParam ) == HTCLIENT ) + { + ImplSalYieldMutexAcquireWithWait(); + nRet = LRESULT(ImplHandleMouseActivateMsg( hWnd )); + ImplSalYieldMutexRelease(); + if ( nRet ) + { + nRet = MA_NOACTIVATE; + rDef = false; + } + } + 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 here is a + // connected (?) window that we have to notify. + if ( rDef ) + rDef = ImplSalWheelMousePos( hWnd, nMsg, wParam, lParam, nRet ); + bInWheelMsg = false; + } + break; + + case WM_COMMAND: + ImplSalYieldMutexAcquireWithWait(); + rDef = !ImplHandleCommand( hWnd, wParam, lParam ); + ImplSalYieldMutexRelease(); + break; + + case WM_INITMENUPOPUP: + ImplSalYieldMutexAcquireWithWait(); + rDef = !ImplHandleMenuActivate( hWnd, wParam, lParam ); + ImplSalYieldMutexRelease(); + break; + + case WM_MENUSELECT: + ImplSalYieldMutexAcquireWithWait(); + rDef = !ImplHandleMenuSelect( hWnd, wParam, lParam ); + ImplSalYieldMutexRelease(); + break; + + case WM_SYSCOMMAND: + ImplSalYieldMutexAcquireWithWait(); + nRet = LRESULT(ImplHandleSysCommand( hWnd, wParam, lParam )); + ImplSalYieldMutexRelease(); + if ( nRet ) + rDef = false; + break; + + case WM_MENUCHAR: + nRet = ImplMenuChar( hWnd, wParam, lParam ); + if( nRet ) + rDef = false; + break; + + case WM_MEASUREITEM: + nRet = ImplMeasureItem(hWnd, wParam, lParam); + if( nRet ) + rDef = false; + break; + + case WM_DRAWITEM: + nRet = ImplDrawItem(hWnd, wParam, lParam); + if( nRet ) + rDef = false; + break; + + case WM_MOVE: + case SAL_MSG_POSTMOVE: + ImplHandleMoveMsg( hWnd ); + rDef = false; + break; + case WM_SIZE: + ImplHandleSizeMsg( hWnd, wParam, lParam ); + rDef = false; + break; + case SAL_MSG_POSTCALLSIZE: + ImplCallSizeHdl( hWnd ); + rDef = false; + break; + + case WM_GETMINMAXINFO: + if ( ImplHandleMinMax( hWnd, lParam ) ) + rDef = false; + break; + + case WM_ERASEBKGND: + nRet = 1; + rDef = false; + break; + case WM_PAINT: + ImplHandlePaintMsg( hWnd ); + rDef = false; + break; + case SAL_MSG_POSTPAINT: + ImplHandlePostPaintMsg( hWnd, reinterpret_cast<RECT*>(wParam) ); + rDef = false; + break; + + case SAL_MSG_FORCEPALETTE: + ImplHandleForcePalette( hWnd ); + rDef = false; + break; + + case WM_QUERYNEWPALETTE: + case SAL_MSG_POSTQUERYNEWPAL: + nRet = ImplHandlePalette( true, hWnd, nMsg, wParam, lParam, rDef ); + break; + + case WM_ACTIVATE: + // Getting activated, we also want to set our palette. + // We do this in Activate, so that other external child windows + // can overwrite our palette. Thus our palette is set only once + // and not recursively, as at all other places it is set only as + // the background palette. + if ( LOWORD( wParam ) != WA_INACTIVE ) + SendMessageW( hWnd, SAL_MSG_FORCEPALETTE, 0, 0 ); + break; + + case WM_ENABLE: + // #95133# a system dialog is opened/closed, using our app window as parent + { + WinSalFrame* pFrame = GetWindowPtr( hWnd ); + vcl::Window *pWin = nullptr; + if( pFrame ) + pWin = pFrame->GetWindow(); + + if( !wParam ) + { + pSVData->maAppData.mnModalMode++; + + ImplHideSplash(); + if( pWin ) + { + pWin->EnableInput( false, nullptr ); + pWin->IncModalCount(); // #106303# support frame based modal count + } + } + else + { + ImplGetSVData()->maAppData.mnModalMode--; + if( pWin ) + { + pWin->EnableInput( true, nullptr ); + pWin->DecModalCount(); // #106303# support frame based modal count + } + } + } + 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. + + if ( nRet ) + { + SolarMutexGuard aGuard; + while ( Application::Reschedule( true ) ); + } + } + else + { + ImplSalYieldMutexAcquireWithWait(); + ImplSalYieldMutexRelease(); + rDef = true; + } + break; + + 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: + 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: + ImplSalYieldMutexAcquireWithWait(); + if ( ImplHandleGetObject( hWnd, lParam, wParam, nRet ) ) + { + rDef = false; + } + ImplSalYieldMutexRelease(); + break; + + case WM_APPCOMMAND: + if( ImplHandleAppCommand( hWnd, lParam, nRet ) ) + { + rDef = false; + } + break; + case WM_IME_REQUEST: + if ( static_cast<sal_uIntPtr>(wParam) == IMR_RECONVERTSTRING ) + { + nRet = ImplHandleIMEReconvertString( hWnd, lParam ); + rDef = false; + } + else if( static_cast<sal_uIntPtr>(wParam) == IMR_CONFIRMRECONVERTSTRING ) + { + nRet = ImplHandleIMEConfirmReconvertString( hWnd, lParam ); + rDef = false; + } + else if ( static_cast<sal_uIntPtr>(wParam) == IMR_QUERYCHARPOSITION ) + { + if ( ImplSalYieldMutexTryToAcquire() ) + { + nRet = ImplHandleIMEQueryCharPosition( hWnd, lParam ); + ImplSalYieldMutexRelease(); + } + else + nRet = FALSE; + rDef = false; + } + break; + } + + return nRet; +} + +LRESULT CALLBACK SalFrameWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) +{ + bool bDef = true; + LRESULT nRet = 0; + __try + { + nRet = SalFrameWndProc( hWnd, nMsg, wParam, lParam, bDef ); + } + __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation())) + { + } + + if ( bDef ) + nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam ); + return nRet; +} + +bool ImplHandleGlobalMsg( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, LRESULT& rlResult ) +{ + // handle all messages concerning all frames so they get processed only once + // Must work for Unicode and none Unicode + bool bResult = false; + if ( (nMsg == WM_PALETTECHANGED) || (nMsg == SAL_MSG_POSTPALCHANGED) ) + { + bResult = true; + rlResult = ImplHandlePalette( false, hWnd, nMsg, wParam, lParam, bResult ); + } + else if( nMsg == WM_DISPLAYCHANGE ) + { + WinSalSystem* pSys = static_cast<WinSalSystem*>(ImplGetSalSystem()); + if( pSys ) + pSys->clearMonitors(); + bResult = (pSys != nullptr); + } + return bResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |