summaryrefslogtreecommitdiffstats
path: root/vcl/win
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/win')
-rw-r--r--vcl/win/app/saldata.cxx78
-rw-r--r--vcl/win/app/salinfo.cxx176
-rw-r--r--vcl/win/app/salinst.cxx1095
-rw-r--r--vcl/win/app/salshl.cxx112
-rw-r--r--vcl/win/app/saltimer.cxx199
-rw-r--r--vcl/win/gdi/DWriteTextRenderer.cxx413
-rw-r--r--vcl/win/gdi/gdiimpl.cxx2710
-rw-r--r--vcl/win/gdi/gdiimpl.hxx250
-rw-r--r--vcl/win/gdi/salbmp.cxx1067
-rw-r--r--vcl/win/gdi/salfont.cxx1766
-rw-r--r--vcl/win/gdi/salgdi.cxx1058
-rw-r--r--vcl/win/gdi/salgdi2.cxx248
-rw-r--r--vcl/win/gdi/salgdi_gdiplus.cxx98
-rw-r--r--vcl/win/gdi/salnativewidgets-luna.cxx1559
-rw-r--r--vcl/win/gdi/salprn.cxx1629
-rw-r--r--vcl/win/gdi/salvd.cxx226
-rw-r--r--vcl/win/gdi/winlayout.cxx635
-rw-r--r--vcl/win/src/50.bmpbin0 -> 94 bytes
-rw-r--r--vcl/win/src/50.pngbin0 -> 125 bytes
-rw-r--r--vcl/win/src/ase.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/asn.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/asne.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/asns.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/asnswe.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/asnw.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/ass.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/asse.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/assw.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/asw.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/aswe.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/chain.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/chainnot.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/chart.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/copydata.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/copydlnk.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/copyf.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/copyf2.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/copyflnk.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/crook.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/crop.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/darc.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/dbezier.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/dcapt.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/dcirccut.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/dconnect.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/dellipse.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/detectiv.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/dfree.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/dline.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/dpie.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/dpolygon.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/drect.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/dtext.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/fill.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/hshear.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/linkdata.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/linkf.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/magnify.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/mirror.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/movebw.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/movedata.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/movedlnk.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/movef.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/movef2.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/moveflnk.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/movept.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/nullptr.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/pivotcol.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/pivotdel.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/pivotfld.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/pivotrow.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/rotate.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/salsrc.rc89
-rw-r--r--vcl/win/src/sd.icobin0 -> 3310 bytes
-rw-r--r--vcl/win/src/tblsele.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/tblsels.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/tblselse.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/tblselsw.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/tblselw.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/vshear.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/vtext.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/wshide.curbin0 -> 326 bytes
-rw-r--r--vcl/win/src/wsshow.curbin0 -> 326 bytes
-rw-r--r--vcl/win/window/keynames.cxx224
-rw-r--r--vcl/win/window/salframe.cxx5925
-rw-r--r--vcl/win/window/salmenu.cxx355
-rw-r--r--vcl/win/window/salobj.cxx727
87 files changed, 20639 insertions, 0 deletions
diff --git a/vcl/win/app/saldata.cxx b/vcl/win/app/saldata.cxx
new file mode 100644
index 000000000..31fa66163
--- /dev/null
+++ b/vcl/win/app/saldata.cxx
@@ -0,0 +1,78 @@
+/* -*- 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 <svsys.h>
+#include <rtl/tencinfo.h>
+#include <vcl/svapp.hxx>
+
+#include <win/saldata.hxx>
+
+rtl_TextEncoding ImplSalGetSystemEncoding()
+{
+ static const UINT nOldAnsiCodePage = 0;
+ static rtl_TextEncoding eEncoding = RTL_TEXTENCODING_MS_1252;
+
+ UINT nAnsiCodePage = GetACP();
+ if ( nAnsiCodePage != nOldAnsiCodePage )
+ {
+ rtl_TextEncoding nEnc
+ = rtl_getTextEncodingFromWindowsCodePage(nAnsiCodePage);
+ if (nEnc != RTL_TEXTENCODING_DONTKNOW)
+ eEncoding = nEnc;
+ }
+
+ return eEncoding;
+}
+
+OUString ImplSalGetUniString(const char* pStr, sal_Int32 const nLen)
+{
+ return OUString( pStr, (-1 == nLen) ? strlen(pStr) : nLen,
+ ImplSalGetSystemEncoding(),
+ RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
+ RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
+ RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT );
+}
+
+int ImplSalWICompareAscii( const wchar_t* pStr1, const char* pStr2 )
+{
+ int nRet;
+ char c2;
+ do
+ {
+ // change to LowerCase if the char is between 'A' and 'Z'
+ wchar_t c1 = *pStr1;
+ c2 = *pStr2;
+ if ( (c1 >= 65) && (c1 <= 90) )
+ c1 += 32;
+ if ( (c2 >= 65) && (c2 <= 90) )
+ c2 += 32;
+ nRet = static_cast<sal_Int32>(c1)- static_cast<sal_Int32>(static_cast<unsigned char>(c2));
+ if ( nRet != 0 )
+ break;
+
+ pStr1++;
+ pStr2++;
+ }
+ while ( c2 );
+
+ return nRet;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/app/salinfo.cxx b/vcl/win/app/salinfo.cxx
new file mode 100644
index 000000000..2787797a6
--- /dev/null
+++ b/vcl/win/app/salinfo.cxx
@@ -0,0 +1,176 @@
+/* -*- 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 <svsys.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <sal/log.hxx>
+#include <vcl/window.hxx>
+
+#include <win/salsys.h>
+#include <win/salframe.h>
+#include <win/salinst.h>
+#include <win/saldata.hxx>
+
+#include <svdata.hxx>
+
+#include <unordered_map>
+
+SalSystem* WinSalInstance::CreateSalSystem()
+{
+ return new WinSalSystem();
+}
+
+WinSalSystem::~WinSalSystem()
+{
+}
+
+static BOOL CALLBACK ImplEnumMonitorProc( HMONITOR hMonitor,
+ HDC hDC,
+ LPRECT lpRect,
+ LPARAM dwData )
+{
+ WinSalSystem* pSys = reinterpret_cast<WinSalSystem*>(dwData);
+ return pSys->handleMonitorCallback( reinterpret_cast<sal_IntPtr>(hMonitor),
+ reinterpret_cast<sal_IntPtr>(hDC),
+ reinterpret_cast<sal_IntPtr>(lpRect) );
+}
+
+bool WinSalSystem::handleMonitorCallback( sal_IntPtr hMonitor, sal_IntPtr, sal_IntPtr )
+{
+ MONITORINFOEXW aInfo;
+ aInfo.cbSize = sizeof( aInfo );
+ if( GetMonitorInfoW( reinterpret_cast<HMONITOR>(hMonitor), &aInfo ) )
+ {
+ aInfo.szDevice[CCHDEVICENAME-1] = 0;
+ OUString aDeviceName( o3tl::toU(aInfo.szDevice) );
+ std::map< OUString, unsigned int >::const_iterator it =
+ m_aDeviceNameToMonitor.find( aDeviceName );
+ if( it != m_aDeviceNameToMonitor.end() )
+ {
+ DisplayMonitor& rMon( m_aMonitors[ it->second ] );
+ rMon.m_aArea = tools::Rectangle( Point( aInfo.rcMonitor.left,
+ aInfo.rcMonitor.top ),
+ Size( aInfo.rcMonitor.right - aInfo.rcMonitor.left,
+ aInfo.rcMonitor.bottom - aInfo.rcMonitor.top ) );
+ if( (aInfo.dwFlags & MONITORINFOF_PRIMARY) != 0 )
+ m_nPrimary = it->second;
+ }
+ }
+ return true;
+}
+
+void WinSalSystem::clearMonitors()
+{
+ m_aMonitors.clear();
+ m_nPrimary = 0;
+}
+
+bool WinSalSystem::initMonitors()
+{
+ if( m_aMonitors.size() > 0 )
+ return true;
+
+ int nMonitors = GetSystemMetrics( SM_CMONITORS );
+ if( nMonitors == 1 )
+ {
+ int w = GetSystemMetrics( SM_CXSCREEN );
+ int h = GetSystemMetrics( SM_CYSCREEN );
+ m_aMonitors.push_back( DisplayMonitor( OUString(),
+ tools::Rectangle( Point(), Size( w, h ) ) ) );
+ m_aDeviceNameToMonitor[ OUString() ] = 0;
+ m_nPrimary = 0;
+ }
+ else
+ {
+ DISPLAY_DEVICEW aDev;
+ aDev.cb = sizeof( aDev );
+ DWORD nDevice = 0;
+ std::unordered_map< OUString, int > aDeviceStringCount;
+ while( EnumDisplayDevicesW( nullptr, nDevice++, &aDev, 0 ) )
+ {
+ if( (aDev.StateFlags & DISPLAY_DEVICE_ACTIVE)
+ && !(aDev.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) ) // sort out non/disabled monitors
+ {
+ aDev.DeviceName[31] = 0;
+ aDev.DeviceString[127] = 0;
+ OUString aDeviceName( o3tl::toU(aDev.DeviceName) );
+ OUString aDeviceString( o3tl::toU(aDev.DeviceString) );
+ aDeviceStringCount[ aDeviceString ]++;
+ m_aDeviceNameToMonitor[ aDeviceName ] = m_aMonitors.size();
+ m_aMonitors.push_back( DisplayMonitor( aDeviceString,
+ tools::Rectangle() ) );
+ }
+ }
+ HDC aDesktopRC = GetDC( nullptr );
+ EnumDisplayMonitors( aDesktopRC, nullptr, ImplEnumMonitorProc, reinterpret_cast<LPARAM>(this) );
+
+ // append monitor numbers to name strings
+ std::unordered_map< OUString, int > aDevCount( aDeviceStringCount );
+ unsigned int nMonitorCount = m_aMonitors.size();
+ for( unsigned int i = 0; i < nMonitorCount; i++ )
+ {
+ const OUString& rDev( m_aMonitors[i].m_aName );
+ if( aDeviceStringCount[ rDev ] > 1 )
+ {
+ int nInstance = aDeviceStringCount[ rDev ] - (-- aDevCount[ rDev ] );
+ m_aMonitors[ i ].m_aName = rDev + " (" + OUString::number( nInstance ) + ")";
+ }
+ }
+ }
+
+ return m_aMonitors.size() > 0;
+}
+
+unsigned int WinSalSystem::GetDisplayScreenCount()
+{
+ initMonitors();
+ return m_aMonitors.size();
+}
+
+unsigned int WinSalSystem::GetDisplayBuiltInScreen()
+{
+ initMonitors();
+ return m_nPrimary;
+}
+
+tools::Rectangle WinSalSystem::GetDisplayScreenPosSizePixel( unsigned int nScreen )
+{
+ initMonitors();
+ if (nScreen >= m_aMonitors.size())
+ {
+ SAL_WARN("vcl", "Requested screen size/pos for screen #"
+ << nScreen << ", but only " << m_aMonitors.size() << " screens found.");
+ assert(false);
+ return tools::Rectangle();
+ }
+ return m_aMonitors[nScreen].m_aArea;
+}
+
+int WinSalSystem::ShowNativeMessageBox(const OUString& rTitle, const OUString& rMessage)
+{
+ ImplHideSplash();
+ return MessageBoxW(
+ nullptr,
+ o3tl::toW(rMessage.getStr()),
+ o3tl::toW(rTitle.getStr()),
+ MB_TASKMODAL | MB_SETFOREGROUND | MB_ICONWARNING | MB_DEFBUTTON1);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/app/salinst.cxx b/vcl/win/app/salinst.cxx
new file mode 100644
index 000000000..5ebb22226
--- /dev/null
+++ b/vcl/win/app/salinst.cxx
@@ -0,0 +1,1095 @@
+/* -*- 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 <string.h>
+#include <svsys.h>
+#include <process.h>
+
+#include <osl/conditn.hxx>
+#include <osl/file.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/time.hxx>
+#include <comphelper/solarmutex.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <vcl/inputtypes.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <vcl/opengl/OpenGLContext.hxx>
+#include <vcl/timer.hxx>
+#include <vclpluginapi.h>
+
+#include <opengl/salbmp.hxx>
+#include <opengl/win/gdiimpl.hxx>
+#include <win/wincomp.hxx>
+#include <win/salids.hrc>
+#include <win/saldata.hxx>
+#include <win/salinst.h>
+#include <win/salframe.h>
+#include <win/salobj.h>
+#include <win/saltimer.h>
+#include <win/salbmp.h>
+#include <win/winlayout.hxx>
+
+#include <config_features.h>
+#include <vcl/skia/SkiaHelper.hxx>
+#if HAVE_FEATURE_SKIA
+#include <config_skia.h>
+#include <skia/salbmp.hxx>
+#include <skia/win/gdiimpl.hxx>
+#endif
+
+#include <salsys.hxx>
+
+#include <desktop/crashreport.hxx>
+
+#if defined _MSC_VER
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#endif
+
+#include <prewin.h>
+
+#include <gdiplus.h>
+#include <shlobj.h>
+
+#include <postwin.h>
+
+void SalAbort( const OUString& rErrorText, bool )
+{
+ ImplFreeSalGDI();
+
+ if ( rErrorText.isEmpty() )
+ {
+ // make sure crash reporter is triggered
+ RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
+ FatalAppExitW( 0, L"Application Error" );
+ }
+ else
+ {
+ CrashReporter::addKeyValue("AbortMessage", rErrorText, CrashReporter::Write);
+ // make sure crash reporter is triggered
+ RaiseException( 0, EXCEPTION_NONCONTINUABLE, 0, nullptr );
+ FatalAppExitW( 0, o3tl::toW(rErrorText.getStr()) );
+ }
+}
+
+static LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam );
+
+class SalYieldMutex : public comphelper::SolarMutex
+{
+public: // for ImplSalYield() and ImplSalYieldMutexAcquireWithWait()
+ osl::Condition m_condition; /// for MsgWaitForMultipleObjects()
+
+protected:
+ virtual void doAcquire( sal_uInt32 nLockCount ) override;
+ virtual sal_uInt32 doRelease( bool bUnlockAll ) override;
+
+ static void BeforeReleaseHandler();
+
+public:
+ explicit SalYieldMutex();
+
+ virtual bool IsCurrentThread() const override;
+ virtual bool tryToAcquire() override;
+};
+
+SalYieldMutex::SalYieldMutex()
+{
+ SetBeforeReleaseHandler( &SalYieldMutex::BeforeReleaseHandler );
+}
+
+void SalYieldMutex::BeforeReleaseHandler()
+{
+ OpenGLContext::prepareForYield();
+
+ if ( GetSalData()->mnAppThreadId != GetCurrentThreadId() )
+ {
+ // If we don't call these message, the Output from the
+ // Java clients doesn't come in the right order
+ GdiFlush();
+ }
+}
+
+/// note: while VCL is fully up and running (other threads started and
+/// before shutdown), the main thread must acquire SolarMutex only via
+/// this function to avoid deadlock
+void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst && pInst->IsMainThread() )
+ {
+ if ( pInst->m_nNoYieldLock )
+ return;
+ // tdf#96887 If this is the main thread, then we must wait for two things:
+ // - the yield mutex being unlocked
+ // - SendMessage() being triggered
+ // This can nicely be done using MsgWaitForMultipleObjects. The 2nd one is
+ // needed because if we don't reschedule, then we create deadlocks if a
+ // Window's create/destroy is called via SendMessage() from another thread.
+ // Have a look at the osl_waitCondition implementation for more info.
+ do {
+ // reset condition *before* acquiring!
+ m_condition.reset();
+ if (m_aMutex.tryToAcquire())
+ break;
+ // wait for SalYieldMutex::release() to set the condition
+ osl::Condition::Result res = m_condition.wait();
+ assert(osl::Condition::Result::result_ok == res);
+ }
+ while ( true );
+ }
+ else
+ m_aMutex.acquire();
+ ++m_nCount;
+ --nLockCount;
+
+ comphelper::SolarMutex::doAcquire( nLockCount );
+}
+
+sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll )
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst && pInst->m_nNoYieldLock && pInst->IsMainThread() )
+ return 1;
+
+ sal_uInt32 nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
+ // wake up ImplSalYieldMutexAcquireWithWait() after release
+ if ( 0 == m_nCount )
+ m_condition.set();
+ return nCount;
+}
+
+bool SalYieldMutex::tryToAcquire()
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst )
+ {
+ if ( pInst->m_nNoYieldLock && pInst->IsMainThread() )
+ return true;
+ else
+ return comphelper::SolarMutex::tryToAcquire();
+ }
+ else
+ return false;
+}
+
+void ImplSalYieldMutexAcquireWithWait( sal_uInt32 nCount )
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst )
+ pInst->GetYieldMutex()->acquire( nCount );
+}
+
+bool ImplSalYieldMutexTryToAcquire()
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ return pInst && pInst->GetYieldMutex()->tryToAcquire();
+}
+
+void ImplSalYieldMutexRelease()
+{
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst )
+ {
+ GdiFlush();
+ pInst->GetYieldMutex()->release();
+ }
+}
+
+bool SalYieldMutex::IsCurrentThread() const
+{
+ if ( !GetSalData()->mpInstance->m_nNoYieldLock )
+ return SolarMutex::IsCurrentThread();
+ else
+ return GetSalData()->mpInstance->IsMainThread();
+}
+
+void SalData::initKeyCodeMap()
+{
+ UINT nKey;
+ #define initKey( a, b )\
+ nKey = LOWORD( VkKeyScanW( a ) );\
+ if( nKey < 0xffff )\
+ maVKMap[ nKey ] = b;
+
+ maVKMap.clear();
+
+ initKey( L'+', KEY_ADD );
+ initKey( L'-', KEY_SUBTRACT );
+ initKey( L'*', KEY_MULTIPLY );
+ initKey( L'/', KEY_DIVIDE );
+ initKey( L'.', KEY_POINT );
+ initKey( L',', KEY_COMMA );
+ initKey( L'<', KEY_LESS );
+ initKey( L'>', KEY_GREATER );
+ initKey( L'=', KEY_EQUAL );
+ initKey( L'~', KEY_TILDE );
+ initKey( L'`', KEY_QUOTELEFT );
+ initKey( L'[', KEY_BRACKETLEFT );
+ initKey( L']', KEY_BRACKETRIGHT );
+ initKey( L';', KEY_SEMICOLON );
+ initKey( L'\'', KEY_QUOTERIGHT );
+}
+
+// SalData
+
+SalData::SalData()
+{
+ mhInst = nullptr; // default instance handle
+ mnCmdShow = 0; // default frame show style
+ mhDitherPal = nullptr; // dither palette
+ mhDitherDIB = nullptr; // dither memory handle
+ mpDitherDIB = nullptr; // dither memory
+ mpDitherDIBData = nullptr; // beginning of DIB data
+ mpDitherDiff = nullptr; // Dither mapping table
+ mpDitherLow = nullptr; // Dither mapping table
+ mpDitherHigh = nullptr; // Dither mapping table
+ mhSalObjMsgHook = nullptr; // hook to get interesting msg for SalObject
+ mhWantLeaveMsg = nullptr; // window handle, that want a MOUSELEAVE message
+ mpMouseLeaveTimer = nullptr; // Timer for MouseLeave Test
+ mpInstance = nullptr; // pointer of first instance
+ mpFirstFrame = nullptr; // pointer of first frame
+ mpFirstObject = nullptr; // pointer of first object window
+ mpFirstVD = nullptr; // first VirDev
+ mpFirstPrinter = nullptr; // first printing printer
+ mpHDCCache = nullptr; // Cache for three DC's
+ mh50Bmp = nullptr; // 50% Bitmap
+ mh50Brush = nullptr; // 50% Brush
+ int i;
+ for(i=0; i<MAX_STOCKPEN; i++)
+ {
+ maStockPenColorAry[i] = 0;
+ mhStockPenAry[i] = nullptr;
+ }
+ for(i=0; i<MAX_STOCKBRUSH; i++)
+ {
+ maStockBrushColorAry[i] = 0;
+ mhStockBrushAry[i] = nullptr;
+ }
+ mnStockPenCount = 0; // count of static pens
+ mnStockBrushCount = 0; // count of static brushes
+ mnSalObjWantKeyEvt = 0; // KeyEvent for the SalObj hook
+ mnCacheDCInUse = 0; // count of CacheDC in use
+ mbObjClassInit = false; // is SALOBJECTCLASS initialised
+ mbInPalChange = false; // is in WM_QUERYNEWPALETTE
+ mnAppThreadId = 0; // Id from Application-Thread
+ mbScrSvrEnabled = FALSE; // ScreenSaver enabled
+ mpFirstIcon = nullptr; // icon cache, points to first icon, NULL if none
+ mpSharedTempFontItem = nullptr;
+ mpOtherTempFontItem = nullptr;
+ mbThemeChanged = false; // true if visual theme was changed: throw away theme handles
+ mbThemeMenuSupport = false;
+
+ // init with NULL
+ gdiplusToken = 0;
+
+ initKeyCodeMap();
+
+ SetSalData( this );
+ initNWF();
+
+ CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); // put main thread in Single Threaded Apartment (STA)
+ static Gdiplus::GdiplusStartupInput gdiplusStartupInput;
+ Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
+}
+
+SalData::~SalData()
+{
+ deInitNWF();
+ SetSalData( nullptr );
+
+ CoUninitialize();
+
+ if (gdiplusToken)
+ Gdiplus::GdiplusShutdown(gdiplusToken);
+}
+
+extern "C" {
+VCLPLUG_WIN_PUBLIC SalInstance* create_SalInstance()
+{
+ SalData* pSalData = new SalData();
+
+ STARTUPINFOW aSI;
+ aSI.cb = sizeof( aSI );
+ GetStartupInfoW( &aSI );
+ pSalData->mhInst = GetModuleHandleW( nullptr );
+ pSalData->mnCmdShow = aSI.wShowWindow;
+
+ pSalData->mnAppThreadId = GetCurrentThreadId();
+
+ // register frame class
+ WNDCLASSEXW aWndClassEx;
+ aWndClassEx.cbSize = sizeof( aWndClassEx );
+ aWndClassEx.style = CS_OWNDC;
+ aWndClassEx.lpfnWndProc = SalFrameWndProcW;
+ aWndClassEx.cbClsExtra = 0;
+ aWndClassEx.cbWndExtra = SAL_FRAME_WNDEXTRA;
+ aWndClassEx.hInstance = pSalData->mhInst;
+ aWndClassEx.hCursor = nullptr;
+ aWndClassEx.hbrBackground = nullptr;
+ aWndClassEx.lpszMenuName = nullptr;
+ aWndClassEx.lpszClassName = SAL_FRAME_CLASSNAMEW;
+ ImplLoadSalIcon( SAL_RESID_ICON_DEFAULT, aWndClassEx.hIcon, aWndClassEx.hIconSm );
+ if ( !RegisterClassExW( &aWndClassEx ) )
+ return nullptr;
+
+ aWndClassEx.hIcon = nullptr;
+ aWndClassEx.hIconSm = nullptr;
+ aWndClassEx.style |= CS_SAVEBITS;
+ aWndClassEx.lpszClassName = SAL_SUBFRAME_CLASSNAMEW;
+ if ( !RegisterClassExW( &aWndClassEx ) )
+ return nullptr;
+
+ // shadow effect for popups on XP
+ aWndClassEx.style |= CS_DROPSHADOW;
+ aWndClassEx.lpszClassName = SAL_TMPSUBFRAME_CLASSNAMEW;
+ if ( !RegisterClassExW( &aWndClassEx ) )
+ return nullptr;
+
+ aWndClassEx.style = 0;
+ aWndClassEx.lpfnWndProc = SalComWndProcW;
+ aWndClassEx.cbWndExtra = 0;
+ aWndClassEx.lpszClassName = SAL_COM_CLASSNAMEW;
+ if ( !RegisterClassExW( &aWndClassEx ) )
+ return nullptr;
+
+ HWND hComWnd = CreateWindowExW( WS_EX_TOOLWINDOW, SAL_COM_CLASSNAMEW,
+ L"", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr,
+ pSalData->mhInst, nullptr );
+ if ( !hComWnd )
+ return nullptr;
+
+ WinSalInstance* pInst = new WinSalInstance;
+
+ // init instance (only one instance in this version !!!)
+ pSalData->mpInstance = pInst;
+ pInst->mhInst = pSalData->mhInst;
+ pInst->mhComWnd = hComWnd;
+
+ // init static GDI Data
+ ImplInitSalGDI();
+
+ return pInst;
+}
+}
+
+WinSalInstance::WinSalInstance()
+ : SalInstance(std::make_unique<SalYieldMutex>())
+ , mhInst( nullptr )
+ , mhComWnd( nullptr )
+ , m_nNoYieldLock( 0 )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ pSVData->maAppData.mxToolkitName = OUString("win");
+#if HAVE_FEATURE_SKIA
+ WinSkiaSalGraphicsImpl::prepareSkia();
+#endif
+}
+
+WinSalInstance::~WinSalInstance()
+{
+ ImplFreeSalGDI();
+ DestroyWindow( mhComWnd );
+#if HAVE_FEATURE_SKIA
+ SkiaHelper::cleanup();
+#endif
+}
+
+static LRESULT ImplSalDispatchMessage( const MSG* pMsg )
+{
+ SalData* pSalData = GetSalData();
+ if ( pSalData->mpFirstObject && ImplSalPreDispatchMsg( pMsg ) )
+ return 0;
+ LRESULT lResult = DispatchMessageW( pMsg );
+ if ( pSalData->mpFirstObject )
+ ImplSalPostDispatchMsg( pMsg );
+ return lResult;
+}
+
+bool ImplSalYield( bool bWait, bool bHandleAllCurrentEvents )
+{
+ static sal_uInt32 nLastTicks = 0;
+ MSG aMsg;
+ bool bWasMsg = false, bOneEvent = false, bWasTimeoutMsg = false;
+ ImplSVData *const pSVData = ImplGetSVData();
+ WinSalTimer* pTimer = static_cast<WinSalTimer*>( pSVData->maSchedCtx.mpSalTimer );
+ const bool bNoYieldLock = (GetSalData()->mpInstance->m_nNoYieldLock > 0);
+
+ assert( !bNoYieldLock );
+ if ( bNoYieldLock )
+ return false;
+
+ sal_uInt32 nCurTicks = 0;
+ if ( bHandleAllCurrentEvents )
+ nCurTicks = GetTickCount();
+
+ bool bHadNewerEvent = false;
+ do
+ {
+ bOneEvent = PeekMessageW( &aMsg, nullptr, 0, 0, PM_REMOVE );
+ if ( bOneEvent )
+ {
+ bWasMsg = true;
+ TranslateMessage( &aMsg );
+ LRESULT nRet = ImplSalDispatchMessage( &aMsg );
+
+ if ( !bWasTimeoutMsg )
+ bWasTimeoutMsg = (SAL_MSG_TIMER_CALLBACK == aMsg.message)
+ && static_cast<bool>( nRet );
+
+ if ( bHandleAllCurrentEvents
+ && !bHadNewerEvent && aMsg.time > nCurTicks
+ && (nLastTicks <= nCurTicks || aMsg.time < nLastTicks) )
+ bHadNewerEvent = true;
+ bOneEvent = !bHadNewerEvent;
+ }
+
+ if ( !(bHandleAllCurrentEvents && bOneEvent) )
+ break;
+ }
+ while( true );
+
+ // 0ms timeouts are handled out-of-bounds to prevent busy-locking the
+ // event loop with timeout messages.
+ // We ensure we never handle more than one timeout per call.
+ // This way we'll always process a normal system message.
+ if ( !bWasTimeoutMsg && pTimer && pTimer->IsDirectTimeout() )
+ {
+ pTimer->ImplHandleElapsedTimer();
+ bWasMsg = true;
+ }
+
+ if ( bHandleAllCurrentEvents )
+ nLastTicks = nCurTicks;
+
+ if ( bWait && !bWasMsg )
+ {
+ if ( GetMessageW( &aMsg, nullptr, 0, 0 ) )
+ {
+ bWasMsg = true;
+ TranslateMessage( &aMsg );
+ ImplSalDispatchMessage( &aMsg );
+ }
+ }
+
+ // we're back in the main loop after resize or move
+ if ( pTimer )
+ pTimer->SetForceRealTimer( false );
+
+ return bWasMsg;
+}
+
+bool WinSalInstance::IsMainThread() const
+{
+ const SalData* pSalData = GetSalData();
+ return pSalData->mnAppThreadId == GetCurrentThreadId();
+}
+
+bool WinSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
+{
+ bool bDidWork = false;
+ SolarMutexReleaser aReleaser;
+ if ( !IsMainThread() )
+ {
+ bDidWork = SendMessageW( mhComWnd, SAL_MSG_THREADYIELD,
+ WPARAM(false), static_cast<LPARAM>(bHandleAllCurrentEvents) );
+ if ( !bDidWork && bWait )
+ {
+ maWaitingYieldCond.reset();
+ maWaitingYieldCond.wait();
+ bDidWork = true;
+ }
+ }
+ else
+ {
+ bDidWork = ImplSalYield( bWait, bHandleAllCurrentEvents );
+ if ( bDidWork )
+ maWaitingYieldCond.set();
+ }
+
+ return bDidWork;
+}
+
+#define CASE_NOYIELDLOCK( salmsg, function ) \
+ case salmsg: \
+ if (bIsOtherThreadMessage) \
+ { \
+ ++pInst->m_nNoYieldLock; \
+ function; \
+ --pInst->m_nNoYieldLock; \
+ } \
+ else \
+ { \
+ DBG_TESTSOLARMUTEX(); \
+ function; \
+ } \
+ break;
+
+#define CASE_NOYIELDLOCK_RESULT( salmsg, function ) \
+ case salmsg: \
+ if (bIsOtherThreadMessage) \
+ { \
+ ++pInst->m_nNoYieldLock; \
+ nRet = reinterpret_cast<LRESULT>( function ); \
+ --pInst->m_nNoYieldLock; \
+ } \
+ else \
+ { \
+ DBG_TESTSOLARMUTEX(); \
+ nRet = reinterpret_cast<LRESULT>( function ); \
+ } \
+ break;
+
+LRESULT CALLBACK SalComWndProc( HWND, UINT nMsg, WPARAM wParam, LPARAM lParam, bool& rDef )
+{
+ const bool bIsOtherThreadMessage = InSendMessage();
+ LRESULT nRet = 0;
+ WinSalInstance *pInst = GetSalData()->mpInstance;
+ WinSalTimer *const pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
+
+ SAL_INFO("vcl.gdi.wndproc", "SalComWndProc(nMsg=" << nMsg << ", wParam=" << wParam
+ << ", lParam=" << lParam << "); inSendMsg: " << bIsOtherThreadMessage);
+
+ switch ( nMsg )
+ {
+ case SAL_MSG_THREADYIELD:
+ assert( !static_cast<bool>(wParam) );
+ nRet = static_cast<LRESULT>(ImplSalYield(
+ false, static_cast<bool>( lParam ) ));
+ break;
+
+ case SAL_MSG_STARTTIMER:
+ {
+ auto const nParam = static_cast<sal_uInt64>( lParam );
+ sal_uInt64 nTime = tools::Time::GetSystemTicks();
+ if ( nTime < nParam )
+ nTime = nParam - nTime;
+ else
+ nTime = 0;
+ assert( pTimer != nullptr );
+ pTimer->ImplStart( nTime );
+ break;
+ }
+
+ case SAL_MSG_STOPTIMER:
+ assert( pTimer != nullptr );
+ pTimer->ImplStop();
+ break;
+
+ CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEFRAME, ImplSalCreateFrame( GetSalData()->mpInstance,
+ reinterpret_cast<HWND>(lParam), static_cast<SalFrameStyleFlags>(wParam)) )
+ CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATEHWND, ImplSalReCreateHWND(
+ reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), false) )
+ CASE_NOYIELDLOCK_RESULT( SAL_MSG_RECREATECHILDHWND, ImplSalReCreateHWND(
+ reinterpret_cast<HWND>(wParam), reinterpret_cast<HWND>(lParam), true) )
+ CASE_NOYIELDLOCK( SAL_MSG_DESTROYFRAME, delete reinterpret_cast<SalFrame*>(lParam) )
+
+ case SAL_MSG_DESTROYHWND:
+ // We only destroy the native window here. We do NOT destroy the SalFrame contained
+ // in the structure (GetWindowPtr()).
+ if (DestroyWindow(reinterpret_cast<HWND>(lParam)) == 0)
+ {
+ OSL_FAIL("DestroyWindow failed!");
+ // Failure: We remove the SalFrame from the window structure. So we avoid that
+ // the window structure may contain an invalid pointer, once the SalFrame is deleted.
+ SetWindowPtr(reinterpret_cast<HWND>(lParam), nullptr);
+ }
+ break;
+
+ CASE_NOYIELDLOCK_RESULT( SAL_MSG_CREATEOBJECT, ImplSalCreateObject(
+ GetSalData()->mpInstance, reinterpret_cast<WinSalFrame*>(lParam)) )
+ CASE_NOYIELDLOCK( SAL_MSG_DESTROYOBJECT, delete reinterpret_cast<SalObject*>(lParam) )
+ CASE_NOYIELDLOCK_RESULT( SAL_MSG_GETCACHEDDC, GetDCEx(
+ reinterpret_cast<HWND>(wParam), nullptr, DCX_CACHE) )
+ CASE_NOYIELDLOCK( SAL_MSG_RELEASEDC, ReleaseDC(
+ reinterpret_cast<HWND>(wParam), reinterpret_cast<HDC>(lParam)) )
+
+ case SAL_MSG_TIMER_CALLBACK:
+ assert( pTimer != nullptr );
+ pTimer->ImplHandleTimerEvent( wParam );
+ break;
+
+ case WM_TIMER:
+ assert( pTimer != nullptr );
+ pTimer->ImplHandle_WM_TIMER( wParam );
+ break;
+
+ case SAL_MSG_FORCE_REAL_TIMER:
+ assert(pTimer != nullptr);
+ pTimer->SetForceRealTimer(true);
+ break;
+
+ case SAL_MSG_DUMMY:
+ break;
+
+ default:
+ rDef = true;
+ break;
+ }
+
+ return nRet;
+}
+
+#undef CASE_NOYIELDLOCK
+#undef CASE_NOYIELDLOCK_RESULT
+
+LRESULT CALLBACK SalComWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
+{
+ bool bDef = false;
+ LRESULT nRet = 0;
+ __try
+ {
+ nRet = SalComWndProc( hWnd, nMsg, wParam, lParam, bDef );
+ }
+ __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
+ {
+ }
+ if ( bDef )
+ {
+ if ( !ImplHandleGlobalMsg( hWnd, nMsg, wParam, lParam, nRet ) )
+ nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam );
+ }
+ return nRet;
+}
+
+namespace {
+
+struct MsgRange
+{
+ UINT nStart;
+ UINT nEnd;
+};
+
+}
+
+static std::vector<MsgRange> GetOtherRanges( VclInputFlags nType )
+{
+ assert( nType != VCL_INPUT_ANY );
+
+ // this array must be kept sorted!
+ const UINT nExcludeMsgIds[] =
+ {
+ 0,
+
+ WM_MOVE, // 3
+ WM_SIZE, // 5
+ WM_PAINT, // 15
+ WM_KEYDOWN, // 256
+ WM_TIMER, // 275
+
+ WM_MOUSEFIRST, // 512
+ 513,
+ 514,
+ 515,
+ 516,
+ 517,
+ 518,
+ 519,
+ 520,
+ WM_MOUSELAST, // 521
+
+ SAL_MSG_POSTMOVE, // WM_USER+136
+ SAL_MSG_POSTCALLSIZE, // WM_USER+137
+
+ SAL_MSG_TIMER_CALLBACK, // WM_USER+162
+
+ UINT_MAX
+ };
+ const unsigned MAX_EXCL = SAL_N_ELEMENTS( nExcludeMsgIds );
+
+ bool aExcludeMsgList[ MAX_EXCL ] = { false, };
+ std::vector<MsgRange> aResult;
+
+ // set the excluded values
+ if ( !(nType & VclInputFlags::MOUSE) )
+ {
+ for ( unsigned i = 0; nExcludeMsgIds[ 6 + i ] <= WM_MOUSELAST; ++i )
+ aExcludeMsgList[ 6 + i ] = true;
+ }
+
+ if ( !(nType & VclInputFlags::KEYBOARD) )
+ aExcludeMsgList[ 4 ] = true;
+
+ if ( !(nType & VclInputFlags::PAINT) )
+ {
+ aExcludeMsgList[ 1 ] = true;
+ aExcludeMsgList[ 2 ] = true;
+ aExcludeMsgList[ 3 ] = true;
+ aExcludeMsgList[ 16 ] = true;
+ aExcludeMsgList[ 17 ] = true;
+ }
+
+ if ( !(nType & VclInputFlags::TIMER) )
+ {
+ aExcludeMsgList[ 5 ] = true;
+ aExcludeMsgList[ 18 ] = true;
+ }
+
+ // build the message ranges to check
+ MsgRange aRange = { 0, 0 };
+ bool doEnd = true;
+ for ( unsigned i = 1; i < MAX_EXCL; ++i )
+ {
+ if ( aExcludeMsgList[ i ] )
+ {
+ if ( !doEnd )
+ {
+ if ( nExcludeMsgIds[ i ] == aRange.nStart )
+ ++aRange.nStart;
+ else
+ doEnd = true;
+ }
+ if ( doEnd )
+ {
+ aRange.nEnd = nExcludeMsgIds[ i ] - 1;
+ aResult.push_back( aRange );
+ doEnd = false;
+ aRange.nStart = aRange.nEnd + 2;
+ }
+ }
+ }
+
+ if ( aRange.nStart != UINT_MAX )
+ {
+ aRange.nEnd = UINT_MAX;
+ aResult.push_back( aRange );
+ }
+
+ return aResult;
+}
+
+bool WinSalInstance::AnyInput( VclInputFlags nType )
+{
+ MSG aMsg;
+
+ if ( nType & VclInputFlags::TIMER )
+ {
+ const WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
+ if ( pTimer && pTimer->HasTimerElapsed() )
+ return true;
+ }
+
+ if ( (nType & VCL_INPUT_ANY) == VCL_INPUT_ANY )
+ {
+ // revert bugfix for #108919# which never reported timeouts when called from the timer handler
+ // which made the application completely unresponsive during background formatting
+ if ( PeekMessageW( &aMsg, nullptr, 0, 0, PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+ }
+ else
+ {
+ const bool bCheck_KEYBOARD (nType & VclInputFlags::KEYBOARD);
+ const bool bCheck_OTHER (nType & VclInputFlags::OTHER);
+
+ // If there is a modifier key event, it counts as OTHER
+ // Previously we were simply ignoring these events...
+ if ( bCheck_KEYBOARD || bCheck_OTHER )
+ {
+ if ( PeekMessageW( &aMsg, nullptr, WM_KEYDOWN, WM_KEYDOWN,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ {
+ const bool bIsModifier = ( (aMsg.wParam == VK_SHIFT) ||
+ (aMsg.wParam == VK_CONTROL) || (aMsg.wParam == VK_MENU) );
+ if ( bCheck_KEYBOARD && !bIsModifier )
+ return true;
+ if ( bCheck_OTHER && bIsModifier )
+ return true;
+ }
+ }
+
+ // Other checks for all messages not excluded.
+ // The less we exclude, the less ranges have to be checked!
+ if ( bCheck_OTHER )
+ {
+ // TIMER and KEYBOARD are already handled, so always exclude them!
+ VclInputFlags nOtherType = nType &
+ ~VclInputFlags(VclInputFlags::KEYBOARD | VclInputFlags::TIMER);
+
+ std::vector<MsgRange> aMsgRangeList( GetOtherRanges( nOtherType ) );
+ for ( MsgRange const & aRange : aMsgRangeList )
+ if ( PeekMessageW( &aMsg, nullptr, aRange.nStart,
+ aRange.nEnd, PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+
+ // MOUSE and PAINT already handled, so skip further checks
+ return false;
+ }
+
+ if ( nType & VclInputFlags::MOUSE )
+ {
+ if ( PeekMessageW( &aMsg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+ }
+
+ if ( nType & VclInputFlags::PAINT )
+ {
+ if ( PeekMessageW( &aMsg, nullptr, WM_PAINT, WM_PAINT,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+
+ if ( PeekMessageW( &aMsg, nullptr, WM_SIZE, WM_SIZE,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+
+ if ( PeekMessageW( &aMsg, nullptr, SAL_MSG_POSTCALLSIZE, SAL_MSG_POSTCALLSIZE,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+
+ if ( PeekMessageW( &aMsg, nullptr, WM_MOVE, WM_MOVE,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+
+ if ( PeekMessageW( &aMsg, nullptr, SAL_MSG_POSTMOVE, SAL_MSG_POSTMOVE,
+ PM_NOREMOVE | PM_NOYIELD ) )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+SalFrame* WinSalInstance::CreateChildFrame( SystemParentData* pSystemParentData, SalFrameStyleFlags nSalFrameStyle )
+{
+ // to switch to Main-Thread
+ return reinterpret_cast<SalFrame*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEFRAME, static_cast<WPARAM>(nSalFrameStyle), reinterpret_cast<LPARAM>(pSystemParentData->hWnd) )));
+}
+
+SalFrame* WinSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle )
+{
+ // to switch to Main-Thread
+ HWND hWndParent;
+ if ( pParent )
+ hWndParent = static_cast<WinSalFrame*>(pParent)->mhWnd;
+ else
+ hWndParent = nullptr;
+ return reinterpret_cast<SalFrame*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEFRAME, static_cast<WPARAM>(nSalFrameStyle), reinterpret_cast<LPARAM>(hWndParent) )));
+}
+
+void WinSalInstance::DestroyFrame( SalFrame* pFrame )
+{
+ OpenGLContext::prepareForYield();
+ SendMessageW( mhComWnd, SAL_MSG_DESTROYFRAME, 0, reinterpret_cast<LPARAM>(pFrame) );
+}
+
+SalObject* WinSalInstance::CreateObject( SalFrame* pParent,
+ SystemWindowData* /*pWindowData*/, // SystemWindowData meaningless on Windows
+ bool /*bShow*/ )
+{
+ // to switch to Main-Thread
+ return reinterpret_cast<SalObject*>(static_cast<sal_IntPtr>(SendMessageW( mhComWnd, SAL_MSG_CREATEOBJECT, 0, reinterpret_cast<LPARAM>(static_cast<WinSalFrame*>(pParent)) )));
+}
+
+void WinSalInstance::DestroyObject( SalObject* pObject )
+{
+ SendMessageW( mhComWnd, SAL_MSG_DESTROYOBJECT, 0, reinterpret_cast<LPARAM>(pObject) );
+}
+
+OUString WinSalInstance::GetConnectionIdentifier()
+{
+ return OUString();
+}
+
+/** Add a file to the system shells recent document list if there is any.
+ This function may have no effect under Unix because there is no
+ standard API among the different desktop managers.
+
+ @param aFileUrl
+ The file url of the document.
+*/
+void WinSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& rDocumentService)
+{
+ if (Application::IsHeadlessModeEnabled())
+ return;
+
+ OUString system_path;
+ osl::FileBase::RC rc = osl::FileBase::getSystemPathFromFileURL(rFileUrl, system_path);
+
+ OSL_ENSURE(osl::FileBase::E_None == rc, "Invalid file url");
+
+ if (osl::FileBase::E_None == rc)
+ {
+ IShellItem* pShellItem = nullptr;
+
+ HRESULT hr = SHCreateItemFromParsingName(o3tl::toW(system_path.getStr()), nullptr, IID_PPV_ARGS(&pShellItem));
+
+ if ( SUCCEEDED(hr) && pShellItem )
+ {
+ OUString sApplicationName;
+
+ if ( rDocumentService == "com.sun.star.text.TextDocument" ||
+ rDocumentService == "com.sun.star.text.GlobalDocument" ||
+ rDocumentService == "com.sun.star.text.WebDocument" ||
+ rDocumentService == "com.sun.star.xforms.XMLFormDocument" )
+ sApplicationName = "Writer";
+ else if ( rDocumentService == "com.sun.star.sheet.SpreadsheetDocument" ||
+ rDocumentService == "com.sun.star.chart2.ChartDocument" )
+ sApplicationName = "Calc";
+ else if ( rDocumentService == "com.sun.star.presentation.PresentationDocument" )
+ sApplicationName = "Impress";
+ else if ( rDocumentService == "com.sun.star.drawing.DrawingDocument" )
+ sApplicationName = "Draw";
+ else if ( rDocumentService == "com.sun.star.formula.FormulaProperties" )
+ sApplicationName = "Math";
+ else if ( rDocumentService == "com.sun.star.sdb.DatabaseDocument" ||
+ rDocumentService == "com.sun.star.sdb.OfficeDatabaseDocument" ||
+ rDocumentService == "com.sun.star.sdb.RelationDesign" ||
+ rDocumentService == "com.sun.star.sdb.QueryDesign" ||
+ rDocumentService == "com.sun.star.sdb.TableDesign" ||
+ rDocumentService == "com.sun.star.sdb.DataSourceBrowser" )
+ sApplicationName = "Base";
+
+ if ( !sApplicationName.isEmpty() )
+ {
+ OUString sApplicationID("TheDocumentFoundation.LibreOffice." + sApplicationName);
+
+ SHARDAPPIDINFO info;
+ info.psi = pShellItem;
+ info.pszAppID = o3tl::toW(sApplicationID.getStr());
+
+ SHAddToRecentDocs ( SHARD_APPIDINFO, &info );
+ return;
+ }
+ }
+ // For whatever reason, we could not use the SHARD_APPIDINFO semantics
+ SHAddToRecentDocs(SHARD_PATHW, system_path.getStr());
+ }
+}
+
+SalTimer* WinSalInstance::CreateSalTimer()
+{
+ return new WinSalTimer();
+}
+
+std::shared_ptr<SalBitmap> WinSalInstance::CreateSalBitmap()
+{
+#if HAVE_FEATURE_SKIA
+ if (SkiaHelper::isVCLSkiaEnabled())
+ return std::make_shared<SkiaSalBitmap>();
+ else
+#endif
+ if (OpenGLHelper::isVCLOpenGLEnabled())
+ return std::make_shared<OpenGLSalBitmap>();
+ else
+ return std::make_shared<WinSalBitmap>();
+}
+
+int WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(int, LPEXCEPTION_POINTERS pExceptionInfo)
+{
+ // Decide if an exception is a c++ (mostly UNO) exception or a process violation.
+ // Depending on this information we pass process violations directly to our signal handler ...
+ // and c++ (UNO) exceptions are sended to the following code on the current stack.
+ // Problem behind: user32.dll sometime consumes exceptions/process violations .-)
+ // see also #112221#
+
+ static const DWORD EXCEPTION_MSC_CPP_EXCEPTION = 0xE06D7363;
+
+ if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_MSC_CPP_EXCEPTION)
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ return UnhandledExceptionFilter( pExceptionInfo );
+}
+
+typedef LONG NTSTATUS;
+typedef NTSTATUS(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
+constexpr NTSTATUS STATUS_SUCCESS = 0x00000000;
+
+OUString WinSalInstance::getOSVersion()
+{
+ OUStringBuffer aVer(50); // capacity for string like "Windows 6.1 Service Pack 1 build 7601"
+ aVer.append("Windows ");
+ // GetVersion(Ex) and VersionHelpers (based on VerifyVersionInfo) API are
+ // subject to manifest-based behavior since Windows 8.1, so give wrong results.
+ // Another approach would be to use NetWkstaGetInfo, but that has some small
+ // reported delays (some milliseconds), and might get slower in domains with
+ // poor network connections.
+ // So go with a solution described at https://msdn.microsoft.com/en-us/library/ms724429
+ bool bHaveVerFromKernel32 = false;
+ if (HMODULE h_kernel32 = GetModuleHandleW(L"kernel32.dll"))
+ {
+ wchar_t szPath[MAX_PATH];
+ DWORD dwCount = GetModuleFileNameW(h_kernel32, szPath, SAL_N_ELEMENTS(szPath));
+ if (dwCount != 0 && dwCount < SAL_N_ELEMENTS(szPath))
+ {
+ dwCount = GetFileVersionInfoSizeW(szPath, nullptr);
+ if (dwCount != 0)
+ {
+ std::unique_ptr<char[]> ver(new char[dwCount]);
+ if (GetFileVersionInfoW(szPath, 0, dwCount, ver.get()) != FALSE)
+ {
+ void* pBlock = nullptr;
+ UINT dwBlockSz = 0;
+ if (VerQueryValueW(ver.get(), L"\\", &pBlock, &dwBlockSz) != FALSE && dwBlockSz >= sizeof(VS_FIXEDFILEINFO))
+ {
+ VS_FIXEDFILEINFO* vi1 = static_cast<VS_FIXEDFILEINFO*>(pBlock);
+ aVer.append(OUString::number(HIWORD(vi1->dwProductVersionMS)) + "."
+ + OUString::number(LOWORD(vi1->dwProductVersionMS)));
+ bHaveVerFromKernel32 = true;
+ }
+ }
+ }
+ }
+ }
+ // Now use RtlGetVersion (which is not subject to deprecation for GetVersion(Ex) API)
+ // to get build number and SP info
+ bool bHaveVerFromRtlGetVersion = false;
+ if (HMODULE h_ntdll = GetModuleHandleW(L"ntdll.dll"))
+ {
+ if (auto RtlGetVersion
+ = reinterpret_cast<RtlGetVersion_t>(GetProcAddress(h_ntdll, "RtlGetVersion")))
+ {
+ RTL_OSVERSIONINFOW vi2{}; // initialize with zeroes - a better alternative to memset
+ vi2.dwOSVersionInfoSize = sizeof(vi2);
+ if (STATUS_SUCCESS == RtlGetVersion(&vi2))
+ {
+ if (!bHaveVerFromKernel32) // we failed above; let's hope this would be useful
+ aVer.append(OUString::number(vi2.dwMajorVersion) + "."
+ + OUString::number(vi2.dwMinorVersion));
+ aVer.append(" ");
+ if (vi2.szCSDVersion[0])
+ aVer.append(o3tl::toU(vi2.szCSDVersion)).append(" ");
+ aVer.append("Build " + OUString::number(vi2.dwBuildNumber));
+ bHaveVerFromRtlGetVersion = true;
+ }
+ }
+ }
+ if (!bHaveVerFromKernel32 && !bHaveVerFromRtlGetVersion)
+ aVer.append("unknown");
+ return aVer.makeStringAndClear();
+}
+
+std::shared_ptr<vcl::BackendCapabilities> WinSalInstance::GetBackendCapabilities()
+{
+ auto pBackendCapabilities = SalInstance::GetBackendCapabilities();
+#if HAVE_FEATURE_SKIA
+#if SKIA_USE_BITMAP32
+ if( SkiaHelper::isVCLSkiaEnabled())
+ pBackendCapabilities->mbSupportsBitmap32 = true;
+#endif
+#endif
+ return pBackendCapabilities;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/app/salshl.cxx b/vcl/win/app/salshl.cxx
new file mode 100644
index 000000000..5619ece1f
--- /dev/null
+++ b/vcl/win/app/salshl.cxx
@@ -0,0 +1,112 @@
+/* -*- 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 <svsys.h>
+#include <sal/log.hxx>
+#include <win/saldata.hxx>
+
+SalShlData aSalShlData;
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hInst, DWORD nReason, LPVOID)
+{
+ if ( nReason == DLL_PROCESS_ATTACH )
+ aSalShlData.mhInst = hInst;
+ return 1;
+}
+
+HCURSOR ImplLoadSalCursor( int nId )
+{
+ SAL_WARN_IF( !aSalShlData.mhInst, "vcl", "no DLL instance handle" );
+
+ HCURSOR hCursor = LoadCursorW( aSalShlData.mhInst, MAKEINTRESOURCEW( nId ) );
+
+ SAL_WARN_IF( !hCursor, "vcl", "cursor not found in sal resource" );
+
+ return hCursor;
+}
+
+HBITMAP ImplLoadSalBitmap( int nId )
+{
+ SAL_WARN_IF( !aSalShlData.mhInst, "vcl", "no DLL instance handle" );
+
+ HBITMAP hBitmap = LoadBitmapW( aSalShlData.mhInst, MAKEINTRESOURCEW( nId ) );
+
+ SAL_WARN_IF( !hBitmap, "vcl", "bitmap not found in sal resource" );
+
+ return hBitmap;
+}
+
+bool ImplLoadSalIcon( int nId, HICON& rIcon, HICON& rSmallIcon )
+{
+ SAL_WARN_IF( !aSalShlData.mhInst, "vcl", "no DLL instance handle" );
+
+ SalData* pSalData = GetSalData();
+
+ // check the cache first
+ SalIcon *pSalIcon = pSalData->mpFirstIcon;
+ while( pSalIcon )
+ {
+ if( pSalIcon->nId != nId )
+ pSalIcon = pSalIcon->pNext;
+ else
+ {
+ rIcon = pSalIcon->hIcon;
+ rSmallIcon = pSalIcon->hSmallIcon;
+ return (rSmallIcon != nullptr);
+ }
+ }
+
+ // Try at first to load the icons from the application exe file
+ rIcon = static_cast<HICON>(LoadImageW( pSalData->mhInst, MAKEINTRESOURCEW( nId ),
+ IMAGE_ICON, GetSystemMetrics( SM_CXICON ), GetSystemMetrics( SM_CYICON ),
+ LR_DEFAULTCOLOR ));
+ if ( !rIcon )
+ {
+ // If the application don't provide these icons, then we try
+ // to load the icon from the VCL resource
+ rIcon = static_cast<HICON>(LoadImageW( aSalShlData.mhInst, MAKEINTRESOURCEW( nId ),
+ IMAGE_ICON, GetSystemMetrics( SM_CXICON ), GetSystemMetrics( SM_CYICON ),
+ LR_DEFAULTCOLOR ));
+ if ( rIcon )
+ {
+ rSmallIcon = static_cast<HICON>(LoadImageW( aSalShlData.mhInst, MAKEINTRESOURCEW( nId ),
+ IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ),
+ LR_DEFAULTCOLOR ));
+ }
+ else
+ rSmallIcon = nullptr;
+ }
+ else
+ {
+ rSmallIcon = static_cast<HICON>(LoadImageW( pSalData->mhInst, MAKEINTRESOURCEW( nId ),
+ IMAGE_ICON, GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ),
+ LR_DEFAULTCOLOR ));
+ }
+
+ if( rIcon )
+ {
+ // add to icon cache
+ pSalData->mpFirstIcon = new SalIcon{
+ nId, rIcon, rSmallIcon, pSalData->mpFirstIcon};
+ }
+
+ return (rSmallIcon != nullptr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/app/saltimer.cxx b/vcl/win/app/saltimer.cxx
new file mode 100644
index 000000000..5a4760ad5
--- /dev/null
+++ b/vcl/win/app/saltimer.cxx
@@ -0,0 +1,199 @@
+/* -*- 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 <sal/config.h>
+#include <sal/log.hxx>
+
+#include <tools/time.hxx>
+
+#include <svsys.h>
+#include <win/saldata.hxx>
+#include <win/saltimer.h>
+#include <win/salinst.h>
+
+void CALLBACK SalTimerProc(PVOID pParameter, BOOLEAN bTimerOrWaitFired);
+
+// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms687003%28v=vs.85%29.aspx
+// (and related pages) for details about the Timer Queues.
+
+// in order to prevent concurrent execution of ImplSalStartTimer and double
+// deletion of timer (which is extremely likely, given that
+// INVALID_HANDLE_VALUE waits for the callback to run on the main thread),
+// this must run on the main thread too
+void WinSalTimer::ImplStop()
+{
+ SalData *const pSalData = GetSalData();
+ const WinSalInstance *pInst = pSalData->mpInstance;
+ assert( !pInst || pSalData->mnAppThreadId == GetCurrentThreadId() );
+
+ if ( m_bSetTimerRunning )
+ {
+ m_bSetTimerRunning = false;
+ KillTimer( GetSalData()->mpInstance->mhComWnd, m_aWmTimerId );
+ }
+ m_bDirectTimeout = false;
+
+ const HANDLE hTimer = m_nTimerId;
+ if ( nullptr == hTimer )
+ return;
+
+ m_nTimerId = nullptr;
+ DeleteTimerQueueTimer( nullptr, hTimer, INVALID_HANDLE_VALUE );
+ // Keep InvalidateEvent after DeleteTimerQueueTimer, because the event id
+ // is set in SalTimerProc, which DeleteTimerQueueTimer will finish or cancel.
+ InvalidateEvent();
+}
+
+void WinSalTimer::ImplStart( sal_uInt64 nMS )
+{
+ SalData* pSalData = GetSalData();
+ assert( !pSalData->mpInstance || pSalData->mnAppThreadId == GetCurrentThreadId() );
+
+ // DueTime parameter is a DWORD, which is always an unsigned 32bit
+ if (nMS > SAL_MAX_UINT32)
+ nMS = SAL_MAX_UINT32;
+
+ // cannot change a one-shot timer, so delete it and create a new one
+ ImplStop();
+
+ // directly indicate an elapsed timer
+ m_bDirectTimeout = ( 0 == nMS );
+ // probably WT_EXECUTEONLYONCE is not needed, but it enforces Period
+ // to be 0 and should not hurt; also see
+ // https://www.microsoft.com/msj/0499/pooling/pooling.aspx
+ if ( !m_bDirectTimeout )
+ CreateTimerQueueTimer(&m_nTimerId, nullptr, SalTimerProc, this,
+ nMS, 0, WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE);
+ else if ( m_bForceRealTimer )
+ {
+ // so we don't block the nested message queue in move and resize
+ // with posted 0ms SAL_MSG_TIMER_CALLBACK messages
+ SetTimer( GetSalData()->mpInstance->mhComWnd, m_aWmTimerId,
+ USER_TIMER_MINIMUM, nullptr );
+ m_bSetTimerRunning = true;
+ }
+ // we don't need any wakeup message, as this code can just run in the
+ // main thread!
+}
+
+WinSalTimer::WinSalTimer()
+ : m_nTimerId( nullptr )
+ , m_bDirectTimeout( false )
+ , m_bForceRealTimer( false )
+ , m_bSetTimerRunning( false )
+{
+}
+
+WinSalTimer::~WinSalTimer()
+{
+ Stop();
+}
+
+void WinSalTimer::Start( sal_uInt64 nMS )
+{
+ WinSalInstance *pInst = GetSalData()->mpInstance;
+ if ( pInst && !pInst->IsMainThread() )
+ {
+ bool const ret = PostMessageW(pInst->mhComWnd,
+ SAL_MSG_STARTTIMER, 0, static_cast<LPARAM>(tools::Time::GetSystemTicks()) + nMS);
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ else
+ ImplStart( nMS );
+}
+
+void WinSalTimer::Stop()
+{
+ WinSalInstance *pInst = GetSalData()->mpInstance;
+ if ( pInst && !pInst->IsMainThread() )
+ {
+ bool const ret = PostMessageW(pInst->mhComWnd,
+ SAL_MSG_STOPTIMER, 0, 0);
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ else
+ ImplStop();
+}
+
+/**
+ * This gets invoked from a Timer Queue thread.
+ * Don't acquire the SolarMutex to avoid deadlocks.
+ */
+void CALLBACK SalTimerProc(PVOID data, BOOLEAN)
+{
+ __try
+ {
+ WinSalTimer *pTimer = static_cast<WinSalTimer*>( data );
+ bool const ret = PostMessageW(
+ GetSalData()->mpInstance->mhComWnd, SAL_MSG_TIMER_CALLBACK,
+ static_cast<WPARAM>(pTimer->GetNextEventVersion()), 0 );
+#if OSL_DEBUG_LEVEL > 0
+ if (!ret) // SEH prevents using SAL_WARN here?
+ fputs("ERROR: PostMessage() failed!\n", stderr);
+#endif
+ }
+ __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))
+ {
+ }
+}
+
+void WinSalTimer::ImplHandleElapsedTimer()
+{
+ // Test for MouseLeave
+ SalTestMouseLeave();
+
+ m_bDirectTimeout = false;
+ ImplSalYieldMutexAcquireWithWait();
+ CallCallback();
+ ImplSalYieldMutexRelease();
+}
+
+void WinSalTimer::ImplHandleTimerEvent( const WPARAM aWPARAM )
+{
+ assert( aWPARAM <= SAL_MAX_INT32 );
+ if ( !IsValidEventVersion( static_cast<sal_Int32>( aWPARAM ) ) )
+ return;
+
+ ImplHandleElapsedTimer();
+}
+
+void WinSalTimer::SetForceRealTimer( const bool bVal )
+{
+ if ( m_bForceRealTimer == bVal )
+ return;
+
+ m_bForceRealTimer = bVal;
+
+ // we need a real timer, as m_bDirectTimeout won't be processed
+ if ( bVal && m_bDirectTimeout )
+ Start( 0 );
+}
+
+void WinSalTimer::ImplHandle_WM_TIMER( const WPARAM aWPARAM )
+{
+ assert( m_aWmTimerId == aWPARAM );
+ if ( !(m_aWmTimerId == aWPARAM && m_bSetTimerRunning) )
+ return;
+
+ m_bSetTimerRunning = false;
+ KillTimer( GetSalData()->mpInstance->mhComWnd, m_aWmTimerId );
+ ImplHandleElapsedTimer();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/DWriteTextRenderer.cxx b/vcl/win/gdi/DWriteTextRenderer.cxx
new file mode 100644
index 000000000..734b68b07
--- /dev/null
+++ b/vcl/win/gdi/DWriteTextRenderer.cxx
@@ -0,0 +1,413 @@
+/* -*- 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 <win/salgdi.h>
+#include <win/saldata.hxx>
+#include <outdev.h>
+
+#include <win/DWriteTextRenderer.hxx>
+
+#include <sft.hxx>
+#include <sallayout.hxx>
+
+#include <shlwapi.h>
+#include <winver.h>
+
+#include <comphelper/windowserrorstring.hxx>
+#include <sal/log.hxx>
+
+namespace
+{
+
+D2DTextAntiAliasMode lclGetSystemTextAntiAliasMode()
+{
+ D2DTextAntiAliasMode eMode = D2DTextAntiAliasMode::Default;
+
+ BOOL bFontSmoothing;
+ if (!SystemParametersInfoW(SPI_GETFONTSMOOTHING, 0, &bFontSmoothing, 0))
+ return eMode;
+
+ if (bFontSmoothing)
+ {
+ eMode = D2DTextAntiAliasMode::AntiAliased;
+
+ UINT nType;
+ if (SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &nType, 0) && nType == FE_FONTSMOOTHINGCLEARTYPE)
+ eMode = D2DTextAntiAliasMode::ClearType;
+ }
+ else
+ {
+ eMode = D2DTextAntiAliasMode::Aliased;
+ }
+
+ return eMode;
+}
+
+IDWriteRenderingParams* lclSetRenderingMode(IDWriteFactory* pDWriteFactory, DWRITE_RENDERING_MODE eRenderingMode)
+{
+ IDWriteRenderingParams* pDefaultParameters = nullptr;
+ pDWriteFactory->CreateRenderingParams(&pDefaultParameters);
+
+ IDWriteRenderingParams* pParameters = nullptr;
+ pDWriteFactory->CreateCustomRenderingParams(
+ pDefaultParameters->GetGamma(),
+ pDefaultParameters->GetEnhancedContrast(),
+ pDefaultParameters->GetClearTypeLevel(),
+ pDefaultParameters->GetPixelGeometry(),
+ eRenderingMode,
+ &pParameters);
+ return pParameters;
+}
+
+#ifdef SAL_LOG_WARN
+HRESULT checkResult(HRESULT hr, const char* file, size_t line)
+{
+ if (FAILED(hr))
+ {
+ OUString sLocationString = OUString::createFromAscii(file) + ":" + OUString::number(line) + " ";
+ SAL_DETAIL_LOG_STREAM(SAL_DETAIL_ENABLE_LOG_WARN, ::SAL_DETAIL_LOG_LEVEL_WARN,
+ "vcl.gdi", sLocationString.toUtf8().getStr(),
+ "HRESULT failed with: 0x" << OUString::number(hr, 16) << ": " << WindowsErrorStringFromHRESULT(hr));
+ }
+ return hr;
+}
+
+#define CHECKHR(funct) checkResult(funct, __FILE__, __LINE__)
+#else
+#define CHECKHR(funct) (funct)
+#endif
+
+
+} // end anonymous namespace
+
+D2DWriteTextOutRenderer::D2DWriteTextOutRenderer()
+ : mpD2DFactory(nullptr),
+ mpDWriteFactory(nullptr),
+ mpGdiInterop(nullptr),
+ mpRT(nullptr),
+ mRTProps(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
+ 0, 0)),
+ mpFontFace(nullptr),
+ mlfEmHeight(0.0f),
+ mhDC(nullptr),
+ meTextAntiAliasMode(D2DTextAntiAliasMode::Default)
+{
+ HRESULT hr = S_OK;
+ hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), nullptr, reinterpret_cast<void **>(&mpD2DFactory));
+ hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&mpDWriteFactory));
+ if (SUCCEEDED(hr))
+ {
+ hr = mpDWriteFactory->GetGdiInterop(&mpGdiInterop);
+ hr = CreateRenderTarget();
+ }
+ meTextAntiAliasMode = lclGetSystemTextAntiAliasMode();
+}
+
+D2DWriteTextOutRenderer::~D2DWriteTextOutRenderer()
+{
+ if (mpRT)
+ mpRT->Release();
+ if (mpGdiInterop)
+ mpGdiInterop->Release();
+ if (mpDWriteFactory)
+ mpDWriteFactory->Release();
+ if (mpD2DFactory)
+ mpD2DFactory->Release();
+}
+
+void D2DWriteTextOutRenderer::applyTextAntiAliasMode()
+{
+ D2D1_TEXT_ANTIALIAS_MODE eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
+ DWRITE_RENDERING_MODE eRenderingMode = DWRITE_RENDERING_MODE_DEFAULT;
+ switch (meTextAntiAliasMode)
+ {
+ case D2DTextAntiAliasMode::Default:
+ eRenderingMode = DWRITE_RENDERING_MODE_DEFAULT;
+ eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
+ break;
+ case D2DTextAntiAliasMode::Aliased:
+ eRenderingMode = DWRITE_RENDERING_MODE_ALIASED;
+ eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
+ break;
+ case D2DTextAntiAliasMode::AntiAliased:
+ eRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
+ eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
+ break;
+ case D2DTextAntiAliasMode::ClearType:
+ eRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
+ eTextAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
+ break;
+ default:
+ break;
+ }
+ mpRT->SetTextRenderingParams(lclSetRenderingMode(mpDWriteFactory, eRenderingMode));
+ mpRT->SetTextAntialiasMode(eTextAAMode);
+}
+
+HRESULT D2DWriteTextOutRenderer::CreateRenderTarget()
+{
+ if (mpRT)
+ {
+ mpRT->Release();
+ mpRT = nullptr;
+ }
+ HRESULT hr = CHECKHR(mpD2DFactory->CreateDCRenderTarget(&mRTProps, &mpRT));
+ if (SUCCEEDED(hr))
+ applyTextAntiAliasMode();
+ return hr;
+}
+
+void D2DWriteTextOutRenderer::changeTextAntiAliasMode(D2DTextAntiAliasMode eMode)
+{
+ if (meTextAntiAliasMode != eMode)
+ {
+ meTextAntiAliasMode = eMode;
+ applyTextAntiAliasMode();
+ }
+}
+
+bool D2DWriteTextOutRenderer::Ready() const
+{
+ return mpGdiInterop && mpRT;
+}
+
+HRESULT D2DWriteTextOutRenderer::BindDC(HDC hDC, tools::Rectangle const & rRect)
+{
+ RECT const rc = { rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom() };
+ return CHECKHR(mpRT->BindDC(hDC, &rc));
+}
+
+bool D2DWriteTextOutRenderer::operator ()(GenericSalLayout const & rLayout, SalGraphics& rGraphics, HDC hDC)
+{
+ bool bRetry = false;
+ bool bResult = false;
+ int nCount = 0;
+ do
+ {
+ bRetry = false;
+ bResult = performRender(rLayout, rGraphics, hDC, bRetry);
+ nCount++;
+ } while (bRetry && nCount < 3);
+ return bResult;
+}
+
+bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, SalGraphics& rGraphics, HDC hDC, bool& bRetry)
+{
+ if (!Ready())
+ return false;
+
+ HRESULT hr = S_OK;
+ hr = BindDC(hDC);
+
+ if (hr == D2DERR_RECREATE_TARGET)
+ {
+ CreateRenderTarget();
+ bRetry = true;
+ return false;
+ }
+ if (FAILED(hr))
+ {
+ // If for any reason we can't bind fallback to legacy APIs.
+ return ExTextOutRenderer()(rLayout, rGraphics, hDC);
+ }
+
+ mlfEmHeight = 0;
+ if (!GetDWriteFaceFromHDC(hDC, &mpFontFace, &mlfEmHeight))
+ return false;
+
+ const WinFontInstance& rWinFont = static_cast<const WinFontInstance&>(rLayout.GetFont());
+ float fHScale = rWinFont.getHScale();
+
+ tools::Rectangle bounds;
+ bool succeeded = rLayout.GetBoundRect(bounds);
+ if (succeeded)
+ {
+ hr = BindDC(hDC, bounds); // Update the bounding rect.
+ succeeded &= SUCCEEDED(hr);
+ }
+
+ ID2D1SolidColorBrush* pBrush = nullptr;
+ if (succeeded)
+ {
+ COLORREF bgrTextColor = GetTextColor(hDC);
+ D2D1::ColorF aD2DColor(GetRValue(bgrTextColor) / 255.0f, GetGValue(bgrTextColor) / 255.0f, GetBValue(bgrTextColor) / 255.0f);
+ succeeded &= SUCCEEDED(CHECKHR(mpRT->CreateSolidColorBrush(aD2DColor, &pBrush)));
+ }
+
+ if (succeeded)
+ {
+ mpRT->BeginDraw();
+
+ int nStart = 0;
+ Point aPos(0, 0);
+ const GlyphItem* pGlyph;
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ UINT16 glyphIndices[] = { pGlyph->glyphId() };
+ FLOAT glyphAdvances[] = { static_cast<FLOAT>(pGlyph->m_nNewWidth) / fHScale };
+ DWRITE_GLYPH_OFFSET glyphOffsets[] = { { 0.0f, 0.0f }, };
+ D2D1_POINT_2F baseline = { static_cast<FLOAT>(aPos.X() - bounds.Left()) / fHScale,
+ static_cast<FLOAT>(aPos.Y() - bounds.Top()) };
+ WinFontTransformGuard aTransformGuard(mpRT, fHScale, rLayout, baseline);
+ DWRITE_GLYPH_RUN glyphs = {
+ mpFontFace,
+ mlfEmHeight,
+ 1,
+ glyphIndices,
+ glyphAdvances,
+ glyphOffsets,
+ false,
+ 0
+ };
+
+ mpRT->DrawGlyphRun(baseline, &glyphs, pBrush);
+ }
+
+ hr = CHECKHR(mpRT->EndDraw());
+ }
+
+ if (pBrush)
+ pBrush->Release();
+
+ ReleaseFont();
+
+ if (hr == D2DERR_RECREATE_TARGET)
+ {
+ CreateRenderTarget();
+ bRetry = true;
+ }
+
+ return succeeded;
+}
+
+bool D2DWriteTextOutRenderer::BindFont(HDC hDC)
+{
+ // A TextOutRender can only be bound to one font at a time, so the
+ assert(mpFontFace == nullptr);
+ if (mpFontFace)
+ {
+ ReleaseFont();
+ return false;
+ }
+
+ // Initially bind to an empty rectangle to get access to the font face,
+ // we'll update it once we've calculated a bounding rect in DrawGlyphs
+ if (FAILED(BindDC(mhDC = hDC)))
+ return false;
+
+ mlfEmHeight = 0;
+ return GetDWriteFaceFromHDC(hDC, &mpFontFace, &mlfEmHeight);
+}
+
+bool D2DWriteTextOutRenderer::ReleaseFont()
+{
+ mpFontFace->Release();
+ mpFontFace = nullptr;
+ mhDC = nullptr;
+
+ return true;
+}
+
+// GetGlyphInkBoxes
+// The inkboxes returned have their origin on the baseline, to a -ve value
+// of Top() means the glyph extends abs(Top()) many pixels above the
+// baseline, and +ve means the ink starts that many pixels below.
+std::vector<tools::Rectangle> D2DWriteTextOutRenderer::GetGlyphInkBoxes(uint16_t const * pGid, uint16_t const * pGidEnd) const
+{
+ ptrdiff_t nGlyphs = pGidEnd - pGid;
+ if (nGlyphs < 0)
+ return std::vector<tools::Rectangle>();
+
+ DWRITE_FONT_METRICS aFontMetrics;
+ mpFontFace->GetMetrics(&aFontMetrics);
+
+ std::vector<DWRITE_GLYPH_METRICS> metrics(nGlyphs);
+ if (!SUCCEEDED(CHECKHR(mpFontFace->GetDesignGlyphMetrics(pGid, nGlyphs, metrics.data()))))
+ return std::vector<tools::Rectangle>();
+
+ std::vector<tools::Rectangle> aOut(nGlyphs);
+ auto pOut = aOut.begin();
+ for (auto &m : metrics)
+ {
+ const long left = m.leftSideBearing,
+ top = m.topSideBearing - m.verticalOriginY,
+ right = m.advanceWidth - m.rightSideBearing,
+ bottom = INT32(m.advanceHeight) - m.verticalOriginY - m.bottomSideBearing;
+
+ // Scale to screen space.
+ pOut->SetLeft( std::floor(left * mlfEmHeight / aFontMetrics.designUnitsPerEm) );
+ pOut->SetTop( std::floor(top * mlfEmHeight / aFontMetrics.designUnitsPerEm) );
+ pOut->SetRight( std::ceil(right * mlfEmHeight / aFontMetrics.designUnitsPerEm) );
+ pOut->SetBottom( std::ceil(bottom * mlfEmHeight / aFontMetrics.designUnitsPerEm) );
+
+ ++pOut;
+ }
+
+ return aOut;
+}
+
+bool D2DWriteTextOutRenderer::GetDWriteFaceFromHDC(HDC hDC, IDWriteFontFace ** ppFontFace, float * lfSize) const
+{
+ bool succeeded = SUCCEEDED(CHECKHR(mpGdiInterop->CreateFontFaceFromHdc(hDC, ppFontFace)));
+
+ if (succeeded)
+ {
+ LOGFONTW aLogFont;
+ HFONT hFont = static_cast<HFONT>(::GetCurrentObject(hDC, OBJ_FONT));
+
+ GetObjectW(hFont, sizeof(LOGFONTW), &aLogFont);
+ float dpix, dpiy;
+ mpRT->GetDpi(&dpix, &dpiy);
+ *lfSize = aLogFont.lfHeight * 96.0f / dpiy;
+
+ assert(*lfSize < 0);
+ *lfSize *= -1;
+ }
+
+ return succeeded;
+}
+
+WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, float fHScale,
+ const GenericSalLayout& rLayout,
+ const D2D1_POINT_2F& rBaseline)
+ : mpRenderTarget(pRenderTarget)
+{
+ pRenderTarget->GetTransform(&maTransform);
+ D2D1::Matrix3x2F aTransform = maTransform;
+ if (fHScale != 1.0f)
+ {
+ aTransform
+ = aTransform * D2D1::Matrix3x2F::Scale(D2D1::Size(fHScale, 1.0f), D2D1::Point2F(0, 0));
+ }
+
+ if (rLayout.GetOrientation() != 0)
+ {
+ // DWrite angle is in clockwise degrees, our orientation is in counter-clockwise 10th
+ // degrees.
+ aTransform = aTransform
+ * D2D1::Matrix3x2F::Rotation(
+ -static_cast<FLOAT>(rLayout.GetOrientation()) / 10, rBaseline);
+ }
+ mpRenderTarget->SetTransform(aTransform);
+}
+
+WinFontTransformGuard::~WinFontTransformGuard() { mpRenderTarget->SetTransform(maTransform); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/gdiimpl.cxx b/vcl/win/gdi/gdiimpl.cxx
new file mode 100644
index 000000000..b01969272
--- /dev/null
+++ b/vcl/win/gdi/gdiimpl.cxx
@@ -0,0 +1,2710 @@
+/* -*- 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 <sal/config.h>
+
+#include <memory>
+#include <numeric>
+
+#include <svsys.h>
+
+#include "gdiimpl.hxx"
+
+#include <string.h>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+#include <tools/poly.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <win/wincomp.hxx>
+#include <win/saldata.hxx>
+#include <win/salgdi.h>
+#include <win/salbmp.h>
+#include <win/scoped_gdi.hxx>
+#include <vcl/BitmapAccessMode.hxx>
+#include <vcl/BitmapBuffer.hxx>
+#include <vcl/BitmapPalette.hxx>
+#include <win/salframe.h>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/utils/systemdependentdata.hxx>
+
+#include <outdata.hxx>
+#include <win/salids.hrc>
+#include <ControlCacheKey.hxx>
+
+#if defined _MSC_VER
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#endif
+
+#include <prewin.h>
+
+#include <gdiplus.h>
+#include <gdiplusenums.h>
+#include <gdipluscolor.h>
+
+#include <postwin.h>
+
+#define SAL_POLYPOLYCOUNT_STACKBUF 8
+#define SAL_POLYPOLYPOINTS_STACKBUF 64
+
+#define SAL_POLY_STACKBUF 32
+
+namespace {
+
+// #100127# Fill point and flag memory from array of points which
+// might also contain bezier control points for the PolyDraw() GDI method
+// Make sure pWinPointAry and pWinFlagAry are big enough
+void ImplPreparePolyDraw( bool bCloseFigures,
+ sal_uLong nPoly,
+ const sal_uInt32* pPoints,
+ const SalPoint* const* pPtAry,
+ const PolyFlags* const* pFlgAry,
+ POINT* pWinPointAry,
+ BYTE* pWinFlagAry )
+{
+ sal_uLong nCurrPoly;
+ for( nCurrPoly=0; nCurrPoly<nPoly; ++nCurrPoly )
+ {
+ const POINT* pCurrPoint = reinterpret_cast<const POINT*>( *pPtAry++ );
+ const PolyFlags* pCurrFlag = *pFlgAry++;
+ const sal_uInt32 nCurrPoints = *pPoints++;
+ const bool bHaveFlagArray( pCurrFlag );
+ sal_uLong nCurrPoint;
+
+ if( nCurrPoints )
+ {
+ // start figure
+ *pWinPointAry++ = *pCurrPoint++;
+ *pWinFlagAry++ = PT_MOVETO;
+ ++pCurrFlag;
+
+ for( nCurrPoint=1; nCurrPoint<nCurrPoints; )
+ {
+ // #102067# Check existence of flag array
+ if( bHaveFlagArray &&
+ ( nCurrPoint + 2 ) < nCurrPoints )
+ {
+ PolyFlags P4( pCurrFlag[ 2 ] );
+
+ if( ( PolyFlags::Control == pCurrFlag[ 0 ] ) &&
+ ( PolyFlags::Control == pCurrFlag[ 1 ] ) &&
+ ( PolyFlags::Normal == P4 || PolyFlags::Smooth == P4 || PolyFlags::Symmetric == P4 ) )
+ {
+ // control point one
+ *pWinPointAry++ = *pCurrPoint++;
+ *pWinFlagAry++ = PT_BEZIERTO;
+
+ // control point two
+ *pWinPointAry++ = *pCurrPoint++;
+ *pWinFlagAry++ = PT_BEZIERTO;
+
+ // end point
+ *pWinPointAry++ = *pCurrPoint++;
+ *pWinFlagAry++ = PT_BEZIERTO;
+
+ nCurrPoint += 3;
+ pCurrFlag += 3;
+ continue;
+ }
+ }
+
+ // regular line point
+ *pWinPointAry++ = *pCurrPoint++;
+ *pWinFlagAry++ = PT_LINETO;
+ ++pCurrFlag;
+ ++nCurrPoint;
+ }
+
+ // end figure?
+ if( bCloseFigures )
+ pWinFlagAry[-1] |= PT_CLOSEFIGURE;
+ }
+ }
+}
+
+Color ImplGetROPColor( SalROPColor nROPColor )
+{
+ Color nColor;
+ if ( nROPColor == SalROPColor::N0 )
+ nColor = Color( 0, 0, 0 );
+ else
+ nColor = Color( 255, 255, 255 );
+ return nColor;
+}
+
+bool IsDitherColor(BYTE nRed, BYTE nGreen, BYTE nBlue)
+{
+ constexpr sal_uInt8 DITHER_PAL_DELTA = 51;
+
+ return !(nRed % DITHER_PAL_DELTA) &&
+ !(nGreen % DITHER_PAL_DELTA) &&
+ !(nBlue % DITHER_PAL_DELTA);
+}
+
+bool IsPaletteColor(BYTE nRed, BYTE nGreen, BYTE nBlue)
+{
+ static const PALETTEENTRY aImplSalSysPalEntryAry[] =
+ {
+ { 0, 0, 0, 0 },
+ { 0, 0, 0x80, 0 },
+ { 0, 0x80, 0, 0 },
+ { 0, 0x80, 0x80, 0 },
+ { 0x80, 0, 0, 0 },
+ { 0x80, 0, 0x80, 0 },
+ { 0x80, 0x80, 0, 0 },
+ { 0x80, 0x80, 0x80, 0 },
+ { 0xC0, 0xC0, 0xC0, 0 },
+ { 0, 0, 0xFF, 0 },
+ { 0, 0xFF, 0, 0 },
+ { 0, 0xFF, 0xFF, 0 },
+ { 0xFF, 0, 0, 0 },
+ { 0xFF, 0, 0xFF, 0 },
+ { 0xFF, 0xFF, 0, 0 },
+ { 0xFF, 0xFF, 0xFF, 0 }
+ };
+
+ for (const auto& rPalEntry : aImplSalSysPalEntryAry)
+ {
+ if(rPalEntry.peRed == nRed &&
+ rPalEntry.peGreen == nGreen &&
+ rPalEntry.peBlue == nBlue)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool IsExtraColor(BYTE nRed, BYTE nGreen, BYTE nBlue)
+{
+ return (nRed == 0) && (nGreen == 184) && (nBlue == 255);
+}
+
+bool ImplIsPaletteEntry(BYTE nRed, BYTE nGreen, BYTE nBlue)
+{
+ return IsDitherColor(nRed, nGreen, nBlue) ||
+ IsPaletteColor(nRed, nGreen, nBlue) ||
+ IsExtraColor(nRed, nGreen, nBlue);
+}
+
+} // namespace
+
+WinSalGraphicsImpl::WinSalGraphicsImpl(WinSalGraphics& rParent):
+ mrParent(rParent),
+ mbXORMode(false),
+ mbPen(false),
+ mhPen(nullptr),
+ mbStockPen(false),
+ mbBrush(false),
+ mbStockBrush(false),
+ mhBrush(nullptr)
+{
+}
+
+WinSalGraphicsImpl::~WinSalGraphicsImpl()
+{
+ if ( mhPen )
+ {
+ if ( !mbStockPen )
+ DeletePen( mhPen );
+ }
+
+ if ( mhBrush )
+ {
+ if ( !mbStockBrush )
+ DeleteBrush( mhBrush );
+ }
+}
+
+void WinSalGraphicsImpl::Init()
+{
+}
+
+void WinSalGraphicsImpl::freeResources()
+{
+}
+
+bool WinSalGraphicsImpl::drawEPS(long, long, long, long, void*, sal_uInt32)
+{
+ return false;
+}
+
+void WinSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
+{
+ HDC hSrcDC;
+ DWORD nRop;
+
+ if ( pSrcGraphics )
+ hSrcDC = static_cast<WinSalGraphics*>(pSrcGraphics)->getHDC();
+ else
+ hSrcDC = mrParent.getHDC();
+
+ if ( mbXORMode )
+ nRop = SRCINVERT;
+ else
+ nRop = SRCCOPY;
+
+ if ( (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
+ (rPosAry.mnSrcHeight == rPosAry.mnDestHeight) )
+ {
+ BitBlt( mrParent.getHDC(),
+ static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
+ static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
+ hSrcDC,
+ static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
+ nRop );
+ }
+ else
+ {
+ int nOldStretchMode = SetStretchBltMode( mrParent.getHDC(), STRETCH_DELETESCANS );
+ StretchBlt( mrParent.getHDC(),
+ static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
+ static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
+ hSrcDC,
+ static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
+ static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
+ nRop );
+ SetStretchBltMode( mrParent.getHDC(), nOldStretchMode );
+ }
+}
+
+namespace
+{
+
+void MakeInvisibleArea(const RECT& rSrcRect,
+ int nLeft, int nTop, int nRight, int nBottom,
+ HRGN& rhInvalidateRgn)
+{
+ if (!rhInvalidateRgn)
+ {
+ rhInvalidateRgn = CreateRectRgnIndirect(&rSrcRect);
+ }
+
+ ScopedHRGN hTempRgn(CreateRectRgn(nLeft, nTop, nRight, nBottom));
+ CombineRgn(rhInvalidateRgn, rhInvalidateRgn, hTempRgn.get(), RGN_DIFF);
+}
+
+void ImplCalcOutSideRgn( const RECT& rSrcRect,
+ int nLeft, int nTop, int nRight, int nBottom,
+ HRGN& rhInvalidateRgn )
+{
+ // calculate area outside the visible region
+ if (rSrcRect.left < nLeft)
+ {
+ MakeInvisibleArea(rSrcRect, -31999, 0, nLeft, 31999, rhInvalidateRgn);
+ }
+ if (rSrcRect.top < nTop)
+ {
+ MakeInvisibleArea(rSrcRect, 0, -31999, 31999, nTop, rhInvalidateRgn);
+ }
+ if (rSrcRect.right > nRight)
+ {
+ MakeInvisibleArea(rSrcRect, nRight, 0, 31999, 31999, rhInvalidateRgn);
+ }
+ if (rSrcRect.bottom > nBottom)
+ {
+ MakeInvisibleArea(rSrcRect, 0, nBottom, 31999, 31999, rhInvalidateRgn);
+ }
+}
+
+} // namespace
+
+void WinSalGraphicsImpl::copyArea( long nDestX, long nDestY,
+ long nSrcX, long nSrcY,
+ long nSrcWidth, long nSrcHeight,
+ bool bWindowInvalidate )
+{
+ bool bRestoreClipRgn = false;
+ HRGN hOldClipRgn = nullptr;
+ int nOldClipRgnType = ERROR;
+ HRGN hInvalidateRgn = nullptr;
+
+ // do we have to invalidate also the overlapping regions?
+ if ( bWindowInvalidate && mrParent.isWindow() )
+ {
+ // compute and invalidate those parts that were either off-screen or covered by other windows
+ // while performing the above BitBlt
+ // those regions then have to be invalidated as they contain useless/wrong data
+ RECT aSrcRect;
+ RECT aClipRect;
+ RECT aTempRect;
+ RECT aTempRect2;
+ HRGN hTempRgn;
+ HWND hWnd;
+
+ // restrict srcRect to this window (calc intersection)
+ aSrcRect.left = static_cast<int>(nSrcX);
+ aSrcRect.top = static_cast<int>(nSrcY);
+ aSrcRect.right = aSrcRect.left+static_cast<int>(nSrcWidth);
+ aSrcRect.bottom = aSrcRect.top+static_cast<int>(nSrcHeight);
+ GetClientRect( mrParent.gethWnd(), &aClipRect );
+ if ( IntersectRect( &aSrcRect, &aSrcRect, &aClipRect ) )
+ {
+ // transform srcRect to screen coordinates
+ POINT aPt;
+ aPt.x = 0;
+ aPt.y = 0;
+ ClientToScreen( mrParent.gethWnd(), &aPt );
+ aSrcRect.left += aPt.x;
+ aSrcRect.top += aPt.y;
+ aSrcRect.right += aPt.x;
+ aSrcRect.bottom += aPt.y;
+ hInvalidateRgn = nullptr;
+
+ // compute the parts that are off screen (ie invisible)
+ RECT theScreen;
+ ImplSalGetWorkArea( nullptr, &theScreen, nullptr ); // find the screen area taking multiple monitors into account
+ ImplCalcOutSideRgn( aSrcRect, theScreen.left, theScreen.top, theScreen.right, theScreen.bottom, hInvalidateRgn );
+
+ // calculate regions that are covered by other windows
+ HRGN hTempRgn2 = nullptr;
+ HWND hWndTopWindow = mrParent.gethWnd();
+ // Find the TopLevel Window, because only Windows which are in
+ // in the foreground of our TopLevel window must be considered
+ if ( GetWindowStyle( hWndTopWindow ) & WS_CHILD )
+ {
+ RECT aTempRect3 = aSrcRect;
+ do
+ {
+ hWndTopWindow = ::GetParent( hWndTopWindow );
+
+ // Test if the Parent clips our window
+ GetClientRect( hWndTopWindow, &aTempRect );
+ POINT aPt2;
+ aPt2.x = 0;
+ aPt2.y = 0;
+ ClientToScreen( hWndTopWindow, &aPt2 );
+ aTempRect.left += aPt2.x;
+ aTempRect.top += aPt2.y;
+ aTempRect.right += aPt2.x;
+ aTempRect.bottom += aPt2.y;
+ IntersectRect( &aTempRect3, &aTempRect3, &aTempRect );
+ }
+ while ( GetWindowStyle( hWndTopWindow ) & WS_CHILD );
+
+ // If one or more Parents clip our window, then we must
+ // calculate the outside area
+ if ( !EqualRect( &aSrcRect, &aTempRect3 ) )
+ {
+ ImplCalcOutSideRgn( aSrcRect,
+ aTempRect3.left, aTempRect3.top,
+ aTempRect3.right, aTempRect3.bottom,
+ hInvalidateRgn );
+ }
+ }
+ // retrieve the top-most (z-order) child window
+ hWnd = GetWindow( GetDesktopWindow(), GW_CHILD );
+ while ( hWnd )
+ {
+ if ( hWnd == hWndTopWindow )
+ break;
+ if ( IsWindowVisible( hWnd ) && !IsIconic( hWnd ) )
+ {
+ GetWindowRect( hWnd, &aTempRect );
+ if ( IntersectRect( &aTempRect2, &aSrcRect, &aTempRect ) )
+ {
+ // hWnd covers part or all of aSrcRect
+ if ( !hInvalidateRgn )
+ hInvalidateRgn = CreateRectRgnIndirect( &aSrcRect );
+
+ // get full bounding box of hWnd
+ hTempRgn = CreateRectRgnIndirect( &aTempRect );
+
+ // get region of hWnd (the window may be shaped)
+ if ( !hTempRgn2 )
+ hTempRgn2 = CreateRectRgn( 0, 0, 0, 0 );
+ int nRgnType = GetWindowRgn( hWnd, hTempRgn2 );
+ if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
+ {
+ // convert window region to screen coordinates
+ OffsetRgn( hTempRgn2, aTempRect.left, aTempRect.top );
+ // and intersect with the window's bounding box
+ CombineRgn( hTempRgn, hTempRgn, hTempRgn2, RGN_AND );
+ }
+ // finally compute that part of aSrcRect which is not covered by any parts of hWnd
+ CombineRgn( hInvalidateRgn, hInvalidateRgn, hTempRgn, RGN_DIFF );
+ DeleteRegion( hTempRgn );
+ }
+ }
+ // retrieve the next window in the z-order, i.e. the window below hwnd
+ hWnd = GetWindow( hWnd, GW_HWNDNEXT );
+ }
+ if ( hTempRgn2 )
+ DeleteRegion( hTempRgn2 );
+ if ( hInvalidateRgn )
+ {
+ // hInvalidateRgn contains the fully visible parts of the original srcRect
+ hTempRgn = CreateRectRgnIndirect( &aSrcRect );
+ // subtract it from the original rect to get the occluded parts
+ int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_DIFF );
+ DeleteRegion( hTempRgn );
+
+ if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
+ {
+ // move the occluded parts to the destination pos
+ int nOffX = static_cast<int>(nDestX-nSrcX);
+ int nOffY = static_cast<int>(nDestY-nSrcY);
+ OffsetRgn( hInvalidateRgn, nOffX-aPt.x, nOffY-aPt.y );
+
+ // by excluding hInvalidateRgn from the system's clip region
+ // we will prevent bitblt from copying useless data
+ // especially now shadows from overlapping windows will appear (#i36344)
+ hOldClipRgn = CreateRectRgn( 0, 0, 0, 0 );
+ nOldClipRgnType = GetClipRgn( mrParent.getHDC(), hOldClipRgn );
+
+ bRestoreClipRgn = true; // indicate changed clipregion and force invalidate
+ ExtSelectClipRgn( mrParent.getHDC(), hInvalidateRgn, RGN_DIFF );
+ }
+ }
+ }
+ }
+
+ BitBlt( mrParent.getHDC(),
+ static_cast<int>(nDestX), static_cast<int>(nDestY),
+ static_cast<int>(nSrcWidth), static_cast<int>(nSrcHeight),
+ mrParent.getHDC(),
+ static_cast<int>(nSrcX), static_cast<int>(nSrcY),
+ SRCCOPY );
+
+ if( bRestoreClipRgn )
+ {
+ // restore old clip region
+ if( nOldClipRgnType != ERROR )
+ SelectClipRgn( mrParent.getHDC(), hOldClipRgn);
+ DeleteRegion( hOldClipRgn );
+
+ // invalidate regions that were not copied
+ bool bInvalidate = true;
+
+ // Combine Invalidate vcl::Region with existing ClipRegion
+ HRGN hTempRgn = CreateRectRgn( 0, 0, 0, 0 );
+ if ( GetClipRgn( mrParent.getHDC(), hTempRgn ) == 1 )
+ {
+ int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_AND );
+ if ( (nRgnType == ERROR) || (nRgnType == NULLREGION) )
+ bInvalidate = false;
+ }
+ DeleteRegion( hTempRgn );
+
+ if ( bInvalidate )
+ {
+ InvalidateRgn( mrParent.gethWnd(), hInvalidateRgn, TRUE );
+ // here we only initiate an update if this is the MainThread,
+ // so that there is no deadlock when handling the Paint event,
+ // as the SolarMutex is already held by this Thread
+ SalData* pSalData = GetSalData();
+ DWORD nCurThreadId = GetCurrentThreadId();
+ if ( pSalData->mnAppThreadId == nCurThreadId )
+ UpdateWindow( mrParent.gethWnd() );
+ }
+
+ DeleteRegion( hInvalidateRgn );
+ }
+
+}
+
+namespace {
+
+void ImplDrawBitmap( HDC hDC, const SalTwoRect& rPosAry, const WinSalBitmap& rSalBitmap,
+ bool bPrinter, int nDrawMode )
+{
+ if( hDC )
+ {
+ HGLOBAL hDrawDIB;
+ HBITMAP hDrawDDB = rSalBitmap.ImplGethDDB();
+ std::unique_ptr<WinSalBitmap> xTmpSalBmp;
+ bool bPrintDDB = ( bPrinter && hDrawDDB );
+
+ if( bPrintDDB )
+ {
+ xTmpSalBmp.reset(new WinSalBitmap);
+ xTmpSalBmp->Create( rSalBitmap, rSalBitmap.GetBitCount() );
+ hDrawDIB = xTmpSalBmp->ImplGethDIB();
+ }
+ else
+ hDrawDIB = rSalBitmap.ImplGethDIB();
+
+ if( hDrawDIB )
+ {
+ PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( hDrawDIB ));
+ PBYTE pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize +
+ WinSalBitmap::ImplGetDIBColorCount( hDrawDIB ) * sizeof( RGBQUAD );
+ const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
+
+ StretchDIBits( hDC,
+ static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
+ static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
+ static_cast<int>(rPosAry.mnSrcX), static_cast<int>(pBI->bmiHeader.biHeight - rPosAry.mnSrcHeight - rPosAry.mnSrcY),
+ static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
+ pBits, pBI, DIB_RGB_COLORS, nDrawMode );
+
+ GlobalUnlock( hDrawDIB );
+ SetStretchBltMode( hDC, nOldStretchMode );
+ }
+ else if( hDrawDDB && !bPrintDDB )
+ {
+ ScopedCachedHDC<CACHED_HDC_DRAW> hBmpDC(hDrawDDB);
+
+ COLORREF nOldBkColor = RGB(0xFF,0xFF,0xFF);
+ COLORREF nOldTextColor = RGB(0,0,0);
+ bool bMono = ( rSalBitmap.GetBitCount() == 1 );
+
+ if( bMono )
+ {
+ COLORREF nBkColor = RGB( 0xFF, 0xFF, 0xFF );
+ COLORREF nTextColor = RGB( 0x00, 0x00, 0x00 );
+ //fdo#33455 handle 1 bit depth pngs with palette entries
+ //to set fore/back colors
+ if (BitmapBuffer* pBitmapBuffer = const_cast<WinSalBitmap&>(rSalBitmap).AcquireBuffer(BitmapAccessMode::Info))
+ {
+ const BitmapPalette& rPalette = pBitmapBuffer->maPalette;
+ if (rPalette.GetEntryCount() == 2)
+ {
+ Color nCol = rPalette[0];
+ nTextColor = RGB( nCol.GetRed(), nCol.GetGreen(), nCol.GetBlue() );
+ nCol = rPalette[1];
+ nBkColor = RGB( nCol.GetRed(), nCol.GetGreen(), nCol.GetBlue() );
+ }
+ const_cast<WinSalBitmap&>(rSalBitmap).ReleaseBuffer(pBitmapBuffer, BitmapAccessMode::Info);
+ }
+ nOldBkColor = SetBkColor( hDC, nBkColor );
+ nOldTextColor = ::SetTextColor( hDC, nTextColor );
+ }
+
+ if ( (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
+ (rPosAry.mnSrcHeight == rPosAry.mnDestHeight) )
+ {
+ BitBlt( hDC,
+ static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
+ static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
+ hBmpDC.get(),
+ static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
+ nDrawMode );
+ }
+ else
+ {
+ const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
+
+ StretchBlt( hDC,
+ static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
+ static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
+ hBmpDC.get(),
+ static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
+ static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
+ nDrawMode );
+
+ SetStretchBltMode( hDC, nOldStretchMode );
+ }
+
+ if( bMono )
+ {
+ SetBkColor( hDC, nOldBkColor );
+ ::SetTextColor( hDC, nOldTextColor );
+ }
+ }
+ }
+}
+
+} // namespace
+
+void WinSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
+{
+ bool bTryDirectPaint(!mrParent.isPrinter() && !mbXORMode);
+
+ if(bTryDirectPaint)
+ {
+ // only paint direct when no scaling and no MapMode, else the
+ // more expensive conversions may be done for short-time Bitmap/BitmapEx
+ // used for buffering only
+ if(rPosAry.mnSrcWidth == rPosAry.mnDestWidth && rPosAry.mnSrcHeight == rPosAry.mnDestHeight)
+ {
+ bTryDirectPaint = false;
+ }
+ }
+
+ // try to draw using GdiPlus directly
+ if(bTryDirectPaint && TryDrawBitmapGDIPlus(rPosAry, rSalBitmap))
+ {
+ return;
+ }
+
+ // fall back old stuff
+ assert(dynamic_cast<const WinSalBitmap*>(&rSalBitmap));
+
+ ImplDrawBitmap(mrParent.getHDC(), rPosAry, static_cast<const WinSalBitmap&>(rSalBitmap),
+ mrParent.isPrinter(),
+ mbXORMode ? SRCINVERT : SRCCOPY );
+}
+
+void WinSalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry,
+ const SalBitmap& rSSalBitmap,
+ const SalBitmap& rSTransparentBitmap )
+{
+ SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
+ bool bTryDirectPaint(!mrParent.isPrinter() && !mbXORMode);
+
+ // try to draw using GdiPlus directly
+ if(bTryDirectPaint && drawAlphaBitmap(rPosAry, rSSalBitmap, rSTransparentBitmap))
+ {
+ return;
+ }
+
+ assert(dynamic_cast<const WinSalBitmap*>(&rSSalBitmap));
+ assert(dynamic_cast<const WinSalBitmap*>(&rSTransparentBitmap));
+
+ const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
+ const WinSalBitmap& rTransparentBitmap = static_cast<const WinSalBitmap&>(rSTransparentBitmap);
+
+ SalTwoRect aPosAry = rPosAry;
+ int nDstX = static_cast<int>(aPosAry.mnDestX);
+ int nDstY = static_cast<int>(aPosAry.mnDestY);
+ int nDstWidth = static_cast<int>(aPosAry.mnDestWidth);
+ int nDstHeight = static_cast<int>(aPosAry.mnDestHeight);
+ HDC hDC = mrParent.getHDC();
+
+ ScopedHBITMAP hMemBitmap;
+ ScopedHBITMAP hMaskBitmap;
+
+ if( ( nDstWidth > CACHED_HDC_DEFEXT ) || ( nDstHeight > CACHED_HDC_DEFEXT ) )
+ {
+ hMemBitmap.reset(CreateCompatibleBitmap(hDC, nDstWidth, nDstHeight));
+ hMaskBitmap.reset(CreateCompatibleBitmap(hDC, nDstWidth, nDstHeight));
+ }
+
+ ScopedCachedHDC<CACHED_HDC_1> hMemDC(hMemBitmap.get());
+ ScopedCachedHDC<CACHED_HDC_2> hMaskDC(hMaskBitmap.get());
+
+ aPosAry.mnDestX = aPosAry.mnDestY = 0;
+ BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hDC, nDstX, nDstY, SRCCOPY );
+
+ // WIN/WNT seems to have a minor problem mapping the correct color of the
+ // mask to the palette if we draw the DIB directly ==> draw DDB
+ if( ( GetBitCount() <= 8 ) && rTransparentBitmap.ImplGethDIB() && rTransparentBitmap.GetBitCount() == 1 )
+ {
+ WinSalBitmap aTmp;
+
+ if( aTmp.Create( rTransparentBitmap, &mrParent ) )
+ ImplDrawBitmap( hMaskDC.get(), aPosAry, aTmp, false, SRCCOPY );
+ }
+ else
+ ImplDrawBitmap( hMaskDC.get(), aPosAry, rTransparentBitmap, false, SRCCOPY );
+
+ // now MemDC contains background, MaskDC the transparency mask
+
+ // #105055# Respect XOR mode
+ if( mbXORMode )
+ {
+ ImplDrawBitmap( hMaskDC.get(), aPosAry, rSalBitmap, false, SRCERASE );
+ // now MaskDC contains the bitmap area with black background
+ BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCINVERT );
+ // now MemDC contains background XORed bitmap area ontop
+ }
+ else
+ {
+ BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCAND );
+ // now MemDC contains background with masked-out bitmap area
+ ImplDrawBitmap( hMaskDC.get(), aPosAry, rSalBitmap, false, SRCERASE );
+ // now MaskDC contains the bitmap area with black background
+ BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCPAINT );
+ // now MemDC contains background and bitmap merged together
+ }
+ // copy to output DC
+ BitBlt( hDC, nDstX, nDstY, nDstWidth, nDstHeight, hMemDC.get(), 0, 0, SRCCOPY );
+}
+
+bool WinSalGraphicsImpl::drawAlphaRect( long nX, long nY, long nWidth,
+ long nHeight, sal_uInt8 nTransparency )
+{
+ if( mbPen || !mbBrush || mbXORMode )
+ return false; // can only perform solid fills without XOR.
+
+ ScopedCachedHDC<CACHED_HDC_1> hMemDC(nullptr);
+ SetPixel( hMemDC.get(), int(0), int(0), mnBrushColor );
+
+ BLENDFUNCTION aFunc = {
+ AC_SRC_OVER,
+ 0,
+ sal::static_int_cast<sal_uInt8>(255 - 255L*nTransparency/100),
+ 0
+ };
+
+ // hMemDC contains a 1x1 bitmap of the right color - stretch-blit
+ // that to dest hdc
+ bool bRet = GdiAlphaBlend(mrParent.getHDC(), nX, nY, nWidth, nHeight,
+ hMemDC.get(), 0,0,1,1,
+ aFunc ) == TRUE;
+
+ return bRet;
+}
+
+void WinSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry,
+ const SalBitmap& rSSalBitmap,
+ Color nMaskColor)
+{
+ SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
+
+ assert(dynamic_cast<const WinSalBitmap*>(&rSSalBitmap));
+
+ const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
+
+ SalTwoRect aPosAry = rPosAry;
+ const HDC hDC = mrParent.getHDC();
+
+ ScopedSelectedHBRUSH hBrush(hDC, CreateSolidBrush(RGB(nMaskColor.GetRed(),
+ nMaskColor.GetGreen(),
+ nMaskColor.GetBlue())));
+
+ // WIN/WNT seems to have a minor problem mapping the correct color of the
+ // mask to the palette if we draw the DIB directly ==> draw DDB
+ if( ( GetBitCount() <= 8 ) && rSalBitmap.ImplGethDIB() && rSalBitmap.GetBitCount() == 1 )
+ {
+ WinSalBitmap aTmp;
+
+ if( aTmp.Create( rSalBitmap, &mrParent ) )
+ ImplDrawBitmap( hDC, aPosAry, aTmp, false, 0x00B8074AUL );
+ }
+ else
+ ImplDrawBitmap( hDC, aPosAry, rSalBitmap, false, 0x00B8074AUL );
+}
+
+std::shared_ptr<SalBitmap> WinSalGraphicsImpl::getBitmap( long nX, long nY, long nDX, long nDY )
+{
+ SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No ::GetBitmap() from printer possible!" );
+
+ std::shared_ptr<WinSalBitmap> pSalBitmap;
+
+ nDX = labs( nDX );
+ nDY = labs( nDY );
+
+ HDC hDC = mrParent.getHDC();
+ HBITMAP hBmpBitmap = CreateCompatibleBitmap( hDC, nDX, nDY );
+ bool bRet;
+
+ {
+ ScopedCachedHDC<CACHED_HDC_1> hBmpDC(hBmpBitmap);
+
+ bRet = BitBlt(hBmpDC.get(), 0, 0,
+ static_cast<int>(nDX), static_cast<int>(nDY), hDC,
+ static_cast<int>(nX), static_cast<int>(nY), SRCCOPY) ? TRUE : FALSE;
+ }
+
+ if( bRet )
+ {
+ pSalBitmap = std::make_shared<WinSalBitmap>();
+
+ if( !pSalBitmap->Create( hBmpBitmap, false, false ) )
+ {
+ pSalBitmap.reset();
+ }
+ }
+ else
+ {
+ // #124826# avoid resource leak! Happens when running without desktop access (remote desktop, service, may be screensavers)
+ DeleteBitmap( hBmpBitmap );
+ }
+
+ return pSalBitmap;
+}
+
+Color WinSalGraphicsImpl::getPixel( long nX, long nY )
+{
+ COLORREF aWinCol = ::GetPixel( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY) );
+
+ if ( CLR_INVALID == aWinCol )
+ return Color( 0, 0, 0 );
+ else
+ return Color( GetRValue( aWinCol ),
+ GetGValue( aWinCol ),
+ GetBValue( aWinCol ) );
+}
+
+namespace
+{
+
+HBRUSH Get50PercentBrush()
+{
+ SalData* pSalData = GetSalData();
+ if ( !pSalData->mh50Brush )
+ {
+ if ( !pSalData->mh50Bmp )
+ pSalData->mh50Bmp = ImplLoadSalBitmap( SAL_RESID_BITMAP_50 );
+ pSalData->mh50Brush = CreatePatternBrush( pSalData->mh50Bmp );
+ }
+
+ return pSalData->mh50Brush;
+}
+
+} // namespace
+
+void WinSalGraphicsImpl::invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags )
+{
+ if ( nFlags & SalInvert::TrackFrame )
+ {
+ HPEN hDotPen = CreatePen( PS_DOT, 0, 0 );
+ HPEN hOldPen = SelectPen( mrParent.getHDC(), hDotPen );
+ HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), GetStockBrush( NULL_BRUSH ) );
+ int nOldROP = SetROP2( mrParent.getHDC(), R2_NOT );
+
+ Rectangle( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY), static_cast<int>(nX+nWidth), static_cast<int>(nY+nHeight) );
+
+ SetROP2( mrParent.getHDC(), nOldROP );
+ SelectPen( mrParent.getHDC(), hOldPen );
+ SelectBrush( mrParent.getHDC(), hOldBrush );
+ DeletePen( hDotPen );
+ }
+ else if ( nFlags & SalInvert::N50 )
+ {
+ COLORREF nOldTextColor = ::SetTextColor( mrParent.getHDC(), 0 );
+ HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), Get50PercentBrush() );
+ PatBlt( mrParent.getHDC(), nX, nY, nWidth, nHeight, PATINVERT );
+ ::SetTextColor( mrParent.getHDC(), nOldTextColor );
+ SelectBrush( mrParent.getHDC(), hOldBrush );
+ }
+ else
+ {
+ RECT aRect;
+ aRect.left = static_cast<int>(nX);
+ aRect.top = static_cast<int>(nY);
+ aRect.right = static_cast<int>(nX)+nWidth;
+ aRect.bottom = static_cast<int>(nY)+nHeight;
+ ::InvertRect( mrParent.getHDC(), &aRect );
+ }
+}
+
+void WinSalGraphicsImpl::invert( sal_uInt32 nPoints, const SalPoint* pPtAry, SalInvert nSalFlags )
+{
+ HPEN hPen;
+ HPEN hOldPen;
+ HBRUSH hBrush;
+ HBRUSH hOldBrush = nullptr;
+ COLORREF nOldTextColor RGB(0,0,0);
+ int nOldROP = SetROP2( mrParent.getHDC(), R2_NOT );
+
+ if ( nSalFlags & SalInvert::TrackFrame )
+ hPen = CreatePen( PS_DOT, 0, 0 );
+ else
+ {
+
+ if ( nSalFlags & SalInvert::N50 )
+ hBrush = Get50PercentBrush();
+ else
+ hBrush = GetStockBrush( BLACK_BRUSH );
+
+ hPen = GetStockPen( NULL_PEN );
+ nOldTextColor = ::SetTextColor( mrParent.getHDC(), 0 );
+ hOldBrush = SelectBrush( mrParent.getHDC(), hBrush );
+ }
+ hOldPen = SelectPen( mrParent.getHDC(), hPen );
+
+ POINT const * pWinPtAry;
+ // for NT, we can handover the array directly
+ static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
+
+ pWinPtAry = reinterpret_cast<POINT const *>(pPtAry);
+ // for Windows 95 and its maximum number of points
+ if ( nSalFlags & SalInvert::TrackFrame )
+ {
+ if ( !Polyline( mrParent.getHDC(), pWinPtAry, static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
+ Polyline( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
+ }
+ else
+ {
+ if ( !Polygon( mrParent.getHDC(), pWinPtAry, static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
+ Polygon( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
+ }
+
+ SetROP2( mrParent.getHDC(), nOldROP );
+ SelectPen( mrParent.getHDC(), hOldPen );
+
+ if ( nSalFlags & SalInvert::TrackFrame )
+ DeletePen( hPen );
+ else
+ {
+ ::SetTextColor( mrParent.getHDC(), nOldTextColor );
+ SelectBrush( mrParent.getHDC(), hOldBrush );
+ }
+}
+
+sal_uInt16 WinSalGraphicsImpl::GetBitCount() const
+{
+ return static_cast<sal_uInt16>(GetDeviceCaps( mrParent.getHDC(), BITSPIXEL ));
+}
+
+long WinSalGraphicsImpl::GetGraphicsWidth() const
+{
+ if( mrParent.gethWnd() && IsWindow( mrParent.gethWnd() ) )
+ {
+ WinSalFrame* pFrame = GetWindowPtr( mrParent.gethWnd() );
+ if( pFrame )
+ {
+ if( pFrame->maGeometry.nWidth )
+ return pFrame->maGeometry.nWidth;
+ else
+ {
+ // TODO: perhaps not needed, maGeometry should always be up-to-date
+ RECT aRect;
+ GetClientRect( mrParent.gethWnd(), &aRect );
+ return aRect.right;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void WinSalGraphicsImpl::ResetClipRegion()
+{
+ if ( mrParent.mhRegion )
+ {
+ DeleteRegion( mrParent.mhRegion );
+ mrParent.mhRegion = nullptr;
+ }
+
+ SelectClipRgn( mrParent.getHDC(), nullptr );
+}
+
+static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolygon& rCandidate)
+{
+ if(rCandidate.areControlPointsUsed())
+ {
+ return false;
+ }
+
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(nPointCount < 2)
+ {
+ return true;
+ }
+
+ const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount + 1 : nPointCount);
+ basegfx::B2DPoint aLast(rCandidate.getB2DPoint(0));
+
+ for(sal_uInt32 a(1); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex(a % nPointCount);
+ const basegfx::B2DPoint aCurrent(rCandidate.getB2DPoint(nNextIndex));
+
+ if(!basegfx::fTools::equal(aLast.getX(), aCurrent.getX()) && !basegfx::fTools::equal(aLast.getY(), aCurrent.getY()))
+ {
+ return false;
+ }
+
+ aLast = aCurrent;
+ }
+
+ return true;
+}
+
+static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolyPolygon& rCandidate)
+{
+ if(rCandidate.areControlPointsUsed())
+ {
+ return false;
+ }
+
+ for(auto const& rPolygon : rCandidate)
+ {
+ if(!containsOnlyHorizontalAndVerticalEdges(rPolygon))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool WinSalGraphicsImpl::setClipRegion( const vcl::Region& i_rClip )
+{
+ if ( mrParent.mhRegion )
+ {
+ DeleteRegion( mrParent.mhRegion );
+ mrParent.mhRegion = nullptr;
+ }
+
+ bool bUsePolygon(i_rClip.HasPolyPolygonOrB2DPolyPolygon());
+ static bool bTryToAvoidPolygon(true);
+
+ // #i122149# try to avoid usage of tools::PolyPolygon ClipRegions when tools::PolyPolygon is no curve
+ // and only contains horizontal/vertical edges. In that case, use the fallback
+ // in GetRegionRectangles which will use vcl::Region::GetAsRegionBand() which will do
+ // the correct polygon-to-RegionBand transformation.
+ // Background is that when using the same Rectangle as rectangle or as Polygon
+ // clip region will lead to different results; the polygon-based one will be
+ // one pixel less to the right and down (see GDI docu for CreatePolygonRgn). This
+ // again is because of the polygon-nature and it's classic handling when filling.
+ // This also means that all cases which use a 'true' polygon-based incarnation of
+ // a vcl::Region should know what they do - it may lead to repaint errors.
+ if(bUsePolygon && bTryToAvoidPolygon)
+ {
+ const basegfx::B2DPolyPolygon aPolyPolygon( i_rClip.GetAsB2DPolyPolygon() );
+
+ if(!aPolyPolygon.areControlPointsUsed())
+ {
+ if(containsOnlyHorizontalAndVerticalEdges(aPolyPolygon))
+ {
+ bUsePolygon = false;
+ }
+ }
+ }
+
+ if(bUsePolygon)
+ {
+ // #i122149# check the comment above to know that this may lead to potential repaint
+ // problems. It may be solved (if needed) by scaling the polygon by one in X
+ // and Y. Currently the workaround to only use it if really unavoidable will
+ // solve most cases. When someone is really using polygon-based Regions he
+ // should know what he is doing.
+ // Added code to do that scaling to check if it works, testing it.
+ const basegfx::B2DPolyPolygon aPolyPolygon( i_rClip.GetAsB2DPolyPolygon() );
+ const sal_uInt32 nCount(aPolyPolygon.count());
+
+ if( nCount )
+ {
+ std::vector< POINT > aPolyPoints;
+ aPolyPoints.reserve( 1024 );
+ std::vector< INT > aPolyCounts( nCount, 0 );
+ basegfx::B2DHomMatrix aExpand;
+ sal_uInt32 nTargetCount(0);
+ static bool bExpandByOneInXandY(true);
+
+ if(bExpandByOneInXandY)
+ {
+ const basegfx::B2DRange aRangeS(aPolyPolygon.getB2DRange());
+ const basegfx::B2DRange aRangeT(aRangeS.getMinimum(), aRangeS.getMaximum() + basegfx::B2DTuple(1.0, 1.0));
+ aExpand = basegfx::utils::createSourceRangeTargetRangeTransform(aRangeS, aRangeT);
+ }
+
+ for(auto const& rPolygon : aPolyPolygon)
+ {
+ const basegfx::B2DPolygon aPoly(
+ basegfx::utils::adaptiveSubdivideByDistance(
+ rPolygon,
+ 1));
+ const sal_uInt32 nPoints(aPoly.count());
+
+ // tdf#40863 For CustomShapes there is a hack (see
+ // f64ef72743e55389e446e0d4bc6febd475011023) that adds polygons
+ // with a single point in top-left and bottom-right corner
+ // of the BoundRect to be able to determine the correct BoundRect
+ // in the slideshow. Unfortunately, CreatePolyPolygonRgn below
+ // fails with polygons containing a single pixel, so clipping is
+ // lost. For now, use only polygons with more than two points - the
+ // ones that may have an area.
+ // Note: polygons with one point which are curves may have an area,
+ // but the polygon is already subdivided here, so no need to test
+ // this.
+ if(nPoints > 2)
+ {
+ aPolyCounts[nTargetCount] = nPoints;
+ nTargetCount++;
+
+ for( sal_uInt32 b = 0; b < nPoints; b++ )
+ {
+ basegfx::B2DPoint aPt(aPoly.getB2DPoint(b));
+
+ if(bExpandByOneInXandY)
+ {
+ aPt = aExpand * aPt;
+ }
+
+ POINT aPOINT;
+ // #i122149# do correct rounding
+ aPOINT.x = basegfx::fround(aPt.getX());
+ aPOINT.y = basegfx::fround(aPt.getY());
+ aPolyPoints.push_back( aPOINT );
+ }
+ }
+ }
+
+ if(nTargetCount)
+ {
+ mrParent.mhRegion = CreatePolyPolygonRgn( aPolyPoints.data(), aPolyCounts.data(), nTargetCount, ALTERNATE );
+ }
+ }
+ }
+ else
+ {
+ RectangleVector aRectangles;
+ i_rClip.GetRegionRectangles(aRectangles);
+
+ sal_uLong nRectBufSize = sizeof(RECT)*aRectangles.size();
+ if ( aRectangles.size() < SAL_CLIPRECT_COUNT )
+ {
+ if ( !mrParent.mpStdClipRgnData )
+ mrParent.mpStdClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+(SAL_CLIPRECT_COUNT*sizeof(RECT))]);
+ mrParent.mpClipRgnData = mrParent.mpStdClipRgnData;
+ }
+ else
+ mrParent.mpClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+nRectBufSize]);
+ mrParent.mpClipRgnData->rdh.dwSize = sizeof( RGNDATAHEADER );
+ mrParent.mpClipRgnData->rdh.iType = RDH_RECTANGLES;
+ mrParent.mpClipRgnData->rdh.nCount = aRectangles.size();
+ mrParent.mpClipRgnData->rdh.nRgnSize = nRectBufSize;
+ RECT* pBoundRect = &(mrParent.mpClipRgnData->rdh.rcBound);
+ SetRectEmpty( pBoundRect );
+ RECT* pNextClipRect = reinterpret_cast<RECT*>(&(mrParent.mpClipRgnData->Buffer));
+ bool bFirstClipRect = true;
+
+ for (auto const& rectangle : aRectangles)
+ {
+ const long nW(rectangle.GetWidth());
+ const long nH(rectangle.GetHeight());
+
+ if(nW && nH)
+ {
+ const long nRight(rectangle.Left() + nW);
+ const long nBottom(rectangle.Top() + nH);
+
+ if(bFirstClipRect)
+ {
+ pBoundRect->left = rectangle.Left();
+ pBoundRect->top = rectangle.Top();
+ pBoundRect->right = nRight;
+ pBoundRect->bottom = nBottom;
+ bFirstClipRect = false;
+ }
+ else
+ {
+ if(rectangle.Left() < pBoundRect->left)
+ {
+ pBoundRect->left = static_cast<int>(rectangle.Left());
+ }
+
+ if(rectangle.Top() < pBoundRect->top)
+ {
+ pBoundRect->top = static_cast<int>(rectangle.Top());
+ }
+
+ if(nRight > pBoundRect->right)
+ {
+ pBoundRect->right = static_cast<int>(nRight);
+ }
+
+ if(nBottom > pBoundRect->bottom)
+ {
+ pBoundRect->bottom = static_cast<int>(nBottom);
+ }
+ }
+
+ pNextClipRect->left = static_cast<int>(rectangle.Left());
+ pNextClipRect->top = static_cast<int>(rectangle.Top());
+ pNextClipRect->right = static_cast<int>(nRight);
+ pNextClipRect->bottom = static_cast<int>(nBottom);
+ pNextClipRect++;
+ }
+ else
+ {
+ mrParent.mpClipRgnData->rdh.nCount--;
+ mrParent.mpClipRgnData->rdh.nRgnSize -= sizeof( RECT );
+ }
+ }
+
+ // create clip region from ClipRgnData
+ if(0 == mrParent.mpClipRgnData->rdh.nCount)
+ {
+ // #i123585# region is empty; this may happen when e.g. a tools::PolyPolygon is given
+ // that contains no polygons or only empty ones (no width/height). This is
+ // perfectly fine and we are done, except setting it (see end of method)
+ }
+ else if(1 == mrParent.mpClipRgnData->rdh.nCount)
+ {
+ RECT* pRect = &(mrParent.mpClipRgnData->rdh.rcBound);
+ mrParent.mhRegion = CreateRectRgn( pRect->left, pRect->top,
+ pRect->right, pRect->bottom );
+ }
+ else if(mrParent.mpClipRgnData->rdh.nCount > 1)
+ {
+ sal_uLong nSize = mrParent.mpClipRgnData->rdh.nRgnSize+sizeof(RGNDATAHEADER);
+ mrParent.mhRegion = ExtCreateRegion( nullptr, nSize, mrParent.mpClipRgnData );
+
+ // if ExtCreateRegion(...) is not supported
+ if( !mrParent.mhRegion )
+ {
+ RGNDATAHEADER const & pHeader = mrParent.mpClipRgnData->rdh;
+
+ if( pHeader.nCount )
+ {
+ RECT* pRect = reinterpret_cast<RECT*>(mrParent.mpClipRgnData->Buffer);
+ mrParent.mhRegion = CreateRectRgn( pRect->left, pRect->top, pRect->right, pRect->bottom );
+ pRect++;
+
+ for( sal_uLong n = 1; n < pHeader.nCount; n++, pRect++ )
+ {
+ ScopedHRGN hRgn(CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom));
+ CombineRgn( mrParent.mhRegion, mrParent.mhRegion, hRgn.get(), RGN_OR );
+ }
+ }
+ }
+
+ if ( mrParent.mpClipRgnData != mrParent.mpStdClipRgnData )
+ delete [] reinterpret_cast<BYTE*>(mrParent.mpClipRgnData);
+ }
+ }
+
+ if( mrParent.mhRegion )
+ {
+ SelectClipRgn( mrParent.getHDC(), mrParent.mhRegion );
+
+ // debug code if you want to check range of the newly applied ClipRegion
+ //RECT aBound;
+ //const int aRegionType = GetRgnBox(mrParent.mhRegion, &aBound);
+
+ //bool bBla = true;
+ }
+ else
+ {
+ // #i123585# See above, this is a valid case, execute it
+ SelectClipRgn( mrParent.getHDC(), nullptr );
+ }
+
+ // #i123585# retval no longer dependent of mrParent.mhRegion, see TaskId comments above
+ return true;
+}
+
+void WinSalGraphicsImpl::SetLineColor()
+{
+ ResetPen(GetStockPen(NULL_PEN));
+
+ // set new data
+ mbPen = false;
+ mbStockPen = true;
+}
+
+void WinSalGraphicsImpl::SetLineColor(Color nColor)
+{
+ COLORREF nPenColor = PALETTERGB(nColor.GetRed(),
+ nColor.GetGreen(),
+ nColor.GetBlue());
+ bool bStockPen = false;
+
+ HPEN hNewPen = SearchStockPen(nPenColor);
+ if (hNewPen)
+ bStockPen = true;
+ else
+ hNewPen = MakePen(nColor);
+
+ ResetPen(hNewPen);
+
+ // set new data
+ mnPenColor = nPenColor;
+ maLineColor = nColor;
+ mbPen = true;
+ mbStockPen = bStockPen;
+}
+
+HPEN WinSalGraphicsImpl::SearchStockPen(COLORREF nPenColor)
+{
+ // Only screen, because printer has problems, when we use stock objects.
+ if (!mrParent.isPrinter())
+ {
+ const SalData* pSalData = GetSalData();
+
+ for (sal_uInt16 i = 0; i < pSalData->mnStockPenCount; i++)
+ {
+ if (nPenColor == pSalData->maStockPenColorAry[i])
+ return pSalData->mhStockPenAry[i];
+ }
+ }
+
+ return nullptr;
+}
+
+HPEN WinSalGraphicsImpl::MakePen(Color nColor)
+{
+ COLORREF nPenColor = PALETTERGB(nColor.GetRed(),
+ nColor.GetGreen(),
+ nColor.GetBlue());
+
+ if (!mrParent.isPrinter())
+ {
+ if (GetSalData()->mhDitherPal && ImplIsSysColorEntry(nColor))
+ {
+ nPenColor = PALRGB_TO_RGB(nPenColor);
+ }
+ }
+
+ return CreatePen(PS_SOLID, mrParent.mnPenWidth, nPenColor);
+}
+
+void WinSalGraphicsImpl::ResetPen(HPEN hNewPen)
+{
+ HPEN hOldPen = SelectPen(mrParent.getHDC(), hNewPen);
+
+ if (mhPen)
+ {
+ if (!mbStockPen)
+ {
+ DeletePen(mhPen);
+ }
+ }
+ else
+ {
+ mrParent.mhDefPen = hOldPen;
+ }
+
+ mhPen = hNewPen;
+}
+
+void WinSalGraphicsImpl::SetFillColor()
+{
+ ResetBrush(GetStockBrush(NULL_BRUSH));
+
+ // set new data
+ mbBrush = false;
+ mbStockBrush = true;
+}
+
+void WinSalGraphicsImpl::SetFillColor(Color nColor)
+{
+ COLORREF nBrushColor = PALETTERGB(nColor.GetRed(),
+ nColor.GetGreen(),
+ nColor.GetBlue());
+ bool bStockBrush = false;
+
+ HBRUSH hNewBrush = SearchStockBrush(nBrushColor);
+ if (hNewBrush)
+ bStockBrush = true;
+ else
+ hNewBrush = MakeBrush(nColor);
+
+ ResetBrush(hNewBrush);
+
+ // set new data
+ mnBrushColor = nBrushColor;
+ maFillColor = nColor;
+ mbBrush = true;
+ mbStockBrush = bStockBrush;
+}
+
+HBRUSH WinSalGraphicsImpl::SearchStockBrush(COLORREF nBrushColor)
+{
+ // Only screen, because printer has problems, when we use stock objects.
+ if (!mrParent.isPrinter())
+ {
+ const SalData* pSalData = GetSalData();
+
+ for (sal_uInt16 i = 0; i < pSalData->mnStockBrushCount; i++)
+ {
+ if (nBrushColor == pSalData->maStockBrushColorAry[i])
+ return pSalData->mhStockBrushAry[i];
+ }
+ }
+
+ return nullptr;
+}
+
+namespace
+{
+
+BYTE GetDitherMappingValue(BYTE nVal, BYTE nThres, const SalData* pSalData)
+{
+ return (pSalData->mpDitherDiff[nVal] > nThres) ?
+ pSalData->mpDitherHigh[nVal] : pSalData->mpDitherLow[nVal];
+}
+
+HBRUSH Make16BitDIBPatternBrush(Color nColor)
+{
+ const SalData* pSalData = GetSalData();
+
+ const BYTE nRed = nColor.GetRed();
+ const BYTE nGreen = nColor.GetGreen();
+ const BYTE nBlue = nColor.GetBlue();
+
+ static const BYTE aOrdDither16Bit[8][8] =
+ {
+ { 0, 6, 1, 7, 0, 6, 1, 7 },
+ { 4, 2, 5, 3, 4, 2, 5, 3 },
+ { 1, 7, 0, 6, 1, 7, 0, 6 },
+ { 5, 3, 4, 2, 5, 3, 4, 2 },
+ { 0, 6, 1, 7, 0, 6, 1, 7 },
+ { 4, 2, 5, 3, 4, 2, 5, 3 },
+ { 1, 7, 0, 6, 1, 7, 0, 6 },
+ { 5, 3, 4, 2, 5, 3, 4, 2 }
+ };
+
+ BYTE* pTmp = pSalData->mpDitherDIBData;
+
+ for(int nY = 0; nY < 8; ++nY)
+ {
+ for(int nX = 0; nX < 8; ++nX)
+ {
+ const BYTE nThres = aOrdDither16Bit[nY][nX];
+ *pTmp++ = GetDitherMappingValue(nBlue, nThres, pSalData);
+ *pTmp++ = GetDitherMappingValue(nGreen, nThres, pSalData);
+ *pTmp++ = GetDitherMappingValue(nRed, nThres, pSalData);
+ }
+ }
+
+ return CreateDIBPatternBrush(pSalData->mhDitherDIB, DIB_RGB_COLORS);
+}
+
+HBRUSH Make8BitDIBPatternBrush(Color nColor)
+{
+ const SalData* pSalData = GetSalData();
+
+ const BYTE nRed = nColor.GetRed();
+ const BYTE nGreen = nColor.GetGreen();
+ const BYTE nBlue = nColor.GetBlue();
+
+ static const BYTE aOrdDither8Bit[8][8] =
+ {
+ { 0, 38, 9, 48, 2, 40, 12, 50 },
+ { 25, 12, 35, 22, 28, 15, 37, 24 },
+ { 6, 44, 3, 41, 8, 47, 5, 44 },
+ { 32, 19, 28, 16, 34, 21, 31, 18 },
+ { 1, 40, 11, 49, 0, 39, 10, 48 },
+ { 27, 14, 36, 24, 26, 13, 36, 23 },
+ { 8, 46, 4, 43, 7, 45, 4, 42 },
+ { 33, 20, 30, 17, 32, 20, 29, 16 }
+ };
+
+ BYTE* pTmp = pSalData->mpDitherDIBData;
+
+ for (int nY = 0; nY < 8; ++nY)
+ {
+ for (int nX = 0; nX < 8; ++nX)
+ {
+ const BYTE nThres = aOrdDither8Bit[nY][nX];
+ *pTmp = GetDitherMappingValue(nRed, nThres, pSalData) +
+ GetDitherMappingValue(nGreen, nThres, pSalData) * 6 +
+ GetDitherMappingValue(nBlue, nThres, pSalData) * 36;
+ pTmp++;
+ }
+ }
+
+ return CreateDIBPatternBrush(pSalData->mhDitherDIB, DIB_PAL_COLORS);
+}
+
+} // namespace
+
+HBRUSH WinSalGraphicsImpl::MakeBrush(Color nColor)
+{
+ const SalData* pSalData = GetSalData();
+
+ const BYTE nRed = nColor.GetRed();
+ const BYTE nGreen = nColor.GetGreen();
+ const BYTE nBlue = nColor.GetBlue();
+ const COLORREF nBrushColor = PALETTERGB(nRed, nGreen, nBlue);
+
+ if (mrParent.isPrinter() || !pSalData->mhDitherDIB)
+ return CreateSolidBrush(nBrushColor);
+
+ if (24 == reinterpret_cast<BITMAPINFOHEADER*>(pSalData->mpDitherDIB)->biBitCount)
+ return Make16BitDIBPatternBrush(nColor);
+
+ if (ImplIsSysColorEntry(nColor))
+ return CreateSolidBrush(PALRGB_TO_RGB(nBrushColor));
+
+ if (ImplIsPaletteEntry(nRed, nGreen, nBlue))
+ return CreateSolidBrush(nBrushColor);
+
+ return Make8BitDIBPatternBrush(nColor);
+}
+
+void WinSalGraphicsImpl::ResetBrush(HBRUSH hNewBrush)
+{
+ HBRUSH hOldBrush = SelectBrush(mrParent.getHDC(), hNewBrush);
+
+ if (mhBrush)
+ {
+ if (!mbStockBrush)
+ {
+ DeleteBrush(mhBrush);
+ }
+ }
+ else
+ {
+ mrParent.mhDefBrush = hOldBrush;
+ }
+
+ mhBrush = hNewBrush;
+}
+
+void WinSalGraphicsImpl::SetXORMode( bool bSet, bool )
+{
+ mbXORMode = bSet;
+ ::SetROP2( mrParent.getHDC(), bSet ? R2_XORPEN : R2_COPYPEN );
+}
+
+void WinSalGraphicsImpl::SetROPLineColor( SalROPColor nROPColor )
+{
+ SetLineColor( ImplGetROPColor( nROPColor ) );
+}
+
+void WinSalGraphicsImpl::SetROPFillColor( SalROPColor nROPColor )
+{
+ SetFillColor( ImplGetROPColor( nROPColor ) );
+}
+
+void WinSalGraphicsImpl::DrawPixelImpl( long nX, long nY, COLORREF crColor )
+{
+ const HDC hDC = mrParent.getHDC();
+
+ if (!mbXORMode)
+ {
+ SetPixel(hDC, static_cast<int>(nX), static_cast<int>(nY), crColor);
+ return;
+ }
+
+ ScopedSelectedHBRUSH hBrush(hDC, CreateSolidBrush(crColor));
+ PatBlt(hDC, static_cast<int>(nX), static_cast<int>(nY), int(1), int(1), PATINVERT);
+}
+
+void WinSalGraphicsImpl::drawPixel( long nX, long nY )
+{
+ DrawPixelImpl( nX, nY, mnPenColor );
+}
+
+void WinSalGraphicsImpl::drawPixel( long nX, long nY, Color nColor )
+{
+ COLORREF nCol = PALETTERGB( nColor.GetRed(),
+ nColor.GetGreen(),
+ nColor.GetBlue() );
+
+ if ( !mrParent.isPrinter() &&
+ GetSalData()->mhDitherPal &&
+ ImplIsSysColorEntry( nColor ) )
+ nCol = PALRGB_TO_RGB( nCol );
+
+ DrawPixelImpl( nX, nY, nCol );
+}
+
+void WinSalGraphicsImpl::drawLine( long nX1, long nY1, long nX2, long nY2 )
+{
+ MoveToEx( mrParent.getHDC(), static_cast<int>(nX1), static_cast<int>(nY1), nullptr );
+
+ LineTo( mrParent.getHDC(), static_cast<int>(nX2), static_cast<int>(nY2) );
+
+ // LineTo doesn't draw the last pixel
+ if ( !mrParent.isPrinter() )
+ DrawPixelImpl( nX2, nY2, mnPenColor );
+}
+
+void WinSalGraphicsImpl::drawRect( long nX, long nY, long nWidth, long nHeight )
+{
+ if ( !mbPen )
+ {
+ if ( !mrParent.isPrinter() )
+ {
+ PatBlt( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY), static_cast<int>(nWidth), static_cast<int>(nHeight),
+ mbXORMode ? PATINVERT : PATCOPY );
+ }
+ else
+ {
+ RECT aWinRect;
+ aWinRect.left = nX;
+ aWinRect.top = nY;
+ aWinRect.right = nX+nWidth;
+ aWinRect.bottom = nY+nHeight;
+ ::FillRect( mrParent.getHDC(), &aWinRect, mhBrush );
+ }
+ }
+ else
+ Rectangle( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY), static_cast<int>(nX+nWidth), static_cast<int>(nY+nHeight) );
+}
+
+void WinSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const SalPoint* pPtAry )
+{
+ // for NT, we can handover the array directly
+ static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
+
+ POINT const * pWinPtAry = reinterpret_cast<POINT const *>(pPtAry);
+
+ // for Windows 95 and its maximum number of points
+ if ( !Polyline( mrParent.getHDC(), pWinPtAry, static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
+ Polyline( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
+
+ // Polyline seems to uses LineTo, which doesn't paint the last pixel (see 87eb8f8ee)
+ if ( !mrParent.isPrinter() )
+ DrawPixelImpl( pWinPtAry[nPoints-1].x, pWinPtAry[nPoints-1].y, mnPenColor );
+}
+
+void WinSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry )
+{
+ // for NT, we can handover the array directly
+ static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
+
+ POINT const * pWinPtAry = reinterpret_cast<POINT const *>(pPtAry);
+ // for Windows 95 and its maximum number of points
+ if ( !Polygon( mrParent.getHDC(), pWinPtAry, static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
+ Polygon( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
+}
+
+void WinSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ PCONSTSALPOINT* pPtAry )
+{
+ UINT aWinPointAry[SAL_POLYPOLYCOUNT_STACKBUF];
+ UINT* pWinPointAry;
+ UINT nPolyPolyPoints = 0;
+ UINT nPoints;
+ UINT i;
+
+ if ( nPoly <= SAL_POLYPOLYCOUNT_STACKBUF )
+ pWinPointAry = aWinPointAry;
+ else
+ pWinPointAry = new UINT[nPoly];
+
+ for ( i = 0; i < static_cast<UINT>(nPoly); i++ )
+ {
+ nPoints = static_cast<UINT>(pPoints[i])+1;
+ pWinPointAry[i] = nPoints;
+ nPolyPolyPoints += nPoints;
+ }
+
+ POINT aWinPointAryAry[SAL_POLYPOLYPOINTS_STACKBUF];
+ POINT* pWinPointAryAry;
+ if ( nPolyPolyPoints <= SAL_POLYPOLYPOINTS_STACKBUF )
+ pWinPointAryAry = aWinPointAryAry;
+ else
+ pWinPointAryAry = new POINT[nPolyPolyPoints];
+ // for NT, we can handover the array directly
+ static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
+ UINT n = 0;
+ for ( i = 0; i < static_cast<UINT>(nPoly); i++ )
+ {
+ nPoints = pWinPointAry[i];
+ const SalPoint* pPolyAry = pPtAry[i];
+ memcpy( pWinPointAryAry+n, pPolyAry, (nPoints-1)*sizeof(POINT) );
+ pWinPointAryAry[n+nPoints-1] = pWinPointAryAry[n];
+ n += nPoints;
+ }
+
+ if ( !PolyPolygon( mrParent.getHDC(), pWinPointAryAry, reinterpret_cast<int*>(pWinPointAry), static_cast<UINT>(nPoly) ) &&
+ (nPolyPolyPoints > MAX_64KSALPOINTS) )
+ {
+ nPolyPolyPoints = 0;
+ nPoly = 0;
+ do
+ {
+ nPolyPolyPoints += pWinPointAry[static_cast<UINT>(nPoly)];
+ nPoly++;
+ }
+ while ( nPolyPolyPoints < MAX_64KSALPOINTS );
+ nPoly--;
+ if ( pWinPointAry[static_cast<UINT>(nPoly)] > MAX_64KSALPOINTS )
+ pWinPointAry[static_cast<UINT>(nPoly)] = MAX_64KSALPOINTS;
+ if ( nPoly == 1 )
+ Polygon( mrParent.getHDC(), pWinPointAryAry, *pWinPointAry );
+ else
+ PolyPolygon( mrParent.getHDC(), pWinPointAryAry, reinterpret_cast<int*>(pWinPointAry), nPoly );
+ }
+
+ if ( pWinPointAry != aWinPointAry )
+ delete [] pWinPointAry;
+ if ( pWinPointAryAry != aWinPointAryAry )
+ delete [] pWinPointAryAry;
+}
+
+bool WinSalGraphicsImpl::drawPolyLineBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry )
+{
+ static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
+
+ // #100127# draw an array of points which might also contain bezier control points
+ if (!nPoints)
+ return true;
+
+ const HDC hdc = mrParent.getHDC();
+
+ // TODO: profile whether the following options are faster:
+ // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp.
+ // b) convert our flag array to window's and use PolyDraw
+ MoveToEx(hdc, pPtAry->mnX, pPtAry->mnY, nullptr);
+ ++pPtAry;
+ ++pFlgAry;
+
+ for(sal_uInt32 i = 1; i < nPoints; ++i)
+ {
+ if(*pFlgAry != PolyFlags::Control)
+ {
+ LineTo(hdc, pPtAry->mnX, pPtAry->mnY);
+ }
+ else if(nPoints - i > 2)
+ {
+ PolyBezierTo(hdc, reinterpret_cast<const POINT*>(pPtAry), 3);
+ i += 2;
+ pPtAry += 2;
+ pFlgAry += 2;
+ }
+
+ ++pPtAry;
+ ++pFlgAry;
+ }
+
+ return true;
+}
+
+bool WinSalGraphicsImpl::drawPolygonBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry )
+{
+ static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
+
+ POINT aStackAry1[SAL_POLY_STACKBUF];
+ BYTE aStackAry2[SAL_POLY_STACKBUF];
+ POINT* pWinPointAry;
+ BYTE* pWinFlagAry;
+ if( nPoints > SAL_POLY_STACKBUF )
+ {
+ pWinPointAry = new POINT[ nPoints ];
+ pWinFlagAry = new BYTE[ nPoints ];
+ }
+ else
+ {
+ pWinPointAry = aStackAry1;
+ pWinFlagAry = aStackAry2;
+ }
+
+ sal_uInt32 nPoints_i32(nPoints);
+ ImplPreparePolyDraw(true, 1, &nPoints_i32, &pPtAry, &pFlgAry, pWinPointAry, pWinFlagAry);
+
+ bool bRet( false );
+
+ if( BeginPath( mrParent.getHDC() ) )
+ {
+ PolyDraw(mrParent.getHDC(), pWinPointAry, pWinFlagAry, nPoints);
+
+ if( EndPath( mrParent.getHDC() ) )
+ {
+ if( StrokeAndFillPath( mrParent.getHDC() ) )
+ bRet = true;
+ }
+ }
+
+ if( pWinPointAry != aStackAry1 )
+ {
+ delete [] pWinPointAry;
+ delete [] pWinFlagAry;
+ }
+
+ return bRet;
+}
+
+bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ const SalPoint* const* pPtAry, const PolyFlags* const* pFlgAry )
+{
+ static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
+
+ sal_uLong nCurrPoly, nTotalPoints;
+ const sal_uInt32* pCurrPoints = pPoints;
+ for( nCurrPoly=0, nTotalPoints=0; nCurrPoly<nPoly; ++nCurrPoly )
+ nTotalPoints += *pCurrPoints++;
+
+ POINT aStackAry1[SAL_POLY_STACKBUF];
+ BYTE aStackAry2[SAL_POLY_STACKBUF];
+ POINT* pWinPointAry;
+ BYTE* pWinFlagAry;
+ if( nTotalPoints > SAL_POLY_STACKBUF )
+ {
+ pWinPointAry = new POINT[ nTotalPoints ];
+ pWinFlagAry = new BYTE[ nTotalPoints ];
+ }
+ else
+ {
+ pWinPointAry = aStackAry1;
+ pWinFlagAry = aStackAry2;
+ }
+
+ ImplPreparePolyDraw(true, nPoly, pPoints, pPtAry, pFlgAry, pWinPointAry, pWinFlagAry);
+
+ bool bRet( false );
+
+ if( BeginPath( mrParent.getHDC() ) )
+ {
+ PolyDraw(mrParent.getHDC(), pWinPointAry, pWinFlagAry, nTotalPoints);
+
+ if( EndPath( mrParent.getHDC() ) )
+ {
+ if( StrokeAndFillPath( mrParent.getHDC() ) )
+ bRet = true;
+ }
+ }
+
+ if( pWinPointAry != aStackAry1 )
+ {
+ delete [] pWinPointAry;
+ delete [] pWinFlagAry;
+ }
+
+ return bRet;
+}
+
+static basegfx::B2DPoint impPixelSnap(
+ const basegfx::B2DPolygon& rPolygon,
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ basegfx::B2DHomMatrix& rObjectToDeviceInv,
+ sal_uInt32 nIndex)
+{
+ const sal_uInt32 nCount(rPolygon.count());
+
+ // get the data
+ const basegfx::B2ITuple aPrevTuple(basegfx::fround(rObjectToDevice * rPolygon.getB2DPoint((nIndex + nCount - 1) % nCount)));
+ const basegfx::B2DPoint aCurrPoint(rObjectToDevice * rPolygon.getB2DPoint(nIndex));
+ const basegfx::B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
+ const basegfx::B2ITuple aNextTuple(basegfx::fround(rObjectToDevice * rPolygon.getB2DPoint((nIndex + 1) % nCount)));
+
+ // get the states
+ const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
+ const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
+ const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
+ const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
+ const bool bSnapX(bPrevVertical || bNextVertical);
+ const bool bSnapY(bPrevHorizontal || bNextHorizontal);
+
+ if(bSnapX || bSnapY)
+ {
+ basegfx::B2DPoint aSnappedPoint(
+ bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
+ bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
+
+ if(rObjectToDeviceInv.isIdentity())
+ {
+ rObjectToDeviceInv = rObjectToDevice;
+ rObjectToDeviceInv.invert();
+ }
+
+ aSnappedPoint *= rObjectToDeviceInv;
+
+ return aSnappedPoint;
+ }
+
+ return rPolygon.getB2DPoint(nIndex);
+}
+
+static void impAddB2DPolygonToGDIPlusGraphicsPathReal(
+ Gdiplus::GraphicsPath& rGraphicsPath,
+ const basegfx::B2DPolygon& rPolygon,
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ bool bNoLineJoin,
+ bool bPixelSnapHairline)
+{
+ sal_uInt32 nCount(rPolygon.count());
+
+ if(nCount)
+ {
+ const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
+
+ if(nEdgeCount)
+ {
+ const bool bControls(rPolygon.areControlPointsUsed());
+ basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
+ basegfx::B2DHomMatrix aObjectToDeviceInv;
+
+ if(bPixelSnapHairline)
+ {
+ aCurr = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, 0);
+ }
+
+ for(sal_uInt32 a(0); a < nEdgeCount; a++)
+ {
+ const sal_uInt32 nNextIndex((a + 1) % nCount);
+ basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
+ const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a));
+ const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex));
+
+ if(bPixelSnapHairline)
+ {
+ aNext = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, nNextIndex);
+ }
+
+ if(b1stControlPointUsed || b2ndControlPointUsed)
+ {
+ basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
+ basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
+
+ // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
+ // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
+ // no 1st or 2nd control point, despite that these are mathematically correct definitions
+ // (basegfx can handle that).
+ // Caution: This error (and it's correction) might be necessary for other graphical
+ // sub-systems in a similar way.
+ // tdf#101026 The 1st attempt to create a mathematically correct replacement control
+ // vector was wrong. Best alternative is one as close as possible which means short.
+ if(!b1stControlPointUsed)
+ {
+ aCa = aCurr + ((aCb - aCurr) * 0.0005);
+ }
+ else if(!b2ndControlPointUsed)
+ {
+ aCb = aNext + ((aCa - aNext) * 0.0005);
+ }
+
+ rGraphicsPath.AddBezier(
+ static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
+ static_cast< Gdiplus::REAL >(aCa.getX()), static_cast< Gdiplus::REAL >(aCa.getY()),
+ static_cast< Gdiplus::REAL >(aCb.getX()), static_cast< Gdiplus::REAL >(aCb.getY()),
+ static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
+ }
+ else
+ {
+ rGraphicsPath.AddLine(
+ static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
+ static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
+ }
+
+ if(a + 1 < nEdgeCount)
+ {
+ aCurr = aNext;
+
+ if(bNoLineJoin)
+ {
+ rGraphicsPath.StartFigure();
+ }
+ }
+ }
+ }
+ }
+}
+
+namespace {
+
+class SystemDependentData_GraphicsPath : public basegfx::SystemDependentData
+{
+private:
+ // the path data itself
+ std::shared_ptr<Gdiplus::GraphicsPath> mpGraphicsPath;
+
+ // all other values the triangulation is based on and
+ // need to be compared with to check for data validity
+ bool mbNoLineJoin;
+ std::vector< double > maStroke;
+
+public:
+ SystemDependentData_GraphicsPath(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+ std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
+ bool bNoLineJoin,
+ const std::vector< double >* pStroke); // MM01
+
+ // read access
+ std::shared_ptr<Gdiplus::GraphicsPath>& getGraphicsPath() { return mpGraphicsPath; }
+ bool getNoLineJoin() const { return mbNoLineJoin; }
+ const std::vector< double >& getStroke() const { return maStroke; }
+
+ virtual sal_Int64 estimateUsageInBytes() const override;
+};
+
+}
+
+SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+ std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
+ bool bNoLineJoin,
+ const std::vector< double >* pStroke)
+: basegfx::SystemDependentData(rSystemDependentDataManager),
+ mpGraphicsPath(rpGraphicsPath),
+ mbNoLineJoin(bNoLineJoin),
+ maStroke()
+{
+ if(nullptr != pStroke)
+ {
+ maStroke = *pStroke;
+ }
+}
+
+sal_Int64 SystemDependentData_GraphicsPath::estimateUsageInBytes() const
+{
+ sal_Int64 nRetval(0);
+
+ if(mpGraphicsPath)
+ {
+ const INT nPointCount(mpGraphicsPath->GetPointCount());
+
+ if(0 != nPointCount)
+ {
+ // Each point has
+ // - 2 x sizeof(Gdiplus::REAL)
+ // - 1 byte (see GetPathTypes in docu)
+ nRetval = nPointCount * ((2 * sizeof(Gdiplus::REAL)) + 1);
+ }
+ }
+
+ return nRetval;
+}
+
+bool WinSalGraphicsImpl::drawPolyPolygon(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fTransparency)
+{
+ const sal_uInt32 nCount(rPolyPolygon.count());
+
+ if(!mbBrush || 0 == nCount || fTransparency < 0.0 || fTransparency > 1.0)
+ {
+ return true;
+ }
+
+ Gdiplus::Graphics aGraphics(mrParent.getHDC());
+ const sal_uInt8 aTrans(sal_uInt8(255) - static_cast<sal_uInt8>(basegfx::fround(fTransparency * 255.0)));
+ const Gdiplus::Color aTestColor(aTrans, maFillColor.GetRed(), maFillColor.GetGreen(), maFillColor.GetBlue());
+ const Gdiplus::SolidBrush aSolidBrush(aTestColor.GetValue());
+
+ // Set full (Object-to-Device) transformation - if used
+ if(rObjectToDevice.isIdentity())
+ {
+ aGraphics.ResetTransform();
+ }
+ else
+ {
+ Gdiplus::Matrix aMatrix;
+
+ aMatrix.SetElements(
+ rObjectToDevice.get(0, 0),
+ rObjectToDevice.get(1, 0),
+ rObjectToDevice.get(0, 1),
+ rObjectToDevice.get(1, 1),
+ rObjectToDevice.get(0, 2),
+ rObjectToDevice.get(1, 2));
+ aGraphics.SetTransform(&aMatrix);
+ }
+
+ // prepare local instance of Gdiplus::GraphicsPath
+ std::shared_ptr<Gdiplus::GraphicsPath> pGraphicsPath;
+
+ // try to access buffered data
+ std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
+ rPolyPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>());
+
+ if(pSystemDependentData_GraphicsPath)
+ {
+ // copy buffered data
+ pGraphicsPath = pSystemDependentData_GraphicsPath->getGraphicsPath();
+ }
+ else
+ {
+ // Note: In principle we could use the same buffered geometry at line
+ // and fill polygons. Checked that in a first try, used
+ // GraphicsPath::AddPath from Gdiplus combined with below used
+ // StartFigure/CloseFigure, worked well (thus the line-draw version
+ // may create non-closed partial Polygon data).
+ //
+ // But in current reality it gets not used due to e.g.
+ // SdrPathPrimitive2D::create2DDecomposition creating transformed
+ // line and fill polygon-primitives (what could be changed).
+ //
+ // There will probably be more hindrances here in other rendering paths
+ // which could all be found - intention to do this would be: Use more
+ // transformations, less modifications of B2DPolygons/B2DPolyPolygons.
+ //
+ // A fix for SdrPathPrimitive2D would be to create the sub-geometry
+ // and embed into a TransformPrimitive2D containing the transformation.
+ //
+ // A 2nd problem is that the NoLineJoin mode (basegfx::B2DLineJoin::NONE
+ // && !bIsHairline) creates polygon fill infos that are not reusable
+ // for the fill case (see ::drawPolyLine below) - thus we would need a
+ // bool and/or two system-dependent paths buffered - doable, but complicated.
+ //
+ // All in all: Make B2DPolyPolygon a SystemDependentDataProvider and buffer
+ // the whole to-be-filled PolyPolygon independent from evtl. line-polygon
+ // (at least for now...)
+
+ // create data
+ pGraphicsPath = std::make_shared<Gdiplus::GraphicsPath>();
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ if(0 != a)
+ {
+ // #i101491# not needed for first run
+ pGraphicsPath->StartFigure();
+ }
+
+ impAddB2DPolygonToGDIPlusGraphicsPathReal(
+ *pGraphicsPath,
+ rPolyPolygon.getB2DPolygon(a),
+ rObjectToDevice, // not used due to the two 'false' values below, but to not forget later
+ false,
+ false);
+
+ pGraphicsPath->CloseFigure();
+ }
+
+ // add to buffering mechanism
+ rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
+ ImplGetSystemDependentDataManager(),
+ pGraphicsPath,
+ false,
+ nullptr);
+ }
+
+ if(mrParent.getAntiAliasB2DDraw())
+ {
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
+ }
+ else
+ {
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
+ }
+
+ if(mrParent.isPrinter())
+ {
+ // #i121591#
+ // Normally GdiPlus should not be used for printing at all since printers cannot
+ // print transparent filled polygon geometry and normally this does not happen
+ // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
+ // and no transparent parts should remain for printing. But this can be overridden
+ // by the user and thus happens. This call can only come (currently) from
+ // OutputDevice::DrawTransparent, see comments there with the same TaskID.
+ // If it is used, the mapping for the printer is wrong and needs to be corrected. I
+ // checked that there is *no* transformation set and estimated that a stable factor
+ // dependent of the printer's DPI is used. Create and set a transformation here to
+ // correct this.
+ const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
+ const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
+
+ // Now the transformation maybe/is already used (see above), so do
+ // modify it without resetting to not destroy it.
+ // I double-checked with MS docu that Gdiplus::MatrixOrderAppend does what
+ // we need - in our notation, would be a multiply from left to execute
+ // current transform first and this scale last.
+ // I tried to trigger this code using Print from the menu and various
+ // targets, but got no hit, thus maybe obsolete anyways. If someone knows
+ // more, feel free to remove it.
+ // One more hint: This *may* also be needed now in ::drawPolyLine below
+ // since it also uses transformations now.
+ //
+ // aGraphics.ResetTransform();
+
+ aGraphics.ScaleTransform(
+ Gdiplus::REAL(100.0) / aDpiX,
+ Gdiplus::REAL(100.0) / aDpiY,
+ Gdiplus::MatrixOrderAppend);
+ }
+
+ // use created or buffered data
+ aGraphics.FillPath(
+ &aSolidBrush,
+ &(*pGraphicsPath));
+
+ return true;
+}
+
+bool WinSalGraphicsImpl::drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon& rPolygon,
+ double fTransparency,
+ double fLineWidth,
+ const std::vector< double >* pStroke, // MM01
+ basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
+{
+ // MM01 check done for simple reasons
+ if(!mbPen || !rPolygon.count() || fTransparency < 0.0 || fTransparency > 1.0)
+ {
+ return true;
+ }
+
+ // need to check/handle LineWidth when ObjectToDevice transformation is used
+ const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity());
+ const bool bIsHairline(fLineWidth == 0);
+
+ // tdf#124848 calculate-back logical LineWidth for a hairline
+ // since this implementation hands over the transformation to
+ // the graphic sub-system
+ if(bIsHairline)
+ {
+ fLineWidth = 1.0;
+
+ if(!bObjectToDeviceIsIdentity)
+ {
+ basegfx::B2DHomMatrix aObjectToDeviceInv(rObjectToDevice);
+ aObjectToDeviceInv.invert();
+ fLineWidth = (aObjectToDeviceInv * basegfx::B2DVector(fLineWidth, 0)).getLength();
+ }
+ }
+
+ Gdiplus::Graphics aGraphics(mrParent.getHDC());
+ const sal_uInt8 aTrans = static_cast<sal_uInt8>(basegfx::fround( 255 * (1.0 - fTransparency) ));
+ const Gdiplus::Color aTestColor(aTrans, maLineColor.GetRed(), maLineColor.GetGreen(), maLineColor.GetBlue());
+ Gdiplus::Pen aPen(aTestColor.GetValue(), Gdiplus::REAL(fLineWidth));
+ bool bNoLineJoin(false);
+
+ // Set full (Object-to-Device) transformation - if used
+ if(bObjectToDeviceIsIdentity)
+ {
+ aGraphics.ResetTransform();
+ }
+ else
+ {
+ Gdiplus::Matrix aMatrix;
+
+ aMatrix.SetElements(
+ rObjectToDevice.get(0, 0),
+ rObjectToDevice.get(1, 0),
+ rObjectToDevice.get(0, 1),
+ rObjectToDevice.get(1, 1),
+ rObjectToDevice.get(0, 2),
+ rObjectToDevice.get(1, 2));
+ aGraphics.SetTransform(&aMatrix);
+ }
+
+ switch(eLineJoin)
+ {
+ case basegfx::B2DLineJoin::NONE :
+ {
+ if(!bIsHairline)
+ {
+ bNoLineJoin = true;
+ }
+ break;
+ }
+ case basegfx::B2DLineJoin::Bevel :
+ {
+ aPen.SetLineJoin(Gdiplus::LineJoinBevel);
+ break;
+ }
+ case basegfx::B2DLineJoin::Miter :
+ {
+ const Gdiplus::REAL aMiterLimit(1.0/sin(fMiterMinimumAngle/2.0));
+
+ aPen.SetMiterLimit(aMiterLimit);
+ // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
+ // graphics, somewhere clipped in some distance from the edge point, dependent
+ // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
+ // that instead
+ aPen.SetLineJoin(Gdiplus::LineJoinMiterClipped);
+ break;
+ }
+ case basegfx::B2DLineJoin::Round :
+ {
+ aPen.SetLineJoin(Gdiplus::LineJoinRound);
+ break;
+ }
+ }
+
+ switch(eLineCap)
+ {
+ default: /*css::drawing::LineCap_BUTT*/
+ {
+ // nothing to do
+ break;
+ }
+ case css::drawing::LineCap_ROUND:
+ {
+ aPen.SetStartCap(Gdiplus::LineCapRound);
+ aPen.SetEndCap(Gdiplus::LineCapRound);
+ break;
+ }
+ case css::drawing::LineCap_SQUARE:
+ {
+ aPen.SetStartCap(Gdiplus::LineCapSquare);
+ aPen.SetEndCap(Gdiplus::LineCapSquare);
+ break;
+ }
+ }
+
+ // prepare local instance of Gdiplus::GraphicsPath
+ std::shared_ptr<Gdiplus::GraphicsPath> pGraphicsPath;
+
+ // try to access buffered data
+ std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
+ rPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>());
+
+ // MM01 need to do line dashing as fallback stuff here now
+ const double fDotDashLength(nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
+ const bool bStrokeUsed(0.0 != fDotDashLength);
+ assert(!bStrokeUsed || (bStrokeUsed && pStroke));
+
+ // MM01 decide if to stroke directly
+ static bool bDoDirectGDIPlusStroke(true);
+
+ // activate to stroke directly
+ if(bDoDirectGDIPlusStroke && bStrokeUsed)
+ {
+ // tdf#124848 the fix of tdf#130478 that was needed here before
+ // gets much easier when already handling the hairline case above,
+ // the back-calculated logical linewidth is already here, just use it.
+ // Still be careful - a zero LineWidth *should* not happen, but...
+ std::vector<Gdiplus::REAL> aDashArray(pStroke->size());
+ const double fFactor(fLineWidth == 0 ? 1.0 : 1.0 / fLineWidth);
+
+ for(size_t a(0); a < pStroke->size(); a++)
+ {
+ aDashArray[a] = Gdiplus::REAL((*pStroke)[a] * fFactor);
+ }
+
+ aPen.SetDashCap(Gdiplus::DashCapFlat);
+ aPen.SetDashOffset(Gdiplus::REAL(0.0));
+ aPen.SetDashPattern(aDashArray.data(), aDashArray.size());
+ }
+
+ if(!bDoDirectGDIPlusStroke && pSystemDependentData_GraphicsPath)
+ {
+ // MM01 - check on stroke change. Used against not used, or if oth used,
+ // equal or different? Triangulation geometry creation depends heavily
+ // on stroke, independent of being transformation independent
+ const bool bStrokeWasUsed(!pSystemDependentData_GraphicsPath->getStroke().empty());
+
+ if(bStrokeWasUsed != bStrokeUsed
+ || (bStrokeUsed && *pStroke != pSystemDependentData_GraphicsPath->getStroke()))
+ {
+ // data invalid, forget
+ pSystemDependentData_GraphicsPath.reset();
+ }
+ }
+
+ if(pSystemDependentData_GraphicsPath)
+ {
+ // check data validity
+ if (pSystemDependentData_GraphicsPath->getNoLineJoin() != bNoLineJoin
+ || bPixelSnapHairline /*tdf#124700*/)
+ {
+ // data invalid, forget
+ pSystemDependentData_GraphicsPath.reset();
+ }
+ }
+
+ if(pSystemDependentData_GraphicsPath)
+ {
+ // copy buffered data
+ pGraphicsPath = pSystemDependentData_GraphicsPath->getGraphicsPath();
+ }
+ else
+ {
+ // fill data of buffered data
+ pGraphicsPath = std::make_shared<Gdiplus::GraphicsPath>();
+
+ if(!bDoDirectGDIPlusStroke && bStrokeUsed)
+ {
+ // MM01 need to do line dashing as fallback stuff here now
+ basegfx::B2DPolyPolygon aPolyPolygonLine;
+
+ // apply LineStyle
+ basegfx::utils::applyLineDashing(
+ rPolygon, // source
+ *pStroke, // pattern
+ &aPolyPolygonLine, // target for lines
+ nullptr, // target for gaps
+ fDotDashLength); // full length if available
+
+ // MM01 checked/verified, ok
+ for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
+ {
+ const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
+ pGraphicsPath->StartFigure();
+ impAddB2DPolygonToGDIPlusGraphicsPathReal(
+ *pGraphicsPath,
+ aPolyLine,
+ rObjectToDevice,
+ bNoLineJoin,
+ bPixelSnapHairline);
+ }
+ }
+ else
+ {
+ // no line dashing or direct stroke, just copy
+ impAddB2DPolygonToGDIPlusGraphicsPathReal(
+ *pGraphicsPath,
+ rPolygon,
+ rObjectToDevice,
+ bNoLineJoin,
+ bPixelSnapHairline);
+
+ if(rPolygon.isClosed() && !bNoLineJoin)
+ {
+ // #i101491# needed to create the correct line joins
+ pGraphicsPath->CloseFigure();
+ }
+ }
+
+ // add to buffering mechanism
+ if (!bPixelSnapHairline /*tdf#124700*/)
+ {
+ rPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
+ ImplGetSystemDependentDataManager(),
+ pGraphicsPath,
+ bNoLineJoin,
+ pStroke);
+ }
+ }
+
+ if(mrParent.getAntiAliasB2DDraw())
+ {
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
+ }
+ else
+ {
+ aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
+ }
+
+ if(mrParent.isPrinter())
+ {
+ // tdf#122384 As mentioned above in WinSalGraphicsImpl::drawPolyPolygon
+ // (look for 'One more hint: This *may* also be needed now in'...).
+ // See comments in same spot above *urgently* before doing changes here,
+ // these comments are *still fully valid* at this place (!)
+ const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
+ const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
+
+ aGraphics.ScaleTransform(
+ Gdiplus::REAL(100.0) / aDpiX,
+ Gdiplus::REAL(100.0) / aDpiY,
+ Gdiplus::MatrixOrderAppend);
+ }
+
+ aGraphics.DrawPath(
+ &aPen,
+ &(*pGraphicsPath));
+
+ return true;
+}
+
+static void paintToGdiPlus(
+ Gdiplus::Graphics& rGraphics,
+ const SalTwoRect& rTR,
+ Gdiplus::Bitmap& rBitmap)
+{
+ // only parts of source are used
+ Gdiplus::PointF aDestPoints[3];
+ Gdiplus::ImageAttributes aAttributes;
+
+ // define target region as parallelogram
+ aDestPoints[0].X = Gdiplus::REAL(rTR.mnDestX);
+ aDestPoints[0].Y = Gdiplus::REAL(rTR.mnDestY);
+ aDestPoints[1].X = Gdiplus::REAL(rTR.mnDestX + rTR.mnDestWidth);
+ aDestPoints[1].Y = Gdiplus::REAL(rTR.mnDestY);
+ aDestPoints[2].X = Gdiplus::REAL(rTR.mnDestX);
+ aDestPoints[2].Y = Gdiplus::REAL(rTR.mnDestY + rTR.mnDestHeight);
+
+ aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
+
+ rGraphics.DrawImage(
+ &rBitmap,
+ aDestPoints,
+ 3,
+ Gdiplus::REAL(rTR.mnSrcX),
+ Gdiplus::REAL(rTR.mnSrcY),
+ Gdiplus::REAL(rTR.mnSrcWidth),
+ Gdiplus::REAL(rTR.mnSrcHeight),
+ Gdiplus::UnitPixel,
+ &aAttributes);
+}
+
+static void setInterpolationMode(
+ Gdiplus::Graphics& rGraphics,
+ long rSrcWidth,
+ long rDestWidth,
+ long rSrcHeight,
+ long rDestHeight)
+{
+ const bool bSameWidth(rSrcWidth == rDestWidth);
+ const bool bSameHeight(rSrcHeight == rDestHeight);
+
+ if(bSameWidth && bSameHeight)
+ {
+ rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeInvalid);
+ }
+ else if(rDestWidth > rSrcWidth && rDestHeight > rSrcHeight)
+ {
+ rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
+ }
+ else if(rDestWidth < rSrcWidth && rDestHeight < rSrcHeight)
+ {
+ rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
+ }
+ else
+ {
+ rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
+ }
+}
+
+bool WinSalGraphicsImpl::TryDrawBitmapGDIPlus(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap)
+{
+ if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
+ {
+ assert(dynamic_cast<const WinSalBitmap*>(&rSrcBitmap));
+
+ const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
+ std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap());
+
+ if(aARGB.get())
+ {
+ Gdiplus::Graphics aGraphics(mrParent.getHDC());
+
+ setInterpolationMode(
+ aGraphics,
+ rTR.mnSrcWidth,
+ rTR.mnDestWidth,
+ rTR.mnSrcHeight,
+ rTR.mnDestHeight);
+
+ paintToGdiPlus(
+ aGraphics,
+ rTR,
+ *aARGB);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool WinSalGraphicsImpl::blendBitmap(
+ const SalTwoRect&,
+ const SalBitmap&)
+{
+ return false;
+}
+
+bool WinSalGraphicsImpl::blendAlphaBitmap(
+ const SalTwoRect&,
+ const SalBitmap&,
+ const SalBitmap&,
+ const SalBitmap&)
+{
+ return false;
+}
+
+bool WinSalGraphicsImpl::drawAlphaBitmap(
+ const SalTwoRect& rTR,
+ const SalBitmap& rSrcBitmap,
+ const SalBitmap& rAlphaBmp)
+{
+ if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
+ {
+ assert(dynamic_cast<const WinSalBitmap*>(&rSrcBitmap));
+ assert(dynamic_cast<const WinSalBitmap*>(&rAlphaBmp));
+
+ const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
+ const WinSalBitmap& rSalAlpha = static_cast< const WinSalBitmap& >(rAlphaBmp);
+ std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap(&rSalAlpha));
+
+ if(aARGB.get())
+ {
+ Gdiplus::Graphics aGraphics(mrParent.getHDC());
+
+ setInterpolationMode(
+ aGraphics,
+ rTR.mnSrcWidth,
+ rTR.mnDestWidth,
+ rTR.mnSrcHeight,
+ rTR.mnDestHeight);
+
+ paintToGdiPlus(
+ aGraphics,
+ rTR,
+ *aARGB);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool WinSalGraphicsImpl::drawTransformedBitmap(
+ const basegfx::B2DPoint& rNull,
+ const basegfx::B2DPoint& rX,
+ const basegfx::B2DPoint& rY,
+ const SalBitmap& rSourceBitmap,
+ const SalBitmap* pAlphaBitmap)
+{
+ assert(dynamic_cast<const WinSalBitmap*>(&rSourceBitmap));
+ assert(!pAlphaBitmap || dynamic_cast<const WinSalBitmap*>(pAlphaBitmap));
+
+ const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSourceBitmap);
+ const WinSalBitmap* pSalAlpha = static_cast< const WinSalBitmap* >(pAlphaBitmap);
+ std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap(pSalAlpha));
+
+ if(aARGB.get())
+ {
+ const long nSrcWidth(aARGB->GetWidth());
+ const long nSrcHeight(aARGB->GetHeight());
+
+ if(nSrcWidth && nSrcHeight)
+ {
+ const long nDestWidth(basegfx::fround(basegfx::B2DVector(rX - rNull).getLength()));
+ const long nDestHeight(basegfx::fround(basegfx::B2DVector(rY - rNull).getLength()));
+
+ if(nDestWidth && nDestHeight)
+ {
+ Gdiplus::Graphics aGraphics(mrParent.getHDC());
+ Gdiplus::PointF aDestPoints[3];
+ Gdiplus::ImageAttributes aAttributes;
+
+ setInterpolationMode(
+ aGraphics,
+ nSrcWidth,
+ nDestWidth,
+ nSrcHeight,
+ nDestHeight);
+
+ // this mode is only capable of drawing the whole bitmap to a parallelogram
+ aDestPoints[0].X = Gdiplus::REAL(rNull.getX());
+ aDestPoints[0].Y = Gdiplus::REAL(rNull.getY());
+ aDestPoints[1].X = Gdiplus::REAL(rX.getX());
+ aDestPoints[1].Y = Gdiplus::REAL(rX.getY());
+ aDestPoints[2].X = Gdiplus::REAL(rY.getX());
+ aDestPoints[2].Y = Gdiplus::REAL(rY.getY());
+
+ aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
+
+ aGraphics.DrawImage(
+ aARGB.get(),
+ aDestPoints,
+ 3,
+ Gdiplus::REAL(0.0),
+ Gdiplus::REAL(0.0),
+ Gdiplus::REAL(nSrcWidth),
+ Gdiplus::REAL(nSrcHeight),
+ Gdiplus::UnitPixel,
+ &aAttributes);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool WinSalGraphicsImpl::drawGradient(const tools::PolyPolygon& /*rPolygon*/,
+ const Gradient& /*rGradient*/)
+{
+ return false;
+}
+
+bool WinSalGraphicsImpl::supportsOperation(OutDevSupportType eType) const
+{
+ static bool bAllowForTest(true);
+ bool bRet = false;
+
+ switch (eType)
+ {
+ case OutDevSupportType::TransparentRect:
+ bRet = mrParent.mbVirDev || mrParent.mbWindow;
+ break;
+ case OutDevSupportType::B2DDraw:
+ bRet = bAllowForTest;
+ break;
+ default:
+ break;
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/gdiimpl.hxx b/vcl/win/gdi/gdiimpl.hxx
new file mode 100644
index 000000000..748afbcf0
--- /dev/null
+++ b/vcl/win/gdi/gdiimpl.hxx
@@ -0,0 +1,250 @@
+/* -*- 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 .
+ */
+
+#ifndef INCLUDED_VCL_WIN_GDI_GDIIMPL_HXX
+#define INCLUDED_VCL_WIN_GDI_GDIIMPL_HXX
+
+#include <salgdiimpl.hxx>
+#include <win/salgdi.h>
+#include <win/wingdiimpl.hxx>
+
+#include <vcl/gradient.hxx>
+
+#include <svsys.h>
+#include <ControlCacheKey.hxx>
+
+class WinSalGraphics;
+
+class WinSalGraphicsImpl : public SalGraphicsImpl, public WinSalGraphicsImplBase
+{
+private:
+
+ WinSalGraphics& mrParent;
+ bool mbXORMode : 1; // _every_ output with RasterOp XOR
+ bool mbPen : 1; // is Pen (FALSE == NULL_PEN)
+ HPEN mhPen; // Pen
+ bool mbStockPen : 1; // is Pen a stockpen
+ bool mbBrush : 1; // is Brush (FALSE == NULL_BRUSH)
+ bool mbStockBrush : 1; // is Brush a stockbrush
+ HBRUSH mhBrush; // Brush
+ COLORREF mnPenColor; // PenColor
+ COLORREF mnBrushColor; // BrushColor
+
+ // remember RGB values for SetLineColor/SetFillColor
+ Color maLineColor;
+ Color maFillColor;
+
+ bool TryDrawBitmapGDIPlus(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap);
+ void DrawPixelImpl(long nX, long nY, COLORREF crColor);
+
+ HPEN SearchStockPen(COLORREF nPenColor);
+ HPEN MakePen(Color nColor);
+ void ResetPen(HPEN hNewPen);
+
+ HBRUSH SearchStockBrush(COLORREF nBrushColor);
+ HBRUSH MakeBrush(Color nColor);
+ void ResetBrush(HBRUSH hNewBrush);
+public:
+
+ explicit WinSalGraphicsImpl(WinSalGraphics& rParent);
+
+ virtual ~WinSalGraphicsImpl() override;
+
+ virtual void Init() override;
+
+ virtual void freeResources() override;
+
+ virtual OUString getRenderBackendName() const override { return "gdi"; }
+
+ virtual bool setClipRegion( const vcl::Region& ) override;
+ //
+ // get the depth of the device
+ virtual sal_uInt16 GetBitCount() const override;
+
+ // get the width of the device
+ virtual long GetGraphicsWidth() const override;
+
+ // set the clip region to empty
+ virtual void ResetClipRegion() override;
+
+ // set the line color to transparent (= don't draw lines)
+
+ virtual void SetLineColor() override;
+
+ // set the line color to a specific color
+ virtual void SetLineColor( Color nColor ) override;
+
+ // set the fill color to transparent (= don't fill)
+ virtual void SetFillColor() override;
+
+ // set the fill color to a specific color, shapes will be
+ // filled accordingly
+ virtual void SetFillColor( Color nColor ) override;
+
+ // enable/disable XOR drawing
+ virtual void SetXORMode( bool bSet, bool bInvertOnly ) override;
+
+ // set line color for raster operations
+ virtual void SetROPLineColor( SalROPColor nROPColor ) override;
+
+ // set fill color for raster operations
+ virtual void SetROPFillColor( SalROPColor nROPColor ) override;
+
+ // draw --> LineColor and FillColor and RasterOp and ClipRegion
+ virtual void drawPixel( long nX, long nY ) override;
+ virtual void drawPixel( long nX, long nY, Color nColor ) override;
+
+ virtual void drawLine( long nX1, long nY1, long nX2, long nY2 ) override;
+
+ virtual void drawRect( long nX, long nY, long nWidth, long nHeight ) override;
+
+ virtual void drawPolyLine( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;
+
+ virtual void drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ) override;
+
+ virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, PCONSTSALPOINT* pPtAry ) override;
+
+ virtual bool drawPolyPolygon(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon&,
+ double fTransparency) override;
+
+ virtual bool drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon&,
+ double fTransparency,
+ double fLineWidth,
+ const std::vector< double >* pStroke, // MM01
+ basegfx::B2DLineJoin,
+ css::drawing::LineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline) override;
+
+ virtual bool drawPolyLineBezier(
+ sal_uInt32 nPoints,
+ const SalPoint* pPtAry,
+ const PolyFlags* pFlgAry ) override;
+
+ virtual bool drawPolygonBezier(
+ sal_uInt32 nPoints,
+ const SalPoint* pPtAry,
+ const PolyFlags* pFlgAry ) override;
+
+ virtual bool drawPolyPolygonBezier(
+ sal_uInt32 nPoly,
+ const sal_uInt32* pPoints,
+ const SalPoint* const* pPtAry,
+ const PolyFlags* const* pFlgAry ) override;
+
+ // CopyArea --> No RasterOp, but ClipRegion
+ virtual void copyArea(
+ long nDestX, long nDestY,
+ long nSrcX, long nSrcY,
+ long nSrcWidth, long nSrcHeight, bool bWindowInvalidate ) override;
+
+ // CopyBits and DrawBitmap --> RasterOp and ClipRegion
+ // CopyBits() --> pSrcGraphics == NULL, then CopyBits on same Graphics
+ virtual void copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics ) override;
+
+ virtual void drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap ) override;
+
+ virtual void drawBitmap(
+ const SalTwoRect& rPosAry,
+ const SalBitmap& rSalBitmap,
+ const SalBitmap& rMaskBitmap ) override;
+
+ virtual void drawMask(
+ const SalTwoRect& rPosAry,
+ const SalBitmap& rSalBitmap,
+ Color nMaskColor ) override;
+
+ virtual std::shared_ptr<SalBitmap> getBitmap( long nX, long nY, long nWidth, long nHeight ) override;
+
+ virtual Color getPixel( long nX, long nY ) override;
+
+ // invert --> ClipRegion (only Windows or VirDevs)
+ virtual void invert(
+ long nX, long nY,
+ long nWidth, long nHeight,
+ SalInvert nFlags) override;
+
+ virtual void invert( sal_uInt32 nPoints, const SalPoint* pPtAry, SalInvert nFlags ) override;
+
+ virtual bool drawEPS(
+ long nX, long nY,
+ long nWidth, long nHeight,
+ void* pPtr,
+ sal_uInt32 nSize ) override;
+
+ virtual bool blendBitmap(
+ const SalTwoRect&,
+ const SalBitmap& rBitmap ) override;
+
+ virtual bool blendAlphaBitmap(
+ const SalTwoRect&,
+ const SalBitmap& rSrcBitmap,
+ const SalBitmap& rMaskBitmap,
+ const SalBitmap& rAlphaBitmap ) override;
+
+ /** Render bitmap with alpha channel
+
+ @param rSourceBitmap
+ Source bitmap to blit
+
+ @param rAlphaBitmap
+ Alpha channel to use for blitting
+
+ @return true, if the operation succeeded, and false
+ otherwise. In this case, clients should try to emulate alpha
+ compositing themselves
+ */
+ virtual bool drawAlphaBitmap(
+ const SalTwoRect&,
+ const SalBitmap& rSourceBitmap,
+ const SalBitmap& rAlphaBitmap ) override;
+
+ /** draw transformed bitmap (maybe with alpha) where Null, X, Y define the coordinate system */
+ virtual bool drawTransformedBitmap(
+ const basegfx::B2DPoint& rNull,
+ const basegfx::B2DPoint& rX,
+ const basegfx::B2DPoint& rY,
+ const SalBitmap& rSourceBitmap,
+ const SalBitmap* pAlphaBitmap) override;
+
+ /** Render solid rectangle with given transparency
+
+ @param nTransparency
+ Transparency value (0-255) to use. 0 blits and opaque, 255 a
+ fully transparent rectangle
+ */
+ virtual bool drawAlphaRect(
+ long nX, long nY,
+ long nWidth, long nHeight,
+ sal_uInt8 nTransparency ) override;
+
+
+ virtual bool drawGradient(const tools::PolyPolygon& rPolygon,
+ const Gradient& rGradient) override;
+
+ virtual bool supportsOperation(OutDevSupportType eType) const override;
+};
+
+#endif // INCLUDED_VCL_WIN_GDI_GDIIMPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/salbmp.cxx b/vcl/win/gdi/salbmp.cxx
new file mode 100644
index 000000000..f2b21a666
--- /dev/null
+++ b/vcl/win/gdi/salbmp.cxx
@@ -0,0 +1,1067 @@
+/*
+ * 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 <svsys.h>
+#include <vcl/bitmap.hxx>
+#include <vcl/BitmapAccessMode.hxx>
+#include <vcl/BitmapBuffer.hxx>
+#include <vcl/BitmapPalette.hxx>
+#include <vcl/ColorMask.hxx>
+#include <vcl/Scanline.hxx>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <win/wincomp.hxx>
+#include <win/salgdi.h>
+#include <win/saldata.hxx>
+#include <win/salbmp.h>
+#include <string.h>
+#include <vcl/timer.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <sal/log.hxx>
+#include <tools/helpers.hxx>
+#include <map>
+
+#if defined _MSC_VER
+#ifndef min
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef max
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+#endif
+
+#include <prewin.h>
+#include <gdiplus.h>
+#include <postwin.h>
+
+#if defined _MSC_VER
+#undef min
+#undef max
+#endif
+
+static void ImplSetPixel4( sal_uInt8* pScanline, long nX, const BYTE cIndex )
+{
+ BYTE& rByte = pScanline[ nX >> 1 ];
+
+ if ( nX & 1 )
+ {
+ rByte &= 0xf0;
+ rByte |= cIndex & 0x0f;
+ }
+ else
+ {
+ rByte &= 0x0f;
+ rByte |= cIndex << 4;
+ }
+}
+
+WinSalBitmap::WinSalBitmap()
+: SalBitmap(),
+ basegfx::SystemDependentDataHolder(),
+ maSize(),
+ mhDIB(nullptr),
+ mhDDB(nullptr),
+ mnBitCount(0)
+{
+}
+
+WinSalBitmap::~WinSalBitmap()
+{
+ Destroy();
+}
+
+void WinSalBitmap::Destroy()
+{
+ if( mhDIB )
+ GlobalFree( mhDIB );
+ else if( mhDDB )
+ DeleteObject( mhDDB );
+
+ maSize = Size();
+ mnBitCount = 0;
+}
+
+namespace {
+
+class SystemDependentData_GdiPlusBitmap : public basegfx::SystemDependentData
+{
+private:
+ std::shared_ptr<Gdiplus::Bitmap> mpGdiPlusBitmap;
+ const WinSalBitmap* mpAssociatedAlpha;
+
+public:
+ SystemDependentData_GdiPlusBitmap(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+ const std::shared_ptr<Gdiplus::Bitmap>& rGdiPlusBitmap,
+ const WinSalBitmap* pAssociatedAlpha);
+
+ const WinSalBitmap* getAssociatedAlpha() const { return mpAssociatedAlpha; }
+ const std::shared_ptr<Gdiplus::Bitmap>& getGdiPlusBitmap() const { return mpGdiPlusBitmap; }
+
+ virtual sal_Int64 estimateUsageInBytes() const override;
+};
+
+}
+
+SystemDependentData_GdiPlusBitmap::SystemDependentData_GdiPlusBitmap(
+ basegfx::SystemDependentDataManager& rSystemDependentDataManager,
+ const std::shared_ptr<Gdiplus::Bitmap>& rGdiPlusBitmap,
+ const WinSalBitmap* pAssociatedAlpha)
+: basegfx::SystemDependentData(rSystemDependentDataManager),
+ mpGdiPlusBitmap(rGdiPlusBitmap),
+ mpAssociatedAlpha(pAssociatedAlpha)
+{
+}
+
+sal_Int64 SystemDependentData_GdiPlusBitmap::estimateUsageInBytes() const
+{
+ sal_Int64 nRetval(0);
+
+ if(mpGdiPlusBitmap)
+ {
+ const UINT nWidth(mpGdiPlusBitmap->GetWidth());
+ const UINT nHeight(mpGdiPlusBitmap->GetHeight());
+
+ if(0 != nWidth && 0 != nHeight)
+ {
+ nRetval = nWidth * nHeight;
+
+ switch(mpGdiPlusBitmap->GetPixelFormat())
+ {
+ case PixelFormat1bppIndexed:
+ nRetval /= 8;
+ break;
+ case PixelFormat4bppIndexed:
+ nRetval /= 4;
+ break;
+ case PixelFormat16bppGrayScale:
+ case PixelFormat16bppRGB555:
+ case PixelFormat16bppRGB565:
+ case PixelFormat16bppARGB1555:
+ nRetval *= 2;
+ break;
+ case PixelFormat24bppRGB:
+ nRetval *= 3;
+ break;
+ case PixelFormat32bppRGB:
+ case PixelFormat32bppARGB:
+ case PixelFormat32bppPARGB:
+ case PixelFormat32bppCMYK:
+ nRetval *= 4;
+ break;
+ case PixelFormat48bppRGB:
+ nRetval *= 6;
+ break;
+ case PixelFormat64bppARGB:
+ case PixelFormat64bppPARGB:
+ nRetval *= 8;
+ break;
+ default:
+ case PixelFormat8bppIndexed:
+ break;
+ }
+ }
+ }
+
+ return nRetval;
+}
+
+std::shared_ptr< Gdiplus::Bitmap > WinSalBitmap::ImplGetGdiPlusBitmap(const WinSalBitmap* pAlphaSource) const
+{
+ std::shared_ptr< Gdiplus::Bitmap > aRetval;
+
+ // try to access buffered data
+ std::shared_ptr<SystemDependentData_GdiPlusBitmap> pSystemDependentData_GdiPlusBitmap(
+ getSystemDependentData<SystemDependentData_GdiPlusBitmap>());
+
+ if(pSystemDependentData_GdiPlusBitmap)
+ {
+ // check data validity
+ if(pSystemDependentData_GdiPlusBitmap->getAssociatedAlpha() != pAlphaSource
+ || 0 == maSize.Width()
+ || 0 == maSize.Height())
+ {
+ // #122350# if associated alpha with which the GDIPlus was constructed has changed
+ // it is necessary to remove it from buffer, reset reference to it and reconstruct
+ // data invalid, forget
+ pSystemDependentData_GdiPlusBitmap.reset();
+ }
+ }
+
+ if(pSystemDependentData_GdiPlusBitmap)
+ {
+ // use from buffer
+ aRetval = pSystemDependentData_GdiPlusBitmap->getGdiPlusBitmap();
+ }
+ else if(!maSize.IsEmpty())
+ {
+ // create and set data
+ const WinSalBitmap* pAssociatedAlpha(nullptr);
+
+ if(pAlphaSource)
+ {
+ aRetval.reset(const_cast< WinSalBitmap* >(this)->ImplCreateGdiPlusBitmap(*pAlphaSource));
+ pAssociatedAlpha = pAlphaSource;
+ }
+ else
+ {
+ aRetval.reset(const_cast< WinSalBitmap* >(this)->ImplCreateGdiPlusBitmap());
+ pAssociatedAlpha = nullptr;
+ }
+
+ // add to buffering mechanism
+ addOrReplaceSystemDependentData<SystemDependentData_GdiPlusBitmap>(
+ ImplGetSystemDependentDataManager(),
+ aRetval,
+ pAssociatedAlpha);
+ }
+
+ return aRetval;
+}
+
+Gdiplus::Bitmap* WinSalBitmap::ImplCreateGdiPlusBitmap()
+{
+ Gdiplus::Bitmap* pRetval(nullptr);
+ WinSalBitmap* pSalRGB = this;
+ WinSalBitmap* pExtraWinSalRGB = nullptr;
+
+ if(!pSalRGB->ImplGethDIB())
+ {
+ // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap
+ pExtraWinSalRGB = new WinSalBitmap();
+ pExtraWinSalRGB->Create(*pSalRGB, pSalRGB->GetBitCount());
+ pSalRGB = pExtraWinSalRGB;
+ }
+
+ BitmapBuffer* pRGB = pSalRGB->AcquireBuffer(BitmapAccessMode::Read);
+ std::unique_ptr<BitmapBuffer> pExtraRGB;
+
+ if(pRGB && ScanlineFormat::N24BitTcBgr != RemoveScanline(pRGB->mnFormat))
+ {
+ // convert source bitmap to BMP_FORMAT_24BIT_TC_BGR format if not yet in that format
+ SalTwoRect aSalTwoRect(0, 0, pRGB->mnWidth, pRGB->mnHeight, 0, 0, pRGB->mnWidth, pRGB->mnHeight);
+ pExtraRGB = StretchAndConvert(
+ *pRGB,
+ aSalTwoRect,
+ ScanlineFormat::N24BitTcBgr);
+
+ pSalRGB->ReleaseBuffer(pRGB, BitmapAccessMode::Write);
+ pRGB = pExtraRGB.get();
+ }
+
+ if(pRGB
+ && pRGB->mnWidth > 0
+ && pRGB->mnHeight > 0
+ && ScanlineFormat::N24BitTcBgr == RemoveScanline(pRGB->mnFormat))
+ {
+ const sal_uInt32 nW(pRGB->mnWidth);
+ const sal_uInt32 nH(pRGB->mnHeight);
+
+ pRetval = new Gdiplus::Bitmap(nW, nH, PixelFormat24bppRGB);
+
+ if ( pRetval->GetLastStatus() == Gdiplus::Ok )
+ {
+ sal_uInt8* pSrcRGB(pRGB->mpBits);
+ const sal_uInt32 nExtraRGB(pRGB->mnScanlineSize - (nW * 3));
+ const bool bTopDown(pRGB->mnFormat & ScanlineFormat::TopDown);
+ const Gdiplus::Rect aAllRect(0, 0, nW, nH);
+ Gdiplus::BitmapData aGdiPlusBitmapData;
+ pRetval->LockBits(&aAllRect, Gdiplus::ImageLockModeWrite, PixelFormat24bppRGB, &aGdiPlusBitmapData);
+
+ // copy data to Gdiplus::Bitmap; format is BGR here in both cases, so memcpy is possible
+ for(sal_uInt32 y(0); y < nH; y++)
+ {
+ const sal_uInt32 nYInsert(bTopDown ? y : nH - y - 1);
+ sal_uInt8* targetPixels = static_cast<sal_uInt8*>(aGdiPlusBitmapData.Scan0) + (nYInsert * aGdiPlusBitmapData.Stride);
+
+ memcpy(targetPixels, pSrcRGB, nW * 3);
+ pSrcRGB += nW * 3 + nExtraRGB;
+ }
+
+ pRetval->UnlockBits(&aGdiPlusBitmapData);
+ }
+ else
+ {
+ delete pRetval;
+ pRetval = nullptr;
+ }
+ }
+
+ if(pExtraRGB)
+ {
+ // #i123478# shockingly, BitmapBuffer does not free the memory it is controlling
+ // in its destructor, this *has to be done by hand*. Doing it here now
+ delete[] pExtraRGB->mpBits;
+ pExtraRGB.reset();
+ }
+ else
+ {
+ pSalRGB->ReleaseBuffer(pRGB, BitmapAccessMode::Read);
+ }
+
+ if(pExtraWinSalRGB)
+ {
+ delete pExtraWinSalRGB;
+ }
+
+ return pRetval;
+}
+
+Gdiplus::Bitmap* WinSalBitmap::ImplCreateGdiPlusBitmap(const WinSalBitmap& rAlphaSource)
+{
+ Gdiplus::Bitmap* pRetval(nullptr);
+ WinSalBitmap* pSalRGB = this;
+ WinSalBitmap* pExtraWinSalRGB = nullptr;
+
+ if(!pSalRGB->ImplGethDIB())
+ {
+ // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap
+ pExtraWinSalRGB = new WinSalBitmap();
+ pExtraWinSalRGB->Create(*pSalRGB, pSalRGB->GetBitCount());
+ pSalRGB = pExtraWinSalRGB;
+ }
+
+ BitmapBuffer* pRGB = pSalRGB->AcquireBuffer(BitmapAccessMode::Read);
+ std::unique_ptr<BitmapBuffer> pExtraRGB;
+
+ if(pRGB && ScanlineFormat::N24BitTcBgr != RemoveScanline(pRGB->mnFormat))
+ {
+ // convert source bitmap to canlineFormat::N24BitTcBgr format if not yet in that format
+ SalTwoRect aSalTwoRect(0, 0, pRGB->mnWidth, pRGB->mnHeight, 0, 0, pRGB->mnWidth, pRGB->mnHeight);
+ pExtraRGB = StretchAndConvert(
+ *pRGB,
+ aSalTwoRect,
+ ScanlineFormat::N24BitTcBgr);
+
+ pSalRGB->ReleaseBuffer(pRGB, BitmapAccessMode::Read);
+ pRGB = pExtraRGB.get();
+ }
+
+ WinSalBitmap* pSalA = const_cast< WinSalBitmap* >(&rAlphaSource);
+ WinSalBitmap* pExtraWinSalA = nullptr;
+
+ if(!pSalA->ImplGethDIB())
+ {
+ // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap
+ pExtraWinSalA = new WinSalBitmap();
+ pExtraWinSalA->Create(*pSalA, pSalA->GetBitCount());
+ pSalA = pExtraWinSalA;
+ }
+
+ BitmapBuffer* pA = pSalA->AcquireBuffer(BitmapAccessMode::Read);
+ std::unique_ptr<BitmapBuffer> pExtraA;
+
+ if(pA && ScanlineFormat::N8BitPal != RemoveScanline(pA->mnFormat))
+ {
+ // convert alpha bitmap to ScanlineFormat::N8BitPal format if not yet in that format
+ SalTwoRect aSalTwoRect(0, 0, pA->mnWidth, pA->mnHeight, 0, 0, pA->mnWidth, pA->mnHeight);
+ const BitmapPalette& rTargetPalette = Bitmap::GetGreyPalette(256);
+
+ pExtraA = StretchAndConvert(
+ *pA,
+ aSalTwoRect,
+ ScanlineFormat::N8BitPal,
+ &rTargetPalette);
+
+ pSalA->ReleaseBuffer(pA, BitmapAccessMode::Read);
+ pA = pExtraA.get();
+ }
+
+ if(pRGB
+ && pA
+ && pRGB->mnWidth > 0
+ && pRGB->mnHeight > 0
+ && pRGB->mnWidth == pA->mnWidth
+ && pRGB->mnHeight == pA->mnHeight
+ && ScanlineFormat::N24BitTcBgr == RemoveScanline(pRGB->mnFormat)
+ && ScanlineFormat::N8BitPal == RemoveScanline(pA->mnFormat))
+ {
+ // we have alpha and bitmap in known formats, create GdiPlus Bitmap as 32bit ARGB
+ const sal_uInt32 nW(pRGB->mnWidth);
+ const sal_uInt32 nH(pRGB->mnHeight);
+
+ pRetval = new Gdiplus::Bitmap(nW, nH, PixelFormat32bppARGB);
+
+ if ( pRetval->GetLastStatus() == Gdiplus::Ok ) // 2nd place to secure with new Gdiplus::Bitmap
+ {
+ sal_uInt8* pSrcRGB(pRGB->mpBits);
+ sal_uInt8* pSrcA(pA->mpBits);
+ const sal_uInt32 nExtraRGB(pRGB->mnScanlineSize - (nW * 3));
+ const sal_uInt32 nExtraA(pA->mnScanlineSize - nW);
+ const bool bTopDown(pRGB->mnFormat & ScanlineFormat::TopDown);
+ const Gdiplus::Rect aAllRect(0, 0, nW, nH);
+ Gdiplus::BitmapData aGdiPlusBitmapData;
+ pRetval->LockBits(&aAllRect, Gdiplus::ImageLockModeWrite, PixelFormat32bppARGB, &aGdiPlusBitmapData);
+
+ // copy data to Gdiplus::Bitmap; format is BGRA; need to mix BGR from Bitmap and
+ // A from alpha, so inner loop is needed (who invented BitmapEx..?)
+ for(sal_uInt32 y(0); y < nH; y++)
+ {
+ const sal_uInt32 nYInsert(bTopDown ? y : nH - y - 1);
+ sal_uInt8* targetPixels = static_cast<sal_uInt8*>(aGdiPlusBitmapData.Scan0) + (nYInsert * aGdiPlusBitmapData.Stride);
+
+ for(sal_uInt32 x(0); x < nW; x++)
+ {
+ *targetPixels++ = *pSrcRGB++;
+ *targetPixels++ = *pSrcRGB++;
+ *targetPixels++ = *pSrcRGB++;
+ *targetPixels++ = 0xff - *pSrcA++;
+ }
+
+ pSrcRGB += nExtraRGB;
+ pSrcA += nExtraA;
+ }
+
+ pRetval->UnlockBits(&aGdiPlusBitmapData);
+ }
+ else
+ {
+ delete pRetval;
+ pRetval = nullptr;
+ }
+ }
+
+ if(pExtraA)
+ {
+ // #i123478# shockingly, BitmapBuffer does not free the memory it is controlling
+ // in its destructor, this *has to be done handish*. Doing it here now
+ delete[] pExtraA->mpBits;
+ pExtraA.reset();
+ }
+ else
+ {
+ pSalA->ReleaseBuffer(pA, BitmapAccessMode::Read);
+ }
+
+ if(pExtraWinSalA)
+ {
+ delete pExtraWinSalA;
+ }
+
+ if(pExtraRGB)
+ {
+ // #i123478# shockingly, BitmapBuffer does not free the memory it is controlling
+ // in its destructor, this *has to be done by hand*. Doing it here now
+ delete[] pExtraRGB->mpBits;
+ pExtraRGB.reset();
+ }
+ else
+ {
+ pSalRGB->ReleaseBuffer(pRGB, BitmapAccessMode::Read);
+ }
+
+ if(pExtraWinSalRGB)
+ {
+ delete pExtraWinSalRGB;
+ }
+
+ return pRetval;
+}
+
+bool WinSalBitmap::Create( HANDLE hBitmap, bool bDIB, bool bCopyHandle )
+{
+ bool bRet = true;
+
+ if( bDIB )
+ mhDIB = static_cast<HGLOBAL>( bCopyHandle ? ImplCopyDIBOrDDB( hBitmap, true ) : hBitmap );
+ else
+ mhDDB = static_cast<HBITMAP>( bCopyHandle ? ImplCopyDIBOrDDB( hBitmap, false ) : hBitmap );
+
+ if( mhDIB )
+ {
+ PBITMAPINFOHEADER pBIH = static_cast<PBITMAPINFOHEADER>(GlobalLock( mhDIB ));
+
+ maSize = Size( pBIH->biWidth, pBIH->biHeight );
+ mnBitCount = pBIH->biBitCount;
+
+ if( mnBitCount )
+ mnBitCount = ( mnBitCount <= 1 ) ? 1 : ( mnBitCount <= 4 ) ? 4 : ( mnBitCount <= 8 ) ? 8 : 24;
+
+ GlobalUnlock( mhDIB );
+ }
+ else if( mhDDB )
+ {
+ BITMAP aDDBInfo;
+
+ if( GetObjectW( mhDDB, sizeof( aDDBInfo ), &aDDBInfo ) )
+ {
+ maSize = Size( aDDBInfo.bmWidth, aDDBInfo.bmHeight );
+ mnBitCount = aDDBInfo.bmPlanes * aDDBInfo.bmBitsPixel;
+
+ if( mnBitCount )
+ {
+ mnBitCount = ( mnBitCount <= 1 ) ? 1 :
+ ( mnBitCount <= 4 ) ? 4 :
+ ( mnBitCount <= 8 ) ? 8 : 24;
+ }
+ }
+ else
+ {
+ mhDDB = nullptr;
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+
+ return bRet;
+}
+
+bool WinSalBitmap::Create( const Size& rSize, sal_uInt16 nBitCount, const BitmapPalette& rPal )
+{
+ bool bRet = false;
+
+ mhDIB = ImplCreateDIB( rSize, nBitCount, rPal );
+
+ if( mhDIB )
+ {
+ maSize = rSize;
+ mnBitCount = nBitCount;
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool WinSalBitmap::Create( const SalBitmap& rSSalBitmap )
+{
+ bool bRet = false;
+ const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
+
+ if ( rSalBitmap.mhDIB || rSalBitmap.mhDDB )
+ {
+ HANDLE hNewHdl = ImplCopyDIBOrDDB( rSalBitmap.mhDIB ? rSalBitmap.mhDIB : rSalBitmap.mhDDB,
+ rSalBitmap.mhDIB != nullptr );
+
+ if ( hNewHdl )
+ {
+ if( rSalBitmap.mhDIB )
+ mhDIB = static_cast<HGLOBAL>(hNewHdl);
+ else if( rSalBitmap.mhDDB )
+ mhDDB = static_cast<HBITMAP>(hNewHdl);
+
+ maSize = rSalBitmap.maSize;
+ mnBitCount = rSalBitmap.mnBitCount;
+
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+bool WinSalBitmap::Create( const SalBitmap& rSSalBmp, SalGraphics* pSGraphics )
+{
+ bool bRet = false;
+
+ const WinSalBitmap& rSalBmp = static_cast<const WinSalBitmap&>(rSSalBmp);
+ WinSalGraphics* pGraphics = static_cast<WinSalGraphics*>(pSGraphics);
+
+ if( rSalBmp.mhDIB )
+ {
+ PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( rSalBmp.mhDIB ));
+ HDC hDC = pGraphics->getHDC();
+ HBITMAP hNewDDB;
+ BITMAP aDDBInfo;
+ PBYTE pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize +
+ ImplGetDIBColorCount( rSalBmp.mhDIB ) * sizeof( RGBQUAD );
+
+ if( pBI->bmiHeader.biBitCount == 1 )
+ {
+ hNewDDB = CreateBitmap( pBI->bmiHeader.biWidth, pBI->bmiHeader.biHeight, 1, 1, nullptr );
+
+ if( hNewDDB )
+ SetDIBits( hDC, hNewDDB, 0, pBI->bmiHeader.biHeight, pBits, pBI, DIB_RGB_COLORS );
+ }
+ else
+ hNewDDB = CreateDIBitmap( hDC, &pBI->bmiHeader, CBM_INIT, pBits, pBI, DIB_RGB_COLORS );
+
+ GlobalUnlock( rSalBmp.mhDIB );
+
+ if( hNewDDB && GetObjectW( hNewDDB, sizeof( aDDBInfo ), &aDDBInfo ) )
+ {
+ mhDDB = hNewDDB;
+ maSize = Size( aDDBInfo.bmWidth, aDDBInfo.bmHeight );
+ mnBitCount = aDDBInfo.bmPlanes * aDDBInfo.bmBitsPixel;
+
+ bRet = true;
+ }
+ else if( hNewDDB )
+ DeleteObject( hNewDDB );
+ }
+
+ return bRet;
+}
+
+bool WinSalBitmap::Create( const SalBitmap& rSSalBmp, sal_uInt16 nNewBitCount )
+{
+ bool bRet = false;
+
+ const WinSalBitmap& rSalBmp = static_cast<const WinSalBitmap&>(rSSalBmp);
+
+ if( rSalBmp.mhDDB )
+ {
+ mhDIB = ImplCreateDIB( rSalBmp.maSize, nNewBitCount, BitmapPalette() );
+
+ if( mhDIB )
+ {
+ PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( mhDIB ));
+ const int nLines = static_cast<int>(rSalBmp.maSize.Height());
+ HDC hDC = GetDC( nullptr );
+ PBYTE pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize +
+ ImplGetDIBColorCount( mhDIB ) * sizeof( RGBQUAD );
+ SalData* pSalData = GetSalData();
+ HPALETTE hOldPal = nullptr;
+
+ if ( pSalData->mhDitherPal )
+ {
+ hOldPal = SelectPalette( hDC, pSalData->mhDitherPal, TRUE );
+ RealizePalette( hDC );
+ }
+
+ if( GetDIBits( hDC, rSalBmp.mhDDB, 0, nLines, pBits, pBI, DIB_RGB_COLORS ) == nLines )
+ {
+ GlobalUnlock( mhDIB );
+ maSize = rSalBmp.maSize;
+ mnBitCount = nNewBitCount;
+ bRet = true;
+ }
+ else
+ {
+ GlobalUnlock( mhDIB );
+ GlobalFree( mhDIB );
+ mhDIB = nullptr;
+ }
+
+ if( hOldPal )
+ SelectPalette( hDC, hOldPal, TRUE );
+
+ ReleaseDC( nullptr, hDC );
+ }
+ }
+
+ return bRet;
+}
+
+bool WinSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& rBitmapCanvas, Size& /*rSize*/, bool bMask )
+{
+ css::uno::Reference< css::beans::XFastPropertySet >
+ xFastPropertySet( rBitmapCanvas, css::uno::UNO_QUERY );
+
+ if( xFastPropertySet.get() ) {
+ css::uno::Sequence< css::uno::Any > args;
+
+ if( xFastPropertySet->getFastPropertyValue(bMask ? 2 : 1) >>= args ) {
+ sal_Int64 aHBmp64;
+
+ if( args[0] >>= aHBmp64 ) {
+ return Create( HBITMAP(aHBmp64), false, false );
+ }
+ }
+ }
+ return false;
+}
+
+sal_uInt16 WinSalBitmap::ImplGetDIBColorCount( HGLOBAL hDIB )
+{
+ sal_uInt16 nColors = 0;
+
+ if( hDIB )
+ {
+ PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( hDIB ));
+
+ if ( pBI->bmiHeader.biSize != sizeof( BITMAPCOREHEADER ) )
+ {
+ if( pBI->bmiHeader.biBitCount <= 8 )
+ {
+ if ( pBI->bmiHeader.biClrUsed )
+ nColors = static_cast<sal_uInt16>(pBI->bmiHeader.biClrUsed);
+ else
+ nColors = 1 << pBI->bmiHeader.biBitCount;
+ }
+ }
+ else if( reinterpret_cast<PBITMAPCOREHEADER>(pBI)->bcBitCount <= 8 )
+ nColors = 1 << reinterpret_cast<PBITMAPCOREHEADER>(pBI)->bcBitCount;
+
+ GlobalUnlock( hDIB );
+ }
+
+ return nColors;
+}
+
+HGLOBAL WinSalBitmap::ImplCreateDIB( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rPal )
+{
+ SAL_WARN_IF( nBits != 1 && nBits != 4 && nBits != 8 && nBits != 24, "vcl", "Unsupported BitCount!" );
+
+ HGLOBAL hDIB = nullptr;
+
+ if( rSize.IsEmpty() )
+ return hDIB;
+
+ // calculate bitmap size in Bytes
+ const sal_uLong nAlignedWidth4Bytes = AlignedWidth4Bytes( nBits * rSize.Width() );
+ const sal_uLong nImageSize = nAlignedWidth4Bytes * rSize.Height();
+ bool bOverflow = (nImageSize / nAlignedWidth4Bytes) != static_cast<sal_uLong>(rSize.Height());
+ if( bOverflow )
+ return hDIB;
+
+ // allocate bitmap memory including header and palette
+ const sal_uInt16 nColors = (nBits <= 8) ? (1 << nBits) : 0;
+ const sal_uLong nHeaderSize = sizeof( BITMAPINFOHEADER ) + nColors * sizeof( RGBQUAD );
+ bOverflow = (nHeaderSize + nImageSize) < nImageSize;
+ if( bOverflow )
+ return hDIB;
+
+ hDIB = GlobalAlloc( GHND, nHeaderSize + nImageSize );
+ if( !hDIB )
+ return hDIB;
+
+ PBITMAPINFO pBI = static_cast<PBITMAPINFO>( GlobalLock( hDIB ) );
+ PBITMAPINFOHEADER pBIH = reinterpret_cast<PBITMAPINFOHEADER>( pBI );
+
+ pBIH->biSize = sizeof( BITMAPINFOHEADER );
+ pBIH->biWidth = rSize.Width();
+ pBIH->biHeight = rSize.Height();
+ pBIH->biPlanes = 1;
+ pBIH->biBitCount = nBits;
+ pBIH->biCompression = BI_RGB;
+ pBIH->biSizeImage = nImageSize;
+ pBIH->biXPelsPerMeter = 0;
+ pBIH->biYPelsPerMeter = 0;
+ pBIH->biClrUsed = 0;
+ pBIH->biClrImportant = 0;
+
+ if( nColors )
+ {
+ // copy the palette entries if any
+ const sal_uInt16 nMinCount = std::min( nColors, rPal.GetEntryCount() );
+ if( nMinCount )
+ memcpy( pBI->bmiColors, rPal.ImplGetColorBuffer(), nMinCount * sizeof(RGBQUAD) );
+ }
+
+ GlobalUnlock( hDIB );
+
+ return hDIB;
+}
+
+HANDLE WinSalBitmap::ImplCopyDIBOrDDB( HANDLE hHdl, bool bDIB )
+{
+ HANDLE hCopy = nullptr;
+
+ if ( bDIB && hHdl )
+ {
+ const sal_uLong nSize = GlobalSize( hHdl );
+
+ if ( (hCopy = GlobalAlloc( GHND, nSize )) != nullptr )
+ {
+ memcpy( GlobalLock( hCopy ), GlobalLock( hHdl ), nSize );
+
+ GlobalUnlock( hCopy );
+ GlobalUnlock( hHdl );
+ }
+ }
+ else if ( hHdl )
+ {
+ BITMAP aBmp;
+
+ // find out size of source bitmap
+ GetObjectW( hHdl, sizeof( aBmp ), &aBmp );
+
+ // create destination bitmap
+ if ( (hCopy = CreateBitmapIndirect( &aBmp )) != nullptr )
+ {
+ HDC hBmpDC = CreateCompatibleDC( nullptr );
+ HBITMAP hBmpOld = static_cast<HBITMAP>(SelectObject( hBmpDC, hHdl ));
+ HDC hCopyDC = CreateCompatibleDC( hBmpDC );
+ HBITMAP hCopyOld = static_cast<HBITMAP>(SelectObject( hCopyDC, hCopy ));
+
+ BitBlt( hCopyDC, 0, 0, aBmp.bmWidth, aBmp.bmHeight, hBmpDC, 0, 0, SRCCOPY );
+
+ SelectObject( hCopyDC, hCopyOld );
+ DeleteDC( hCopyDC );
+
+ SelectObject( hBmpDC, hBmpOld );
+ DeleteDC( hBmpDC );
+ }
+ }
+
+ return hCopy;
+}
+
+BitmapBuffer* WinSalBitmap::AcquireBuffer( BitmapAccessMode /*nMode*/ )
+{
+ BitmapBuffer* pBuffer = nullptr;
+
+ if( mhDIB )
+ {
+ PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( mhDIB ));
+ PBITMAPINFOHEADER pBIH = &pBI->bmiHeader;
+
+ if( ( pBIH->biCompression == BI_RLE4 ) || ( pBIH->biCompression == BI_RLE8 ) )
+ {
+ Size aSizePix( pBIH->biWidth, pBIH->biHeight );
+ HGLOBAL hNewDIB = ImplCreateDIB( aSizePix, pBIH->biBitCount, BitmapPalette() );
+
+ if( hNewDIB )
+ {
+ PBITMAPINFO pNewBI = static_cast<PBITMAPINFO>(GlobalLock( hNewDIB ));
+ PBITMAPINFOHEADER pNewBIH = &pNewBI->bmiHeader;
+ const sal_uInt16 nColorCount = ImplGetDIBColorCount( hNewDIB );
+ const sal_uLong nOffset = pBI->bmiHeader.biSize + nColorCount * sizeof( RGBQUAD );
+ BYTE* pOldBits = reinterpret_cast<PBYTE>(pBI) + nOffset;
+ BYTE* pNewBits = reinterpret_cast<PBYTE>(pNewBI) + nOffset;
+
+ memcpy( pNewBI, pBI, nOffset );
+ pNewBIH->biCompression = 0;
+ ImplDecodeRLEBuffer( pOldBits, pNewBits, aSizePix, pBIH->biCompression == BI_RLE4 );
+
+ GlobalUnlock( mhDIB );
+ GlobalFree( mhDIB );
+ mhDIB = hNewDIB;
+ pBI = pNewBI;
+ pBIH = pNewBIH;
+ }
+ }
+
+ if( pBIH->biPlanes == 1 )
+ {
+ pBuffer = new BitmapBuffer;
+
+ pBuffer->mnFormat = pBIH->biBitCount == 1 ? ScanlineFormat::N1BitMsbPal :
+ pBIH->biBitCount == 4 ? ScanlineFormat::N4BitMsnPal :
+ pBIH->biBitCount == 8 ? ScanlineFormat::N8BitPal :
+ pBIH->biBitCount == 24 ? ScanlineFormat::N24BitTcBgr :
+ pBIH->biBitCount == 32 ? ScanlineFormat::N32BitTcMask :
+ ScanlineFormat::NONE;
+
+ if( RemoveScanline( pBuffer->mnFormat ) != ScanlineFormat::NONE )
+ {
+ pBuffer->mnWidth = maSize.Width();
+ pBuffer->mnHeight = maSize.Height();
+ pBuffer->mnScanlineSize = AlignedWidth4Bytes( maSize.Width() * pBIH->biBitCount );
+ pBuffer->mnBitCount = static_cast<sal_uInt16>(pBIH->biBitCount);
+
+ if( pBuffer->mnBitCount <= 8 )
+ {
+ const sal_uInt16 nPalCount = ImplGetDIBColorCount( mhDIB );
+
+ pBuffer->maPalette.SetEntryCount( nPalCount );
+ memcpy( pBuffer->maPalette.ImplGetColorBuffer(), pBI->bmiColors, nPalCount * sizeof( RGBQUAD ) );
+ pBuffer->mpBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize + nPalCount * sizeof( RGBQUAD );
+ }
+ else if( ( pBIH->biBitCount == 16 ) || ( pBIH->biBitCount == 32 ) )
+ {
+ sal_uLong nOffset = 0;
+
+ if( pBIH->biCompression == BI_BITFIELDS )
+ {
+ nOffset = 3 * sizeof( RGBQUAD );
+ ColorMaskElement aRedMask(*reinterpret_cast<UINT32*>(&pBI->bmiColors[ 0 ]));
+ aRedMask.CalcMaskShift();
+ ColorMaskElement aGreenMask(*reinterpret_cast<UINT32*>(&pBI->bmiColors[ 1 ]));
+ aGreenMask.CalcMaskShift();
+ ColorMaskElement aBlueMask(*reinterpret_cast<UINT32*>(&pBI->bmiColors[ 2 ]));
+ aBlueMask.CalcMaskShift();
+ pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
+ }
+ else if( pBIH->biBitCount == 16 )
+ {
+ ColorMaskElement aRedMask(0x00007c00UL);
+ aRedMask.CalcMaskShift();
+ ColorMaskElement aGreenMask(0x000003e0UL);
+ aGreenMask.CalcMaskShift();
+ ColorMaskElement aBlueMask(0x0000001fUL);
+ aBlueMask.CalcMaskShift();
+ pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
+ }
+ else
+ {
+ ColorMaskElement aRedMask(0x00ff0000UL);
+ aRedMask.CalcMaskShift();
+ ColorMaskElement aGreenMask(0x0000ff00UL);
+ aGreenMask.CalcMaskShift();
+ ColorMaskElement aBlueMask(0x000000ffUL);
+ aBlueMask.CalcMaskShift();
+ pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
+ }
+
+ pBuffer->mpBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize + nOffset;
+ }
+ else
+ pBuffer->mpBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize;
+ }
+ else
+ {
+ GlobalUnlock( mhDIB );
+ delete pBuffer;
+ pBuffer = nullptr;
+ }
+ }
+ else
+ GlobalUnlock( mhDIB );
+ }
+
+ return pBuffer;
+}
+
+void WinSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, BitmapAccessMode nMode )
+{
+ if( pBuffer )
+ {
+ if( mhDIB )
+ {
+ if( nMode == BitmapAccessMode::Write && !!pBuffer->maPalette )
+ {
+ PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( mhDIB ));
+ const sal_uInt16 nCount = pBuffer->maPalette.GetEntryCount();
+ const sal_uInt16 nDIBColorCount = ImplGetDIBColorCount( mhDIB );
+ memcpy( pBI->bmiColors, pBuffer->maPalette.ImplGetColorBuffer(), std::min( nDIBColorCount, nCount ) * sizeof( RGBQUAD ) );
+ GlobalUnlock( mhDIB );
+ }
+
+ GlobalUnlock( mhDIB );
+ }
+
+ delete pBuffer;
+ }
+ if( nMode == BitmapAccessMode::Write )
+ InvalidateChecksum();
+}
+
+void WinSalBitmap::ImplDecodeRLEBuffer( const BYTE* pSrcBuf, BYTE* pDstBuf,
+ const Size& rSizePixel, bool bRLE4 )
+{
+ sal_uInt8 const * pRLE = pSrcBuf;
+ sal_uInt8* pDIB = pDstBuf;
+ sal_uInt8* pRow = pDstBuf;
+ sal_uLong nWidthAl = AlignedWidth4Bytes( rSizePixel.Width() * ( bRLE4 ? 4UL : 8UL ) );
+ sal_uInt8* pLast = pDIB + rSizePixel.Height() * nWidthAl - 1;
+ sal_uLong nCountByte;
+ sal_uLong nRunByte;
+ sal_uLong i;
+ BYTE cTmp;
+ bool bEndDecoding = false;
+
+ if( pRLE && pDIB )
+ {
+ sal_uLong nX = 0;
+ do
+ {
+ if( ( nCountByte = *pRLE++ ) == 0 )
+ {
+ nRunByte = *pRLE++;
+
+ if( nRunByte > 2 )
+ {
+ if( bRLE4 )
+ {
+ nCountByte = nRunByte >> 1;
+
+ for( i = 0; i < nCountByte; i++ )
+ {
+ cTmp = *pRLE++;
+ ImplSetPixel4( pDIB, nX++, cTmp >> 4 );
+ ImplSetPixel4( pDIB, nX++, cTmp & 0x0f );
+ }
+
+ if( nRunByte & 1 )
+ ImplSetPixel4( pDIB, nX++, *pRLE++ >> 4 );
+
+ if( ( ( nRunByte + 1 ) >> 1 ) & 1 )
+ pRLE++;
+ }
+ else
+ {
+ memcpy( &pDIB[ nX ], pRLE, nRunByte );
+ pRLE += nRunByte;
+ nX += nRunByte;
+
+ if( nRunByte & 1 )
+ pRLE++;
+ }
+ }
+ else if( !nRunByte )
+ {
+ pDIB = ( pRow += nWidthAl );
+ nX = 0;
+ }
+ else if( nRunByte == 1 )
+ bEndDecoding = true;
+ else
+ {
+ nX += *pRLE++;
+ pDIB = ( pRow += ( *pRLE++ ) * nWidthAl );
+ }
+ }
+ else
+ {
+ cTmp = *pRLE++;
+
+ if( bRLE4 )
+ {
+ nRunByte = nCountByte >> 1;
+
+ for( i = 0; i < nRunByte; i++ )
+ {
+ ImplSetPixel4( pDIB, nX++, cTmp >> 4 );
+ ImplSetPixel4( pDIB, nX++, cTmp & 0x0f );
+ }
+
+ if( nCountByte & 1 )
+ ImplSetPixel4( pDIB, nX++, cTmp >> 4 );
+ }
+ else
+ {
+ for( i = 0; i < nCountByte; i++ )
+ pDIB[ nX++ ] = cTmp;
+ }
+ }
+ }
+ while( !bEndDecoding && ( pDIB <= pLast ) );
+ }
+}
+
+bool WinSalBitmap::GetSystemData( BitmapSystemData& rData )
+{
+ bool bRet = false;
+ if( mhDIB || mhDDB )
+ {
+ bRet = true;
+ rData.pDIB = mhDIB;
+ const Size& rSize = GetSize ();
+ rData.mnWidth = rSize.Width();
+ rData.mnHeight = rSize.Height();
+ }
+ return bRet;
+}
+
+bool WinSalBitmap::ScalingSupported() const
+{
+ return false;
+}
+
+bool WinSalBitmap::Scale( const double& /*rScaleX*/, const double& /*rScaleY*/, BmpScaleFlag /*nScaleFlag*/ )
+{
+ return false;
+}
+
+bool WinSalBitmap::Replace( const Color& /*rSearchColor*/, const Color& /*rReplaceColor*/, sal_uInt8 /*nTol*/ )
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/salfont.cxx b/vcl/win/gdi/salfont.cxx
new file mode 100644
index 000000000..1ed293888
--- /dev/null
+++ b/vcl/win/gdi/salfont.cxx
@@ -0,0 +1,1766 @@
+/* -*- 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 <sal/types.h>
+#include <config_folders.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <string.h>
+#include <svsys.h>
+#include <vector>
+
+#include <o3tl/lru_map.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <tools/helpers.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/fontcfg.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <vcl/opengl/OpenGLWrapper.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <fontsubset.hxx>
+#include <outdev.h>
+#include <PhysicalFontCollection.hxx>
+#include <PhysicalFontFace.hxx>
+#include <sft.hxx>
+#include <win/saldata.hxx>
+#include <win/salgdi.h>
+#include <win/winlayout.hxx>
+#include <win/wingdiimpl.hxx>
+#include <impfontcharmap.hxx>
+#include <impfontmetricdata.hxx>
+#include <impglyphitem.hxx>
+
+using namespace vcl;
+
+static FIXED FixedFromDouble( double d )
+{
+ const long l = static_cast<long>( d * 65536. );
+ return *reinterpret_cast<FIXED const *>(&l);
+}
+
+static int IntTimes256FromFixed(FIXED f)
+{
+ int nFixedTimes256 = (f.value << 8) + ((f.fract+0x80) >> 8);
+ return nFixedTimes256;
+}
+
+namespace {
+
+// raw font data with a scoped lifetime
+class RawFontData
+{
+public:
+ explicit RawFontData( HDC, DWORD nTableTag=0 );
+ const unsigned char* get() const { return mpRawBytes.get(); }
+ const unsigned char* steal() { return mpRawBytes.release(); }
+ int size() const { return mnByteCount; }
+
+private:
+ std::unique_ptr<unsigned char[]> mpRawBytes;
+ unsigned mnByteCount;
+};
+
+}
+
+RawFontData::RawFontData( HDC hDC, DWORD nTableTag )
+: mnByteCount( 0 )
+{
+ // get required size in bytes
+ mnByteCount = ::GetFontData( hDC, nTableTag, 0, nullptr, 0 );
+ if (mnByteCount == GDI_ERROR)
+ mnByteCount = 0;
+ if (!mnByteCount)
+ return;
+
+ // allocate the array
+ mpRawBytes.reset(new unsigned char[ mnByteCount ]);
+
+ // get raw data in chunks small enough for GetFontData()
+ unsigned nRawDataOfs = 0;
+ DWORD nMaxChunkSize = 0x100000;
+ for(;;)
+ {
+ // calculate remaining raw data to get
+ DWORD nFDGet = mnByteCount - nRawDataOfs;
+ if( nFDGet <= 0 )
+ break;
+ // #i56745# limit GetFontData requests
+ if( nFDGet > nMaxChunkSize )
+ nFDGet = nMaxChunkSize;
+ const DWORD nFDGot = ::GetFontData( hDC, nTableTag, nRawDataOfs,
+ mpRawBytes.get() + nRawDataOfs, nFDGet );
+ if( !nFDGot )
+ break;
+ else if( nFDGot != GDI_ERROR )
+ nRawDataOfs += nFDGot;
+ else
+ {
+ // was the chunk too big? reduce it
+ nMaxChunkSize /= 2;
+ if( nMaxChunkSize < 0x10000 )
+ break;
+ }
+ }
+
+ // cleanup if the raw data is incomplete
+ if( nRawDataOfs != mnByteCount )
+ {
+ mpRawBytes.reset();
+ // mnByteCount must correspond to mpRawBytes length
+ SAL_WARN( "vcl", "Raw data of font is incomplete: " << nRawDataOfs << " byte(s) found whereas " << mnByteCount << " byte(s) expected!" );
+ mnByteCount = 0;
+ }
+}
+
+// platform specific font substitution hooks for glyph fallback enhancement
+
+namespace {
+
+class WinPreMatchFontSubstititution
+: public ImplPreMatchFontSubstitution
+{
+public:
+ bool FindFontSubstitute(FontSelectPattern&) const override;
+};
+
+class WinGlyphFallbackSubstititution
+: public ImplGlyphFallbackFontSubstitution
+{
+public:
+ explicit WinGlyphFallbackSubstititution()
+ : mhDC(GetDC(nullptr))
+ {
+ };
+
+ ~WinGlyphFallbackSubstititution() override
+ {
+ ReleaseDC(nullptr, mhDC);
+ };
+
+ bool FindFontSubstitute(FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString& rMissingChars) const override;
+private:
+ HDC mhDC;
+ bool HasMissingChars(PhysicalFontFace*, OUString& rMissingChars) const;
+};
+
+}
+
+// does a font face hold the given missing characters?
+bool WinGlyphFallbackSubstititution::HasMissingChars(PhysicalFontFace* pFace, OUString& rMissingChars) const
+{
+ WinFontFace* pWinFont = static_cast< WinFontFace* >(pFace);
+ FontCharMapRef xFontCharMap = pWinFont->GetFontCharMap();
+ if( !xFontCharMap.is() )
+ {
+ // construct a Size structure as the parameter of constructor of class FontSelectPattern
+ const Size aSize( pFace->GetWidth(), pFace->GetHeight() );
+ // create a FontSelectPattern object for getting s LOGFONT
+ const FontSelectPattern aFSD( *pFace, aSize, static_cast<float>(aSize.Height()), 0, false );
+ // construct log font
+ LOGFONTW aLogFont;
+ ImplGetLogFontFromFontSelect( mhDC, aFSD, pFace, aLogFont );
+
+ // create HFONT from log font
+ HFONT hNewFont = ::CreateFontIndirectW( &aLogFont );
+ // select the new font into device
+ HFONT hOldFont = ::SelectFont( mhDC, hNewFont );
+
+ // read CMAP table to update their xFontCharMap
+ pWinFont->UpdateFromHDC( mhDC );
+
+ // cleanup temporary font
+ ::SelectFont( mhDC, hOldFont );
+ ::DeleteFont( hNewFont );
+
+ // get the new charmap
+ xFontCharMap = pWinFont->GetFontCharMap();
+ }
+
+ // avoid fonts with unknown CMAP subtables for glyph fallback
+ if( !xFontCharMap.is() || xFontCharMap->IsDefaultMap() )
+ return false;
+
+ int nMatchCount = 0;
+ std::vector<sal_UCS4> rRemainingCodes;
+ const sal_Int32 nStrLen = rMissingChars.getLength();
+ sal_Int32 nStrIdx = 0;
+ while (nStrIdx < nStrLen)
+ {
+ const sal_UCS4 uChar = rMissingChars.iterateCodePoints( &nStrIdx );
+ if (xFontCharMap->HasChar(uChar))
+ nMatchCount++;
+ else
+ rRemainingCodes.push_back(uChar);
+ }
+
+ xFontCharMap = nullptr;
+
+ if (nMatchCount > 0)
+ rMissingChars = OUString(rRemainingCodes.data(), rRemainingCodes.size());
+
+ return nMatchCount > 0;
+}
+
+namespace
+{
+ //used by 2-level font fallback
+ PhysicalFontFamily* findDevFontListByLocale(const PhysicalFontCollection &rFontCollection,
+ const LanguageTag& rLanguageTag )
+ {
+ // get the default font for a specified locale
+ const utl::DefaultFontConfiguration& rDefaults = utl::DefaultFontConfiguration::get();
+ const OUString aDefault = rDefaults.getUserInterfaceFont(rLanguageTag);
+ return rFontCollection.FindFontFamilyByTokenNames(aDefault);
+ }
+}
+
+// These are Win 3.1 bitmap fonts using "FON" font format
+// which is not supported with DirectWrite so let's substitute them
+// with a font that is supported and always available.
+// Based on:
+// https://dxr.mozilla.org/mozilla-esr10/source/gfx/thebes/gfxDWriteFontList.cpp#1057
+static const std::map<OUString, OUString> aBitmapFontSubs =
+{
+ { "MS Sans Serif", "Microsoft Sans Serif" },
+ { "MS Serif", "Times New Roman" },
+ { "Small Fonts", "Arial" },
+ { "Courier", "Courier New" },
+ { "Roman", "Times New Roman" },
+ { "Script", "Mistral" }
+};
+
+// TODO: See if Windows have API that we can use here to improve font fallback.
+bool WinPreMatchFontSubstititution::FindFontSubstitute(FontSelectPattern& rFontSelData) const
+{
+ if (rFontSelData.IsSymbolFont() || IsStarSymbol(rFontSelData.maSearchName))
+ return false;
+
+ for (const auto& aSub : aBitmapFontSubs)
+ {
+ if (rFontSelData.maSearchName == GetEnglishSearchFontName(aSub.first))
+ {
+ rFontSelData.maSearchName = aSub.second;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// find a fallback font for missing characters
+// TODO: should stylistic matches be searched and preferred?
+bool WinGlyphFallbackSubstititution::FindFontSubstitute(FontSelectPattern& rFontSelData, LogicalFontInstance* /*pLogicalFont*/, OUString& rMissingChars) const
+{
+ // guess a locale matching to the missing chars
+ LanguageType eLang = rFontSelData.meLanguage;
+ LanguageTag aLanguageTag( eLang);
+
+ // fall back to default UI locale if the font language is inconclusive
+ if( eLang == LANGUAGE_DONTKNOW )
+ aLanguageTag = Application::GetSettings().GetUILanguageTag();
+
+ // first level fallback:
+ // try use the locale specific default fonts defined in VCL.xcu
+ const PhysicalFontCollection* pFontCollection = ImplGetSVData()->maGDIData.mxScreenFontList.get();
+ PhysicalFontFamily* pFontFamily = findDevFontListByLocale(*pFontCollection, aLanguageTag);
+ if( pFontFamily )
+ {
+ PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
+ if( HasMissingChars( pFace, rMissingChars ) )
+ {
+ rFontSelData.maSearchName = pFontFamily->GetSearchName();
+ return true;
+ }
+ }
+
+ // are the missing characters symbols?
+ pFontFamily = pFontCollection->FindFontFamilyByAttributes( ImplFontAttrs::Symbol,
+ rFontSelData.GetWeight(),
+ rFontSelData.GetWidthType(),
+ rFontSelData.GetItalic(),
+ rFontSelData.maSearchName );
+ if( pFontFamily )
+ {
+ PhysicalFontFace* pFace = pFontFamily->FindBestFontFace( rFontSelData );
+ if( HasMissingChars( pFace, rMissingChars ) )
+ {
+ rFontSelData.maSearchName = pFontFamily->GetSearchName();
+ return true;
+ }
+ }
+
+ // last level fallback, check each font type face one by one
+ std::unique_ptr<ImplDeviceFontList> pTestFontList = pFontCollection->GetDeviceFontList();
+ // limit the count of fonts to be checked to prevent hangs
+ static const int MAX_GFBFONT_COUNT = 600;
+ int nTestFontCount = pTestFontList->Count();
+ if( nTestFontCount > MAX_GFBFONT_COUNT )
+ nTestFontCount = MAX_GFBFONT_COUNT;
+
+ bool bFound = false;
+ for( int i = 0; i < nTestFontCount; ++i )
+ {
+ PhysicalFontFace* pFace = pTestFontList->Get( i );
+ bFound = HasMissingChars( pFace, rMissingChars );
+ if( !bFound )
+ continue;
+ rFontSelData.maSearchName = pFace->GetFamilyName();
+ break;
+ }
+
+ return bFound;
+}
+
+namespace {
+
+struct ImplEnumInfo
+{
+ HDC mhDC;
+ PhysicalFontCollection* mpList;
+ OUString* mpName;
+ LOGFONTW* mpLogFont;
+ bool mbPrinter;
+ int mnFontCount;
+};
+
+}
+
+static rtl_TextEncoding ImplCharSetToSal( BYTE nCharSet )
+{
+ rtl_TextEncoding eTextEncoding;
+
+ if ( nCharSet == OEM_CHARSET )
+ {
+ UINT nCP = static_cast<sal_uInt16>(GetOEMCP());
+ switch ( nCP )
+ {
+ // It is unclear why these two (undefined?) code page numbers are
+ // handled specially here:
+ case 1004: eTextEncoding = RTL_TEXTENCODING_MS_1252; break;
+ case 65400: eTextEncoding = RTL_TEXTENCODING_SYMBOL; break;
+ default:
+ eTextEncoding = rtl_getTextEncodingFromWindowsCodePage(nCP);
+ break;
+ }
+ }
+ else
+ {
+ if( nCharSet )
+ eTextEncoding = rtl_getTextEncodingFromWindowsCharset( nCharSet );
+ else
+ eTextEncoding = RTL_TEXTENCODING_UNICODE;
+ }
+
+ return eTextEncoding;
+}
+
+static FontFamily ImplFamilyToSal( BYTE nFamily )
+{
+ switch ( nFamily & 0xF0 )
+ {
+ case FF_DECORATIVE:
+ return FAMILY_DECORATIVE;
+
+ case FF_MODERN:
+ return FAMILY_MODERN;
+
+ case FF_ROMAN:
+ return FAMILY_ROMAN;
+
+ case FF_SCRIPT:
+ return FAMILY_SCRIPT;
+
+ case FF_SWISS:
+ return FAMILY_SWISS;
+
+ default:
+ break;
+ }
+
+ return FAMILY_DONTKNOW;
+}
+
+static BYTE ImplFamilyToWin( FontFamily eFamily )
+{
+ switch ( eFamily )
+ {
+ case FAMILY_DECORATIVE:
+ return FF_DECORATIVE;
+
+ case FAMILY_MODERN:
+ return FF_MODERN;
+
+ case FAMILY_ROMAN:
+ return FF_ROMAN;
+
+ case FAMILY_SCRIPT:
+ return FF_SCRIPT;
+
+ case FAMILY_SWISS:
+ return FF_SWISS;
+
+ case FAMILY_SYSTEM:
+ return FF_SWISS;
+
+ default:
+ break;
+ }
+
+ return FF_DONTCARE;
+}
+
+static FontWeight ImplWeightToSal( int nWeight )
+{
+ if ( nWeight <= FW_THIN )
+ return WEIGHT_THIN;
+ else if ( nWeight <= FW_ULTRALIGHT )
+ return WEIGHT_ULTRALIGHT;
+ else if ( nWeight <= FW_LIGHT )
+ return WEIGHT_LIGHT;
+ else if ( nWeight < FW_MEDIUM )
+ return WEIGHT_NORMAL;
+ else if ( nWeight == FW_MEDIUM )
+ return WEIGHT_MEDIUM;
+ else if ( nWeight <= FW_SEMIBOLD )
+ return WEIGHT_SEMIBOLD;
+ else if ( nWeight <= FW_BOLD )
+ return WEIGHT_BOLD;
+ else if ( nWeight <= FW_ULTRABOLD )
+ return WEIGHT_ULTRABOLD;
+ else
+ return WEIGHT_BLACK;
+}
+
+static int ImplWeightToWin( FontWeight eWeight )
+{
+ switch ( eWeight )
+ {
+ case WEIGHT_THIN:
+ return FW_THIN;
+
+ case WEIGHT_ULTRALIGHT:
+ return FW_ULTRALIGHT;
+
+ case WEIGHT_LIGHT:
+ return FW_LIGHT;
+
+ case WEIGHT_SEMILIGHT:
+ case WEIGHT_NORMAL:
+ return FW_NORMAL;
+
+ case WEIGHT_MEDIUM:
+ return FW_MEDIUM;
+
+ case WEIGHT_SEMIBOLD:
+ return FW_SEMIBOLD;
+
+ case WEIGHT_BOLD:
+ return FW_BOLD;
+
+ case WEIGHT_ULTRABOLD:
+ return FW_ULTRABOLD;
+
+ case WEIGHT_BLACK:
+ return FW_BLACK;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static FontPitch ImplLogPitchToSal( BYTE nPitch )
+{
+ if ( nPitch & FIXED_PITCH )
+ return PITCH_FIXED;
+ else
+ return PITCH_VARIABLE;
+}
+
+static FontPitch ImplMetricPitchToSal( BYTE nPitch )
+{
+ // Grrrr! See NT help
+ if ( !(nPitch & TMPF_FIXED_PITCH) )
+ return PITCH_FIXED;
+ else
+ return PITCH_VARIABLE;
+}
+
+static BYTE ImplPitchToWin( FontPitch ePitch )
+{
+ if ( ePitch == PITCH_FIXED )
+ return FIXED_PITCH;
+ else if ( ePitch == PITCH_VARIABLE )
+ return VARIABLE_PITCH;
+ else
+ return DEFAULT_PITCH;
+}
+
+static FontAttributes WinFont2DevFontAttributes( const ENUMLOGFONTEXW& rEnumFont,
+ const NEWTEXTMETRICW& rMetric)
+{
+ FontAttributes aDFA;
+
+ const LOGFONTW rLogFont = rEnumFont.elfLogFont;
+
+ // get font face attributes
+ aDFA.SetFamilyType(ImplFamilyToSal( rLogFont.lfPitchAndFamily ));
+ aDFA.SetWidthType(WIDTH_DONTKNOW);
+ aDFA.SetWeight(ImplWeightToSal( rLogFont.lfWeight ));
+ aDFA.SetItalic((rLogFont.lfItalic) ? ITALIC_NORMAL : ITALIC_NONE);
+ aDFA.SetPitch(ImplLogPitchToSal( rLogFont.lfPitchAndFamily ));
+ aDFA.SetSymbolFlag(rLogFont.lfCharSet == SYMBOL_CHARSET);
+
+ // get the font face name
+ aDFA.SetFamilyName(o3tl::toU(rLogFont.lfFaceName));
+
+ // use the face's style name only if it looks reasonable
+ const wchar_t* pStyleName = rEnumFont.elfStyle;
+ const wchar_t* pEnd = pStyleName + sizeof(rEnumFont.elfStyle)/sizeof(*rEnumFont.elfStyle);
+ const wchar_t* p = pStyleName;
+ for(; *p && (p < pEnd); ++p )
+ if( *p < 0x0020 )
+ break;
+ if( p < pEnd )
+ aDFA.SetStyleName(o3tl::toU(pStyleName));
+
+ // heuristics for font quality
+ // - opentypeTT > truetype
+ aDFA.SetQuality( 0 );
+ if( rMetric.tmPitchAndFamily & TMPF_TRUETYPE )
+ aDFA.IncreaseQualityBy( 50 );
+ if( 0 != (rMetric.ntmFlags & (NTM_TT_OPENTYPE | NTM_PS_OPENTYPE)) )
+ aDFA.IncreaseQualityBy( 10 );
+
+ // TODO: add alias names
+ return aDFA;
+}
+
+
+static rtl::Reference<WinFontFace> ImplLogMetricToDevFontDataW( const ENUMLOGFONTEXW* pLogFont,
+ const NEWTEXTMETRICW* pMetric)
+{
+ rtl::Reference<WinFontFace> pData = new WinFontFace(
+ WinFont2DevFontAttributes(*pLogFont, *pMetric),
+ pLogFont->elfLogFont.lfCharSet,
+ pMetric->tmPitchAndFamily );
+
+ return pData;
+}
+
+void ImplSalLogFontToFontW( HDC hDC, const LOGFONTW& rLogFont, Font& rFont )
+{
+ OUString aFontName( o3tl::toU(rLogFont.lfFaceName) );
+ if (!aFontName.isEmpty())
+ {
+ rFont.SetFamilyName( aFontName );
+ rFont.SetCharSet( ImplCharSetToSal( rLogFont.lfCharSet ) );
+ rFont.SetFamily( ImplFamilyToSal( rLogFont.lfPitchAndFamily ) );
+ rFont.SetPitch( ImplLogPitchToSal( rLogFont.lfPitchAndFamily ) );
+ rFont.SetWeight( ImplWeightToSal( rLogFont.lfWeight ) );
+
+ long nFontHeight = rLogFont.lfHeight;
+ if ( nFontHeight < 0 )
+ nFontHeight = -nFontHeight;
+ long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
+ if( !nDPIY )
+ nDPIY = 600;
+ nFontHeight *= 72;
+ nFontHeight += nDPIY/2;
+ nFontHeight /= nDPIY;
+ rFont.SetFontSize( Size( 0, nFontHeight ) );
+ rFont.SetOrientation( static_cast<short>(rLogFont.lfEscapement) );
+ if ( rLogFont.lfItalic )
+ rFont.SetItalic( ITALIC_NORMAL );
+ else
+ rFont.SetItalic( ITALIC_NONE );
+ if ( rLogFont.lfUnderline )
+ rFont.SetUnderline( LINESTYLE_SINGLE );
+ else
+ rFont.SetUnderline( LINESTYLE_NONE );
+ if ( rLogFont.lfStrikeOut )
+ rFont.SetStrikeout( STRIKEOUT_SINGLE );
+ else
+ rFont.SetStrikeout( STRIKEOUT_NONE );
+ }
+}
+
+WinFontFace::WinFontFace( const FontAttributes& rDFS,
+ BYTE eWinCharSet, BYTE nPitchAndFamily )
+: PhysicalFontFace( rDFS ),
+ mnId( 0 ),
+ mbFontCapabilitiesRead( false ),
+ meWinCharSet( eWinCharSet ),
+ mnPitchAndFamily( nPitchAndFamily ),
+ mbAliasSymbolsHigh( false ),
+ mbAliasSymbolsLow( false )
+{
+ if( eWinCharSet == SYMBOL_CHARSET )
+ {
+ if( (nPitchAndFamily & TMPF_TRUETYPE) != 0 )
+ {
+ // truetype fonts need their symbols as U+F0xx
+ mbAliasSymbolsHigh = true;
+ }
+ else if( (nPitchAndFamily & (TMPF_VECTOR|TMPF_DEVICE))
+ == (TMPF_VECTOR|TMPF_DEVICE) )
+ {
+ // scalable device fonts (e.g. builtin printer fonts)
+ // need their symbols as U+00xx
+ mbAliasSymbolsLow = true;
+ }
+ else if( (nPitchAndFamily & (TMPF_VECTOR|TMPF_TRUETYPE)) == 0 )
+ {
+ // bitmap fonts need their symbols as U+F0xx
+ mbAliasSymbolsHigh = true;
+ }
+ }
+}
+
+WinFontFace::~WinFontFace()
+{
+ mxUnicodeMap.clear();
+}
+
+sal_IntPtr WinFontFace::GetFontId() const
+{
+ return mnId;
+}
+
+rtl::Reference<LogicalFontInstance> WinFontFace::CreateFontInstance(const FontSelectPattern& rFSD) const
+{
+ return new WinFontInstance(*this, rFSD);
+}
+
+static DWORD CalcTag( const char p[5]) { return (p[0]+(p[1]<<8)+(p[2]<<16)+(p[3]<<24)); }
+
+void WinFontFace::UpdateFromHDC( HDC hDC ) const
+{
+ // short circuit if already initialized
+ if( mxUnicodeMap.is() )
+ return;
+
+ ReadCmapTable( hDC );
+ GetFontCapabilities( hDC );
+}
+
+FontCharMapRef WinFontFace::GetFontCharMap() const
+{
+ return mxUnicodeMap;
+}
+
+bool WinFontFace::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
+{
+ rFontCapabilities = maFontCapabilities;
+ return rFontCapabilities.oUnicodeRange || rFontCapabilities.oCodePageRange;
+}
+
+void WinFontFace::ReadCmapTable( HDC hDC ) const
+{
+ if( mxUnicodeMap.is() )
+ return;
+
+ bool bIsSymbolFont = (meWinCharSet == SYMBOL_CHARSET);
+ // get the CMAP table from the font which is selected into the DC
+ const DWORD nCmapTag = CalcTag( "cmap" );
+ const RawFontData aRawFontData( hDC, nCmapTag );
+ // parse the CMAP table if available
+ if( aRawFontData.get() ) {
+ CmapResult aResult;
+ ParseCMAP( aRawFontData.get(), aRawFontData.size(), aResult );
+ aResult.mbSymbolic = bIsSymbolFont;
+ if( aResult.mnRangeCount > 0 )
+ {
+ FontCharMapRef pUnicodeMap(new FontCharMap(aResult));
+ mxUnicodeMap = pUnicodeMap;
+ }
+ }
+
+ if( !mxUnicodeMap.is() )
+ {
+ mxUnicodeMap = FontCharMap::GetDefaultMap( bIsSymbolFont );
+ }
+}
+
+void WinFontFace::GetFontCapabilities( HDC hDC ) const
+{
+ // read this only once per font
+ if( mbFontCapabilitiesRead )
+ return;
+
+ mbFontCapabilitiesRead = true;
+
+ // OS/2 table
+ const DWORD OS2Tag = CalcTag( "OS/2" );
+ DWORD nLength = ::GetFontData( hDC, OS2Tag, 0, nullptr, 0 );
+ if( (nLength != GDI_ERROR) && nLength )
+ {
+ std::vector<unsigned char> aTable( nLength );
+ unsigned char* pTable = aTable.data();
+ ::GetFontData( hDC, OS2Tag, 0, pTable, nLength );
+ vcl::getTTCoverage(maFontCapabilities.oUnicodeRange, maFontCapabilities.oCodePageRange, pTable, nLength);
+ }
+}
+
+void WinSalGraphics::SetTextColor( Color nColor )
+{
+ COLORREF aCol = PALETTERGB( nColor.GetRed(),
+ nColor.GetGreen(),
+ nColor.GetBlue() );
+
+ if( !mbPrinter &&
+ GetSalData()->mhDitherPal &&
+ ImplIsSysColorEntry( nColor ) )
+ {
+ aCol = PALRGB_TO_RGB( aCol );
+ }
+
+ ::SetTextColor( getHDC(), aCol );
+}
+
+static int CALLBACK SalEnumQueryFontProcExW( const LOGFONTW*,
+ const TEXTMETRICW*,
+ DWORD, LPARAM lParam )
+{
+ *reinterpret_cast<bool*>(lParam) = true;
+ return 0;
+}
+
+void ImplGetLogFontFromFontSelect( HDC hDC,
+ const FontSelectPattern& rFont,
+ const PhysicalFontFace* pFontFace,
+ LOGFONTW& rLogFont )
+{
+ OUString aName;
+ if (pFontFace)
+ aName = pFontFace->GetFamilyName();
+ else
+ aName = rFont.GetFamilyName().getToken( 0, ';' );
+
+ UINT nNameLen = aName.getLength();
+ if (nNameLen >= LF_FACESIZE)
+ nNameLen = LF_FACESIZE - 1;
+ memcpy( rLogFont.lfFaceName, aName.getStr(), nNameLen*sizeof( wchar_t ) );
+ rLogFont.lfFaceName[nNameLen] = 0;
+
+ if (pFontFace)
+ {
+ const WinFontFace* pWinFontData = static_cast<const WinFontFace*>(pFontFace);
+ rLogFont.lfCharSet = pWinFontData->GetCharSet();
+ rLogFont.lfPitchAndFamily = pWinFontData->GetPitchAndFamily();
+ }
+ else
+ {
+ rLogFont.lfCharSet = rFont.IsSymbolFont() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
+ rLogFont.lfPitchAndFamily = ImplPitchToWin( rFont.GetPitch() )
+ | ImplFamilyToWin( rFont.GetFamilyType() );
+ }
+
+ static BYTE nDefaultQuality = NONANTIALIASED_QUALITY;
+ if (nDefaultQuality == NONANTIALIASED_QUALITY)
+ {
+ if (OpenGLWrapper::isVCLOpenGLEnabled())
+ nDefaultQuality = ANTIALIASED_QUALITY;
+ else
+ nDefaultQuality = DEFAULT_QUALITY;
+ }
+
+ rLogFont.lfWeight = ImplWeightToWin( rFont.GetWeight() );
+ rLogFont.lfHeight = static_cast<LONG>(-rFont.mnHeight);
+ rLogFont.lfWidth = static_cast<LONG>(rFont.mnWidth);
+ rLogFont.lfUnderline = 0;
+ rLogFont.lfStrikeOut = 0;
+ rLogFont.lfItalic = BYTE(rFont.GetItalic() != ITALIC_NONE);
+ rLogFont.lfEscapement = rFont.mnOrientation;
+ rLogFont.lfOrientation = rLogFont.lfEscapement;
+ rLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ rLogFont.lfQuality = nDefaultQuality;
+ rLogFont.lfOutPrecision = OUT_TT_PRECIS;
+ if ( rFont.mnOrientation )
+ rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
+
+ // disable antialiasing if requested
+ if ( rFont.mbNonAntialiased )
+ rLogFont.lfQuality = NONANTIALIASED_QUALITY;
+
+ // select vertical mode if requested and available
+ if ( rFont.mbVertical && nNameLen )
+ {
+ // vertical fonts start with an '@'
+ memmove( &rLogFont.lfFaceName[1], &rLogFont.lfFaceName[0],
+ sizeof(rLogFont.lfFaceName)-sizeof(rLogFont.lfFaceName[0]) );
+ rLogFont.lfFaceName[0] = '@';
+
+ // check availability of vertical mode for this font
+ bool bAvailable = false;
+ EnumFontFamiliesExW( hDC, &rLogFont, SalEnumQueryFontProcExW,
+ reinterpret_cast<LPARAM>(&bAvailable), 0 );
+
+ if( !bAvailable )
+ {
+ // restore non-vertical name if not vertical mode isn't available
+ memcpy( &rLogFont.lfFaceName[0], aName.getStr(), nNameLen*sizeof(wchar_t) );
+ rLogFont.lfFaceName[nNameLen] = '\0';
+ // keep it upright and create the font for sideway glyphs later.
+ rLogFont.lfEscapement = rLogFont.lfEscapement - 2700;
+ rLogFont.lfOrientation = rLogFont.lfEscapement;
+ }
+ }
+}
+
+HFONT WinSalGraphics::ImplDoSetFont(FontSelectPattern const & i_rFont,
+ const PhysicalFontFace * i_pFontFace,
+ HFONT& o_rOldFont)
+{
+ HFONT hNewFont = nullptr;
+
+ LOGFONTW aLogFont;
+ ImplGetLogFontFromFontSelect( getHDC(), i_rFont, i_pFontFace, aLogFont );
+
+ hNewFont = ::CreateFontIndirectW( &aLogFont );
+
+ HDC hdcScreen = nullptr;
+ if( mbVirDev )
+ // only required for virtual devices, see below for details
+ hdcScreen = GetDC(nullptr);
+ if( hdcScreen )
+ {
+ // select font into screen hdc first to get an antialiased font
+ // and instantly restore the default font!
+ // see knowledge base article 305290:
+ // "PRB: Fonts Not Drawn Antialiased on Device Context for DirectDraw Surface"
+ SelectFont( hdcScreen, SelectFont( hdcScreen , hNewFont ) );
+ }
+ o_rOldFont = ::SelectFont( getHDC(), hNewFont );
+
+ TEXTMETRICW aTextMetricW;
+ if( !::GetTextMetricsW( getHDC(), &aTextMetricW ) )
+ {
+ // the selected font doesn't work => try a replacement
+ // TODO: use its font fallback instead
+ lstrcpynW( aLogFont.lfFaceName, L"Courier New", 12 );
+ aLogFont.lfPitchAndFamily = FIXED_PITCH;
+ HFONT hNewFont2 = CreateFontIndirectW( &aLogFont );
+ SelectFont( getHDC(), hNewFont2 );
+ DeleteFont( hNewFont );
+ hNewFont = hNewFont2;
+ }
+
+ if( hdcScreen )
+ ::ReleaseDC( nullptr, hdcScreen );
+
+ return hNewFont;
+}
+
+void WinSalGraphics::SetFont(LogicalFontInstance* pFont, int nFallbackLevel)
+{
+ // return early if there is no new font
+ if( !pFont )
+ {
+ if (!mpWinFontEntry[nFallbackLevel].is())
+ return;
+
+ // select original DC font
+ assert(mhDefFont);
+ ::SelectFont(getHDC(), mhDefFont);
+ mhDefFont = nullptr;
+
+ // release no longer referenced font handles
+ for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
+ mpWinFontEntry[i] = nullptr;
+ return;
+ }
+
+ WinFontInstance *pFontInstance = static_cast<WinFontInstance*>(pFont);
+ mpWinFontEntry[ nFallbackLevel ] = pFontInstance;
+
+ HFONT hOldFont = nullptr;
+ HFONT hNewFont = pFontInstance->GetHFONT();
+ if (!hNewFont)
+ {
+ pFontInstance->SetGraphics(this);
+ hNewFont = pFontInstance->GetHFONT();
+ }
+ hOldFont = ::SelectFont(getHDC(), hNewFont);
+
+ // keep default font
+ if( !mhDefFont )
+ mhDefFont = hOldFont;
+ else
+ {
+ // release no longer referenced font handles
+ for( int i = nFallbackLevel + 1; i < MAX_FALLBACK && mpWinFontEntry[i].is(); ++i )
+ mpWinFontEntry[i] = nullptr;
+ }
+
+ // now the font is live => update font face
+ const WinFontFace* pFontFace = pFontInstance->GetFontFace();
+ pFontFace->UpdateFromHDC(getHDC());
+}
+
+void WinSalGraphics::GetFontMetric( ImplFontMetricDataRef& rxFontMetric, int nFallbackLevel )
+{
+ // temporarily change the HDC to the font in the fallback level
+ rtl::Reference<WinFontInstance> pFontInstance = mpWinFontEntry[nFallbackLevel];
+ const HFONT hOldFont = SelectFont(getHDC(), pFontInstance->GetHFONT());
+
+ wchar_t aFaceName[LF_FACESIZE+60];
+ if( GetTextFaceW( getHDC(), SAL_N_ELEMENTS(aFaceName), aFaceName ) )
+ rxFontMetric->SetFamilyName(o3tl::toU(aFaceName));
+
+ rxFontMetric->SetMinKashida(pFontInstance->GetKashidaWidth());
+ rxFontMetric->ImplCalcLineSpacing(pFontInstance.get());
+
+ // get the font metric
+ OUTLINETEXTMETRICW aOutlineMetric;
+ const bool bOK = GetOutlineTextMetricsW(getHDC(), sizeof(aOutlineMetric), &aOutlineMetric);
+ // restore the HDC to the font in the base level
+ SelectFont( getHDC(), hOldFont );
+ if( !bOK )
+ return;
+
+ TEXTMETRICW aWinMetric = aOutlineMetric.otmTextMetrics;
+
+ // device independent font attributes
+ rxFontMetric->SetFamilyType(ImplFamilyToSal( aWinMetric.tmPitchAndFamily ));
+ rxFontMetric->SetSymbolFlag(aWinMetric.tmCharSet == SYMBOL_CHARSET);
+ rxFontMetric->SetWeight(ImplWeightToSal( aWinMetric.tmWeight ));
+ rxFontMetric->SetPitch(ImplMetricPitchToSal( aWinMetric.tmPitchAndFamily ));
+ rxFontMetric->SetItalic(aWinMetric.tmItalic ? ITALIC_NORMAL : ITALIC_NONE);
+ rxFontMetric->SetSlant( 0 );
+
+ // transformation dependent font metrics
+ rxFontMetric->SetWidth(static_cast<int>(pFontInstance->GetScale() * aWinMetric.tmAveCharWidth));
+}
+
+FontCharMapRef WinSalGraphics::GetFontCharMap() const
+{
+ if (!mpWinFontEntry[0])
+ {
+ return FontCharMapRef( new FontCharMap() );
+ }
+ return mpWinFontEntry[0]->GetFontFace()->GetFontCharMap();
+}
+
+bool WinSalGraphics::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
+{
+ if (!mpWinFontEntry[0])
+ return false;
+ return mpWinFontEntry[0]->GetFontFace()->GetFontCapabilities(rFontCapabilities);
+}
+
+static int CALLBACK SalEnumFontsProcExW( const LOGFONTW* lpelfe,
+ const TEXTMETRICW* lpntme,
+ DWORD nFontType, LPARAM lParam )
+{
+ ENUMLOGFONTEXW const * pLogFont
+ = reinterpret_cast<ENUMLOGFONTEXW const *>(lpelfe);
+ NEWTEXTMETRICEXW const * pMetric
+ = reinterpret_cast<NEWTEXTMETRICEXW const *>(lpntme);
+ ImplEnumInfo* pInfo = reinterpret_cast<ImplEnumInfo*>(lParam);
+ if ( !pInfo->mpName )
+ {
+ // Ignore vertical fonts
+ if ( pLogFont->elfLogFont.lfFaceName[0] != '@' )
+ {
+ OUString aName = o3tl::toU(pLogFont->elfLogFont.lfFaceName);
+ pInfo->mpName = &aName;
+ memcpy(pInfo->mpLogFont->lfFaceName, pLogFont->elfLogFont.lfFaceName, (aName.getLength()+1)*sizeof(wchar_t));
+ pInfo->mpLogFont->lfCharSet = pLogFont->elfLogFont.lfCharSet;
+ EnumFontFamiliesExW(pInfo->mhDC, pInfo->mpLogFont, SalEnumFontsProcExW,
+ reinterpret_cast<LPARAM>(pInfo), 0);
+ pInfo->mpLogFont->lfFaceName[0] = '\0';
+ pInfo->mpLogFont->lfCharSet = DEFAULT_CHARSET;
+ pInfo->mpName = nullptr;
+ }
+ }
+ else
+ {
+ // Ignore non-device fonts on printers.
+ if (pInfo->mbPrinter)
+ {
+ if ((nFontType & RASTER_FONTTYPE) && !(nFontType & DEVICE_FONTTYPE))
+ {
+ SAL_INFO("vcl.fonts", "Unsupported printer font ignored: " << OUString(o3tl::toU(pLogFont->elfLogFont.lfFaceName)));
+ return 1;
+ }
+ }
+ // Only SFNT fonts are supported, ignore anything else.
+ else if (!(nFontType & TRUETYPE_FONTTYPE) &&
+ !(pMetric->ntmTm.ntmFlags & NTM_PS_OPENTYPE) &&
+ !(pMetric->ntmTm.ntmFlags & NTM_TT_OPENTYPE))
+ {
+ SAL_INFO("vcl.fonts", "Unsupported font ignored: " << OUString(o3tl::toU(pLogFont->elfLogFont.lfFaceName)));
+ return 1;
+ }
+
+ rtl::Reference<WinFontFace> pData = ImplLogMetricToDevFontDataW(pLogFont, &(pMetric->ntmTm));
+ pData->SetFontId( sal_IntPtr( pInfo->mnFontCount++ ) );
+
+ pInfo->mpList->Add( pData.get() );
+ SAL_INFO("vcl.fonts", "SalEnumFontsProcExW: font added: " << pData->GetFamilyName() << " " << pData->GetStyleName());
+ }
+
+ return 1;
+}
+
+struct TempFontItem
+{
+ OUString maFontResourcePath;
+ TempFontItem* mpNextItem;
+};
+
+static int lcl_AddFontResource(SalData& rSalData, const OUString& rFontFileURL, bool bShared)
+{
+ OUString aFontSystemPath;
+ OSL_VERIFY(!osl::FileBase::getSystemPathFromFileURL(rFontFileURL, aFontSystemPath));
+
+ int nRet = AddFontResourceExW(o3tl::toW(aFontSystemPath.getStr()), FR_PRIVATE, nullptr);
+ SAL_WARN_IF(nRet <= 0, "vcl.fonts", "AddFontResourceExW failed for " << rFontFileURL);
+ if (nRet > 0)
+ {
+ TempFontItem* pNewItem = new TempFontItem;
+ pNewItem->maFontResourcePath = aFontSystemPath;
+ if (bShared)
+ {
+ pNewItem->mpNextItem = rSalData.mpSharedTempFontItem;
+ rSalData.mpSharedTempFontItem = pNewItem;
+ }
+ else
+ {
+ pNewItem->mpNextItem = rSalData.mpOtherTempFontItem;
+ rSalData.mpOtherTempFontItem = pNewItem;
+ }
+ }
+ return nRet;
+}
+
+void ImplReleaseTempFonts(SalData& rSalData, bool bAll)
+{
+ while (TempFontItem* p = rSalData.mpOtherTempFontItem)
+ {
+ RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
+ rSalData.mpOtherTempFontItem = p->mpNextItem;
+ delete p;
+ }
+
+ if (!bAll)
+ return;
+
+ while (TempFontItem* p = rSalData.mpSharedTempFontItem)
+ {
+ RemoveFontResourceExW(o3tl::toW(p->maFontResourcePath.getStr()), FR_PRIVATE, nullptr);
+ rSalData.mpSharedTempFontItem = p->mpNextItem;
+ delete p;
+ }
+}
+
+static OUString lcl_GetFontFamilyName(const OUString& rFontFileURL)
+{
+ // Create temporary file name
+ OUString aTempFileURL;
+ if (osl::File::E_None != osl::File::createTempFile(nullptr, nullptr, &aTempFileURL))
+ return OUString();
+ osl::File::remove(aTempFileURL);
+ OUString aResSystemPath;
+ osl::FileBase::getSystemPathFromFileURL(aTempFileURL, aResSystemPath);
+
+ // Create font resource file (.fot)
+ // There is a limit of 127 characters for the full path passed via lpszFile, so we have to
+ // split the font URL and pass it as two parameters. As a result we can't use
+ // CreateScalableFontResource for renaming, as it now expects the font in the system path.
+ // But it's still good to use it for family name extraction, we're currently after.
+ // BTW: it doesn't help to prefix the lpszFile with \\?\ to support larger paths.
+ // TODO: use TTLoadEmbeddedFont (needs an EOT as input, so we have to add a header to the TTF)
+ // TODO: forward the EOT from the AddTempDevFont call side, if VCL supports it
+ INetURLObject aTTFUrl(rFontFileURL);
+ // GetBase() strips the extension
+ OUString aFilename = aTTFUrl.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
+ if (!CreateScalableFontResourceW(0, o3tl::toW(aResSystemPath.getStr()),
+ o3tl::toW(aFilename.getStr()), o3tl::toW(aTTFUrl.GetPath().getStr())))
+ {
+ sal_uInt32 nError = GetLastError();
+ SAL_WARN("vcl.fonts", "CreateScalableFontResource failed for " << aResSystemPath << " "
+ << aFilename << " " << aTTFUrl.GetPath() << " " << nError);
+ return OUString();
+ }
+
+ // Open and read the font resource file
+ osl::File aFotFile(aTempFileURL);
+ if (osl::FileBase::E_None != aFotFile.open(osl_File_OpenFlag_Read))
+ return OUString();
+
+ sal_uInt64 nBytesRead = 0;
+ char aBuffer[4096];
+ aFotFile.read( aBuffer, sizeof( aBuffer ), nBytesRead );
+ // clean up temporary resource file
+ aFotFile.close();
+ osl::File::remove(aTempFileURL);
+
+ // retrieve font family name from byte offset 0x4F6
+ static const sal_uInt64 nNameOfs = 0x4F6;
+ sal_uInt64 nPos = nNameOfs;
+ for (; (nPos < nBytesRead) && (aBuffer[nPos] != 0); nPos++);
+ if (nPos >= nBytesRead || (nPos == nNameOfs))
+ return OUString();
+
+ return OUString(aBuffer + nNameOfs, nPos - nNameOfs, osl_getThreadTextEncoding());
+}
+
+bool WinSalGraphics::AddTempDevFont(PhysicalFontCollection* pFontCollection,
+ const OUString& rFontFileURL, const OUString& rFontName)
+{
+ OUString aFontFamily = lcl_GetFontFamilyName(rFontFileURL);
+ if (aFontFamily.isEmpty())
+ {
+ SAL_WARN("vcl.fonts", "error extracting font family from " << rFontFileURL);
+ return false;
+ }
+
+ if (rFontName != aFontFamily)
+ {
+ SAL_WARN("vcl.fonts", "font family renaming not implemented; skipping embedded " << rFontName);
+ return false;
+ }
+
+ int nFonts = lcl_AddFontResource(*GetSalData(), rFontFileURL, false);
+ if (nFonts <= 0)
+ return false;
+
+ ImplEnumInfo aInfo;
+ aInfo.mhDC = getHDC();
+ aInfo.mpList = pFontCollection;
+ aInfo.mpName = &aFontFamily;
+ aInfo.mbPrinter = mbPrinter;
+ aInfo.mnFontCount = pFontCollection->Count();
+ const int nExpectedFontCount = aInfo.mnFontCount + nFonts;
+
+ LOGFONTW aLogFont = {};
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+ aInfo.mpLogFont = &aLogFont;
+
+ // add the font to the PhysicalFontCollection
+ EnumFontFamiliesExW(getHDC(), &aLogFont,
+ SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0);
+
+ SAL_WARN_IF(nExpectedFontCount != pFontCollection->Count(), "vcl.fonts",
+ "temp font was registered but is not in enumeration: " << rFontFileURL);
+
+ return true;
+}
+
+void WinSalGraphics::GetDevFontList( PhysicalFontCollection* pFontCollection )
+{
+ // make sure all LO shared fonts are registered temporarily
+ static std::once_flag init;
+ std::call_once(init, []()
+ {
+ auto registerFontsIn = [](const OUString& dir) {
+ // collect fonts in font path that could not be registered
+ osl::Directory aFontDir(dir);
+ osl::FileBase::RC rcOSL = aFontDir.open();
+ if (rcOSL == osl::FileBase::E_None)
+ {
+ osl::DirectoryItem aDirItem;
+ SalData* pSalData = GetSalData();
+ assert(pSalData);
+
+ while (aFontDir.getNextItem(aDirItem, 10) == osl::FileBase::E_None)
+ {
+ osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL);
+ rcOSL = aDirItem.getFileStatus(aFileStatus);
+ if (rcOSL == osl::FileBase::E_None)
+ lcl_AddFontResource(*pSalData, aFileStatus.getFileURL(), true);
+ }
+ }
+ };
+
+ // determine font path
+ // since we are only interested in fonts that could not be
+ // registered before because of missing administration rights
+ // only the font path of the user installation is needed
+ OUString aPath("$BRAND_BASE_DIR");
+ rtl_bootstrap_expandMacros(&aPath.pData);
+
+ // internal font resources, required for normal operation, like OpenSymbol
+ registerFontsIn(aPath + "/" LIBO_SHARE_RESOURCE_FOLDER "/common/fonts");
+
+ // collect fonts in font path that could not be registered
+ registerFontsIn(aPath + "/" LIBO_SHARE_FOLDER "/fonts/truetype");
+
+ return true;
+ });
+
+ ImplEnumInfo aInfo;
+ aInfo.mhDC = getHDC();
+ aInfo.mpList = pFontCollection;
+ aInfo.mpName = nullptr;
+ aInfo.mbPrinter = mbPrinter;
+ aInfo.mnFontCount = 0;
+
+ LOGFONTW aLogFont = {};
+ aLogFont.lfCharSet = DEFAULT_CHARSET;
+ aInfo.mpLogFont = &aLogFont;
+
+ // fill the PhysicalFontCollection
+ EnumFontFamiliesExW( getHDC(), &aLogFont,
+ SalEnumFontsProcExW, reinterpret_cast<LPARAM>(&aInfo), 0 );
+
+ // set glyph fallback hook
+ static WinGlyphFallbackSubstititution aSubstFallback;
+ static WinPreMatchFontSubstititution aPreMatchFont;
+ pFontCollection->SetFallbackHook( &aSubstFallback );
+ pFontCollection->SetPreMatchHook(&aPreMatchFont);
+}
+
+void WinSalGraphics::ClearDevFontCache()
+{
+ WinSalGraphicsImplBase* pImpl = dynamic_cast<WinSalGraphicsImplBase*>(GetImpl());
+ assert(pImpl != nullptr);
+ pImpl->ClearDevFontCache();
+ ImplReleaseTempFonts(*GetSalData(), false);
+}
+
+bool WinFontInstance::ImplGetGlyphBoundRect(sal_GlyphId nId, tools::Rectangle& rRect, bool) const
+{
+ assert(m_pGraphics);
+ HDC hDC = m_pGraphics->getHDC();
+ const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
+ const HFONT hFont = GetHFONT();
+ if (hFont != hOrigFont)
+ SelectObject(hDC, hFont);
+
+ const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]()
+ { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
+ const float fFontScale = GetScale();
+
+ // use unity matrix
+ MAT2 aMat;
+ aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
+ aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
+
+ UINT nGGOFlags = GGO_METRICS;
+ nGGOFlags |= GGO_GLYPH_INDEX;
+
+ GLYPHMETRICS aGM;
+ aGM.gmptGlyphOrigin.x = aGM.gmptGlyphOrigin.y = 0;
+ aGM.gmBlackBoxX = aGM.gmBlackBoxY = 0;
+ DWORD nSize = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGM, 0, nullptr, &aMat);
+ if (nSize == GDI_ERROR)
+ return false;
+
+ rRect = tools::Rectangle( Point( +aGM.gmptGlyphOrigin.x, -aGM.gmptGlyphOrigin.y ),
+ Size( aGM.gmBlackBoxX, aGM.gmBlackBoxY ) );
+ rRect.SetLeft(static_cast<int>( fFontScale * rRect.Left() ));
+ rRect.SetRight(static_cast<int>( fFontScale * rRect.Right() ) + 1);
+ rRect.SetTop(static_cast<int>( fFontScale * rRect.Top() ));
+ rRect.SetBottom(static_cast<int>( fFontScale * rRect.Bottom() ) + 1);
+ return true;
+}
+
+bool WinFontInstance::GetGlyphOutline(sal_GlyphId nId, basegfx::B2DPolyPolygon& rB2DPolyPoly, bool) const
+{
+ rB2DPolyPoly.clear();
+
+ assert(m_pGraphics);
+ HDC hDC = m_pGraphics->getHDC();
+ const HFONT hOrigFont = static_cast<HFONT>(GetCurrentObject(hDC, OBJ_FONT));
+ const HFONT hFont = GetHFONT();
+ if (hFont != hOrigFont)
+ SelectObject(hDC, hFont);
+
+ const ::comphelper::ScopeGuard aFontRestoreScopeGuard([hFont, hOrigFont, hDC]()
+ { if (hFont != hOrigFont) SelectObject(hDC, hOrigFont); });
+
+ // use unity matrix
+ MAT2 aMat;
+ aMat.eM11 = aMat.eM22 = FixedFromDouble( 1.0 );
+ aMat.eM12 = aMat.eM21 = FixedFromDouble( 0.0 );
+
+ UINT nGGOFlags = GGO_NATIVE;
+ nGGOFlags |= GGO_GLYPH_INDEX;
+
+ GLYPHMETRICS aGlyphMetrics;
+ const DWORD nSize1 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags, &aGlyphMetrics, 0, nullptr, &aMat);
+ if( !nSize1 ) // blank glyphs are ok
+ return true;
+ else if( nSize1 == GDI_ERROR )
+ return false;
+
+ BYTE* pData = new BYTE[ nSize1 ];
+ const DWORD nSize2 = ::GetGlyphOutlineW(hDC, nId, nGGOFlags,
+ &aGlyphMetrics, nSize1, pData, &aMat );
+
+ if( nSize1 != nSize2 )
+ return false;
+
+ // TODO: avoid tools polygon by creating B2DPolygon directly
+ int nPtSize = 512;
+ Point* pPoints = new Point[ nPtSize ];
+ PolyFlags* pFlags = new PolyFlags[ nPtSize ];
+
+ TTPOLYGONHEADER* pHeader = reinterpret_cast<TTPOLYGONHEADER*>(pData);
+ while( reinterpret_cast<BYTE*>(pHeader) < pData+nSize2 )
+ {
+ // only outline data is interesting
+ if( pHeader->dwType != TT_POLYGON_TYPE )
+ break;
+
+ // get start point; next start points are end points
+ // of previous segment
+ sal_uInt16 nPnt = 0;
+
+ long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
+ long nY = IntTimes256FromFixed( pHeader->pfxStart.y );
+ pPoints[ nPnt ] = Point( nX, nY );
+ pFlags[ nPnt++ ] = PolyFlags::Normal;
+
+ bool bHasOfflinePoints = false;
+ TTPOLYCURVE* pCurve = reinterpret_cast<TTPOLYCURVE*>( pHeader + 1 );
+ pHeader = reinterpret_cast<TTPOLYGONHEADER*>( reinterpret_cast<BYTE*>(pHeader) + pHeader->cb );
+ while( reinterpret_cast<BYTE*>(pCurve) < reinterpret_cast<BYTE*>(pHeader) )
+ {
+ int nNeededSize = nPnt + 16 + 3 * pCurve->cpfx;
+ if( nPtSize < nNeededSize )
+ {
+ Point* pOldPoints = pPoints;
+ PolyFlags* pOldFlags = pFlags;
+ nPtSize = 2 * nNeededSize;
+ pPoints = new Point[ nPtSize ];
+ pFlags = new PolyFlags[ nPtSize ];
+ for( sal_uInt16 i = 0; i < nPnt; ++i )
+ {
+ pPoints[ i ] = pOldPoints[ i ];
+ pFlags[ i ] = pOldFlags[ i ];
+ }
+ delete[] pOldPoints;
+ delete[] pOldFlags;
+ }
+
+ int i = 0;
+ if( TT_PRIM_LINE == pCurve->wType )
+ {
+ while( i < pCurve->cpfx )
+ {
+ nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
+ nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
+ ++i;
+ pPoints[ nPnt ] = Point( nX, nY );
+ pFlags[ nPnt ] = PolyFlags::Normal;
+ ++nPnt;
+ }
+ }
+ else if( TT_PRIM_QSPLINE == pCurve->wType )
+ {
+ bHasOfflinePoints = true;
+ while( i < pCurve->cpfx )
+ {
+ // get control point of quadratic bezier spline
+ nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
+ nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
+ ++i;
+ Point aControlP( nX, nY );
+
+ // calculate first cubic control point
+ // P0 = 1/3 * (PBeg + 2 * PQControl)
+ nX = pPoints[ nPnt-1 ].X() + 2 * aControlP.X();
+ nY = pPoints[ nPnt-1 ].Y() + 2 * aControlP.Y();
+ pPoints[ nPnt+0 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
+ pFlags[ nPnt+0 ] = PolyFlags::Control;
+
+ // calculate endpoint of segment
+ nX = IntTimes256FromFixed( pCurve->apfx[ i ].x );
+ nY = IntTimes256FromFixed( pCurve->apfx[ i ].y );
+
+ if ( i+1 >= pCurve->cpfx )
+ {
+ // endpoint is either last point in segment => advance
+ ++i;
+ }
+ else
+ {
+ // or endpoint is the middle of two control points
+ nX += IntTimes256FromFixed( pCurve->apfx[ i-1 ].x );
+ nY += IntTimes256FromFixed( pCurve->apfx[ i-1 ].y );
+ nX = (nX + 1) / 2;
+ nY = (nY + 1) / 2;
+ // no need to advance, because the current point
+ // is the control point in next bezier spline
+ }
+
+ pPoints[ nPnt+2 ] = Point( nX, nY );
+ pFlags[ nPnt+2 ] = PolyFlags::Normal;
+
+ // calculate second cubic control point
+ // P1 = 1/3 * (PEnd + 2 * PQControl)
+ nX = pPoints[ nPnt+2 ].X() + 2 * aControlP.X();
+ nY = pPoints[ nPnt+2 ].Y() + 2 * aControlP.Y();
+ pPoints[ nPnt+1 ] = Point( (2*nX+3)/6, (2*nY+3)/6 );
+ pFlags[ nPnt+1 ] = PolyFlags::Control;
+
+ nPnt += 3;
+ }
+ }
+
+ // next curve segment
+ pCurve = reinterpret_cast<TTPOLYCURVE*>(&pCurve->apfx[ i ]);
+ }
+
+ // end point is start point for closed contour
+ // disabled, because Polygon class closes the contour itself
+ // pPoints[nPnt++] = pPoints[0];
+ // #i35928#
+ // Added again, but add only when not yet closed
+ if(pPoints[nPnt - 1] != pPoints[0])
+ {
+ if( bHasOfflinePoints )
+ pFlags[nPnt] = pFlags[0];
+
+ pPoints[nPnt++] = pPoints[0];
+ }
+
+ // convert y-coordinates W32 -> VCL
+ for( int i = 0; i < nPnt; ++i )
+ pPoints[i].setY(-pPoints[i].Y());
+
+ // insert into polypolygon
+ tools::Polygon aPoly( nPnt, pPoints, (bHasOfflinePoints ? pFlags : nullptr) );
+ // convert to B2DPolyPolygon
+ // TODO: get rid of the intermediate PolyPolygon
+ rB2DPolyPoly.append( aPoly.getB2DPolygon() );
+ }
+
+ delete[] pPoints;
+ delete[] pFlags;
+
+ delete[] pData;
+
+ // rescaling needed for the tools::PolyPolygon conversion
+ if( rB2DPolyPoly.count() )
+ {
+ const double fFactor(GetScale()/256);
+ rB2DPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fFactor, fFactor));
+ }
+
+ return true;
+}
+
+class ScopedFont
+{
+public:
+ explicit ScopedFont(WinSalGraphics & rData);
+
+ ~ScopedFont();
+
+private:
+ WinSalGraphics & m_rData;
+ HFONT m_hOrigFont;
+};
+
+ScopedFont::ScopedFont(WinSalGraphics & rData): m_rData(rData), m_hOrigFont(nullptr)
+{
+ if (m_rData.mpWinFontEntry[0])
+ {
+ m_hOrigFont = m_rData.mpWinFontEntry[0]->GetHFONT();
+ m_rData.mpWinFontEntry[0]->SetHFONT(nullptr);
+ }
+}
+
+ScopedFont::~ScopedFont()
+{
+ if( m_hOrigFont )
+ {
+ // restore original font, destroy temporary font
+ HFONT hTempFont = m_rData.mpWinFontEntry[0]->GetHFONT();
+ m_rData.mpWinFontEntry[0]->SetHFONT(m_hOrigFont);
+ SelectObject( m_rData.getHDC(), m_hOrigFont );
+ DeleteObject( hTempFont );
+ }
+}
+
+namespace {
+
+class ScopedTrueTypeFont
+{
+public:
+ ScopedTrueTypeFont(): m_pFont(nullptr) {}
+
+ ~ScopedTrueTypeFont();
+
+ SFErrCodes open(void const * pBuffer, sal_uInt32 nLen, sal_uInt32 nFaceNum);
+
+ TrueTypeFont * get() const { return m_pFont; }
+
+private:
+ TrueTypeFont * m_pFont;
+};
+
+}
+
+ScopedTrueTypeFont::~ScopedTrueTypeFont()
+{
+ if (m_pFont != nullptr)
+ CloseTTFont(m_pFont);
+}
+
+SFErrCodes ScopedTrueTypeFont::open(void const * pBuffer, sal_uInt32 nLen,
+ sal_uInt32 nFaceNum)
+{
+ OSL_ENSURE(m_pFont == nullptr, "already open");
+ return OpenTTFontBuffer(pBuffer, nLen, nFaceNum, &m_pFont);
+}
+
+bool WinSalGraphics::CreateFontSubset( const OUString& rToFile,
+ const PhysicalFontFace* pFont, const sal_GlyphId* pGlyphIds, const sal_uInt8* pEncoding,
+ sal_Int32* pGlyphWidths, int nGlyphCount, FontSubsetInfo& rInfo )
+{
+ // TODO: use more of the central font-subsetting code, move stuff there if needed
+
+ // create matching FontSelectPattern
+ // we need just enough to get to the font file data
+ // use height=1000 for easier debugging (to match psprint's font units)
+ FontSelectPattern aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
+
+ // TODO: much better solution: move SetFont and restoration of old font to caller
+ ScopedFont aOldFont(*this);
+ HFONT hOldFont = nullptr;
+ ImplDoSetFont(aIFSD, pFont, hOldFont);
+
+ WinFontFace const * pWinFontData = static_cast<WinFontFace const *>(pFont);
+
+#if OSL_DEBUG_LEVEL > 1
+ // get font metrics
+ TEXTMETRICW aWinMetric;
+ if( !::GetTextMetricsW( getHDC(), &aWinMetric ) )
+ return FALSE;
+
+ SAL_WARN_IF( (aWinMetric.tmPitchAndFamily & TMPF_DEVICE), "vcl", "cannot subset device font" );
+ SAL_WARN_IF( !(aWinMetric.tmPitchAndFamily & TMPF_TRUETYPE), "vcl", "can only subset TT font" );
+#endif
+
+ OUString aSysPath;
+ if( osl_File_E_None != osl_getSystemPathFromFileURL( rToFile.pData, &aSysPath.pData ) )
+ return false;
+ const rtl_TextEncoding aThreadEncoding = osl_getThreadTextEncoding();
+ const OString aToFile(OUStringToOString(aSysPath, aThreadEncoding));
+
+ // check if the font has a CFF-table
+ const DWORD nCffTag = CalcTag( "CFF " );
+ const RawFontData aRawCffData( getHDC(), nCffTag );
+ if( aRawCffData.get() )
+ {
+ pWinFontData->UpdateFromHDC( getHDC() );
+
+ // provide a font subset from the CFF-table
+ FILE* pOutFile = fopen( aToFile.getStr(), "wb" );
+ rInfo.LoadFont( FontType::CFF_FONT, aRawCffData.get(), aRawCffData.size() );
+ bool bRC = rInfo.CreateFontSubset( FontType::TYPE1_PFB, pOutFile, nullptr,
+ pGlyphIds, pEncoding, nGlyphCount, pGlyphWidths );
+ fclose( pOutFile );
+ return bRC;
+ }
+
+ // get raw font file data
+ const RawFontData xRawFontData( getHDC(), 0 );
+ if( !xRawFontData.get() )
+ return false;
+
+ // open font file
+ sal_uInt32 nFaceNum = 0;
+ if( !*xRawFontData.get() ) // TTC candidate
+ nFaceNum = ~0U; // indicate "TTC font extracts only"
+
+ ScopedTrueTypeFont aSftTTF;
+ SFErrCodes nRC = aSftTTF.open( xRawFontData.get(), xRawFontData.size(), nFaceNum );
+ if( nRC != SFErrCodes::Ok )
+ return false;
+
+ TTGlobalFontInfo aTTInfo;
+ ::GetTTGlobalFontInfo( aSftTTF.get(), &aTTInfo );
+ rInfo.m_nFontType = FontType::SFNT_TTF;
+ rInfo.m_aPSName = ImplSalGetUniString( aTTInfo.psname );
+ rInfo.m_nAscent = aTTInfo.winAscent;
+ rInfo.m_nDescent = aTTInfo.winDescent;
+ rInfo.m_aFontBBox = tools::Rectangle( Point( aTTInfo.xMin, aTTInfo.yMin ),
+ Point( aTTInfo.xMax, aTTInfo.yMax ) );
+ rInfo.m_nCapHeight = aTTInfo.yMax; // Well ...
+
+ // subset TTF-glyphs and get their properties
+ // take care that subset fonts require the NotDef glyph in pos 0
+ int nOrigCount = nGlyphCount;
+ sal_uInt16 aShortIDs[ 256 ];
+ sal_uInt8 aTempEncs[ 256 ];
+
+ int nNotDef=-1, i;
+ for( i = 0; i < nGlyphCount; ++i )
+ {
+ aTempEncs[i] = pEncoding[i];
+ aShortIDs[i] = static_cast<sal_uInt16>(pGlyphIds[i]);
+ if (!aShortIDs[i])
+ if( nNotDef < 0 )
+ nNotDef = i; // first NotDef glyph found
+ }
+
+ if( nNotDef != 0 )
+ {
+ // add fake NotDef glyph if needed
+ if( nNotDef < 0 )
+ nNotDef = nGlyphCount++;
+
+ // NotDef glyph must be in pos 0 => swap glyphids
+ aShortIDs[ nNotDef ] = aShortIDs[0];
+ aTempEncs[ nNotDef ] = aTempEncs[0];
+ aShortIDs[0] = 0;
+ aTempEncs[0] = 0;
+ }
+ SAL_WARN_IF( nGlyphCount >= 257, "vcl", "too many glyphs for subsetting" );
+
+ // fill pWidth array
+ std::unique_ptr<sal_uInt16[]> pMetrics =
+ ::GetTTSimpleGlyphMetrics( aSftTTF.get(), aShortIDs, nGlyphCount, aIFSD.mbVertical );
+ if( !pMetrics )
+ return false;
+ sal_uInt16 nNotDefAdv = pMetrics[0];
+ pMetrics[0] = pMetrics[nNotDef];
+ pMetrics[nNotDef] = nNotDefAdv;
+ for( i = 0; i < nOrigCount; ++i )
+ pGlyphWidths[i] = pMetrics[i];
+ pMetrics.reset();
+
+ // write subset into destination file
+ nRC = ::CreateTTFromTTGlyphs( aSftTTF.get(), aToFile.getStr(), aShortIDs,
+ aTempEncs, nGlyphCount );
+ return (nRC == SFErrCodes::Ok);
+}
+
+const void* WinSalGraphics::GetEmbedFontData(const PhysicalFontFace* pFont, long* pDataLen)
+{
+ // create matching FontSelectPattern
+ // we need just enough to get to the font file data
+ FontSelectPattern aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
+
+ ScopedFont aOldFont(*this);
+
+ HFONT hOldFont = nullptr;
+ ImplDoSetFont(aIFSD, pFont, hOldFont);
+
+ // get the raw font file data
+ RawFontData aRawFontData( getHDC() );
+ *pDataLen = aRawFontData.size();
+ if( !aRawFontData.get() )
+ return nullptr;
+
+ const unsigned char* pData = aRawFontData.steal();
+ return pData;
+}
+
+void WinSalGraphics::FreeEmbedFontData( const void* pData, long /*nLen*/ )
+{
+ delete[] static_cast<char const *>(pData);
+}
+
+void WinSalGraphics::GetGlyphWidths( const PhysicalFontFace* pFont,
+ bool bVertical,
+ std::vector< sal_Int32 >& rWidths,
+ Ucs2UIntMap& rUnicodeEnc )
+{
+ // create matching FontSelectPattern
+ // we need just enough to get to the font file data
+ FontSelectPattern aIFSD( *pFont, Size(0,1000), 1000.0, 0, false );
+
+ // TODO: much better solution: move SetFont and restoration of old font to caller
+ ScopedFont aOldFont(*this);
+
+ HFONT hOldFont = nullptr;
+ ImplDoSetFont(aIFSD, pFont, hOldFont);
+
+ // get raw font file data
+ const RawFontData xRawFontData( getHDC() );
+ if( !xRawFontData.get() )
+ return;
+
+ // open font file
+ sal_uInt32 nFaceNum = 0;
+ if( !*xRawFontData.get() ) // TTC candidate
+ nFaceNum = ~0U; // indicate "TTC font extracts only"
+
+ ScopedTrueTypeFont aSftTTF;
+ SFErrCodes nRC = aSftTTF.open( xRawFontData.get(), xRawFontData.size(), nFaceNum );
+ if( nRC != SFErrCodes::Ok )
+ return;
+
+ int nGlyphs = GetTTGlyphCount( aSftTTF.get() );
+ if( nGlyphs > 0 )
+ {
+ rWidths.resize(nGlyphs);
+ std::vector<sal_uInt16> aGlyphIds(nGlyphs);
+ for( int i = 0; i < nGlyphs; i++ )
+ aGlyphIds[i] = sal_uInt16(i);
+ std::unique_ptr<sal_uInt16[]> pMetrics = ::GetTTSimpleGlyphMetrics( aSftTTF.get(),
+ aGlyphIds.data(),
+ nGlyphs,
+ bVertical );
+ if( pMetrics )
+ {
+ for( int i = 0; i< nGlyphs; i++ )
+ rWidths[i] = pMetrics[i];
+ pMetrics.reset();
+ rUnicodeEnc.clear();
+ }
+ const WinFontFace* pWinFont = static_cast<const WinFontFace*>(pFont);
+ FontCharMapRef xFCMap = pWinFont->GetFontCharMap();
+ SAL_WARN_IF( !xFCMap.is() || !xFCMap->GetCharCount(), "vcl", "no map" );
+
+ int nCharCount = xFCMap->GetCharCount();
+ sal_uInt32 nChar = xFCMap->GetFirstChar();
+ for( int i = 0; i < nCharCount; i++ )
+ {
+ if( nChar < 0x00010000 )
+ {
+ sal_uInt16 nGlyph = ::MapChar( aSftTTF.get(),
+ static_cast<sal_Ucs>(nChar));
+ if( nGlyph )
+ rUnicodeEnc[ static_cast<sal_Unicode>(nChar) ] = nGlyph;
+ }
+ nChar = xFCMap->GetNextChar( nChar );
+ }
+
+ xFCMap = nullptr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/salgdi.cxx b/vcl/win/gdi/salgdi.cxx
new file mode 100644
index 000000000..4b47b10b8
--- /dev/null
+++ b/vcl/win/gdi/salgdi.cxx
@@ -0,0 +1,1058 @@
+/* -*- 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 <string.h>
+#include <svsys.h>
+#include <rtl/strbuf.hxx>
+#include <tools/poly.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <win/wincomp.hxx>
+#include <win/saldata.hxx>
+#include <win/salgdi.h>
+#include <win/salframe.h>
+#include <win/salvd.h>
+#include <win/winlayout.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+#include <salgdiimpl.hxx>
+#include "gdiimpl.hxx"
+#include <opengl/win/gdiimpl.hxx>
+
+#include <vcl/opengl/OpenGLHelper.hxx>
+
+#include <config_features.h>
+#include <vcl/skia/SkiaHelper.hxx>
+#if HAVE_FEATURE_SKIA
+#include <skia/win/gdiimpl.hxx>
+#endif
+
+
+#define DITHER_PAL_DELTA 51
+#define DITHER_PAL_STEPS 6
+#define DITHER_PAL_COUNT (DITHER_PAL_STEPS*DITHER_PAL_STEPS*DITHER_PAL_STEPS)
+#define DITHER_MAX_SYSCOLOR 16
+#define DITHER_EXTRA_COLORS 1
+
+namespace
+{
+
+struct SysColorEntry
+{
+ DWORD nRGB;
+ SysColorEntry* pNext;
+};
+
+SysColorEntry* pFirstSysColor = nullptr;
+SysColorEntry* pActSysColor = nullptr;
+
+void DeleteSysColorList()
+{
+ SysColorEntry* pEntry = pFirstSysColor;
+ pActSysColor = pFirstSysColor = nullptr;
+
+ while( pEntry )
+ {
+ SysColorEntry* pTmp = pEntry->pNext;
+ delete pEntry;
+ pEntry = pTmp;
+ }
+}
+
+} // namespace
+
+// Blue7
+static PALETTEENTRY aImplExtraColor1 =
+{
+ 0, 184, 255, 0
+};
+
+static PALETTEENTRY aImplSalSysPalEntryAry[ DITHER_MAX_SYSCOLOR ] =
+{
+{ 0, 0, 0, 0 },
+{ 0, 0, 0x80, 0 },
+{ 0, 0x80, 0, 0 },
+{ 0, 0x80, 0x80, 0 },
+{ 0x80, 0, 0, 0 },
+{ 0x80, 0, 0x80, 0 },
+{ 0x80, 0x80, 0, 0 },
+{ 0x80, 0x80, 0x80, 0 },
+{ 0xC0, 0xC0, 0xC0, 0 },
+{ 0, 0, 0xFF, 0 },
+{ 0, 0xFF, 0, 0 },
+{ 0, 0xFF, 0xFF, 0 },
+{ 0xFF, 0, 0, 0 },
+{ 0xFF, 0, 0xFF, 0 },
+{ 0xFF, 0xFF, 0, 0 },
+{ 0xFF, 0xFF, 0xFF, 0 }
+};
+
+// we must create pens with 1-pixel width; otherwise the S3-graphics card
+// map has many paint problems when drawing polygons/polyLines and a
+// complex is set
+#define GSL_PEN_WIDTH 1
+
+void ImplInitSalGDI()
+{
+ SalData* pSalData = GetSalData();
+
+ pSalData->mbResourcesAlreadyFreed = false;
+
+ // init stock brushes
+ pSalData->maStockPenColorAry[0] = PALETTERGB( 0, 0, 0 );
+ pSalData->maStockPenColorAry[1] = PALETTERGB( 0xFF, 0xFF, 0xFF );
+ pSalData->maStockPenColorAry[2] = PALETTERGB( 0xC0, 0xC0, 0xC0 );
+ pSalData->maStockPenColorAry[3] = PALETTERGB( 0x80, 0x80, 0x80 );
+ pSalData->mhStockPenAry[0] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[0] );
+ pSalData->mhStockPenAry[1] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[1] );
+ pSalData->mhStockPenAry[2] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[2] );
+ pSalData->mhStockPenAry[3] = CreatePen( PS_SOLID, GSL_PEN_WIDTH, pSalData->maStockPenColorAry[3] );
+ pSalData->mnStockPenCount = 4;
+
+ pSalData->maStockBrushColorAry[0] = PALETTERGB( 0, 0, 0 );
+ pSalData->maStockBrushColorAry[1] = PALETTERGB( 0xFF, 0xFF, 0xFF );
+ pSalData->maStockBrushColorAry[2] = PALETTERGB( 0xC0, 0xC0, 0xC0 );
+ pSalData->maStockBrushColorAry[3] = PALETTERGB( 0x80, 0x80, 0x80 );
+ pSalData->mhStockBrushAry[0] = CreateSolidBrush( pSalData->maStockBrushColorAry[0] );
+ pSalData->mhStockBrushAry[1] = CreateSolidBrush( pSalData->maStockBrushColorAry[1] );
+ pSalData->mhStockBrushAry[2] = CreateSolidBrush( pSalData->maStockBrushColorAry[2] );
+ pSalData->mhStockBrushAry[3] = CreateSolidBrush( pSalData->maStockBrushColorAry[3] );
+ pSalData->mnStockBrushCount = 4;
+
+ // initialize cache of device contexts
+ pSalData->mpHDCCache = new HDCCache[ CACHESIZE_HDC ];
+ memset( pSalData->mpHDCCache, 0, CACHESIZE_HDC * sizeof( HDCCache ) );
+
+ // initialize temporary font lists
+ pSalData->mpSharedTempFontItem = nullptr;
+ pSalData->mpOtherTempFontItem = nullptr;
+
+ // support palettes for 256 color displays
+ HDC hDC = GetDC( nullptr );
+ int nBitsPixel = GetDeviceCaps( hDC, BITSPIXEL );
+ int nPlanes = GetDeviceCaps( hDC, PLANES );
+ int nRasterCaps = GetDeviceCaps( hDC, RASTERCAPS );
+ int nBitCount = nBitsPixel * nPlanes;
+
+ if ( (nBitCount > 8) && (nBitCount < 24) )
+ {
+ // test if we have to dither
+ HDC hMemDC = ::CreateCompatibleDC( hDC );
+ HBITMAP hMemBmp = ::CreateCompatibleBitmap( hDC, 8, 8 );
+ HBITMAP hBmpOld = static_cast<HBITMAP>(::SelectObject( hMemDC, hMemBmp ));
+ HBRUSH hMemBrush = ::CreateSolidBrush( PALETTERGB( 175, 171, 169 ) );
+ HBRUSH hBrushOld = static_cast<HBRUSH>(::SelectObject( hMemDC, hMemBrush ));
+ bool bDither16 = true;
+
+ ::PatBlt( hMemDC, 0, 0, 8, 8, PATCOPY );
+ const COLORREF aCol( ::GetPixel( hMemDC, 0, 0 ) );
+
+ for( int nY = 0; ( nY < 8 ) && bDither16; nY++ )
+ for( int nX = 0; ( nX < 8 ) && bDither16; nX++ )
+ if( ::GetPixel( hMemDC, nX, nY ) != aCol )
+ bDither16 = false;
+
+ ::SelectObject( hMemDC, hBrushOld );
+ ::DeleteObject( hMemBrush );
+ ::SelectObject( hMemDC, hBmpOld );
+ ::DeleteObject( hMemBmp );
+ ::DeleteDC( hMemDC );
+
+ if( bDither16 )
+ {
+ // create DIBPattern for 16Bit dithering
+ long n;
+
+ pSalData->mhDitherDIB = GlobalAlloc( GMEM_FIXED, sizeof( BITMAPINFOHEADER ) + 192 );
+ pSalData->mpDitherDIB = static_cast<BYTE*>(GlobalLock( pSalData->mhDitherDIB ));
+ pSalData->mpDitherDiff = new long[ 256 ];
+ pSalData->mpDitherLow = new BYTE[ 256 ];
+ pSalData->mpDitherHigh = new BYTE[ 256 ];
+ pSalData->mpDitherDIBData = pSalData->mpDitherDIB + sizeof( BITMAPINFOHEADER );
+ memset( pSalData->mpDitherDIB, 0, sizeof( BITMAPINFOHEADER ) );
+
+ BITMAPINFOHEADER* pBIH = reinterpret_cast<BITMAPINFOHEADER*>(pSalData->mpDitherDIB);
+
+ pBIH->biSize = sizeof( BITMAPINFOHEADER );
+ pBIH->biWidth = 8;
+ pBIH->biHeight = 8;
+ pBIH->biPlanes = 1;
+ pBIH->biBitCount = 24;
+
+ for( n = 0; n < 256; n++ )
+ pSalData->mpDitherDiff[ n ] = n - ( n & 248L );
+
+ for( n = 0; n < 256; n++ )
+ pSalData->mpDitherLow[ n ] = static_cast<BYTE>( n & 248 );
+
+ for( n = 0; n < 256; n++ )
+ pSalData->mpDitherHigh[ n ] = static_cast<BYTE>(std::min( pSalData->mpDitherLow[ n ] + 8, 255 ));
+ }
+ }
+ else if ( (nRasterCaps & RC_PALETTE) && (nBitCount == 8) )
+ {
+ BYTE nRed, nGreen, nBlue;
+ BYTE nR, nG, nB;
+ PALETTEENTRY* pPalEntry;
+ LOGPALETTE* pLogPal;
+ const sal_uInt16 nDitherPalCount = DITHER_PAL_COUNT;
+ sal_uLong nTotalCount = DITHER_MAX_SYSCOLOR + nDitherPalCount + DITHER_EXTRA_COLORS;
+
+ // create logical palette
+ pLogPal = reinterpret_cast<LOGPALETTE*>(new char[ sizeof( LOGPALETTE ) + ( nTotalCount * sizeof( PALETTEENTRY ) ) ]);
+ pLogPal->palVersion = 0x0300;
+ pLogPal->palNumEntries = static_cast<sal_uInt16>(nTotalCount);
+ pPalEntry = pLogPal->palPalEntry;
+
+ // Standard colors
+ memcpy( pPalEntry, aImplSalSysPalEntryAry, DITHER_MAX_SYSCOLOR * sizeof( PALETTEENTRY ) );
+ pPalEntry += DITHER_MAX_SYSCOLOR;
+
+ // own palette (6/6/6)
+ for( nB=0, nBlue=0; nB < DITHER_PAL_STEPS; nB++, nBlue += DITHER_PAL_DELTA )
+ {
+ for( nG=0, nGreen=0; nG < DITHER_PAL_STEPS; nG++, nGreen += DITHER_PAL_DELTA )
+ {
+ for( nR=0, nRed=0; nR < DITHER_PAL_STEPS; nR++, nRed += DITHER_PAL_DELTA )
+ {
+ pPalEntry->peRed = nRed;
+ pPalEntry->peGreen = nGreen;
+ pPalEntry->peBlue = nBlue;
+ pPalEntry->peFlags = 0;
+ pPalEntry++;
+ }
+ }
+ }
+
+ // insert special 'Blue' as standard drawing color
+ *pPalEntry++ = aImplExtraColor1;
+
+ // create palette
+ pSalData->mhDitherPal = CreatePalette( pLogPal );
+ delete[] reinterpret_cast<char*>(pLogPal);
+
+ if( pSalData->mhDitherPal )
+ {
+ // create DIBPattern for 8Bit dithering
+ long const nSize = sizeof( BITMAPINFOHEADER ) + ( 256 * sizeof( short ) ) + 64;
+ long n;
+
+ pSalData->mhDitherDIB = GlobalAlloc( GMEM_FIXED, nSize );
+ pSalData->mpDitherDIB = static_cast<BYTE*>(GlobalLock( pSalData->mhDitherDIB ));
+ pSalData->mpDitherDiff = new long[ 256 ];
+ pSalData->mpDitherLow = new BYTE[ 256 ];
+ pSalData->mpDitherHigh = new BYTE[ 256 ];
+ pSalData->mpDitherDIBData = pSalData->mpDitherDIB + sizeof( BITMAPINFOHEADER ) + ( 256 * sizeof( short ) );
+ memset( pSalData->mpDitherDIB, 0, sizeof( BITMAPINFOHEADER ) );
+
+ BITMAPINFOHEADER* pBIH = reinterpret_cast<BITMAPINFOHEADER*>(pSalData->mpDitherDIB);
+ short* pColors = reinterpret_cast<short*>( pSalData->mpDitherDIB + sizeof( BITMAPINFOHEADER ) );
+
+ pBIH->biSize = sizeof( BITMAPINFOHEADER );
+ pBIH->biWidth = 8;
+ pBIH->biHeight = 8;
+ pBIH->biPlanes = 1;
+ pBIH->biBitCount = 8;
+
+ for( n = 0; n < nDitherPalCount; n++ )
+ pColors[ n ] = static_cast<short>( n + DITHER_MAX_SYSCOLOR );
+
+ for( n = 0; n < 256; n++ )
+ pSalData->mpDitherDiff[ n ] = n % 51;
+
+ for( n = 0; n < 256; n++ )
+ pSalData->mpDitherLow[ n ] = static_cast<BYTE>( n / 51 );
+
+ for( n = 0; n < 256; n++ )
+ pSalData->mpDitherHigh[ n ] = static_cast<BYTE>(std::min( pSalData->mpDitherLow[ n ] + 1, 5 ));
+ }
+
+ // get system color entries
+ ImplUpdateSysColorEntries();
+ }
+
+ ReleaseDC( nullptr, hDC );
+}
+
+void ImplFreeSalGDI()
+{
+ SalData* pSalData = GetSalData();
+
+ if (pSalData->mbResourcesAlreadyFreed)
+ return;
+
+ // destroy stock objects
+ int i;
+ for ( i = 0; i < pSalData->mnStockPenCount; i++ )
+ DeletePen( pSalData->mhStockPenAry[i] );
+ for ( i = 0; i < pSalData->mnStockBrushCount; i++ )
+ DeleteBrush( pSalData->mhStockBrushAry[i] );
+
+ // delete 50% Brush
+ if ( pSalData->mh50Brush )
+ {
+ DeleteBrush( pSalData->mh50Brush );
+ pSalData->mh50Brush = nullptr;
+ }
+
+ // delete 50% Bitmap
+ if ( pSalData->mh50Bmp )
+ {
+ DeleteBitmap( pSalData->mh50Bmp );
+ pSalData->mh50Bmp = nullptr;
+ }
+
+ ImplClearHDCCache( pSalData );
+ delete[] pSalData->mpHDCCache;
+
+ // delete Ditherpalette, if existing
+ if ( pSalData->mhDitherPal )
+ {
+ DeleteObject( pSalData->mhDitherPal );
+ pSalData->mhDitherPal = nullptr;
+ }
+
+ // delete buffers for dithering DIB patterns, if necessary
+ if ( pSalData->mhDitherDIB )
+ {
+ GlobalUnlock( pSalData->mhDitherDIB );
+ GlobalFree( pSalData->mhDitherDIB );
+ pSalData->mhDitherDIB = nullptr;
+ delete[] pSalData->mpDitherDiff;
+ delete[] pSalData->mpDitherLow;
+ delete[] pSalData->mpDitherHigh;
+ }
+
+ DeleteSysColorList();
+
+ // delete icon cache
+ SalIcon* pIcon = pSalData->mpFirstIcon;
+ pSalData->mpFirstIcon = nullptr;
+ while( pIcon )
+ {
+ SalIcon* pTmp = pIcon->pNext;
+ DestroyIcon( pIcon->hIcon );
+ DestroyIcon( pIcon->hSmallIcon );
+ delete pIcon;
+ pIcon = pTmp;
+ }
+
+ // delete temporary font list
+ ImplReleaseTempFonts(*pSalData, true);
+
+ pSalData->mbResourcesAlreadyFreed = true;
+}
+
+int ImplIsSysColorEntry( Color nColor )
+{
+ SysColorEntry* pEntry = pFirstSysColor;
+ const DWORD nTestRGB = static_cast<DWORD>(RGB( nColor.GetRed(),
+ nColor.GetGreen(),
+ nColor.GetBlue() ));
+
+ while ( pEntry )
+ {
+ if ( pEntry->nRGB == nTestRGB )
+ return TRUE;
+ pEntry = pEntry->pNext;
+ }
+
+ return FALSE;
+}
+
+static int ImplIsPaletteEntry( BYTE nRed, BYTE nGreen, BYTE nBlue )
+{
+ // dither color?
+ if ( !(nRed % DITHER_PAL_DELTA) && !(nGreen % DITHER_PAL_DELTA) && !(nBlue % DITHER_PAL_DELTA) )
+ return TRUE;
+
+ PALETTEENTRY* pPalEntry = aImplSalSysPalEntryAry;
+
+ // standard palette color?
+ for ( sal_uInt16 i = 0; i < DITHER_MAX_SYSCOLOR; i++, pPalEntry++ )
+ {
+ if( pPalEntry->peRed == nRed && pPalEntry->peGreen == nGreen && pPalEntry->peBlue == nBlue )
+ return TRUE;
+ }
+
+ // extra color?
+ if ( aImplExtraColor1.peRed == nRed &&
+ aImplExtraColor1.peGreen == nGreen &&
+ aImplExtraColor1.peBlue == nBlue )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void ImplInsertSysColorEntry( int nSysIndex )
+{
+ const DWORD nRGB = GetSysColor( nSysIndex );
+
+ if ( !ImplIsPaletteEntry( GetRValue( nRGB ), GetGValue( nRGB ), GetBValue( nRGB ) ) )
+ {
+ if ( !pFirstSysColor )
+ {
+ pActSysColor = pFirstSysColor = new SysColorEntry;
+ pFirstSysColor->nRGB = nRGB;
+ pFirstSysColor->pNext = nullptr;
+ }
+ else
+ {
+ pActSysColor = pActSysColor->pNext = new SysColorEntry;
+ pActSysColor->nRGB = nRGB;
+ pActSysColor->pNext = nullptr;
+ }
+ }
+}
+
+void ImplUpdateSysColorEntries()
+{
+ DeleteSysColorList();
+
+ // create new sys color list
+ ImplInsertSysColorEntry( COLOR_ACTIVEBORDER );
+ ImplInsertSysColorEntry( COLOR_INACTIVEBORDER );
+ ImplInsertSysColorEntry( COLOR_GRADIENTACTIVECAPTION );
+ ImplInsertSysColorEntry( COLOR_GRADIENTINACTIVECAPTION );
+ ImplInsertSysColorEntry( COLOR_3DFACE );
+ ImplInsertSysColorEntry( COLOR_3DHILIGHT );
+ ImplInsertSysColorEntry( COLOR_3DLIGHT );
+ ImplInsertSysColorEntry( COLOR_3DSHADOW );
+ ImplInsertSysColorEntry( COLOR_3DDKSHADOW );
+ ImplInsertSysColorEntry( COLOR_INFOBK );
+ ImplInsertSysColorEntry( COLOR_INFOTEXT );
+ ImplInsertSysColorEntry( COLOR_BTNTEXT );
+ ImplInsertSysColorEntry( COLOR_WINDOW );
+ ImplInsertSysColorEntry( COLOR_WINDOWTEXT );
+ ImplInsertSysColorEntry( COLOR_HIGHLIGHT );
+ ImplInsertSysColorEntry( COLOR_HIGHLIGHTTEXT );
+ ImplInsertSysColorEntry( COLOR_MENU );
+ ImplInsertSysColorEntry( COLOR_MENUTEXT );
+ ImplInsertSysColorEntry( COLOR_ACTIVECAPTION );
+ ImplInsertSysColorEntry( COLOR_CAPTIONTEXT );
+ ImplInsertSysColorEntry( COLOR_INACTIVECAPTION );
+ ImplInsertSysColorEntry( COLOR_INACTIVECAPTIONTEXT );
+}
+
+void WinSalGraphics::InitGraphics()
+{
+ // calculate the minimal line width for the printer
+ if ( isPrinter() )
+ {
+ int nDPIX = GetDeviceCaps( getHDC(), LOGPIXELSX );
+ if ( nDPIX <= 300 )
+ mnPenWidth = 0;
+ else
+ mnPenWidth = nDPIX/300;
+ }
+
+ ::SetTextAlign( getHDC(), TA_BASELINE | TA_LEFT | TA_NOUPDATECP );
+ ::SetBkMode( getHDC(), TRANSPARENT );
+ ::SetROP2( getHDC(), R2_COPYPEN );
+
+ mpImpl->Init();
+}
+
+void WinSalGraphics::DeInitGraphics()
+{
+ // clear clip region
+ SelectClipRgn( getHDC(), nullptr );
+ // select default objects
+ if ( mhDefPen )
+ SelectPen( getHDC(), mhDefPen );
+ if ( mhDefBrush )
+ SelectBrush( getHDC(), mhDefBrush );
+ if ( mhDefFont )
+ SelectFont( getHDC(), mhDefFont );
+
+ mpImpl->DeInit();
+}
+
+HDC ImplGetCachedDC( sal_uLong nID, HBITMAP hBmp )
+{
+ SalData* pSalData = GetSalData();
+ HDCCache* pC = &pSalData->mpHDCCache[ nID ];
+
+ if( !pC->mhDC )
+ {
+ HDC hDC = GetDC( nullptr );
+
+ // create new DC with DefaultBitmap
+ pC->mhDC = CreateCompatibleDC( hDC );
+
+ if( pSalData->mhDitherPal )
+ {
+ pC->mhDefPal = SelectPalette( pC->mhDC, pSalData->mhDitherPal, TRUE );
+ RealizePalette( pC->mhDC );
+ }
+
+ pC->mhSelBmp = CreateCompatibleBitmap( hDC, CACHED_HDC_DEFEXT, CACHED_HDC_DEFEXT );
+ pC->mhDefBmp = static_cast<HBITMAP>(SelectObject( pC->mhDC, pC->mhSelBmp ));
+
+ ReleaseDC( nullptr, hDC );
+ }
+
+ if ( hBmp )
+ SelectObject( pC->mhDC, pC->mhActBmp = hBmp );
+ else
+ pC->mhActBmp = nullptr;
+
+ return pC->mhDC;
+}
+
+void ImplReleaseCachedDC( sal_uLong nID )
+{
+ SalData* pSalData = GetSalData();
+ HDCCache* pC = &pSalData->mpHDCCache[ nID ];
+
+ if ( pC->mhActBmp )
+ SelectObject( pC->mhDC, pC->mhSelBmp );
+}
+
+void ImplClearHDCCache( SalData* pData )
+{
+ for( sal_uLong i = 0; i < CACHESIZE_HDC; i++ )
+ {
+ HDCCache* pC = &pData->mpHDCCache[ i ];
+
+ if( pC->mhDC )
+ {
+ SelectObject( pC->mhDC, pC->mhDefBmp );
+
+ if( pC->mhDefPal )
+ SelectPalette( pC->mhDC, pC->mhDefPal, TRUE );
+
+ DeleteDC( pC->mhDC );
+ DeleteObject( pC->mhSelBmp );
+ }
+ }
+}
+
+std::unique_ptr< CompatibleDC > CompatibleDC::create(SalGraphics &rGraphics, int x, int y, int width, int height)
+{
+#if HAVE_FEATURE_SKIA
+ if (SkiaHelper::isVCLSkiaEnabled())
+ return std::make_unique< SkiaCompatibleDC >( rGraphics, x, y, width, height );
+#endif
+ if (OpenGLHelper::isVCLOpenGLEnabled())
+ return std::make_unique< OpenGLCompatibleDC >( rGraphics, x, y, width, height );
+ return std::unique_ptr< CompatibleDC >( new CompatibleDC( rGraphics, x, y, width, height ));
+}
+
+CompatibleDC::CompatibleDC(SalGraphics &rGraphics, int x, int y, int width, int height, bool disable)
+ : mhBitmap(nullptr)
+ , mpData(nullptr)
+ , maRects(0, 0, width, height, x, y, width, height)
+ , mpImpl(nullptr)
+{
+ WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
+
+ if( disable )
+ {
+ // we avoid the OpenGL drawing, instead we draw directly to the DC
+ mhCompatibleDC = rWinGraphics.getHDC();
+ return;
+ }
+
+ mpImpl = dynamic_cast<WinSalGraphicsImplBase*>(rWinGraphics.GetImpl());
+ assert(mpImpl != nullptr);
+ mhCompatibleDC = CreateCompatibleDC(rWinGraphics.getHDC());
+
+ // move the origin so that we always paint at 0,0 - to keep the bitmap
+ // small
+ OffsetViewportOrgEx(mhCompatibleDC, -x, -y, nullptr);
+
+ mhBitmap = WinSalVirtualDevice::ImplCreateVirDevBitmap(mhCompatibleDC, width, height, 32, reinterpret_cast<void **>(&mpData));
+
+ mhOrigBitmap = static_cast<HBITMAP>(SelectObject(mhCompatibleDC, mhBitmap));
+}
+
+CompatibleDC::~CompatibleDC()
+{
+ if (mpImpl)
+ {
+ SelectObject(mhCompatibleDC, mhOrigBitmap);
+ DeleteObject(mhBitmap);
+ DeleteDC(mhCompatibleDC);
+ }
+}
+
+void CompatibleDC::fill(sal_uInt32 color)
+{
+ if (!mpData)
+ return;
+
+ sal_uInt32 *p = mpData;
+ for (int i = maRects.mnSrcWidth * maRects.mnSrcHeight; i > 0; --i)
+ *p++ = color;
+}
+
+WinSalGraphics::WinSalGraphics(WinSalGraphics::Type eType, bool bScreen, HWND hWnd, SalGeometryProvider *pProvider):
+ mhLocalDC(nullptr),
+ mbPrinter(eType == WinSalGraphics::PRINTER),
+ mbVirDev(eType == WinSalGraphics::VIRTUAL_DEVICE),
+ mbWindow(eType == WinSalGraphics::WINDOW),
+ mbScreen(bScreen),
+ mhWnd(hWnd),
+ mhRegion(nullptr),
+ mhDefPen(nullptr),
+ mhDefBrush(nullptr),
+ mhDefFont(nullptr),
+ mhDefPal(nullptr),
+ mpStdClipRgnData(nullptr),
+ mnPenWidth(GSL_PEN_WIDTH)
+{
+#if HAVE_FEATURE_SKIA
+ if (SkiaHelper::isVCLSkiaEnabled() && !mbPrinter)
+ mpImpl.reset(new WinSkiaSalGraphicsImpl(*this, pProvider));
+ else
+#endif
+ if (OpenGLHelper::isVCLOpenGLEnabled() && !mbPrinter)
+ mpImpl.reset(new WinOpenGLSalGraphicsImpl(*this, pProvider));
+ else
+ mpImpl.reset(new WinSalGraphicsImpl(*this));
+}
+
+WinSalGraphics::~WinSalGraphics()
+{
+ // free obsolete GDI objects
+ ReleaseFonts();
+
+ if ( mhRegion )
+ {
+ DeleteRegion( mhRegion );
+ mhRegion = nullptr;
+ }
+
+ // delete cache data
+ delete [] reinterpret_cast<BYTE*>(mpStdClipRgnData);
+}
+
+SalGraphicsImpl* WinSalGraphics::GetImpl() const
+{
+ return mpImpl.get();
+}
+
+bool WinSalGraphics::isPrinter() const
+{
+ return mbPrinter;
+}
+
+bool WinSalGraphics::isVirtualDevice() const
+{
+ return mbVirDev;
+}
+
+bool WinSalGraphics::isWindow() const
+{
+ return mbWindow;
+}
+
+bool WinSalGraphics::isScreen() const
+{
+ return mbScreen;
+}
+
+HWND WinSalGraphics::gethWnd()
+{
+ return mhWnd;
+}
+
+void WinSalGraphics::setHWND(HWND hWnd)
+{
+ mhWnd = hWnd;
+}
+
+HPALETTE WinSalGraphics::getDefPal() const
+{
+ return mhDefPal;
+}
+
+void WinSalGraphics::setDefPal(HPALETTE hDefPal)
+{
+ mhDefPal = hDefPal;
+}
+
+HRGN WinSalGraphics::getRegion() const
+{
+ return mhRegion;
+}
+
+void WinSalGraphics::GetResolution( sal_Int32& rDPIX, sal_Int32& rDPIY )
+{
+ rDPIX = GetDeviceCaps( getHDC(), LOGPIXELSX );
+ rDPIY = GetDeviceCaps( getHDC(), LOGPIXELSY );
+
+ // #111139# this fixes the symptom of div by zero on startup
+ // however, printing will fail most likely as communication with
+ // the printer seems not to work in this case
+ if( !rDPIX || !rDPIY )
+ rDPIX = rDPIY = 600;
+}
+
+sal_uInt16 WinSalGraphics::GetBitCount() const
+{
+ return mpImpl->GetBitCount();
+}
+
+long WinSalGraphics::GetGraphicsWidth() const
+{
+ return mpImpl->GetGraphicsWidth();
+}
+
+void WinSalGraphics::ResetClipRegion()
+{
+ mpImpl->ResetClipRegion();
+}
+
+bool WinSalGraphics::setClipRegion( const vcl::Region& i_rClip )
+{
+ return mpImpl->setClipRegion( i_rClip );
+}
+
+void WinSalGraphics::SetLineColor()
+{
+ mpImpl->SetLineColor();
+}
+
+void WinSalGraphics::SetLineColor( Color nColor )
+{
+ mpImpl->SetLineColor( nColor );
+}
+
+void WinSalGraphics::SetFillColor()
+{
+ mpImpl->SetFillColor();
+}
+
+void WinSalGraphics::SetFillColor( Color nColor )
+{
+ mpImpl->SetFillColor( nColor );
+}
+
+void WinSalGraphics::SetXORMode( bool bSet, bool bInvertOnly )
+{
+ mpImpl->SetXORMode( bSet, bInvertOnly );
+}
+
+void WinSalGraphics::SetROPLineColor( SalROPColor nROPColor )
+{
+ mpImpl->SetROPLineColor( nROPColor );
+}
+
+void WinSalGraphics::SetROPFillColor( SalROPColor nROPColor )
+{
+ mpImpl->SetROPFillColor( nROPColor );
+}
+
+void WinSalGraphics::drawPixel( long nX, long nY )
+{
+ mpImpl->drawPixel( nX, nY );
+}
+
+void WinSalGraphics::drawPixel( long nX, long nY, Color nColor )
+{
+ mpImpl->drawPixel( nX, nY, nColor );
+}
+
+void WinSalGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 )
+{
+ mpImpl->drawLine( nX1, nY1, nX2, nY2 );
+}
+
+void WinSalGraphics::drawRect( long nX, long nY, long nWidth, long nHeight )
+{
+ mpImpl->drawRect( nX, nY, nWidth, nHeight );
+}
+
+void WinSalGraphics::drawPolyLine( sal_uInt32 nPoints, const SalPoint* pPtAry )
+{
+ mpImpl->drawPolyLine( nPoints, pPtAry );
+}
+
+void WinSalGraphics::drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry )
+{
+ mpImpl->drawPolygon( nPoints, pPtAry );
+}
+
+void WinSalGraphics::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ PCONSTSALPOINT* pPtAry )
+{
+ mpImpl->drawPolyPolygon( nPoly, pPoints, pPtAry );
+}
+
+bool WinSalGraphics::drawPolyLineBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry )
+{
+ return mpImpl->drawPolyLineBezier( nPoints, pPtAry, pFlgAry );
+}
+
+bool WinSalGraphics::drawPolygonBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry )
+{
+ return mpImpl->drawPolygonBezier( nPoints, pPtAry, pFlgAry );
+}
+
+bool WinSalGraphics::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ const SalPoint* const* pPtAry, const PolyFlags* const* pFlgAry )
+{
+ return mpImpl->drawPolyPolygonBezier( nPoly, pPoints, pPtAry, pFlgAry );
+}
+
+static BYTE* ImplSearchEntry( BYTE* pSource, BYTE const * pDest, sal_uLong nComp, sal_uLong nSize )
+{
+ while ( nComp-- >= nSize )
+ {
+ sal_uLong i;
+ for ( i = 0; i < nSize; i++ )
+ {
+ if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
+ break;
+ }
+ if ( i == nSize )
+ return pSource;
+ pSource++;
+ }
+ return nullptr;
+}
+
+static bool ImplGetBoundingBox( double* nNumb, BYTE* pSource, sal_uLong nSize )
+{
+ bool bRetValue = false;
+ BYTE* pDest = ImplSearchEntry( pSource, reinterpret_cast<BYTE const *>("%%BoundingBox:"), nSize, 14 );
+ if ( pDest )
+ {
+ nNumb[0] = nNumb[1] = nNumb[2] = nNumb[3] = 0;
+ pDest += 14;
+
+ int nSizeLeft = nSize - ( pDest - pSource );
+ if ( nSizeLeft > 100 )
+ nSizeLeft = 100; // only 100 bytes following the bounding box will be checked
+
+ int i;
+ for ( i = 0; ( i < 4 ) && nSizeLeft; i++ )
+ {
+ int nDivision = 1;
+ bool bDivision = false;
+ bool bNegative = false;
+ bool bValid = true;
+
+ while ( ( --nSizeLeft ) && ( ( *pDest == ' ' ) || ( *pDest == 0x9 ) ) ) pDest++;
+ BYTE nByte = *pDest;
+ while ( nSizeLeft && ( nByte != ' ' ) && ( nByte != 0x9 ) && ( nByte != 0xd ) && ( nByte != 0xa ) )
+ {
+ switch ( nByte )
+ {
+ case '.' :
+ if ( bDivision )
+ bValid = false;
+ else
+ bDivision = true;
+ break;
+ case '-' :
+ bNegative = true;
+ break;
+ default :
+ if ( ( nByte < '0' ) || ( nByte > '9' ) )
+ nSizeLeft = 1; // error parsing the bounding box values
+ else if ( bValid )
+ {
+ if ( bDivision )
+ nDivision*=10;
+ nNumb[i] *= 10;
+ nNumb[i] += nByte - '0';
+ }
+ break;
+ }
+ nSizeLeft--;
+ nByte = *(++pDest);
+ }
+ if ( bNegative )
+ nNumb[i] = -nNumb[i];
+ if ( bDivision && ( nDivision != 1 ) )
+ nNumb[i] /= nDivision;
+ }
+ if ( i == 4 )
+ bRetValue = true;
+ }
+ return bRetValue;
+}
+
+#define POSTSCRIPT_BUFSIZE 0x4000 // MAXIMUM BUFSIZE EQ 0xFFFF
+
+bool WinSalGraphics::drawEPS( long nX, long nY, long nWidth, long nHeight, void* pPtr, sal_uInt32 nSize )
+{
+ bool bRetValue = false;
+
+ if ( mbPrinter )
+ {
+ int nEscape = POSTSCRIPT_PASSTHROUGH;
+
+ if ( Escape( getHDC(), QUERYESCSUPPORT, sizeof( int ), reinterpret_cast<LPSTR>(&nEscape), nullptr ) )
+ {
+ double nBoundingBox[4];
+
+ if ( ImplGetBoundingBox( nBoundingBox, static_cast<BYTE*>(pPtr), nSize ) )
+ {
+ OStringBuffer aBuf( POSTSCRIPT_BUFSIZE );
+
+ // reserve place for a sal_uInt16
+ aBuf.append( "aa" );
+
+ // #107797# Write out EPS encapsulation header
+
+ // directly taken from the PLRM 3.0, p. 726. Note:
+ // this will definitely cause problems when
+ // recursively creating and embedding PostScript files
+ // in OOo, since we use statically-named variables
+ // here (namely, b4_Inc_state_salWin, dict_count_salWin and
+ // op_count_salWin). Currently, I have no idea on how to
+ // work around that, except from scanning and
+ // interpreting the EPS for unused identifiers.
+
+ // append the real text
+ aBuf.append( "\n\n/b4_Inc_state_salWin save def\n"
+ "/dict_count_salWin countdictstack def\n"
+ "/op_count_salWin count 1 sub def\n"
+ "userdict begin\n"
+ "/showpage {} def\n"
+ "0 setgray 0 setlinecap\n"
+ "1 setlinewidth 0 setlinejoin\n"
+ "10 setmiterlimit [] 0 setdash newpath\n"
+ "/languagelevel where\n"
+ "{\n"
+ " pop languagelevel\n"
+ " 1 ne\n"
+ " {\n"
+ " false setstrokeadjust false setoverprint\n"
+ " } if\n"
+ "} if\n\n" );
+
+ // #i10737# Apply clipping manually
+
+ // Windows seems to ignore any clipping at the HDC,
+ // when followed by a POSTSCRIPT_PASSTHROUGH
+
+ // Check whether we've got a clipping, consisting of
+ // exactly one rect (other cases should be, but aren't
+ // handled currently)
+
+ // TODO: Handle more than one rectangle here (take
+ // care, the buffer can handle only POSTSCRIPT_BUFSIZE
+ // characters!)
+ if ( mhRegion != nullptr &&
+ mpStdClipRgnData != nullptr &&
+ mpClipRgnData == mpStdClipRgnData &&
+ mpClipRgnData->rdh.nCount == 1 )
+ {
+ RECT* pRect = &(mpClipRgnData->rdh.rcBound);
+
+ aBuf.append( "\nnewpath\n" );
+ aBuf.append( pRect->left );
+ aBuf.append( " " );
+ aBuf.append( pRect->top );
+ aBuf.append( " moveto\n" );
+ aBuf.append( pRect->right );
+ aBuf.append( " " );
+ aBuf.append( pRect->top );
+ aBuf.append( " lineto\n" );
+ aBuf.append( pRect->right );
+ aBuf.append( " " );
+ aBuf.append( pRect->bottom );
+ aBuf.append( " lineto\n" );
+ aBuf.append( pRect->left );
+ aBuf.append( " " );
+ aBuf.append( pRect->bottom );
+ aBuf.append( " lineto\n"
+ "closepath\n"
+ "clip\n"
+ "newpath\n" );
+ }
+
+ // #107797# Write out buffer
+
+ *reinterpret_cast<sal_uInt16*>(const_cast<char *>(aBuf.getStr())) = static_cast<sal_uInt16>( aBuf.getLength() - 2 );
+ Escape ( getHDC(), nEscape, aBuf.getLength(), aBuf.getStr(), nullptr );
+
+ // #107797# Write out EPS transformation code
+
+ double dM11 = nWidth / ( nBoundingBox[2] - nBoundingBox[0] );
+ double dM22 = nHeight / (nBoundingBox[1] - nBoundingBox[3] );
+ // reserve a sal_uInt16 again
+ aBuf.setLength( 2 );
+ aBuf.append( "\n\n[" );
+ aBuf.append( dM11 );
+ aBuf.append( " 0 0 " );
+ aBuf.append( dM22 );
+ aBuf.append( ' ' );
+ aBuf.append( nX - ( dM11 * nBoundingBox[0] ) );
+ aBuf.append( ' ' );
+ aBuf.append( nY - ( dM22 * nBoundingBox[3] ) );
+ aBuf.append( "] concat\n"
+ "%%BeginDocument:\n" );
+ *reinterpret_cast<sal_uInt16*>(const_cast<char *>(aBuf.getStr())) = static_cast<sal_uInt16>( aBuf.getLength() - 2 );
+ Escape ( getHDC(), nEscape, aBuf.getLength(), aBuf.getStr(), nullptr );
+
+ // #107797# Write out actual EPS content
+
+ sal_uLong nToDo = nSize;
+ sal_uLong nDoNow;
+ while ( nToDo )
+ {
+ nDoNow = nToDo;
+ if ( nToDo > POSTSCRIPT_BUFSIZE - 2 )
+ nDoNow = POSTSCRIPT_BUFSIZE - 2;
+ // the following is based on the string buffer allocation
+ // of size POSTSCRIPT_BUFSIZE at construction time of aBuf
+ *reinterpret_cast<sal_uInt16*>(const_cast<char *>(aBuf.getStr())) = static_cast<sal_uInt16>(nDoNow);
+ memcpy( const_cast<char *>(aBuf.getStr() + 2), static_cast<BYTE*>(pPtr) + nSize - nToDo, nDoNow );
+ sal_uLong nResult = Escape ( getHDC(), nEscape, nDoNow + 2, aBuf.getStr(), nullptr );
+ if (!nResult )
+ break;
+ nToDo -= nResult;
+ }
+
+ // #107797# Write out EPS encapsulation footer
+
+ // reserve a sal_uInt16 again
+ aBuf.setLength( 2 );
+ aBuf.append( "%%EndDocument\n"
+ "count op_count_salWin sub {pop} repeat\n"
+ "countdictstack dict_count_salWin sub {end} repeat\n"
+ "b4_Inc_state_salWin restore\n\n" );
+ *reinterpret_cast<sal_uInt16*>(const_cast<char *>(aBuf.getStr())) = static_cast<sal_uInt16>( aBuf.getLength() - 2 );
+ Escape ( getHDC(), nEscape, aBuf.getLength(), aBuf.getStr(), nullptr );
+ bRetValue = true;
+ }
+ }
+ }
+
+ return bRetValue;
+}
+
+SystemGraphicsData WinSalGraphics::GetGraphicsData() const
+{
+ SystemGraphicsData aRes;
+ aRes.nSize = sizeof(aRes);
+ aRes.hDC = getHDC();
+ return aRes;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/salgdi2.cxx b/vcl/win/gdi/salgdi2.cxx
new file mode 100644
index 000000000..fdac864d0
--- /dev/null
+++ b/vcl/win/gdi/salgdi2.cxx
@@ -0,0 +1,248 @@
+/* -*- 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 <memory>
+#include <string.h>
+#include <stdlib.h>
+
+#include <svsys.h>
+
+#include <win/wincomp.hxx>
+#include <win/salbmp.h>
+#include <win/saldata.hxx>
+#include <win/salids.hrc>
+#include <win/salgdi.h>
+#include <win/salframe.h>
+#include <opengl/salbmp.hxx>
+
+#include <vcl/BitmapAccessMode.hxx>
+#include <vcl/BitmapBuffer.hxx>
+#include <vcl/BitmapPalette.hxx>
+#include <vcl/Scanline.hxx>
+#include <vcl/bitmapaccess.hxx>
+#include <outdata.hxx>
+#include <salgdiimpl.hxx>
+#include <opengl/win/gdiimpl.hxx>
+
+#include <config_features.h>
+#if HAVE_FEATURE_SKIA
+#include <skia/win/gdiimpl.hxx>
+#include <skia/salbmp.hxx>
+#endif
+
+
+bool WinSalGraphics::supportsOperation( OutDevSupportType eType ) const
+{
+ return mpImpl->supportsOperation(eType);
+}
+
+void WinSalGraphics::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
+{
+ mpImpl->copyBits( rPosAry, pSrcGraphics );
+}
+
+void WinSalGraphics::copyArea( long nDestX, long nDestY,
+ long nSrcX, long nSrcY,
+ long nSrcWidth, long nSrcHeight,
+ bool bWindowInvalidate )
+{
+ mpImpl->copyArea( nDestX, nDestY, nSrcX, nSrcY,
+ nSrcWidth, nSrcHeight, bWindowInvalidate );
+}
+
+namespace
+{
+
+class ColorScanlineConverter
+{
+public:
+ ScanlineFormat meSourceFormat;
+
+ int mnComponentSize;
+ int mnComponentExchangeIndex;
+
+ long mnScanlineSize;
+
+ ColorScanlineConverter(ScanlineFormat eSourceFormat, int nComponentSize, long nScanlineSize)
+ : meSourceFormat(eSourceFormat)
+ , mnComponentSize(nComponentSize)
+ , mnComponentExchangeIndex(0)
+ , mnScanlineSize(nScanlineSize)
+ {
+ if (meSourceFormat == ScanlineFormat::N32BitTcAbgr ||
+ meSourceFormat == ScanlineFormat::N32BitTcArgb)
+ {
+ mnComponentExchangeIndex = 1;
+ }
+ }
+
+ void convertScanline(sal_uInt8* pSource, sal_uInt8* pDestination)
+ {
+ for (long x = 0; x < mnScanlineSize; x += mnComponentSize)
+ {
+ for (int i = 0; i < mnComponentSize; ++i)
+ {
+ pDestination[x + i] = pSource[x + i];
+ }
+ pDestination[x + mnComponentExchangeIndex + 0] = pSource[x + mnComponentExchangeIndex + 2];
+ pDestination[x + mnComponentExchangeIndex + 2] = pSource[x + mnComponentExchangeIndex + 0];
+ }
+ }
+};
+
+void convertToWinSalBitmap(SalBitmap& rSalBitmap, WinSalBitmap& rWinSalBitmap)
+{
+ BitmapPalette aBitmapPalette;
+ OpenGLSalBitmap* pGLSalBitmap = dynamic_cast<OpenGLSalBitmap*>(&rSalBitmap);
+ if (pGLSalBitmap != nullptr)
+ {
+ aBitmapPalette = pGLSalBitmap->GetBitmapPalette();
+ }
+#if HAVE_FEATURE_SKIA
+ if(SkiaSalBitmap* pSkiaSalBitmap = dynamic_cast<SkiaSalBitmap*>(&rSalBitmap))
+ aBitmapPalette = pSkiaSalBitmap->Palette();
+#endif
+
+ BitmapBuffer* pRead = rSalBitmap.AcquireBuffer(BitmapAccessMode::Read);
+
+ rWinSalBitmap.Create(rSalBitmap.GetSize(), rSalBitmap.GetBitCount(), aBitmapPalette);
+ BitmapBuffer* pWrite = rWinSalBitmap.AcquireBuffer(BitmapAccessMode::Write);
+
+ sal_uInt8* pSource(pRead->mpBits);
+ sal_uInt8* pDestination(pWrite->mpBits);
+ long readRowChange = pRead->mnScanlineSize;
+ if(pRead->mnFormat & ScanlineFormat::TopDown)
+ {
+ pSource += pRead->mnScanlineSize * (pRead->mnHeight - 1);
+ readRowChange = -readRowChange;
+ }
+
+ std::unique_ptr<ColorScanlineConverter> pConverter;
+
+ if (RemoveScanline(pRead->mnFormat) == ScanlineFormat::N24BitTcRgb)
+ pConverter.reset(new ColorScanlineConverter(ScanlineFormat::N24BitTcRgb,
+ 3, pRead->mnScanlineSize));
+ else if (RemoveScanline(pRead->mnFormat) == ScanlineFormat::N32BitTcRgba)
+ pConverter.reset(new ColorScanlineConverter(ScanlineFormat::N32BitTcRgba,
+ 4, pRead->mnScanlineSize));
+ if (pConverter)
+ {
+ for (long y = 0; y < pRead->mnHeight; y++)
+ {
+ pConverter->convertScanline(pSource, pDestination);
+ pSource += readRowChange;
+ pDestination += pWrite->mnScanlineSize;
+ }
+ }
+ else
+ {
+ for (long y = 0; y < pRead->mnHeight; y++)
+ {
+ memcpy(pDestination, pSource, pRead->mnScanlineSize);
+ pSource += readRowChange;
+ pDestination += pWrite->mnScanlineSize;
+ }
+ }
+ rWinSalBitmap.ReleaseBuffer(pWrite, BitmapAccessMode::Write);
+
+ rSalBitmap.ReleaseBuffer(pRead, BitmapAccessMode::Read);
+}
+
+} // end anonymous namespace
+
+void WinSalGraphics::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
+{
+ if (dynamic_cast<WinOpenGLSalGraphicsImpl*>(mpImpl.get()) == nullptr &&
+#if HAVE_FEATURE_SKIA
+ dynamic_cast<WinSkiaSalGraphicsImpl*>(mpImpl.get()) == nullptr &&
+#endif
+ dynamic_cast<const WinSalBitmap*>(&rSalBitmap) == nullptr)
+ {
+ std::unique_ptr<WinSalBitmap> pWinSalBitmap(new WinSalBitmap());
+ SalBitmap& rConstBitmap = const_cast<SalBitmap&>(rSalBitmap);
+ convertToWinSalBitmap(rConstBitmap, *pWinSalBitmap);
+ mpImpl->drawBitmap(rPosAry, *pWinSalBitmap);
+ }
+ else
+ {
+ mpImpl->drawBitmap(rPosAry, rSalBitmap);
+ }
+}
+
+void WinSalGraphics::drawBitmap( const SalTwoRect& rPosAry,
+ const SalBitmap& rSSalBitmap,
+ const SalBitmap& rSTransparentBitmap )
+{
+ if (dynamic_cast<WinOpenGLSalGraphicsImpl*>(mpImpl.get()) == nullptr &&
+#if HAVE_FEATURE_SKIA
+ dynamic_cast<WinSkiaSalGraphicsImpl*>(mpImpl.get()) == nullptr &&
+#endif
+ dynamic_cast<const WinSalBitmap*>(&rSSalBitmap) == nullptr)
+ {
+ std::unique_ptr<WinSalBitmap> pWinSalBitmap(new WinSalBitmap());
+ SalBitmap& rConstBitmap = const_cast<SalBitmap&>(rSSalBitmap);
+ convertToWinSalBitmap(rConstBitmap, *pWinSalBitmap);
+
+
+ std::unique_ptr<WinSalBitmap> pWinTransparentSalBitmap(new WinSalBitmap());
+ SalBitmap& rConstTransparentBitmap = const_cast<SalBitmap&>(rSTransparentBitmap);
+ convertToWinSalBitmap(rConstTransparentBitmap, *pWinTransparentSalBitmap);
+
+ mpImpl->drawBitmap(rPosAry, *pWinSalBitmap, *pWinTransparentSalBitmap);
+ }
+ else
+ {
+ mpImpl->drawBitmap(rPosAry, rSSalBitmap, rSTransparentBitmap);
+ }
+}
+
+bool WinSalGraphics::drawAlphaRect( long nX, long nY, long nWidth,
+ long nHeight, sal_uInt8 nTransparency )
+{
+ return mpImpl->drawAlphaRect( nX, nY, nWidth, nHeight, nTransparency );
+}
+
+void WinSalGraphics::drawMask( const SalTwoRect& rPosAry,
+ const SalBitmap& rSSalBitmap,
+ Color nMaskColor )
+{
+ mpImpl->drawMask( rPosAry, rSSalBitmap, nMaskColor );
+}
+
+std::shared_ptr<SalBitmap> WinSalGraphics::getBitmap( long nX, long nY, long nDX, long nDY )
+{
+ return mpImpl->getBitmap( nX, nY, nDX, nDY );
+}
+
+Color WinSalGraphics::getPixel( long nX, long nY )
+{
+ return mpImpl->getPixel( nX, nY );
+}
+
+void WinSalGraphics::invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags )
+{
+ mpImpl->invert( nX, nY, nWidth, nHeight, nFlags );
+}
+
+void WinSalGraphics::invert( sal_uInt32 nPoints, const SalPoint* pPtAry, SalInvert nSalFlags )
+{
+ mpImpl->invert( nPoints, pPtAry, nSalFlags );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/salgdi_gdiplus.cxx b/vcl/win/gdi/salgdi_gdiplus.cxx
new file mode 100644
index 000000000..f56a22760
--- /dev/null
+++ b/vcl/win/gdi/salgdi_gdiplus.cxx
@@ -0,0 +1,98 @@
+/* -*- 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 <string.h>
+#include <svsys.h>
+#include <win/wincomp.hxx>
+#include <win/saldata.hxx>
+#include <win/salgdi.h>
+#include <win/salbmp.h>
+
+#include "gdiimpl.hxx"
+
+bool WinSalGraphics::drawPolyPolygon(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fTransparency)
+{
+ return mpImpl->drawPolyPolygon(
+ rObjectToDevice,
+ rPolyPolygon,
+ fTransparency);
+}
+
+bool WinSalGraphics::drawPolyLine(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolygon& rPolygon,
+ double fTransparency,
+ double fLineWidth,
+ const std::vector< double >* pStroke, // MM01
+ basegfx::B2DLineJoin eLineJoin,
+ css::drawing::LineCap eLineCap,
+ double fMiterMinimumAngle,
+ bool bPixelSnapHairline)
+{
+ return mpImpl->drawPolyLine(
+ rObjectToDevice,
+ rPolygon,
+ fTransparency,
+ fLineWidth,
+ pStroke, // MM01
+ eLineJoin,
+ eLineCap,
+ fMiterMinimumAngle,
+ bPixelSnapHairline);
+}
+
+bool WinSalGraphics::blendBitmap(
+ const SalTwoRect& rTR,
+ const SalBitmap& rBmp)
+{
+ return mpImpl->blendBitmap(rTR, rBmp);
+}
+
+bool WinSalGraphics::blendAlphaBitmap(
+ const SalTwoRect& rTR,
+ const SalBitmap& rSrcBmp,
+ const SalBitmap& rMaskBmp,
+ const SalBitmap& rAlphaBmp)
+{
+ return mpImpl->blendAlphaBitmap(rTR, rSrcBmp, rMaskBmp, rAlphaBmp);
+}
+
+bool WinSalGraphics::drawAlphaBitmap(
+ const SalTwoRect& rTR,
+ const SalBitmap& rSrcBitmap,
+ const SalBitmap& rAlphaBmp)
+{
+ return mpImpl->drawAlphaBitmap(rTR, rSrcBitmap, rAlphaBmp);
+}
+
+bool WinSalGraphics::drawTransformedBitmap(
+ const basegfx::B2DPoint& rNull,
+ const basegfx::B2DPoint& rX,
+ const basegfx::B2DPoint& rY,
+ const SalBitmap& rSourceBitmap,
+ const SalBitmap* pAlphaBitmap)
+{
+ return mpImpl->drawTransformedBitmap(rNull, rX, rY,
+ rSourceBitmap, pAlphaBitmap);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/salnativewidgets-luna.cxx b/vcl/win/gdi/salnativewidgets-luna.cxx
new file mode 100644
index 000000000..68a18b020
--- /dev/null
+++ b/vcl/win/gdi/salnativewidgets-luna.cxx
@@ -0,0 +1,1559 @@
+/* -*- 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 .
+ */
+
+// General info:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/hh270423%28v=vs.85%29.aspx
+// http://msdn.microsoft.com/en-us/library/windows/desktop/bb773178%28v=vs.85%29.aspx
+
+// Useful tool to explore the themes & their rendering:
+// http://privat.rejbrand.se/UxExplore.exe
+// (found at http://stackoverflow.com/questions/4009701/windows-visual-themes-gallery-of-parts-and-states/4009712#4009712)
+
+// Theme subclasses:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/bb773218%28v=vs.85%29.aspx
+
+// Drawing in non-client area (general DWM-related info):
+// http://msdn.microsoft.com/en-us/library/windows/desktop/bb688195%28v=vs.85%29.aspx
+
+#include <rtl/ustring.h>
+
+#include <osl/diagnose.h>
+#include <osl/module.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <opengl/win/gdiimpl.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <win/svsys.h>
+#include <win/salgdi.h>
+#include <win/saldata.hxx>
+#include <win/scoped_gdi.hxx>
+
+#include <uxtheme.h>
+#include <vssym32.h>
+
+#include <map>
+#include <string>
+#include <optional>
+#include <ControlCacheKey.hxx>
+
+using namespace std;
+
+typedef map< wstring, HTHEME > ThemeMap;
+static ThemeMap aThemeMap;
+
+/****************************************************
+ wrap visual styles API to avoid linking against it
+ it is not available on all Windows platforms
+*****************************************************/
+
+namespace {
+
+class VisualStylesAPI
+{
+private:
+ typedef HTHEME (WINAPI * OpenThemeData_Proc_T) ( HWND hwnd, LPCWSTR pszClassList );
+ typedef HRESULT (WINAPI * CloseThemeData_Proc_T) ( HTHEME hTheme );
+ typedef HRESULT (WINAPI * GetThemeBackgroundContentRect_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pBoundingRect, RECT *pContentRect );
+ typedef HRESULT (WINAPI * DrawThemeBackground_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT *pClipRect );
+ typedef HRESULT (WINAPI * DrawThemeText_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect );
+ typedef HRESULT (WINAPI * GetThemePartSize_Proc_T) ( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *psz );
+ typedef BOOL (WINAPI * IsThemeActive_Proc_T) ( void );
+
+ OpenThemeData_Proc_T lpfnOpenThemeData;
+ CloseThemeData_Proc_T lpfnCloseThemeData;
+ GetThemeBackgroundContentRect_Proc_T lpfnGetThemeBackgroundContentRect;
+ DrawThemeBackground_Proc_T lpfnDrawThemeBackground;
+ DrawThemeText_Proc_T lpfnDrawThemeText;
+ GetThemePartSize_Proc_T lpfnGetThemePartSize;
+ IsThemeActive_Proc_T lpfnIsThemeActive;
+
+ oslModule mhModule;
+
+public:
+ VisualStylesAPI();
+ ~VisualStylesAPI();
+
+ HTHEME OpenThemeData( HWND hwnd, LPCWSTR pszClassList );
+ HRESULT CloseThemeData( HTHEME hTheme );
+ HRESULT GetThemeBackgroundContentRect( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pBoundingRect, RECT *pContentRect );
+ HRESULT DrawThemeBackground( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT *pClipRect );
+ HRESULT DrawThemeText( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect );
+ HRESULT GetThemePartSize( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *psz );
+ bool IsThemeActive();
+};
+
+}
+
+static VisualStylesAPI vsAPI;
+
+VisualStylesAPI::VisualStylesAPI()
+ : lpfnOpenThemeData( nullptr ),
+ lpfnCloseThemeData( nullptr ),
+ lpfnGetThemeBackgroundContentRect( nullptr ),
+ lpfnDrawThemeBackground( nullptr ),
+ lpfnDrawThemeText( nullptr ),
+ lpfnGetThemePartSize( nullptr ),
+ lpfnIsThemeActive( nullptr )
+{
+ OUString aLibraryName( "uxtheme.dll" );
+ mhModule = osl_loadModule( aLibraryName.pData, SAL_LOADMODULE_DEFAULT );
+
+ if ( mhModule )
+ {
+ lpfnOpenThemeData = reinterpret_cast<OpenThemeData_Proc_T>(osl_getAsciiFunctionSymbol( mhModule, "OpenThemeData" ));
+ lpfnCloseThemeData = reinterpret_cast<CloseThemeData_Proc_T>(osl_getAsciiFunctionSymbol( mhModule, "CloseThemeData" ));
+ lpfnGetThemeBackgroundContentRect = reinterpret_cast<GetThemeBackgroundContentRect_Proc_T>(osl_getAsciiFunctionSymbol( mhModule, "GetThemeBackgroundContentRect" ));
+ lpfnDrawThemeBackground = reinterpret_cast<DrawThemeBackground_Proc_T>(osl_getAsciiFunctionSymbol( mhModule, "DrawThemeBackground" ));
+ lpfnDrawThemeText = reinterpret_cast<DrawThemeText_Proc_T>(osl_getAsciiFunctionSymbol( mhModule, "DrawThemeText" ));
+ lpfnGetThemePartSize = reinterpret_cast<GetThemePartSize_Proc_T>(osl_getAsciiFunctionSymbol( mhModule, "GetThemePartSize" ));
+ lpfnIsThemeActive = reinterpret_cast<IsThemeActive_Proc_T>(osl_getAsciiFunctionSymbol( mhModule, "IsThemeActive" ));
+ }
+}
+
+VisualStylesAPI::~VisualStylesAPI()
+{
+ if( mhModule )
+ osl_unloadModule( mhModule );
+}
+
+HTHEME VisualStylesAPI::OpenThemeData( HWND hwnd, LPCWSTR pszClassList )
+{
+ if(lpfnOpenThemeData)
+ return (*lpfnOpenThemeData) (hwnd, pszClassList);
+ else
+ return nullptr;
+}
+
+HRESULT VisualStylesAPI::CloseThemeData( HTHEME hTheme )
+{
+ if(lpfnCloseThemeData)
+ return (*lpfnCloseThemeData) (hTheme);
+ else
+ return S_FALSE;
+}
+
+HRESULT VisualStylesAPI::GetThemeBackgroundContentRect( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pBoundingRect, RECT *pContentRect )
+{
+ if(lpfnGetThemeBackgroundContentRect)
+ return (*lpfnGetThemeBackgroundContentRect) ( hTheme, hdc, iPartId, iStateId, pBoundingRect, pContentRect );
+ else
+ return S_FALSE;
+}
+
+HRESULT VisualStylesAPI::DrawThemeBackground( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, const RECT *pRect, const RECT *pClipRect )
+{
+ if(lpfnDrawThemeBackground)
+ return (*lpfnDrawThemeBackground) (hTheme, hdc, iPartId, iStateId, pRect, pClipRect);
+ else
+ return S_FALSE;
+}
+
+HRESULT VisualStylesAPI::DrawThemeText( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect )
+{
+ if(lpfnDrawThemeText)
+ return (*lpfnDrawThemeText) (hTheme, hdc, iPartId, iStateId, pszText, iCharCount, dwTextFlags, dwTextFlags2, pRect);
+ else
+ return S_FALSE;
+}
+
+HRESULT VisualStylesAPI::GetThemePartSize( HTHEME hTheme, HDC hdc, int iPartId, int iStateId, RECT *prc, THEMESIZE eSize, SIZE *psz )
+{
+ if(lpfnGetThemePartSize)
+ return (*lpfnGetThemePartSize) (hTheme, hdc, iPartId, iStateId, prc, eSize, psz);
+ else
+ return S_FALSE;
+}
+
+bool VisualStylesAPI::IsThemeActive()
+{
+ if(lpfnIsThemeActive)
+ return (*lpfnIsThemeActive) ();
+ else
+ return false;
+}
+
+/*********************************************************
+ * Initialize XP theming and local stuff
+ *********************************************************/
+void SalData::initNWF()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // the menu bar and the top docking area should have a common background (gradient)
+ pSVData->maNWFData.mbMenuBarDockingAreaCommonBG = true;
+}
+
+// *********************************************************
+// * Release theming handles
+// ********************************************************
+void SalData::deInitNWF()
+{
+ for( auto& rEntry : aThemeMap )
+ vsAPI.CloseThemeData(rEntry.second);
+ aThemeMap.clear();
+}
+
+static HTHEME getThemeHandle( HWND hWnd, LPCWSTR name )
+{
+ if( GetSalData()->mbThemeChanged )
+ {
+ // throw away invalid theme handles
+ SalData::deInitNWF();
+ GetSalData()->mbThemeChanged = false;
+ }
+
+ ThemeMap::iterator iter;
+ if( (iter = aThemeMap.find( name )) != aThemeMap.end() )
+ return iter->second;
+ // theme not found -> add it to map
+ HTHEME hTheme = vsAPI.OpenThemeData( hWnd, name );
+ if( hTheme != nullptr )
+ aThemeMap[name] = hTheme;
+ return hTheme;
+}
+
+bool WinSalGraphics::isNativeControlSupported( ControlType nType, ControlPart nPart )
+{
+ HTHEME hTheme = nullptr;
+
+ switch( nType )
+ {
+ case ControlType::Pushbutton:
+ case ControlType::Radiobutton:
+ case ControlType::Checkbox:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Button");
+ break;
+ case ControlType::Scrollbar:
+ if( nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert )
+ return false; // no background painting needed
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Scrollbar");
+ break;
+ case ControlType::Combobox:
+ if( nPart == ControlPart::HasBackgroundTexture )
+ return false; // we do not paint the inner part (ie the selection background/focus indication)
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ else if( nPart == ControlPart::ButtonDown )
+ hTheme = getThemeHandle( mhWnd, L"Combobox");
+ break;
+ case ControlType::Spinbox:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ else if( nPart == ControlPart::AllButtons ||
+ nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonDown ||
+ nPart == ControlPart::ButtonLeft|| nPart == ControlPart::ButtonRight )
+ hTheme = getThemeHandle( mhWnd, L"Spin");
+ break;
+ case ControlType::SpinButtons:
+ if( nPart == ControlPart::Entire || nPart == ControlPart::AllButtons )
+ hTheme = getThemeHandle( mhWnd, L"Spin");
+ break;
+ case ControlType::Editbox:
+ case ControlType::MultilineEditbox:
+ if( nPart == ControlPart::HasBackgroundTexture )
+ return false; // we do not paint the inner part (ie the selection background/focus indication)
+ //return TRUE;
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ break;
+ case ControlType::Listbox:
+ if( nPart == ControlPart::HasBackgroundTexture )
+ return false; // we do not paint the inner part (ie the selection background/focus indication)
+ if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow )
+ hTheme = getThemeHandle( mhWnd, L"Listview");
+ else if( nPart == ControlPart::ButtonDown )
+ hTheme = getThemeHandle( mhWnd, L"Combobox");
+ break;
+ case ControlType::TabPane:
+ case ControlType::TabBody:
+ case ControlType::TabItem:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Tab");
+ break;
+ case ControlType::Toolbar:
+ if( nPart == ControlPart::Entire || nPart == ControlPart::Button )
+ hTheme = getThemeHandle( mhWnd, L"Toolbar");
+ else
+ // use rebar theme for grip and background
+ hTheme = getThemeHandle( mhWnd, L"Rebar");
+ break;
+ case ControlType::Menubar:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Rebar");
+ else if( GetSalData()->mbThemeMenuSupport )
+ {
+ if( nPart == ControlPart::MenuItem )
+ hTheme = getThemeHandle( mhWnd, L"Menu" );
+ }
+ break;
+ case ControlType::MenuPopup:
+ if( GetSalData()->mbThemeMenuSupport )
+ {
+ if( nPart == ControlPart::Entire ||
+ nPart == ControlPart::MenuItem ||
+ nPart == ControlPart::MenuItemCheckMark ||
+ nPart == ControlPart::MenuItemRadioMark ||
+ nPart == ControlPart::Separator )
+ hTheme = getThemeHandle( mhWnd, L"Menu" );
+ }
+ break;
+ case ControlType::Progress:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Progress");
+ break;
+ case ControlType::Slider:
+ if( nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea )
+ hTheme = getThemeHandle( mhWnd, L"Trackbar" );
+ break;
+ case ControlType::ListNode:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"TreeView" );
+ break;
+ default:
+ hTheme = nullptr;
+ break;
+ }
+
+ return (hTheme != nullptr);
+}
+
+bool WinSalGraphics::hitTestNativeControl( ControlType,
+ ControlPart,
+ const tools::Rectangle&,
+ const Point&,
+ bool& )
+{
+ return false;
+}
+
+static bool ImplDrawTheme( HTHEME hTheme, HDC hDC, int iPart, int iState, RECT rc, const OUString& aStr)
+{
+ HRESULT hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
+
+ if( aStr.getLength() )
+ {
+ RECT rcContent;
+ hr = vsAPI.GetThemeBackgroundContentRect( hTheme, hDC, iPart, iState, &rc, &rcContent);
+ hr = vsAPI.DrawThemeText( hTheme, hDC, iPart, iState,
+ o3tl::toW(aStr.getStr()), -1,
+ DT_CENTER | DT_VCENTER | DT_SINGLELINE,
+ 0, &rcContent);
+ }
+ return (hr == S_OK);
+}
+
+static tools::Rectangle ImplGetThemeRect( HTHEME hTheme, HDC hDC, int iPart, int iState, const tools::Rectangle& /* aRect */, THEMESIZE eTS = TS_TRUE )
+{
+ SIZE aSz;
+ HRESULT hr = vsAPI.GetThemePartSize( hTheme, hDC, iPart, iState, nullptr, eTS, &aSz ); // TS_TRUE returns optimal size
+ if( hr == S_OK )
+ return tools::Rectangle( 0, 0, aSz.cx, aSz.cy );
+ else
+ return tools::Rectangle();
+}
+
+// Helper functions
+
+static void ImplConvertSpinbuttonValues( ControlPart nControlPart, const ControlState& rState, const tools::Rectangle& rRect,
+ int* pLunaPart, int *pLunaState, RECT *pRect )
+{
+ if( nControlPart == ControlPart::ButtonDown )
+ {
+ *pLunaPart = SPNP_DOWN;
+ if( rState & ControlState::PRESSED )
+ *pLunaState = DNS_PRESSED;
+ else if( !(rState & ControlState::ENABLED) )
+ *pLunaState = DNS_DISABLED;
+ else if( rState & ControlState::ROLLOVER )
+ *pLunaState = DNS_HOT;
+ else
+ *pLunaState = DNS_NORMAL;
+ }
+ if( nControlPart == ControlPart::ButtonUp )
+ {
+ *pLunaPart = SPNP_UP;
+ if( rState & ControlState::PRESSED )
+ *pLunaState = UPS_PRESSED;
+ else if( !(rState & ControlState::ENABLED) )
+ *pLunaState = UPS_DISABLED;
+ else if( rState & ControlState::ROLLOVER )
+ *pLunaState = UPS_HOT;
+ else
+ *pLunaState = UPS_NORMAL;
+ }
+ if( nControlPart == ControlPart::ButtonRight )
+ {
+ *pLunaPart = SPNP_UPHORZ;
+ if( rState & ControlState::PRESSED )
+ *pLunaState = DNHZS_PRESSED;
+ else if( !(rState & ControlState::ENABLED) )
+ *pLunaState = DNHZS_DISABLED;
+ else if( rState & ControlState::ROLLOVER )
+ *pLunaState = DNHZS_HOT;
+ else
+ *pLunaState = DNHZS_NORMAL;
+ }
+ if( nControlPart == ControlPart::ButtonLeft )
+ {
+ *pLunaPart = SPNP_DOWNHORZ;
+ if( rState & ControlState::PRESSED )
+ *pLunaState = UPHZS_PRESSED;
+ else if( !(rState & ControlState::ENABLED) )
+ *pLunaState = UPHZS_DISABLED;
+ else if( rState & ControlState::ROLLOVER )
+ *pLunaState = UPHZS_HOT;
+ else
+ *pLunaState = UPHZS_NORMAL;
+ }
+
+ pRect->left = rRect.Left();
+ pRect->right = rRect.Right()+1;
+ pRect->top = rRect.Top();
+ pRect->bottom = rRect.Bottom()+1;
+}
+
+/// Draw an own toolbar style on Windows Vista or later, looks better there
+static void impl_drawAeroToolbar( HDC hDC, RECT rc, bool bHorizontal )
+{
+ if ( rc.top == 0 && bHorizontal )
+ {
+ const long GRADIENT_HEIGHT = 32;
+
+ long gradient_break = rc.top;
+ long gradient_bottom = rc.bottom - 1;
+ GRADIENT_RECT g_rect[1] = { { 0, 1 } };
+
+ // very slow gradient at the top (if we have space for that)
+ if ( gradient_bottom - rc.top > GRADIENT_HEIGHT )
+ {
+ gradient_break = gradient_bottom - GRADIENT_HEIGHT;
+
+ TRIVERTEX vert[2] = {
+ { rc.left, rc.top, 0xff00, 0xff00, 0xff00, 0xff00 },
+ { rc.right, gradient_break, 0xfa00, 0xfa00, 0xfa00, 0xff00 },
+ };
+ GdiGradientFill( hDC, vert, 2, g_rect, 1, GRADIENT_FILL_RECT_V );
+ }
+
+ // gradient at the bottom
+ TRIVERTEX vert[2] = {
+ { rc.left, gradient_break, 0xfa00, 0xfa00, 0xfa00, 0xff00 },
+ { rc.right, gradient_bottom, 0xf000, 0xf000, 0xf000, 0xff00 }
+ };
+ GdiGradientFill( hDC, vert, 2, g_rect, 1, GRADIENT_FILL_RECT_V );
+
+ // and a darker horizontal line under that
+ ScopedSelectedHPEN hPen(hDC, CreatePen(PS_SOLID, 1, RGB( 0xb0, 0xb0, 0xb0)));
+
+ MoveToEx( hDC, rc.left, gradient_bottom, nullptr );
+ LineTo( hDC, rc.right, gradient_bottom );
+ }
+ else
+ {
+ ScopedHBRUSH hbrush(CreateSolidBrush(RGB(0xf0, 0xf0, 0xf0)));
+ FillRect(hDC, &rc, hbrush.get());
+
+ // darker line to distinguish the toolbar and viewshell
+ // it is drawn only for the horizontal toolbars; it did not look well
+ // when done for the vertical ones too
+ if ( bHorizontal )
+ {
+ long from_x, from_y, to_x, to_y;
+
+ from_x = rc.left;
+ to_x = rc.right;
+ from_y = to_y = rc.top;
+
+ ScopedSelectedHPEN hPen(hDC, CreatePen(PS_SOLID, 1, RGB( 0xb0, 0xb0, 0xb0)));
+
+ MoveToEx( hDC, from_x, from_y, nullptr );
+ LineTo( hDC, to_x, to_y );
+ }
+ }
+}
+
+/**
+ * Gives the actual rectangle used for rendering by ControlType::MenuPopup's
+ * ControlPart::MenuItemCheckMark or ControlPart::MenuItemRadioMark.
+ */
+static tools::Rectangle GetMenuPopupMarkRegion(const ImplControlValue& rValue)
+{
+ tools::Rectangle aRet;
+
+ auto pMVal = dynamic_cast<const MenupopupValue*>(&rValue);
+ if (!pMVal)
+ return aRet;
+
+ aRet.SetTop(pMVal->maItemRect.Top());
+ aRet.SetBottom(pMVal->maItemRect.Bottom() + 1); // see below in drawNativeControl
+ if (AllSettings::GetLayoutRTL())
+ {
+ aRet.SetRight(pMVal->maItemRect.Right() + 1);
+ aRet.SetLeft(aRet.Right() - (pMVal->getNumericVal() - pMVal->maItemRect.Left()));
+ }
+ else
+ {
+ aRet.SetRight(pMVal->getNumericVal());
+ aRet.SetLeft(pMVal->maItemRect.Left());
+ }
+
+ return aRet;
+}
+
+static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,
+ ControlType nType,
+ ControlPart nPart,
+ ControlState nState,
+ const ImplControlValue& aValue,
+ OUString const & aCaption )
+{
+ // a listbox dropdown is actually a combobox dropdown
+ if( nType == ControlType::Listbox )
+ if( nPart == ControlPart::ButtonDown )
+ nType = ControlType::Combobox;
+
+ // draw entire combobox as a large edit box
+ if( nType == ControlType::Combobox )
+ if( nPart == ControlPart::Entire )
+ nType = ControlType::Editbox;
+
+ // draw entire spinbox as a large edit box
+ if( nType == ControlType::Spinbox )
+ if( nPart == ControlPart::Entire )
+ nType = ControlType::Editbox;
+
+ int iPart(0), iState(0);
+ if( nType == ControlType::Scrollbar )
+ {
+ HRESULT hr;
+ if( nPart == ControlPart::ButtonUp )
+ {
+ iPart = SBP_ARROWBTN;
+ if( nState & ControlState::PRESSED )
+ iState = ABS_UPPRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = ABS_UPDISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = ABS_UPHOT;
+ else
+ iState = ABS_UPNORMAL;
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
+ return (hr == S_OK);
+ }
+ if( nPart == ControlPart::ButtonDown )
+ {
+ iPart = SBP_ARROWBTN;
+ if( nState & ControlState::PRESSED )
+ iState = ABS_DOWNPRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = ABS_DOWNDISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = ABS_DOWNHOT;
+ else
+ iState = ABS_DOWNNORMAL;
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
+ return (hr == S_OK);
+ }
+ if( nPart == ControlPart::ButtonLeft )
+ {
+ iPart = SBP_ARROWBTN;
+ if( nState & ControlState::PRESSED )
+ iState = ABS_LEFTPRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = ABS_LEFTDISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = ABS_LEFTHOT;
+ else
+ iState = ABS_LEFTNORMAL;
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
+ return (hr == S_OK);
+ }
+ if( nPart == ControlPart::ButtonRight )
+ {
+ iPart = SBP_ARROWBTN;
+ if( nState & ControlState::PRESSED )
+ iState = ABS_RIGHTPRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = ABS_RIGHTDISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = ABS_RIGHTHOT;
+ else
+ iState = ABS_RIGHTNORMAL;
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
+ return (hr == S_OK);
+ }
+ if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert )
+ {
+ iPart = (nPart == ControlPart::ThumbHorz) ? SBP_THUMBBTNHORZ : SBP_THUMBBTNVERT;
+ if( nState & ControlState::PRESSED )
+ iState = SCRBS_PRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = SCRBS_DISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = SCRBS_HOT;
+ else
+ iState = SCRBS_NORMAL;
+
+ SIZE sz;
+ vsAPI.GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_MIN, &sz);
+ vsAPI.GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_TRUE, &sz);
+ vsAPI.GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_DRAW, &sz);
+
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
+ // paint gripper on thumb if enough space
+ if( ( (nPart == ControlPart::ThumbVert) && (rc.bottom-rc.top > 12) ) ||
+ ( (nPart == ControlPart::ThumbHorz) && (rc.right-rc.left > 12) ) )
+ {
+ iPart = (nPart == ControlPart::ThumbHorz) ? SBP_GRIPPERHORZ : SBP_GRIPPERVERT;
+ iState = 0;
+ vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
+ }
+ return (hr == S_OK);
+ }
+ if( nPart == ControlPart::TrackHorzLeft || nPart == ControlPart::TrackHorzRight || nPart == ControlPart::TrackVertUpper || nPart == ControlPart::TrackVertLower )
+ {
+ switch( nPart )
+ {
+ case ControlPart::TrackHorzLeft: iPart = SBP_UPPERTRACKHORZ; break;
+ case ControlPart::TrackHorzRight: iPart = SBP_LOWERTRACKHORZ; break;
+ case ControlPart::TrackVertUpper: iPart = SBP_UPPERTRACKVERT; break;
+ case ControlPart::TrackVertLower: iPart = SBP_LOWERTRACKVERT; break;
+ default: break;
+ }
+
+ if( nState & ControlState::PRESSED )
+ iState = SCRBS_PRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = SCRBS_DISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = SCRBS_HOT;
+ else
+ iState = SCRBS_NORMAL;
+ hr = vsAPI.DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
+ return (hr == S_OK);
+ }
+ }
+ if( nType == ControlType::SpinButtons && nPart == ControlPart::AllButtons )
+ {
+ if( aValue.getType() == ControlType::SpinButtons )
+ {
+ const SpinbuttonValue* pValue = (aValue.getType() == ControlType::SpinButtons) ? static_cast<const SpinbuttonValue*>(&aValue) : nullptr;
+
+ RECT rect;
+ ImplConvertSpinbuttonValues( pValue->mnUpperPart, pValue->mnUpperState, pValue->maUpperRect, &iPart, &iState, &rect );
+ bool bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
+
+ if( bOk )
+ {
+ ImplConvertSpinbuttonValues( pValue->mnLowerPart, pValue->mnLowerState, pValue->maLowerRect, &iPart, &iState, &rect );
+ bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
+ }
+
+ return bOk;
+ }
+ }
+ if( nType == ControlType::Spinbox )
+ {
+ if( nPart == ControlPart::AllButtons )
+ {
+ if( aValue.getType() == ControlType::SpinButtons )
+ {
+ const SpinbuttonValue *pValue = static_cast<const SpinbuttonValue*>(&aValue);
+
+ RECT rect;
+ ImplConvertSpinbuttonValues( pValue->mnUpperPart, pValue->mnUpperState, pValue->maUpperRect, &iPart, &iState, &rect );
+ bool bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
+
+ if( bOk )
+ {
+ ImplConvertSpinbuttonValues( pValue->mnLowerPart, pValue->mnLowerState, pValue->maLowerRect, &iPart, &iState, &rect );
+ bOk = ImplDrawTheme( hTheme, hDC, iPart, iState, rect, aCaption);
+ }
+
+ return bOk;
+ }
+ }
+
+ if( nPart == ControlPart::ButtonDown )
+ {
+ iPart = SPNP_DOWN;
+ if( nState & ControlState::PRESSED )
+ iState = DNS_PRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = DNS_DISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = DNS_HOT;
+ else
+ iState = DNS_NORMAL;
+ }
+ if( nPart == ControlPart::ButtonUp )
+ {
+ iPart = SPNP_UP;
+ if( nState & ControlState::PRESSED )
+ iState = UPS_PRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = UPS_DISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = UPS_HOT;
+ else
+ iState = UPS_NORMAL;
+ }
+ if( nPart == ControlPart::ButtonRight )
+ {
+ iPart = SPNP_DOWNHORZ;
+ if( nState & ControlState::PRESSED )
+ iState = DNHZS_PRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = DNHZS_DISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = DNHZS_HOT;
+ else
+ iState = DNHZS_NORMAL;
+ }
+ if( nPart == ControlPart::ButtonLeft )
+ {
+ iPart = SPNP_UPHORZ;
+ if( nState & ControlState::PRESSED )
+ iState = UPHZS_PRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = UPHZS_DISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = UPHZS_HOT;
+ else
+ iState = UPHZS_NORMAL;
+ }
+ if( nPart == ControlPart::ButtonLeft || nPart == ControlPart::ButtonRight || nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonDown )
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ if( nType == ControlType::Combobox )
+ {
+ if( nPart == ControlPart::ButtonDown )
+ {
+ iPart = CP_DROPDOWNBUTTON;
+ if( nState & ControlState::PRESSED )
+ iState = CBXS_PRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = CBXS_DISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = CBXS_HOT;
+ else
+ iState = CBXS_NORMAL;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ }
+ if( nType == ControlType::Pushbutton )
+ {
+ iPart = BP_PUSHBUTTON;
+ if( nState & ControlState::PRESSED )
+ iState = PBS_PRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = PBS_DISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = PBS_HOT;
+ else if( nState & ControlState::DEFAULT )
+ iState = PBS_DEFAULTED;
+ //else if( nState & ControlState::FOCUSED )
+ // iState = PBS_DEFAULTED; // may need to draw focus rect
+ else
+ iState = PBS_NORMAL;
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == ControlType::Radiobutton )
+ {
+ iPart = BP_RADIOBUTTON;
+ bool bChecked = ( aValue.getTristateVal() == ButtonValue::On );
+
+ if( nState & ControlState::PRESSED )
+ iState = bChecked ? RBS_CHECKEDPRESSED : RBS_UNCHECKEDPRESSED;
+ else if( !(nState & ControlState::ENABLED) )
+ iState = bChecked ? RBS_CHECKEDDISABLED : RBS_UNCHECKEDDISABLED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = bChecked ? RBS_CHECKEDHOT : RBS_UNCHECKEDHOT;
+ else
+ iState = bChecked ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL;
+
+ //if( nState & ControlState::FOCUSED )
+ // iState |= PBS_DEFAULTED; // may need to draw focus rect
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == ControlType::Checkbox )
+ {
+ iPart = BP_CHECKBOX;
+ ButtonValue v = aValue.getTristateVal();
+
+ if( nState & ControlState::PRESSED )
+ iState = (v == ButtonValue::On) ? CBS_CHECKEDPRESSED :
+ ( (v == ButtonValue::Off) ? CBS_UNCHECKEDPRESSED : CBS_MIXEDPRESSED );
+ else if( !(nState & ControlState::ENABLED) )
+ iState = (v == ButtonValue::On) ? CBS_CHECKEDDISABLED :
+ ( (v == ButtonValue::Off) ? CBS_UNCHECKEDDISABLED : CBS_MIXEDDISABLED );
+ else if( nState & ControlState::ROLLOVER )
+ iState = (v == ButtonValue::On) ? CBS_CHECKEDHOT :
+ ( (v == ButtonValue::Off) ? CBS_UNCHECKEDHOT : CBS_MIXEDHOT );
+ else
+ iState = (v == ButtonValue::On) ? CBS_CHECKEDNORMAL :
+ ( (v == ButtonValue::Off) ? CBS_UNCHECKEDNORMAL : CBS_MIXEDNORMAL );
+
+ //if( nState & ControlState::FOCUSED )
+ // iState |= PBS_DEFAULTED; // may need to draw focus rect
+
+ //SIZE sz;
+ //THEMESIZE eSize = TS_DRAW; // TS_MIN, TS_TRUE, TS_DRAW
+ //vsAPI.GetThemePartSize( hTheme, hDC, iPart, iState, &rc, eSize, &sz);
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( ( nType == ControlType::Editbox ) || ( nType == ControlType::MultilineEditbox ) )
+ {
+ iPart = EP_EDITTEXT;
+ if( !(nState & ControlState::ENABLED) )
+ iState = ETS_DISABLED;
+ else if( nState & ControlState::FOCUSED )
+ iState = ETS_FOCUSED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = ETS_HOT;
+ else
+ iState = ETS_NORMAL;
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == ControlType::Listbox )
+ {
+ if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow )
+ {
+ iPart = LVP_EMPTYTEXT; // ??? no idea which part to choose here
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ }
+
+ if( nType == ControlType::TabPane )
+ {
+ iPart = TABP_PANE;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == ControlType::TabBody )
+ {
+ iPart = TABP_BODY;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == ControlType::TabItem )
+ {
+ iPart = TABP_TABITEMLEFTEDGE;
+ rc.bottom--;
+
+ OSL_ASSERT( aValue.getType() == ControlType::TabItem );
+
+ const TabitemValue& rValue = static_cast<const TabitemValue&>(aValue);
+ if (rValue.isBothAligned())
+ {
+ iPart = TABP_TABITEMLEFTEDGE;
+ rc.right--;
+ }
+ else if (rValue.isLeftAligned())
+ iPart = TABP_TABITEMLEFTEDGE;
+ else if (rValue.isRightAligned())
+ iPart = TABP_TABITEMRIGHTEDGE;
+ else iPart = TABP_TABITEM;
+
+ if( !(nState & ControlState::ENABLED) )
+ iState = TILES_DISABLED;
+ else if( nState & ControlState::SELECTED )
+ {
+ iState = TILES_SELECTED;
+ // increase the selected tab
+ rc.left-=2;
+ if (rValue.isBothAligned())
+ {
+ if (rValue.isLeftAligned() || rValue.isNotAligned())
+ rc.right+=2;
+ if (rValue.isRightAligned())
+ rc.right+=1;
+ }
+ rc.top-=2;
+ rc.bottom+=2;
+ }
+ else if( nState & ControlState::ROLLOVER )
+ iState = TILES_HOT;
+ else if( nState & ControlState::FOCUSED )
+ iState = TILES_FOCUSED; // may need to draw focus rect
+ else
+ iState = TILES_NORMAL;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == ControlType::Toolbar )
+ {
+ if( nPart == ControlPart::Button )
+ {
+ iPart = TP_BUTTON;
+ bool bChecked = ( aValue.getTristateVal() == ButtonValue::On );
+ if( !(nState & ControlState::ENABLED) )
+ //iState = TS_DISABLED;
+ // disabled buttons are typically not painted at all but we need visual
+ // feedback when travelling by keyboard over disabled entries
+ iState = TS_HOT;
+ else if( nState & ControlState::PRESSED )
+ iState = TS_PRESSED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = bChecked ? TS_HOTCHECKED : TS_HOT;
+ else
+ iState = bChecked ? TS_CHECKED : TS_NORMAL;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ else if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert )
+ {
+ // the vertical gripper is not supported in most themes and it makes no
+ // sense to only support horizontal gripper
+ //iPart = (nPart == ControlPart::ThumbHorz) ? RP_GRIPPERVERT : RP_GRIPPER;
+ //return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ else if( nPart == ControlPart::DrawBackgroundHorz || nPart == ControlPart::DrawBackgroundVert )
+ {
+ if( aValue.getType() == ControlType::Toolbar )
+ {
+ const ToolbarValue *pValue = static_cast<const ToolbarValue*>(&aValue);
+ if( pValue->mbIsTopDockingArea )
+ rc.top = 0; // extend potential gradient to cover menu bar as well
+ }
+
+ // make it more compatible with Aero
+ if( ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames )
+ {
+ impl_drawAeroToolbar( hDC, rc, nPart == ControlPart::DrawBackgroundHorz );
+ return true;
+ }
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ }
+
+ if( nType == ControlType::Menubar )
+ {
+ if( nPart == ControlPart::Entire )
+ {
+ if( aValue.getType() == ControlType::Menubar )
+ {
+ const MenubarValue *pValue = static_cast<const MenubarValue*>(&aValue);
+ rc.bottom += pValue->maTopDockingAreaHeight; // extend potential gradient to cover docking area as well
+
+ // make it more compatible with Aero
+ if( ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames )
+ {
+ impl_drawAeroToolbar( hDC, rc, true );
+ return true;
+ }
+ }
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+ else if( nPart == ControlPart::MenuItem )
+ {
+ if( nState & ControlState::ENABLED )
+ {
+ if( nState & ControlState::SELECTED )
+ iState = MBI_PUSHED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = MBI_HOT;
+ else
+ iState = MBI_NORMAL;
+ }
+ else
+ {
+ if( nState & ControlState::SELECTED )
+ iState = MBI_DISABLEDPUSHED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = MBI_DISABLEDHOT;
+ else
+ iState = MBI_DISABLED;
+ }
+ return ImplDrawTheme( hTheme, hDC, MENU_BARITEM, iState, rc, aCaption );
+ }
+ }
+
+ if( nType == ControlType::Progress )
+ {
+ if( nPart != ControlPart::Entire )
+ return false;
+
+ if( ! ImplDrawTheme( hTheme, hDC, PP_BAR, iState, rc, aCaption) )
+ return false;
+ RECT aProgressRect = rc;
+ if( vsAPI.GetThemeBackgroundContentRect( hTheme, hDC, PP_BAR, iState, &rc, &aProgressRect) != S_OK )
+ return false;
+
+ long nProgressWidth = aValue.getNumericVal();
+ nProgressWidth *= (aProgressRect.right - aProgressRect.left);
+ nProgressWidth /= (rc.right - rc.left);
+ if( AllSettings::GetLayoutRTL() )
+ aProgressRect.left = aProgressRect.right - nProgressWidth;
+ else
+ aProgressRect.right = aProgressRect.left + nProgressWidth;
+
+ return ImplDrawTheme( hTheme, hDC, PP_CHUNK, iState, aProgressRect, aCaption );
+ }
+
+ if( nType == ControlType::Slider )
+ {
+ iPart = (nPart == ControlPart::TrackHorzArea) ? TKP_TRACK : TKP_TRACKVERT;
+ iState = (nPart == ControlPart::TrackHorzArea) ? static_cast<int>(TRS_NORMAL) : static_cast<int>(TRVS_NORMAL);
+
+ tools::Rectangle aTrackRect = ImplGetThemeRect( hTheme, hDC, iPart, iState, tools::Rectangle() );
+ RECT aTRect = rc;
+ if( nPart == ControlPart::TrackHorzArea )
+ {
+ long nH = aTrackRect.GetHeight();
+ aTRect.top += (rc.bottom - rc.top - nH)/2;
+ aTRect.bottom = aTRect.top + nH;
+ }
+ else
+ {
+ long nW = aTrackRect.GetWidth();
+ aTRect.left += (rc.right - rc.left - nW)/2;
+ aTRect.right = aTRect.left + nW;
+ }
+ ImplDrawTheme( hTheme, hDC, iPart, iState, aTRect, aCaption );
+
+ RECT aThumbRect;
+ OSL_ASSERT( aValue.getType() == ControlType::Slider );
+ const SliderValue* pVal = static_cast<const SliderValue*>(&aValue);
+ aThumbRect.left = pVal->maThumbRect.Left();
+ aThumbRect.top = pVal->maThumbRect.Top();
+ aThumbRect.right = pVal->maThumbRect.Right();
+ aThumbRect.bottom = pVal->maThumbRect.Bottom();
+ iPart = (nPart == ControlPart::TrackHorzArea) ? TKP_THUMB : TKP_THUMBVERT;
+ iState = (nState & ControlState::ENABLED) ? TUS_NORMAL : TUS_DISABLED;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, aThumbRect, aCaption );
+ }
+
+ if( nType == ControlType::ListNode )
+ {
+ if( nPart != ControlPart::Entire )
+ return false;
+
+ ButtonValue aButtonValue = aValue.getTristateVal();
+ iPart = TVP_GLYPH;
+ switch( aButtonValue )
+ {
+ case ButtonValue::On:
+ iState = GLPS_OPENED;
+ break;
+ case ButtonValue::Off:
+ iState = GLPS_CLOSED;
+ break;
+ default:
+ return false;
+ }
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption );
+ }
+
+ if( GetSalData()->mbThemeMenuSupport )
+ {
+ if( nType == ControlType::MenuPopup )
+ {
+ if( nPart == ControlPart::Entire )
+ {
+ RECT aGutterRC = rc;
+ if( AllSettings::GetLayoutRTL() )
+ {
+ aGutterRC.right -= aValue.getNumericVal()+1;
+ aGutterRC.left = aGutterRC.right-3;
+ }
+ else
+ {
+ aGutterRC.left += aValue.getNumericVal();
+ aGutterRC.right = aGutterRC.left+3;
+ }
+ return
+ ImplDrawTheme( hTheme, hDC, MENU_POPUPBACKGROUND, 0, rc, aCaption ) &&
+ ImplDrawTheme( hTheme, hDC, MENU_POPUPGUTTER, 0, aGutterRC, aCaption )
+ ;
+ }
+ else if( nPart == ControlPart::MenuItem )
+ {
+ if( nState & ControlState::ENABLED )
+ iState = (nState & ControlState::SELECTED) ? MPI_HOT : MPI_NORMAL;
+ else
+ iState = (nState & ControlState::SELECTED) ? MPI_DISABLEDHOT : MPI_DISABLED;
+ return ImplDrawTheme( hTheme, hDC, MENU_POPUPITEM, iState, rc, aCaption );
+ }
+ else if( nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark )
+ {
+ if( nState & ControlState::PRESSED )
+ {
+ RECT aBGRect = rc;
+ if( aValue.getType() == ControlType::MenuPopup )
+ {
+ tools::Rectangle aRectangle = GetMenuPopupMarkRegion(aValue);
+ aBGRect.top = aRectangle.Top();
+ aBGRect.left = aRectangle.Left();
+ aBGRect.bottom = aRectangle.Bottom();
+ aBGRect.right = aRectangle.Right();
+ rc = aBGRect;
+ }
+ iState = (nState & ControlState::ENABLED) ? MCB_NORMAL : MCB_DISABLED;
+ ImplDrawTheme( hTheme, hDC, MENU_POPUPCHECKBACKGROUND, iState, aBGRect, aCaption );
+ if( nPart == ControlPart::MenuItemCheckMark )
+ iState = (nState & ControlState::ENABLED) ? MC_CHECKMARKNORMAL : MC_CHECKMARKDISABLED;
+ else
+ iState = (nState & ControlState::ENABLED) ? MC_BULLETNORMAL : MC_BULLETDISABLED;
+ return ImplDrawTheme( hTheme, hDC, MENU_POPUPCHECK, iState, rc, aCaption );
+ }
+ else
+ return true; // unchecked: do nothing
+ }
+ else if( nPart == ControlPart::Separator )
+ {
+ // adjust for gutter position
+ if( AllSettings::GetLayoutRTL() )
+ rc.right -= aValue.getNumericVal()+1;
+ else
+ rc.left += aValue.getNumericVal()+1;
+ tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC,
+ MENU_POPUPSEPARATOR, 0, tools::Rectangle( rc.left, rc.top, rc.right, rc.bottom ) ) );
+ // center the separator inside the passed rectangle
+ long nDY = ((rc.bottom - rc.top + 1) - aRect.GetHeight()) / 2;
+ rc.top += nDY;
+ rc.bottom = rc.top+aRect.GetHeight()-1;
+ return ImplDrawTheme( hTheme, hDC, MENU_POPUPSEPARATOR, 0, rc, aCaption );
+ }
+ }
+ }
+
+ return false;
+}
+
+bool WinSalGraphics::drawNativeControl( ControlType nType,
+ ControlPart nPart,
+ const tools::Rectangle& rControlRegion,
+ ControlState nState,
+ const ImplControlValue& aValue,
+ const OUString& aCaption,
+ const Color& /*rBackgroundColor*/ )
+{
+ bool bOk = false;
+ HTHEME hTheme = nullptr;
+
+ tools::Rectangle buttonRect = rControlRegion;
+ tools::Rectangle cacheRect = rControlRegion;
+ Size keySize = cacheRect.GetSize();
+
+ WinSalGraphicsImplBase* pImpl = dynamic_cast<WinSalGraphicsImplBase*>(mpImpl.get());
+ if( !pImpl->UseRenderNativeControl())
+ pImpl = nullptr;
+
+ // tdf#95618 - A few controls render outside the region they're given.
+ if (pImpl && nType == ControlType::TabItem)
+ {
+ tools::Rectangle rNativeBoundingRegion;
+ tools::Rectangle rNativeContentRegion;
+ if (getNativeControlRegion(nType, nPart, rControlRegion, nState, aValue, aCaption,
+ rNativeBoundingRegion, rNativeContentRegion))
+ {
+ cacheRect = rNativeBoundingRegion;
+ keySize = rNativeBoundingRegion.GetSize();
+ }
+ }
+
+ if (pImpl && nType == ControlType::MenuPopup && (nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark))
+ {
+ tools::Rectangle aRectangle = GetMenuPopupMarkRegion(aValue);
+ if (!aRectangle.IsEmpty())
+ {
+ cacheRect = GetMenuPopupMarkRegion(aValue);
+ buttonRect = cacheRect;
+ keySize = cacheRect.GetSize();
+ }
+ }
+
+
+ ControlCacheKey aControlCacheKey(nType, nPart, nState, keySize);
+ if (pImpl != nullptr && pImpl->TryRenderCachedNativeControl(aControlCacheKey, buttonRect.Left(), buttonRect.Top()))
+ {
+ return true;
+ }
+
+ switch( nType )
+ {
+ case ControlType::Pushbutton:
+ case ControlType::Radiobutton:
+ case ControlType::Checkbox:
+ hTheme = getThemeHandle( mhWnd, L"Button");
+ break;
+ case ControlType::Scrollbar:
+ hTheme = getThemeHandle( mhWnd, L"Scrollbar");
+ break;
+ case ControlType::Combobox:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ else if( nPart == ControlPart::ButtonDown )
+ hTheme = getThemeHandle( mhWnd, L"Combobox");
+ break;
+ case ControlType::Spinbox:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ else
+ hTheme = getThemeHandle( mhWnd, L"Spin");
+ break;
+ case ControlType::SpinButtons:
+ hTheme = getThemeHandle( mhWnd, L"Spin");
+ break;
+ case ControlType::Editbox:
+ case ControlType::MultilineEditbox:
+ hTheme = getThemeHandle( mhWnd, L"Edit");
+ break;
+ case ControlType::Listbox:
+ if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow )
+ hTheme = getThemeHandle( mhWnd, L"Listview");
+ else if( nPart == ControlPart::ButtonDown )
+ hTheme = getThemeHandle( mhWnd, L"Combobox");
+ break;
+ case ControlType::TabPane:
+ case ControlType::TabBody:
+ case ControlType::TabItem:
+ hTheme = getThemeHandle( mhWnd, L"Tab");
+ break;
+ case ControlType::Toolbar:
+ if( nPart == ControlPart::Entire || nPart == ControlPart::Button )
+ hTheme = getThemeHandle( mhWnd, L"Toolbar");
+ else
+ // use rebar for grip and background
+ hTheme = getThemeHandle( mhWnd, L"Rebar");
+ break;
+ case ControlType::Menubar:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Rebar");
+ else if( GetSalData()->mbThemeMenuSupport )
+ {
+ if( nPart == ControlPart::MenuItem )
+ hTheme = getThemeHandle( mhWnd, L"Menu" );
+ }
+ break;
+ case ControlType::Progress:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"Progress");
+ break;
+ case ControlType::ListNode:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle( mhWnd, L"TreeView");
+ break;
+ case ControlType::Slider:
+ if( nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea )
+ hTheme = getThemeHandle( mhWnd, L"Trackbar" );
+ break;
+ case ControlType::MenuPopup:
+ if( GetSalData()->mbThemeMenuSupport )
+ {
+ if( nPart == ControlPart::Entire || nPart == ControlPart::MenuItem ||
+ nPart == ControlPart::MenuItemCheckMark || nPart == ControlPart::MenuItemRadioMark ||
+ nPart == ControlPart::Separator
+ )
+ hTheme = getThemeHandle( mhWnd, L"Menu" );
+ }
+ break;
+ default:
+ hTheme = nullptr;
+ break;
+ }
+
+ if( !hTheme )
+ return false;
+
+ RECT rc;
+ rc.left = buttonRect.Left();
+ rc.right = buttonRect.Right()+1;
+ rc.top = buttonRect.Top();
+ rc.bottom = buttonRect.Bottom()+1;
+
+ OUString aCaptionStr(aCaption.replace('~', '&')); // translate mnemonics
+
+ if (pImpl == nullptr)
+ {
+ // set default text alignment
+ int ta = SetTextAlign(getHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);
+
+ bOk = ImplDrawNativeControl(getHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr);
+
+ // restore alignment
+ SetTextAlign(getHDC(), ta);
+ }
+ else
+ {
+ // We can do OpenGL/Skia
+ std::unique_ptr<CompatibleDC> aBlackDC(CompatibleDC::create(*this, cacheRect.Left(), cacheRect.Top(), cacheRect.GetWidth()+1, cacheRect.GetHeight()+1));
+ SetTextAlign(aBlackDC->getCompatibleHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);
+ aBlackDC->fill(RGB(0, 0, 0));
+
+ std::unique_ptr<CompatibleDC> aWhiteDC(CompatibleDC::create(*this, cacheRect.Left(), cacheRect.Top(), cacheRect.GetWidth()+1, cacheRect.GetHeight()+1));
+ SetTextAlign(aWhiteDC->getCompatibleHDC(), TA_LEFT|TA_TOP|TA_NOUPDATECP);
+ aWhiteDC->fill(RGB(0xff, 0xff, 0xff));
+
+ if (ImplDrawNativeControl(aBlackDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr) &&
+ ImplDrawNativeControl(aWhiteDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr))
+ {
+ bOk = pImpl->RenderAndCacheNativeControl(*aWhiteDC, *aBlackDC, cacheRect.Left(), cacheRect.Top(), aControlCacheKey);
+ }
+ }
+
+ return bOk;
+}
+
+bool WinSalGraphics::getNativeControlRegion( ControlType nType,
+ ControlPart nPart,
+ const tools::Rectangle& rControlRegion,
+ ControlState nState,
+ const ImplControlValue& rControlValue,
+ const OUString&,
+ tools::Rectangle &rNativeBoundingRegion,
+ tools::Rectangle &rNativeContentRegion )
+{
+ bool bRet = false;
+
+ // FIXME: rNativeBoundingRegion has a different origin
+ // depending on which part is used; horrors.
+
+ HDC hDC = GetDC( mhWnd );
+ if( nType == ControlType::Toolbar )
+ {
+ if( nPart == ControlPart::ThumbHorz || nPart == ControlPart::ThumbVert )
+ {
+ /*
+ // the vertical gripper is not supported in most themes and it makes no
+ // sense to only support horizontal gripper
+
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Rebar");
+ if( hTheme )
+ {
+ tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, nPart == ControlPart::ThumbHorz ? RP_GRIPPERVERT : RP_GRIPPER,
+ 0, rControlRegion.GetBoundRect() ) );
+ if( nPart == ControlPart::ThumbHorz && !aRect.IsEmpty() )
+ {
+ tools::Rectangle aVertRect( 0, 0, aRect.getHeight(), aRect.getWidth() );
+ rNativeContentRegion = aVertRect;
+ }
+ else
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ if( !rNativeContentRegion.IsEmpty() )
+ bRet = TRUE;
+ }
+ */
+ }
+ if( nPart == ControlPart::Button )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Toolbar");
+ if( hTheme )
+ {
+ tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, TP_SPLITBUTTONDROPDOWN,
+ TS_HOT, rControlRegion ) );
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ if( !rNativeContentRegion.IsEmpty() )
+ bRet = true;
+ }
+ }
+ }
+ if( nType == ControlType::Progress && nPart == ControlPart::Entire )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Progress");
+ if( hTheme )
+ {
+ tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, PP_BAR,
+ 0, rControlRegion ) );
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ if( !rNativeContentRegion.IsEmpty() )
+ bRet = true;
+ }
+ }
+ if( (nType == ControlType::Listbox || nType == ControlType::Combobox ) && nPart == ControlPart::Entire )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Combobox");
+ if( hTheme )
+ {
+ tools::Rectangle aBoxRect( rControlRegion );
+ tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, CP_DROPDOWNBUTTON,
+ CBXS_NORMAL, aBoxRect ) );
+ if( aRect.GetHeight() > aBoxRect.GetHeight() )
+ aBoxRect.SetBottom( aBoxRect.Top() + aRect.GetHeight() );
+ if( aRect.GetWidth() > aBoxRect.GetWidth() )
+ aBoxRect.SetRight( aBoxRect.Left() + aRect.GetWidth() );
+ rNativeContentRegion = aBoxRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ if( !aRect.IsEmpty() )
+ bRet = true;
+ }
+ }
+
+ if( (nType == ControlType::Editbox || nType == ControlType::Spinbox) && nPart == ControlPart::Entire )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Edit");
+ if( hTheme )
+ {
+ // get border size
+ tools::Rectangle aBoxRect( rControlRegion );
+ tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC, EP_BACKGROUNDWITHBORDER,
+ EBWBS_HOT, aBoxRect ) );
+ // ad app font height
+ NONCLIENTMETRICSW aNonClientMetrics;
+ aNonClientMetrics.cbSize = sizeof( aNonClientMetrics );
+ if ( SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, sizeof( aNonClientMetrics ), &aNonClientMetrics, 0 ) )
+ {
+ long nFontHeight = aNonClientMetrics.lfMessageFont.lfHeight;
+ if( nFontHeight < 0 )
+ nFontHeight = -nFontHeight;
+
+ if( aRect.GetHeight() && nFontHeight )
+ {
+ aRect.AdjustBottom(aRect.GetHeight());
+ aRect.AdjustBottom(nFontHeight);
+ if( aRect.GetHeight() > aBoxRect.GetHeight() )
+ aBoxRect.SetBottom( aBoxRect.Top() + aRect.GetHeight() );
+ if( aRect.GetWidth() > aBoxRect.GetWidth() )
+ aBoxRect.SetRight( aBoxRect.Left() + aRect.GetWidth() );
+ rNativeContentRegion = aBoxRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ bRet = true;
+ }
+ }
+ }
+ }
+
+ if( GetSalData()->mbThemeMenuSupport )
+ {
+ if( nType == ControlType::MenuPopup )
+ {
+ if( nPart == ControlPart::MenuItemCheckMark ||
+ nPart == ControlPart::MenuItemRadioMark )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Menu");
+ tools::Rectangle aBoxRect( rControlRegion );
+ tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC,
+ MENU_POPUPCHECK,
+ MC_CHECKMARKNORMAL,
+ aBoxRect ) );
+ if( aBoxRect.GetWidth() && aBoxRect.GetHeight() )
+ {
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ bRet = true;
+ }
+ }
+ }
+ }
+
+ if( nType == ControlType::Slider && ( (nPart == ControlPart::ThumbHorz) || (nPart == ControlPart::ThumbVert) ) )
+ {
+ HTHEME hTheme = getThemeHandle( mhWnd, L"Trackbar");
+ if( hTheme )
+ {
+ int iPart = (nPart == ControlPart::ThumbHorz) ? TKP_THUMB : TKP_THUMBVERT;
+ int iState = (nPart == ControlPart::ThumbHorz) ? static_cast<int>(TUS_NORMAL) : static_cast<int>(TUVS_NORMAL);
+ tools::Rectangle aThumbRect = ImplGetThemeRect( hTheme, hDC, iPart, iState, tools::Rectangle() );
+ if( nPart == ControlPart::ThumbHorz )
+ {
+ long nW = aThumbRect.GetWidth();
+ tools::Rectangle aRect( rControlRegion );
+ aRect.SetRight( aRect.Left() + nW - 1 );
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ }
+ else
+ {
+ long nH = aThumbRect.GetHeight();
+ tools::Rectangle aRect( rControlRegion );
+ aRect.SetBottom( aRect.Top() + nH - 1 );
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ }
+ bRet = true;
+ }
+ }
+
+ if ( ( nType == ControlType::TabItem ) && ( nPart == ControlPart::Entire ) )
+ {
+ tools::Rectangle aControlRect( rControlRegion );
+ rNativeContentRegion = aControlRect;
+
+ aControlRect.AdjustBottom(-1);
+
+ if( rControlValue.getType() == ControlType::TabItem )
+ {
+ const TabitemValue& rValue = static_cast<const TabitemValue&>(rControlValue);
+ if (rValue.isBothAligned())
+ aControlRect.AdjustRight(-1);
+
+ if ( nState & ControlState::SELECTED )
+ {
+ aControlRect.AdjustLeft(-2);
+ if (!rValue.isBothAligned())
+ {
+ if (rValue.isLeftAligned() || rValue.isNotAligned())
+ aControlRect.AdjustRight(2);
+ if (rValue.isRightAligned())
+ aControlRect.AdjustRight(1);
+ }
+ aControlRect.AdjustTop(-2);
+ aControlRect.AdjustBottom(2);
+ }
+ }
+ rNativeBoundingRegion = aControlRect;
+ bRet = true;
+ }
+
+ ReleaseDC( mhWnd, hDC );
+ return bRet;
+}
+
+void WinSalGraphics::updateSettingsNative( AllSettings& rSettings )
+{
+ if ( !vsAPI.IsThemeActive() )
+ return;
+
+ StyleSettings aStyleSettings = rSettings.GetStyleSettings();
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // don't draw frame around each and every toolbar
+ pSVData->maNWFData.mbDockingAreaAvoidTBFrames = true;
+
+ // FIXME get the color directly from the theme, not from the settings
+ Color aMenuBarTextColor = aStyleSettings.GetPersonaMenuBarTextColor().value_or( aStyleSettings.GetMenuTextColor() );
+ // in aero menuitem highlight text is drawn in the same color as normal
+ aStyleSettings.SetMenuHighlightTextColor( aStyleSettings.GetMenuTextColor() );
+ aStyleSettings.SetMenuBarRolloverTextColor( aMenuBarTextColor );
+ aStyleSettings.SetMenuBarHighlightTextColor( aMenuBarTextColor );
+ pSVData->maNWFData.mnMenuFormatBorderX = 2;
+ pSVData->maNWFData.mnMenuFormatBorderY = 2;
+ pSVData->maNWFData.maMenuBarHighlightTextColor = aMenuBarTextColor;
+ GetSalData()->mbThemeMenuSupport = true;
+
+ rSettings.SetStyleSettings( aStyleSettings );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/salprn.cxx b/vcl/win/gdi/salprn.cxx
new file mode 100644
index 000000000..98886c72a
--- /dev/null
+++ b/vcl/win/gdi/salprn.cxx
@@ -0,0 +1,1629 @@
+/* -*- 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 <sal/config.h>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <memory>
+#include <string.h>
+
+#include <svsys.h>
+
+#include <osl/module.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <tools/urlobj.hxx>
+
+#include <vcl/weld.hxx>
+
+#include <win/wincomp.hxx>
+#include <win/saldata.hxx>
+#include <win/salinst.h>
+#include <win/salgdi.h>
+#include <win/salframe.h>
+#include <win/salprn.h>
+
+#include <salptype.hxx>
+#include <print.h>
+#include <jobset.h>
+
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/ui/dialogs/FilePicker.hpp>
+#include <com/sun/star/ui/dialogs/XFilterManager.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <comphelper/processfactory.hxx>
+
+#include <vcl/threadex.hxx>
+
+#include <malloc.h>
+
+#include <winspool.h>
+#if defined GetDefaultPrinter
+# undef GetDefaultPrinter
+#endif
+#if defined SetPrinterData
+# undef SetPrinterData
+#endif
+
+#define CATCH_DRIVER_EX_BEGIN \
+ __try \
+ {
+#define CATCH_DRIVER_EX_END(mes, p) \
+ } \
+ __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))\
+ { \
+ OSL_FAIL( mes ); \
+ p->markInvalid(); \
+ }
+#define CATCH_DRIVER_EX_END_2(mes) \
+ } \
+ __except(WinSalInstance::WorkaroundExceptionHandlingInUSER32Lib(GetExceptionCode(), GetExceptionInformation()))\
+ { \
+ OSL_FAIL( mes ); \
+ }
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::ui::dialogs;
+
+static const wchar_t aImplWindows[] = L"windows";
+static const wchar_t aImplDevice[] = L"device";
+
+static DEVMODEW const * SAL_DEVMODE_W( const ImplJobSetup* pSetupData )
+{
+ DEVMODEW const * pRet = nullptr;
+ SalDriverData const * pDrv = reinterpret_cast<SalDriverData const *>(pSetupData->GetDriverData());
+ if( pSetupData->GetDriverDataLen() >= sizeof(DEVMODEW)+sizeof(SalDriverData)-1 )
+ pRet = reinterpret_cast<DEVMODEW const *>((pSetupData->GetDriverData()) + (pDrv->mnDriverOffset));
+ return pRet;
+}
+
+static PrintQueueFlags ImplWinQueueStatusToSal( DWORD nWinStatus )
+{
+ PrintQueueFlags nStatus = PrintQueueFlags::NONE;
+ if ( nWinStatus & PRINTER_STATUS_PAUSED )
+ nStatus |= PrintQueueFlags::Paused;
+ if ( nWinStatus & PRINTER_STATUS_ERROR )
+ nStatus |= PrintQueueFlags::Error;
+ if ( nWinStatus & PRINTER_STATUS_PENDING_DELETION )
+ nStatus |= PrintQueueFlags::PendingDeletion;
+ if ( nWinStatus & PRINTER_STATUS_PAPER_JAM )
+ nStatus |= PrintQueueFlags::PaperJam;
+ if ( nWinStatus & PRINTER_STATUS_PAPER_OUT )
+ nStatus |= PrintQueueFlags::PaperOut;
+ if ( nWinStatus & PRINTER_STATUS_MANUAL_FEED )
+ nStatus |= PrintQueueFlags::ManualFeed;
+ if ( nWinStatus & PRINTER_STATUS_PAPER_PROBLEM )
+ nStatus |= PrintQueueFlags::PaperProblem;
+ if ( nWinStatus & PRINTER_STATUS_OFFLINE )
+ nStatus |= PrintQueueFlags::Offline;
+ if ( nWinStatus & PRINTER_STATUS_IO_ACTIVE )
+ nStatus |= PrintQueueFlags::IOActive;
+ if ( nWinStatus & PRINTER_STATUS_BUSY )
+ nStatus |= PrintQueueFlags::Busy;
+ if ( nWinStatus & PRINTER_STATUS_PRINTING )
+ nStatus |= PrintQueueFlags::Printing;
+ if ( nWinStatus & PRINTER_STATUS_OUTPUT_BIN_FULL )
+ nStatus |= PrintQueueFlags::OutputBinFull;
+ if ( nWinStatus & PRINTER_STATUS_WAITING )
+ nStatus |= PrintQueueFlags::Waiting;
+ if ( nWinStatus & PRINTER_STATUS_PROCESSING )
+ nStatus |= PrintQueueFlags::Processing;
+ if ( nWinStatus & PRINTER_STATUS_INITIALIZING )
+ nStatus |= PrintQueueFlags::Initializing;
+ if ( nWinStatus & PRINTER_STATUS_WARMING_UP )
+ nStatus |= PrintQueueFlags::WarmingUp;
+ if ( nWinStatus & PRINTER_STATUS_TONER_LOW )
+ nStatus |= PrintQueueFlags::TonerLow;
+ if ( nWinStatus & PRINTER_STATUS_NO_TONER )
+ nStatus |= PrintQueueFlags::NoToner;
+ if ( nWinStatus & PRINTER_STATUS_PAGE_PUNT )
+ nStatus |= PrintQueueFlags::PagePunt;
+ if ( nWinStatus & PRINTER_STATUS_USER_INTERVENTION )
+ nStatus |= PrintQueueFlags::UserIntervention;
+ if ( nWinStatus & PRINTER_STATUS_OUT_OF_MEMORY )
+ nStatus |= PrintQueueFlags::OutOfMemory;
+ if ( nWinStatus & PRINTER_STATUS_DOOR_OPEN )
+ nStatus |= PrintQueueFlags::DoorOpen;
+ if ( nWinStatus & PRINTER_STATUS_SERVER_UNKNOWN )
+ nStatus |= PrintQueueFlags::StatusUnknown;
+ if ( nWinStatus & PRINTER_STATUS_POWER_SAVE )
+ nStatus |= PrintQueueFlags::PowerSave;
+ if ( nStatus == PrintQueueFlags::NONE && !(nWinStatus & PRINTER_STATUS_NOT_AVAILABLE) )
+ nStatus |= PrintQueueFlags::Ready;
+ return nStatus;
+}
+
+
+void WinSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
+{
+ DWORD i;
+ DWORD nBytes = 0;
+ DWORD nInfoPrn4 = 0;
+ EnumPrintersW( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 4, nullptr, 0, &nBytes, &nInfoPrn4 );
+ if ( nBytes )
+ {
+ PRINTER_INFO_4W* pWinInfo4 = static_cast<PRINTER_INFO_4W*>(std::malloc( nBytes ));
+ if ( EnumPrintersW( PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS, nullptr, 4, reinterpret_cast<LPBYTE>(pWinInfo4), nBytes, &nBytes, &nInfoPrn4 ) )
+ {
+ for ( i = 0; i < nInfoPrn4; i++ )
+ {
+ std::unique_ptr<SalPrinterQueueInfo> pInfo(new SalPrinterQueueInfo);
+ pInfo->maPrinterName = o3tl::toU(pWinInfo4[i].pPrinterName);
+ pInfo->mnStatus = PrintQueueFlags::NONE;
+ pInfo->mnJobs = 0;
+ pList->Add( std::move(pInfo) );
+ }
+ }
+ std::free( pWinInfo4 );
+ }
+}
+
+void WinSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* pInfo )
+{
+ HANDLE hPrinter = nullptr;
+ LPWSTR pPrnName = const_cast<LPWSTR>(o3tl::toW(pInfo->maPrinterName.getStr()));
+ if( OpenPrinterW( pPrnName, &hPrinter, nullptr ) )
+ {
+ DWORD nBytes = 0;
+ GetPrinterW( hPrinter, 2, nullptr, 0, &nBytes );
+ if( nBytes )
+ {
+ PRINTER_INFO_2W* pWinInfo2 = static_cast<PRINTER_INFO_2W*>(std::malloc(nBytes));
+ if( GetPrinterW( hPrinter, 2, reinterpret_cast<LPBYTE>(pWinInfo2), nBytes, &nBytes ) )
+ {
+ if( pWinInfo2->pDriverName )
+ pInfo->maDriver = o3tl::toU(pWinInfo2->pDriverName);
+ OUString aPortName;
+ if ( pWinInfo2->pPortName )
+ aPortName = o3tl::toU(pWinInfo2->pPortName);
+ // pLocation can be 0 (the Windows docu doesn't describe this)
+ if ( pWinInfo2->pLocation && *pWinInfo2->pLocation )
+ pInfo->maLocation = o3tl::toU(pWinInfo2->pLocation);
+ else
+ pInfo->maLocation = aPortName;
+ // pComment can be 0 (the Windows docu doesn't describe this)
+ if ( pWinInfo2->pComment )
+ pInfo->maComment = o3tl::toU(pWinInfo2->pComment);
+ pInfo->mnStatus = ImplWinQueueStatusToSal( pWinInfo2->Status );
+ pInfo->mnJobs = pWinInfo2->cJobs;
+ if( ! pInfo->mpPortName )
+ pInfo->mpPortName.reset(new OUString(aPortName));
+ }
+ std::free(pWinInfo2);
+ }
+ ClosePrinter( hPrinter );
+ }
+}
+
+OUString WinSalInstance::GetDefaultPrinter()
+{
+ DWORD nChars = 0;
+ GetDefaultPrinterW( nullptr, &nChars );
+ if( nChars )
+ {
+ LPWSTR pStr = static_cast<LPWSTR>(std::malloc(nChars*sizeof(WCHAR)));
+ OUString aDefPrt;
+ if( GetDefaultPrinterW( pStr, &nChars ) )
+ {
+ aDefPrt = o3tl::toU(pStr);
+ }
+ std::free( pStr );
+ if( !aDefPrt.isEmpty() )
+ return aDefPrt;
+ }
+
+ // get default printer from win.ini
+ wchar_t szBuffer[256];
+ GetProfileStringW( aImplWindows, aImplDevice, L"", szBuffer, SAL_N_ELEMENTS( szBuffer ) );
+ if ( szBuffer[0] )
+ {
+ // search for printer name
+ wchar_t* pBuf = szBuffer;
+ wchar_t* pTmp = pBuf;
+ while ( *pTmp && (*pTmp != ',') )
+ pTmp++;
+ return OUString( o3tl::toU(pBuf), static_cast<sal_Int32>(pTmp-pBuf) );
+ }
+ else
+ return OUString();
+}
+
+static DWORD ImplDeviceCaps( WinSalInfoPrinter const * pPrinter, WORD nCaps,
+ BYTE* pOutput, const ImplJobSetup* pSetupData )
+{
+ DEVMODEW const * pDevMode;
+ if ( !pSetupData || !pSetupData->GetDriverData() )
+ pDevMode = nullptr;
+ else
+ pDevMode = SAL_DEVMODE_W( pSetupData );
+
+ return DeviceCapabilitiesW( o3tl::toW(pPrinter->maDeviceName.getStr()),
+ o3tl::toW(pPrinter->maPortName.getStr()),
+ nCaps, reinterpret_cast<LPWSTR>(pOutput), pDevMode );
+}
+
+static bool ImplTestSalJobSetup( WinSalInfoPrinter const * pPrinter,
+ ImplJobSetup* pSetupData, bool bDelete )
+{
+ if ( pSetupData && pSetupData->GetDriverData() )
+ {
+ // signature and size must fit to avoid using
+ // JobSetups from a wrong system
+
+ // initialize versions from jobsetup
+ // those will be overwritten with driver's version
+ DEVMODEW const * pDevModeW = nullptr;
+ LONG dmSpecVersion = -1;
+ LONG dmDriverVersion = -1;
+ SalDriverData const * pSalDriverData = reinterpret_cast<SalDriverData const *>(pSetupData->GetDriverData());
+ BYTE const * pDriverData = reinterpret_cast<BYTE const *>(pSalDriverData) + pSalDriverData->mnDriverOffset;
+ pDevModeW = reinterpret_cast<DEVMODEW const *>(pDriverData);
+
+ long nSysJobSize = -1;
+ if( pPrinter && pDevModeW )
+ {
+ // just too many driver crashes in that area -> check the dmSpecVersion and dmDriverVersion fields always !!!
+ // this prevents using the jobsetup between different Windows versions (eg from XP to 9x) but we
+ // can avoid potential driver crashes as their jobsetups are often not compatible
+ // #110800#, #111151#, #112381#, #i16580#, #i14173# and perhaps #112375#
+ HANDLE hPrn;
+ LPWSTR pPrinterNameW = const_cast<LPWSTR>(o3tl::toW(pPrinter->maDeviceName.getStr()));
+ if ( !OpenPrinterW( pPrinterNameW, &hPrn, nullptr ) )
+ return false;
+
+ // #131642# hPrn==HGDI_ERROR even though OpenPrinter() succeeded!
+ if( hPrn == HGDI_ERROR )
+ return false;
+
+ nSysJobSize = DocumentPropertiesW( nullptr, hPrn,
+ pPrinterNameW,
+ nullptr, nullptr, 0 );
+
+ if( nSysJobSize < 0 )
+ {
+ ClosePrinter( hPrn );
+ return false;
+ }
+ DEVMODEW *pBuffer = static_cast<DEVMODEW*>(_alloca( nSysJobSize ));
+ LONG nRet = DocumentPropertiesW( nullptr, hPrn,
+ pPrinterNameW,
+ pBuffer, nullptr, DM_OUT_BUFFER );
+ if( nRet < 0 )
+ {
+ ClosePrinter( hPrn );
+ return false;
+ }
+
+ // the spec version differs between the windows platforms, ie 98,NT,2000/XP
+ // this allows us to throw away printer settings from other platforms that might crash a buggy driver
+ // we check the driver version as well
+ dmSpecVersion = pBuffer->dmSpecVersion;
+ dmDriverVersion = pBuffer->dmDriverVersion;
+
+ ClosePrinter( hPrn );
+ }
+ SalDriverData const * pSetupDriverData = reinterpret_cast<SalDriverData const *>(pSetupData->GetDriverData());
+ if ( (pSetupData->GetSystem() == JOBSETUP_SYSTEM_WINDOWS) &&
+ (pPrinter->maDriverName == pSetupData->GetDriver()) &&
+ (pSetupData->GetDriverDataLen() > sizeof( SalDriverData )) &&
+ static_cast<long>(pSetupData->GetDriverDataLen() - pSetupDriverData->mnDriverOffset) == nSysJobSize &&
+ pSetupDriverData->mnSysSignature == SAL_DRIVERDATA_SYSSIGN )
+ {
+ if( pDevModeW &&
+ (dmSpecVersion == pDevModeW->dmSpecVersion) &&
+ (dmDriverVersion == pDevModeW->dmDriverVersion) )
+ return true;
+ }
+ if ( bDelete )
+ {
+ std::free( const_cast<sal_uInt8*>(pSetupData->GetDriverData()) );
+ pSetupData->SetDriverData( nullptr );
+ pSetupData->SetDriverDataLen( 0 );
+ }
+ }
+
+ return false;
+}
+
+static bool ImplUpdateSalJobSetup( WinSalInfoPrinter const * pPrinter, ImplJobSetup* pSetupData,
+ bool bIn, weld::Window* pVisibleDlgParent )
+{
+ HANDLE hPrn;
+ LPWSTR pPrinterNameW = const_cast<LPWSTR>(o3tl::toW(pPrinter->maDeviceName.getStr()));
+ if ( !OpenPrinterW( pPrinterNameW, &hPrn, nullptr ) )
+ return false;
+ // #131642# hPrn==HGDI_ERROR even though OpenPrinter() succeeded!
+ if( hPrn == HGDI_ERROR )
+ return false;
+
+ LONG nRet;
+ HWND hWnd = nullptr;
+ DWORD nMode = DM_OUT_BUFFER;
+ SalDriverData* pOutBuffer = nullptr;
+ BYTE const * pInBuffer = nullptr;
+
+ LONG nSysJobSize = DocumentPropertiesW( hWnd, hPrn,
+ pPrinterNameW,
+ nullptr, nullptr, 0 );
+ if ( nSysJobSize < 0 )
+ {
+ ClosePrinter( hPrn );
+ return false;
+ }
+
+ // make Outputbuffer
+ const std::size_t nDriverDataLen = sizeof(SalDriverData) + nSysJobSize-1;
+ pOutBuffer = static_cast<SalDriverData*>(rtl_allocateZeroMemory( nDriverDataLen ));
+ pOutBuffer->mnSysSignature = SAL_DRIVERDATA_SYSSIGN;
+ // calculate driver data offset including structure padding
+ pOutBuffer->mnDriverOffset = sal::static_int_cast<sal_uInt16>(
+ reinterpret_cast<char*>(pOutBuffer->maDriverData) -
+ reinterpret_cast<char*>(pOutBuffer) );
+
+ // check if we have a suitable input buffer
+ if ( bIn && ImplTestSalJobSetup( pPrinter, pSetupData, false ) )
+ {
+ pInBuffer = pSetupData->GetDriverData() + reinterpret_cast<SalDriverData const *>(pSetupData->GetDriverData())->mnDriverOffset;
+ nMode |= DM_IN_BUFFER;
+ }
+
+ // check if the dialog should be shown
+ if ( pVisibleDlgParent )
+ {
+ hWnd = pVisibleDlgParent->get_system_data().hWnd;
+ nMode |= DM_IN_PROMPT;
+ }
+
+ // Release mutex, in the other case we don't get paints and so on
+ sal_uInt32 nMutexCount = 0;
+ WinSalInstance* pInst = GetSalData()->mpInstance;
+ if ( pInst && pVisibleDlgParent )
+ nMutexCount = pInst->ReleaseYieldMutexAll();
+
+ BYTE* pOutDevMode = reinterpret_cast<BYTE*>(pOutBuffer) + pOutBuffer->mnDriverOffset;
+ nRet = DocumentPropertiesW( hWnd, hPrn,
+ pPrinterNameW,
+ reinterpret_cast<LPDEVMODEW>(pOutDevMode), reinterpret_cast<LPDEVMODEW>(const_cast<BYTE *>(pInBuffer)), nMode );
+ if ( pInst && pVisibleDlgParent )
+ pInst->AcquireYieldMutex( nMutexCount );
+ ClosePrinter( hPrn );
+
+ if( (nRet < 0) || (pVisibleDlgParent && (nRet == IDCANCEL)) )
+ {
+ std::free( pOutBuffer );
+ return false;
+ }
+
+ // fill up string buffers with 0 so they do not influence a JobSetup's memcmp
+ if( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmSize >= 64 )
+ {
+ sal_Int32 nLen = rtl_ustr_getLength( o3tl::toU(reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmDeviceName) );
+ if ( sal::static_int_cast<size_t>(nLen) < SAL_N_ELEMENTS( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmDeviceName ) )
+ memset( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmDeviceName+nLen, 0, sizeof( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmDeviceName )-(nLen*sizeof(sal_Unicode)) );
+ }
+ if( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmSize >= 166 )
+ {
+ sal_Int32 nLen = rtl_ustr_getLength( o3tl::toU(reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmFormName) );
+ if ( sal::static_int_cast<size_t>(nLen) < SAL_N_ELEMENTS( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmFormName ) )
+ memset( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmFormName+nLen, 0, sizeof( reinterpret_cast<LPDEVMODEW>(pOutDevMode)->dmFormName )-(nLen*sizeof(sal_Unicode)) );
+ }
+
+ // update data
+ if ( pSetupData->GetDriverData() )
+ std::free( const_cast<sal_uInt8*>(pSetupData->GetDriverData()) );
+ pSetupData->SetDriverDataLen( nDriverDataLen );
+ pSetupData->SetDriverData(reinterpret_cast<BYTE*>(pOutBuffer));
+ pSetupData->SetSystem( JOBSETUP_SYSTEM_WINDOWS );
+
+ return true;
+}
+
+static void ImplDevModeToJobSetup( WinSalInfoPrinter const * pPrinter, ImplJobSetup* pSetupData, JobSetFlags nFlags )
+{
+ if ( !pSetupData || !pSetupData->GetDriverData() )
+ return;
+
+ DEVMODEW const * pDevModeW = SAL_DEVMODE_W(pSetupData);
+ if( pDevModeW == nullptr )
+ return;
+
+ // Orientation
+ if ( nFlags & JobSetFlags::ORIENTATION )
+ {
+ if ( pDevModeW->dmOrientation == DMORIENT_PORTRAIT )
+ pSetupData->SetOrientation( Orientation::Portrait );
+ else if ( pDevModeW->dmOrientation == DMORIENT_LANDSCAPE )
+ pSetupData->SetOrientation( Orientation::Landscape );
+ }
+
+ // PaperBin
+ if ( nFlags & JobSetFlags::PAPERBIN )
+ {
+ const DWORD nCount = ImplDeviceCaps( pPrinter, DC_BINS, nullptr, pSetupData );
+
+ if ( nCount && (nCount != GDI_ERROR) )
+ {
+ WORD* pBins = static_cast<WORD*>(rtl_allocateZeroMemory( nCount*sizeof(WORD) ));
+ ImplDeviceCaps( pPrinter, DC_BINS, reinterpret_cast<BYTE*>(pBins), pSetupData );
+ pSetupData->SetPaperBin( 0 );
+
+ // search the right bin and assign index to mnPaperBin
+ for( DWORD i = 0; i < nCount; ++i )
+ {
+ if( pDevModeW->dmDefaultSource == pBins[ i ] )
+ {
+ pSetupData->SetPaperBin( static_cast<sal_uInt16>(i) );
+ break;
+ }
+ }
+
+ std::free( pBins );
+ }
+ }
+
+ // PaperSize
+ if ( nFlags & JobSetFlags::PAPERSIZE )
+ {
+ if( (pDevModeW->dmFields & (DM_PAPERWIDTH|DM_PAPERLENGTH)) == (DM_PAPERWIDTH|DM_PAPERLENGTH) )
+ {
+ pSetupData->SetPaperWidth( pDevModeW->dmPaperWidth*10 );
+ pSetupData->SetPaperHeight( pDevModeW->dmPaperLength*10 );
+ }
+ else
+ {
+ const DWORD nPaperCount = ImplDeviceCaps( pPrinter, DC_PAPERS, nullptr, pSetupData );
+ WORD* pPapers = nullptr;
+ const DWORD nPaperSizeCount = ImplDeviceCaps( pPrinter, DC_PAPERSIZE, nullptr, pSetupData );
+ POINT* pPaperSizes = nullptr;
+ if ( nPaperCount && (nPaperCount != GDI_ERROR) )
+ {
+ pPapers = static_cast<WORD*>(rtl_allocateZeroMemory(nPaperCount*sizeof(WORD)));
+ ImplDeviceCaps( pPrinter, DC_PAPERS, reinterpret_cast<BYTE*>(pPapers), pSetupData );
+ }
+ if ( nPaperSizeCount && (nPaperSizeCount != GDI_ERROR) )
+ {
+ pPaperSizes = static_cast<POINT*>(rtl_allocateZeroMemory(nPaperSizeCount*sizeof(POINT)));
+ ImplDeviceCaps( pPrinter, DC_PAPERSIZE, reinterpret_cast<BYTE*>(pPaperSizes), pSetupData );
+ }
+ if( nPaperSizeCount == nPaperCount && pPaperSizes && pPapers )
+ {
+ for( DWORD i = 0; i < nPaperCount; ++i )
+ {
+ if( pPapers[ i ] == pDevModeW->dmPaperSize )
+ {
+ pSetupData->SetPaperWidth( pPaperSizes[ i ].x*10 );
+ pSetupData->SetPaperHeight( pPaperSizes[ i ].y*10 );
+ break;
+ }
+ }
+ }
+ if( pPapers )
+ std::free( pPapers );
+ if( pPaperSizes )
+ std::free( pPaperSizes );
+ }
+ switch( pDevModeW->dmPaperSize )
+ {
+ case DMPAPER_LETTER:
+ pSetupData->SetPaperFormat( PAPER_LETTER );
+ break;
+ case DMPAPER_TABLOID:
+ pSetupData->SetPaperFormat( PAPER_TABLOID );
+ break;
+ case DMPAPER_LEDGER:
+ pSetupData->SetPaperFormat( PAPER_LEDGER );
+ break;
+ case DMPAPER_LEGAL:
+ pSetupData->SetPaperFormat( PAPER_LEGAL );
+ break;
+ case DMPAPER_STATEMENT:
+ pSetupData->SetPaperFormat( PAPER_STATEMENT );
+ break;
+ case DMPAPER_EXECUTIVE:
+ pSetupData->SetPaperFormat( PAPER_EXECUTIVE );
+ break;
+ case DMPAPER_A3:
+ pSetupData->SetPaperFormat( PAPER_A3 );
+ break;
+ case DMPAPER_A4:
+ pSetupData->SetPaperFormat( PAPER_A4 );
+ break;
+ case DMPAPER_A5:
+ pSetupData->SetPaperFormat( PAPER_A5 );
+ break;
+ //See http://wiki.openoffice.org/wiki/DefaultPaperSize
+ //i.e.
+ //http://msdn.microsoft.com/en-us/library/dd319099(VS.85).aspx
+ //DMPAPER_B4 12 B4 (JIS) 257 x 364 mm
+ //http://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf
+ //also says that the MS DMPAPER_B4 is JIS, which makes most sense. And
+ //matches our Excel filter's belief about the matching XlPaperSize
+ //enumeration.
+
+ //http://msdn.microsoft.com/en-us/library/ms776398(VS.85).aspx said
+ ////"DMPAPER_B4 12 B4 (JIS) 250 x 354"
+ //which is bogus as it's either JIS 257 x 364 or ISO 250 x 353
+ //(cmc)
+ case DMPAPER_B4:
+ pSetupData->SetPaperFormat( PAPER_B4_JIS );
+ break;
+ case DMPAPER_B5:
+ pSetupData->SetPaperFormat( PAPER_B5_JIS );
+ break;
+ case DMPAPER_QUARTO:
+ pSetupData->SetPaperFormat( PAPER_QUARTO );
+ break;
+ case DMPAPER_10X14:
+ pSetupData->SetPaperFormat( PAPER_10x14 );
+ break;
+ case DMPAPER_NOTE:
+ pSetupData->SetPaperFormat( PAPER_LETTER );
+ break;
+ case DMPAPER_ENV_9:
+ pSetupData->SetPaperFormat( PAPER_ENV_9 );
+ break;
+ case DMPAPER_ENV_10:
+ pSetupData->SetPaperFormat( PAPER_ENV_10 );
+ break;
+ case DMPAPER_ENV_11:
+ pSetupData->SetPaperFormat( PAPER_ENV_11 );
+ break;
+ case DMPAPER_ENV_12:
+ pSetupData->SetPaperFormat( PAPER_ENV_12 );
+ break;
+ case DMPAPER_ENV_14:
+ pSetupData->SetPaperFormat( PAPER_ENV_14 );
+ break;
+ case DMPAPER_CSHEET:
+ pSetupData->SetPaperFormat( PAPER_C );
+ break;
+ case DMPAPER_DSHEET:
+ pSetupData->SetPaperFormat( PAPER_D );
+ break;
+ case DMPAPER_ESHEET:
+ pSetupData->SetPaperFormat( PAPER_E );
+ break;
+ case DMPAPER_ENV_DL:
+ pSetupData->SetPaperFormat( PAPER_ENV_DL );
+ break;
+ case DMPAPER_ENV_C5:
+ pSetupData->SetPaperFormat( PAPER_ENV_C5 );
+ break;
+ case DMPAPER_ENV_C3:
+ pSetupData->SetPaperFormat( PAPER_ENV_C3 );
+ break;
+ case DMPAPER_ENV_C4:
+ pSetupData->SetPaperFormat( PAPER_ENV_C4 );
+ break;
+ case DMPAPER_ENV_C6:
+ pSetupData->SetPaperFormat( PAPER_ENV_C6 );
+ break;
+ case DMPAPER_ENV_C65:
+ pSetupData->SetPaperFormat( PAPER_ENV_C65 );
+ break;
+ case DMPAPER_ENV_ITALY:
+ pSetupData->SetPaperFormat( PAPER_ENV_ITALY );
+ break;
+ case DMPAPER_ENV_MONARCH:
+ pSetupData->SetPaperFormat( PAPER_ENV_MONARCH );
+ break;
+ case DMPAPER_ENV_PERSONAL:
+ pSetupData->SetPaperFormat( PAPER_ENV_PERSONAL );
+ break;
+ case DMPAPER_FANFOLD_US:
+ pSetupData->SetPaperFormat( PAPER_FANFOLD_US );
+ break;
+ case DMPAPER_FANFOLD_STD_GERMAN:
+ pSetupData->SetPaperFormat( PAPER_FANFOLD_DE );
+ break;
+ case DMPAPER_FANFOLD_LGL_GERMAN:
+ pSetupData->SetPaperFormat( PAPER_FANFOLD_LEGAL_DE );
+ break;
+ case DMPAPER_ISO_B4:
+ pSetupData->SetPaperFormat( PAPER_B4_ISO );
+ break;
+ case DMPAPER_JAPANESE_POSTCARD:
+ pSetupData->SetPaperFormat( PAPER_POSTCARD_JP );
+ break;
+ case DMPAPER_9X11:
+ pSetupData->SetPaperFormat( PAPER_9x11 );
+ break;
+ case DMPAPER_10X11:
+ pSetupData->SetPaperFormat( PAPER_10x11 );
+ break;
+ case DMPAPER_15X11:
+ pSetupData->SetPaperFormat( PAPER_15x11 );
+ break;
+ case DMPAPER_ENV_INVITE:
+ pSetupData->SetPaperFormat( PAPER_ENV_INVITE );
+ break;
+ case DMPAPER_A_PLUS:
+ pSetupData->SetPaperFormat( PAPER_A_PLUS );
+ break;
+ case DMPAPER_B_PLUS:
+ pSetupData->SetPaperFormat( PAPER_B_PLUS );
+ break;
+ case DMPAPER_LETTER_PLUS:
+ pSetupData->SetPaperFormat( PAPER_LETTER_PLUS );
+ break;
+ case DMPAPER_A4_PLUS:
+ pSetupData->SetPaperFormat( PAPER_A4_PLUS );
+ break;
+ case DMPAPER_A2:
+ pSetupData->SetPaperFormat( PAPER_A2 );
+ break;
+ case DMPAPER_DBL_JAPANESE_POSTCARD:
+ pSetupData->SetPaperFormat( PAPER_DOUBLEPOSTCARD_JP );
+ break;
+ case DMPAPER_A6:
+ pSetupData->SetPaperFormat( PAPER_A6 );
+ break;
+ case DMPAPER_B6_JIS:
+ pSetupData->SetPaperFormat( PAPER_B6_JIS );
+ break;
+ case DMPAPER_12X11:
+ pSetupData->SetPaperFormat( PAPER_12x11 );
+ break;
+ default:
+ pSetupData->SetPaperFormat( PAPER_USER );
+ break;
+ }
+ }
+
+ if( nFlags & JobSetFlags::DUPLEXMODE )
+ {
+ DuplexMode eDuplex = DuplexMode::Unknown;
+ if( pDevModeW->dmFields & DM_DUPLEX )
+ {
+ if( pDevModeW->dmDuplex == DMDUP_SIMPLEX )
+ eDuplex = DuplexMode::Off;
+ else if( pDevModeW->dmDuplex == DMDUP_VERTICAL )
+ eDuplex = DuplexMode::LongEdge;
+ else if( pDevModeW->dmDuplex == DMDUP_HORIZONTAL )
+ eDuplex = DuplexMode::ShortEdge;
+ }
+ pSetupData->SetDuplexMode( eDuplex );
+ }
+}
+
+static void ImplJobSetupToDevMode( WinSalInfoPrinter const * pPrinter, const ImplJobSetup* pSetupData, JobSetFlags nFlags )
+{
+ if ( !pSetupData || !pSetupData->GetDriverData() )
+ return;
+
+ DEVMODEW* pDevModeW = const_cast<DEVMODEW *>(SAL_DEVMODE_W(pSetupData));
+ if( pDevModeW == nullptr )
+ return;
+
+ // Orientation
+ if ( nFlags & JobSetFlags::ORIENTATION )
+ {
+ pDevModeW->dmFields |= DM_ORIENTATION;
+ if ( pSetupData->GetOrientation() == Orientation::Portrait )
+ pDevModeW->dmOrientation = DMORIENT_PORTRAIT;
+ else
+ pDevModeW->dmOrientation = DMORIENT_LANDSCAPE;
+ }
+
+ // PaperBin
+ if ( nFlags & JobSetFlags::PAPERBIN )
+ {
+ const DWORD nCount = ImplDeviceCaps( pPrinter, DC_BINS, nullptr, pSetupData );
+
+ if ( nCount && (nCount != GDI_ERROR) )
+ {
+ WORD* pBins = static_cast<WORD*>(rtl_allocateZeroMemory(nCount*sizeof(WORD)));
+ ImplDeviceCaps( pPrinter, DC_BINS, reinterpret_cast<BYTE*>(pBins), pSetupData );
+ pDevModeW->dmFields |= DM_DEFAULTSOURCE;
+ pDevModeW->dmDefaultSource = pBins[ pSetupData->GetPaperBin() ];
+ std::free( pBins );
+ }
+ }
+
+ // PaperSize
+ if ( nFlags & JobSetFlags::PAPERSIZE )
+ {
+ pDevModeW->dmFields |= DM_PAPERSIZE;
+ pDevModeW->dmPaperWidth = 0;
+ pDevModeW->dmPaperLength = 0;
+
+ switch( pSetupData->GetPaperFormat() )
+ {
+ case PAPER_A2:
+ pDevModeW->dmPaperSize = DMPAPER_A2;
+ break;
+ case PAPER_A3:
+ pDevModeW->dmPaperSize = DMPAPER_A3;
+ break;
+ case PAPER_A4:
+ pDevModeW->dmPaperSize = DMPAPER_A4;
+ break;
+ case PAPER_A5:
+ pDevModeW->dmPaperSize = DMPAPER_A5;
+ break;
+ case PAPER_B4_ISO:
+ pDevModeW->dmPaperSize = DMPAPER_ISO_B4;
+ break;
+ case PAPER_LETTER:
+ pDevModeW->dmPaperSize = DMPAPER_LETTER;
+ break;
+ case PAPER_LEGAL:
+ pDevModeW->dmPaperSize = DMPAPER_LEGAL;
+ break;
+ case PAPER_TABLOID:
+ pDevModeW->dmPaperSize = DMPAPER_TABLOID;
+ break;
+
+ // http://msdn.microsoft.com/en-us/library/ms776398(VS.85).aspx
+ // DMPAPER_ENV_B6 is documented as:
+ // "DMPAPER_ENV_B6 35 Envelope B6 176 x 125 mm"
+ // which is the wrong way around, it is surely 125 x 176, i.e.
+ // compare DMPAPER_ENV_B4 and DMPAPER_ENV_B4 as
+ // DMPAPER_ENV_B4 33 Envelope B4 250 x 353 mm
+ // DMPAPER_ENV_B5 34 Envelope B5 176 x 250 mm
+
+ case PAPER_ENV_C4:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_C4;
+ break;
+ case PAPER_ENV_C5:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_C5;
+ break;
+ case PAPER_ENV_C6:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_C6;
+ break;
+ case PAPER_ENV_C65:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_C65;
+ break;
+ case PAPER_ENV_DL:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_DL;
+ break;
+ case PAPER_C:
+ pDevModeW->dmPaperSize = DMPAPER_CSHEET;
+ break;
+ case PAPER_D:
+ pDevModeW->dmPaperSize = DMPAPER_DSHEET;
+ break;
+ case PAPER_E:
+ pDevModeW->dmPaperSize = DMPAPER_ESHEET;
+ break;
+ case PAPER_EXECUTIVE:
+ pDevModeW->dmPaperSize = DMPAPER_EXECUTIVE;
+ break;
+ case PAPER_FANFOLD_LEGAL_DE:
+ pDevModeW->dmPaperSize = DMPAPER_FANFOLD_LGL_GERMAN;
+ break;
+ case PAPER_ENV_MONARCH:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_MONARCH;
+ break;
+ case PAPER_ENV_PERSONAL:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_PERSONAL;
+ break;
+ case PAPER_ENV_9:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_9;
+ break;
+ case PAPER_ENV_10:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_10;
+ break;
+ case PAPER_ENV_11:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_11;
+ break;
+ case PAPER_ENV_12:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_12;
+ break;
+ //See the comments on DMPAPER_B4 above
+ case PAPER_B4_JIS:
+ pDevModeW->dmPaperSize = DMPAPER_B4;
+ break;
+ case PAPER_B5_JIS:
+ pDevModeW->dmPaperSize = DMPAPER_B5;
+ break;
+ case PAPER_B6_JIS:
+ pDevModeW->dmPaperSize = DMPAPER_B6_JIS;
+ break;
+ case PAPER_LEDGER:
+ pDevModeW->dmPaperSize = DMPAPER_LEDGER;
+ break;
+ case PAPER_STATEMENT:
+ pDevModeW->dmPaperSize = DMPAPER_STATEMENT;
+ break;
+ case PAPER_10x14:
+ pDevModeW->dmPaperSize = DMPAPER_10X14;
+ break;
+ case PAPER_ENV_14:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_14;
+ break;
+ case PAPER_ENV_C3:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_C3;
+ break;
+ case PAPER_ENV_ITALY:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_ITALY;
+ break;
+ case PAPER_FANFOLD_US:
+ pDevModeW->dmPaperSize = DMPAPER_FANFOLD_US;
+ break;
+ case PAPER_FANFOLD_DE:
+ pDevModeW->dmPaperSize = DMPAPER_FANFOLD_STD_GERMAN;
+ break;
+ case PAPER_POSTCARD_JP:
+ pDevModeW->dmPaperSize = DMPAPER_JAPANESE_POSTCARD;
+ break;
+ case PAPER_9x11:
+ pDevModeW->dmPaperSize = DMPAPER_9X11;
+ break;
+ case PAPER_10x11:
+ pDevModeW->dmPaperSize = DMPAPER_10X11;
+ break;
+ case PAPER_15x11:
+ pDevModeW->dmPaperSize = DMPAPER_15X11;
+ break;
+ case PAPER_ENV_INVITE:
+ pDevModeW->dmPaperSize = DMPAPER_ENV_INVITE;
+ break;
+ case PAPER_A_PLUS:
+ pDevModeW->dmPaperSize = DMPAPER_A_PLUS;
+ break;
+ case PAPER_B_PLUS:
+ pDevModeW->dmPaperSize = DMPAPER_B_PLUS;
+ break;
+ case PAPER_LETTER_PLUS:
+ pDevModeW->dmPaperSize = DMPAPER_LETTER_PLUS;
+ break;
+ case PAPER_A4_PLUS:
+ pDevModeW->dmPaperSize = DMPAPER_A4_PLUS;
+ break;
+ case PAPER_DOUBLEPOSTCARD_JP:
+ pDevModeW->dmPaperSize = DMPAPER_DBL_JAPANESE_POSTCARD;
+ break;
+ case PAPER_A6:
+ pDevModeW->dmPaperSize = DMPAPER_A6;
+ break;
+ case PAPER_12x11:
+ pDevModeW->dmPaperSize = DMPAPER_12X11;
+ break;
+ default:
+ {
+ short nPaper = 0;
+ const DWORD nPaperCount = ImplDeviceCaps( pPrinter, DC_PAPERS, nullptr, pSetupData );
+ WORD* pPapers = nullptr;
+ const DWORD nPaperSizeCount = ImplDeviceCaps( pPrinter, DC_PAPERSIZE, nullptr, pSetupData );
+ POINT* pPaperSizes = nullptr;
+ DWORD nLandscapeAngle = ImplDeviceCaps( pPrinter, DC_ORIENTATION, nullptr, pSetupData );
+ if ( nPaperCount && (nPaperCount != GDI_ERROR) )
+ {
+ pPapers = static_cast<WORD*>(rtl_allocateZeroMemory(nPaperCount*sizeof(WORD)));
+ ImplDeviceCaps( pPrinter, DC_PAPERS, reinterpret_cast<BYTE*>(pPapers), pSetupData );
+ }
+ if ( nPaperSizeCount && (nPaperSizeCount != GDI_ERROR) )
+ {
+ pPaperSizes = static_cast<POINT*>(rtl_allocateZeroMemory(nPaperSizeCount*sizeof(POINT)));
+ ImplDeviceCaps( pPrinter, DC_PAPERSIZE, reinterpret_cast<BYTE*>(pPaperSizes), pSetupData );
+ }
+ if ( (nPaperSizeCount == nPaperCount) && pPapers && pPaperSizes )
+ {
+ PaperInfo aInfo(pSetupData->GetPaperWidth(), pSetupData->GetPaperHeight());
+ // compare paper formats and select a good match
+ for ( DWORD i = 0; i < nPaperCount; ++i )
+ {
+ if ( aInfo.sloppyEqual(PaperInfo(pPaperSizes[i].x*10, pPaperSizes[i].y*10)))
+ {
+ nPaper = pPapers[i];
+ break;
+ }
+ }
+
+ // If the printer supports landscape orientation, check paper sizes again
+ // with landscape orientation. This is necessary as a printer driver provides
+ // all paper sizes with portrait orientation only!!
+ if ( !nPaper && nLandscapeAngle != 0 )
+ {
+ PaperInfo aRotatedInfo(pSetupData->GetPaperHeight(), pSetupData->GetPaperWidth());
+ for ( DWORD i = 0; i < nPaperCount; ++i )
+ {
+ if ( aRotatedInfo.sloppyEqual(PaperInfo(pPaperSizes[i].x*10, pPaperSizes[i].y*10)) )
+ {
+ nPaper = pPapers[i];
+ break;
+ }
+ }
+ }
+
+ if ( nPaper )
+ pDevModeW->dmPaperSize = nPaper;
+ }
+
+ if ( !nPaper )
+ {
+ pDevModeW->dmFields |= DM_PAPERLENGTH | DM_PAPERWIDTH;
+ pDevModeW->dmPaperSize = DMPAPER_USER;
+ pDevModeW->dmPaperWidth = static_cast<short>(pSetupData->GetPaperWidth()/10);
+ pDevModeW->dmPaperLength = static_cast<short>(pSetupData->GetPaperHeight()/10);
+ }
+
+ if ( pPapers )
+ std::free(pPapers);
+ if ( pPaperSizes )
+ std::free(pPaperSizes);
+
+ break;
+ }
+ }
+ }
+ if( nFlags & JobSetFlags::DUPLEXMODE )
+ {
+ switch( pSetupData->GetDuplexMode() )
+ {
+ case DuplexMode::Off:
+ pDevModeW->dmFields |= DM_DUPLEX;
+ pDevModeW->dmDuplex = DMDUP_SIMPLEX;
+ break;
+ case DuplexMode::ShortEdge:
+ pDevModeW->dmFields |= DM_DUPLEX;
+ pDevModeW->dmDuplex = DMDUP_HORIZONTAL;
+ break;
+ case DuplexMode::LongEdge:
+ pDevModeW->dmFields |= DM_DUPLEX;
+ pDevModeW->dmDuplex = DMDUP_VERTICAL;
+ break;
+ case DuplexMode::Unknown:
+ break;
+ }
+ }
+}
+
+static HDC ImplCreateICW_WithCatch( LPWSTR pDriver,
+ LPCWSTR pDevice,
+ DEVMODEW const * pDevMode )
+{
+ HDC hDC = nullptr;
+ CATCH_DRIVER_EX_BEGIN;
+ hDC = CreateICW( pDriver, pDevice, nullptr, pDevMode );
+ CATCH_DRIVER_EX_END_2( "exception in CreateICW" );
+ return hDC;
+}
+
+static HDC ImplCreateSalPrnIC( WinSalInfoPrinter const * pPrinter, const ImplJobSetup* pSetupData )
+{
+ HDC hDC = nullptr;
+ DEVMODEW const * pDevMode;
+ if ( pSetupData && pSetupData->GetDriverData() )
+ pDevMode = SAL_DEVMODE_W( pSetupData );
+ else
+ pDevMode = nullptr;
+ // #95347 some buggy drivers (eg, OKI) write to those buffers in CreateIC, although declared const - so provide some space
+ // pl: does this hold true for Unicode functions ?
+ if( pPrinter->maDriverName.getLength() > 2048 || pPrinter->maDeviceName.getLength() > 2048 )
+ return nullptr;
+ sal_Unicode pDriverName[ 4096 ];
+ sal_Unicode pDeviceName[ 4096 ];
+ memcpy( pDriverName, pPrinter->maDriverName.getStr(), pPrinter->maDriverName.getLength()*sizeof(sal_Unicode));
+ memset( pDriverName+pPrinter->maDriverName.getLength(), 0, 32 );
+ memcpy( pDeviceName, pPrinter->maDeviceName.getStr(), pPrinter->maDeviceName.getLength()*sizeof(sal_Unicode));
+ memset( pDeviceName+pPrinter->maDeviceName.getLength(), 0, 32 );
+ hDC = ImplCreateICW_WithCatch( o3tl::toW(pDriverName),
+ o3tl::toW(pDeviceName),
+ pDevMode );
+ return hDC;
+}
+
+static WinSalGraphics* ImplCreateSalPrnGraphics( HDC hDC )
+{
+ WinSalGraphics* pGraphics = new WinSalGraphics(WinSalGraphics::PRINTER, false, nullptr, /* CHECKME */ nullptr);
+ pGraphics->SetLayout( SalLayoutFlags::NONE );
+ pGraphics->setHDC(hDC);
+ pGraphics->InitGraphics();
+ return pGraphics;
+}
+
+static bool ImplUpdateSalPrnIC( WinSalInfoPrinter* pPrinter, const ImplJobSetup* pSetupData )
+{
+ HDC hNewDC = ImplCreateSalPrnIC( pPrinter, pSetupData );
+ if ( !hNewDC )
+ return false;
+
+ if ( pPrinter->mpGraphics )
+ {
+ pPrinter->mpGraphics->DeInitGraphics();
+ DeleteDC( pPrinter->mpGraphics->getHDC() );
+ delete pPrinter->mpGraphics;
+ }
+
+ pPrinter->mpGraphics = ImplCreateSalPrnGraphics( hNewDC );
+ pPrinter->mhDC = hNewDC;
+
+ return true;
+}
+
+
+SalInfoPrinter* WinSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
+ ImplJobSetup* pSetupData )
+{
+ WinSalInfoPrinter* pPrinter = new WinSalInfoPrinter;
+ if( ! pQueueInfo->mpPortName )
+ GetPrinterQueueState( pQueueInfo );
+ pPrinter->maDriverName = pQueueInfo->maDriver;
+ pPrinter->maDeviceName = pQueueInfo->maPrinterName;
+ pPrinter->maPortName = pQueueInfo->mpPortName ? *pQueueInfo->mpPortName : OUString();
+
+ // check if the provided setup data match the actual printer
+ ImplTestSalJobSetup( pPrinter, pSetupData, true );
+
+ HDC hDC = ImplCreateSalPrnIC( pPrinter, pSetupData );
+ if ( !hDC )
+ {
+ delete pPrinter;
+ return nullptr;
+ }
+
+ pPrinter->mpGraphics = ImplCreateSalPrnGraphics( hDC );
+ pPrinter->mhDC = hDC;
+ if ( !pSetupData->GetDriverData() )
+ ImplUpdateSalJobSetup( pPrinter, pSetupData, false, nullptr );
+ ImplDevModeToJobSetup( pPrinter, pSetupData, JobSetFlags::ALL );
+ pSetupData->SetSystem( JOBSETUP_SYSTEM_WINDOWS );
+
+ return pPrinter;
+}
+
+void WinSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
+{
+ delete pPrinter;
+}
+
+
+WinSalInfoPrinter::WinSalInfoPrinter() :
+ mpGraphics( nullptr ),
+ mhDC( nullptr ),
+ mbGraphics( false )
+{
+ m_bPapersInit = false;
+}
+
+WinSalInfoPrinter::~WinSalInfoPrinter()
+{
+ if ( mpGraphics )
+ {
+ mpGraphics->DeInitGraphics();
+ DeleteDC( mpGraphics->getHDC() );
+ delete mpGraphics;
+ }
+}
+
+void WinSalInfoPrinter::InitPaperFormats( const ImplJobSetup* pSetupData )
+{
+ m_aPaperFormats.clear();
+
+ DWORD nCount = ImplDeviceCaps( this, DC_PAPERSIZE, nullptr, pSetupData );
+ if( nCount == GDI_ERROR )
+ nCount = 0;
+
+ if( nCount )
+ {
+ POINT* pPaperSizes = static_cast<POINT*>(rtl_allocateZeroMemory(nCount*sizeof(POINT)));
+ ImplDeviceCaps( this, DC_PAPERSIZE, reinterpret_cast<BYTE*>(pPaperSizes), pSetupData );
+
+ sal_Unicode* pNamesBuffer = static_cast<sal_Unicode*>(std::malloc(nCount*64*sizeof(sal_Unicode)));
+ ImplDeviceCaps( this, DC_PAPERNAMES, reinterpret_cast<BYTE*>(pNamesBuffer), pSetupData );
+ for( DWORD i = 0; i < nCount; ++i )
+ {
+ PaperInfo aInfo(pPaperSizes[i].x * 10, pPaperSizes[i].y * 10);
+ m_aPaperFormats.push_back( aInfo );
+ }
+ std::free( pNamesBuffer );
+ std::free( pPaperSizes );
+ }
+
+ m_bPapersInit = true;
+}
+
+int WinSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* pSetupData )
+{
+ const DWORD nRet = ImplDeviceCaps( this, DC_ORIENTATION, nullptr, pSetupData );
+
+ if( nRet != GDI_ERROR )
+ return static_cast<int>(nRet) * 10;
+ return 900; // guess
+}
+
+SalGraphics* WinSalInfoPrinter::AcquireGraphics()
+{
+ if ( mbGraphics )
+ return nullptr;
+
+ if ( mpGraphics )
+ mbGraphics = true;
+
+ return mpGraphics;
+}
+
+void WinSalInfoPrinter::ReleaseGraphics( SalGraphics* )
+{
+ mbGraphics = false;
+}
+
+bool WinSalInfoPrinter::Setup(weld::Window* pFrame, ImplJobSetup* pSetupData)
+{
+ if ( ImplUpdateSalJobSetup(this, pSetupData, true, pFrame))
+ {
+ ImplDevModeToJobSetup( this, pSetupData, JobSetFlags::ALL );
+ return ImplUpdateSalPrnIC( this, pSetupData );
+ }
+
+ return false;
+}
+
+bool WinSalInfoPrinter::SetPrinterData( ImplJobSetup* pSetupData )
+{
+ if ( !ImplTestSalJobSetup( this, pSetupData, false ) )
+ return false;
+ return ImplUpdateSalPrnIC( this, pSetupData );
+}
+
+bool WinSalInfoPrinter::SetData( JobSetFlags nFlags, ImplJobSetup* pSetupData )
+{
+ ImplJobSetupToDevMode( this, pSetupData, nFlags );
+ if ( ImplUpdateSalJobSetup( this, pSetupData, true, nullptr ) )
+ {
+ ImplDevModeToJobSetup( this, pSetupData, nFlags );
+ return ImplUpdateSalPrnIC( this, pSetupData );
+ }
+
+ return false;
+}
+
+sal_uInt16 WinSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pSetupData )
+{
+ DWORD nRet = ImplDeviceCaps( this, DC_BINS, nullptr, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return nRet;
+ else
+ return 0;
+}
+
+OUString WinSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pSetupData, sal_uInt16 nPaperBin )
+{
+ OUString aPaperBinName;
+
+ DWORD nBins = ImplDeviceCaps( this, DC_BINNAMES, nullptr, pSetupData );
+ if ( (nPaperBin < nBins) && (nBins != GDI_ERROR) )
+ {
+ auto pBuffer = std::make_unique<sal_Unicode[]>(nBins*24);
+ DWORD nRet = ImplDeviceCaps( this, DC_BINNAMES, reinterpret_cast<BYTE*>(pBuffer.get()), pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ aPaperBinName = OUString( pBuffer.get() + (nPaperBin*24) );
+ }
+
+ return aPaperBinName;
+}
+
+sal_uInt32 WinSalInfoPrinter::GetCapabilities( const ImplJobSetup* pSetupData, PrinterCapType nType )
+{
+ DWORD nRet;
+
+ switch ( nType )
+ {
+ case PrinterCapType::SupportDialog:
+ return TRUE;
+ case PrinterCapType::Copies:
+ nRet = ImplDeviceCaps( this, DC_COPIES, nullptr, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return nRet;
+ return 0;
+ case PrinterCapType::CollateCopies:
+ nRet = ImplDeviceCaps( this, DC_COLLATE, nullptr, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ {
+ nRet = ImplDeviceCaps( this, DC_COPIES, nullptr, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return nRet;
+ }
+ return 0;
+
+ case PrinterCapType::SetOrientation:
+ nRet = ImplDeviceCaps( this, DC_ORIENTATION, nullptr, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return TRUE;
+ return FALSE;
+
+ case PrinterCapType::SetPaperSize:
+ case PrinterCapType::SetPaper:
+ nRet = ImplDeviceCaps( this, DC_PAPERS, nullptr, pSetupData );
+ if ( nRet && (nRet != GDI_ERROR) )
+ return TRUE;
+ return FALSE;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void WinSalInfoPrinter::GetPageInfo( const ImplJobSetup*,
+ long& rOutWidth, long& rOutHeight,
+ Point& rPageOffset,
+ Size& rPaperSize )
+{
+ HDC hDC = mhDC;
+
+ rOutWidth = GetDeviceCaps( hDC, HORZRES );
+ rOutHeight = GetDeviceCaps( hDC, VERTRES );
+
+ rPageOffset.setX( GetDeviceCaps( hDC, PHYSICALOFFSETX ) );
+ rPageOffset.setY( GetDeviceCaps( hDC, PHYSICALOFFSETY ) );
+ rPaperSize.setWidth( GetDeviceCaps( hDC, PHYSICALWIDTH ) );
+ rPaperSize.setHeight( GetDeviceCaps( hDC, PHYSICALHEIGHT ) );
+}
+
+
+std::unique_ptr<SalPrinter> WinSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
+{
+ WinSalPrinter* pPrinter = new WinSalPrinter;
+ pPrinter->mpInfoPrinter = static_cast<WinSalInfoPrinter*>(pInfoPrinter);
+ return std::unique_ptr<SalPrinter>(pPrinter);
+}
+
+static BOOL CALLBACK SalPrintAbortProc( HDC hPrnDC, int /* nError */ )
+{
+ SalData* pSalData = GetSalData();
+ WinSalPrinter* pPrinter;
+ int i = 0;
+ bool bWhile = true;
+
+ // Ensure we handle the mutex which will be released in WinSalInstance::DoYield
+ SolarMutexGuard aSolarMutexGuard;
+ do
+ {
+ // process messages
+ bWhile = Application::Reschedule( true );
+ if (i > 15)
+ bWhile = false;
+ else
+ ++i;
+
+ pPrinter = pSalData->mpFirstPrinter;
+ while ( pPrinter )
+ {
+ if( pPrinter->mhDC == hPrnDC )
+ break;
+
+ pPrinter = pPrinter->mpNextPrinter;
+ }
+
+ if ( !pPrinter || pPrinter->mbAbort )
+ return FALSE;
+ }
+ while ( bWhile );
+
+ return TRUE;
+}
+
+static DEVMODEW const * ImplSalSetCopies( DEVMODEW const * pDevMode, sal_uLong nCopies, bool bCollate )
+{
+ if ( pDevMode && (nCopies > 1) )
+ {
+ if ( nCopies > 32765 )
+ nCopies = 32765;
+ sal_uLong nDevSize = pDevMode->dmSize+pDevMode->dmDriverExtra;
+ LPDEVMODEW pNewDevMode = static_cast<LPDEVMODEW>(std::malloc( nDevSize ));
+ assert(pNewDevMode); // Don't handle OOM conditions
+ memcpy( pNewDevMode, pDevMode, nDevSize );
+ pNewDevMode->dmFields |= DM_COPIES;
+ pNewDevMode->dmCopies = static_cast<short>(static_cast<sal_uInt16>(nCopies));
+ pNewDevMode->dmFields |= DM_COLLATE;
+ if ( bCollate )
+ pNewDevMode->dmCollate = DMCOLLATE_TRUE;
+ else
+ pNewDevMode->dmCollate = DMCOLLATE_FALSE;
+ return pNewDevMode;
+ }
+ else
+ {
+ return pDevMode;
+ }
+}
+
+
+WinSalPrinter::WinSalPrinter() :
+ mpGraphics( nullptr ),
+ mpInfoPrinter( nullptr ),
+ mpNextPrinter( nullptr ),
+ mhDC( nullptr ),
+ mnError( SalPrinterError::NONE ),
+ mnCopies( 0 ),
+ mbCollate( false ),
+ mbAbort( false ),
+ mbValid( true )
+{
+ SalData* pSalData = GetSalData();
+ // insert printer in printerlist
+ mpNextPrinter = pSalData->mpFirstPrinter;
+ pSalData->mpFirstPrinter = this;
+}
+
+WinSalPrinter::~WinSalPrinter()
+{
+ SalData* pSalData = GetSalData();
+
+ // release DC if there is one still around because of AbortJob
+ HDC hDC = mhDC;
+ if ( hDC )
+ {
+ if ( mpGraphics )
+ {
+ mpGraphics->DeInitGraphics();
+ delete mpGraphics;
+ }
+
+ DeleteDC( hDC );
+ }
+
+ // remove printer from printerlist
+ if ( this == pSalData->mpFirstPrinter )
+ pSalData->mpFirstPrinter = mpNextPrinter;
+ else
+ {
+ WinSalPrinter* pTempPrinter = pSalData->mpFirstPrinter;
+
+ while( pTempPrinter->mpNextPrinter != this )
+ pTempPrinter = pTempPrinter->mpNextPrinter;
+
+ pTempPrinter->mpNextPrinter = mpNextPrinter;
+ }
+ mbValid = false;
+}
+
+void WinSalPrinter::markInvalid()
+{
+ mbValid = false;
+}
+
+// need wrappers for StarTocW/A to use structured exception handling
+// since SEH does not mix with standard exception handling's cleanup
+static int lcl_StartDocW( HDC hDC, DOCINFOW const * pInfo, WinSalPrinter* pPrt )
+{
+ int nRet = 0;
+ CATCH_DRIVER_EX_BEGIN;
+ nRet = ::StartDocW( hDC, pInfo );
+ CATCH_DRIVER_EX_END( "exception in StartDocW", pPrt );
+ return nRet;
+}
+
+bool WinSalPrinter::StartJob( const OUString* pFileName,
+ const OUString& rJobName,
+ const OUString&,
+ sal_uInt32 nCopies,
+ bool bCollate,
+ bool /*bDirect*/,
+ ImplJobSetup* pSetupData )
+{
+ mnError = SalPrinterError::NONE;
+ mbAbort = false;
+ mnCopies = nCopies;
+ mbCollate = bCollate;
+
+ DEVMODEW const * pOrgDevModeW = nullptr;
+ DEVMODEW const * pDevModeW = nullptr;
+ HDC hDC = nullptr;
+ if ( pSetupData && pSetupData->GetDriverData() )
+ {
+ pOrgDevModeW = SAL_DEVMODE_W( pSetupData );
+ pDevModeW = ImplSalSetCopies( pOrgDevModeW, nCopies, bCollate );
+ }
+
+ // #95347 some buggy drivers (eg, OKI) write to those buffers in CreateDC, although declared const - so provide some space
+ sal_Unicode aDrvBuf[4096];
+ sal_Unicode aDevBuf[4096];
+ memcpy( aDrvBuf, mpInfoPrinter->maDriverName.getStr(), (mpInfoPrinter->maDriverName.getLength()+1)*sizeof(sal_Unicode));
+ memcpy( aDevBuf, mpInfoPrinter->maDeviceName.getStr(), (mpInfoPrinter->maDeviceName.getLength()+1)*sizeof(sal_Unicode));
+ hDC = CreateDCW( o3tl::toW(aDrvBuf),
+ o3tl::toW(aDevBuf),
+ nullptr,
+ pDevModeW );
+
+ if ( pDevModeW != pOrgDevModeW )
+ std::free( const_cast<DEVMODEW *>(pDevModeW) );
+
+ if ( !hDC )
+ {
+ mnError = SalPrinterError::General;
+ return false;
+ }
+
+ // make sure mhDC is set before the printer driver may call our abortproc
+ mhDC = hDC;
+ if ( SetAbortProc( hDC, SalPrintAbortProc ) <= 0 )
+ {
+ mnError = SalPrinterError::General;
+ return false;
+ }
+
+ mnError = SalPrinterError::NONE;
+ mbAbort = false;
+
+ // As the Telecom Balloon Fax driver tends to send messages repeatedly
+ // we try to process first all, and then insert a dummy message
+ for (int i = 0; Application::Reschedule( true ) && i <= 15; ++i);
+ bool const ret = PostMessageW(GetSalData()->mpInstance->mhComWnd, SAL_MSG_DUMMY, 0, 0);
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+
+ // bring up a file chooser if printing to file port but no file name given
+ OUString aOutFileName;
+ if( mpInfoPrinter->maPortName.equalsIgnoreAsciiCase( "FILE:" ) && !(pFileName && !pFileName->isEmpty()) )
+ {
+
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ uno::Reference< XFilePicker3 > xFilePicker = FilePicker::createWithMode(xContext, TemplateDescription::FILESAVE_SIMPLE);
+
+ if( xFilePicker->execute() == ExecutableDialogResults::OK )
+ {
+ Sequence< OUString > aPathSeq( xFilePicker->getSelectedFiles() );
+ INetURLObject aObj( aPathSeq[0] );
+ aOutFileName = aObj.PathToFileName();
+ }
+ else
+ {
+ mnError = SalPrinterError::Abort;
+ return false;
+ }
+ }
+
+ DOCINFOW aInfo = {};
+ aInfo.cbSize = sizeof( aInfo );
+ aInfo.lpszDocName = o3tl::toW(rJobName.getStr());
+ if ( pFileName || aOutFileName.getLength() )
+ {
+ if ( (pFileName && !pFileName->isEmpty()) || aOutFileName.getLength() )
+ {
+ aInfo.lpszOutput = o3tl::toW((pFileName && !pFileName->isEmpty()) ? pFileName->getStr() : aOutFileName.getStr());
+ }
+ else
+ aInfo.lpszOutput = L"FILE:";
+ }
+ else
+ aInfo.lpszOutput = nullptr;
+
+ // start Job, in the main thread
+ int nRet = vcl::solarthread::syncExecute([hDC, this, &aInfo]() -> int { return lcl_StartDocW(hDC, &aInfo, this); });
+
+ if ( nRet <= 0 )
+ {
+ long nError = GetLastError();
+ if ( (nRet == SP_USERABORT) || (nRet == SP_APPABORT) || (nError == ERROR_PRINT_CANCELLED) || (nError == ERROR_CANCELLED) )
+ mnError = SalPrinterError::Abort;
+ else
+ mnError = SalPrinterError::General;
+ return false;
+ }
+
+ return true;
+}
+
+void WinSalPrinter::DoEndDoc(HDC hDC)
+{
+ CATCH_DRIVER_EX_BEGIN;
+ if( ::EndDoc( hDC ) <= 0 )
+ GetLastError();
+ CATCH_DRIVER_EX_END( "exception in EndDoc", this );
+}
+
+bool WinSalPrinter::EndJob()
+{
+ HDC hDC = mhDC;
+ if ( isValid() && hDC )
+ {
+ if ( mpGraphics )
+ {
+ mpGraphics->DeInitGraphics();
+ delete mpGraphics;
+ mpGraphics = nullptr;
+ }
+
+ // #i54419# Windows fax printer brings up a dialog in EndDoc
+ // which text previously copied in soffice process can be
+ // pasted to -> deadlock due to mutex not released.
+ // it should be safe to release the yield mutex over the EndDoc
+ // call, however the real solution is supposed to be the threading
+ // framework yet to come.
+ {
+ SolarMutexReleaser aReleaser;
+ DoEndDoc( hDC );
+ }
+ DeleteDC( hDC );
+ mhDC = nullptr;
+ }
+
+ return true;
+}
+
+SalGraphics* WinSalPrinter::StartPage( ImplJobSetup* pSetupData, bool bNewJobData )
+{
+ if( ! isValid() || mhDC == nullptr )
+ return nullptr;
+
+ HDC hDC = mhDC;
+ if ( pSetupData && pSetupData->GetDriverData() && bNewJobData )
+ {
+ DEVMODEW const * pOrgDevModeW;
+ DEVMODEW const * pDevModeW;
+ pOrgDevModeW = SAL_DEVMODE_W( pSetupData );
+ pDevModeW = ImplSalSetCopies( pOrgDevModeW, mnCopies, mbCollate );
+ ResetDCW( hDC, pDevModeW );
+ if ( pDevModeW != pOrgDevModeW )
+ std::free( const_cast<DEVMODEW *>(pDevModeW) );
+ }
+ volatile int nRet = 0;
+ CATCH_DRIVER_EX_BEGIN;
+ nRet = ::StartPage( hDC );
+ CATCH_DRIVER_EX_END( "exception in StartPage", this );
+
+ if ( nRet <= 0 )
+ {
+ GetLastError();
+ mnError = SalPrinterError::General;
+ return nullptr;
+ }
+
+ // Hack to work around old PostScript printer drivers optimizing away empty pages
+ // TODO: move into ImplCreateSalPrnGraphics()?
+ HPEN hTempPen = SelectPen( hDC, GetStockPen( NULL_PEN ) );
+ HBRUSH hTempBrush = SelectBrush( hDC, GetStockBrush( NULL_BRUSH ) );
+ Rectangle( hDC, -8000, -8000, -7999, -7999 );
+ SelectPen( hDC, hTempPen );
+ SelectBrush( hDC, hTempBrush );
+
+ mpGraphics = ImplCreateSalPrnGraphics( hDC );
+ return mpGraphics;
+}
+
+void WinSalPrinter::EndPage()
+{
+ HDC hDC = mhDC;
+ if ( hDC && mpGraphics )
+ {
+ mpGraphics->DeInitGraphics();
+ delete mpGraphics;
+ mpGraphics = nullptr;
+ }
+
+ if( ! isValid() )
+ return;
+
+ volatile int nRet = 0;
+ CATCH_DRIVER_EX_BEGIN;
+ nRet = ::EndPage( hDC );
+ CATCH_DRIVER_EX_END( "exception in EndPage", this );
+
+ if ( nRet <= 0 )
+ {
+ GetLastError();
+ mnError = SalPrinterError::General;
+ }
+}
+
+SalPrinterError WinSalPrinter::GetErrorCode()
+{
+ return mnError;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/salvd.cxx b/vcl/win/gdi/salvd.cxx
new file mode 100644
index 000000000..e1cacb8b0
--- /dev/null
+++ b/vcl/win/gdi/salvd.cxx
@@ -0,0 +1,226 @@
+/* -*- 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 <svsys.h>
+
+#include <comphelper/windowserrorstring.hxx>
+
+#include <vcl/sysdata.hxx>
+
+#include <win/wincomp.hxx>
+#include <win/saldata.hxx>
+#include <win/salinst.h>
+#include <win/salgdi.h>
+#include <win/salvd.h>
+#include <opengl/win/gdiimpl.hxx>
+#include <sal/log.hxx>
+#include <o3tl/temporary.hxx>
+
+HBITMAP WinSalVirtualDevice::ImplCreateVirDevBitmap(HDC hDC, long nDX, long nDY, sal_uInt16 nBitCount, void **ppData)
+{
+ HBITMAP hBitmap;
+
+ if ( nBitCount == 1 )
+ {
+ hBitmap = CreateBitmap( static_cast<int>(nDX), static_cast<int>(nDY), 1, 1, nullptr );
+ SAL_WARN_IF( !hBitmap, "vcl", "CreateBitmap failed: " << WindowsErrorString( GetLastError() ) );
+ ppData = nullptr;
+ }
+ else
+ {
+ if (nBitCount == 0)
+ nBitCount = static_cast<WORD>(GetDeviceCaps(hDC, BITSPIXEL));
+
+ // #146839# Don't use CreateCompatibleBitmap() - there seem to
+ // be built-in limits for those HBITMAPs, at least this fails
+ // rather often on large displays/multi-monitor setups.
+ BITMAPINFO aBitmapInfo;
+ aBitmapInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
+ aBitmapInfo.bmiHeader.biWidth = nDX;
+ aBitmapInfo.bmiHeader.biHeight = nDY;
+ aBitmapInfo.bmiHeader.biPlanes = 1;
+ aBitmapInfo.bmiHeader.biBitCount = nBitCount;
+ aBitmapInfo.bmiHeader.biCompression = BI_RGB;
+ aBitmapInfo.bmiHeader.biSizeImage = 0;
+ aBitmapInfo.bmiHeader.biXPelsPerMeter = 0;
+ aBitmapInfo.bmiHeader.biYPelsPerMeter = 0;
+ aBitmapInfo.bmiHeader.biClrUsed = 0;
+ aBitmapInfo.bmiHeader.biClrImportant = 0;
+
+ hBitmap = CreateDIBSection( hDC, &aBitmapInfo,
+ DIB_RGB_COLORS, ppData, nullptr,
+ 0 );
+ SAL_WARN_IF( !hBitmap, "vcl", "CreateDIBSection failed: " << WindowsErrorString( GetLastError() ) );
+ }
+
+ return hBitmap;
+}
+
+std::unique_ptr<SalVirtualDevice> WinSalInstance::CreateVirtualDevice( SalGraphics* pSGraphics,
+ long &nDX, long &nDY,
+ DeviceFormat eFormat,
+ const SystemGraphicsData* pData )
+{
+ WinSalGraphics* pGraphics = static_cast<WinSalGraphics*>(pSGraphics);
+ HDC hDC = nullptr;
+
+ if( pData )
+ {
+ hDC = (pData->hDC) ? pData->hDC : GetDC(pData->hWnd);
+ if (hDC)
+ {
+ nDX = GetDeviceCaps( hDC, HORZRES );
+ nDY = GetDeviceCaps( hDC, VERTRES );
+ }
+ else
+ {
+ nDX = 0;
+ nDY = 0;
+ }
+ }
+ else
+ {
+ hDC = CreateCompatibleDC( pGraphics->getHDC() );
+ SAL_WARN_IF( !hDC, "vcl", "CreateCompatibleDC failed: " << WindowsErrorString( GetLastError() ) );
+ }
+
+ if (!hDC)
+ return nullptr;
+
+ sal_uInt16 nBitCount = (eFormat == DeviceFormat::BITMASK) ? 1 : 0;
+
+ HBITMAP hBmp = nullptr;
+ if (!pData)
+ {
+ // #124826# continue even if hBmp could not be created
+ // if we would return a failure in this case, the process
+ // would terminate which is not required
+ hBmp = WinSalVirtualDevice::ImplCreateVirDevBitmap(pGraphics->getHDC(),
+ nDX, nDY, nBitCount,
+ &o3tl::temporary<void*>(nullptr));
+ }
+
+ const bool bForeignDC = pData != nullptr && pData->hDC != nullptr;
+ const SalData* pSalData = GetSalData();
+
+ WinSalVirtualDevice* pVDev = new WinSalVirtualDevice(hDC, hBmp, nBitCount,
+ bForeignDC, nDX, nDY);
+
+ WinSalGraphics* pVirGraphics = new WinSalGraphics(WinSalGraphics::VIRTUAL_DEVICE,
+ pGraphics->isScreen(), nullptr, pVDev);
+
+ // by default no! mirroring for VirtualDevices, can be enabled with EnableRTL()
+ pVirGraphics->SetLayout( SalLayoutFlags::NONE );
+ pVirGraphics->setHDC(hDC);
+
+ if ( pSalData->mhDitherPal && pVirGraphics->isScreen() )
+ {
+ pVirGraphics->setDefPal(SelectPalette( hDC, pSalData->mhDitherPal, TRUE ));
+ RealizePalette( hDC );
+ }
+
+ pVirGraphics->InitGraphics();
+ pVDev->setGraphics(pVirGraphics);
+
+ return std::unique_ptr<SalVirtualDevice>(pVDev);
+}
+
+WinSalVirtualDevice::WinSalVirtualDevice(HDC hDC, HBITMAP hBMP, sal_uInt16 nBitCount, bool bForeignDC, long nWidth, long nHeight)
+ : mhLocalDC(hDC), // HDC or 0 for Cache Device
+ mhBmp(hBMP), // Memory Bitmap
+ mnBitCount(nBitCount), // BitCount (0 or 1)
+ mbGraphics(false), // is Graphics used
+ mbForeignDC(bForeignDC), // uses a foreign DC instead of a bitmap
+ mnWidth(nWidth),
+ mnHeight(nHeight)
+{
+ // Default Bitmap
+ if (hBMP)
+ mhDefBmp = SelectBitmap(hDC, hBMP);
+ else
+ mhDefBmp = nullptr;
+
+ // insert VirDev into list of virtual devices
+ SalData* pSalData = GetSalData();
+ mpNext = pSalData->mpFirstVD;
+ pSalData->mpFirstVD = this;
+}
+
+WinSalVirtualDevice::~WinSalVirtualDevice()
+{
+ // remove VirDev from list of virtual devices
+ SalData* pSalData = GetSalData();
+ WinSalVirtualDevice** ppVirDev = &pSalData->mpFirstVD;
+ for(; (*ppVirDev != this) && *ppVirDev; ppVirDev = &(*ppVirDev)->mpNext );
+ if( *ppVirDev )
+ *ppVirDev = mpNext;
+
+ // destroy saved DC
+ if( mpGraphics->getDefPal() )
+ SelectPalette( mpGraphics->getHDC(), mpGraphics->getDefPal(), TRUE );
+ mpGraphics->DeInitGraphics();
+ if( mhDefBmp )
+ SelectBitmap( mpGraphics->getHDC(), mhDefBmp );
+ if( !mbForeignDC )
+ DeleteDC( mpGraphics->getHDC() );
+}
+
+SalGraphics* WinSalVirtualDevice::AcquireGraphics()
+{
+ if ( mbGraphics )
+ return nullptr;
+
+ if ( mpGraphics )
+ mbGraphics = true;
+
+ return mpGraphics.get();
+}
+
+void WinSalVirtualDevice::ReleaseGraphics( SalGraphics* )
+{
+ mbGraphics = false;
+}
+
+bool WinSalVirtualDevice::SetSize( long nDX, long nDY )
+{
+ if( mbForeignDC || !mhBmp )
+ return true; // ???
+
+ HBITMAP hNewBmp = ImplCreateVirDevBitmap(getHDC(), nDX, nDY, mnBitCount,
+ &o3tl::temporary<void*>(nullptr));
+ if (!hNewBmp)
+ {
+ mnWidth = 0;
+ mnHeight = 0;
+ return false;
+ }
+
+ mnWidth = nDX;
+ mnHeight = nDY;
+
+ SelectBitmap(getHDC(), hNewBmp);
+ mhBmp.reset(hNewBmp);
+
+ if (mpGraphics)
+ mpGraphics->GetImpl()->Init();
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
new file mode 100644
index 000000000..056540921
--- /dev/null
+++ b/vcl/win/gdi/winlayout.cxx
@@ -0,0 +1,635 @@
+
+/* -*- 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 <config_features.h>
+
+#include <memory>
+#include <osl/module.h>
+#include <osl/file.h>
+#include <sal/log.hxx>
+
+#include <comphelper/windowserrorstring.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <opengl/win/gdiimpl.hxx>
+#include <opengl/win/winlayout.hxx>
+
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <win/salgdi.h>
+#include <win/saldata.hxx>
+#include <win/wingdiimpl.hxx>
+#include <outdev.h>
+
+#include <win/DWriteTextRenderer.hxx>
+#include <win/scoped_gdi.hxx>
+
+#include <sft.hxx>
+#include <sallayout.hxx>
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <rtl/character.hxx>
+
+#include <boost/functional/hash.hpp>
+#include <algorithm>
+
+#include <shlwapi.h>
+#include <winver.h>
+
+GlobalWinGlyphCache * GlobalWinGlyphCache::get()
+{
+ SalData *data = GetSalData();
+ if (!data->m_pGlobalWinGlyphCache)
+ {
+ if (OpenGLHelper::isVCLOpenGLEnabled())
+ data->m_pGlobalWinGlyphCache.reset(new OpenGLGlobalWinGlyphCache);
+ }
+ return data->m_pGlobalWinGlyphCache.get();
+}
+
+bool WinFontInstance::CacheGlyphToAtlas(HDC hDC, HFONT hFont, int nGlyphIndex,
+ SalGraphics& rGraphics, const GenericSalLayout& rLayout)
+{
+ WinGlyphDrawElement aElement;
+
+ ScopedHDC aHDC(CreateCompatibleDC(hDC));
+
+ if (!aHDC)
+ {
+ SAL_WARN("vcl.gdi", "CreateCompatibleDC failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+
+ const HFONT hOrigFont = static_cast<HFONT>(SelectObject(aHDC.get(), hFont));
+ if (hOrigFont == nullptr)
+ {
+ SAL_WARN("vcl.gdi", "SelectObject failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+ const ::comphelper::ScopeGuard aHFONTrestoreScopeGuard(
+ [&aHDC,hOrigFont]() { SelectFont(aHDC.get(), hOrigFont); });
+
+ // For now we assume DWrite is present and we won't bother with fallback paths.
+ D2DWriteTextOutRenderer * pTxt = dynamic_cast<D2DWriteTextOutRenderer *>(&TextOutRenderer::get(true));
+ if (!pTxt)
+ return false;
+
+ pTxt->changeTextAntiAliasMode(D2DTextAntiAliasMode::AntiAliased);
+
+ if (!pTxt->BindFont(aHDC.get()))
+ {
+ SAL_WARN("vcl.gdi", "Binding of font failed. The font might not be supported by DirectWrite.");
+ return false;
+ }
+ const ::comphelper::ScopeGuard aFontReleaseScopeGuard([&pTxt]() { pTxt->ReleaseFont(); });
+
+ std::vector<WORD> aGlyphIndices(1);
+ aGlyphIndices[0] = nGlyphIndex;
+ // Fetch the ink boxes and calculate the size of the atlas.
+ tools::Rectangle bounds(0, 0, 0, 0);
+ auto aInkBoxes = pTxt->GetGlyphInkBoxes(aGlyphIndices.data(), aGlyphIndices.data() + 1);
+ if (aInkBoxes.empty())
+ return false;
+
+ for (auto &box : aInkBoxes)
+ bounds.Union(box + Point(bounds.Right(), 0));
+
+ // bounds.Top() is the offset from the baseline at (0,0) to the top of the
+ // inkbox.
+ aElement.mnBaselineOffset = -bounds.Top();
+ aElement.mnHeight = bounds.getHeight();
+ aElement.mbVertical = false;
+
+ // Try hard to avoid overlap as we want to be able to use
+ // individual rectangles for each glyph. The ABC widths don't
+ // take anti-aliasing into consideration. Let's hope that leaving
+ // "extra" space between glyphs will help.
+ std::vector<float> aGlyphAdv(1); // offsets between glyphs
+ std::vector<DWRITE_GLYPH_OFFSET> aGlyphOffset(1, {0.0f, 0.0f});
+ std::vector<int> aEnds(1); // end of each glyph box
+ float fHScale = getHScale();
+ float totWidth = 0;
+ {
+ int overhang = aInkBoxes[0].Left();
+ int blackWidth = aInkBoxes[0].getWidth() * fHScale; // width of non-AA pixels
+ aElement.maLeftOverhangs = overhang;
+
+ aGlyphAdv[0] = blackWidth + aElement.getExtraSpace();
+ aGlyphOffset[0].advanceOffset = -overhang;
+
+ totWidth += aGlyphAdv[0];
+ aEnds[0] = totWidth;
+ }
+ // Leave extra space also at top and bottom
+ int nBitmapWidth = totWidth;
+ int nBitmapHeight = bounds.getHeight() + aElement.getExtraSpace();
+
+ UINT nPos = 0;
+
+ aElement.maLocation.SetLeft(nPos);
+ aElement.maLocation.SetRight(aEnds[0]);
+ aElement.maLocation.SetTop(0);
+ aElement.maLocation.SetBottom(bounds.getHeight() + aElement.getExtraSpace());
+ nPos = aEnds[0];
+
+ std::unique_ptr<CompatibleDC> aDC(CompatibleDC::create(rGraphics, 0, 0, nBitmapWidth, nBitmapHeight));
+
+ SetTextColor(aDC->getCompatibleHDC(), RGB(0, 0, 0));
+ SetBkColor(aDC->getCompatibleHDC(), RGB(255, 255, 255));
+
+ aDC->fill(RGB(0xff, 0xff, 0xff));
+
+ pTxt->BindDC(aDC->getCompatibleHDC(), tools::Rectangle(0, 0, nBitmapWidth, nBitmapHeight));
+ auto pRT = pTxt->GetRenderTarget();
+
+ ID2D1SolidColorBrush* pBrush = nullptr;
+ if (!SUCCEEDED(pRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &pBrush)))
+ return false;
+
+ D2D1_POINT_2F baseline = {
+ static_cast<FLOAT>(aElement.getExtraOffset()),
+ static_cast<FLOAT>(aElement.getExtraOffset() + aElement.mnBaselineOffset)
+ };
+
+ DWRITE_GLYPH_RUN glyphs = {
+ pTxt->GetFontFace(),
+ pTxt->GetEmHeight(),
+ 1,
+ aGlyphIndices.data(),
+ aGlyphAdv.data(),
+ aGlyphOffset.data(),
+ false,
+ 0
+ };
+
+ WinFontTransformGuard aTransformGuard(pRT, fHScale, rLayout, baseline);
+ pRT->BeginDraw();
+ pRT->DrawGlyphRun(baseline, &glyphs, pBrush);
+ HRESULT hResult = pRT->EndDraw();
+
+ pBrush->Release();
+
+ switch (hResult)
+ {
+ case S_OK:
+ break;
+ case D2DERR_RECREATE_TARGET:
+ pTxt->CreateRenderTarget();
+ break;
+ default:
+ SAL_WARN("vcl.gdi", "DrawGlyphRun-EndDraw failed: " << WindowsErrorString(GetLastError()));
+ return false;
+ }
+
+ if (!GlobalWinGlyphCache::get()->AllocateTexture(aElement, aDC.get()))
+ return false;
+
+ maWinGlyphCache.PutDrawElementInCache(std::move(aElement), nGlyphIndex);
+
+ return true;
+}
+
+TextOutRenderer & TextOutRenderer::get(bool bUseDWrite)
+{
+ SalData *const pSalData = GetSalData();
+
+ if (!pSalData)
+ { // don't call this after DeInitVCL()
+ fprintf(stderr, "TextOutRenderer fatal error: no SalData");
+ abort();
+ }
+
+ if (bUseDWrite)
+ {
+ if (!pSalData->m_pD2DWriteTextOutRenderer)
+ {
+ pSalData->m_pD2DWriteTextOutRenderer.reset(new D2DWriteTextOutRenderer());
+ }
+ return *pSalData->m_pD2DWriteTextOutRenderer;
+ }
+ if (!pSalData->m_pExTextOutRenderer)
+ {
+ pSalData->m_pExTextOutRenderer.reset(new ExTextOutRenderer);
+ }
+ return *pSalData->m_pExTextOutRenderer;
+}
+
+
+bool ExTextOutRenderer::operator ()(GenericSalLayout const &rLayout,
+ SalGraphics & /*rGraphics*/,
+ HDC hDC)
+{
+ HFONT hFont = static_cast<HFONT>(GetCurrentObject( hDC, OBJ_FONT ));
+ ScopedHFONT hAltFont;
+ bool bUseAltFont = false;
+ bool bShift = false;
+ if (rLayout.GetFont().GetFontSelectPattern().mbVertical)
+ {
+ LOGFONTW aLogFont;
+ GetObjectW(hFont, sizeof(aLogFont), &aLogFont);
+ if (aLogFont.lfFaceName[0] == '@')
+ {
+ memmove(&aLogFont.lfFaceName[0], &aLogFont.lfFaceName[1],
+ sizeof(aLogFont.lfFaceName)-sizeof(aLogFont.lfFaceName[0]));
+ hAltFont.reset(CreateFontIndirectW(&aLogFont));
+ }
+ else
+ {
+ bShift = true;
+ aLogFont.lfEscapement += 2700;
+ aLogFont.lfOrientation = aLogFont.lfEscapement;
+ hAltFont.reset(CreateFontIndirectW(&aLogFont));
+ }
+ }
+
+ UINT nTextAlign = GetTextAlign ( hDC );
+ int nStart = 0;
+ Point aPos(0, 0);
+ const GlyphItem* pGlyph;
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ WORD glyphWStr[] = { pGlyph->glyphId() };
+ if (hAltFont && pGlyph->IsVertical() == bUseAltFont)
+ {
+ bUseAltFont = !bUseAltFont;
+ SelectFont(hDC, bUseAltFont ? hAltFont.get() : hFont);
+ }
+ if (bShift && pGlyph->IsVertical())
+ SetTextAlign(hDC, TA_TOP|TA_LEFT);
+
+ ExtTextOutW(hDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX, nullptr, LPCWSTR(&glyphWStr), 1, nullptr);
+
+ if (bShift && pGlyph->IsVertical())
+ SetTextAlign(hDC, nTextAlign);
+ }
+ if (hAltFont)
+ {
+ if (bUseAltFont)
+ SelectFont(hDC, hFont);
+ }
+
+ return true;
+}
+
+std::unique_ptr<GenericSalLayout> WinSalGraphics::GetTextLayout(int nFallbackLevel)
+{
+ assert(mpWinFontEntry[nFallbackLevel]);
+ if (!mpWinFontEntry[nFallbackLevel])
+ return nullptr;
+
+ assert(mpWinFontEntry[nFallbackLevel]->GetFontFace());
+
+ mpWinFontEntry[nFallbackLevel]->SetGraphics(this);
+ return std::make_unique<GenericSalLayout>(*mpWinFontEntry[nFallbackLevel]);
+}
+
+WinFontInstance::WinFontInstance(const WinFontFace& rPFF, const FontSelectPattern& rFSP)
+ : LogicalFontInstance(rPFF, rFSP)
+ , m_pGraphics(nullptr)
+ , m_hFont(nullptr)
+ , m_fScale(1.0f)
+{
+}
+
+WinFontInstance::~WinFontInstance()
+{
+ if (m_hFont)
+ ::DeleteFont(m_hFont);
+}
+
+bool WinFontInstance::hasHScale() const
+{
+ const FontSelectPattern &rPattern = GetFontSelectPattern();
+ int nHeight(rPattern.mnHeight);
+ int nWidth(rPattern.mnWidth ? rPattern.mnWidth * GetAverageWidthFactor() : nHeight);
+ return nWidth != nHeight;
+}
+
+float WinFontInstance::getHScale() const
+{
+ const FontSelectPattern& rPattern = GetFontSelectPattern();
+ int nHeight(rPattern.mnHeight);
+ if (!nHeight)
+ return 1.0;
+ float nWidth(rPattern.mnWidth ? rPattern.mnWidth * GetAverageWidthFactor() : nHeight);
+ return nWidth / nHeight;
+}
+
+namespace {
+
+struct BlobReference
+{
+ hb_blob_t* mpBlob;
+ BlobReference(hb_blob_t* pBlob) : mpBlob(pBlob)
+ {
+ hb_blob_reference(mpBlob);
+ }
+ BlobReference(BlobReference const & other)
+ : mpBlob(other.mpBlob)
+ {
+ hb_blob_reference(mpBlob);
+ }
+ ~BlobReference() { hb_blob_destroy(mpBlob); }
+};
+
+}
+
+using BlobCacheKey = std::pair<rtl::Reference<PhysicalFontFace>, hb_tag_t>;
+
+namespace {
+
+struct BlobCacheKeyHash
+{
+ std::size_t operator()(BlobCacheKey const& rKey) const
+ {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, rKey.first.get());
+ boost::hash_combine(seed, rKey.second);
+ return seed;
+ }
+};
+
+}
+
+static hb_blob_t* getFontTable(hb_face_t* /*face*/, hb_tag_t nTableTag, void* pUserData)
+{
+ static o3tl::lru_map<BlobCacheKey, BlobReference, BlobCacheKeyHash> gCache(50);
+
+ WinFontInstance* pFont = static_cast<WinFontInstance*>(pUserData);
+ HDC hDC = pFont->GetGraphics()->getHDC();
+ HFONT hFont = pFont->GetHFONT();
+ assert(hDC);
+ assert(hFont);
+
+ BlobCacheKey cacheKey { rtl::Reference<PhysicalFontFace>(pFont->GetFontFace()), nTableTag };
+ auto it = gCache.find(cacheKey);
+ if (it != gCache.end())
+ {
+ hb_blob_reference(it->second.mpBlob);
+ return it->second.mpBlob;
+ }
+
+ sal_uLong nLength = 0;
+ unsigned char* pBuffer = nullptr;
+
+ HGDIOBJ hOrigFont = SelectObject(hDC, hFont);
+ nLength = ::GetFontData(hDC, OSL_NETDWORD(nTableTag), 0, nullptr, 0);
+ if (nLength > 0 && nLength != GDI_ERROR)
+ {
+ pBuffer = new unsigned char[nLength];
+ ::GetFontData(hDC, OSL_NETDWORD(nTableTag), 0, pBuffer, nLength);
+ }
+ SelectObject(hDC, hOrigFont);
+
+ if (!pBuffer)
+ return nullptr;
+
+ hb_blob_t* pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY,
+ pBuffer, [](void* data){ delete[] static_cast<unsigned char*>(data); });
+ if (!pBlob)
+ return pBlob;
+ gCache.insert({cacheKey, BlobReference(pBlob)});
+ return pBlob;
+}
+
+hb_font_t* WinFontInstance::ImplInitHbFont()
+{
+ assert(m_pGraphics);
+ hb_font_t* pHbFont = InitHbFont(hb_face_create_for_tables(getFontTable, this, nullptr));
+
+ // Calculate the AverageWidthFactor, see LogicalFontInstance::GetScale().
+ if (GetFontSelectPattern().mnWidth)
+ {
+ double nUPEM = hb_face_get_upem(hb_font_get_face(pHbFont));
+
+ LOGFONTW aLogFont;
+ GetObjectW(m_hFont, sizeof(LOGFONTW), &aLogFont);
+
+ // Set the height (font size) to EM to minimize rounding errors.
+ aLogFont.lfHeight = -nUPEM;
+ // Set width to the default to get the original value in the metrics.
+ aLogFont.lfWidth = 0;
+
+ TEXTMETRICW aFontMetric;
+ {
+ // Get the font metrics.
+ HDC hDC = m_pGraphics->getHDC();
+ ScopedSelectedHFONT hFont(hDC, CreateFontIndirectW(&aLogFont));
+ GetTextMetricsW(hDC, &aFontMetric);
+ }
+
+ SetAverageWidthFactor(nUPEM / aFontMetric.tmAveCharWidth);
+ }
+
+ return pHbFont;
+}
+
+void WinFontInstance::SetGraphics(WinSalGraphics *pGraphics)
+{
+ m_pGraphics = pGraphics;
+ if (m_hFont)
+ return;
+ HFONT hOrigFont;
+ m_hFont = m_pGraphics->ImplDoSetFont(GetFontSelectPattern(), GetFontFace(), hOrigFont);
+ SelectObject(m_pGraphics->getHDC(), hOrigFont);
+}
+
+bool WinSalGraphics::CacheGlyphs(const GenericSalLayout& rLayout)
+{
+ static bool bDoGlyphCaching = (std::getenv("SAL_DISABLE_GLYPH_CACHING") == nullptr);
+ if (!bDoGlyphCaching)
+ return false;
+
+ if (rLayout.GetOrientation())
+ // Our caching is incomplete, skip it for non-horizontal text.
+ return false;
+
+ HDC hDC = getHDC();
+ WinFontInstance& rFont = *static_cast<WinFontInstance*>(&rLayout.GetFont());
+ HFONT hFONT = rFont.GetHFONT();
+
+ int nStart = 0;
+ Point aPos(0, 0);
+ const GlyphItem* pGlyph;
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ if (!rFont.GetWinGlyphCache().IsGlyphCached(pGlyph->glyphId()))
+ {
+ if (!rFont.CacheGlyphToAtlas(hDC, hFONT, pGlyph->glyphId(), *this, rLayout))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool WinSalGraphics::DrawCachedGlyphs(const GenericSalLayout& rLayout)
+{
+ HDC hDC = getHDC();
+
+ tools::Rectangle aRect;
+ rLayout.GetBoundRect(aRect);
+
+ COLORREF color = GetTextColor(hDC);
+ Color salColor(GetRValue(color), GetGValue(color), GetBValue(color));
+
+ WinSalGraphicsImplBase *pImpl = dynamic_cast<WinSalGraphicsImplBase*>(mpImpl.get());
+ if (!pImpl->UseTextDraw())
+ return false;
+
+ WinFontInstance& rFont = *static_cast<WinFontInstance*>(&rLayout.GetFont());
+
+ int nStart = 0;
+ Point aPos(0, 0);
+ const GlyphItem* pGlyph;
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ WinGlyphDrawElement& rElement(rFont.GetWinGlyphCache().GetDrawElement(pGlyph->glyphId()));
+ const CompatibleDC::Texture* texture = rElement.maTexture.get();
+
+ if (!texture || !texture->isValid())
+ return false;
+
+ SalTwoRect a2Rects(0, 0,
+ texture->GetWidth(), texture->GetHeight(),
+ aPos.X() - rElement.getExtraOffset() + rElement.maLeftOverhangs,
+ aPos.Y() - rElement.mnBaselineOffset - rElement.getExtraOffset(),
+ texture->GetWidth(), texture->GetHeight());
+
+ pImpl->DeferredTextDraw(texture, salColor, a2Rects);
+ }
+
+ return true;
+}
+
+static void PruneGlyphCache()
+{
+ GlobalWinGlyphCache::get()->Prune();
+}
+
+void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout, HDC hDC, bool bUseDWrite)
+{
+ TextOutRenderer &render = TextOutRenderer::get(bUseDWrite);
+ render(rLayout, *this, hDC);
+}
+
+void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
+{
+ WinSalGraphicsImplBase* pImpl = dynamic_cast<WinSalGraphicsImplBase*>(mpImpl.get());
+ if( !mbPrinter && pImpl->DrawTextLayout(rLayout))
+ return; // handled by pImpl
+
+ HDC hDC = getHDC();
+ const WinFontInstance* pWinFont = static_cast<const WinFontInstance*>(&rLayout.GetFont());
+ const HFONT hLayoutFont = pWinFont->GetHFONT();
+ bool bUseClassic = !pImpl->UseTextDraw() || mbPrinter;
+
+ // Our DirectWrite renderer is incomplete, skip it for vertical text where glyphs are not
+ // rotated.
+ bool bForceGDI = rLayout.GetFont().GetFontSelectPattern().mbVertical;
+
+ if (bUseClassic)
+ {
+ // no OpenGL, just classic rendering
+ const HFONT hOrigFont = ::SelectFont(hDC, hLayoutFont);
+ DrawTextLayout(rLayout, hDC, false);
+ ::SelectFont(hDC, hOrigFont);
+ }
+ // if we can't draw the cached OpenGL glyphs, try to draw a full OpenGL layout
+ else if (!bForceGDI && CacheGlyphs(rLayout) && DrawCachedGlyphs(rLayout))
+ {
+ PruneGlyphCache();
+ }
+ else
+ {
+ PruneGlyphCache(); // prune the cache from the failed calls above
+
+ // We have to render the text to a hidden texture, and draw it.
+ //
+ // Note that Windows GDI does not really support the alpha correctly
+ // when drawing - ie. it draws nothing to the alpha channel when
+ // rendering the text, even the antialiasing is done as 'real' pixels,
+ // not alpha...
+ //
+ // Luckily, this does not really limit us:
+ //
+ // To blend properly, we draw the texture, but then use it as an alpha
+ // channel for solid color (that will define the text color). This
+ // destroys the subpixel antialiasing - turns it into 'classic'
+ // antialiasing - but that is the best we can do, because the subpixel
+ // antialiasing needs to know what is in the background: When the
+ // background is white, or white-ish, it does the subpixel, but when
+ // there is a color, it just darkens the color (and does this even
+ // when part of the character is on a colored background, and part on
+ // white). It has to work this way, the results would look strange
+ // otherwise.
+ //
+ // For the GL rendering to work even with the subpixel antialiasing,
+ // we would need to get the current texture from the screen, let GDI
+ // draw the text to it (so that it can decide well where to use the
+ // subpixel and where not), and draw the result - but in that case we
+ // don't need alpha anyway.
+ //
+ // TODO: check the performance of this 2nd approach at some stage and
+ // switch to that if it performs well.
+
+ tools::Rectangle aRect;
+ rLayout.GetBoundRect(aRect);
+ if( aRect.IsEmpty())
+ return;
+
+ pImpl->PreDrawText();
+
+ std::unique_ptr<CompatibleDC> aDC(CompatibleDC::create(*this, aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight()));
+
+ // we are making changes to the DC, make sure we got a new one
+ assert(aDC->getCompatibleHDC() != hDC);
+
+ RECT aWinRect = { aRect.Left(), aRect.Top(), aRect.Left() + aRect.GetWidth(), aRect.Top() + aRect.GetHeight() };
+ ::FillRect(aDC->getCompatibleHDC(), &aWinRect, static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH)));
+
+ // setup the hidden DC with black color and white background, we will
+ // use the result of the text drawing later as a mask only
+ const HFONT hOrigFont = ::SelectFont(aDC->getCompatibleHDC(), hLayoutFont);
+
+ ::SetTextColor(aDC->getCompatibleHDC(), RGB(0, 0, 0));
+ ::SetBkColor(aDC->getCompatibleHDC(), RGB(255, 255, 255));
+
+ UINT nTextAlign = ::GetTextAlign(hDC);
+ ::SetTextAlign(aDC->getCompatibleHDC(), nTextAlign);
+
+ COLORREF color = ::GetTextColor(hDC);
+ Color salColor(GetRValue(color), GetGValue(color), GetBValue(color));
+
+ // the actual drawing
+ DrawTextLayout(rLayout, aDC->getCompatibleHDC(), !bForceGDI);
+
+ std::unique_ptr<CompatibleDC::Texture> xTexture(aDC->getAsMaskTexture());
+ if (xTexture)
+ pImpl->DrawTextMask(xTexture.get(), salColor, aDC->getTwoRect());
+
+ ::SelectFont(aDC->getCompatibleHDC(), hOrigFont);
+
+ pImpl->PostDrawText();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/src/50.bmp b/vcl/win/src/50.bmp
new file mode 100644
index 000000000..b9d56fcd1
--- /dev/null
+++ b/vcl/win/src/50.bmp
Binary files differ
diff --git a/vcl/win/src/50.png b/vcl/win/src/50.png
new file mode 100644
index 000000000..8517d965f
--- /dev/null
+++ b/vcl/win/src/50.png
Binary files differ
diff --git a/vcl/win/src/ase.cur b/vcl/win/src/ase.cur
new file mode 100644
index 000000000..7634a7d34
--- /dev/null
+++ b/vcl/win/src/ase.cur
Binary files differ
diff --git a/vcl/win/src/asn.cur b/vcl/win/src/asn.cur
new file mode 100644
index 000000000..e444e42bf
--- /dev/null
+++ b/vcl/win/src/asn.cur
Binary files differ
diff --git a/vcl/win/src/asne.cur b/vcl/win/src/asne.cur
new file mode 100644
index 000000000..e92cc65e7
--- /dev/null
+++ b/vcl/win/src/asne.cur
Binary files differ
diff --git a/vcl/win/src/asns.cur b/vcl/win/src/asns.cur
new file mode 100644
index 000000000..04d0b09c3
--- /dev/null
+++ b/vcl/win/src/asns.cur
Binary files differ
diff --git a/vcl/win/src/asnswe.cur b/vcl/win/src/asnswe.cur
new file mode 100644
index 000000000..a0e25b16d
--- /dev/null
+++ b/vcl/win/src/asnswe.cur
Binary files differ
diff --git a/vcl/win/src/asnw.cur b/vcl/win/src/asnw.cur
new file mode 100644
index 000000000..20322bc97
--- /dev/null
+++ b/vcl/win/src/asnw.cur
Binary files differ
diff --git a/vcl/win/src/ass.cur b/vcl/win/src/ass.cur
new file mode 100644
index 000000000..7166636a1
--- /dev/null
+++ b/vcl/win/src/ass.cur
Binary files differ
diff --git a/vcl/win/src/asse.cur b/vcl/win/src/asse.cur
new file mode 100644
index 000000000..8cb71234b
--- /dev/null
+++ b/vcl/win/src/asse.cur
Binary files differ
diff --git a/vcl/win/src/assw.cur b/vcl/win/src/assw.cur
new file mode 100644
index 000000000..46ee06d16
--- /dev/null
+++ b/vcl/win/src/assw.cur
Binary files differ
diff --git a/vcl/win/src/asw.cur b/vcl/win/src/asw.cur
new file mode 100644
index 000000000..0ccac50f4
--- /dev/null
+++ b/vcl/win/src/asw.cur
Binary files differ
diff --git a/vcl/win/src/aswe.cur b/vcl/win/src/aswe.cur
new file mode 100644
index 000000000..c238b7e10
--- /dev/null
+++ b/vcl/win/src/aswe.cur
Binary files differ
diff --git a/vcl/win/src/chain.cur b/vcl/win/src/chain.cur
new file mode 100644
index 000000000..02abb7ab7
--- /dev/null
+++ b/vcl/win/src/chain.cur
Binary files differ
diff --git a/vcl/win/src/chainnot.cur b/vcl/win/src/chainnot.cur
new file mode 100644
index 000000000..938ece03f
--- /dev/null
+++ b/vcl/win/src/chainnot.cur
Binary files differ
diff --git a/vcl/win/src/chart.cur b/vcl/win/src/chart.cur
new file mode 100644
index 000000000..25fe85b76
--- /dev/null
+++ b/vcl/win/src/chart.cur
Binary files differ
diff --git a/vcl/win/src/copydata.cur b/vcl/win/src/copydata.cur
new file mode 100644
index 000000000..d3c4bc93a
--- /dev/null
+++ b/vcl/win/src/copydata.cur
Binary files differ
diff --git a/vcl/win/src/copydlnk.cur b/vcl/win/src/copydlnk.cur
new file mode 100644
index 000000000..495fd5e17
--- /dev/null
+++ b/vcl/win/src/copydlnk.cur
Binary files differ
diff --git a/vcl/win/src/copyf.cur b/vcl/win/src/copyf.cur
new file mode 100644
index 000000000..450c09443
--- /dev/null
+++ b/vcl/win/src/copyf.cur
Binary files differ
diff --git a/vcl/win/src/copyf2.cur b/vcl/win/src/copyf2.cur
new file mode 100644
index 000000000..ac8de5da6
--- /dev/null
+++ b/vcl/win/src/copyf2.cur
Binary files differ
diff --git a/vcl/win/src/copyflnk.cur b/vcl/win/src/copyflnk.cur
new file mode 100644
index 000000000..e67f0539f
--- /dev/null
+++ b/vcl/win/src/copyflnk.cur
Binary files differ
diff --git a/vcl/win/src/crook.cur b/vcl/win/src/crook.cur
new file mode 100644
index 000000000..c40cf591e
--- /dev/null
+++ b/vcl/win/src/crook.cur
Binary files differ
diff --git a/vcl/win/src/crop.cur b/vcl/win/src/crop.cur
new file mode 100644
index 000000000..327fb0697
--- /dev/null
+++ b/vcl/win/src/crop.cur
Binary files differ
diff --git a/vcl/win/src/darc.cur b/vcl/win/src/darc.cur
new file mode 100644
index 000000000..38504fa23
--- /dev/null
+++ b/vcl/win/src/darc.cur
Binary files differ
diff --git a/vcl/win/src/dbezier.cur b/vcl/win/src/dbezier.cur
new file mode 100644
index 000000000..f630b837d
--- /dev/null
+++ b/vcl/win/src/dbezier.cur
Binary files differ
diff --git a/vcl/win/src/dcapt.cur b/vcl/win/src/dcapt.cur
new file mode 100644
index 000000000..10dd5ba0d
--- /dev/null
+++ b/vcl/win/src/dcapt.cur
Binary files differ
diff --git a/vcl/win/src/dcirccut.cur b/vcl/win/src/dcirccut.cur
new file mode 100644
index 000000000..b19d3f825
--- /dev/null
+++ b/vcl/win/src/dcirccut.cur
Binary files differ
diff --git a/vcl/win/src/dconnect.cur b/vcl/win/src/dconnect.cur
new file mode 100644
index 000000000..5318d8f22
--- /dev/null
+++ b/vcl/win/src/dconnect.cur
Binary files differ
diff --git a/vcl/win/src/dellipse.cur b/vcl/win/src/dellipse.cur
new file mode 100644
index 000000000..c489a6403
--- /dev/null
+++ b/vcl/win/src/dellipse.cur
Binary files differ
diff --git a/vcl/win/src/detectiv.cur b/vcl/win/src/detectiv.cur
new file mode 100644
index 000000000..30e5685b6
--- /dev/null
+++ b/vcl/win/src/detectiv.cur
Binary files differ
diff --git a/vcl/win/src/dfree.cur b/vcl/win/src/dfree.cur
new file mode 100644
index 000000000..3ff56d007
--- /dev/null
+++ b/vcl/win/src/dfree.cur
Binary files differ
diff --git a/vcl/win/src/dline.cur b/vcl/win/src/dline.cur
new file mode 100644
index 000000000..623c33ac2
--- /dev/null
+++ b/vcl/win/src/dline.cur
Binary files differ
diff --git a/vcl/win/src/dpie.cur b/vcl/win/src/dpie.cur
new file mode 100644
index 000000000..3b911cd01
--- /dev/null
+++ b/vcl/win/src/dpie.cur
Binary files differ
diff --git a/vcl/win/src/dpolygon.cur b/vcl/win/src/dpolygon.cur
new file mode 100644
index 000000000..9467f1e28
--- /dev/null
+++ b/vcl/win/src/dpolygon.cur
Binary files differ
diff --git a/vcl/win/src/drect.cur b/vcl/win/src/drect.cur
new file mode 100644
index 000000000..60a5242c2
--- /dev/null
+++ b/vcl/win/src/drect.cur
Binary files differ
diff --git a/vcl/win/src/dtext.cur b/vcl/win/src/dtext.cur
new file mode 100644
index 000000000..01e7d31ea
--- /dev/null
+++ b/vcl/win/src/dtext.cur
Binary files differ
diff --git a/vcl/win/src/fill.cur b/vcl/win/src/fill.cur
new file mode 100644
index 000000000..78f5fad87
--- /dev/null
+++ b/vcl/win/src/fill.cur
Binary files differ
diff --git a/vcl/win/src/hshear.cur b/vcl/win/src/hshear.cur
new file mode 100644
index 000000000..5cf221145
--- /dev/null
+++ b/vcl/win/src/hshear.cur
Binary files differ
diff --git a/vcl/win/src/linkdata.cur b/vcl/win/src/linkdata.cur
new file mode 100644
index 000000000..e47c1dea2
--- /dev/null
+++ b/vcl/win/src/linkdata.cur
Binary files differ
diff --git a/vcl/win/src/linkf.cur b/vcl/win/src/linkf.cur
new file mode 100644
index 000000000..6cc498a02
--- /dev/null
+++ b/vcl/win/src/linkf.cur
Binary files differ
diff --git a/vcl/win/src/magnify.cur b/vcl/win/src/magnify.cur
new file mode 100644
index 000000000..1e32b9235
--- /dev/null
+++ b/vcl/win/src/magnify.cur
Binary files differ
diff --git a/vcl/win/src/mirror.cur b/vcl/win/src/mirror.cur
new file mode 100644
index 000000000..e05eb836e
--- /dev/null
+++ b/vcl/win/src/mirror.cur
Binary files differ
diff --git a/vcl/win/src/movebw.cur b/vcl/win/src/movebw.cur
new file mode 100644
index 000000000..d079eb9fe
--- /dev/null
+++ b/vcl/win/src/movebw.cur
Binary files differ
diff --git a/vcl/win/src/movedata.cur b/vcl/win/src/movedata.cur
new file mode 100644
index 000000000..4d67cbe47
--- /dev/null
+++ b/vcl/win/src/movedata.cur
Binary files differ
diff --git a/vcl/win/src/movedlnk.cur b/vcl/win/src/movedlnk.cur
new file mode 100644
index 000000000..1bb7b0306
--- /dev/null
+++ b/vcl/win/src/movedlnk.cur
Binary files differ
diff --git a/vcl/win/src/movef.cur b/vcl/win/src/movef.cur
new file mode 100644
index 000000000..6abee2381
--- /dev/null
+++ b/vcl/win/src/movef.cur
Binary files differ
diff --git a/vcl/win/src/movef2.cur b/vcl/win/src/movef2.cur
new file mode 100644
index 000000000..d044981a3
--- /dev/null
+++ b/vcl/win/src/movef2.cur
Binary files differ
diff --git a/vcl/win/src/moveflnk.cur b/vcl/win/src/moveflnk.cur
new file mode 100644
index 000000000..630fa1bc3
--- /dev/null
+++ b/vcl/win/src/moveflnk.cur
Binary files differ
diff --git a/vcl/win/src/movept.cur b/vcl/win/src/movept.cur
new file mode 100644
index 000000000..81d3af5a0
--- /dev/null
+++ b/vcl/win/src/movept.cur
Binary files differ
diff --git a/vcl/win/src/nullptr.cur b/vcl/win/src/nullptr.cur
new file mode 100644
index 000000000..28dbb2a90
--- /dev/null
+++ b/vcl/win/src/nullptr.cur
Binary files differ
diff --git a/vcl/win/src/pivotcol.cur b/vcl/win/src/pivotcol.cur
new file mode 100644
index 000000000..061b3ba92
--- /dev/null
+++ b/vcl/win/src/pivotcol.cur
Binary files differ
diff --git a/vcl/win/src/pivotdel.cur b/vcl/win/src/pivotdel.cur
new file mode 100644
index 000000000..4497dacd9
--- /dev/null
+++ b/vcl/win/src/pivotdel.cur
Binary files differ
diff --git a/vcl/win/src/pivotfld.cur b/vcl/win/src/pivotfld.cur
new file mode 100644
index 000000000..efbbead89
--- /dev/null
+++ b/vcl/win/src/pivotfld.cur
Binary files differ
diff --git a/vcl/win/src/pivotrow.cur b/vcl/win/src/pivotrow.cur
new file mode 100644
index 000000000..649444e9e
--- /dev/null
+++ b/vcl/win/src/pivotrow.cur
Binary files differ
diff --git a/vcl/win/src/rotate.cur b/vcl/win/src/rotate.cur
new file mode 100644
index 000000000..43c2a54a1
--- /dev/null
+++ b/vcl/win/src/rotate.cur
Binary files differ
diff --git a/vcl/win/src/salsrc.rc b/vcl/win/src/salsrc.rc
new file mode 100644
index 000000000..a4c5cf574
--- /dev/null
+++ b/vcl/win/src/salsrc.rc
@@ -0,0 +1,89 @@
+/* -*- Mode: Fundamental; 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 <win/salids.hrc>
+
+SAL_RESID_POINTER_NULL CURSOR nullptr.cur
+SAL_RESID_POINTER_MAGNIFY CURSOR magnify.cur
+SAL_RESID_POINTER_FILL CURSOR fill.cur
+SAL_RESID_POINTER_ROTATE CURSOR rotate.cur
+SAL_RESID_POINTER_HSHEAR CURSOR hshear.cur
+SAL_RESID_POINTER_VSHEAR CURSOR vshear.cur
+SAL_RESID_POINTER_MIRROR CURSOR mirror.cur
+SAL_RESID_POINTER_CROOK CURSOR crook.cur
+SAL_RESID_POINTER_CROP CURSOR crop.cur
+SAL_RESID_POINTER_MOVEPOINT CURSOR movept.cur
+SAL_RESID_POINTER_MOVEBEZIERWEIGHT CURSOR movebw.cur
+SAL_RESID_POINTER_MOVEDATA CURSOR movedata.cur
+SAL_RESID_POINTER_COPYDATA CURSOR copydata.cur
+SAL_RESID_POINTER_LINKDATA CURSOR linkdata.cur
+SAL_RESID_POINTER_MOVEDATALINK CURSOR movedlnk.cur
+SAL_RESID_POINTER_COPYDATALINK CURSOR copydlnk.cur
+SAL_RESID_POINTER_MOVEFILE CURSOR movef.cur
+SAL_RESID_POINTER_COPYFILE CURSOR copyf.cur
+SAL_RESID_POINTER_LINKFILE CURSOR linkf.cur
+SAL_RESID_POINTER_MOVEFILELINK CURSOR moveflnk.cur
+SAL_RESID_POINTER_COPYFILELINK CURSOR copyflnk.cur
+SAL_RESID_POINTER_MOVEFILES CURSOR movef2.cur
+SAL_RESID_POINTER_COPYFILES CURSOR copyf2.cur
+SAL_RESID_POINTER_DRAW_LINE CURSOR dline.cur
+SAL_RESID_POINTER_DRAW_RECT CURSOR drect.cur
+SAL_RESID_POINTER_DRAW_POLYGON CURSOR dpolygon.cur
+SAL_RESID_POINTER_DRAW_BEZIER CURSOR dbezier.cur
+SAL_RESID_POINTER_DRAW_ARC CURSOR darc.cur
+SAL_RESID_POINTER_DRAW_PIE CURSOR dpie.cur
+SAL_RESID_POINTER_DRAW_CIRCLECUT CURSOR dcirccut.cur
+SAL_RESID_POINTER_DRAW_ELLIPSE CURSOR dellipse.cur
+SAL_RESID_POINTER_DRAW_FREEHAND CURSOR dfree.cur
+SAL_RESID_POINTER_DRAW_CONNECT CURSOR dconnect.cur
+SAL_RESID_POINTER_DRAW_TEXT CURSOR dtext.cur
+SAL_RESID_POINTER_DRAW_CAPTION CURSOR dcapt.cur
+SAL_RESID_POINTER_CHART CURSOR chart.cur
+SAL_RESID_POINTER_DETECTIVE CURSOR detectiv.cur
+SAL_RESID_POINTER_PIVOT_COL CURSOR pivotcol.cur
+SAL_RESID_POINTER_PIVOT_ROW CURSOR pivotrow.cur
+SAL_RESID_POINTER_PIVOT_FIELD CURSOR pivotfld.cur
+SAL_RESID_POINTER_PIVOT_DELETE CURSOR pivotdel.cur
+SAL_RESID_POINTER_CHAIN CURSOR chain.cur
+SAL_RESID_POINTER_CHAIN_NOTALLOWED CURSOR chainnot.cur
+SAL_RESID_POINTER_AUTOSCROLL_N CURSOR asn.cur
+SAL_RESID_POINTER_AUTOSCROLL_S CURSOR ass.cur
+SAL_RESID_POINTER_AUTOSCROLL_W CURSOR asw.cur
+SAL_RESID_POINTER_AUTOSCROLL_E CURSOR ase.cur
+SAL_RESID_POINTER_AUTOSCROLL_NW CURSOR asnw.cur
+SAL_RESID_POINTER_AUTOSCROLL_NE CURSOR asne.cur
+SAL_RESID_POINTER_AUTOSCROLL_SW CURSOR assw.cur
+SAL_RESID_POINTER_AUTOSCROLL_SE CURSOR asse.cur
+SAL_RESID_POINTER_AUTOSCROLL_NS CURSOR asns.cur
+SAL_RESID_POINTER_AUTOSCROLL_WE CURSOR aswe.cur
+SAL_RESID_POINTER_AUTOSCROLL_NSWE CURSOR asnswe.cur
+SAL_RESID_POINTER_TEXT_VERTICAL CURSOR vtext.cur
+SAL_RESID_POINTER_TAB_SELECT_S CURSOR tblsels.cur
+SAL_RESID_POINTER_TAB_SELECT_E CURSOR tblsele.cur
+SAL_RESID_POINTER_TAB_SELECT_SE CURSOR tblselse.cur
+SAL_RESID_POINTER_TAB_SELECT_W CURSOR tblselw.cur
+SAL_RESID_POINTER_TAB_SELECT_SW CURSOR tblselsw.cur
+SAL_RESID_POINTER_HIDEWHITESPACE CURSOR wshide.cur
+SAL_RESID_POINTER_SHOWWHITESPACE CURSOR wsshow.cur
+
+SAL_RESID_BITMAP_50 BITMAP "50.bmp"
+
+SAL_RESID_ICON_DEFAULT ICON sd.ico
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/src/sd.ico b/vcl/win/src/sd.ico
new file mode 100644
index 000000000..b2a0a07a6
--- /dev/null
+++ b/vcl/win/src/sd.ico
Binary files differ
diff --git a/vcl/win/src/tblsele.cur b/vcl/win/src/tblsele.cur
new file mode 100644
index 000000000..3683e20df
--- /dev/null
+++ b/vcl/win/src/tblsele.cur
Binary files differ
diff --git a/vcl/win/src/tblsels.cur b/vcl/win/src/tblsels.cur
new file mode 100644
index 000000000..007182d73
--- /dev/null
+++ b/vcl/win/src/tblsels.cur
Binary files differ
diff --git a/vcl/win/src/tblselse.cur b/vcl/win/src/tblselse.cur
new file mode 100644
index 000000000..986f01395
--- /dev/null
+++ b/vcl/win/src/tblselse.cur
Binary files differ
diff --git a/vcl/win/src/tblselsw.cur b/vcl/win/src/tblselsw.cur
new file mode 100644
index 000000000..adabba1a2
--- /dev/null
+++ b/vcl/win/src/tblselsw.cur
Binary files differ
diff --git a/vcl/win/src/tblselw.cur b/vcl/win/src/tblselw.cur
new file mode 100644
index 000000000..a95eb85af
--- /dev/null
+++ b/vcl/win/src/tblselw.cur
Binary files differ
diff --git a/vcl/win/src/vshear.cur b/vcl/win/src/vshear.cur
new file mode 100644
index 000000000..a4bbf7e8e
--- /dev/null
+++ b/vcl/win/src/vshear.cur
Binary files differ
diff --git a/vcl/win/src/vtext.cur b/vcl/win/src/vtext.cur
new file mode 100644
index 000000000..776177901
--- /dev/null
+++ b/vcl/win/src/vtext.cur
Binary files differ
diff --git a/vcl/win/src/wshide.cur b/vcl/win/src/wshide.cur
new file mode 100644
index 000000000..bfa8fdfdb
--- /dev/null
+++ b/vcl/win/src/wshide.cur
Binary files differ
diff --git a/vcl/win/src/wsshow.cur b/vcl/win/src/wsshow.cur
new file mode 100644
index 000000000..e0c210603
--- /dev/null
+++ b/vcl/win/src/wsshow.cur
Binary files differ
diff --git a/vcl/win/window/keynames.cxx b/vcl/win/window/keynames.cxx
new file mode 100644
index 000000000..0d4f12f82
--- /dev/null
+++ b/vcl/win/window/keynames.cxx
@@ -0,0 +1,224 @@
+/* -*- 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 <string.h>
+#include <rtl/ustring.hxx>
+#include <sal/macros.h>
+
+#include <win/salframe.h>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+// Use unique ;) names to avoid clashes with the KEY_* (especially
+// KEY_SHIFT) from <vcl/vclenum.hxx>
+
+#define PAPUGA_KEY_ESC 0x10000
+#define PAPUGA_KEY_BACK 0xE0000
+#define PAPUGA_KEY_ENTER 0x1C0000
+#define PAPUGA_KEY_SPACEBAR 0x390000
+#define PAPUGA_KEY_HOME 0x1470000
+#define PAPUGA_KEY_UP 0x1480000
+#define PAPUGA_KEY_PAGEUP 0x1490000
+#define PAPUGA_KEY_LEFT 0x14B0000
+#define PAPUGA_KEY_RIGHT 0x14D0000
+#define PAPUGA_KEY_END 0x14F0000
+#define PAPUGA_KEY_DOWN 0x1500000
+#define PAPUGA_KEY_PAGEDOWN 0x1510000
+#define PAPUGA_KEY_INSERT 0x1520000
+#define PAPUGA_KEY_DELETE 0x1530000
+#define PAPUGA_KEY_CONTROL 0x21D0000
+#define PAPUGA_KEY_SHIFT 0x22A0000
+#define PAPUGA_KEY_ALT 0x2380000
+
+namespace vcl_sal {
+
+ namespace {
+
+ struct KeysNameReplacement
+ {
+ LONG aSymbol;
+ const char* pName;
+ };
+
+ struct KeyboardReplacements
+ {
+ const char* pLangName;
+ const KeysNameReplacement* pReplacements;
+ int nReplacements;
+ };
+
+ }
+
+ // CAUTION CAUTION CAUTION
+ // Every string value in the replacements tables must be in UTF-8
+ // but with the UTF-8 bytes encoded, not as such! Be careful!
+
+ static const struct KeysNameReplacement aImplReplacements_Asturian[] =
+ {
+ { PAPUGA_KEY_BACK, "Retrocesu" },
+ { PAPUGA_KEY_ENTER, "Intro" },
+ { PAPUGA_KEY_SPACEBAR, "Espaciu" },
+ { PAPUGA_KEY_HOME, "Aniciu" },
+ { PAPUGA_KEY_UP, "Arriba" },
+ { PAPUGA_KEY_PAGEUP, "Re P\xc3\xa1" "x" },
+ { PAPUGA_KEY_LEFT, "Izquierda" },
+ { PAPUGA_KEY_RIGHT, "Drecha" },
+ { PAPUGA_KEY_END, "Fin" },
+ { PAPUGA_KEY_DOWN, "Abaxo" },
+ { PAPUGA_KEY_PAGEDOWN, "Av P\xc3\xa1" "x" },
+ { PAPUGA_KEY_INSERT, "Ins" },
+ { PAPUGA_KEY_DELETE, "Supr" },
+ { PAPUGA_KEY_SHIFT, "May\xc3\xba" "s" },
+ };
+
+ static const struct KeysNameReplacement aImplReplacements_Catalan[] =
+ {
+ { PAPUGA_KEY_BACK, "Retroc\xc3\xa9" "s" },
+ { PAPUGA_KEY_ENTER, "Retorn" },
+ { PAPUGA_KEY_SPACEBAR, "Espai" },
+ { PAPUGA_KEY_HOME, "Inici" },
+ { PAPUGA_KEY_UP, "Amunt" },
+ { PAPUGA_KEY_PAGEUP, "Re P\xc3\xa0" "g" },
+ { PAPUGA_KEY_LEFT, "Esquerra" },
+ { PAPUGA_KEY_RIGHT, "Dreta" },
+ { PAPUGA_KEY_END, "Fi" },
+ { PAPUGA_KEY_DOWN, "Avall" },
+ { PAPUGA_KEY_PAGEDOWN, "Av P\xc3\xa0" "g" },
+ { PAPUGA_KEY_INSERT, "Ins" },
+ { PAPUGA_KEY_DELETE, "Supr" },
+ { PAPUGA_KEY_SHIFT, "Maj" },
+ };
+
+ static const struct KeysNameReplacement aImplReplacements_Estonian[] =
+ {
+ { PAPUGA_KEY_RIGHT, "Nool paremale" },
+ { PAPUGA_KEY_LEFT, "Nool vasakule" },
+ { PAPUGA_KEY_UP, "Nool \xc3\xbc" "les" },
+ { PAPUGA_KEY_DOWN, "Nool alla" },
+ { PAPUGA_KEY_BACK, "Tagasil\xc3\xbc" "ke" },
+ { PAPUGA_KEY_ENTER, "Enter" },
+ { PAPUGA_KEY_SPACEBAR, "T\xc3\xbc" "hik" },
+ };
+
+ static const struct KeysNameReplacement aImplReplacements_Lithuanian[] =
+ {
+ { PAPUGA_KEY_ESC, "Gr" },
+ { PAPUGA_KEY_BACK, "Naikinti" },
+ { PAPUGA_KEY_ENTER, "\xc4\xae" "vesti" },
+ { PAPUGA_KEY_SPACEBAR, "Tarpas" },
+ { PAPUGA_KEY_HOME, "Prad" },
+ { PAPUGA_KEY_UP, "Auk\xc5\xa1" "tyn" },
+ { PAPUGA_KEY_PAGEUP, "Psl\xe2\x86\x91" },
+ { PAPUGA_KEY_LEFT, "Kair\xc4\x97" "n" },
+ { PAPUGA_KEY_RIGHT, "De\xc5\xa1" "in\xc4\x97" "n" },
+ { PAPUGA_KEY_END, "Pab" },
+ { PAPUGA_KEY_DOWN, "\xc5\xbd" "emyn" },
+ { PAPUGA_KEY_PAGEDOWN, "Psl\xe2\x86\x93" },
+ { PAPUGA_KEY_INSERT, "\xc4\xae" "terpti" },
+ { PAPUGA_KEY_DELETE, "\xc5\xa0" "al" },
+ { PAPUGA_KEY_CONTROL, "Vald" },
+ { PAPUGA_KEY_SHIFT, "Lyg2" },
+ { PAPUGA_KEY_ALT, "Alt" },
+ };
+
+ static const struct KeysNameReplacement aImplReplacements_Slovenian[] =
+ {
+ { PAPUGA_KEY_ESC, "Ube\xc5\xbe" "nica" },
+ { PAPUGA_KEY_BACK, "Vra\xc4\x8d" "alka" },
+ { PAPUGA_KEY_ENTER, "Vna\xc5\xa1" "alka" },
+ { PAPUGA_KEY_SPACEBAR, "Preslednica" },
+ { PAPUGA_KEY_HOME, "Za\xc4\x8d" "etek" },
+ { PAPUGA_KEY_UP, "Navzgor" },
+ { PAPUGA_KEY_PAGEUP, "Prej\xc5\xa1" "nja stran" },
+ { PAPUGA_KEY_LEFT, "Levo" },
+ { PAPUGA_KEY_RIGHT, "Desno" },
+ { PAPUGA_KEY_END, "Konec" },
+ { PAPUGA_KEY_DOWN, "Navzdol" },
+ { PAPUGA_KEY_PAGEDOWN, "Naslednja stran" },
+ { PAPUGA_KEY_INSERT, "Vrivalka" },
+ { PAPUGA_KEY_DELETE, "Brisalka" },
+ { PAPUGA_KEY_CONTROL, "Krmilka" },
+ { PAPUGA_KEY_SHIFT, "Dvigalka" },
+ { PAPUGA_KEY_ALT, "Izmenjalka" },
+ };
+
+ static const struct KeysNameReplacement aImplReplacements_Spanish[] =
+ {
+ { PAPUGA_KEY_BACK, "Retroceso" },
+ { PAPUGA_KEY_ENTER, "Intro" },
+ { PAPUGA_KEY_SPACEBAR, "Espacio" },
+ { PAPUGA_KEY_HOME, "Inicio" },
+ { PAPUGA_KEY_UP, "Arriba" },
+ { PAPUGA_KEY_PAGEUP, "Re P\xc3\xa1" "g" },
+ { PAPUGA_KEY_LEFT, "Izquierda" },
+ { PAPUGA_KEY_RIGHT, "Derecha" },
+ { PAPUGA_KEY_END, "Fin" },
+ { PAPUGA_KEY_DOWN, "Abajo" },
+ { PAPUGA_KEY_PAGEDOWN, "Av P\xc3\xa1" "g" },
+ { PAPUGA_KEY_INSERT, "Ins" },
+ { PAPUGA_KEY_DELETE, "Supr" },
+ { PAPUGA_KEY_SHIFT, "May\xc3\xba" "s" },
+ };
+
+ static const struct KeysNameReplacement aImplReplacements_Hungarian[] =
+ {
+ { PAPUGA_KEY_RIGHT, "Jobbra" },
+ { PAPUGA_KEY_LEFT, "Balra" },
+ { PAPUGA_KEY_UP, "Fel" },
+ { PAPUGA_KEY_DOWN, "Le" },
+ { PAPUGA_KEY_ENTER, "Enter" },
+ { PAPUGA_KEY_SPACEBAR, "Sz\xc3\xb3" "k\xc3\xb6" "z" },
+ };
+
+ static const struct KeyboardReplacements aKeyboards[] =
+ {
+ { "ast",aImplReplacements_Asturian, SAL_N_ELEMENTS(aImplReplacements_Asturian) },
+ { "ca", aImplReplacements_Catalan, SAL_N_ELEMENTS(aImplReplacements_Catalan) },
+ { "et", aImplReplacements_Estonian, SAL_N_ELEMENTS(aImplReplacements_Estonian) },
+ { "hu", aImplReplacements_Hungarian, SAL_N_ELEMENTS(aImplReplacements_Hungarian) },
+ { "lt", aImplReplacements_Lithuanian, SAL_N_ELEMENTS(aImplReplacements_Lithuanian) },
+ { "sl", aImplReplacements_Slovenian, SAL_N_ELEMENTS(aImplReplacements_Slovenian) },
+ { "es", aImplReplacements_Spanish, SAL_N_ELEMENTS(aImplReplacements_Spanish) },
+ };
+
+ // translate keycodes, used within the displayed menu shortcuts
+ OUString getKeysReplacementName( OUString const & pLang, LONG nSymbol )
+ {
+ for( unsigned int n = 0; n < SAL_N_ELEMENTS(aKeyboards); n++ )
+ {
+ if( pLang.equalsAscii( aKeyboards[n].pLangName ) )
+ {
+ const struct KeysNameReplacement* pRepl = aKeyboards[n].pReplacements;
+ for( int m = aKeyboards[n].nReplacements ; m ; )
+ {
+ if( nSymbol == pRepl[--m].aSymbol )
+ return OUString( pRepl[m].pName, strlen(pRepl[m].pName), RTL_TEXTENCODING_UTF8 );
+ }
+ }
+ }
+
+ return OUString();
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/window/salframe.cxx b/vcl/win/window/salframe.cxx
new file mode 100644
index 000000000..789df0dd2
--- /dev/null
+++ b/vcl/win/window/salframe.cxx
@@ -0,0 +1,5925 @@
+/* -*- 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 <unotools/misccfg.hxx>
+
+#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/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>
+#ifndef WM_GETOBJECT // TESTME does this ever happen ?
+# define WM_GETOBJECT 0x003D
+#endif
+
+#include <time.h>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <shobjidl.h>
+#include <propkey.h>
+#include <propvarutil.h>
+#include <shellapi.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_UTF32ToSurrogate1(ch) (((unsigned long) (ch) - 0x10000) / 0x400 + 0xD800)
+#define Uni_UTF32ToSurrogate2(ch) ((static_cast<unsigned long>(ch) - 0x10000) % 0x400 + 0xDC00)
+#define Uni_SupplementaryPlanesStart 0x10000
+
+static void UpdateFrameGeometry( HWND hWnd, WinSalFrame* pFrame );
+static void SetMaximizedFrameGeometry( HWND hWnd, WinSalFrame* pFrame, RECT* pParentRect = nullptr );
+
+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 ) )
+ {
+ RECT aRect = aPlacement.rcNormalPosition;
+ RECT aRect2 = aRect;
+ AdjustWindowRectEx( &aRect2, GetWindowStyle( pFrame->mhWnd ),
+ FALSE, GetWindowExStyle( pFrame->mhWnd ) );
+ long nTopDeco = abs( aRect.top - aRect2.top );
+ long nLeftDeco = abs( aRect.left - aRect2.left );
+ long nBottomDeco = abs( aRect.bottom - aRect2.bottom );
+ 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;
+ }
+ }
+ else
+ {
+ RECT aRect;
+ GetWindowRect( pFrame->mhWnd, &aRect );
+
+ // to be consistent with Unix, the frame state is without(!) decoration
+ RECT aRect2 = aRect;
+ AdjustWindowRectEx( &aRect2, GetWindowStyle( pFrame->mhWnd ),
+ FALSE, GetWindowExStyle( pFrame->mhWnd ) );
+ long nTopDeco = abs( aRect.top - aRect2.top );
+ long nLeftDeco = abs( aRect.left - aRect2.left );
+ long nBottomDeco = abs( aRect.bottom - aRect2.bottom );
+ long nRightDeco = abs( aRect.right - aRect2.right );
+
+ pFrame->maState.mnState &= ~WindowStateState(WindowStateState::Minimized | WindowStateState::Maximized);
+ // subtract decoration
+ 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;
+ 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;
+ }
+ }
+ }
+}
+
+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
+
+static 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.IsInside( 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;
+ if ( pGraphics->getDefPal() )
+ SelectPalette( hDC, pGraphics->getDefPal(), TRUE );
+ pGraphics->DeInitGraphics();
+ SendMessageW( pSalData->mpInstance->mhComWnd, SAL_MSG_RELEASEDC,
+ reinterpret_cast<WPARAM>(mhWnd), reinterpret_cast<LPARAM>(hDC) );
+ if ( pGraphics == mpThreadGraphics )
+ pSalData->mnCacheDCInUse--;
+ pGraphics->setHDC(nullptr);
+ 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->setDefPal(SelectPalette( hDC, pSalData->mhDitherPal, TRUE ));
+ RealizePalette( hDC );
+ }
+ pGraphics->InitGraphics();
+
+ 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( long nWidth, long nHeight )
+{
+ mnMinWidth = nWidth;
+ mnMinHeight = nHeight;
+}
+
+void WinSalFrame::SetMaxClientSize( long nWidth, long nHeight )
+{
+ mnMaxWidth = nWidth;
+ mnMaxHeight = nHeight;
+}
+
+void WinSalFrame::SetPosSize( long nX, long nY, long nWidth, 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;
+}
+
+bool WinSalFrame::SetPluginParent( SystemParentData* pNewParent )
+{
+ if ( pNewParent->hWnd == nullptr )
+ {
+ pNewParent->hWnd = GetDesktopWindow();
+ }
+
+ WinSalFrame::mbInReparent = true;
+ ImplSetParentFrame( pNewParent->hWnd, true );
+ WinSalFrame::mbInReparent = false;
+ return true;
+}
+
+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( long& rWidth, 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 ) );
+ long nTopDeco = abs( aWinRect.top - aRect2.top );
+ long nLeftDeco = abs( aWinRect.left - aRect2.left );
+ long nBottomDeco = abs( aWinRect.bottom - aRect2.bottom );
+ 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.IsInside( 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
+ };
+
+ // 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( long nX, 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()
+{
+ 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;
+ HDC hDC = GetDC( pFrame->mhWnd );
+ // In case of vertical writing, always append a '@' to the
+ // Windows font name, not only if such a Windows font really is
+ // available (bTestVerticalAvail == false in the below call):
+ // The Windows IME's candidates window seems to always use a
+ // font that has all necessary glyphs, not necessarily the one
+ // specified by this font name; but it seems to decide whether
+ // to use that font's horizontal or vertical variant based on a
+ // '@' in front of this font name.
+ ImplGetLogFontFromFontSelect(hDC, pContext->mpFont->GetFontSelectPattern(),
+ nullptr, aLogFont);
+ ReleaseDC( pFrame->mhWnd, hDC );
+ 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;
+ }
+ }
+
+ 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 long ImplW2I( const wchar_t* pStr )
+{
+ 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 ) );
+ long nDragWidth = GetSystemMetrics( SM_CXDRAG );
+ 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();
+
+ 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 ) ) );
+ aStyleSettings.SetFaceColor( ImplWinColorToSal( GetSysColor( COLOR_3DFACE ) ) );
+ aStyleSettings.SetInactiveTabColor( aStyleSettings.GetFaceColor() );
+ aStyleSettings.SetLightColor( ImplWinColorToSal( GetSysColor( COLOR_3DHILIGHT ) ) );
+ aStyleSettings.SetLightBorderColor( ImplWinColorToSal( GetSysColor( COLOR_3DLIGHT ) ) );
+ aStyleSettings.SetShadowColor( ImplWinColorToSal( GetSysColor( COLOR_3DSHADOW ) ) );
+ aStyleSettings.SetDarkShadowColor( ImplWinColorToSal( GetSysColor( COLOR_3DDKSHADOW ) ) );
+ aStyleSettings.SetHelpColor( ImplWinColorToSal( GetSysColor( COLOR_INFOBK ) ) );
+ aStyleSettings.SetHelpTextColor( ImplWinColorToSal( GetSysColor( COLOR_INFOTEXT ) ) );
+
+ Color aControlTextColor(ImplWinColorToSal(GetSysColor(COLOR_BTNTEXT)));
+
+ aStyleSettings.SetDialogColor(aStyleSettings.GetFaceColor());
+ aStyleSettings.SetDialogTextColor(aControlTextColor);
+
+ aStyleSettings.SetDefaultButtonTextColor(aControlTextColor);
+ aStyleSettings.SetButtonTextColor(aControlTextColor);
+ aStyleSettings.SetDefaultActionButtonTextColor(aControlTextColor);
+ aStyleSettings.SetActionButtonTextColor(aControlTextColor);
+ aStyleSettings.SetFlatButtonTextColor(aControlTextColor);
+ aStyleSettings.SetDefaultButtonRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetButtonRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetDefaultActionButtonRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetActionButtonRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetFlatButtonRolloverTextColor(aControlTextColor);
+ 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.SetRadioCheckTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) );
+ aStyleSettings.SetGroupTextColor( aStyleSettings.GetRadioCheckTextColor() );
+ aStyleSettings.SetLabelTextColor( aStyleSettings.GetRadioCheckTextColor() );
+ aStyleSettings.SetWindowColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOW ) ) );
+ aStyleSettings.SetActiveTabColor( aStyleSettings.GetWindowColor() );
+ aStyleSettings.SetWindowTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) );
+ aStyleSettings.SetToolTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) );
+ aStyleSettings.SetFieldColor( aStyleSettings.GetWindowColor() );
+ aStyleSettings.SetFieldTextColor( aStyleSettings.GetWindowTextColor() );
+ aStyleSettings.SetFieldRolloverTextColor( aStyleSettings.GetFieldTextColor() );
+ aStyleSettings.SetHighlightColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHT ) ) );
+ aStyleSettings.SetHighlightTextColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHTTEXT ) ) );
+ 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.SetMenuTextColor( ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ) );
+ if ( std::optional<Color> aColor = aStyleSettings.GetPersonaMenuBarTextColor() )
+ {
+ aStyleSettings.SetMenuBarTextColor( *aColor );
+ aStyleSettings.SetMenuBarRolloverTextColor( *aColor );
+ }
+ else
+ {
+ aStyleSettings.SetMenuBarTextColor( ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ) );
+ aStyleSettings.SetMenuBarRolloverTextColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHTTEXT ) ) );
+ }
+ 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 );
+
+ // 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 );
+
+ // 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) )
+ {
+ MiscSettings aMiscSettings = rSettings.GetMiscSettings();
+ utl::MiscCfg().SetYear2000( static_cast<sal_Int32>(nValue-99) );
+ rSettings.SetMiscSettings( aMiscSettings );
+ }
+ }
+ }
+
+ 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( long nX, long nY, long nWidth, long nHeight )
+{
+ if( ! mpClipRgnData )
+ return;
+
+ RECT* pRect = mpNextClipRect;
+ RECT* pBoundRect = &(mpClipRgnData->rdh.rcBound);
+ long nRight = nX + nWidth;
+ 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;
+ pSalData->mpMouseLeaveTimer->SetDebugName( "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.IsInside(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 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;
+ 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 = static_cast<sal_Unicode>(Uni_UTF32ToSurrogate2( 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;
+
+ if ((nModCode & (KEY_MOD1 | KEY_MOD2)) == (KEY_MOD1 | KEY_MOD2) &&
+ aKeyEvt.mnCharCode)
+ {
+ // this is actually AltGr and should not be handled as Alt
+ aKeyEvt.mnCode &= ~(KEY_MOD1 | KEY_MOD2);
+ }
+
+ 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<int>(LOWORD(lParam));
+ pFrame->mnHeight = static_cast<int>(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();
+ }
+
+ 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())
+ {
+ SelectPalette( pGraphics->getHDC(), hPal, FALSE );
+ if ( RealizePalette( pGraphics->getHDC() ) )
+ {
+ 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;
+ UINT nCols;
+ bool bStdDC;
+ bool bUpdate;
+
+ pSalData->mbInPalChange = true;
+
+ // reset all palettes in VirDevs and Frames
+ pTempVD = pSalData->mpFirstVD;
+ while ( pTempVD )
+ {
+ pGraphics = pTempVD->getGraphics();
+ if ( pGraphics->getDefPal() )
+ {
+ SelectPalette( pGraphics->getHDC(),
+ pGraphics->getDefPal(),
+ TRUE );
+ }
+ pTempVD = pTempVD->getNext();
+ }
+ pTempFrame = pSalData->mpFirstFrame;
+ while ( pTempFrame )
+ {
+ pGraphics = pTempFrame->mpLocalGraphics;
+ if ( pGraphics && pGraphics->getHDC() && pGraphics->getDefPal() )
+ {
+ SelectPalette( pGraphics->getHDC(),
+ pGraphics->getDefPal(),
+ TRUE );
+ }
+ pTempFrame = pTempFrame->mpNextFrame;
+ }
+
+ // re-initialize palette
+ WinSalFrame* pFrame = nullptr;
+ if ( bFrame )
+ pFrame = GetWindowPtr( hWnd );
+ if ( pFrame && pFrame->mpLocalGraphics && pFrame->mpLocalGraphics->getHDC() )
+ {
+ hDC = pFrame->mpLocalGraphics->getHDC();
+ bStdDC = true;
+ }
+ else
+ {
+ hDC = GetDC( hWnd );
+ bStdDC = false;
+ }
+ UnrealizeObject( hPal );
+ hOldPal = SelectPalette( hDC, hPal, TRUE );
+ nCols = RealizePalette( hDC );
+ bUpdate = nCols != 0;
+ if ( !bStdDC )
+ {
+ 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() )
+ {
+ SelectPalette( pGraphics->getHDC(), hPal, TRUE );
+ RealizePalette( pGraphics->getHDC() );
+ }
+ pTempVD = pTempVD->getNext();
+ }
+ pTempFrame = pSalData->mpFirstFrame;
+ while ( pTempFrame )
+ {
+ if ( pTempFrame != pFrame )
+ {
+ pGraphics = pTempFrame->mpLocalGraphics;
+ if ( pGraphics && pGraphics->getHDC() && pGraphics->getDefPal() )
+ {
+ SelectPalette( pGraphics->getHDC(), hPal, TRUE );
+ if ( RealizePalette( pGraphics->getHDC() ) )
+ bUpdate = true;
+ }
+ }
+ pTempFrame = pTempFrame->mpNextFrame;
+ }
+
+ // if colors changed, update the window
+ if ( bUpdate )
+ {
+ pTempFrame = pSalData->mpFirstFrame;
+ while ( pTempFrame )
+ {
+ pGraphics = pTempFrame->mpLocalGraphics;
+ if ( pGraphics && pGraphics->getHDC() && 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() ), 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 )
+ {
+ 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;
+
+ ExtTextInputAttr* pSalAttrAry = nullptr;
+ 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 = new ExtTextInputAttr[nTextLen2];
+ memset( pSalAttrAry, 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;
+ }
+ }
+
+ // 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->mbCandidateMode )
+ {
+ 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 );
+ }
+
+ if ( pSalAttrAry )
+ delete [] pSalAttrAry;
+ }
+
+ 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 )
+ 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)
+{
+ // IA2 should be enabled automatically
+ AllSettings aSettings = Application::GetSettings();
+ MiscSettings aMisc = aSettings.GetMiscSettings();
+ aMisc.SetEnableATToolSupport( true );
+ aSettings.SetMiscSettings( aMisc );
+ Application::SetSettings( aSettings );
+
+ 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 );
+ 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 );
+ // 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: */
diff --git a/vcl/win/window/salmenu.cxx b/vcl/win/window/salmenu.cxx
new file mode 100644
index 000000000..6f8dc8bff
--- /dev/null
+++ b/vcl/win/window/salmenu.cxx
@@ -0,0 +1,355 @@
+/* -*- 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 <svsys.h>
+
+#include <vcl/menu.hxx>
+#include <vcl/sysdata.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/safeint.hxx>
+
+#include <win/wincomp.hxx>
+#include <win/saldata.hxx>
+#include <win/salinst.h>
+#include <win/salframe.h>
+#include <win/salmenu.h>
+
+#include <salgdi.hxx>
+
+static DWORD myerr=0;
+
+bool SalData::IsKnownMenuHandle( HMENU hMenu )
+{
+ if( mhMenuSet.find( hMenu ) == mhMenuSet.end() )
+ return false;
+ else
+ return true;
+}
+
+// WinSalInst factory methods
+
+std::unique_ptr<SalMenu> WinSalInstance::CreateMenu( bool bMenuBar, Menu* )
+{
+ WinSalMenu *pSalMenu = new WinSalMenu();
+
+ pSalMenu->mbMenuBar = bMenuBar;
+ pSalMenu->mhWnd = nullptr;
+ if( bMenuBar )
+ pSalMenu->mhMenu = ::CreateMenu();
+ else
+ pSalMenu->mhMenu = ::CreatePopupMenu();
+
+ if( pSalMenu->mhMenu )
+ GetSalData()->mhMenuSet.insert( pSalMenu->mhMenu );
+
+ return std::unique_ptr<SalMenu>(pSalMenu);
+}
+
+std::unique_ptr<SalMenuItem> WinSalInstance::CreateMenuItem( const SalItemParams & rItemData )
+{
+ WinSalMenuItem *pSalMenuItem = new WinSalMenuItem();
+ memset( &pSalMenuItem->mInfo, 0, sizeof( MENUITEMINFOW ) );
+ pSalMenuItem->mInfo.cbSize = sizeof( MENUITEMINFOW );
+
+ if( rItemData.eType == MenuItemType::SEPARATOR )
+ {
+ // separator
+ pSalMenuItem->mInfo.fMask = MIIM_TYPE;
+ pSalMenuItem->mInfo.fType = MFT_SEPARATOR;
+ }
+ else
+ {
+ // item
+ pSalMenuItem->mText = rItemData.aText;
+ pSalMenuItem->mpMenu = rItemData.pMenu;
+ pSalMenuItem->maBitmap= !!rItemData.aImage ? rItemData.aImage.GetBitmapEx().GetBitmap() : Bitmap();
+ pSalMenuItem->mnId = rItemData.nId;
+
+ // 'translate' mnemonics
+ pSalMenuItem->mText = pSalMenuItem->mText.replaceAll( "~", "&" );
+
+ pSalMenuItem->mInfo.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID | MIIM_DATA;
+ pSalMenuItem->mInfo.fType = MFT_STRING;
+ pSalMenuItem->mInfo.dwTypeData = o3tl::toW(const_cast<sal_Unicode *>(pSalMenuItem->mText.getStr()));
+ pSalMenuItem->mInfo.cch = pSalMenuItem->mText.getLength();
+
+ pSalMenuItem->mInfo.wID = rItemData.nId;
+ pSalMenuItem->mInfo.dwItemData = reinterpret_cast<ULONG_PTR>(pSalMenuItem); // user data
+ }
+
+ return std::unique_ptr<SalMenuItem>(pSalMenuItem);
+}
+
+static void ImplDrawMenuBar( SalMenu *pMenu )
+{
+ if( pMenu->VisibleMenuBar() )
+ {
+ // redrawing the menubar all the time actually seems to be unnecessary (it just flickers)
+ /*
+ WinSalMenu *pMenuBar = ImplFindMenuBar( pMenu );
+ if( pMenuBar && pMenuBar->mhWnd )
+ ::DrawMenuBar( pMenuBar->mhWnd );
+ */
+ }
+}
+
+/*
+ * WinSalMenu
+ */
+
+WinSalMenu::WinSalMenu()
+{
+ mhMenu = nullptr;
+ mbMenuBar = false;
+ mhWnd = nullptr;
+ mpParentMenu = nullptr;
+}
+
+WinSalMenu::~WinSalMenu()
+{
+ // only required if not associated to a window...
+ GetSalData()->mhMenuSet.erase( mhMenu );
+ ::DestroyMenu( mhMenu );
+}
+
+bool WinSalMenu::VisibleMenuBar()
+{
+ // The Win32 implementation never shows a native
+ // menubar. Thus, native menus are only visible
+ // when the menu is merged with an OLE container.
+ // The reason are missing tooltips, ownerdraw
+ // issues and accessibility which are better supported
+ // by VCL menus.
+ // Nevertheless, the native menus are always created
+ // and the application will properly react to all native
+ // menu messages.
+
+ return false;
+}
+
+void WinSalMenu::SetFrame( const SalFrame *pFrame )
+{
+ if( pFrame )
+ mhWnd = static_cast<const WinSalFrame*>(pFrame)->mhWnd;
+ else
+ mhWnd = nullptr;
+}
+
+void WinSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
+{
+ if( pSalMenuItem )
+ {
+ WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
+ if( nPos == MENU_APPEND )
+ {
+ nPos = ::GetMenuItemCount( mhMenu );
+ if( nPos == static_cast<unsigned>( -1 ) )
+ return;
+ }
+
+ if(!::InsertMenuItemW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
+ myerr = GetLastError();
+ else
+ {
+ pWItem->mpSalMenu = this;
+ ImplDrawMenuBar( this );
+ }
+ }
+}
+
+void WinSalMenu::RemoveItem( unsigned nPos )
+{
+ int num = ::GetMenuItemCount( mhMenu );
+ if( num != -1 && nPos < o3tl::make_unsigned(num) )
+ {
+ WinSalMenuItem *pSalMenuItem = nullptr;
+
+ MENUITEMINFOW mi = {};
+ mi.cbSize = sizeof( mi );
+ mi.fMask = MIIM_DATA;
+ if( !GetMenuItemInfoW( mhMenu, nPos, TRUE, &mi) )
+ myerr = GetLastError();
+ else
+ pSalMenuItem = reinterpret_cast<WinSalMenuItem *>(mi.dwItemData);
+
+ if( !::RemoveMenu( mhMenu, nPos, MF_BYPOSITION ) )
+ myerr = GetLastError();
+ else
+ {
+ if( pSalMenuItem )
+ pSalMenuItem->mpSalMenu = nullptr;
+ ImplDrawMenuBar( this );
+ }
+ }
+}
+
+static void ImplRemoveItemById( WinSalMenu *pSalMenu, unsigned nItemId )
+{
+ if( !pSalMenu )
+ return;
+
+ WinSalMenuItem *pSalMenuItem = nullptr;
+
+ MENUITEMINFOW mi = {};
+ mi.cbSize = sizeof( mi );
+ mi.fMask = MIIM_DATA;
+ if( !GetMenuItemInfoW( pSalMenu->mhMenu, nItemId, FALSE, &mi) )
+ myerr = GetLastError();
+ else
+ pSalMenuItem = reinterpret_cast<WinSalMenuItem *>(mi.dwItemData);
+
+ if( !::RemoveMenu( pSalMenu->mhMenu, nItemId, MF_BYCOMMAND ) )
+ myerr = GetLastError();
+ else
+ {
+ if( pSalMenuItem )
+ pSalMenuItem->mpSalMenu = nullptr;
+ ImplDrawMenuBar( pSalMenu );
+ }
+}
+
+void WinSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos )
+{
+ if( pSalMenuItem )
+ {
+ WinSalMenuItem* pWMenuItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
+ WinSalMenu* pWSubMenu = static_cast<WinSalMenu*>(pSubMenu);
+ if( pWMenuItem->mInfo.hSubMenu )
+ {
+ GetSalData()->mhMenuSet.erase( pWMenuItem->mInfo.hSubMenu );
+ ::DestroyMenu( pWMenuItem->mInfo.hSubMenu );
+ }
+
+ pWMenuItem->mInfo.fMask |= MIIM_SUBMENU;
+ if( !pSubMenu )
+ pWMenuItem->mInfo.hSubMenu = nullptr;
+ else
+ {
+ pWMenuItem->mInfo.hSubMenu = pWSubMenu->mhMenu;
+ pWSubMenu->mpParentMenu = this;
+ }
+
+ if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWMenuItem->mInfo ) )
+ myerr = GetLastError();
+ else
+ ImplDrawMenuBar( this );
+ }
+}
+
+void WinSalMenu::CheckItem( unsigned nPos, bool bCheck )
+{
+ if( static_cast<unsigned>( -1 ) != ::CheckMenuItem( mhMenu, nPos, MF_BYPOSITION|(bCheck ? MF_CHECKED : MF_UNCHECKED) ) )
+ ImplDrawMenuBar( this );
+}
+
+void WinSalMenu::EnableItem( unsigned nPos, bool bEnable )
+{
+ if( -1 != ::EnableMenuItem( mhMenu, nPos, MF_BYPOSITION|(bEnable ? MF_ENABLED : (MF_DISABLED|MF_GRAYED) ) ) )
+ ImplDrawMenuBar( this );
+}
+
+void WinSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem* pSalMenuItem, const Image& rImage )
+{
+ if( pSalMenuItem )
+ {
+ WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
+ if( !!rImage )
+ pWItem->maBitmap = rImage.GetBitmapEx().GetBitmap();
+ else
+ pWItem->maBitmap = Bitmap();
+ }
+}
+
+void WinSalMenu::SetItemText( unsigned nPos, SalMenuItem* pSalMenuItem, const OUString& rText )
+{
+ if( pSalMenuItem )
+ {
+ WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
+ pWItem->mText = rText;
+ // 'translate' mnemonics
+ pWItem->mText = pWItem->mText.replaceAll( "~", "&" );
+ pWItem->mInfo.fMask = MIIM_TYPE | MIIM_DATA;
+ pWItem->mInfo.fType = MFT_STRING;
+
+ // combine text and accelerator text
+ OUString aStr( pWItem->mText );
+ if( pWItem->mAccelText.getLength() )
+ {
+ aStr += "\t" + pWItem->mAccelText;
+ }
+ pWItem->mInfo.dwTypeData = o3tl::toW(const_cast<sal_Unicode *>(aStr.getStr()));
+ pWItem->mInfo.cch = aStr.getLength();
+
+ if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
+ myerr = GetLastError();
+ else
+ ImplDrawMenuBar( this );
+ }
+}
+
+void WinSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const vcl::KeyCode&, const OUString& rKeyName )
+{
+ if( pSalMenuItem )
+ {
+ WinSalMenuItem* pWItem = static_cast<WinSalMenuItem*>(pSalMenuItem);
+ pWItem->mAccelText = rKeyName;
+ pWItem->mInfo.fMask = MIIM_TYPE | MIIM_DATA;
+ pWItem->mInfo.fType = MFT_STRING;
+
+ // combine text and accelerator text
+ OUString aStr( pWItem->mText );
+ if( pWItem->mAccelText.getLength() )
+ {
+ aStr += "\t" + pWItem->mAccelText;
+ }
+ pWItem->mInfo.dwTypeData = o3tl::toW(const_cast<sal_Unicode *>(aStr.getStr()));
+ pWItem->mInfo.cch = aStr.getLength();
+
+ if(!::SetMenuItemInfoW( mhMenu, nPos, TRUE, &pWItem->mInfo ))
+ myerr = GetLastError();
+ else
+ ImplDrawMenuBar( this );
+ }
+}
+
+void WinSalMenu::GetSystemMenuData( SystemMenuData* pData )
+{
+ if( pData )
+ pData->hMenu = mhMenu;
+}
+
+/*
+ * SalMenuItem
+ */
+
+WinSalMenuItem::WinSalMenuItem()
+{
+ memset( &mInfo, 0, sizeof( MENUITEMINFOW ) );
+ mpMenu = nullptr;
+ mnId = 0xFFFF;
+ mpSalMenu = nullptr;
+}
+
+WinSalMenuItem::~WinSalMenuItem()
+{
+ if( mpSalMenu )
+ ImplRemoveItemById( mpSalMenu, mnId );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/window/salobj.cxx b/vcl/win/window/salobj.cxx
new file mode 100644
index 000000000..0cf2fa8ad
--- /dev/null
+++ b/vcl/win/window/salobj.cxx
@@ -0,0 +1,727 @@
+/* -*- 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 <string.h>
+
+#include <svsys.h>
+
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+#include <win/wincomp.hxx>
+#include <win/saldata.hxx>
+#include <win/salinst.h>
+#include <win/salframe.h>
+#include <win/salobj.h>
+
+#include <comphelper/windowserrorstring.hxx>
+
+static bool ImplIsSysWindowOrChild( HWND hWndParent, HWND hWndChild )
+{
+ if ( hWndParent == hWndChild )
+ return true;
+
+ HWND hTempWnd = ::GetParent( hWndChild );
+ while ( hTempWnd )
+ {
+ // stop searching if not a child window
+ if ( !(GetWindowStyle( hTempWnd ) & WS_CHILD) )
+ return false;
+ if ( hTempWnd == hWndParent )
+ return true;
+ hTempWnd = ::GetParent( hTempWnd );
+ }
+
+ return false;
+}
+
+WinSalObject* ImplFindSalObject( HWND hWndChild )
+{
+ SalData* pSalData = GetSalData();
+ WinSalObject* pObject = pSalData->mpFirstObject;
+ while ( pObject )
+ {
+ if ( ImplIsSysWindowOrChild( pObject->mhWndChild, hWndChild ) )
+ return pObject;
+
+ pObject = pObject->mpNextObject;
+ }
+
+ return nullptr;
+}
+
+static WinSalFrame* ImplFindSalObjectFrame( HWND hWnd )
+{
+ WinSalFrame* pFrame = nullptr;
+ WinSalObject* pObject = ImplFindSalObject( hWnd );
+ if ( pObject )
+ {
+ // find matching frame
+ HWND hWnd2 = ::GetParent( pObject->mhWnd );
+ pFrame = GetSalData()->mpFirstFrame;
+ while ( pFrame )
+ {
+ if ( pFrame->mhWnd == hWnd2 )
+ break;
+
+ pFrame = pFrame->mpNextFrame;
+ }
+ }
+
+ return pFrame;
+}
+
+static LRESULT CALLBACK SalSysMsgProc( int nCode, WPARAM wParam, LPARAM lParam )
+{
+ // Used for Unicode and none Unicode
+ SalData* pSalData = GetSalData();
+
+ if ( (nCode >= 0) && lParam )
+ {
+ CWPSTRUCT* pData = reinterpret_cast<CWPSTRUCT*>(lParam);
+ if ( (pData->message != WM_KEYDOWN) &&
+ (pData->message != WM_KEYUP) )
+ pSalData->mnSalObjWantKeyEvt = 0;
+
+
+ // check if we need to process data for a SalObject-window
+ WinSalObject* pObject;
+ if ( pData->message == WM_SETFOCUS )
+ {
+ pObject = ImplFindSalObject( pData->hwnd );
+ if ( pObject )
+ {
+ pObject->mhLastFocusWnd = pData->hwnd;
+ if ( ImplSalYieldMutexTryToAcquire() )
+ {
+ pObject->CallCallback( SalObjEvent::GetFocus );
+ ImplSalYieldMutexRelease();
+ }
+ else
+ {
+ bool const ret = PostMessageW(pObject->mhWnd, SALOBJ_MSG_POSTFOCUS, 0, 0);
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ }
+ }
+ else if ( pData->message == WM_KILLFOCUS )
+ {
+ pObject = ImplFindSalObject( pData->hwnd );
+ if ( pObject && !ImplFindSalObject( reinterpret_cast<HWND>(pData->wParam) ) )
+ {
+ // only call LoseFocus, if truly no child window gets the focus
+ if ( !pData->wParam || !ImplFindSalObject( reinterpret_cast<HWND>(pData->wParam) ) )
+ {
+ if ( ImplSalYieldMutexTryToAcquire() )
+ {
+ pObject->CallCallback( SalObjEvent::LoseFocus );
+ ImplSalYieldMutexRelease();
+ }
+ else
+ {
+ bool const ret = PostMessageW(pObject->mhWnd, SALOBJ_MSG_POSTFOCUS, 0, 0);
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ }
+ else
+ pObject->mhLastFocusWnd = reinterpret_cast<HWND>(pData->wParam);
+ }
+ }
+ }
+
+ return CallNextHookEx( pSalData->mhSalObjMsgHook, nCode, wParam, lParam );
+}
+
+bool ImplSalPreDispatchMsg( const MSG* pMsg )
+{
+ // Used for Unicode and none Unicode
+ SalData* pSalData = GetSalData();
+ WinSalObject* pObject;
+
+ if ( (pMsg->message == WM_LBUTTONDOWN) ||
+ (pMsg->message == WM_RBUTTONDOWN) ||
+ (pMsg->message == WM_MBUTTONDOWN) )
+ {
+ ImplSalYieldMutexAcquireWithWait();
+ pObject = ImplFindSalObject( pMsg->hwnd );
+ if ( pObject && !pObject->IsMouseTransparent() )
+ {
+ bool const ret = PostMessageW(pObject->mhWnd, SALOBJ_MSG_TOTOP, 0, 0);
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ ImplSalYieldMutexRelease();
+ }
+
+ if ( (pMsg->message == WM_KEYDOWN) ||
+ (pMsg->message == WM_KEYUP) )
+ {
+ // process KeyEvents even if the control does not process them itself
+ // SysKeys are processed as WM_SYSCOMMAND
+ // Char-Events are not processed, as they are not accelerator-relevant
+ bool bWantedKeyCode = false;
+ // A-Z, 0-9 only when combined with the Control-key
+ if ( ((pMsg->wParam >= 65) && (pMsg->wParam <= 90)) ||
+ ((pMsg->wParam >= 48) && (pMsg->wParam <= 57)) )
+ {
+ if ( GetKeyState( VK_CONTROL ) & 0x8000 )
+ bWantedKeyCode = true;
+ }
+ else if ( ((pMsg->wParam >= VK_F1) && (pMsg->wParam <= VK_F24)) ||
+ ((pMsg->wParam >= VK_SPACE) && (pMsg->wParam <= VK_HELP)) ||
+ (pMsg->wParam == VK_BACK) || (pMsg->wParam == VK_TAB) ||
+ (pMsg->wParam == VK_CLEAR) || (pMsg->wParam == VK_RETURN) ||
+ (pMsg->wParam == VK_ESCAPE) )
+ bWantedKeyCode = true;
+ if ( bWantedKeyCode )
+ {
+ ImplSalYieldMutexAcquireWithWait();
+ pObject = ImplFindSalObject( pMsg->hwnd );
+ if ( pObject )
+ pSalData->mnSalObjWantKeyEvt = pMsg->wParam;
+ ImplSalYieldMutexRelease();
+ }
+ }
+ // check WM_SYSCHAR, to activate menu with Alt key
+ else if ( pMsg->message == WM_SYSCHAR )
+ {
+ pSalData->mnSalObjWantKeyEvt = 0;
+
+ sal_uInt16 nKeyCode = LOWORD( pMsg->wParam );
+ // only 0-9 and A-Z
+ if ( ((nKeyCode >= 48) && (nKeyCode <= 57)) ||
+ ((nKeyCode >= 65) && (nKeyCode <= 90)) ||
+ ((nKeyCode >= 97) && (nKeyCode <= 122)) )
+ {
+ bool bRet = false;
+ ImplSalYieldMutexAcquireWithWait();
+ pObject = ImplFindSalObject( pMsg->hwnd );
+ if ( pObject )
+ {
+ if ( pMsg->hwnd == ::GetFocus() )
+ {
+ WinSalFrame* pFrame = ImplFindSalObjectFrame( pMsg->hwnd );
+ if ( pFrame )
+ {
+ if ( ImplHandleSalObjSysCharMsg( pFrame->mhWnd, pMsg->wParam, pMsg->lParam ) )
+ bRet = true;
+ }
+ }
+ }
+ ImplSalYieldMutexRelease();
+ if ( bRet )
+ return true;
+ }
+ }
+ else
+ pSalData->mnSalObjWantKeyEvt = 0;
+
+ return false;
+}
+
+void ImplSalPostDispatchMsg( const MSG* pMsg )
+{
+ // Used for Unicode and none Unicode
+ SalData *pSalData = GetSalData();
+
+ if ( (pMsg->message == WM_KEYDOWN) || (pMsg->message == WM_KEYUP) )
+ {
+ if ( pSalData->mnSalObjWantKeyEvt == pMsg->wParam )
+ {
+ pSalData->mnSalObjWantKeyEvt = 0;
+ if ( pMsg->hwnd == ::GetFocus() )
+ {
+ ImplSalYieldMutexAcquireWithWait();
+ WinSalFrame* pFrame = ImplFindSalObjectFrame( pMsg->hwnd );
+ if ( pFrame )
+ ImplHandleSalObjKeyMsg( pFrame->mhWnd, pMsg->message, pMsg->wParam, pMsg->lParam );
+ ImplSalYieldMutexRelease();
+ }
+ }
+ }
+
+ pSalData->mnSalObjWantKeyEvt = 0;
+}
+
+static LRESULT CALLBACK SalSysObjWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, int& rDef )
+{
+ WinSalObject* pSysObj;
+ LRESULT nRet = 0;
+
+ switch( nMsg )
+ {
+ case WM_ERASEBKGND:
+ nRet = 1;
+ rDef = FALSE;
+ break;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT aPs;
+ BeginPaint( hWnd, &aPs );
+ EndPaint( hWnd, &aPs );
+ rDef = FALSE;
+ }
+ break;
+
+ case WM_PARENTNOTIFY:
+ {
+ UINT nNotifyMsg = LOWORD( wParam );
+ if ( (nNotifyMsg == WM_LBUTTONDOWN) ||
+ (nNotifyMsg == WM_RBUTTONDOWN) ||
+ (nNotifyMsg == WM_MBUTTONDOWN) )
+ {
+ ImplSalYieldMutexAcquireWithWait();
+ pSysObj = GetSalObjWindowPtr( hWnd );
+ if ( pSysObj && !pSysObj->IsMouseTransparent() )
+ pSysObj->CallCallback( SalObjEvent::ToTop );
+ ImplSalYieldMutexRelease();
+ }
+ }
+ break;
+
+ case WM_MOUSEACTIVATE:
+ {
+ ImplSalYieldMutexAcquireWithWait();
+ pSysObj = GetSalObjWindowPtr( hWnd );
+ if ( pSysObj && !pSysObj->IsMouseTransparent() )
+ {
+ bool const ret = PostMessageW( hWnd, SALOBJ_MSG_TOTOP, 0, 0 );
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ ImplSalYieldMutexRelease();
+ }
+ break;
+
+ case SALOBJ_MSG_TOTOP:
+ if ( ImplSalYieldMutexTryToAcquire() )
+ {
+ pSysObj = GetSalObjWindowPtr( hWnd );
+ pSysObj->CallCallback( SalObjEvent::ToTop );
+ ImplSalYieldMutexRelease();
+ rDef = FALSE;
+ }
+ else
+ {
+ bool const ret = PostMessageW( hWnd, SALOBJ_MSG_TOTOP, 0, 0 );
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ break;
+
+ case SALOBJ_MSG_POSTFOCUS:
+ if ( ImplSalYieldMutexTryToAcquire() )
+ {
+ pSysObj = GetSalObjWindowPtr( hWnd );
+ HWND hFocusWnd = ::GetFocus();
+ SalObjEvent nEvent;
+ if ( hFocusWnd && ImplIsSysWindowOrChild( hWnd, hFocusWnd ) )
+ nEvent = SalObjEvent::GetFocus;
+ else
+ nEvent = SalObjEvent::LoseFocus;
+ pSysObj->CallCallback( nEvent );
+ ImplSalYieldMutexRelease();
+ }
+ else
+ {
+ bool const ret = PostMessageW(hWnd, SALOBJ_MSG_POSTFOCUS, 0, 0);
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ rDef = FALSE;
+ break;
+
+ case WM_SIZE:
+ {
+ HWND hWndChild = GetWindow( hWnd, GW_CHILD );
+ if ( hWndChild )
+ {
+ SetWindowPos( hWndChild,
+ nullptr, 0, 0, static_cast<int>(LOWORD( lParam )), static_cast<int>(HIWORD( lParam )),
+ SWP_NOZORDER | SWP_NOACTIVATE );
+ }
+ }
+ rDef = FALSE;
+ break;
+
+ case WM_CREATE:
+ {
+ // Save the window instance at the window handle.
+ // Can also be used for the A-Version, because the struct
+ // to access lpCreateParams is the same structure
+ CREATESTRUCTW* pStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
+ pSysObj = static_cast<WinSalObject*>(pStruct->lpCreateParams);
+ SetSalObjWindowPtr( hWnd, pSysObj );
+ // set HWND already here,
+ // as instance data might be used during CreateWindow() events
+ pSysObj->mhWnd = hWnd;
+ rDef = FALSE;
+ }
+ break;
+ }
+
+ return nRet;
+}
+
+static LRESULT CALLBACK SalSysObjWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
+{
+ int bDef = TRUE;
+ LRESULT nRet = SalSysObjWndProc( hWnd, nMsg, wParam, lParam, bDef );
+ if ( bDef )
+ nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam );
+ return nRet;
+}
+
+static LRESULT CALLBACK SalSysObjChildWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, int& rDef )
+{
+ LRESULT nRet = 0;
+
+ switch( nMsg )
+ {
+ // clear background for plugins
+ case WM_ERASEBKGND:
+ {
+ WinSalObject* pSysObj = GetSalObjWindowPtr( ::GetParent( hWnd ) );
+
+ if( pSysObj && !pSysObj->IsEraseBackgroundEnabled() )
+ {
+ // do not erase background
+ nRet = 1;
+ rDef = FALSE;
+ }
+ }
+ break;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT aPs;
+ BeginPaint( hWnd, &aPs );
+ EndPaint( hWnd, &aPs );
+ rDef = FALSE;
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ {
+ WinSalObject* pSysObj;
+ pSysObj = GetSalObjWindowPtr( ::GetParent( hWnd ) );
+
+ if( pSysObj && pSysObj->IsMouseTransparent() )
+ {
+ // forward mouse events to parent frame
+ HWND hWndParent = ::GetParent( pSysObj->mhWnd );
+
+ // transform coordinates
+ POINT pt;
+ pt.x = static_cast<long>(LOWORD( lParam ));
+ pt.y = static_cast<long>(HIWORD( lParam ));
+ MapWindowPoints( hWnd, hWndParent, &pt, 1 );
+ lParam = MAKELPARAM( static_cast<WORD>(pt.x), static_cast<WORD>(pt.y) );
+
+ nRet = SendMessageW( hWndParent, nMsg, wParam, lParam );
+ rDef = FALSE;
+ }
+ }
+ break;
+ }
+
+ return nRet;
+}
+
+static LRESULT CALLBACK SalSysObjChildWndProcW( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
+{
+ int bDef = TRUE;
+ LRESULT nRet = SalSysObjChildWndProc( hWnd, nMsg, wParam, lParam, bDef );
+ if ( bDef )
+ nRet = DefWindowProcW( hWnd, nMsg, wParam, lParam );
+ return nRet;
+}
+
+SalObject* ImplSalCreateObject( WinSalInstance* pInst, WinSalFrame* pParent )
+{
+ SalData* pSalData = GetSalData();
+
+ // install hook, if it is the first SalObject
+ if ( !pSalData->mpFirstObject )
+ {
+ pSalData->mhSalObjMsgHook = SetWindowsHookExW( WH_CALLWNDPROC,
+ SalSysMsgProc,
+ pSalData->mhInst,
+ pSalData->mnAppThreadId );
+ }
+
+ if ( !pSalData->mbObjClassInit )
+ {
+ WNDCLASSEXW aWndClassEx;
+ aWndClassEx.cbSize = sizeof( aWndClassEx );
+ aWndClassEx.style = 0;
+ aWndClassEx.lpfnWndProc = SalSysObjWndProcW;
+ aWndClassEx.cbClsExtra = 0;
+ aWndClassEx.cbWndExtra = SAL_OBJECT_WNDEXTRA;
+ aWndClassEx.hInstance = pSalData->mhInst;
+ aWndClassEx.hIcon = nullptr;
+ aWndClassEx.hIconSm = nullptr;
+ aWndClassEx.hCursor = LoadCursor( nullptr, IDC_ARROW );
+ aWndClassEx.hbrBackground = nullptr;
+ aWndClassEx.lpszMenuName = nullptr;
+ aWndClassEx.lpszClassName = SAL_OBJECT_CLASSNAMEW;
+ if ( RegisterClassExW( &aWndClassEx ) )
+ {
+ // Clean background first because of plugins.
+ aWndClassEx.cbWndExtra = 0;
+ aWndClassEx.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
+ aWndClassEx.lpfnWndProc = SalSysObjChildWndProcW;
+ aWndClassEx.lpszClassName = SAL_OBJECT_CHILDCLASSNAMEW;
+ if ( RegisterClassExW( &aWndClassEx ) )
+ pSalData->mbObjClassInit = true;
+ }
+ }
+
+ if ( pSalData->mbObjClassInit )
+ {
+ WinSalObject* pObject = new WinSalObject;
+
+ // #135235# Clip siblings of this
+ // SystemChildWindow. Otherwise, DXCanvas (using a hidden
+ // SystemChildWindow) clobbers applets/plugins during
+ // animations .
+ HWND hWnd = CreateWindowExW( 0, SAL_OBJECT_CLASSNAMEW, L"",
+ WS_CHILD | WS_CLIPSIBLINGS, 0, 0, 0, 0,
+ pParent->mhWnd, nullptr,
+ pInst->mhInst, pObject );
+
+ HWND hWndChild = nullptr;
+ if ( hWnd )
+ {
+ // #135235# Explicitly stack SystemChildWindows in
+ // the order they're created - since there's no notion
+ // of zorder.
+ SetWindowPos(hWnd,HWND_TOP,0,0,0,0,
+ SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOREDRAW|SWP_NOSIZE);
+ hWndChild = CreateWindowExW( 0, SAL_OBJECT_CHILDCLASSNAMEW, L"",
+ WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE,
+ 0, 0, 0, 0,
+ hWnd, nullptr,
+ pInst->mhInst, nullptr );
+ }
+
+ if ( !hWndChild )
+ {
+ SAL_WARN("vcl", "CreateWindowExW failed: " << WindowsErrorString(GetLastError()));
+
+ delete pObject;
+ return nullptr;
+ }
+
+ if ( hWnd )
+ {
+ pObject->mhWnd = hWnd;
+ pObject->mhWndChild = hWndChild;
+ pObject->maSysData.hWnd = hWndChild;
+ return pObject;
+ }
+ }
+
+ return nullptr;
+}
+
+WinSalObject::WinSalObject()
+{
+ SalData* pSalData = GetSalData();
+
+ mhWnd = nullptr;
+ mhWndChild = nullptr;
+ mhLastFocusWnd = nullptr;
+ mpStdClipRgnData = nullptr;
+
+ // Insert object in objectlist
+ mpNextObject = pSalData->mpFirstObject;
+ pSalData->mpFirstObject = this;
+}
+
+WinSalObject::~WinSalObject()
+{
+ SalData* pSalData = GetSalData();
+
+ // remove frame from framelist
+ if ( this == pSalData->mpFirstObject )
+ {
+ pSalData->mpFirstObject = mpNextObject;
+
+ // remove hook, if it is the last SalObject
+ if ( !pSalData->mpFirstObject )
+ UnhookWindowsHookEx( pSalData->mhSalObjMsgHook );
+ }
+ else
+ {
+ WinSalObject* pTempObject = pSalData->mpFirstObject;
+ while ( pTempObject->mpNextObject != this )
+ pTempObject = pTempObject->mpNextObject;
+
+ pTempObject->mpNextObject = mpNextObject;
+ }
+
+ // destroy cache data
+ delete [] reinterpret_cast<BYTE*>(mpStdClipRgnData);
+
+ HWND hWndParent = ::GetParent( mhWnd );
+
+ if ( mhWndChild )
+ DestroyWindow( mhWndChild );
+ if ( mhWnd )
+ DestroyWindow( mhWnd );
+
+ // reset palette, if no external child window is left,
+ // as they might have overwritten our palette
+ if ( hWndParent &&
+ ::GetActiveWindow() == hWndParent &&
+ !GetWindow( hWndParent, GW_CHILD ) )
+ SendMessageW( hWndParent, SAL_MSG_FORCEPALETTE, 0, 0 );
+}
+
+void WinSalObject::ResetClipRegion()
+{
+ SetWindowRgn( mhWnd, nullptr, TRUE );
+}
+
+void WinSalObject::BeginSetClipRegion( sal_uInt32 nRectCount )
+{
+ sal_uLong nRectBufSize = sizeof(RECT)*nRectCount;
+ if ( nRectCount < SAL_CLIPRECT_COUNT )
+ {
+ if ( !mpStdClipRgnData )
+ mpStdClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+(SAL_CLIPRECT_COUNT*sizeof(RECT))]);
+ mpClipRgnData = mpStdClipRgnData;
+ }
+ else
+ mpClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+nRectBufSize]);
+ mpClipRgnData->rdh.dwSize = sizeof( RGNDATAHEADER );
+ mpClipRgnData->rdh.iType = RDH_RECTANGLES;
+ mpClipRgnData->rdh.nCount = nRectCount;
+ mpClipRgnData->rdh.nRgnSize = nRectBufSize;
+ SetRectEmpty( &(mpClipRgnData->rdh.rcBound) );
+ mpNextClipRect = reinterpret_cast<RECT*>(&(mpClipRgnData->Buffer));
+ mbFirstClipRect = true;
+}
+
+void WinSalObject::UnionClipRegion( long nX, long nY, long nWidth, long nHeight )
+{
+ RECT* pRect = mpNextClipRect;
+ RECT* pBoundRect = &(mpClipRgnData->rdh.rcBound);
+ long nRight = nX + nWidth;
+ 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);
+ mpNextClipRect++;
+}
+
+void WinSalObject::EndSetClipRegion()
+{
+ HRGN hRegion;
+
+ // create a ClipRegion from the vcl::Region data
+ 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 );
+ if ( mpClipRgnData != mpStdClipRgnData )
+ delete [] reinterpret_cast<BYTE*>(mpClipRgnData);
+ }
+
+ SAL_WARN_IF( !hRegion, "vcl", "SalObject::EndSetClipRegion() - Can't create ClipRegion" );
+ SetWindowRgn( mhWnd, hRegion, TRUE );
+}
+
+void WinSalObject::SetPosSize( long nX, long nY, long nWidth, long nHeight )
+{
+ sal_uLong nStyle = 0;
+ bool bVisible = (GetWindowStyle( mhWnd ) & WS_VISIBLE) != 0;
+ if ( bVisible )
+ {
+ ShowWindow( mhWnd, SW_HIDE );
+ nStyle |= SWP_SHOWWINDOW;
+ }
+ SetWindowPos( mhWnd, nullptr,
+ static_cast<int>(nX), static_cast<int>(nY), static_cast<int>(nWidth), static_cast<int>(nHeight),
+ SWP_NOZORDER | SWP_NOACTIVATE | nStyle );
+}
+
+void WinSalObject::Show( bool bVisible )
+{
+ if ( bVisible )
+ ShowWindow( mhWnd, SW_SHOWNORMAL );
+ else
+ ShowWindow( mhWnd, SW_HIDE );
+}
+
+void WinSalObject::Enable( bool bEnable )
+{
+ EnableWindow( mhWnd, bEnable );
+}
+
+void WinSalObject::GrabFocus()
+{
+ if ( mhLastFocusWnd &&
+ IsWindow( mhLastFocusWnd ) &&
+ ImplIsSysWindowOrChild( mhWndChild, mhLastFocusWnd ) )
+ ::SetFocus( mhLastFocusWnd );
+ else
+ ::SetFocus( mhWndChild );
+}
+
+const SystemEnvData* WinSalObject::GetSystemData() const
+{
+ return &maSysData;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */