summaryrefslogtreecommitdiffstats
path: root/vcl/win
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /vcl/win
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/win')
-rw-r--r--vcl/win/app/fileregistration.cxx182
-rw-r--r--vcl/win/app/saldata.cxx78
-rw-r--r--vcl/win/app/salinfo.cxx177
-rw-r--r--vcl/win/app/salinst.cxx1008
-rw-r--r--vcl/win/app/salshl.cxx112
-rw-r--r--vcl/win/app/saltimer.cxx201
-rw-r--r--vcl/win/dtrans/APNDataObject.cxx320
-rw-r--r--vcl/win/dtrans/APNDataObject.hxx74
-rw-r--r--vcl/win/dtrans/DOTransferable.cxx577
-rw-r--r--vcl/win/dtrans/DOTransferable.hxx98
-rw-r--r--vcl/win/dtrans/DTransHelper.cxx205
-rw-r--r--vcl/win/dtrans/DTransHelper.hxx167
-rw-r--r--vcl/win/dtrans/DataFmtTransl.cxx252
-rw-r--r--vcl/win/dtrans/DataFmtTransl.hxx64
-rw-r--r--vcl/win/dtrans/DtObjFactory.cxx34
-rw-r--r--vcl/win/dtrans/DtObjFactory.hxx33
-rw-r--r--vcl/win/dtrans/Fetc.cxx166
-rw-r--r--vcl/win/dtrans/Fetc.hxx76
-rw-r--r--vcl/win/dtrans/FetcList.cxx340
-rw-r--r--vcl/win/dtrans/FetcList.hxx139
-rw-r--r--vcl/win/dtrans/FmtFilter.cxx435
-rw-r--r--vcl/win/dtrans/FmtFilter.hxx84
-rw-r--r--vcl/win/dtrans/ImplHelper.cxx352
-rw-r--r--vcl/win/dtrans/ImplHelper.hxx74
-rw-r--r--vcl/win/dtrans/MimeAttrib.hxx31
-rw-r--r--vcl/win/dtrans/MtaOleClipb.cxx748
-rw-r--r--vcl/win/dtrans/MtaOleClipb.hxx110
-rw-r--r--vcl/win/dtrans/TxtCnvtHlp.cxx125
-rw-r--r--vcl/win/dtrans/TxtCnvtHlp.hxx41
-rw-r--r--vcl/win/dtrans/WinClip.hxx26
-rw-r--r--vcl/win/dtrans/WinClipboard.cxx383
-rw-r--r--vcl/win/dtrans/WinClipboard.hxx112
-rw-r--r--vcl/win/dtrans/XNotifyingDataObject.cxx149
-rw-r--r--vcl/win/dtrans/XNotifyingDataObject.hxx84
-rw-r--r--vcl/win/dtrans/XTDataObject.cxx751
-rw-r--r--vcl/win/dtrans/XTDataObject.hxx134
-rw-r--r--vcl/win/dtrans/clipboardmanager.cxx198
-rw-r--r--vcl/win/dtrans/clipboardmanager.hxx89
-rw-r--r--vcl/win/dtrans/ftransl.cxx550
-rw-r--r--vcl/win/dtrans/ftransl.hxx59
-rw-r--r--vcl/win/dtrans/generic_clipboard.cxx136
-rw-r--r--vcl/win/dtrans/generic_clipboard.hxx101
-rw-r--r--vcl/win/dtrans/globals.cxx130
-rw-r--r--vcl/win/dtrans/globals.hxx70
-rw-r--r--vcl/win/dtrans/idroptarget.cxx96
-rw-r--r--vcl/win/dtrans/idroptarget.hxx65
-rw-r--r--vcl/win/dtrans/source.cxx373
-rw-r--r--vcl/win/dtrans/sourcecontext.cxx134
-rw-r--r--vcl/win/dtrans/sourcecontext.hxx66
-rw-r--r--vcl/win/dtrans/target.cxx646
-rw-r--r--vcl/win/dtrans/targetdragcontext.cxx39
-rw-r--r--vcl/win/dtrans/targetdragcontext.hxx50
-rw-r--r--vcl/win/dtrans/targetdropcontext.cxx50
-rw-r--r--vcl/win/dtrans/targetdropcontext.hxx52
-rw-r--r--vcl/win/gdi/DWriteTextRenderer.cxx341
-rw-r--r--vcl/win/gdi/dw-extra.h141
-rw-r--r--vcl/win/gdi/gdiimpl.cxx2731
-rw-r--r--vcl/win/gdi/gdiimpl.hxx253
-rw-r--r--vcl/win/gdi/salbmp.cxx910
-rw-r--r--vcl/win/gdi/salfont.cxx1377
-rw-r--r--vcl/win/gdi/salgdi.cxx1139
-rw-r--r--vcl/win/gdi/salgdi2.cxx240
-rw-r--r--vcl/win/gdi/salgdi_gdiplus.cxx104
-rw-r--r--vcl/win/gdi/salnativewidgets-luna.cxx1634
-rw-r--r--vcl/win/gdi/salprn.cxx1604
-rw-r--r--vcl/win/gdi/salvd.cxx223
-rw-r--r--vcl/win/gdi/winlayout.cxx235
-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/fatcross.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.rc90
-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.cxx225
-rw-r--r--vcl/win/window/salframe.cxx6099
-rw-r--r--vcl/win/window/salmenu.cxx329
-rw-r--r--vcl/win/window/salobj.cxx679
138 files changed, 29200 insertions, 0 deletions
diff --git a/vcl/win/app/fileregistration.cxx b/vcl/win/app/fileregistration.cxx
new file mode 100644
index 0000000000..ec7ccbcee2
--- /dev/null
+++ b/vcl/win/app/fileregistration.cxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+
+#if !defined(NTDDI_VERSION) || NTDDI_VERSION < NTDDI_WIN8
+#define NTDDI_VERSION NTDDI_WIN8 // needed for IApplicationActivationManager
+#endif
+
+#include <sal/config.h>
+
+#include <comphelper/scopeguard.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <unotools/resmgr.hxx>
+#include <vcl/abstdlg.hxx>
+#include <vcl/fileregistration.hxx>
+
+#include <strings.hrc>
+#include <svdata.hxx>
+
+#include <utility>
+
+#include <prewin.h>
+#include <Shobjidl.h>
+#include <systools/win32/comtools.hxx>
+#include <versionhelpers.h>
+#include <postwin.h>
+
+#define MAX_LONG_PATH 32767
+
+namespace vcl::fileregistration
+{
+static void LaunchModernSettingsDialogDefaultApps()
+{
+ sal::systools::COMReference<IApplicationActivationManager> pIf(
+ CLSID_ApplicationActivationManager, nullptr, CLSCTX_INPROC_SERVER);
+
+ DWORD pid;
+ HRESULT hr = pIf->ActivateApplication(L"windows.immersivecontrolpanel_cw5n1h2txyewy"
+ L"!microsoft.windows.immersivecontrolpanel",
+ L"page=SettingsPageAppsDefaults", AO_NONE, &pid);
+ if (SUCCEEDED(hr))
+ {
+ // Do not check error because we could at least open
+ // the "Default apps" setting.
+ pIf->ActivateApplication(L"windows.immersivecontrolpanel_cw5n1h2txyewy"
+ L"!microsoft.windows.immersivecontrolpanel",
+ L"page=SettingsPageAppsDefaults"
+ L"&target=SettingsPageAppsDefaultsDefaultAppsListView",
+ AO_NONE, &pid);
+ }
+}
+
+static HRESULT
+IsPathDefaultForClass(sal::systools::COMReference<IApplicationAssociationRegistration>& pAAR,
+ LPCWSTR aClassName, LPCWSTR progID)
+{
+ // Make sure the Prog ID matches what we have
+ sal::systools::CoTaskMemAllocated<wchar_t> registeredApp;
+ HRESULT hr
+ = pAAR->QueryCurrentDefault(aClassName, AT_FILEEXTENSION, AL_EFFECTIVE, &registeredApp);
+ if (SUCCEEDED(hr))
+ {
+ if (wcsnicmp(registeredApp, progID, wcslen(progID)) == 0)
+ hr = S_OK;
+ else
+ hr = S_FALSE;
+ }
+
+ return hr;
+}
+
+static bool IsDefaultAppInstalledInReg()
+{
+ const wchar_t* keyPath = L"SOFTWARE\\LibreOffice\\UNO\\InstallPath";
+
+ WCHAR szRegPath[MAX_LONG_PATH];
+ DWORD cbData = static_cast<DWORD>(MAX_LONG_PATH * sizeof(WCHAR));
+ auto rc = RegGetValueW(HKEY_LOCAL_MACHINE, keyPath, nullptr, RRF_RT_REG_SZ, nullptr,
+ static_cast<PVOID>(szRegPath), &cbData);
+ if (rc != ERROR_SUCCESS)
+ return false;
+
+ WCHAR szProcPath[MAX_LONG_PATH];
+ if (!GetModuleFileNameW(nullptr, szProcPath, MAX_LONG_PATH))
+ return false;
+
+ WCHAR szFullProcPath[MAX_LONG_PATH];
+ if (!GetFullPathNameW(szProcPath, MAX_LONG_PATH, szFullProcPath, nullptr))
+ return false;
+
+ if (!GetLongPathNameW(szFullProcPath, szFullProcPath, MAX_LONG_PATH))
+ return false;
+
+ if (!GetLongPathNameW(szRegPath, szRegPath, MAX_LONG_PATH))
+ return false;
+
+ if (wcslen(szRegPath) > 0 && wcsstr(szFullProcPath, szRegPath) != nullptr)
+ return true;
+
+ return false;
+}
+
+void LaunchRegistrationUI()
+{
+ try
+ {
+ sal::systools::CoInitializeGuard aGuard(COINIT_APARTMENTTHREADED);
+ if (IsWindows10OrGreater())
+ {
+ LaunchModernSettingsDialogDefaultApps();
+ }
+ else
+ {
+ sal::systools::COMReference<IApplicationAssociationRegistrationUI> pIf(
+ CLSID_ApplicationAssociationRegistrationUI, nullptr, CLSCTX_INPROC_SERVER);
+
+ // LaunchAdvancedAssociationUI only works for applications registered under
+ // Software\RegisteredApplications. See scp2/source/ooo/registryitem_ooo.scp
+ const OUString expanded = Translate::ExpandVariables("%PRODUCTNAME %PRODUCTVERSION");
+ pIf->LaunchAdvancedAssociationUI(o3tl::toW(expanded.getStr()));
+ }
+ }
+ catch (...)
+ {
+ // Just ignore any error here: this is not something we need to make sure to succeed
+ }
+}
+
+void CheckFileExtRegistration(weld::Window* pDialogParent)
+{
+ if (!officecfg::Office::Common::Misc::PerformFileExtCheck::get())
+ return;
+
+ if (!IsDefaultAppInstalledInReg())
+ return;
+
+ sal::systools::CoInitializeGuard aGuard(COINIT_APARTMENTTHREADED, false,
+ sal::systools::CoInitializeGuard::WhenFailed::NoThrow);
+ sal::systools::COMReference<IApplicationAssociationRegistration> pAAR;
+ try
+ {
+ pAAR.CoCreateInstance(CLSID_ApplicationAssociationRegistration, nullptr,
+ CLSCTX_INPROC_SERVER);
+ }
+ catch (...)
+ {
+ // Just return on any error here: this is not something we need to make sure to succeed
+ return;
+ }
+
+ static const std::pair<LPCWSTR, LPCWSTR> formats[] = {
+ { L".odp", L"LibreOffice.ImpressDocument.1" },
+ { L".odt", L"LibreOffice.WriterDocument.1" },
+ { L".ods", L"LibreOffice.CalcDocument.1" },
+ };
+ OUString aNonDefaults;
+
+ for (const auto & [ szExt, szProgId ] : formats)
+ {
+ if (IsPathDefaultForClass(pAAR, szExt, szProgId) == S_FALSE)
+ aNonDefaults += OUString::Concat(o3tl::toU(szExt)) + "\n";
+ }
+
+ if (!aNonDefaults.isEmpty())
+ {
+ OUString aMsg(VclResId(STR_FILEEXT_NONDEFAULT_ASK_MSG).replaceFirst("$1", aNonDefaults));
+
+ VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
+ ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateFileExtCheckDialog(
+ pDialogParent, VclResId(STR_FILEEXT_NONDEFAULT_ASK_TITLE), aMsg));
+ pDlg->Execute();
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/vcl/win/app/saldata.cxx b/vcl/win/app/saldata.cxx
new file mode 100644
index 0000000000..31fa661636
--- /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 0000000000..23d542d23b
--- /dev/null
+++ b/vcl/win/app/salinfo.cxx
@@ -0,0 +1,177 @@
+/* -*- 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 = AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint( aInfo.rcMonitor.left,
+ aInfo.rcMonitor.top ),
+ AbsoluteScreenPixelSize( 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 );
+ AbsoluteScreenPixelRectangle aRect(AbsoluteScreenPixelPoint(), AbsoluteScreenPixelSize( w, h ));
+ m_aMonitors.push_back( DisplayMonitor( OUString(), aRect ) );
+ 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,
+ AbsoluteScreenPixelRectangle() ) );
+ }
+ }
+ 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 ) + ")";
+ }
+ }
+ ReleaseDC(nullptr, aDesktopRC);
+ }
+
+ return m_aMonitors.size() > 0;
+}
+
+unsigned int WinSalSystem::GetDisplayScreenCount()
+{
+ initMonitors();
+ return m_aMonitors.size();
+}
+
+unsigned int WinSalSystem::GetDisplayBuiltInScreen()
+{
+ initMonitors();
+ return m_nPrimary;
+}
+
+AbsoluteScreenPixelRectangle 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 AbsoluteScreenPixelRectangle();
+ }
+ 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 0000000000..eb4adb853e
--- /dev/null
+++ b/vcl/win/app/salinst.cxx
@@ -0,0 +1,1008 @@
+/* -*- 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/processfactory.hxx>
+#include <comphelper/solarmutex.hxx>
+#include <comphelper/windowserrorstring.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+
+#include <dndhelper.hxx>
+#include <vcl/inputtypes.hxx>
+#include <vcl/opengl/OpenGLContext.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/timer.hxx>
+#include <vclpluginapi.h>
+
+#include <win/dnd_source.hxx>
+#include <win/dnd_target.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>
+
+#include <prewin.h>
+
+#include <gdiplus.h>
+#include <shlobj.h>
+
+#include <postwin.h>
+
+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, which is called in
+ // m_condition.wait(). 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 {
+ // Calling Condition::reset frequently turns out to be a little expensive,
+ // and the vast majority of the time there is no contention, so first
+ // try just acquiring the mutex.
+ if (m_aMutex.tryToAcquire())
+ break;
+ // 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);
+ (void) 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 );
+ initKey( L'}', KEY_RIGHTCURLYBRACKET );
+ initKey( L'#', KEY_NUMBERSIGN);
+ initKey( L':', KEY_COLON );
+}
+
+// SalData
+
+SalData::SalData()
+ : sal::systools::CoInitializeGuard(COINIT_APARTMENTTHREADED, false,
+ sal::systools::CoInitializeGuard::WhenFailed::NoThrow)
+ // put main thread in Single Threaded Apartment (STA)
+{
+ 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
+ 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
+ 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
+ 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();
+
+ static Gdiplus::GdiplusStartupInput gdiplusStartupInput;
+ Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);
+}
+
+SalData::~SalData()
+{
+ deInitNWF();
+ SetSalData( nullptr );
+
+ if (gdiplusToken)
+ Gdiplus::GdiplusShutdown(gdiplusToken);
+}
+
+bool OSSupportsDarkMode()
+{
+ bool bRet = false;
+ if (HMODULE h_ntdll = GetModuleHandleW(L"ntdll.dll"))
+ {
+ typedef LONG(WINAPI* RtlGetVersion_t)(PRTL_OSVERSIONINFOW);
+ if (auto RtlGetVersion
+ = reinterpret_cast<RtlGetVersion_t>(GetProcAddress(h_ntdll, "RtlGetVersion")))
+ {
+ RTL_OSVERSIONINFOW vi2{};
+ vi2.dwOSVersionInfoSize = sizeof(vi2);
+ if (RtlGetVersion(&vi2) == 0)
+ {
+ if (vi2.dwMajorVersion > 10)
+ bRet = true;
+ else if (vi2.dwMajorVersion == 10)
+ {
+ if (vi2.dwMinorVersion > 0)
+ bRet = true;
+ else if (vi2.dwBuildNumber >= 18362)
+ bRet = true;
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+namespace {
+
+enum PreferredAppMode
+{
+ Default,
+ AllowDark,
+ ForceDark,
+ ForceLight,
+ Max
+};
+
+}
+
+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();
+
+ static bool bSetAllowDarkMode = OSSupportsDarkMode(); // too early to additionally check LibreOffice's config
+ if (bSetAllowDarkMode)
+ {
+ typedef PreferredAppMode(WINAPI* SetPreferredAppMode_t)(PreferredAppMode);
+ if (HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
+ {
+ if (auto SetPreferredAppMode = reinterpret_cast<SetPreferredAppMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(135))))
+ SetPreferredAppMode(AllowDark);
+ FreeLibrary(hUxthemeLib);
+ }
+ }
+
+ // 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");
+ m_bSupportsOpenGL = true;
+#if HAVE_FEATURE_SKIA
+ WinSkiaSalGraphicsImpl::prepareSkia();
+#if SKIA_USE_BITMAP32
+ if (SkiaHelper::isVCLSkiaEnabled())
+ m_bSupportsBitmap32 = true;
+#endif
+#endif
+}
+
+WinSalInstance::~WinSalInstance()
+{
+ ImplFreeSalGDI();
+ DestroyWindow( mhComWnd );
+#if HAVE_FEATURE_SKIA
+ SkiaHelper::cleanup();
+#endif
+}
+
+void WinSalInstance::AfterAppInit()
+{
+// (1) Ideally this would be done at the place that creates the thread, but since this thread is normally
+// just the default/main thread, that is not possible.
+// (2) Don't do this on unix, where it causes tools like pstree on Linux to
+// confusingly report soffice.bin as VCL Main instead.
+ osl_setThreadName("VCL Main");
+}
+
+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;
+}
+
+// probably can't be static, because of SalTimer friend? (static gives C4211)
+bool ImplSalYield(const bool bWait, const bool bHandleAllCurrentEvents)
+{
+ // used to abort further message processing on tick count wraps
+ static sal_uInt32 nLastTicks = 0;
+
+ // we should never yield in m_nNoYieldLock mode!
+ const bool bNoYieldLock = (GetSalData()->mpInstance->m_nNoYieldLock > 0);
+ assert(!bNoYieldLock);
+ if (bNoYieldLock)
+ return false;
+
+ MSG aMsg;
+ bool bWasMsg = false, bWasTimeoutMsg = false;
+ WinSalTimer* pTimer = static_cast<WinSalTimer*>(ImplGetSVData()->maSchedCtx.mpSalTimer);
+
+ sal_uInt32 nCurTicks = GetTickCount();
+
+ do
+ {
+ if (!PeekMessageW(&aMsg, nullptr, 0, 0, PM_REMOVE))
+ break;
+
+ bWasMsg = true;
+ TranslateMessage(&aMsg);
+ LRESULT nRet = ImplSalDispatchMessage(&aMsg);
+
+ bWasTimeoutMsg |= (SAL_MSG_TIMER_CALLBACK == aMsg.message) && static_cast<bool>(nRet);
+
+ if (!bHandleAllCurrentEvents)
+ break;
+
+ if ((aMsg.time > nCurTicks) && (nLastTicks <= nCurTicks || aMsg.time < nLastTicks))
+ 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;
+ }
+
+ nLastTicks = nCurTicks;
+
+ if ( bWait && !bWasMsg )
+ {
+ switch (GetMessageW(&aMsg, nullptr, 0, 0))
+ {
+ case -1:
+ SAL_WARN("vcl.schedule", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
+ // should we std::abort() / SalAbort here?
+ break;
+ case 0:
+ SAL_INFO("vcl.schedule", "GetMessageW received WM_QUIT while waiting");
+ break;
+ default:
+ bWasMsg = true;
+ TranslateMessage(&aMsg);
+ ImplSalDispatchMessage(&aMsg);
+ break;
+ }
+ }
+
+ // If we enabled ForceRealTimer mode skipping our direct timeout processing,
+ // mainly because some Windows API call spawns its own nested message loop,
+ // switch back to our own processing (like after window 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);
+
+ if (ImplGetSVData()->mbDeInit)
+ {
+ SAL_WARN("vcl.gdi.wndproc", "ignoring timer event because we are shutting down");
+ return 0;
+ }
+
+ 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;
+}
+
+bool WinSalInstance::AnyInput( VclInputFlags nType )
+{
+ if ( nType & VclInputFlags::TIMER )
+ {
+ const WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
+ if ( pTimer && pTimer->HasTimerElapsed() )
+ return true;
+ }
+
+ // Note: Do not use PeekMessage(), despite the name it may dispatch events,
+ // even with PM_NOREMOVE specified, which may lead to unwanted recursion.
+
+ 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 ( GetQueueStatus( QS_ALLEVENTS ))
+ return true;
+ }
+ else
+ {
+ UINT flags = 0;
+
+ // This code previously considered modifier keys as OTHER,
+ // but that makes this hard to do without PeekMessage,
+ // is inconsistent with the X11 backend, and I see no good reason.
+ if ( nType & VclInputFlags::KEYBOARD )
+ flags |= QS_KEY;
+
+ if ( nType & VclInputFlags::MOUSE )
+ flags |= QS_MOUSE;
+
+ if ( nType & VclInputFlags::PAINT )
+ flags |= QS_PAINT;
+
+ if ( nType & VclInputFlags::TIMER )
+ flags |= QS_TIMER;
+
+ if( nType & VclInputFlags::OTHER )
+ flags |= QS_ALLEVENTS & ~QS_KEY & ~QS_MOUSE & ~QS_PAINT & ~QS_TIMER;
+
+ if( GetQueueStatus( flags ))
+ 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
+ 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(OUString::Concat(o3tl::toU(vi2.szCSDVersion)) + " ");
+ aVer.append("Build " + OUString::number(vi2.dwBuildNumber));
+ bHaveVerFromRtlGetVersion = true;
+ }
+ }
+ }
+ if (!bHaveVerFromKernel32 && !bHaveVerFromRtlGetVersion)
+ aVer.append("unknown");
+ return aVer.makeStringAndClear();
+}
+
+void WinSalInstance::BeforeAbort(const OUString&, bool)
+{
+ ImplFreeSalGDI();
+}
+
+css::uno::Reference<css::uno::XInterface> WinSalInstance::ImplCreateDragSource(const SystemEnvData* pSysEnv)
+{
+ return vcl::OleDnDHelper(new DragSource(comphelper::getProcessComponentContext()),
+ reinterpret_cast<sal_IntPtr>(pSysEnv->hWnd), vcl::DragOrDrop::Drag);
+}
+
+css::uno::Reference<css::uno::XInterface> WinSalInstance::ImplCreateDropTarget(const SystemEnvData* pSysEnv)
+{
+ return vcl::OleDnDHelper(new DropTarget(comphelper::getProcessComponentContext()),
+ reinterpret_cast<sal_IntPtr>(pSysEnv->hWnd), vcl::DragOrDrop::Drop);
+}
+
+/* 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 0000000000..5619ece1f0
--- /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 0000000000..1ccab54e96
--- /dev/null
+++ b/vcl/win/app/saltimer.cxx
@@ -0,0 +1,201 @@
+/* -*- 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( pInst->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 )
+{
+#if !defined NDEBUG
+ SalData* pSalData = GetSalData();
+ assert( !pSalData->mpInstance || pSalData->mnAppThreadId == GetCurrentThreadId() );
+#endif
+
+ // 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/dtrans/APNDataObject.cxx b/vcl/win/dtrans/APNDataObject.cxx
new file mode 100644
index 0000000000..2492647f26
--- /dev/null
+++ b/vcl/win/dtrans/APNDataObject.cxx
@@ -0,0 +1,320 @@
+/* -*- 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 "APNDataObject.hxx"
+#include <osl/diagnose.h>
+
+#include <systools/win32/comtools.hxx>
+
+#define FREE_HGLOB_ON_RELEASE TRUE
+#define KEEP_HGLOB_ON_RELEASE FALSE
+
+// ctor
+
+CAPNDataObject::CAPNDataObject( IDataObjectPtr rIDataObject ) :
+ m_rIDataObjectOrg( rIDataObject ),
+ m_hGlobal( nullptr ),
+ m_nRefCnt( 0 )
+{
+
+ OSL_ENSURE( m_rIDataObjectOrg.get( ), "constructing CAPNDataObject with empty data object" );
+
+ // we marshal the IDataObject interface pointer here so
+ // that it can be unmarshalled multiple times when this
+ // class will be used from another apartment
+ sal::systools::COMReference<IStream> pStm;
+ HRESULT hr = CreateStreamOnHGlobal( nullptr, KEEP_HGLOB_ON_RELEASE, &pStm );
+
+ OSL_ENSURE( E_INVALIDARG != hr, "invalid args passed to CreateStreamOnHGlobal" );
+
+ if ( SUCCEEDED( hr ) )
+ {
+ HRESULT hr_marshal = CoMarshalInterface(
+ pStm,
+ __uuidof(IDataObject),
+ m_rIDataObjectOrg,
+ MSHCTX_LOCAL,
+ nullptr,
+ MSHLFLAGS_TABLEWEAK );
+
+ OSL_ENSURE( CO_E_NOTINITIALIZED != hr_marshal, "COM is not initialized" );
+
+ // marshalling may fail if COM is not initialized
+ // for the calling thread which is a program time
+ // error or because of stream errors which are runtime
+ // errors for instance E_OUTOFMEMORY etc.
+
+ hr = GetHGlobalFromStream(pStm, &m_hGlobal );
+
+ OSL_ENSURE( E_INVALIDARG != hr, "invalid stream passed to GetHGlobalFromStream" );
+
+ // if the marshalling failed we free the
+ // global memory again and set m_hGlobal
+ // to a defined value
+ if (FAILED(hr_marshal))
+ {
+ OSL_FAIL("marshalling failed");
+
+ HGLOBAL hGlobal =
+ GlobalFree(m_hGlobal);
+ OSL_ENSURE(nullptr == hGlobal, "GlobalFree failed");
+ m_hGlobal = nullptr;
+ }
+ }
+}
+
+CAPNDataObject::~CAPNDataObject( )
+{
+ if (m_hGlobal)
+ {
+ sal::systools::COMReference<IStream> pStm;
+ HRESULT hr = CreateStreamOnHGlobal(m_hGlobal, FREE_HGLOB_ON_RELEASE, &pStm);
+
+ OSL_ENSURE( E_INVALIDARG != hr, "invalid args passed to CreateStreamOnHGlobal" );
+
+ if (SUCCEEDED(hr))
+ {
+ hr = CoReleaseMarshalData(pStm);
+ OSL_ENSURE(SUCCEEDED(hr), "CoReleaseMarshalData failed");
+ }
+ }
+}
+
+// IUnknown->QueryInterface
+
+STDMETHODIMP CAPNDataObject::QueryInterface( REFIID iid, void** ppvObject )
+{
+ OSL_ASSERT( nullptr != ppvObject );
+
+ if ( nullptr == ppvObject )
+ return E_INVALIDARG;
+
+ HRESULT hr = E_NOINTERFACE;
+ *ppvObject = nullptr;
+
+ if ( ( __uuidof( IUnknown ) == iid ) || ( __uuidof( IDataObject ) == iid ) )
+ {
+ *ppvObject = static_cast< IUnknown* >( this );
+ AddRef( );
+ hr = S_OK;
+ }
+
+ return hr;
+}
+
+// IUnknown->AddRef
+
+STDMETHODIMP_(ULONG) CAPNDataObject::AddRef( )
+{
+ return static_cast< ULONG >( InterlockedIncrement( &m_nRefCnt ) );
+}
+
+// IUnknown->Release
+
+STDMETHODIMP_(ULONG) CAPNDataObject::Release( )
+{
+ // we need a helper variable because it's not allowed to access
+ // a member variable after an object is destroyed
+ ULONG nRefCnt = static_cast< ULONG >( InterlockedDecrement( &m_nRefCnt ) );
+
+ if ( 0 == nRefCnt )
+ delete this;
+
+ return nRefCnt;
+}
+
+// IDataObject->GetData
+
+STDMETHODIMP CAPNDataObject::GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium )
+{
+ HRESULT hr = m_rIDataObjectOrg->GetData( pFormatetc, pmedium );
+
+ if (RPC_E_WRONG_THREAD == hr)
+ {
+ IDataObjectPtr pIDOTmp;
+ hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
+
+ if (SUCCEEDED(hr))
+ hr = pIDOTmp->GetData(pFormatetc, pmedium);
+ }
+ return hr;
+}
+
+// IDataObject->EnumFormatEtc
+
+STDMETHODIMP CAPNDataObject::EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc )
+{
+ HRESULT hr = m_rIDataObjectOrg->EnumFormatEtc(dwDirection, ppenumFormatetc);
+
+ if (RPC_E_WRONG_THREAD == hr)
+ {
+ IDataObjectPtr pIDOTmp;
+ hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
+
+ if (SUCCEEDED(hr))
+ hr = pIDOTmp->EnumFormatEtc(dwDirection, ppenumFormatetc);
+ }
+ return hr;
+}
+
+// IDataObject->QueryGetData
+
+STDMETHODIMP CAPNDataObject::QueryGetData( FORMATETC * pFormatetc )
+{
+ HRESULT hr = m_rIDataObjectOrg->QueryGetData( pFormatetc );
+
+ if (RPC_E_WRONG_THREAD == hr)
+ {
+ IDataObjectPtr pIDOTmp;
+ hr = MarshalIDataObjectIntoCurrentApartment( &pIDOTmp );
+
+ if (SUCCEEDED(hr))
+ hr = pIDOTmp->QueryGetData(pFormatetc);
+ }
+ return hr;
+}
+
+// IDataObject->GetDataHere
+
+STDMETHODIMP CAPNDataObject::GetDataHere( FORMATETC * pFormatetc, STGMEDIUM * pmedium )
+{
+ HRESULT hr = m_rIDataObjectOrg->GetDataHere(pFormatetc, pmedium);
+
+ if (RPC_E_WRONG_THREAD == hr)
+ {
+ IDataObjectPtr pIDOTmp;
+ hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
+
+ if (SUCCEEDED(hr))
+ hr = pIDOTmp->GetDataHere(pFormatetc, pmedium);
+ }
+ return hr;
+}
+
+// IDataObject->GetCanonicalFormatEtc
+
+STDMETHODIMP CAPNDataObject::GetCanonicalFormatEtc(FORMATETC * pFormatectIn, FORMATETC * pFormatetcOut)
+{
+ HRESULT hr = m_rIDataObjectOrg->GetCanonicalFormatEtc( pFormatectIn, pFormatetcOut );
+
+ if (RPC_E_WRONG_THREAD == hr)
+ {
+ IDataObjectPtr pIDOTmp;
+ hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
+
+ if (SUCCEEDED(hr))
+ hr = pIDOTmp->GetCanonicalFormatEtc(pFormatectIn, pFormatetcOut);
+ }
+ return hr;
+}
+
+// IDataObject->SetData
+
+STDMETHODIMP CAPNDataObject::SetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease )
+{
+ HRESULT hr = m_rIDataObjectOrg->SetData( pFormatetc, pmedium, fRelease );
+
+ if (RPC_E_WRONG_THREAD == hr)
+ {
+ IDataObjectPtr pIDOTmp;
+ hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
+
+ if (SUCCEEDED(hr))
+ hr = pIDOTmp->SetData(pFormatetc, pmedium, fRelease);
+ }
+ return hr;
+}
+
+// IDataObject->DAdvise
+
+STDMETHODIMP CAPNDataObject::DAdvise( FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD * pdwConnection )
+{
+ HRESULT hr = m_rIDataObjectOrg->DAdvise(pFormatetc, advf, pAdvSink, pdwConnection);
+
+ if (RPC_E_WRONG_THREAD == hr)
+ {
+ IDataObjectPtr pIDOTmp;
+ hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
+
+ if (SUCCEEDED(hr))
+ hr = pIDOTmp->DAdvise(pFormatetc, advf, pAdvSink, pdwConnection);
+ }
+ return hr;
+}
+
+// IDataObject->DUnadvise
+
+STDMETHODIMP CAPNDataObject::DUnadvise( DWORD dwConnection )
+{
+ HRESULT hr = m_rIDataObjectOrg->DUnadvise( dwConnection );
+
+ if (RPC_E_WRONG_THREAD == hr)
+ {
+ IDataObjectPtr pIDOTmp;
+ hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
+
+ if (SUCCEEDED(hr))
+ hr = pIDOTmp->DUnadvise(dwConnection);
+ }
+ return hr;
+}
+
+// IDataObject->EnumDAdvise
+
+STDMETHODIMP CAPNDataObject::EnumDAdvise( IEnumSTATDATA ** ppenumAdvise )
+{
+ HRESULT hr = m_rIDataObjectOrg->EnumDAdvise(ppenumAdvise);
+
+ if (RPC_E_WRONG_THREAD == hr)
+ {
+ IDataObjectPtr pIDOTmp;
+ hr = MarshalIDataObjectIntoCurrentApartment(&pIDOTmp);
+
+ if (SUCCEEDED(hr))
+ hr = pIDOTmp->EnumDAdvise(ppenumAdvise);
+ }
+ return hr;
+}
+
+// helper function
+
+HRESULT CAPNDataObject::MarshalIDataObjectIntoCurrentApartment( IDataObject** ppIDataObj )
+{
+ OSL_ASSERT(nullptr != ppIDataObj);
+
+ *ppIDataObj = nullptr;
+ HRESULT hr = E_FAIL;
+
+ if (m_hGlobal)
+ {
+ sal::systools::COMReference<IStream> pStm;
+ hr = CreateStreamOnHGlobal(m_hGlobal, KEEP_HGLOB_ON_RELEASE, &pStm);
+
+ OSL_ENSURE(E_INVALIDARG != hr, "CreateStreamOnHGlobal with invalid args called");
+
+ if (SUCCEEDED(hr))
+ {
+ hr = CoUnmarshalInterface(pStm, IID_PPV_ARGS(ppIDataObj));
+ OSL_ENSURE(CO_E_NOTINITIALIZED != hr, "COM is not initialized");
+ }
+ }
+ return hr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/APNDataObject.hxx b/vcl/win/dtrans/APNDataObject.hxx
new file mode 100644
index 0000000000..5de8ccfcc8
--- /dev/null
+++ b/vcl/win/dtrans/APNDataObject.hxx
@@ -0,0 +1,74 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <objidl.h>
+
+#include <systools/win32/comtools.hxx>
+
+/*
+ an APartment Neutral dataobject wrapper; this wrapper of an IDataObject
+ pointer can be used from any apartment without RPC_E_WRONG_THREAD
+ which normally occurs if an apartment tries to use an interface
+ pointer of another apartment; we use containment to hold the original
+ DataObject
+*/
+class CAPNDataObject : public IDataObject
+{
+public:
+ explicit CAPNDataObject(IDataObjectPtr rIDataObject);
+ virtual ~CAPNDataObject();
+
+ //IUnknown interface methods
+
+ STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override;
+ STDMETHODIMP_( ULONG ) AddRef( ) override;
+ STDMETHODIMP_( ULONG ) Release( ) override;
+
+ // IDataObject interface methods
+
+ STDMETHODIMP GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override;
+ STDMETHODIMP GetDataHere( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override;
+ STDMETHODIMP QueryGetData( FORMATETC * pFormatetc ) override;
+ STDMETHODIMP GetCanonicalFormatEtc( FORMATETC * pFormatectIn, FORMATETC * pFormatetcOut ) override;
+ STDMETHODIMP SetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease ) override;
+ STDMETHODIMP EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc ) override;
+ STDMETHODIMP DAdvise( FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD* pdwConnection ) override;
+ STDMETHODIMP DUnadvise( DWORD dwConnection ) override;
+ STDMETHODIMP EnumDAdvise( IEnumSTATDATA** ppenumAdvise ) override;
+
+private:
+ HRESULT MarshalIDataObjectIntoCurrentApartment( IDataObject** ppIDataObj );
+
+private:
+ IDataObjectPtr m_rIDataObjectOrg;
+ HGLOBAL m_hGlobal;
+ LONG m_nRefCnt;
+
+// prevent copy and assignment
+private:
+ CAPNDataObject( const CAPNDataObject& theOther ) = delete;
+ CAPNDataObject& operator=( const CAPNDataObject& theOther ) = delete;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/DOTransferable.cxx b/vcl/win/dtrans/DOTransferable.cxx
new file mode 100644
index 0000000000..c2b54bf83a
--- /dev/null
+++ b/vcl/win/dtrans/DOTransferable.cxx
@@ -0,0 +1,577 @@
+/* -*- 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 <rtl/process.h>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include "DOTransferable.hxx"
+#include "ImplHelper.hxx"
+#include "WinClip.hxx"
+#include "WinClipboard.hxx"
+#include "DTransHelper.hxx"
+#include "TxtCnvtHlp.hxx"
+#include "MimeAttrib.hxx"
+#include "FmtFilter.hxx"
+#include "Fetc.hxx"
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
+#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace osl;
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::io;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+
+namespace
+{
+ const Type CPPUTYPE_SEQINT8 = cppu::UnoType<Sequence< sal_Int8 >>::get();
+ const Type CPPUTYPE_OUSTRING = cppu::UnoType<OUString>::get();
+
+ bool isValidFlavor( const DataFlavor& aFlavor )
+ {
+ return ( aFlavor.MimeType.getLength( ) &&
+ ( ( aFlavor.DataType == CPPUTYPE_SEQINT8 ) ||
+ ( aFlavor.DataType == CPPUTYPE_OUSTRING ) ) );
+ }
+
+void clipDataToByteStream( CLIPFORMAT cf, STGMEDIUM stgmedium, CDOTransferable::ByteSequence_t& aByteSequence )
+{
+ CStgTransferHelper memTransferHelper;
+ LPSTREAM pStream = nullptr;
+
+ switch( stgmedium.tymed )
+ {
+ case TYMED_HGLOBAL:
+ memTransferHelper.init( stgmedium.hGlobal );
+ break;
+
+ case TYMED_MFPICT:
+ memTransferHelper.init( stgmedium.hMetaFilePict );
+ break;
+
+ case TYMED_ENHMF:
+ memTransferHelper.init( stgmedium.hEnhMetaFile );
+ break;
+
+ case TYMED_ISTREAM:
+ pStream = stgmedium.pstm;
+ break;
+
+ default:
+ throw UnsupportedFlavorException( );
+ break;
+ }
+
+ if (pStream)
+ {
+ // We have a stream, read from it.
+ STATSTG aStat;
+ HRESULT hr = pStream->Stat(&aStat, STATFLAG_NONAME);
+ if (FAILED(hr))
+ {
+ SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Stat() failed");
+ return;
+ }
+
+ size_t nMemSize = aStat.cbSize.QuadPart;
+ aByteSequence.realloc(nMemSize);
+ LARGE_INTEGER li;
+ li.QuadPart = 0;
+ hr = pStream->Seek(li, STREAM_SEEK_SET, nullptr);
+ if (FAILED(hr))
+ {
+ SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Seek() failed");
+ }
+
+ ULONG nRead = 0;
+ hr = pStream->Read(aByteSequence.getArray(), nMemSize, &nRead);
+ if (FAILED(hr))
+ {
+ SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Read() failed");
+ }
+ if (nRead < nMemSize)
+ {
+ SAL_WARN("vcl.win.dtrans", "clipDataToByteStream: Read() was partial");
+ }
+
+ return;
+ }
+
+ int nMemSize = memTransferHelper.memSize( cf );
+ aByteSequence.realloc( nMemSize );
+ memTransferHelper.read( aByteSequence.getArray( ), nMemSize );
+}
+
+OUString byteStreamToOUString( CDOTransferable::ByteSequence_t& aByteStream )
+{
+ sal_Int32 nWChars;
+ sal_Int32 nMemSize = aByteStream.getLength( );
+
+ // if there is a trailing L"\0" subtract 1 from length
+ // for unknown reason, the sequence may sometimes arrive empty
+ if ( aByteStream.getLength( ) > 1 &&
+ 0 == aByteStream[ aByteStream.getLength( ) - 2 ] &&
+ 0 == aByteStream[ aByteStream.getLength( ) - 1 ] )
+ nWChars = static_cast< sal_Int32 >( nMemSize / sizeof( sal_Unicode ) ) - 1;
+ else
+ nWChars = static_cast< sal_Int32 >( nMemSize / sizeof( sal_Unicode ) );
+
+ return OUString( reinterpret_cast< sal_Unicode* >( aByteStream.getArray( ) ), nWChars );
+}
+
+Any byteStreamToAny( CDOTransferable::ByteSequence_t& aByteStream, const Type& aRequestedDataType )
+{
+ Any aAny;
+
+ if ( aRequestedDataType == CPPUTYPE_OUSTRING )
+ {
+ OUString str = byteStreamToOUString( aByteStream );
+ if (str.isEmpty())
+ throw RuntimeException();
+ aAny <<= str;
+ }
+ else
+ aAny <<= aByteStream;
+
+ return aAny;
+}
+
+bool cmpFullMediaType(
+ const Reference< XMimeContentType >& xLhs, const Reference< XMimeContentType >& xRhs )
+{
+ return xLhs->getFullMediaType().equalsIgnoreAsciiCase( xRhs->getFullMediaType( ) );
+}
+
+bool cmpAllContentTypeParameter(
+ const Reference< XMimeContentType >& xLhs, const Reference< XMimeContentType >& xRhs )
+{
+ Sequence< OUString > xLhsFlavors = xLhs->getParameters( );
+ Sequence< OUString > xRhsFlavors = xRhs->getParameters( );
+ bool bRet = true;
+
+ try
+ {
+ if ( xLhsFlavors.getLength( ) == xRhsFlavors.getLength( ) )
+ {
+ OUString pLhs;
+ OUString pRhs;
+
+ for ( sal_Int32 i = 0; i < xLhsFlavors.getLength( ); i++ )
+ {
+ pLhs = xLhs->getParameterValue( xLhsFlavors[i] );
+ pRhs = xRhs->getParameterValue( xLhsFlavors[i] );
+
+ if ( !pLhs.equalsIgnoreAsciiCase( pRhs ) )
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ else
+ bRet = false;
+ }
+ catch( NoSuchElementException& )
+ {
+ bRet = false;
+ }
+ catch( IllegalArgumentException& )
+ {
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+} // end namespace
+
+CDOTransferable::CDOTransferable(
+ const Reference< XComponentContext >& rxContext, IDataObjectPtr rDataObject ) :
+ m_rDataObject( rDataObject ),
+ m_xContext( rxContext ),
+ m_DataFormatTranslator( rxContext ),
+ m_bUnicodeRegistered( false ),
+ m_TxtFormatOnClipboard( CF_INVALID )
+{
+ initFlavorList();
+}
+
+CDOTransferable::CDOTransferable(
+ const Reference<XComponentContext>& rxContext,
+ const css::uno::Reference<css::datatransfer::clipboard::XClipboard>& xClipboard,
+ const std::vector<sal_uInt32>& rFormats)
+ : m_xClipboard(xClipboard)
+ , m_xContext(rxContext)
+ , m_DataFormatTranslator(rxContext)
+ , m_bUnicodeRegistered(false)
+ , m_TxtFormatOnClipboard(CF_INVALID)
+{
+ initFlavorListFromFormatList(rFormats);
+}
+
+Any SAL_CALL CDOTransferable::getTransferData( const DataFlavor& aFlavor )
+{
+ OSL_ASSERT( isValidFlavor( aFlavor ) );
+
+ std::unique_lock aGuard( m_aMutex );
+
+ // convert dataflavor to formatetc
+
+ CFormatEtc fetc = m_DataFormatTranslator.getFormatEtcFromDataFlavor( aFlavor );
+ OSL_ASSERT( CF_INVALID != fetc.getClipformat() );
+
+ // get the data from clipboard in a byte stream
+
+ ByteSequence_t clipDataStream;
+
+ try
+ {
+ clipDataStream = getClipboardData( fetc );
+ }
+ catch( UnsupportedFlavorException& )
+ {
+ if ( CDataFormatTranslator::isUnicodeTextFormat( fetc.getClipformat( ) ) &&
+ m_bUnicodeRegistered )
+ {
+ OUString aUnicodeText = synthesizeUnicodeText( );
+ Any aAny( aUnicodeText );
+ return aAny;
+ }
+ // #i124085# CF_DIBV5 should not be possible, but keep for reading from the
+ // clipboard for being on the safe side
+ else if(CF_DIBV5 == fetc.getClipformat())
+ {
+ // #i123407# CF_DIBV5 has priority; if the try to fetch this failed,
+ // check CF_DIB availability as an alternative
+ fetc.setClipformat(CF_DIB);
+
+ clipDataStream = getClipboardData( fetc );
+ // pass UnsupportedFlavorException out, tried all possibilities
+ }
+ else
+ throw; // pass through exception
+ }
+
+ // return the data as any
+
+ return byteStreamToAny( clipDataStream, aFlavor.DataType );
+}
+
+// getTransferDataFlavors
+
+Sequence< DataFlavor > SAL_CALL CDOTransferable::getTransferDataFlavors( )
+{
+ return m_FlavorList;
+}
+
+// isDataFlavorSupported
+// returns true if we find a DataFlavor with the same MimeType and
+// DataType
+
+sal_Bool SAL_CALL CDOTransferable::isDataFlavorSupported( const DataFlavor& aFlavor )
+{
+ OSL_ASSERT( isValidFlavor( aFlavor ) );
+
+ for ( DataFlavor const & df : std::as_const(m_FlavorList) )
+ if ( compareDataFlavors( aFlavor, df ) )
+ return true;
+
+ return false;
+}
+
+// the list of dataflavors currently on the clipboard will be initialized
+// only once; if the client of this Transferable will hold a reference
+// to it and the underlying clipboard content changes, the client does
+// possible operate on an invalid list
+// if there is only text on the clipboard we will also offer unicode text
+// an synthesize this format on the fly if requested, to accomplish this
+// we save the first offered text format which we will later use for the
+// conversion
+
+void CDOTransferable::initFlavorList( )
+{
+ std::vector<sal_uInt32> aFormats;
+ sal::systools::COMReference<IEnumFORMATETC> pEnumFormatEtc;
+ HRESULT hr = m_rDataObject->EnumFormatEtc( DATADIR_GET, &pEnumFormatEtc );
+ if ( SUCCEEDED( hr ) )
+ {
+ pEnumFormatEtc->Reset( );
+
+ FORMATETC fetc;
+ while ( S_OK == pEnumFormatEtc->Next( 1, &fetc, nullptr ) )
+ {
+ aFormats.push_back(fetc.cfFormat);
+ // see MSDN IEnumFORMATETC
+ CoTaskMemFree( fetc.ptd );
+ }
+ initFlavorListFromFormatList(aFormats);
+ }
+}
+
+void CDOTransferable::initFlavorListFromFormatList(const std::vector<sal_uInt32>& rFormats)
+{
+ for (sal_uInt32 cfFormat : rFormats)
+ {
+ // we use locales only to determine the
+ // charset if there is text on the clipboard
+ // we don't offer this format
+ if (CF_LOCALE == cfFormat)
+ continue;
+
+ // if text or oemtext is offered we pretend to have unicode text
+ if (CDataFormatTranslator::isTextFormat(cfFormat))
+ {
+ if (!m_bUnicodeRegistered)
+ {
+ m_TxtFormatOnClipboard = cfFormat;
+ m_bUnicodeRegistered = true;
+
+ // register unicode text as format
+ addSupportedFlavor(formatEtcToDataFlavor(CF_UNICODETEXT));
+ }
+ }
+ else
+ addSupportedFlavor(formatEtcToDataFlavor(cfFormat));
+ }
+}
+
+inline
+void CDOTransferable::addSupportedFlavor( const DataFlavor& aFlavor )
+{
+ // we ignore all formats that couldn't be translated
+ if ( aFlavor.MimeType.getLength( ) )
+ {
+ OSL_ASSERT( isValidFlavor( aFlavor ) );
+
+ m_FlavorList.realloc( m_FlavorList.getLength( ) + 1 );
+ m_FlavorList.getArray()[m_FlavorList.getLength( ) - 1] = aFlavor;
+ }
+}
+
+DataFlavor CDOTransferable::formatEtcToDataFlavor(sal_uInt32 cfFormat)
+{
+ return m_DataFormatTranslator.getDataFlavorFromFormatEtc(cfFormat);
+}
+
+// returns the current locale on clipboard; if there is no locale on
+// clipboard the function returns the current thread locale
+
+LCID CDOTransferable::getLocaleFromClipboard( )
+{
+ LCID lcid = GetThreadLocale( );
+
+ try
+ {
+ CFormatEtc fetc = CDataFormatTranslator::getFormatEtcForClipformat( CF_LOCALE );
+ ByteSequence_t aLCIDSeq = getClipboardData( fetc );
+ lcid = *reinterpret_cast<LCID*>( aLCIDSeq.getArray( ) );
+
+ // because of a Win95/98 Bug; there the high word
+ // of a locale has the same value as the
+ // low word e.g. 0x07040704 that's not right
+ // correct is 0x00000704
+ lcid &= 0x0000FFFF;
+ }
+ catch(...)
+ {
+ // we take the default locale
+ }
+
+ return lcid;
+}
+
+void CDOTransferable::tryToGetIDataObjectIfAbsent()
+{
+ if (!m_rDataObject.is())
+ {
+ auto xClipboard = m_xClipboard.get(); // holding the reference while we get the object
+ if (CWinClipboard* pWinClipboard = dynamic_cast<CWinClipboard*>(xClipboard.get()))
+ {
+ m_rDataObject = pWinClipboard->getIDataObject();
+ }
+ }
+}
+
+// I think it's not necessary to call ReleaseStgMedium
+// in case of failures because nothing should have been
+// allocated etc.
+
+CDOTransferable::ByteSequence_t CDOTransferable::getClipboardData( CFormatEtc& aFormatEtc )
+{
+ STGMEDIUM stgmedium;
+ tryToGetIDataObjectIfAbsent();
+ if (!m_rDataObject.is()) // Maybe we are shutting down, and clipboard is already destroyed?
+ throw RuntimeException();
+ HRESULT hr = m_rDataObject->GetData( aFormatEtc, &stgmedium );
+
+ // in case of failure to get a WMF metafile handle, try to get a memory block
+ if( FAILED( hr ) &&
+ ( CF_METAFILEPICT == aFormatEtc.getClipformat() ) &&
+ ( TYMED_MFPICT == aFormatEtc.getTymed() ) )
+ {
+ CFormatEtc aTempFormat( aFormatEtc );
+ aTempFormat.setTymed( TYMED_HGLOBAL );
+ hr = m_rDataObject->GetData( aTempFormat, &stgmedium );
+ }
+
+ if (FAILED(hr) && aFormatEtc.getTymed() == TYMED_HGLOBAL)
+ {
+ // Handle type is not memory, try stream.
+ CFormatEtc aTempFormat(aFormatEtc);
+ aTempFormat.setTymed(TYMED_ISTREAM);
+ hr = m_rDataObject->GetData(aTempFormat, &stgmedium);
+ }
+
+ if ( FAILED( hr ) )
+ {
+ OSL_ASSERT( (hr != E_INVALIDARG) &&
+ (hr != DV_E_DVASPECT) &&
+ (hr != DV_E_LINDEX) &&
+ (hr != DV_E_TYMED) );
+
+ if ( DV_E_FORMATETC == hr )
+ throw UnsupportedFlavorException( );
+ else if ( STG_E_MEDIUMFULL == hr )
+ throw IOException( );
+ else
+ throw RuntimeException( );
+ }
+
+ ByteSequence_t byteStream;
+
+ try
+ {
+ if ( CF_ENHMETAFILE == aFormatEtc.getClipformat() )
+ byteStream = WinENHMFPictToOOMFPict( stgmedium.hEnhMetaFile );
+ else if (CF_HDROP == aFormatEtc.getClipformat())
+ byteStream = CF_HDROPToFileList(stgmedium.hGlobal);
+ else if ( CF_BITMAP == aFormatEtc.getClipformat() )
+ {
+ byteStream = WinBITMAPToOOBMP(stgmedium.hBitmap);
+ if( aFormatEtc.getTymed() == TYMED_GDI &&
+ ! stgmedium.pUnkForRelease )
+ {
+ DeleteObject(stgmedium.hBitmap);
+ }
+ }
+ else
+ {
+ clipDataToByteStream( aFormatEtc.getClipformat( ), stgmedium, byteStream );
+
+ // format conversion if necessary
+ // #i124085# DIBV5 should not happen currently, but keep as a hint here
+ if(CF_DIBV5 == aFormatEtc.getClipformat() || CF_DIB == aFormatEtc.getClipformat())
+ {
+ byteStream = WinDIBToOOBMP(byteStream);
+ }
+ else if(CF_METAFILEPICT == aFormatEtc.getClipformat())
+ {
+ byteStream = WinMFPictToOOMFPict(byteStream);
+ }
+ }
+
+ ReleaseStgMedium( &stgmedium );
+ }
+ catch( CStgTransferHelper::CStgTransferException& )
+ {
+ ReleaseStgMedium( &stgmedium );
+ throw IOException( );
+ }
+
+ return byteStream;
+}
+
+OUString CDOTransferable::synthesizeUnicodeText( )
+{
+ ByteSequence_t aTextSequence;
+ CFormatEtc fetc;
+ LCID lcid = getLocaleFromClipboard( );
+ sal_uInt32 cpForTxtCnvt = 0;
+
+ if ( CF_TEXT == m_TxtFormatOnClipboard )
+ {
+ fetc = CDataFormatTranslator::getFormatEtcForClipformat( CF_TEXT );
+ aTextSequence = getClipboardData( fetc );
+
+ // determine the codepage used for text conversion
+ cpForTxtCnvt = getWinCPFromLocaleId( lcid, LOCALE_IDEFAULTANSICODEPAGE ).toInt32( );
+ }
+ else if ( CF_OEMTEXT == m_TxtFormatOnClipboard )
+ {
+ fetc = CDataFormatTranslator::getFormatEtcForClipformat( CF_OEMTEXT );
+ aTextSequence = getClipboardData( fetc );
+
+ // determine the codepage used for text conversion
+ cpForTxtCnvt = getWinCPFromLocaleId( lcid, LOCALE_IDEFAULTCODEPAGE ).toInt32( );
+ }
+ else
+ OSL_ASSERT( false );
+
+ CStgTransferHelper stgTransferHelper;
+
+ // convert the text
+ MultiByteToWideCharEx( cpForTxtCnvt,
+ reinterpret_cast<char*>( aTextSequence.getArray( ) ),
+ -1,
+ stgTransferHelper,
+ false);
+
+ CRawHGlobalPtr ptrHGlob(stgTransferHelper);
+ sal_Unicode* pWChar = static_cast<sal_Unicode*>(ptrHGlob.GetMemPtr());
+
+ return OUString(pWChar);
+}
+
+bool CDOTransferable::compareDataFlavors(
+ const DataFlavor& lhs, const DataFlavor& rhs )
+{
+ if ( !m_rXMimeCntFactory.is( ) )
+ {
+ m_rXMimeCntFactory = MimeContentTypeFactory::create( m_xContext );
+ }
+
+ bool bRet = false;
+
+ try
+ {
+ Reference< XMimeContentType > xLhs( m_rXMimeCntFactory->createMimeContentType( lhs.MimeType ) );
+ Reference< XMimeContentType > xRhs( m_rXMimeCntFactory->createMimeContentType( rhs.MimeType ) );
+
+ if ( cmpFullMediaType( xLhs, xRhs ) )
+ {
+ bRet = cmpAllContentTypeParameter( xLhs, xRhs );
+ }
+ }
+ catch( IllegalArgumentException& )
+ {
+ OSL_FAIL( "Invalid content type detected" );
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/DOTransferable.hxx b/vcl/win/dtrans/DOTransferable.hxx
new file mode 100644
index 0000000000..1824c7b448
--- /dev/null
+++ b/vcl/win/dtrans/DOTransferable.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include "DataFmtTransl.hxx"
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/datatransfer/XMimeContentTypeFactory.hpp>
+#include <com/sun/star/datatransfer/XMimeContentType.hpp>
+#include <com/sun/star/datatransfer/XSystemTransferable.hpp>
+#include <cppuhelper/weakref.hxx>
+
+#include <systools/win32/comtools.hxx>
+
+#include <mutex>
+#include <vector>
+
+// forward
+class CFormatEtc;
+
+class CDOTransferable : public ::cppu::WeakImplHelper<
+ css::datatransfer::XTransferable>
+{
+public:
+ typedef css::uno::Sequence< sal_Int8 > ByteSequence_t;
+
+ // XTransferable
+
+ virtual css::uno::Any SAL_CALL getTransferData( const css::datatransfer::DataFlavor& aFlavor ) override;
+
+ virtual css::uno::Sequence< css::datatransfer::DataFlavor > SAL_CALL getTransferDataFlavors( ) override;
+
+ virtual sal_Bool SAL_CALL isDataFlavorSupported( const css::datatransfer::DataFlavor& aFlavor ) override;
+
+ explicit CDOTransferable(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference<css::datatransfer::clipboard::XClipboard>& xClipboard,
+ const std::vector<sal_uInt32>& rFormats);
+
+ explicit CDOTransferable(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ IDataObjectPtr rDataObject );
+
+private:
+ // some helper functions
+
+ void initFlavorList( );
+ void initFlavorListFromFormatList(const std::vector<sal_uInt32>& rFormats);
+
+ void addSupportedFlavor( const css::datatransfer::DataFlavor& aFlavor );
+ css::datatransfer::DataFlavor formatEtcToDataFlavor(sal_uInt32 cfFormat);
+
+ void tryToGetIDataObjectIfAbsent();
+ ByteSequence_t getClipboardData( CFormatEtc& aFormatEtc );
+ OUString synthesizeUnicodeText( );
+
+ LCID getLocaleFromClipboard( );
+
+ bool compareDataFlavors( const css::datatransfer::DataFlavor& lhs,
+ const css::datatransfer::DataFlavor& rhs );
+
+private:
+ css::uno::WeakReference<css::datatransfer::clipboard::XClipboard> m_xClipboard;
+ IDataObjectPtr m_rDataObject;
+ css::uno::Sequence< css::datatransfer::DataFlavor > m_FlavorList;
+ const css::uno::Reference< css::uno::XComponentContext > m_xContext;
+ CDataFormatTranslator m_DataFormatTranslator;
+ css::uno::Reference< css::datatransfer::XMimeContentTypeFactory > m_rXMimeCntFactory;
+ std::mutex m_aMutex;
+ bool m_bUnicodeRegistered;
+ CLIPFORMAT m_TxtFormatOnClipboard;
+
+// non supported operations
+private:
+ CDOTransferable( const CDOTransferable& );
+ CDOTransferable& operator=( const CDOTransferable& );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/DTransHelper.cxx b/vcl/win/dtrans/DTransHelper.cxx
new file mode 100644
index 0000000000..66d18f9fb5
--- /dev/null
+++ b/vcl/win/dtrans/DTransHelper.cxx
@@ -0,0 +1,205 @@
+/* -*- 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 <rtl/ustring.h>
+#include <osl/diagnose.h>
+#include "DTransHelper.hxx"
+
+// implementation
+
+CStgTransferHelper::CStgTransferHelper( bool bAutoInit,
+ HGLOBAL hGlob,
+ bool bDelStgOnRelease ) :
+ m_lpStream( nullptr ),
+ m_bDelStgOnRelease( bDelStgOnRelease )
+{
+ if ( bAutoInit )
+ init( hGlob, m_bDelStgOnRelease );
+}
+
+// dtor
+
+CStgTransferHelper::~CStgTransferHelper( )
+{
+ if ( m_lpStream )
+ m_lpStream->Release( );
+}
+
+// TransferData into the
+
+void CStgTransferHelper::write( const void* lpData, ULONG cb, ULONG* cbWritten )
+{
+ HRESULT hr = E_FAIL;
+
+ if ( m_lpStream )
+ hr = m_lpStream->Write( lpData, cb, cbWritten );
+
+ if ( FAILED( hr ) )
+ throw CStgTransferException( hr );
+
+#if OSL_DEBUG_LEVEL > 0
+ HGLOBAL hGlob;
+ hr = GetHGlobalFromStream( m_lpStream, &hGlob );
+ OSL_ASSERT( SUCCEEDED( hr ) );
+
+ /*DWORD dwSize =*/ GlobalSize( hGlob );
+ /*LPVOID lpdbgData =*/ GlobalLock( hGlob );
+ GlobalUnlock( hGlob );
+#endif
+}
+
+// read
+
+void CStgTransferHelper::read( LPVOID pv, ULONG cb, ULONG* pcbRead )
+{
+ HRESULT hr = E_FAIL;
+
+ if ( m_lpStream )
+ hr = m_lpStream->Read( pv, cb , pcbRead );
+
+ if ( FAILED( hr ) )
+ throw CStgTransferException( hr );
+}
+
+// GetHGlobal
+
+HGLOBAL CStgTransferHelper::getHGlobal( ) const
+{
+ OSL_ASSERT( m_lpStream );
+
+ HGLOBAL hGlob = nullptr;
+
+ if ( m_lpStream )
+ {
+ HRESULT hr = GetHGlobalFromStream( m_lpStream, &hGlob );
+ if ( FAILED( hr ) )
+ hGlob = nullptr;
+ }
+
+ return hGlob;
+}
+
+// getIStream
+
+void CStgTransferHelper::getIStream( LPSTREAM* ppStream )
+{
+ OSL_ASSERT( ppStream );
+ *ppStream = m_lpStream;
+ if ( *ppStream )
+ static_cast< LPUNKNOWN >( *ppStream )->AddRef( );
+}
+
+// Init
+
+void CStgTransferHelper::init( SIZE_T newSize,
+ sal_uInt32 uiFlags,
+ bool bDelStgOnRelease )
+{
+ cleanup( );
+
+ m_bDelStgOnRelease = bDelStgOnRelease;
+
+ HGLOBAL hGlob = GlobalAlloc( uiFlags, newSize );
+ if ( nullptr == hGlob )
+ throw CStgTransferException( STG_E_MEDIUMFULL );
+
+ HRESULT hr = CreateStreamOnHGlobal( hGlob, m_bDelStgOnRelease, &m_lpStream );
+ if ( FAILED( hr ) )
+ {
+ GlobalFree( hGlob );
+ m_lpStream = nullptr;
+ throw CStgTransferException( hr );
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ STATSTG statstg;
+ hr = m_lpStream->Stat( &statstg, STATFLAG_DEFAULT );
+ OSL_ASSERT( SUCCEEDED( hr ) );
+#endif
+}
+
+// Init
+
+void CStgTransferHelper::init( HGLOBAL hGlob,
+ bool bDelStgOnRelease )
+{
+ cleanup( );
+
+ m_bDelStgOnRelease = bDelStgOnRelease;
+
+ HRESULT hr = CreateStreamOnHGlobal( hGlob, m_bDelStgOnRelease, &m_lpStream );
+ if ( FAILED( hr ) )
+ throw CStgTransferException( hr );
+}
+
+// free the global memory and invalidate the stream pointer
+
+void CStgTransferHelper::cleanup( )
+{
+ if ( m_lpStream && !m_bDelStgOnRelease )
+ {
+ HGLOBAL hGlob;
+ GetHGlobalFromStream( m_lpStream, &hGlob );
+ GlobalFree( hGlob );
+ }
+
+ if ( m_lpStream )
+ {
+ m_lpStream->Release( );
+ m_lpStream = nullptr;
+ }
+}
+
+// return the size of memory we point to
+
+sal_uInt32 CStgTransferHelper::memSize( CLIPFORMAT cf ) const
+{
+ DWORD dwSize = 0;
+
+ if ( nullptr != m_lpStream )
+ {
+ HGLOBAL hGlob;
+ GetHGlobalFromStream( m_lpStream, &hGlob );
+
+ if ( CF_TEXT == cf || RegisterClipboardFormatW( L"HTML Format" ) == cf )
+ {
+ char* pText = static_cast< char* >( GlobalLock( hGlob ) );
+ if ( pText )
+ {
+ dwSize = strlen(pText) + 1; // strlen + trailing '\0'
+ GlobalUnlock( hGlob );
+ }
+ }
+ else if ( CF_UNICODETEXT == cf )
+ {
+ sal_Unicode* pText = static_cast< sal_Unicode* >( GlobalLock( hGlob ) );
+ if ( pText )
+ {
+ dwSize = rtl_ustr_getLength( pText ) * sizeof( sal_Unicode );
+ GlobalUnlock( hGlob );
+ }
+ }
+ else
+ dwSize = GlobalSize( hGlob );
+ }
+
+ return dwSize;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/DTransHelper.hxx b/vcl/win/dtrans/DTransHelper.hxx
new file mode 100644
index 0000000000..d8ab3349cb
--- /dev/null
+++ b/vcl/win/dtrans/DTransHelper.hxx
@@ -0,0 +1,167 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <objidl.h>
+#include "WinClip.hxx"
+
+#define AUTO_INIT true
+
+// a helper class to manage a global memory area, the clients can write
+// into the global memory area and extract the handle to the global mem
+// note: not thread-safe
+
+class CStgTransferHelper
+{
+public:
+ // will be thrown in case of failures
+ class CStgTransferException
+ {
+ public:
+ HRESULT m_hr;
+ explicit CStgTransferException( HRESULT hr ) : m_hr( hr ) {};
+ };
+
+public:
+ CStgTransferHelper(
+ bool bAutoInit = false,
+ HGLOBAL hGlob = nullptr,
+ bool bDelStgOnRelease = false );
+
+ ~CStgTransferHelper( );
+
+ void write( const void* lpData, ULONG cb, ULONG* cbWritten = nullptr );
+ void read( LPVOID pv, ULONG cb, ULONG* pcbRead = nullptr );
+
+ HGLOBAL getHGlobal( ) const;
+ void getIStream( LPSTREAM* ppStream );
+
+ void init(
+ SIZE_T newSize,
+ sal_uInt32 uiFlags = GHND,
+ bool bDelStgOnRelease = false );
+
+ void init(
+ HGLOBAL hGlob,
+ bool bDelStgOnRelease = false );
+
+ // returns the size of the managed memory
+ sal_uInt32 memSize( CLIPFORMAT cf = CF_INVALID ) const;
+
+ // free the global memory and necessary
+ // release the internal stream pointer
+ void cleanup( );
+
+private:
+ LPSTREAM m_lpStream;
+ bool m_bDelStgOnRelease;
+
+private:
+ CStgTransferHelper( const CStgTransferHelper& );
+ CStgTransferHelper& operator=( const CStgTransferHelper& );
+};
+
+// something like an auto-pointer - allows access to the memory belonging
+// to a HGLOBAL and automatically unlocks a global memory at destruction
+// time
+
+class CRawHGlobalPtr
+{
+public:
+
+ // ctor
+
+ explicit CRawHGlobalPtr( HGLOBAL hGlob ) :
+ m_hGlob( hGlob ),
+ m_bIsLocked( false ),
+ m_pGlobMem( nullptr )
+ {
+ }
+
+ // ctor
+
+ explicit CRawHGlobalPtr( const CStgTransferHelper& theHGlobalHelper ) :
+ m_hGlob( theHGlobalHelper.getHGlobal( ) ),
+ m_bIsLocked( false ),
+ m_pGlobMem( nullptr )
+ {
+ }
+
+ // dtor
+
+ ~CRawHGlobalPtr( )
+ {
+ if ( m_bIsLocked )
+ GlobalUnlock( m_hGlob );
+ }
+
+ // lock the global memory (initializes a
+ // pointer to this memory)
+
+ BOOL Lock( )
+ {
+ if ( !m_bIsLocked && ( nullptr != m_hGlob ) )
+ {
+ m_pGlobMem = GlobalLock( m_hGlob );
+ m_bIsLocked = ( nullptr != m_pGlobMem );
+ }
+
+ return m_bIsLocked;
+ }
+
+ // unlock the global memory (invalidates the
+ // pointer to this memory)
+
+ BOOL Unlock( )
+ {
+ GlobalUnlock( m_hGlob );
+ m_bIsLocked = false;
+ m_pGlobMem = nullptr;
+
+ return ( NO_ERROR == GetLastError( ) );
+ }
+
+ // locks the global memory and returns a
+ // pointer to this memory
+
+ LPVOID GetMemPtr( )
+ {
+ Lock( );
+ return m_pGlobMem;
+ }
+
+ // size of mem we point to
+
+ int MemSize( ) const
+ {
+ return GlobalSize( m_hGlob );
+ }
+
+private:
+ HGLOBAL m_hGlob;
+ bool m_bIsLocked;
+ LPVOID m_pGlobMem;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/DataFmtTransl.cxx b/vcl/win/dtrans/DataFmtTransl.cxx
new file mode 100644
index 0000000000..5daf0b453f
--- /dev/null
+++ b/vcl/win/dtrans/DataFmtTransl.cxx
@@ -0,0 +1,252 @@
+/* -*- 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 "DataFmtTransl.hxx"
+#include <rtl/string.hxx>
+#include <osl/diagnose.h>
+#include <rtl/tencinfo.h>
+#include "ImplHelper.hxx"
+#include "WinClip.hxx"
+#include "MimeAttrib.hxx"
+#include "DTransHelper.hxx"
+#include <rtl/string.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include "Fetc.hxx"
+#include <com/sun/star/datatransfer/DataFormatTranslator.hpp>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <shlobj.h>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::lang;
+
+const Type CPPUTYPE_SALINT32 = cppu::UnoType<sal_Int32>::get();
+const Type CPPUTYPE_SALINT8 = cppu::UnoType<sal_Int8>::get();
+const Type CPPUTYPE_OUSTRING = cppu::UnoType<OUString>::get();
+const Type CPPUTYPE_SEQSALINT8 = cppu::UnoType<Sequence< sal_Int8>>::get();
+const sal_Int32 MAX_CLIPFORMAT_NAME = 256;
+
+CDataFormatTranslator::CDataFormatTranslator( const Reference< XComponentContext >& rxContext )
+{
+ m_XDataFormatTranslator = DataFormatTranslator::create( rxContext );
+}
+
+CFormatEtc CDataFormatTranslator::getFormatEtcFromDataFlavor( const DataFlavor& aDataFlavor ) const
+{
+ sal_Int32 cf = CF_INVALID;
+
+ try
+ {
+ if( m_XDataFormatTranslator.is( ) )
+ {
+ Any aFormat = m_XDataFormatTranslator->getSystemDataTypeFromDataFlavor( aDataFlavor );
+
+ if ( aFormat.hasValue( ) )
+ {
+ if ( aFormat.getValueType( ) == CPPUTYPE_SALINT32 )
+ {
+ aFormat >>= cf;
+ OSL_ENSURE( CF_INVALID != cf, "Invalid Clipboard format delivered" );
+ }
+ else if ( aFormat.getValueType( ) == CPPUTYPE_OUSTRING )
+ {
+ OUString aClipFmtName;
+ aFormat >>= aClipFmtName;
+
+ OSL_ASSERT( aClipFmtName.getLength( ) );
+ cf = RegisterClipboardFormatW( o3tl::toW(aClipFmtName.getStr( )) );
+
+ OSL_ENSURE( CF_INVALID != cf, "RegisterClipboardFormat failed" );
+ }
+ else
+ OSL_FAIL( "Wrong Any-Type detected" );
+ }
+ }
+ }
+ catch( ... )
+ {
+ OSL_FAIL( "Unexpected error" );
+ }
+
+ return sal::static_int_cast<CFormatEtc>(getFormatEtcForClipformat( sal::static_int_cast<CLIPFORMAT>(cf) ));
+}
+
+DataFlavor CDataFormatTranslator::getDataFlavorFromFormatEtc(sal_uInt32 cfFormat, LCID lcid) const
+{
+ DataFlavor aFlavor;
+
+ try
+ {
+ CLIPFORMAT aClipformat = cfFormat;
+
+ Any aAny;
+ aAny <<= static_cast< sal_Int32 >( aClipformat );
+
+ if ( isOemOrAnsiTextFormat( aClipformat ) )
+ {
+ aFlavor.MimeType = "text/plain;charset=";
+ aFlavor.MimeType += getTextCharsetFromLCID( lcid, aClipformat );
+
+ aFlavor.HumanPresentableName = "OEM/ANSI Text";
+ aFlavor.DataType = CPPUTYPE_SEQSALINT8;
+ }
+ else if ( CF_INVALID != aClipformat )
+ {
+ if ( m_XDataFormatTranslator.is( ) )
+ {
+ aFlavor = m_XDataFormatTranslator->getDataFlavorFromSystemDataType( aAny );
+
+ if ( !aFlavor.MimeType.getLength( ) )
+ {
+ // lookup of DataFlavor from clipboard format id
+ // failed, so we try to resolve via clipboard
+ // format name
+ OUString clipFormatName = getClipboardFormatName( aClipformat );
+
+ // if we could not get a clipboard format name an
+ // error must have occurred or it is a standard
+ // clipboard format that we don't translate, e.g.
+ // CF_BITMAP (the office only uses CF_DIB)
+ if ( clipFormatName.getLength( ) )
+ {
+ aAny <<= clipFormatName;
+ aFlavor = m_XDataFormatTranslator->getDataFlavorFromSystemDataType( aAny );
+ }
+ }
+ }
+ }
+ }
+ catch( ... )
+ {
+ OSL_FAIL( "Unexpected error" );
+ }
+
+ return aFlavor;
+}
+
+CFormatEtc CDataFormatTranslator::getFormatEtcForClipformatName( const OUString& aClipFmtName )
+{
+ // check parameter
+ if ( !aClipFmtName.getLength( ) )
+ return CFormatEtc( CF_INVALID );
+
+ CLIPFORMAT cf = sal::static_int_cast<CLIPFORMAT>(RegisterClipboardFormatW( o3tl::toW(aClipFmtName.getStr( )) ));
+ return getFormatEtcForClipformat( cf );
+}
+
+OUString CDataFormatTranslator::getClipboardFormatName( CLIPFORMAT aClipformat )
+{
+ OSL_PRECOND( CF_INVALID != aClipformat, "Invalid clipboard format" );
+
+ sal_Unicode wBuff[ MAX_CLIPFORMAT_NAME + 1 ]; // Null terminator isn't counted, apparently.
+ sal_Int32 nLen = GetClipboardFormatNameW( aClipformat, o3tl::toW(wBuff), MAX_CLIPFORMAT_NAME );
+
+ return OUString( wBuff, nLen );
+}
+
+CFormatEtc CDataFormatTranslator::getFormatEtcForClipformat( CLIPFORMAT cf )
+{
+ CFormatEtc fetc( cf, TYMED_NULL, nullptr, DVASPECT_CONTENT );
+
+ switch( cf )
+ {
+ case CF_METAFILEPICT:
+ fetc.setTymed( TYMED_MFPICT );
+ break;
+
+ case CF_ENHMETAFILE:
+ fetc.setTymed( TYMED_ENHMF );
+ break;
+
+ default:
+ fetc.setTymed( TYMED_HGLOBAL /*| TYMED_ISTREAM*/ );
+ }
+
+ /*
+ hack: in order to paste urls copied by Internet Explorer
+ with "copy link" we set the lindex member to 0
+ but if we really want to support CFSTR_FILECONTENT and
+ the accompany format CFSTR_FILEDESCRIPTOR (FileGroupDescriptor)
+ the client of the clipboard service has to provide a id
+ of which FileContents it wants to paste
+ see MSDN: "Handling Shell Data Transfer Scenarios"
+ */
+ if ( cf == RegisterClipboardFormat( CFSTR_FILECONTENTS ) )
+ fetc.setLindex( 0 );
+
+ return fetc;
+}
+
+bool CDataFormatTranslator::isOemOrAnsiTextFormat( CLIPFORMAT cf )
+{
+ return ( (cf == CF_TEXT) || (cf == CF_OEMTEXT) );
+}
+
+bool CDataFormatTranslator::isUnicodeTextFormat( CLIPFORMAT cf )
+{
+ return ( cf == CF_UNICODETEXT );
+}
+
+bool CDataFormatTranslator::isTextFormat( CLIPFORMAT cf )
+{
+ return ( isOemOrAnsiTextFormat( cf ) || isUnicodeTextFormat( cf ) );
+}
+
+bool CDataFormatTranslator::isHTMLFormat( CLIPFORMAT cf )
+{
+ OUString clipFormatName = getClipboardFormatName( cf );
+ return ( clipFormatName == "HTML Format" );
+}
+
+bool CDataFormatTranslator::isTextHtmlFormat( CLIPFORMAT cf )
+{
+ OUString clipFormatName = getClipboardFormatName( cf );
+ return clipFormatName.equalsIgnoreAsciiCase( "HTML (HyperText Markup Language)" );
+}
+
+OUString CDataFormatTranslator::getTextCharsetFromLCID( LCID lcid, CLIPFORMAT aClipformat )
+{
+ OSL_ASSERT( isOemOrAnsiTextFormat( aClipformat ) );
+
+ OUString charset;
+ if ( CF_TEXT == aClipformat )
+ {
+ charset = getMimeCharsetFromLocaleId(
+ lcid,
+ LOCALE_IDEFAULTANSICODEPAGE,
+ PRE_WINDOWS_CODEPAGE );
+ }
+ else if ( CF_OEMTEXT == aClipformat )
+ {
+ charset = getMimeCharsetFromLocaleId(
+ lcid,
+ LOCALE_IDEFAULTCODEPAGE,
+ PRE_OEM_CODEPAGE );
+ }
+ else // CF_UNICODE
+ OSL_ASSERT( false );
+
+ return charset;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/DataFmtTransl.hxx b/vcl/win/dtrans/DataFmtTransl.hxx
new file mode 100644
index 0000000000..e003f2538b
--- /dev/null
+++ b/vcl/win/dtrans/DataFmtTransl.hxx
@@ -0,0 +1,64 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/datatransfer/XDataFormatTranslator.hpp>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <objidl.h>
+
+// declaration
+
+class CFormatEtc;
+
+class CDataFormatTranslator
+{
+public:
+ explicit CDataFormatTranslator( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ CFormatEtc getFormatEtcFromDataFlavor( const css::datatransfer::DataFlavor& aDataFlavor ) const;
+ css::datatransfer::DataFlavor getDataFlavorFromFormatEtc(
+ sal_uInt32 cfFormat, LCID lcid = GetThreadLocale()) const;
+
+ static CFormatEtc getFormatEtcForClipformat( CLIPFORMAT cf );
+ static CFormatEtc getFormatEtcForClipformatName( const OUString& aClipFmtName );
+ static OUString getClipboardFormatName( CLIPFORMAT aClipformat );
+
+ static bool isHTMLFormat( CLIPFORMAT cf );
+ static bool isTextHtmlFormat( CLIPFORMAT cf );
+ static bool isOemOrAnsiTextFormat( CLIPFORMAT cf );
+ static bool isUnicodeTextFormat( CLIPFORMAT cf );
+ static bool isTextFormat( CLIPFORMAT cf );
+
+private:
+ static OUString getTextCharsetFromLCID( LCID lcid, CLIPFORMAT aClipformat );
+
+private:
+ css::uno::Reference< css::datatransfer::XDataFormatTranslator > m_XDataFormatTranslator;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/DtObjFactory.cxx b/vcl/win/dtrans/DtObjFactory.cxx
new file mode 100644
index 0000000000..8fe03789b1
--- /dev/null
+++ b/vcl/win/dtrans/DtObjFactory.cxx
@@ -0,0 +1,34 @@
+/* -*- 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 "DtObjFactory.hxx"
+#include "XTDataObject.hxx"
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::lang;
+
+IDataObjectPtr
+CDTransObjFactory::createDataObjFromTransferable(const Reference<XComponentContext>& rxContext,
+ const Reference<XTransferable>& refXTransferable)
+{
+ return (IDataObjectPtr(new CXTDataObject(rxContext, refXTransferable)));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/DtObjFactory.hxx b/vcl/win/dtrans/DtObjFactory.hxx
new file mode 100644
index 0000000000..b5d5f50075
--- /dev/null
+++ b/vcl/win/dtrans/DtObjFactory.hxx
@@ -0,0 +1,33 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <systools/win32/comtools.hxx>
+
+namespace CDTransObjFactory
+{
+ IDataObjectPtr createDataObjFromTransferable( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::datatransfer::XTransferable >& refXTransferable );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/Fetc.cxx b/vcl/win/dtrans/Fetc.cxx
new file mode 100644
index 0000000000..9bcb2f609f
--- /dev/null
+++ b/vcl/win/dtrans/Fetc.cxx
@@ -0,0 +1,166 @@
+/* -*- 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 <osl/diagnose.h>
+#include "Fetc.hxx"
+#include "ImplHelper.hxx"
+
+CFormatEtc::CFormatEtc( )
+{
+ m_FormatEtc.cfFormat = 0;
+ m_FormatEtc.ptd = nullptr;
+ m_FormatEtc.dwAspect = 0;
+ m_FormatEtc.lindex = -1;
+ m_FormatEtc.tymed = TYMED_NULL;
+}
+
+// transfer of ownership
+
+CFormatEtc::CFormatEtc( const FORMATETC& aFormatEtc )
+{
+ CopyFormatEtc( &m_FormatEtc, &const_cast< FORMATETC& >( aFormatEtc ) );
+}
+
+CFormatEtc::~CFormatEtc( )
+{
+ DeleteTargetDevice( m_FormatEtc.ptd );
+}
+
+CFormatEtc::CFormatEtc( CLIPFORMAT cf, DWORD tymed, DVTARGETDEVICE* ptd, DWORD dwAspect, LONG lindex )
+{
+ m_FormatEtc.cfFormat = cf;
+ m_FormatEtc.ptd = CopyTargetDevice( ptd );
+ m_FormatEtc.dwAspect = dwAspect;
+ m_FormatEtc.lindex = lindex;
+ m_FormatEtc.tymed = tymed;
+}
+
+CFormatEtc::CFormatEtc( const CFormatEtc& theOther )
+{
+ m_FormatEtc.cfFormat = theOther.m_FormatEtc.cfFormat;
+ m_FormatEtc.ptd = CopyTargetDevice( theOther.m_FormatEtc.ptd );
+ m_FormatEtc.dwAspect = theOther.m_FormatEtc.dwAspect;
+ m_FormatEtc.lindex = theOther.m_FormatEtc.lindex;
+ m_FormatEtc.tymed = theOther.m_FormatEtc.tymed;
+}
+
+CFormatEtc& CFormatEtc::operator=( const CFormatEtc& theOther )
+{
+ if ( this != &theOther )
+ {
+ DeleteTargetDevice( m_FormatEtc.ptd );
+
+ m_FormatEtc.cfFormat = theOther.m_FormatEtc.cfFormat;
+ m_FormatEtc.ptd = CopyTargetDevice( theOther.m_FormatEtc.ptd );
+ m_FormatEtc.dwAspect = theOther.m_FormatEtc.dwAspect;
+ m_FormatEtc.lindex = theOther.m_FormatEtc.lindex;
+ m_FormatEtc.tymed = theOther.m_FormatEtc.tymed;
+ }
+
+ return *this;
+}
+
+CFormatEtc::operator FORMATETC*( )
+{
+ return &m_FormatEtc;
+}
+
+CFormatEtc::operator FORMATETC( )
+{
+ return m_FormatEtc;
+}
+
+void CFormatEtc::getFORMATETC( LPFORMATETC lpFormatEtc )
+{
+ OSL_ASSERT( lpFormatEtc );
+ OSL_ASSERT( !IsBadWritePtr( lpFormatEtc, sizeof( FORMATETC ) ) );
+
+ CopyFormatEtc( lpFormatEtc, &m_FormatEtc );
+}
+
+CLIPFORMAT CFormatEtc::getClipformat( ) const
+{
+ return m_FormatEtc.cfFormat;
+}
+
+DWORD CFormatEtc::getTymed( ) const
+{
+ return m_FormatEtc.tymed;
+}
+
+void CFormatEtc::getTargetDevice( DVTARGETDEVICE** lpDvTargetDevice ) const
+{
+ OSL_ASSERT( lpDvTargetDevice );
+ OSL_ASSERT( !IsBadWritePtr( lpDvTargetDevice, sizeof( DVTARGETDEVICE ) ) );
+
+ *lpDvTargetDevice = nullptr;
+
+ if ( m_FormatEtc.ptd )
+ *lpDvTargetDevice = CopyTargetDevice( m_FormatEtc.ptd );
+}
+
+DWORD CFormatEtc::getDvAspect( ) const
+{
+ return m_FormatEtc.dwAspect;
+}
+
+LONG CFormatEtc::getLindex( ) const
+{
+ return m_FormatEtc.lindex;
+}
+
+void CFormatEtc::setClipformat( CLIPFORMAT cf )
+{
+ m_FormatEtc.cfFormat = cf;
+}
+
+void CFormatEtc::setTymed( DWORD tymed )
+{
+ m_FormatEtc.tymed = tymed;
+}
+
+// transfer of ownership!
+
+void CFormatEtc::setTargetDevice( DVTARGETDEVICE* ptd )
+{
+ DeleteTargetDevice( m_FormatEtc.ptd );
+ m_FormatEtc.ptd = ptd;
+}
+
+void CFormatEtc::setDvAspect( DWORD dwAspect )
+{
+ m_FormatEtc.dwAspect = dwAspect;
+}
+
+void CFormatEtc::setLindex( LONG lindex )
+{
+ m_FormatEtc.lindex = lindex;
+}
+
+bool operator==( const CFormatEtc& lhs, const CFormatEtc& rhs )
+{
+ return CompareFormatEtc( &lhs.m_FormatEtc, &rhs.m_FormatEtc );
+}
+
+bool operator!=( const CFormatEtc& lhs, const CFormatEtc& rhs )
+{
+ return !( lhs == rhs );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/Fetc.hxx b/vcl/win/dtrans/Fetc.hxx
new file mode 100644
index 0000000000..e0880acd64
--- /dev/null
+++ b/vcl/win/dtrans/Fetc.hxx
@@ -0,0 +1,76 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <objidl.h>
+
+/**********************************************************************
+ stl container elements must fulfill the following requirements:
+ 1. they need a copy ctor and assignment operator(?)
+ 2. they must be comparable
+ because the FORMATETC structure has a pointer to a TARGETDEVICE
+ structure we need a simple wrapper class to fulfill these needs
+***********************************************************************/
+
+class CFormatEtc
+{
+public:
+ CFormatEtc( );
+ explicit CFormatEtc( const FORMATETC& aFormatEtc );
+ CFormatEtc( CLIPFORMAT cf, DWORD tymed = TYMED_HGLOBAL, DVTARGETDEVICE* ptd = nullptr, DWORD dwAspect = DVASPECT_CONTENT, LONG lindex = -1 );
+ CFormatEtc( const CFormatEtc& theOther );
+
+ ~CFormatEtc( );
+
+ CFormatEtc& operator=( const CFormatEtc& theOther );
+ operator FORMATETC*( );
+ operator FORMATETC( );
+
+ void getFORMATETC( LPFORMATETC lpFormatEtc );
+
+ CLIPFORMAT getClipformat( ) const;
+ DWORD getTymed( ) const;
+ void getTargetDevice( DVTARGETDEVICE** ptd ) const;
+ DWORD getDvAspect( ) const;
+ LONG getLindex( ) const;
+
+ void setClipformat( CLIPFORMAT cf );
+ void setTymed( DWORD tymed );
+ void setTargetDevice( DVTARGETDEVICE* ptd );
+ void setDvAspect( DWORD dwAspect );
+ void setLindex( LONG lindex );
+
+private:
+ FORMATETC m_FormatEtc;
+
+ friend bool operator==( const CFormatEtc& lhs, const CFormatEtc& rhs );
+ friend bool operator!=( const CFormatEtc& lhs, const CFormatEtc& rhs );
+};
+
+bool operator==( const CFormatEtc& lhs, const CFormatEtc& rhs );
+bool operator!=( const CFormatEtc& lhs, const CFormatEtc& rhs );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/FetcList.cxx b/vcl/win/dtrans/FetcList.cxx
new file mode 100644
index 0000000000..5ace3cdca6
--- /dev/null
+++ b/vcl/win/dtrans/FetcList.cxx
@@ -0,0 +1,340 @@
+/* -*- 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 <osl/diagnose.h>
+#include "FetcList.hxx"
+#include "Fetc.hxx"
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
+#include <com/sun/star/datatransfer/XMimeContentType.hpp>
+
+#include "DataFmtTransl.hxx"
+#include "ImplHelper.hxx"
+#include "WinClip.hxx"
+
+#include <algorithm>
+
+#include "MimeAttrib.hxx"
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+
+LCID CFormatRegistrar::m_TxtLocale = 0;
+sal_uInt32 CFormatRegistrar::m_TxtCodePage = GetACP( );
+
+CFormatEtcContainer::CFormatEtcContainer( )
+{
+ m_EnumIterator = m_FormatMap.begin( );
+}
+
+void CFormatEtcContainer::addFormatEtc( const CFormatEtc& fetc )
+{
+ m_FormatMap.push_back( fetc );
+}
+
+void CFormatEtcContainer::removeFormatEtc( const CFormatEtc& fetc )
+{
+ FormatEtcMap_t::iterator iter =
+ find( m_FormatMap.begin(), m_FormatMap.end(), fetc );
+
+ if ( iter != m_FormatMap.end( ) )
+ m_FormatMap.erase( iter );
+}
+
+void CFormatEtcContainer::removeAllFormatEtc( )
+{
+ m_FormatMap.clear( );
+}
+
+bool CFormatEtcContainer::hasFormatEtc( const CFormatEtc& fetc ) const
+{
+ FormatEtcMap_t::const_iterator iter =
+ find( m_FormatMap.begin(), m_FormatMap.end(), fetc );
+
+ return ( iter != m_FormatMap.end( ) );
+}
+
+bool CFormatEtcContainer::hasElements( ) const
+{
+ return !m_FormatMap.empty();
+}
+
+void CFormatEtcContainer::beginEnumFormatEtc( )
+{
+ m_EnumIterator = m_FormatMap.begin( );
+}
+
+sal_uInt32 CFormatEtcContainer::nextFormatEtc( LPFORMATETC lpFetc,
+ sal_uInt32 aNum )
+{
+ OSL_ASSERT( lpFetc );
+ OSL_ASSERT( !IsBadWritePtr( lpFetc, sizeof( FORMATETC ) * aNum ) );
+
+ sal_uInt32 nFetched = 0;
+
+ for ( sal_uInt32 i = 0; i < aNum; i++, nFetched++, lpFetc++, ++m_EnumIterator )
+ {
+ if ( m_EnumIterator == m_FormatMap.end() )
+ break;
+ CopyFormatEtc( lpFetc, *m_EnumIterator );
+ }
+
+ return nFetched;
+}
+
+bool CFormatEtcContainer::skipFormatEtc( sal_uInt32 aNum )
+{
+ FormatEtcMap_t::const_iterator iter_end = m_FormatMap.end( );
+ for ( sal_uInt32 i = 0;
+ (i < aNum) && (m_EnumIterator != iter_end);
+ i++, ++m_EnumIterator )
+ ;/* intentionally left empty */
+
+ return ( m_EnumIterator != m_FormatMap.end( ) );
+}
+
+CFormatRegistrar::CFormatRegistrar( const Reference< XComponentContext >& rxContext,
+ const CDataFormatTranslator& aDataFormatTranslator ) :
+ m_DataFormatTranslator( aDataFormatTranslator ),
+ m_bHasSynthesizedLocale( false ),
+ m_xContext( rxContext )
+{
+}
+
+// this function converts all DataFlavors of the given FlavorList into
+// an appropriate FORMATETC structure, for some formats like unicodetext,
+// text and text/html we will offer an accompany format e.g.:
+//
+// DataFlavor | Registered Clipformat | Registered accompany clipformat
+// -------------------------|---------------------------|-----------------------------------
+// text/plain;charset=ansi | CF_TEXT | CF_UNICODETEXT
+// | | CF_LOCALE (if charset != GetACP()
+// | |
+// text/plain;charset=oem | CF_OEMTEXT | CF_UNICODETEXT
+// | | CF_LOCALE (if charset != GetOEMCP()
+// | |
+// text/plain;charset=utf-16| CF_UNICODETEXT | CF_TEXT
+// | |
+// text/html | HTML (Hypertext ...) | HTML Format
+// | |
+//
+// if some tries to register different text formats with different charsets the last
+// registered wins and the others are ignored
+
+void CFormatRegistrar::RegisterFormats(
+ const Reference< XTransferable >& aXTransferable, CFormatEtcContainer& aFormatEtcContainer )
+{
+ Sequence< DataFlavor > aFlavorList = aXTransferable->getTransferDataFlavors( );
+ sal_Int32 nFlavors = aFlavorList.getLength( );
+ bool bUnicodeRegistered = false;
+ DataFlavor aFlavor;
+
+ for( sal_Int32 i = 0; i < nFlavors; i++ )
+ {
+ aFlavor = aFlavorList[i];
+ CFormatEtc fetc = m_DataFormatTranslator.getFormatEtcFromDataFlavor( aFlavor );
+
+ // maybe an internal format so we ignore it
+ if ( CF_INVALID == fetc.getClipformat( ) )
+ continue;
+
+ if ( !needsToSynthesizeAccompanyFormats( fetc ) )
+ aFormatEtcContainer.addFormatEtc( fetc );
+ else
+ {
+ // if we haven't registered any text format up to now
+ if ( CDataFormatTranslator::isTextFormat( fetc.getClipformat() ) && !bUnicodeRegistered )
+ {
+ // if the transferable supports unicode text we ignore
+ // any further text format the transferable offers
+ // because we can create it from Unicode text in addition
+ // we register CF_TEXT for non unicode clients
+ if ( CDataFormatTranslator::isUnicodeTextFormat( fetc.getClipformat() ) )
+ {
+ aFormatEtcContainer.addFormatEtc( fetc ); // add CF_UNICODE
+ aFormatEtcContainer.addFormatEtc(
+ CDataFormatTranslator::getFormatEtcForClipformat( CF_TEXT ) ); // add CF_TEXT
+ bUnicodeRegistered = true;
+ }
+ else if ( !hasUnicodeFlavor( aXTransferable ) )
+ {
+ // we try to investigate the charset and make a valid
+ // windows codepage from this charset the default
+ // return value is the result of GetACP( )
+ OUString charset = getCharsetFromDataFlavor( aFlavor );
+ sal_uInt32 txtCP = getWinCPFromMimeCharset( charset );
+
+ // we try to get a Locale appropriate for this codepage
+ if ( findLocaleForTextCodePage( ) )
+ {
+ m_TxtCodePage = txtCP;
+
+ aFormatEtcContainer.addFormatEtc(
+ CDataFormatTranslator::getFormatEtcForClipformat( CF_UNICODETEXT ) );
+
+ if ( !IsOEMCP( m_TxtCodePage ) )
+ aFormatEtcContainer.addFormatEtc(
+ CDataFormatTranslator::getFormatEtcForClipformat( CF_TEXT ) );
+ else
+ aFormatEtcContainer.addFormatEtc(
+ CDataFormatTranslator::getFormatEtcForClipformat( CF_OEMTEXT ) );
+
+ aFormatEtcContainer.addFormatEtc(
+ CDataFormatTranslator::getFormatEtcForClipformat( CF_LOCALE ) );
+
+ // we save the flavor so it's easier when
+ // queried for it in XTDataObject::GetData(...)
+ m_RegisteredTextFlavor = aFlavor;
+ m_bHasSynthesizedLocale = true;
+ }
+ }
+ }
+ else if ( CDataFormatTranslator::isTextHtmlFormat( fetc.getClipformat( ) ) ) // Html (Hyper Text...)
+ {
+ // we add text/html ( HTML (HyperText Markup Language) )
+ aFormatEtcContainer.addFormatEtc( fetc );
+
+ // and HTML Format
+ aFormatEtcContainer.addFormatEtc(
+ CDataFormatTranslator::getFormatEtcForClipformatName( "HTML Format" ) );
+ }
+ }
+ }
+}
+
+bool CFormatRegistrar::hasSynthesizedLocale( ) const
+{
+ return m_bHasSynthesizedLocale;
+}
+
+LCID CFormatRegistrar::getSynthesizedLocale( )
+{
+ return m_TxtLocale;
+}
+
+sal_uInt32 CFormatRegistrar::getRegisteredTextCodePage( )
+{
+ return m_TxtCodePage;
+}
+
+DataFlavor CFormatRegistrar::getRegisteredTextFlavor( ) const
+{
+ return m_RegisteredTextFlavor;
+}
+
+bool CFormatRegistrar::isSynthesizeableFormat( const CFormatEtc& aFormatEtc )
+{
+ return ( CDataFormatTranslator::isOemOrAnsiTextFormat( aFormatEtc.getClipformat() ) ||
+ CDataFormatTranslator::isUnicodeTextFormat( aFormatEtc.getClipformat() ) ||
+ CDataFormatTranslator::isHTMLFormat( aFormatEtc.getClipformat() ) );
+}
+
+inline
+bool CFormatRegistrar::needsToSynthesizeAccompanyFormats( const CFormatEtc& aFormatEtc )
+{
+ return ( CDataFormatTranslator::isOemOrAnsiTextFormat( aFormatEtc.getClipformat() ) ||
+ CDataFormatTranslator::isUnicodeTextFormat( aFormatEtc.getClipformat() ) ||
+ CDataFormatTranslator::isTextHtmlFormat( aFormatEtc.getClipformat( ) ) );
+}
+
+OUString CFormatRegistrar::getCharsetFromDataFlavor( const DataFlavor& aFlavor )
+{
+ OUString charset;
+
+ try
+ {
+ Reference< XMimeContentTypeFactory > xMimeFac =
+ MimeContentTypeFactory::create(m_xContext);
+
+ Reference< XMimeContentType > xMimeType( xMimeFac->createMimeContentType( aFlavor.MimeType ) );
+ if ( xMimeType->hasParameter( TEXTPLAIN_PARAM_CHARSET ) )
+ charset = xMimeType->getParameterValue( TEXTPLAIN_PARAM_CHARSET );
+ else
+ charset = getMimeCharsetFromWinCP( GetACP( ), PRE_WINDOWS_CODEPAGE );
+ }
+ catch(NoSuchElementException&)
+ {
+ OSL_FAIL( "Unexpected" );
+ }
+ catch(...)
+ {
+ OSL_FAIL( "Invalid data flavor" );
+ }
+
+ return charset;
+}
+
+bool CFormatRegistrar::hasUnicodeFlavor( const Reference< XTransferable >& aXTransferable ) const
+{
+ DataFlavor aFlavor = m_DataFormatTranslator.getDataFlavorFromFormatEtc(CF_UNICODETEXT);
+
+ return aXTransferable->isDataFlavorSupported( aFlavor );
+}
+
+bool CFormatRegistrar::findLocaleForTextCodePage( )
+{
+ m_TxtLocale = 0;
+ EnumSystemLocalesA( CFormatRegistrar::EnumLocalesProc, LCID_INSTALLED );
+ return IsValidLocale( m_TxtLocale, LCID_INSTALLED );
+}
+
+bool CFormatRegistrar::isLocaleCodePage( LCID lcid, LCTYPE lctype, sal_uInt32 codepage )
+{
+ char buff[6];
+ sal_uInt32 localeCodePage;
+
+ OSL_ASSERT( IsValidLocale( lcid, LCID_INSTALLED ) );
+
+ // get the ansi codepage of the current locale
+ GetLocaleInfoA( lcid, lctype, buff, sizeof( buff ) );
+ localeCodePage = atol( buff );
+
+ return ( localeCodePage == codepage );
+}
+
+inline
+bool CFormatRegistrar::isLocaleOemCodePage( LCID lcid, sal_uInt32 codepage )
+{
+ return isLocaleCodePage( lcid, LOCALE_IDEFAULTCODEPAGE, codepage );
+}
+
+inline
+bool CFormatRegistrar::isLocaleAnsiCodePage( LCID lcid, sal_uInt32 codepage )
+{
+ return isLocaleCodePage( lcid, LOCALE_IDEFAULTANSICODEPAGE, codepage );
+}
+
+BOOL CALLBACK CFormatRegistrar::EnumLocalesProc( LPSTR lpLocaleStr )
+{
+ // the lpLocaleStr parameter is hexadecimal
+ LCID lcid = strtol( lpLocaleStr, nullptr, 16 );
+
+ if ( isLocaleAnsiCodePage( lcid, CFormatRegistrar::m_TxtCodePage ) ||
+ isLocaleOemCodePage( lcid, CFormatRegistrar::m_TxtCodePage ) )
+ {
+ CFormatRegistrar::m_TxtLocale = lcid;
+ return false; // stop enumerating
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/FetcList.hxx b/vcl/win/dtrans/FetcList.hxx
new file mode 100644
index 0000000000..2df28ff575
--- /dev/null
+++ b/vcl/win/dtrans/FetcList.hxx
@@ -0,0 +1,139 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include "Fetc.hxx"
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <vector>
+
+/*****************************************************************
+ a simple container for FORMATECT structures
+ instances of this class are not thread-safe
+*****************************************************************/
+
+class CFormatEtcContainer
+{
+public:
+ CFormatEtcContainer( );
+
+ // duplicates not allowed
+ void addFormatEtc( const CFormatEtc& fetc );
+
+ // removes the specified formatetc
+ void removeFormatEtc( const CFormatEtc& fetc );
+
+ // removes the formatetc at pos
+ void removeAllFormatEtc( );
+
+ bool hasFormatEtc( const CFormatEtc& fetc ) const;
+
+ bool hasElements( ) const;
+
+ // begin enumeration
+ void beginEnumFormatEtc( );
+
+ // copies the specified number of formatetc structures starting
+ // at the current enum position
+ // the return value is the number of copied elements; if the
+ // current enum position is at the end the return value is 0
+ sal_uInt32 nextFormatEtc( LPFORMATETC lpFetc, sal_uInt32 aNum = 1 );
+
+ // skips the specified number of elements in the container
+ bool skipFormatEtc( sal_uInt32 aNum );
+
+protected:
+ typedef std::vector< CFormatEtc > FormatEtcMap_t;
+
+private:
+ FormatEtcMap_t m_FormatMap;
+ FormatEtcMap_t::iterator m_EnumIterator;
+};
+
+/*****************************************************************
+ a helper class which converts data flavors to clipformats,
+ creates an appropriate formatetc structures and if possible
+ synthesizes clipboard formats if necessary, e.g. if text
+ is provided a locale will also be provided;
+ the class registers the formatetc within a CFormatEtcContainer
+
+ instances of this class are not thread-safe and multiple
+ instances of this class would use the same static variables
+ that's why this class should not be used by multiple threads,
+ only one thread of a process should use it
+*****************************************************************/
+
+// forward
+class CDataFormatTranslator;
+
+class CFormatRegistrar
+{
+public:
+ CFormatRegistrar( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const CDataFormatTranslator& aDataFormatTranslator );
+
+ void RegisterFormats( const css::uno::Reference< css::datatransfer::XTransferable >& aXTransferable,
+ CFormatEtcContainer& aFormatEtcContainer );
+
+ bool hasSynthesizedLocale( ) const;
+ static LCID getSynthesizedLocale( );
+ static sal_uInt32 getRegisteredTextCodePage( );
+ css::datatransfer::DataFlavor getRegisteredTextFlavor( ) const;
+
+ static bool isSynthesizeableFormat( const CFormatEtc& aFormatEtc );
+ static bool needsToSynthesizeAccompanyFormats( const CFormatEtc& aFormatEtc );
+
+private:
+ OUString getCharsetFromDataFlavor( const css::datatransfer::DataFlavor& aFlavor );
+
+ bool hasUnicodeFlavor(
+ const css::uno::Reference< css::datatransfer::XTransferable >& aXTransferable ) const;
+
+ static bool findLocaleForTextCodePage( );
+
+ static bool isLocaleOemCodePage( LCID lcid, sal_uInt32 codepage );
+ static bool isLocaleAnsiCodePage( LCID lcid, sal_uInt32 codepage );
+ static bool isLocaleCodePage( LCID lcid, LCTYPE lctype, sal_uInt32 codepage );
+
+ static BOOL CALLBACK EnumLocalesProc( LPSTR lpLocaleStr );
+
+private:
+ const CDataFormatTranslator& m_DataFormatTranslator;
+ bool m_bHasSynthesizedLocale;
+ css::datatransfer::DataFlavor m_RegisteredTextFlavor;
+
+ const css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ static LCID m_TxtLocale;
+ static sal_uInt32 m_TxtCodePage;
+
+private:
+ CFormatRegistrar( const CFormatRegistrar& );
+ CFormatRegistrar& operator=( const CFormatRegistrar& );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/FmtFilter.cxx b/vcl/win/dtrans/FmtFilter.cxx
new file mode 100644
index 0000000000..27d051f7e8
--- /dev/null
+++ b/vcl/win/dtrans/FmtFilter.cxx
@@ -0,0 +1,435 @@
+/* -*- 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 "FmtFilter.hxx"
+
+#include <o3tl/safeint.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <osl/diagnose.h>
+
+#include <shobjidl.h>
+#include <shlguid.h>
+#include <objidl.h>
+#include <shellapi.h>
+
+#include <string>
+#include <sstream>
+#include <vector>
+#include <iomanip>
+
+#include <systools/win32/comtools.hxx>
+
+using namespace com::sun::star::uno;
+
+namespace {
+
+#pragma pack(2)
+struct METAFILEHEADER
+{
+ DWORD key;
+ short hmf;
+ SMALL_RECT bbox;
+ WORD inch;
+ DWORD reserved;
+ WORD checksum;
+};
+#pragma pack()
+
+}
+
+// convert a windows metafile picture to a LibreOffice metafile picture
+
+Sequence< sal_Int8 > WinMFPictToOOMFPict( Sequence< sal_Int8 >& aMetaFilePict )
+{
+ OSL_ASSERT( aMetaFilePict.getLength( ) == sizeof( METAFILEPICT ) );
+
+ Sequence< sal_Int8 > mfpictStream;
+ METAFILEPICT* pMFPict = reinterpret_cast< METAFILEPICT* >( aMetaFilePict.getArray( ) );
+ HMETAFILE hMf = pMFPict->hMF;
+ sal_uInt32 nCount = GetMetaFileBitsEx( hMf, 0, nullptr );
+
+ if ( nCount > 0 )
+ {
+ mfpictStream.realloc( nCount + sizeof( METAFILEHEADER ) );
+
+ METAFILEHEADER* pMFHeader = reinterpret_cast< METAFILEHEADER* >( mfpictStream.getArray( ) );
+ SMALL_RECT aRect = { 0,
+ 0,
+ static_cast< short >( pMFPict->xExt ),
+ static_cast< short >( pMFPict->yExt ) };
+ USHORT nInch;
+
+ switch( pMFPict->mm )
+ {
+ case MM_TEXT:
+ nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::pt);
+ break;
+
+ case MM_LOMETRIC:
+ nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm10);
+ break;
+
+ case MM_HIMETRIC:
+ nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::mm100);
+ break;
+
+ case MM_LOENGLISH:
+ nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::in100);
+ break;
+
+ case MM_HIENGLISH:
+ case MM_ISOTROPIC:
+ case MM_ANISOTROPIC:
+ nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::in1000);
+ break;
+
+ case MM_TWIPS:
+ nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::twip);
+ break;
+
+ default:
+ nInch = o3tl::convert(1, o3tl::Length::in, o3tl::Length::master);
+ }
+
+ pMFHeader->key = 0x9AC6CDD7L;
+ pMFHeader->hmf = 0;
+ pMFHeader->bbox = aRect;
+ pMFHeader->inch = nInch;
+ pMFHeader->reserved = 0;
+ pMFHeader->checksum = 0;
+
+ char* pMFBuff = reinterpret_cast< char* >( mfpictStream.getArray( ) );
+
+ nCount = GetMetaFileBitsEx( pMFPict->hMF, nCount, pMFBuff + sizeof( METAFILEHEADER ) );
+ OSL_ASSERT( nCount > 0 );
+ }
+
+ return mfpictStream;
+}
+
+// convert a windows enhanced metafile to a LibreOffice metafile
+
+Sequence< sal_Int8 > WinENHMFPictToOOMFPict( HENHMETAFILE hEnhMetaFile )
+{
+ Sequence< sal_Int8 > aRet;
+ UINT nSize = 0;
+
+ if( hEnhMetaFile &&
+ ( ( nSize = GetEnhMetaFileBits( hEnhMetaFile, 0, nullptr ) ) != 0 ) )
+ {
+ aRet.realloc( nSize );
+
+ if( GetEnhMetaFileBits( hEnhMetaFile, nSize, reinterpret_cast<unsigned char*>(aRet.getArray()) ) != nSize )
+ aRet.realloc( 0 );
+ }
+
+ return aRet;
+}
+
+// convert a LibreOffice metafile picture to a windows metafile picture
+
+HMETAFILEPICT OOMFPictToWinMFPict( Sequence< sal_Int8 > const & aOOMetaFilePict )
+{
+ HMETAFILEPICT hPict = nullptr;
+ HMETAFILE hMtf = SetMetaFileBitsEx( aOOMetaFilePict.getLength(), reinterpret_cast<unsigned char const *>(aOOMetaFilePict.getConstArray()) );
+
+ if( hMtf )
+ {
+ METAFILEPICT* pPict = static_cast<METAFILEPICT*>(GlobalLock( hPict = GlobalAlloc( GHND, sizeof( METAFILEPICT ) ) ));
+
+ pPict->mm = 8;
+ pPict->xExt = 0;
+ pPict->yExt = 0;
+ pPict->hMF = hMtf;
+
+ GlobalUnlock( hPict );
+ }
+
+ return hPict;
+}
+
+// convert a LibreOffice metafile picture to a windows enhanced metafile picture
+
+HENHMETAFILE OOMFPictToWinENHMFPict( Sequence< sal_Int8 > const & aOOMetaFilePict )
+{
+ HENHMETAFILE hEnhMtf = SetEnhMetaFileBits( aOOMetaFilePict.getLength(), reinterpret_cast<unsigned char const *>(aOOMetaFilePict.getConstArray()) );
+
+ return hEnhMtf;
+}
+
+// convert a windows device independent bitmap into a LibreOffice bitmap
+
+Sequence< sal_Int8 > WinDIBToOOBMP( const Sequence< sal_Int8 >& aWinDIB )
+{
+ OSL_ENSURE(o3tl::make_unsigned(aWinDIB.getLength()) > sizeof(BITMAPINFOHEADER), "CF_DIBV5/CF_DIB too small (!)");
+ Sequence< sal_Int8 > ooBmpStream;
+
+ ooBmpStream.realloc(aWinDIB.getLength( ) + sizeof(BITMAPFILEHEADER));
+ const BITMAPINFOHEADER* pBmpInfoHdr = reinterpret_cast< const BITMAPINFOHEADER* >(aWinDIB.getConstArray());
+ BITMAPFILEHEADER* pBmpFileHdr = reinterpret_cast< BITMAPFILEHEADER* >(ooBmpStream.getArray());
+ const DWORD nSizeInfoOrV5(pBmpInfoHdr->biSize > sizeof(BITMAPINFOHEADER) ? sizeof(BITMAPV5HEADER) : sizeof(BITMAPINFOHEADER));
+ DWORD nOffset(sizeof(BITMAPFILEHEADER) + nSizeInfoOrV5);
+
+ memcpy(pBmpFileHdr + 1, pBmpInfoHdr, aWinDIB.getLength());
+
+ if(pBmpInfoHdr->biBitCount <= 8)
+ {
+ nOffset += (pBmpInfoHdr->biClrUsed ? pBmpInfoHdr->biClrUsed : (1 << pBmpInfoHdr->biBitCount)) << 2;
+ }
+ else if((BI_BITFIELDS == pBmpInfoHdr->biCompression ) && ((16 == pBmpInfoHdr->biBitCount ) || (32 == pBmpInfoHdr->biBitCount )))
+ {
+ nOffset += 12;
+ }
+
+ pBmpFileHdr->bfType = ('M' << 8) | 'B';
+ pBmpFileHdr->bfSize = 0; // maybe: nMemSize + sizeof(BITMAPFILEHEADER)
+ pBmpFileHdr->bfReserved1 = 0;
+ pBmpFileHdr->bfReserved2 = 0;
+ pBmpFileHdr->bfOffBits = nOffset;
+
+ return ooBmpStream;
+}
+
+// convert a LibreOffice bitmap into a windows device independent bitmap
+
+Sequence< sal_Int8 > OOBmpToWinDIB( Sequence< sal_Int8 >& aOOBmp )
+{
+ Sequence< sal_Int8 > winDIBStream( aOOBmp.getLength( ) - sizeof( BITMAPFILEHEADER ) );
+
+ memcpy( winDIBStream.getArray( ),
+ aOOBmp.getArray( ) + sizeof( BITMAPFILEHEADER ),
+ aOOBmp.getLength( ) - sizeof( BITMAPFILEHEADER ) );
+
+ return winDIBStream;
+}
+
+static std::string GetHtmlFormatHeader(size_t startHtml, size_t endHtml, size_t startFragment, size_t endFragment)
+{
+ std::ostringstream htmlHeader;
+ htmlHeader << "Version:1.0" << '\r' << '\n';
+ htmlHeader << "StartHTML:" << std::setw(10) << std::setfill('0') << std::dec << startHtml << '\r' << '\n';
+ htmlHeader << "EndHTML:" << std::setw(10) << std::setfill('0') << std::dec << endHtml << '\r' << '\n';
+ htmlHeader << "StartFragment:" << std::setw(10) << std::setfill('0') << std::dec << startFragment << '\r' << '\n';
+ htmlHeader << "EndFragment:" << std::setw(10) << std::setfill('0') << std::dec << endFragment << '\r' << '\n';
+ return htmlHeader.str();
+}
+
+// the case of these tags has to match what we output in our filters
+// both tags don't allow parameters
+const std::string TAG_HTML("<html>");
+const std::string TAG_END_HTML("</html>");
+
+// The body tag may have parameters so we need to search for the
+// closing '>' manually e.g. <body param> #92840#
+const std::string TAG_BODY("<body");
+const std::string TAG_END_BODY("</body");
+
+Sequence<sal_Int8> TextHtmlToHTMLFormat(Sequence<sal_Int8> const & aTextHtml)
+{
+ OSL_ASSERT(aTextHtml.getLength() > 0);
+
+ if (aTextHtml.getLength() <= 0)
+ return Sequence<sal_Int8>();
+
+ // fill the buffer with dummy values to calc the exact length
+ std::string dummyHtmlHeader = GetHtmlFormatHeader(0, 0, 0, 0);
+ size_t lHtmlFormatHeader = dummyHtmlHeader.length();
+
+ std::string textHtml(
+ reinterpret_cast<const char*>(aTextHtml.getConstArray()),
+ reinterpret_cast<const char*>(aTextHtml.getConstArray()) + aTextHtml.getLength());
+
+ std::string::size_type nStartHtml = textHtml.find(TAG_HTML) + lHtmlFormatHeader - 1; // we start one before '<HTML>' Word 2000 does also so
+ std::string::size_type nEndHtml = textHtml.find(TAG_END_HTML) + lHtmlFormatHeader + TAG_END_HTML.length() + 1; // our SOffice 5.2 wants 2 behind </HTML>?
+
+ // The body tag may have parameters so we need to search for the
+ // closing '>' manually e.g. <BODY param> #92840#
+ std::string::size_type nStartFragment = textHtml.find(">", textHtml.find(TAG_BODY)) + lHtmlFormatHeader + 1;
+ std::string::size_type nEndFragment = textHtml.find(TAG_END_BODY) + lHtmlFormatHeader;
+
+ std::string htmlFormat = GetHtmlFormatHeader(nStartHtml, nEndHtml, nStartFragment, nEndFragment);
+ htmlFormat += textHtml;
+
+ Sequence<sal_Int8> byteSequence(htmlFormat.length() + 1); // space the trailing '\0'
+ memset(byteSequence.getArray(), 0, byteSequence.getLength());
+
+ memcpy(
+ static_cast<void*>(byteSequence.getArray()),
+ static_cast<const void*>(htmlFormat.c_str()),
+ htmlFormat.length());
+
+ return byteSequence;
+}
+
+static std::wstring getFileExtension(const std::wstring& aFilename)
+{
+ std::wstring::size_type idx = aFilename.rfind(L".");
+ if (idx != std::wstring::npos)
+ {
+ return std::wstring(aFilename, idx);
+ }
+ return std::wstring();
+}
+
+const std::wstring SHELL_LINK_FILE_EXTENSION = L".lnk";
+
+static bool isShellLink(const std::wstring& aFilename)
+{
+ std::wstring ext = getFileExtension(aFilename);
+ return (_wcsicmp(ext.c_str(), SHELL_LINK_FILE_EXTENSION.c_str()) == 0);
+}
+
+/** Resolve a Windows Shell Link (lnk) file. If a resolution
+ is not possible simply return the provided name of the
+ lnk file. */
+static std::wstring getShellLinkTarget(const std::wstring& aLnkFile)
+{
+ OSL_ASSERT(isShellLink(aLnkFile));
+
+ std::wstring target = aLnkFile;
+
+ try
+ {
+ sal::systools::COMReference<IShellLinkW> pIShellLink;
+ pIShellLink.CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER);
+
+ sal::systools::COMReference<IPersistFile> pIPersistFile(pIShellLink,
+ sal::systools::COM_QUERY_THROW);
+
+ HRESULT hr = pIPersistFile->Load(aLnkFile.c_str(), STGM_READ);
+ if (FAILED(hr))
+ return target;
+
+ hr = pIShellLink->Resolve(nullptr, SLR_UPDATE | SLR_NO_UI);
+ if (FAILED(hr))
+ return target;
+
+ wchar_t pathW[MAX_PATH];
+ WIN32_FIND_DATAW wfd;
+ hr = pIShellLink->GetPath(pathW, MAX_PATH, &wfd, SLGP_RAWPATH);
+ if (FAILED(hr))
+ return target;
+
+ target = pathW;
+ }
+ catch(sal::systools::ComError& ex)
+ {
+ OSL_FAIL(ex.what());
+ }
+ return target;
+}
+
+typedef Sequence<sal_Int8> ByteSequence_t;
+
+/* Calculate the size required for turning a string list into
+ a double '\0' terminated string buffer */
+static size_t CalcSizeForStringListBuffer(const std::vector<std::wstring>& fileList)
+{
+ if ( fileList.empty() )
+ return 0;
+
+ size_t size = 1; // one for the very final '\0'
+ for (auto const& elem : fileList)
+ {
+ size += elem.length() + 1; // length including terminating '\0'
+ }
+ return (size * sizeof(std::vector<std::wstring>::value_type::value_type));
+}
+
+static ByteSequence_t FileListToByteSequence(const std::vector<std::wstring>& fileList)
+{
+ ByteSequence_t bseq;
+ size_t size = CalcSizeForStringListBuffer(fileList);
+
+ if (size > 0)
+ {
+ bseq.realloc(size);
+ wchar_t* p = reinterpret_cast<wchar_t*>(bseq.getArray());
+ ZeroMemory(p, size);
+
+ for (auto const& elem : fileList)
+ {
+ wcsncpy(p, elem.c_str(), elem.length());
+ p += (elem.length() + 1);
+ }
+ }
+ return bseq;
+}
+
+css::uno::Sequence<sal_Int8> CF_HDROPToFileList(HGLOBAL hGlobal)
+{
+ UINT nFiles = DragQueryFileW(static_cast<HDROP>(hGlobal), 0xFFFFFFFF, nullptr, 0);
+ std::vector<std::wstring> files;
+
+ for (UINT i = 0; i < nFiles; i++)
+ {
+ wchar_t buff[MAX_PATH];
+ /*UINT size =*/ DragQueryFileW(static_cast<HDROP>(hGlobal), i, buff, MAX_PATH);
+ std::wstring filename = buff;
+ if (isShellLink(filename))
+ filename = getShellLinkTarget(filename);
+ files.push_back(filename);
+ }
+ return FileListToByteSequence(files);
+}
+
+// convert a windows bitmap handle into a LibreOffice bitmap
+
+Sequence< sal_Int8 > WinBITMAPToOOBMP( HBITMAP aHBMP )
+{
+ Sequence< sal_Int8 > ooBmpStream;
+
+ SIZE aBmpSize;
+ if( GetBitmapDimensionEx( aHBMP, &aBmpSize ) )
+ {
+ // fill bitmap info header
+ size_t nDataBytes = 4 * aBmpSize.cy * aBmpSize.cy;
+ Sequence< sal_Int8 > aBitmapStream(
+ sizeof(BITMAPINFO) +
+ nDataBytes
+ );
+ PBITMAPINFOHEADER pBmp = reinterpret_cast<PBITMAPINFOHEADER>(aBitmapStream.getArray());
+ pBmp->biSize = sizeof( BITMAPINFOHEADER );
+ pBmp->biWidth = aBmpSize.cx;
+ pBmp->biHeight = aBmpSize.cy;
+ pBmp->biPlanes = 1;
+ pBmp->biBitCount = 32;
+ pBmp->biCompression = BI_RGB;
+ pBmp->biSizeImage = static_cast<DWORD>(nDataBytes);
+ pBmp->biXPelsPerMeter = 1000;
+ pBmp->biYPelsPerMeter = 1000;
+ pBmp->biClrUsed = 0;
+ pBmp->biClrImportant = 0;
+ if( GetDIBits( nullptr, // DC, 0 is a default GC, basically that of the desktop
+ aHBMP,
+ 0, aBmpSize.cy,
+ aBitmapStream.getArray() + sizeof(BITMAPINFO),
+ reinterpret_cast<LPBITMAPINFO>(pBmp),
+ DIB_RGB_COLORS ) )
+ {
+ ooBmpStream = WinDIBToOOBMP( aBitmapStream );
+ }
+ }
+
+ return ooBmpStream;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/FmtFilter.hxx b/vcl/win/dtrans/FmtFilter.hxx
new file mode 100644
index 0000000000..b4fb1e1fc3
--- /dev/null
+++ b/vcl/win/dtrans/FmtFilter.hxx
@@ -0,0 +1,84 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+#if !defined WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <objidl.h>
+
+/*------------------------------------------------------------------------
+ input:
+ aMetaFilePict - a sequence of bytes containing a METAFILEPICT struct
+------------------------------------------------------------------------*/
+css::uno::Sequence<sal_Int8> WinMFPictToOOMFPict(css::uno::Sequence<sal_Int8>& aMetaFilePict);
+css::uno::Sequence<sal_Int8> WinENHMFPictToOOMFPict(HENHMETAFILE hEnhMetaFile);
+
+/*------------------------------------------------------------------------
+ input:
+ aByteStream - a sequence of bytes containing a LibreOffice metafile
+ picture with a leading METAFILEHEADER
+------------------------------------------------------------------------*/
+HMETAFILEPICT OOMFPictToWinMFPict(css::uno::Sequence<sal_Int8> const& aOOMetaFilePict);
+HENHMETAFILE OOMFPictToWinENHMFPict(css::uno::Sequence<sal_Int8> const& aOOMetaFilePict);
+
+/*------------------------------------------------------------------------
+ input:
+ aWinDIB - sequence of bytes containing a windows device independent
+ bitmap
+------------------------------------------------------------------------*/
+css::uno::Sequence<sal_Int8> WinDIBToOOBMP(const css::uno::Sequence<sal_Int8>& aWinDIB);
+
+/*------------------------------------------------------------------------
+ input:
+ aWinDIB - sequence of bytes containing a windows bitmap handle
+------------------------------------------------------------------------*/
+css::uno::Sequence<sal_Int8> WinBITMAPToOOBMP(HBITMAP);
+
+/*------------------------------------------------------------------------
+ input:
+ aOOBmp - sequence of bytes containing a LibreOffice bitmap
+ May contain CF_DIBV5 or CF_DIB, but removing the BITMAPFILEHEADER
+ is always the same size
+------------------------------------------------------------------------*/
+css::uno::Sequence<sal_Int8> OOBmpToWinDIB(css::uno::Sequence<sal_Int8>& aOOBmp);
+
+/*------------------------------------------------------------------------
+ input:
+ aTextHtml - a sequence of text/html which will be converted to the
+ HTML Format; the HTML Format has header before the real html data
+ the Format is described in the MSDN Library under HTML Clipboard
+ Format
+------------------------------------------------------------------------*/
+css::uno::Sequence<sal_Int8> TextHtmlToHTMLFormat(css::uno::Sequence<sal_Int8> const& aTextHtml);
+
+/**
+ Return a FileList in which Windows Shell Links (lnk) are resolved.
+ If for whatever reason a resolution is not possible leave the
+ original lnk file.
+*/
+css::uno::Sequence<sal_Int8> CF_HDROPToFileList(HGLOBAL hGlobal);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/ImplHelper.cxx b/vcl/win/dtrans/ImplHelper.cxx
new file mode 100644
index 0000000000..84c7383b2c
--- /dev/null
+++ b/vcl/win/dtrans/ImplHelper.cxx
@@ -0,0 +1,352 @@
+/* -*- 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 <osl/diagnose.h>
+#include "ImplHelper.hxx"
+#include <rtl/tencinfo.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <string.h>
+#include <memory>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+#include <vector>
+
+#define FORMATETC_EXACT_MATCH 1
+#define FORMATETC_PARTIAL_MATCH -1
+#define FORMATETC_NO_MATCH 0
+
+// returns a windows codepage appropriate to the
+// given mime charset parameter value
+
+sal_uInt32 getWinCPFromMimeCharset( const OUString& charset )
+{
+ sal_uInt32 winCP = GetACP( );
+
+ if ( charset.getLength( ) )
+ {
+ OString osCharset(
+ charset.getStr( ), charset.getLength( ), RTL_TEXTENCODING_ASCII_US );
+
+ rtl_TextEncoding txtEnc =
+ rtl_getTextEncodingFromMimeCharset( osCharset.getStr( ) );
+
+ sal_uIntPtr winChrs = rtl_getBestWindowsCharsetFromTextEncoding( txtEnc );
+
+ CHARSETINFO chrsInf;
+ bool bRet = TranslateCharsetInfo( reinterpret_cast<DWORD*>(winChrs), &chrsInf, TCI_SRCCHARSET );
+
+ // if one of the above functions fails
+ // we will return the current ANSI codepage
+ // of this thread
+ if ( bRet )
+ winCP = chrsInf.ciACP;
+ }
+
+ return winCP;
+}
+
+// returns a windows codepage appropriate to the
+// given locale and locale type
+
+OUString getWinCPFromLocaleId( LCID lcid, LCTYPE lctype )
+{
+ OSL_ASSERT( IsValidLocale( lcid, LCID_SUPPORTED ) );
+
+ // we set a default value
+ OUString winCP;
+
+ // set a default value
+ if ( LOCALE_IDEFAULTCODEPAGE == lctype )
+ {
+ winCP = OUString::number( static_cast<sal_Int32>(GetOEMCP( )) );
+ }
+ else if ( LOCALE_IDEFAULTANSICODEPAGE == lctype )
+ {
+ winCP = OUString::number( static_cast<sal_Int32>(GetACP( )) );
+ }
+ else
+ OSL_ASSERT( false );
+
+ // First, get required buffer size, in characters
+ int nResult = GetLocaleInfoW(
+ lcid, lctype, nullptr, 0 );
+
+ OSL_ASSERT( nResult );
+
+ if ( nResult )
+ {
+ std::unique_ptr<wchar_t[]> buff( new wchar_t[nResult] );
+ // Now get the actual data
+ nResult = GetLocaleInfoW( lcid, lctype, buff.get(), nResult );
+
+ OSL_ASSERT(nResult);
+
+ if (nResult)
+ winCP = o3tl::toU( buff.get() );
+
+ }
+
+ return winCP;
+}
+
+// returns a mime charset parameter value appropriate
+// to the given codepage, optional a prefix can be
+// given, e.g. "windows-" or "cp"
+
+OUString getMimeCharsetFromWinCP( sal_uInt32 cp, std::u16string_view aPrefix )
+{
+ return aPrefix + cptostr( cp );
+}
+
+// returns a mime charset parameter value appropriate
+// to the given locale id and locale type, optional a
+// prefix can be given, e.g. "windows-" or "cp"
+
+OUString getMimeCharsetFromLocaleId( LCID lcid, LCTYPE lctype, std::u16string_view aPrefix )
+{
+ OUString charset = getWinCPFromLocaleId( lcid, lctype );
+ return aPrefix + charset;
+}
+
+// IsOEMCP
+
+bool IsOEMCP( sal_uInt32 codepage )
+{
+ OSL_ASSERT( IsValidCodePage( codepage ) );
+
+ sal_uInt32 arrOEMCP[] = { 437, 708, 709, 710, 720, 737,
+ 775, 850, 852, 855, 857, 860,
+ 861, 862, 863, 864, 865, 866,
+ 869, 874, 932, 936, 949, 950, 1361 };
+
+ for ( size_t i = 0; i < SAL_N_ELEMENTS( arrOEMCP ); ++i )
+ if ( arrOEMCP[i] == codepage )
+ return true;
+
+ return false;
+}
+
+// converts a codepage into its string representation
+
+OUString cptostr( sal_uInt32 codepage )
+{
+ OSL_ASSERT( IsValidCodePage( codepage ) );
+
+ return OUString::number( static_cast<sal_Int64>( codepage ) );
+}
+
+// OleStdDeleteTargetDevice()
+//
+// Purpose:
+//
+// Parameters:
+//
+// Return Value:
+// SCODE - S_OK if successful
+void DeleteTargetDevice( DVTARGETDEVICE* ptd )
+{
+ __try
+ {
+ CoTaskMemFree( ptd );
+ }
+ __except( EXCEPTION_EXECUTE_HANDLER )
+ {
+ OSL_FAIL( "Error DeleteTargetDevice" );
+ }
+}
+
+// OleStdCopyTargetDevice()
+//
+// Purpose:
+// duplicate a TARGETDEVICE struct. this function allocates memory for
+// the copy. the caller MUST free the allocated copy when done with it
+// using the standard allocator returned from CoGetMalloc.
+// (OleStdFree can be used to free the copy).
+//
+// Parameters:
+// ptdSrc pointer to source TARGETDEVICE
+//
+// Return Value:
+// pointer to allocated copy of ptdSrc
+// if ptdSrc==NULL then returns NULL is returned.
+// if ptdSrc!=NULL and memory allocation fails, then NULL is returned
+DVTARGETDEVICE* CopyTargetDevice( DVTARGETDEVICE* ptdSrc )
+{
+ DVTARGETDEVICE* ptdDest = nullptr;
+
+ __try
+ {
+ if ( nullptr != ptdSrc )
+ {
+ ptdDest = static_cast< DVTARGETDEVICE* >( CoTaskMemAlloc( ptdSrc->tdSize ) );
+ memcpy( ptdDest, ptdSrc, static_cast< size_t >( ptdSrc->tdSize ) );
+ }
+ }
+ __except( EXCEPTION_EXECUTE_HANDLER )
+ {
+ }
+
+ return ptdDest;
+}
+
+// OleStdCopyFormatEtc()
+//
+// Purpose:
+// Copies the contents of a FORMATETC structure. this function takes
+// special care to copy correctly copying the pointer to the TARGETDEVICE
+// contained within the source FORMATETC structure.
+// if the source FORMATETC has a non-NULL TARGETDEVICE, then a copy
+// of the TARGETDEVICE will be allocated for the destination of the
+// FORMATETC (petcDest).
+//
+// NOTE: the caller MUST free the allocated copy of the TARGETDEVICE
+// within the destination FORMATETC when done with it
+// using the standard allocator returned from CoGetMalloc.
+// (OleStdFree can be used to free the copy).
+//
+// Parameters:
+// petcDest pointer to destination FORMATETC
+// petcSrc pointer to source FORMATETC
+//
+// Return Value:
+// returns TRUE if copy was successful;
+// returns FALSE if not successful, e.g. one or both of the pointers
+// were invalid or the pointers were equal
+bool CopyFormatEtc( LPFORMATETC petcDest, LPFORMATETC petcSrc )
+{
+ bool bRet = false;
+
+ __try
+ {
+ if ( petcDest != petcSrc )
+ {
+
+ petcDest->cfFormat = petcSrc->cfFormat;
+
+ petcDest->ptd = nullptr;
+ if ( nullptr != petcSrc->ptd )
+ petcDest->ptd = CopyTargetDevice(petcSrc->ptd);
+
+ petcDest->dwAspect = petcSrc->dwAspect;
+ petcDest->lindex = petcSrc->lindex;
+ petcDest->tymed = petcSrc->tymed;
+
+ bRet = true;
+ }
+ }
+ __except( EXCEPTION_EXECUTE_HANDLER )
+ {
+ OSL_FAIL( "Error CopyFormatEtc" );
+ }
+
+ return bRet;
+}
+
+// returns:
+// 1 for exact match,
+// 0 for no match,
+// -1 for partial match (which is defined to mean the left is a subset
+// of the right: fewer aspects, null target device, fewer medium).
+
+sal_Int32 CompareFormatEtc( const FORMATETC* pFetcLhs, const FORMATETC* pFetcRhs )
+{
+ sal_Int32 nMatch = FORMATETC_EXACT_MATCH;
+
+ __try
+ {
+ if ( pFetcLhs != pFetcRhs )
+ {
+ if ( ( pFetcLhs->cfFormat != pFetcRhs->cfFormat ) ||
+ ( pFetcLhs->lindex != pFetcRhs->lindex ) ||
+ !CompareTargetDevice( pFetcLhs->ptd, pFetcRhs->ptd ) )
+ {
+ nMatch = FORMATETC_NO_MATCH;
+ }
+
+ else if ( pFetcLhs->dwAspect == pFetcRhs->dwAspect )
+ // same aspects; equal
+ ;
+ else if ( ( pFetcLhs->dwAspect & ~pFetcRhs->dwAspect ) != 0 )
+ {
+ // left not subset of aspects of right; not equal
+ nMatch = FORMATETC_NO_MATCH;
+ }
+ else
+ // left subset of right
+ nMatch = FORMATETC_PARTIAL_MATCH;
+
+ if ( nMatch == FORMATETC_EXACT_MATCH || nMatch == FORMATETC_PARTIAL_MATCH )
+ {
+ if ( pFetcLhs->tymed == pFetcRhs->tymed )
+ // same medium flags; equal
+ ;
+ else if ( ( pFetcLhs->tymed & ~pFetcRhs->tymed ) != 0 )
+ {
+ // left not subset of medium flags of right; not equal
+ nMatch = FORMATETC_NO_MATCH;
+ }
+ else
+ // left subset of right
+ nMatch = FORMATETC_PARTIAL_MATCH;
+ }
+ }
+ }
+ __except( EXCEPTION_EXECUTE_HANDLER )
+ {
+ OSL_FAIL( "Error CompareFormatEtc" );
+ nMatch = FORMATETC_NO_MATCH;
+ }
+
+ return nMatch;
+}
+
+bool CompareTargetDevice( DVTARGETDEVICE* ptdLeft, DVTARGETDEVICE const * ptdRight )
+{
+ bool bRet = false;
+
+ __try
+ {
+ if ( ptdLeft == ptdRight )
+ {
+ // same address of td; must be same (handles NULL case)
+ bRet = true;
+ }
+
+ // one of the two is NULL
+ else if ( ( nullptr != ptdRight ) && ( nullptr != ptdLeft ) )
+
+ if ( ptdLeft->tdSize == ptdRight->tdSize )
+
+ if ( memcmp( ptdLeft, ptdRight, ptdLeft->tdSize ) == 0 )
+ bRet = true;
+ }
+ __except( EXCEPTION_EXECUTE_HANDLER )
+ {
+ OSL_FAIL( "Error CompareTargetDevice" );
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/ImplHelper.hxx b/vcl/win/dtrans/ImplHelper.hxx
new file mode 100644
index 0000000000..df6731ec7a
--- /dev/null
+++ b/vcl/win/dtrans/ImplHelper.hxx
@@ -0,0 +1,74 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+#if !defined WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <objidl.h>
+
+// target device and formatetc helper
+void DeleteTargetDevice(DVTARGETDEVICE* ptd);
+bool CopyFormatEtc(LPFORMATETC petcDest, LPFORMATETC petcSrc);
+sal_Int32 CompareFormatEtc(const FORMATETC* pFetcLeft, const FORMATETC* pFetcRight);
+bool CompareTargetDevice(DVTARGETDEVICE* ptdLeft, DVTARGETDEVICE const* ptdRight);
+DVTARGETDEVICE* CopyTargetDevice(DVTARGETDEVICE* ptdSrc);
+
+// some codepage helper functions
+
+// returns a windows codepage appropriate to the
+// given mime charset parameter value
+
+sal_uInt32 getWinCPFromMimeCharset(const OUString& charset);
+
+// returns a windows codepage appropriate to the
+// given locale and locale type
+
+OUString getWinCPFromLocaleId(LCID lcid, LCTYPE lctype);
+
+// returns a mime charset parameter value appropriate
+// to the given codepage, optional a prefix can be
+// given, e.g. "windows-" or "cp"
+
+OUString getMimeCharsetFromWinCP(sal_uInt32 cp, std::u16string_view aPrefix);
+
+// returns a mime charset parameter value appropriate
+// to the given locale id and locale type, optional a
+// prefix can be given, e.g. "windows-" or "cp"
+
+OUString getMimeCharsetFromLocaleId(LCID lcid, LCTYPE lctype, std::u16string_view aPrefix);
+
+// returns true, if a given codepage is an oem codepage
+
+bool IsOEMCP(sal_uInt32 codepage);
+
+// converts a codepage into a string representation
+
+OUString cptostr(sal_uInt32 codepage);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/MimeAttrib.hxx b/vcl/win/dtrans/MimeAttrib.hxx
new file mode 100644
index 0000000000..48dd5fc11e
--- /dev/null
+++ b/vcl/win/dtrans/MimeAttrib.hxx
@@ -0,0 +1,31 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+
+constexpr OUString TEXTPLAIN_PARAM_CHARSET(u"charset"_ustr);
+
+constexpr OUString PRE_WINDOWS_CODEPAGE(u"windows"_ustr);
+constexpr OUString PRE_OEM_CODEPAGE(u"cp"_ustr);
+constexpr OUString CHARSET_UTF16(u"utf-16"_ustr);
+constexpr OUString CHARSET_UNICODE(u"unicode"_ustr);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/MtaOleClipb.cxx b/vcl/win/dtrans/MtaOleClipb.cxx
new file mode 100644
index 0000000000..fcf16df1ad
--- /dev/null
+++ b/vcl/win/dtrans/MtaOleClipb.cxx
@@ -0,0 +1,748 @@
+/* -*- 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 .
+ */
+
+/*
+ MtaOleClipb.cxx - documentation
+
+ This class setup a single threaded apartment (sta) thread to deal with
+ the ole clipboard, which runs only in an sta thread.
+ The consequence is that callback from the ole clipboard are in the
+ context of this sta thread. In the soffice applications this may lead
+ to problems because they all use the one and only mutex called
+ SolarMutex.
+ In order to transfer clipboard requests to our sta thread we use a
+ hidden window and forward these requests via window messages.
+*/
+
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include "MtaOleClipb.hxx"
+
+#include <svsys.h>
+#include <win/saldata.hxx>
+
+#include <osl/thread.h>
+
+#include <wchar.h>
+#include <process.h>
+
+#include <systools/win32/comtools.hxx>
+#include <systools/win32/retry_if_failed.hxx>
+
+#include <comphelper/windowserrorstring.hxx>
+
+namespace /* private */
+{
+ const wchar_t g_szWndClsName[] = L"MtaOleReqWnd###";
+
+ // messages constants
+
+ const sal_uInt32 MSG_SETCLIPBOARD = WM_USER + 0x0001;
+ const sal_uInt32 MSG_GETCLIPBOARD = WM_USER + 0x0002;
+ const sal_uInt32 MSG_REGCLIPVIEWER = WM_USER + 0x0003;
+ const sal_uInt32 MSG_FLUSHCLIPBOARD = WM_USER + 0x0004;
+ const sal_uInt32 MSG_SHUTDOWN = WM_USER + 0x0005;
+
+ const sal_uInt32 MAX_WAITTIME = 10000; // msec
+ const sal_uInt32 MAX_WAIT_SHUTDOWN = 10000; // msec
+
+ const bool MANUAL_RESET = true;
+ const bool INIT_NONSIGNALED = false;
+
+ /* Cannot use osl conditions because they are blocking
+ without waking up on messages sent by another thread
+ this leads to deadlocks because we are blocking the
+ communication between inter-thread marshalled COM
+ pointers.
+ COM Proxy-Stub communication uses SendMessages for
+ synchronization purposes.
+ */
+ class Win32Condition
+ {
+ public:
+ Win32Condition() = default;
+
+ ~Win32Condition() { CloseHandle(m_hEvent); }
+
+ // wait infinite for own event (or abort event) be signaled
+ // leave messages sent through
+ bool wait(HANDLE hEvtAbort)
+ {
+ const HANDLE hWaitArray[2] = { m_hEvent, hEvtAbort };
+ while (true)
+ {
+ DWORD dwResult
+ = MsgWaitForMultipleObjects(2, hWaitArray, FALSE, INFINITE, QS_SENDMESSAGE);
+
+ switch (dwResult)
+ {
+ case WAIT_OBJECT_0: // wait successful
+ return true;
+
+ case WAIT_OBJECT_0 + 1: // wait aborted
+ return false;
+
+ case WAIT_OBJECT_0 + 2:
+ {
+ /* PeekMessage processes all messages in the SendMessage
+ queue that's what we want, messages from the PostMessage
+ queue stay untouched */
+ MSG msg;
+ PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
+
+ break;
+ }
+
+ default: // WAIT_FAILED?
+ return false;
+ }
+ }
+ }
+
+ // set the event
+ void set() { SetEvent(m_hEvent); }
+
+ private:
+ HANDLE m_hEvent = CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr);
+
+ // prevent copy/assignment
+ Win32Condition(const Win32Condition&) = delete;
+ Win32Condition& operator=(const Win32Condition&) = delete;
+ };
+
+ // we use one condition for every request
+
+ struct MsgCtx
+ {
+ Win32Condition aCondition;
+ HRESULT hr;
+ };
+
+} /* namespace private */
+
+// static member initialization
+
+CMtaOleClipboard* CMtaOleClipboard::s_theMtaOleClipboardInst = nullptr;
+
+// marshal an IDataObject
+
+//inline
+static HRESULT MarshalIDataObjectInStream( IDataObject* pIDataObject, LPSTREAM* ppStream )
+{
+ OSL_ASSERT( nullptr != pIDataObject );
+ OSL_ASSERT( nullptr != ppStream );
+
+ *ppStream = nullptr;
+ return CoMarshalInterThreadInterfaceInStream(
+ __uuidof(IDataObject), //The IID of interface to be marshalled
+ pIDataObject, //The interface pointer
+ ppStream //IStream pointer
+ );
+}
+
+// unmarshal an IDataObject
+
+//inline
+static HRESULT UnmarshalIDataObjectAndReleaseStream( LPSTREAM lpStream, IDataObject** ppIDataObject )
+{
+ OSL_ASSERT( nullptr != lpStream );
+ OSL_ASSERT( nullptr != ppIDataObject );
+
+ *ppIDataObject = nullptr;
+ return CoGetInterfaceAndReleaseStream(
+ lpStream,
+ __uuidof(IDataObject),
+ reinterpret_cast<LPVOID*>(ppIDataObject));
+}
+
+// helper class to ensure that the calling thread has com initialized
+
+namespace {
+
+class CAutoComInit
+{
+public:
+ /*
+ to be safe we call CoInitializeEx
+ although it is not necessary if
+ the calling thread was created
+ using osl_CreateThread because
+ this function calls CoInitializeEx
+ for every thread it creates
+ */
+ CAutoComInit( ) : m_hResult( CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ) )
+ {
+ if ( S_OK == m_hResult )
+ OSL_FAIL(
+ "com was not yet initialized, the thread was not created using osl_createThread" );
+ else if ( FAILED( m_hResult ) && !( RPC_E_CHANGED_MODE == m_hResult ) )
+ OSL_FAIL(
+ "com could not be initialized, maybe the thread was not created using osl_createThread" );
+ }
+
+ ~CAutoComInit( )
+ {
+ /*
+ we only call CoUninitialize when
+ CoInitializeEx returned S_FALSE, what
+ means that com was already initialize
+ for that thread so we keep the balance
+ if CoInitializeEx returned S_OK what means
+ com was not yet initialized we better
+ let com initialized or we may run into
+ the realm of undefined behaviour
+ */
+ if ( m_hResult == S_FALSE )
+ CoUninitialize( );
+ }
+
+private:
+ HRESULT m_hResult;
+};
+
+}
+
+// ctor
+
+CMtaOleClipboard::CMtaOleClipboard( ) :
+ m_hOleThread( nullptr ),
+ m_uOleThreadId( 0 ),
+ // signals that the thread was successfully setup
+ m_hEvtThrdReady(CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr )),
+ m_hwndMtaOleReqWnd( nullptr ),
+ // signals that the window is destroyed - to stop waiting any winproc result
+ m_hEvtWndDisposed(CreateEventW(nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr)),
+ m_MtaOleReqWndClassAtom( 0 ),
+ m_pfncClipViewerCallback( nullptr ),
+ m_bRunClipboardNotifierThread( true ),
+ m_hClipboardChangedEvent( m_hClipboardChangedNotifierEvents[0] ),
+ m_hTerminateClipboardChangedNotifierEvent( m_hClipboardChangedNotifierEvents[1] ),
+ m_ClipboardChangedEventCount( 0 )
+{
+ OSL_ASSERT( nullptr != m_hEvtThrdReady );
+ SAL_WARN_IF(!m_hEvtWndDisposed, "vcl.win.dtrans", "CreateEventW failed: m_hEvtWndDisposed is nullptr");
+
+ s_theMtaOleClipboardInst = this;
+
+ m_hOleThread = reinterpret_cast<HANDLE>(_beginthreadex(
+ nullptr, 0, CMtaOleClipboard::oleThreadProc, this, 0, &m_uOleThreadId ));
+ OSL_ASSERT( nullptr != m_hOleThread );
+
+ // setup the clipboard changed notifier thread
+
+ m_hClipboardChangedNotifierEvents[0] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr );
+ OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[0] );
+
+ m_hClipboardChangedNotifierEvents[1] = CreateEventW( nullptr, MANUAL_RESET, INIT_NONSIGNALED, nullptr );
+ OSL_ASSERT( nullptr != m_hClipboardChangedNotifierEvents[1] );
+
+ m_hClipboardChangedNotifierThread = reinterpret_cast<HANDLE>(_beginthreadex(
+ nullptr, 0, CMtaOleClipboard::clipboardChangedNotifierThreadProc, this, 0, nullptr ));
+
+ OSL_ASSERT( nullptr != m_hClipboardChangedNotifierThread );
+}
+
+// dtor
+
+CMtaOleClipboard::~CMtaOleClipboard( )
+{
+ // block calling threads out
+ if ( nullptr != m_hEvtThrdReady )
+ ResetEvent( m_hEvtThrdReady );
+
+ // terminate the clipboard changed notifier thread
+ m_bRunClipboardNotifierThread = false;
+ SetEvent( m_hTerminateClipboardChangedNotifierEvent );
+
+ // unblock whoever could still wait for event processing
+ if (m_hEvtWndDisposed)
+ SetEvent(m_hEvtWndDisposed);
+
+ sal_uInt32 dwResult = WaitForSingleObject(
+ m_hClipboardChangedNotifierThread, MAX_WAIT_SHUTDOWN );
+
+ OSL_ENSURE( dwResult == WAIT_OBJECT_0, "clipboard notifier thread could not terminate" );
+
+ if ( nullptr != m_hClipboardChangedNotifierThread )
+ CloseHandle( m_hClipboardChangedNotifierThread );
+
+ if ( nullptr != m_hClipboardChangedNotifierEvents[0] )
+ CloseHandle( m_hClipboardChangedNotifierEvents[0] );
+
+ if ( nullptr != m_hClipboardChangedNotifierEvents[1] )
+ CloseHandle( m_hClipboardChangedNotifierEvents[1] );
+
+ // end the thread
+ // because DestroyWindow can only be called
+ // from within the thread that created the window
+ sendMessage( MSG_SHUTDOWN );
+
+ // wait for thread shutdown
+ dwResult = WaitForSingleObject( m_hOleThread, MAX_WAIT_SHUTDOWN );
+ OSL_ENSURE( dwResult == WAIT_OBJECT_0, "OleThread could not terminate" );
+
+ if ( nullptr != m_hOleThread )
+ CloseHandle( m_hOleThread );
+
+ if ( nullptr != m_hEvtThrdReady )
+ CloseHandle( m_hEvtThrdReady );
+
+ if (m_hEvtWndDisposed)
+ CloseHandle(m_hEvtWndDisposed);
+
+ if ( m_MtaOleReqWndClassAtom )
+ UnregisterClassW( g_szWndClsName, nullptr );
+
+ OSL_ENSURE( ( nullptr == m_pfncClipViewerCallback ),
+ "Clipboard viewer not properly unregistered" );
+}
+
+HRESULT CMtaOleClipboard::flushClipboard( )
+{
+ if ( !WaitForThreadReady( ) )
+ {
+ OSL_FAIL( "clipboard sta thread not ready" );
+ return E_FAIL;
+ }
+
+ OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId,
+ "flushClipboard from within clipboard sta thread called" );
+
+ MsgCtx aMsgCtx;
+
+ const bool bWaitSuccess = postMessage(MSG_FLUSHCLIPBOARD, 0, reinterpret_cast<LPARAM>(&aMsgCtx))
+ && aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
+
+ return bWaitSuccess ? aMsgCtx.hr : E_ABORT;
+}
+
+HRESULT CMtaOleClipboard::getClipboard( IDataObject** ppIDataObject )
+{
+ OSL_PRECOND( nullptr != ppIDataObject, "invalid parameter" );
+ OSL_PRECOND( GetCurrentThreadId( ) != m_uOleThreadId, "getClipboard from within clipboard sta thread called" );
+
+ if ( !WaitForThreadReady( ) )
+ {
+ OSL_FAIL( "clipboard sta thread not ready" );
+ return E_FAIL;
+ }
+
+ CAutoComInit comAutoInit;
+
+ LPSTREAM lpStream;
+
+ *ppIDataObject = nullptr;
+
+ MsgCtx aMsgCtx;
+
+ const bool bWaitSuccess = postMessage(MSG_GETCLIPBOARD, reinterpret_cast<WPARAM>(&lpStream),
+ reinterpret_cast<LPARAM>(&aMsgCtx))
+ && aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
+
+ HRESULT hr = bWaitSuccess ? aMsgCtx.hr : E_ABORT;
+
+ if ( SUCCEEDED( hr ) )
+ {
+ hr = UnmarshalIDataObjectAndReleaseStream( lpStream, ppIDataObject );
+ OSL_ENSURE( SUCCEEDED( hr ), "unmarshalling clipboard data object failed" );
+ }
+
+ return hr;
+}
+
+// this is an asynchronous method that's why we don't wait until the
+// request is completed
+
+HRESULT CMtaOleClipboard::setClipboard( IDataObject* pIDataObject )
+{
+ if ( !WaitForThreadReady( ) )
+ {
+ OSL_FAIL( "clipboard sta thread not ready" );
+ return E_FAIL;
+ }
+
+ CAutoComInit comAutoInit;
+
+ OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "setClipboard from within the clipboard sta thread called" );
+
+ // because we marshall this request
+ // into the sta thread we better
+ // acquire the interface here so
+ // that the object will not be
+ // destroyed before the ole clipboard
+ // can acquire it
+ // remember: pIDataObject may be NULL
+ // which is a request to clear the
+ // current clipboard content
+ if ( pIDataObject )
+ pIDataObject->AddRef( );
+
+ postMessage(
+ MSG_SETCLIPBOARD,
+ reinterpret_cast< WPARAM >( pIDataObject ) );
+
+ // because this is an asynchronous function
+ // the return value is useless
+ return S_OK;
+}
+
+// register a clipboard viewer
+
+bool CMtaOleClipboard::registerClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback )
+{
+ if ( !WaitForThreadReady( ) )
+ {
+ OSL_FAIL( "clipboard sta thread not ready" );
+ return false;
+ }
+
+ OSL_ENSURE( GetCurrentThreadId( ) != m_uOleThreadId, "registerClipViewer from within the OleThread called" );
+
+ MsgCtx aMsgCtx;
+
+ if (postMessage(MSG_REGCLIPVIEWER, reinterpret_cast<WPARAM>(pfncClipViewerCallback),
+ reinterpret_cast<LPARAM>(&aMsgCtx)))
+ aMsgCtx.aCondition.wait(m_hEvtWndDisposed);
+
+ return false;
+}
+
+// register a clipboard viewer
+
+bool CMtaOleClipboard::onRegisterClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback )
+{
+ bool bRet = false;
+
+ // we need exclusive access because the clipboard changed notifier
+ // thread also accesses this variable
+ std::unique_lock aGuard( m_pfncClipViewerCallbackMutex );
+
+ // register if not yet done
+ if ( ( nullptr != pfncClipViewerCallback ) && ( nullptr == m_pfncClipViewerCallback ) )
+ {
+ // SetClipboardViewer sends a WM_DRAWCLIPBOARD message we ignore
+ // this message if we register ourself as clip viewer
+ m_bInRegisterClipViewer = true;
+ bRet = AddClipboardFormatListener(m_hwndMtaOleReqWnd);
+ m_bInRegisterClipViewer = false;
+
+ // save the new callback function
+ m_pfncClipViewerCallback = pfncClipViewerCallback;
+ }
+ else if ( ( nullptr == pfncClipViewerCallback ) && ( nullptr != m_pfncClipViewerCallback ) )
+ {
+ m_pfncClipViewerCallback = nullptr;
+
+ // unregister if input parameter is NULL and we previously registered
+ // as clipboard viewer
+ bRet = RemoveClipboardFormatListener(m_hwndMtaOleReqWnd);
+ }
+
+ return bRet;
+}
+
+HRESULT CMtaOleClipboard::onSetClipboard( IDataObject* pIDataObject )
+{
+ return sal::systools::RetryIfFailed(10, 100,
+ [pIDataObject] { return OleSetClipboard(pIDataObject); });
+}
+
+HRESULT CMtaOleClipboard::onGetClipboard( LPSTREAM* ppStream )
+{
+ OSL_ASSERT(nullptr != ppStream);
+
+ IDataObjectPtr pIDataObject;
+
+ // forward the request to the OleClipboard
+ HRESULT hr
+ = sal::systools::RetryIfFailed(10, 100, [p = &pIDataObject] { return OleGetClipboard(p); });
+ if ( SUCCEEDED( hr ) )
+ {
+ hr = MarshalIDataObjectInStream(pIDataObject.get(), ppStream);
+ OSL_ENSURE(SUCCEEDED(hr), "marshalling clipboard data object failed");
+ }
+ return hr;
+}
+
+// flush the ole-clipboard
+
+HRESULT CMtaOleClipboard::onFlushClipboard( )
+{
+ return sal::systools::RetryIfFailed(10, 100, [] { return OleFlushClipboard(); });
+}
+
+// handle clipboard update event
+
+LRESULT CMtaOleClipboard::onClipboardUpdate()
+{
+ // we don't send a notification if we are
+ // registering ourself as clipboard
+ if ( !m_bInRegisterClipViewer )
+ {
+ std::unique_lock aGuard( m_ClipboardChangedEventCountMutex );
+
+ m_ClipboardChangedEventCount++;
+ SetEvent( m_hClipboardChangedEvent );
+ }
+
+ return 0;
+}
+
+// SendMessage so we don't need to supply the HWND if we send
+// something to our wrapped window
+
+LRESULT CMtaOleClipboard::sendMessage( UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ return ::SendMessageW( m_hwndMtaOleReqWnd, msg, wParam, lParam );
+}
+
+// PostMessage so we don't need to supply the HWND if we send
+// something to our wrapped window
+
+bool CMtaOleClipboard::postMessage( UINT msg, WPARAM wParam, LPARAM lParam )
+{
+ bool const ret = PostMessageW(m_hwndMtaOleReqWnd, msg, wParam, lParam);
+ SAL_WARN_IF(!ret, "vcl.win.dtrans", "ERROR: PostMessage() failed!");
+ return ret;
+}
+
+// the window proc
+
+LRESULT CALLBACK CMtaOleClipboard::mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+ LRESULT lResult = 0;
+
+ // get a connection to the class-instance via the static member
+ CMtaOleClipboard* pImpl = CMtaOleClipboard::s_theMtaOleClipboardInst;
+ OSL_ASSERT( nullptr != pImpl );
+
+ switch( uMsg )
+ {
+ case MSG_SETCLIPBOARD:
+ {
+ IDataObject* pIDataObject = reinterpret_cast< IDataObject* >( wParam );
+ CMtaOleClipboard::onSetClipboard( pIDataObject );
+
+ // in setClipboard we did acquire the
+ // interface pointer in order to prevent
+ // destruction of the object before the
+ // ole clipboard can acquire the interface
+ // now we release the interface so that
+ // our lostOwnership mechanism works
+ // remember: pIDataObject may be NULL
+ if ( pIDataObject )
+ pIDataObject->Release( );
+ }
+ break;
+
+ case MSG_GETCLIPBOARD:
+ {
+ MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
+ OSL_ASSERT( aMsgCtx );
+
+ aMsgCtx->hr = CMtaOleClipboard::onGetClipboard( reinterpret_cast< LPSTREAM* >(wParam) );
+ aMsgCtx->aCondition.set( );
+ }
+ break;
+
+ case MSG_FLUSHCLIPBOARD:
+ {
+ MsgCtx* aMsgCtx = reinterpret_cast< MsgCtx* >( lParam );
+ OSL_ASSERT( aMsgCtx );
+
+ aMsgCtx->hr = CMtaOleClipboard::onFlushClipboard( );
+ aMsgCtx->aCondition.set( );
+ }
+ break;
+
+ case MSG_REGCLIPVIEWER:
+ {
+ MsgCtx* pMsgCtx = reinterpret_cast<MsgCtx*>(lParam);
+ SAL_WARN_IF(!pMsgCtx, "vcl.win.dtrans", "pMsgCtx is nullptr");
+
+ pImpl->onRegisterClipViewer(
+ reinterpret_cast<CMtaOleClipboard::LPFNC_CLIPVIEWER_CALLBACK_t>(wParam));
+ pMsgCtx->aCondition.set();
+ }
+ break;
+
+ case WM_CLIPBOARDUPDATE:
+ lResult = pImpl->onClipboardUpdate();
+ break;
+
+ case MSG_SHUTDOWN:
+ DestroyWindow( pImpl->m_hwndMtaOleReqWnd );
+ break;
+
+ // force the sta thread to end
+ case WM_DESTROY:
+ SetEvent(pImpl->m_hEvtWndDisposed); // stop waiting for conditions set by this wndproc
+ PostQuitMessage( 0 );
+ break;
+
+ default:
+ lResult = DefWindowProcW( hWnd, uMsg, wParam, lParam );
+ break;
+ }
+
+ return lResult;
+}
+
+void CMtaOleClipboard::createMtaOleReqWnd( )
+{
+ WNDCLASSEXW wcex;
+
+ SalData* pSalData = GetSalData();
+ OSL_ASSERT(nullptr != pSalData->mhInst);
+
+ ZeroMemory( &wcex, sizeof(wcex) );
+
+ wcex.cbSize = sizeof(wcex);
+ wcex.style = 0;
+ wcex.lpfnWndProc = CMtaOleClipboard::mtaOleReqWndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = pSalData->mhInst;
+ wcex.hIcon = nullptr;
+ wcex.hCursor = nullptr;
+ wcex.hbrBackground = nullptr;
+ wcex.lpszMenuName = nullptr;
+ wcex.lpszClassName = g_szWndClsName;
+ wcex.hIconSm = nullptr;
+
+ m_MtaOleReqWndClassAtom = RegisterClassExW( &wcex );
+
+ if ( 0 != m_MtaOleReqWndClassAtom )
+ m_hwndMtaOleReqWnd = CreateWindowW(
+ g_szWndClsName, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, pSalData->mhInst, nullptr );
+}
+
+unsigned int CMtaOleClipboard::run( )
+{
+ HRESULT hr = OleInitialize( nullptr );
+ OSL_ASSERT( SUCCEEDED( hr ) );
+
+ createMtaOleReqWnd( );
+
+ unsigned int nRet = ~0U; // = error
+
+ if ( IsWindow( m_hwndMtaOleReqWnd ) )
+ {
+ if ( nullptr != m_hEvtThrdReady )
+ SetEvent( m_hEvtThrdReady );
+
+ nRet = 0;
+
+ // pumping messages
+ for (;;)
+ {
+ MSG msg;
+ int const bRet = GetMessageW(&msg, nullptr, 0, 0);
+ if (bRet == 0)
+ {
+ break;
+ }
+ if (-1 == bRet)
+ {
+ SAL_WARN("vcl.win.dtrans", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
+ nRet = ~0U;
+ break;
+ }
+ DispatchMessageW(&msg);
+ }
+ }
+
+ OleUninitialize( );
+
+ return nRet;
+}
+
+unsigned __stdcall CMtaOleClipboard::oleThreadProc( void* pParam )
+{
+ osl_setThreadName("CMtaOleClipboard::run()");
+
+ CMtaOleClipboard* pInst =
+ static_cast<CMtaOleClipboard*>( pParam );
+ OSL_ASSERT( nullptr != pInst );
+
+ return pInst->run( );
+}
+
+unsigned __stdcall CMtaOleClipboard::clipboardChangedNotifierThreadProc(void* pParam)
+{
+ osl_setThreadName("CMtaOleClipboard::clipboardChangedNotifierThreadProc()");
+ CMtaOleClipboard* pInst = static_cast< CMtaOleClipboard* >( pParam );
+ OSL_ASSERT( nullptr != pInst );
+
+ sal::systools::CoInitializeGuard aGuard(COINIT_APARTMENTTHREADED, false,
+ sal::systools::CoInitializeGuard::WhenFailed::NoThrow);
+
+ // assuming we don't need a lock for
+ // a boolean variable like m_bRun...
+ while ( pInst->m_bRunClipboardNotifierThread )
+ {
+ // process window messages because of CoInitializeEx
+ MSG Msg;
+ while (PeekMessageW(&Msg, nullptr, 0, 0, PM_REMOVE))
+ DispatchMessageW(&Msg);
+
+ // wait for clipboard changed or terminate event
+ MsgWaitForMultipleObjects(2, pInst->m_hClipboardChangedNotifierEvents, false, INFINITE,
+ QS_ALLINPUT | QS_ALLPOSTMESSAGE);
+
+ std::unique_lock aGuard2( pInst->m_ClipboardChangedEventCountMutex );
+
+ if ( pInst->m_ClipboardChangedEventCount > 0 )
+ {
+ pInst->m_ClipboardChangedEventCount--;
+ if ( 0 == pInst->m_ClipboardChangedEventCount )
+ ResetEvent( pInst->m_hClipboardChangedEvent );
+
+ aGuard2.unlock( );
+
+ // nobody should touch m_pfncClipViewerCallback while we do
+ std::unique_lock aClipViewerGuard( pInst->m_pfncClipViewerCallbackMutex );
+
+ // notify all clipboard listener
+ if ( pInst->m_pfncClipViewerCallback )
+ pInst->m_pfncClipViewerCallback( );
+ }
+ else
+ aGuard2.unlock( );
+ }
+
+ return 0;
+}
+
+bool CMtaOleClipboard::WaitForThreadReady( ) const
+{
+ bool bRet = false;
+
+ if ( nullptr != m_hEvtThrdReady )
+ {
+ DWORD dwResult = WaitForSingleObject(
+ m_hEvtThrdReady, MAX_WAITTIME );
+ bRet = ( dwResult == WAIT_OBJECT_0 );
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/MtaOleClipb.hxx b/vcl/win/dtrans/MtaOleClipb.hxx
new file mode 100644
index 0000000000..f76becb50c
--- /dev/null
+++ b/vcl/win/dtrans/MtaOleClipb.hxx
@@ -0,0 +1,110 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <mutex>
+
+#include <objidl.h>
+
+// the Mta-Ole clipboard class is for internal use only!
+// only one instance of this class should be created, the
+// user has to ensure this!
+// the class is not thread-safe because it will be used
+// only from within the clipboard service and the methods
+// of the clipboard service are already synchronized
+
+class CMtaOleClipboard
+{
+public:
+ typedef void ( WINAPI *LPFNC_CLIPVIEWER_CALLBACK_t )( void );
+
+public:
+ CMtaOleClipboard( );
+ ~CMtaOleClipboard( );
+
+ // clipboard functions
+ HRESULT setClipboard( IDataObject* pIDataObject );
+ HRESULT getClipboard( IDataObject** ppIDataObject );
+ HRESULT flushClipboard( );
+
+ // register/unregister a clipboard viewer; there can only
+ // be one at a time; parameter NULL means unregister
+ // a clipboard viewer
+ // returns true on success else false; use GetLastError( ) in
+ // false case
+ bool registerClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback );
+
+private:
+ unsigned int run( );
+
+ // create a hidden window which serves as a request target; so we
+ // guarantee synchronization
+ void createMtaOleReqWnd( );
+
+ // message support
+ bool postMessage( UINT msg, WPARAM wParam = 0, LPARAM lParam = 0 );
+ LRESULT sendMessage( UINT msg, WPARAM wParam = 0, LPARAM lParam = 0 );
+
+ // message handler functions; remember these functions are called
+ // from a different thread context!
+
+ static HRESULT onSetClipboard( IDataObject* pIDataObject );
+ static HRESULT onGetClipboard( LPSTREAM* ppStream );
+ static HRESULT onFlushClipboard( );
+ bool onRegisterClipViewer( LPFNC_CLIPVIEWER_CALLBACK_t pfncClipViewerCallback );
+
+ // win32 clipboard listener support
+ LRESULT onClipboardUpdate();
+
+ static LRESULT CALLBACK mtaOleReqWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
+ static unsigned __stdcall oleThreadProc(void* pParam);
+
+ static unsigned __stdcall clipboardChangedNotifierThreadProc(void* pParam);
+
+ bool WaitForThreadReady( ) const;
+
+private:
+ HANDLE m_hOleThread;
+ unsigned m_uOleThreadId;
+ HANDLE m_hEvtThrdReady;
+ HWND m_hwndMtaOleReqWnd;
+ HANDLE m_hEvtWndDisposed;
+ ATOM m_MtaOleReqWndClassAtom;
+ LPFNC_CLIPVIEWER_CALLBACK_t m_pfncClipViewerCallback;
+ bool m_bInRegisterClipViewer;
+
+ bool m_bRunClipboardNotifierThread;
+ HANDLE m_hClipboardChangedNotifierThread;
+ HANDLE m_hClipboardChangedNotifierEvents[2];
+ HANDLE& m_hClipboardChangedEvent;
+ HANDLE& m_hTerminateClipboardChangedNotifierEvent;
+ std::mutex m_ClipboardChangedEventCountMutex;
+ sal_Int32 m_ClipboardChangedEventCount;
+
+ std::mutex m_pfncClipViewerCallbackMutex;
+
+ static CMtaOleClipboard* s_theMtaOleClipboardInst;
+
+ CMtaOleClipboard( const CMtaOleClipboard& ) = delete;
+ CMtaOleClipboard& operator=( const CMtaOleClipboard& ) = delete;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/TxtCnvtHlp.cxx b/vcl/win/dtrans/TxtCnvtHlp.cxx
new file mode 100644
index 0000000000..d7ab386fc1
--- /dev/null
+++ b/vcl/win/dtrans/TxtCnvtHlp.cxx
@@ -0,0 +1,125 @@
+/* -*- 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 <osl/diagnose.h>
+
+#include "TxtCnvtHlp.hxx"
+#include "DTransHelper.hxx"
+#include "ImplHelper.hxx"
+
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::uno;
+
+// assuming a '\0' terminated string if no length specified
+
+static int CalcBuffSizeForTextConversion( UINT code_page, LPCSTR lpMultiByteString, int nLen = -1 )
+{
+ return ( MultiByteToWideChar( code_page,
+ 0,
+ lpMultiByteString,
+ nLen,
+ nullptr,
+ 0 ) * sizeof( sal_Unicode ) );
+}
+
+// assuming a '\0' terminated string if no length specified
+
+static int CalcBuffSizeForTextConversion( UINT code_page, LPCWSTR lpWideCharString, int nLen = -1 )
+{
+ return WideCharToMultiByte( code_page,
+ 0,
+ lpWideCharString,
+ nLen,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr );
+}
+
+// converts text in one code page into unicode text
+// automatically calculates the necessary buffer size and allocates
+// the buffer
+
+int MultiByteToWideCharEx( UINT cp_src,
+ LPCSTR lpMultiByteString,
+ int lenStr,
+ CStgTransferHelper& refDTransHelper,
+ BOOL bEnsureTrailingZero )
+{
+ OSL_ASSERT( IsValidCodePage( cp_src ) );
+ OSL_ASSERT( nullptr != lpMultiByteString );
+
+ // calculate the required buff size
+ int reqSize = CalcBuffSizeForTextConversion( cp_src, lpMultiByteString, lenStr );
+
+ if ( bEnsureTrailingZero )
+ reqSize += sizeof( sal_Unicode );
+
+ // initialize the data-transfer helper
+ refDTransHelper.init( reqSize );
+
+ // setup a global memory pointer
+ CRawHGlobalPtr ptrHGlob( refDTransHelper );
+
+ // do the conversion and return
+ return MultiByteToWideChar( cp_src,
+ 0,
+ lpMultiByteString,
+ lenStr,
+ static_cast< LPWSTR >( ptrHGlob.GetMemPtr( ) ),
+ ptrHGlob.MemSize( ) );
+}
+
+// converts unicode text into text of the specified code page
+// automatically calculates the necessary buffer size and allocates
+// the buffer
+
+int WideCharToMultiByteEx( UINT cp_dest,
+ LPCWSTR lpWideCharString,
+ int lenStr,
+ CStgTransferHelper& refDTransHelper,
+ BOOL bEnsureTrailingZero )
+{
+ OSL_ASSERT( IsValidCodePage( cp_dest ) );
+ OSL_ASSERT( nullptr != lpWideCharString );
+
+ // calculate the required buff size
+ int reqSize = CalcBuffSizeForTextConversion( cp_dest, lpWideCharString, lenStr );
+
+ if ( bEnsureTrailingZero )
+ reqSize += sizeof( sal_Int8 );
+
+ // initialize the data-transfer helper
+ refDTransHelper.init( reqSize );
+
+ // setup a global memory pointer
+ CRawHGlobalPtr ptrHGlob( refDTransHelper );
+
+ // do the conversion and return
+ return WideCharToMultiByte( cp_dest,
+ 0,
+ lpWideCharString,
+ lenStr,
+ static_cast< LPSTR >( ptrHGlob.GetMemPtr( ) ),
+ ptrHGlob.MemSize( ),
+ nullptr,
+ nullptr );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/TxtCnvtHlp.hxx b/vcl/win/dtrans/TxtCnvtHlp.hxx
new file mode 100644
index 0000000000..5294879337
--- /dev/null
+++ b/vcl/win/dtrans/TxtCnvtHlp.hxx
@@ -0,0 +1,41 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/datatransfer/DataFlavor.hpp>
+
+#include "DTransHelper.hxx"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+int MultiByteToWideCharEx( UINT cp_src,
+ LPCSTR lpMultiByteString,
+ int lenStr,
+ CStgTransferHelper& refDTransHelper,
+ BOOL bEnsureTrailingZero = TRUE );
+
+int WideCharToMultiByteEx( UINT cp_dest,
+ LPCWSTR lpWideCharString,
+ int lenStr,
+ CStgTransferHelper& refDTransHelper,
+ BOOL bEnsureTrailingZero = TRUE );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/WinClip.hxx b/vcl/win/dtrans/WinClip.hxx
new file mode 100644
index 0000000000..f90d5eea06
--- /dev/null
+++ b/vcl/win/dtrans/WinClip.hxx
@@ -0,0 +1,26 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+const sal_Int32 CF_INVALID = 0;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/WinClipboard.cxx b/vcl/win/dtrans/WinClipboard.cxx
new file mode 100644
index 0000000000..1a8eaea151
--- /dev/null
+++ b/vcl/win/dtrans/WinClipboard.cxx
@@ -0,0 +1,383 @@
+/* -*- 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 <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <com/sun/star/datatransfer/clipboard/ClipboardEvent.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <vcl/svapp.hxx>
+#include <svdata.hxx>
+#include <salinst.hxx>
+
+#include <com/sun/star/datatransfer/clipboard/RenderingCapabilities.hpp>
+#include "XNotifyingDataObject.hxx"
+
+#include <systools/win32/comtools.hxx>
+#include "DtObjFactory.hxx"
+#include "APNDataObject.hxx"
+#include "DOTransferable.hxx"
+#include "WinClipboard.hxx"
+
+#if !defined WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <ole2.h>
+#include <objidl.h>
+
+using namespace com::sun::star;
+
+namespace
+{
+CWinClipboard* s_pCWinClipbImpl = nullptr;
+osl::Mutex s_aClipboardSingletonMutex;
+}
+
+/*XEventListener,*/
+CWinClipboard::CWinClipboard(const uno::Reference<uno::XComponentContext>& rxContext,
+ const OUString& aClipboardName)
+ : m_xContext(rxContext)
+ , m_itsName(aClipboardName)
+ , m_pCurrentClipContent(nullptr)
+{
+ // necessary to reassociate from
+ // the static callback function
+ {
+ osl::MutexGuard aGuard(s_aClipboardSingletonMutex);
+ s_pCWinClipbImpl = this;
+ }
+
+ registerClipboardViewer();
+}
+
+CWinClipboard::~CWinClipboard()
+{
+ {
+ osl::MutexGuard aGuard(s_aClipboardSingletonMutex);
+ s_pCWinClipbImpl = nullptr;
+ }
+
+ unregisterClipboardViewer();
+}
+
+void CWinClipboard::disposing(std::unique_lock<std::mutex>& mutex)
+{
+ {
+ osl::MutexGuard aGuard(s_aClipboardSingletonMutex);
+ s_pCWinClipbImpl = nullptr;
+ }
+
+ unregisterClipboardViewer();
+
+ WeakComponentImplHelper::disposing(mutex);
+}
+
+// XClipboard
+
+// to avoid unnecessary traffic we check first if there is a clipboard
+// content which was set via setContent, in this case we don't need
+// to query the content from the clipboard, create a new wrapper object
+// and so on, we simply return the original XTransferable instead of our
+// DOTransferable
+
+uno::Reference<datatransfer::XTransferable> SAL_CALL CWinClipboard::getContents()
+{
+ osl::MutexGuard aGuard(m_aContentMutex);
+
+ if (m_bDisposed)
+ throw lang::DisposedException("object is already disposed",
+ static_cast<XClipboardEx*>(this));
+
+ // use the shortcut or create a transferable from
+ // system clipboard
+ {
+ osl::MutexGuard aGuard2(m_aContentCacheMutex);
+
+ if (nullptr != m_pCurrentClipContent)
+ return m_pCurrentClipContent->m_XTransferable;
+
+ // Content cached?
+ if (m_foreignContent.is())
+ return m_foreignContent;
+
+ // release the mutex, so that the variable may be
+ // changed by other threads
+ }
+
+ uno::Reference<datatransfer::XTransferable> rClipContent;
+
+ // get the current format list from clipboard
+ if (UINT nFormats; !GetUpdatedClipboardFormats(nullptr, 0, &nFormats)
+ && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ std::vector<UINT> aUINTFormats(nFormats);
+ if (GetUpdatedClipboardFormats(aUINTFormats.data(), nFormats, &nFormats))
+ {
+ std::vector<sal_uInt32> aFormats(aUINTFormats.begin(), aUINTFormats.end());
+ rClipContent = new CDOTransferable(m_xContext, this, aFormats);
+
+ osl::MutexGuard aGuard2(m_aContentCacheMutex);
+ m_foreignContent = rClipContent;
+ }
+ }
+
+ return rClipContent;
+}
+
+IDataObjectPtr CWinClipboard::getIDataObject()
+{
+ osl::MutexGuard aGuard(m_aContentMutex);
+
+ if (m_bDisposed)
+ throw lang::DisposedException("object is already disposed",
+ static_cast<XClipboardEx*>(this));
+
+ // get the current dataobject from clipboard
+ IDataObjectPtr pIDataObject;
+ HRESULT hr = m_MtaOleClipboard.getClipboard(&pIDataObject);
+
+ if (SUCCEEDED(hr))
+ {
+ // create an apartment neutral dataobject and initialize it with a
+ // com smart pointer to the IDataObject from clipboard
+ pIDataObject = new CAPNDataObject(pIDataObject);
+ }
+
+ return pIDataObject;
+}
+
+void SAL_CALL CWinClipboard::setContents(
+ const uno::Reference<datatransfer::XTransferable>& xTransferable,
+ const uno::Reference<datatransfer::clipboard::XClipboardOwner>& xClipboardOwner)
+{
+ osl::MutexGuard aGuard(m_aContentMutex);
+
+ if (m_bDisposed)
+ throw lang::DisposedException("object is already disposed",
+ static_cast<XClipboardEx*>(this));
+
+ IDataObjectPtr pIDataObj;
+
+ if (xTransferable.is())
+ {
+ {
+ osl::MutexGuard aGuard2(m_aContentCacheMutex);
+
+ m_foreignContent.clear();
+
+ m_pCurrentClipContent = new CXNotifyingDataObject(
+ CDTransObjFactory::createDataObjFromTransferable(m_xContext, xTransferable),
+ xTransferable, xClipboardOwner, this);
+ }
+
+ pIDataObj = IDataObjectPtr(m_pCurrentClipContent);
+ }
+
+ m_MtaOleClipboard.setClipboard(pIDataObj.get());
+}
+
+OUString SAL_CALL CWinClipboard::getName()
+{
+ if (m_bDisposed)
+ throw lang::DisposedException("object is already disposed",
+ static_cast<XClipboardEx*>(this));
+
+ return m_itsName;
+}
+
+// XFlushableClipboard
+
+void SAL_CALL CWinClipboard::flushClipboard()
+{
+ osl::MutexGuard aGuard(m_aContentMutex);
+
+ if (m_bDisposed)
+ throw lang::DisposedException("object is already disposed",
+ static_cast<XClipboardEx*>(this));
+
+ // actually it should be ClearableMutexGuard aGuard( m_aContentCacheMutex );
+ // but it does not work since FlushClipboard does a callback and frees DataObject
+ // which results in a deadlock in onReleaseDataObject.
+ // FlushClipboard had to be synchron in order to prevent shutdown until all
+ // clipboard-formats are rendered.
+ // The request is needed to prevent flushing if we are not clipboard owner (it is
+ // not known what happens if we flush but aren't clipboard owner).
+ // It may be possible to move the request to the clipboard STA thread by saving the
+ // DataObject and call OleIsCurrentClipboard before flushing.
+
+ if (nullptr != m_pCurrentClipContent)
+ m_MtaOleClipboard.flushClipboard();
+}
+
+// XClipboardEx
+
+sal_Int8 SAL_CALL CWinClipboard::getRenderingCapabilities()
+{
+ if (m_bDisposed)
+ throw lang::DisposedException("object is already disposed",
+ static_cast<XClipboardEx*>(this));
+
+ using namespace datatransfer::clipboard::RenderingCapabilities;
+ return (Delayed | Persistent);
+}
+
+// XClipboardNotifier
+
+void SAL_CALL CWinClipboard::addClipboardListener(
+ const uno::Reference<datatransfer::clipboard::XClipboardListener>& listener)
+{
+ if (m_bDisposed)
+ throw lang::DisposedException("object is already disposed",
+ static_cast<XClipboardEx*>(this));
+
+ // check input parameter
+ if (!listener.is())
+ throw lang::IllegalArgumentException("empty reference", static_cast<XClipboardEx*>(this),
+ 1);
+
+ std::unique_lock aGuard(m_aMutex);
+ maClipboardListeners.addInterface(aGuard, listener);
+}
+
+void SAL_CALL CWinClipboard::removeClipboardListener(
+ const uno::Reference<datatransfer::clipboard::XClipboardListener>& listener)
+{
+ if (m_bDisposed)
+ throw lang::DisposedException("object is already disposed",
+ static_cast<XClipboardEx*>(this));
+
+ // check input parameter
+ if (!listener.is())
+ throw lang::IllegalArgumentException("empty reference", static_cast<XClipboardEx*>(this),
+ 1);
+
+ std::unique_lock aGuard(m_aMutex);
+ maClipboardListeners.removeInterface(aGuard, listener);
+}
+
+void CWinClipboard::notifyAllClipboardListener()
+{
+ if (m_bDisposed)
+ return;
+
+ std::unique_lock aGuard(m_aMutex);
+ if (m_bDisposed)
+ return;
+
+ if (!maClipboardListeners.getLength(aGuard))
+ return;
+
+ try
+ {
+ uno::Reference<datatransfer::XTransferable> rXTransf(getContents());
+ datatransfer::clipboard::ClipboardEvent aClipbEvent(static_cast<XClipboard*>(this),
+ rXTransf);
+ maClipboardListeners.notifyEach(
+ aGuard, &datatransfer::clipboard::XClipboardListener::changedContents, aClipbEvent);
+ }
+ catch (const lang::DisposedException&)
+ {
+ OSL_FAIL("Service Manager disposed");
+
+ aGuard.unlock();
+ // no further clipboard changed notifications
+ unregisterClipboardViewer();
+ }
+}
+
+// XServiceInfo
+
+OUString SAL_CALL CWinClipboard::getImplementationName()
+{
+ return "com.sun.star.datatransfer.clipboard.ClipboardW32";
+}
+
+sal_Bool SAL_CALL CWinClipboard::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL CWinClipboard::getSupportedServiceNames()
+{
+ return { "com.sun.star.datatransfer.clipboard.SystemClipboard" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+dtrans_CWinClipboard_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const& args)
+{
+ // We run unit tests in parallel, which is a problem when touching a shared resource
+ // like the system clipboard, so rather use the dummy GenericClipboard.
+ static const bool bRunningUnitTest = getenv("LO_TESTNAME");
+
+ if (bRunningUnitTest)
+ {
+ SolarMutexGuard aGuard;
+ auto xClipboard = ImplGetSVData()->mpDefInst->CreateClipboard(args);
+ if (xClipboard.is())
+ xClipboard->acquire();
+ return xClipboard.get();
+ }
+ else
+ {
+ return cppu::acquire(new CWinClipboard(context, ""));
+ }
+}
+
+void CWinClipboard::onReleaseDataObject(CXNotifyingDataObject* theCaller)
+{
+ OSL_ASSERT(nullptr != theCaller);
+
+ if (theCaller)
+ theCaller->lostOwnership();
+
+ // if the current caller is the one we currently hold, then set it to NULL
+ // because an external source must be the clipboardowner now
+ osl::MutexGuard aGuard(m_aContentCacheMutex);
+
+ if (m_pCurrentClipContent == theCaller)
+ m_pCurrentClipContent = nullptr;
+}
+
+void CWinClipboard::registerClipboardViewer()
+{
+ m_MtaOleClipboard.registerClipViewer(CWinClipboard::onClipboardContentChanged);
+}
+
+void CWinClipboard::unregisterClipboardViewer() { m_MtaOleClipboard.registerClipViewer(nullptr); }
+
+void WINAPI CWinClipboard::onClipboardContentChanged()
+{
+ osl::MutexGuard aGuard(s_aClipboardSingletonMutex);
+
+ // reassociation to instance through static member
+ if (nullptr != s_pCWinClipbImpl)
+ {
+ s_pCWinClipbImpl->m_foreignContent.clear();
+ s_pCWinClipbImpl->notifyAllClipboardListener();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/WinClipboard.hxx b/vcl/win/dtrans/WinClipboard.hxx
new file mode 100644
index 0000000000..fbaa1b2062
--- /dev/null
+++ b/vcl/win/dtrans/WinClipboard.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <comphelper/compbase.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardListener.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
+#include <com/sun/star/datatransfer/clipboard/XSystemClipboard.hpp>
+#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <osl/conditn.hxx>
+#include <systools/win32/comtools.hxx>
+
+#include "MtaOleClipb.hxx"
+#include "XNotifyingDataObject.hxx"
+
+// implements the XClipboard[Ex] ... interfaces
+// for the clipboard viewer mechanism we need a static callback function
+// and a static member to reassociate from this static function to the
+// class instance
+// watch out: we are using only one static member variable and not a list
+// because we assume to be instantiated only once
+// this will be assured by a OneInstanceFactory of the service and not
+// by this class!
+
+class CWinClipboard final
+ : public comphelper::WeakComponentImplHelper<css::datatransfer::clipboard::XSystemClipboard,
+ css::datatransfer::clipboard::XFlushableClipboard,
+ css::lang::XServiceInfo>
+{
+ friend STDMETHODIMP_(ULONG) CXNotifyingDataObject::Release();
+
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ const OUString m_itsName;
+ CMtaOleClipboard m_MtaOleClipboard;
+ CXNotifyingDataObject* m_pCurrentClipContent;
+ com::sun::star::uno::Reference<com::sun::star::datatransfer::XTransferable> m_foreignContent;
+ osl::Mutex m_aContentMutex;
+ osl::Mutex m_aContentCacheMutex;
+ comphelper::OInterfaceContainerHelper4<css::datatransfer::clipboard::XClipboardListener>
+ maClipboardListeners;
+
+ void notifyAllClipboardListener();
+ void onReleaseDataObject(CXNotifyingDataObject* theCaller);
+
+ void registerClipboardViewer();
+ void unregisterClipboardViewer();
+
+ static void WINAPI onClipboardContentChanged();
+
+public:
+ CWinClipboard(const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const OUString& aClipboardName);
+ virtual ~CWinClipboard() override;
+
+ // XClipboard
+ virtual css::uno::Reference<css::datatransfer::XTransferable> SAL_CALL getContents() override;
+ virtual void SAL_CALL setContents(
+ const css::uno::Reference<css::datatransfer::XTransferable>& xTransferable,
+ const css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner>& xClipboardOwner)
+ override;
+ virtual OUString SAL_CALL getName() override;
+
+ // XFlushableClipboard
+ virtual void SAL_CALL flushClipboard() override;
+
+ // XClipboardEx
+ virtual sal_Int8 SAL_CALL getRenderingCapabilities() override;
+
+ // XClipboardNotifier
+ virtual void SAL_CALL addClipboardListener(
+ const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener)
+ override;
+ virtual void SAL_CALL removeClipboardListener(
+ const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener)
+ override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ IDataObjectPtr getIDataObject();
+
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/XNotifyingDataObject.cxx b/vcl/win/dtrans/XNotifyingDataObject.cxx
new file mode 100644
index 0000000000..aab168f043
--- /dev/null
+++ b/vcl/win/dtrans/XNotifyingDataObject.cxx
@@ -0,0 +1,149 @@
+/* -*- 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 <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+#include "XNotifyingDataObject.hxx"
+#include "WinClipboard.hxx"
+
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::datatransfer::clipboard;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::Reference;
+
+CXNotifyingDataObject::CXNotifyingDataObject(
+ const IDataObjectPtr& aIDataObject,
+ const Reference< XTransferable >& aXTransferable,
+ const Reference< XClipboardOwner >& aXClipOwner,
+ CWinClipboard* const theWinClipoard) :
+ m_nRefCnt( 0 ),
+ m_aIDataObject( aIDataObject ),
+ m_XTransferable( aXTransferable ),
+ m_XClipboardOwner( aXClipOwner ),
+ m_pWinClipImpl( theWinClipoard )
+{
+}
+
+STDMETHODIMP CXNotifyingDataObject::QueryInterface( REFIID iid, void** ppvObject )
+{
+ if ( nullptr == ppvObject )
+ return E_INVALIDARG;
+
+ HRESULT hr = E_NOINTERFACE;
+
+ *ppvObject = nullptr;
+ if ( ( __uuidof( IUnknown ) == iid ) ||
+ ( __uuidof( IDataObject ) == iid ) )
+ {
+ *ppvObject = static_cast< IUnknown* >( this );
+ static_cast<LPUNKNOWN>(*ppvObject)->AddRef( );
+ hr = S_OK;
+ }
+
+ return hr;
+}
+
+STDMETHODIMP_(ULONG) CXNotifyingDataObject::AddRef( )
+{
+ return static_cast< ULONG >( InterlockedIncrement( &m_nRefCnt ) );
+}
+
+STDMETHODIMP_(ULONG) CXNotifyingDataObject::Release( )
+{
+ ULONG nRefCnt =
+ static_cast< ULONG >( InterlockedDecrement( &m_nRefCnt ) );
+
+ if ( 0 == nRefCnt )
+ {
+ if ( m_pWinClipImpl )
+ m_pWinClipImpl->onReleaseDataObject( this );
+
+ delete this;
+ }
+
+ return nRefCnt;
+}
+
+STDMETHODIMP CXNotifyingDataObject::GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium )
+{
+ return m_aIDataObject->GetData(pFormatetc, pmedium);
+}
+
+STDMETHODIMP CXNotifyingDataObject::EnumFormatEtc(
+ DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc )
+{
+ return m_aIDataObject->EnumFormatEtc(dwDirection, ppenumFormatetc);
+}
+
+STDMETHODIMP CXNotifyingDataObject::QueryGetData( FORMATETC * pFormatetc )
+{
+ return m_aIDataObject->QueryGetData(pFormatetc);
+}
+
+STDMETHODIMP CXNotifyingDataObject::GetDataHere( FORMATETC * lpFetc, STGMEDIUM * lpStgMedium )
+{
+ return m_aIDataObject->GetDataHere(lpFetc, lpStgMedium);
+}
+
+STDMETHODIMP CXNotifyingDataObject::GetCanonicalFormatEtc( FORMATETC * lpFetc, FORMATETC * lpCanonicalFetc )
+{
+ return m_aIDataObject->GetCanonicalFormatEtc(lpFetc, lpCanonicalFetc);
+}
+
+STDMETHODIMP CXNotifyingDataObject::SetData( FORMATETC * lpFetc, STGMEDIUM * lpStgMedium, BOOL bRelease )
+{
+ return m_aIDataObject->SetData( lpFetc, lpStgMedium, bRelease );
+}
+
+STDMETHODIMP CXNotifyingDataObject::DAdvise(
+ FORMATETC * lpFetc, DWORD advf, IAdviseSink * lpAdvSink, DWORD* pdwConnection )
+{
+ return m_aIDataObject->DAdvise( lpFetc, advf, lpAdvSink, pdwConnection );
+}
+
+STDMETHODIMP CXNotifyingDataObject::DUnadvise( DWORD dwConnection )
+{
+ return m_aIDataObject->DUnadvise( dwConnection );
+}
+
+STDMETHODIMP CXNotifyingDataObject::EnumDAdvise( IEnumSTATDATA ** ppenumAdvise )
+{
+ return m_aIDataObject->EnumDAdvise( ppenumAdvise );
+}
+
+CXNotifyingDataObject::operator IDataObject*( )
+{
+ return static_cast< IDataObject* >( this );
+}
+
+void CXNotifyingDataObject::lostOwnership( )
+{
+ try
+ {
+ if (m_XClipboardOwner.is())
+ m_XClipboardOwner->lostOwnership(
+ static_cast<XClipboardEx*>(m_pWinClipImpl), m_XTransferable);
+ }
+ catch(RuntimeException&)
+ {
+ TOOLS_WARN_EXCEPTION( "vcl", "" );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/XNotifyingDataObject.hxx b/vcl/win/dtrans/XNotifyingDataObject.hxx
new file mode 100644
index 0000000000..408413a5de
--- /dev/null
+++ b/vcl/win/dtrans/XNotifyingDataObject.hxx
@@ -0,0 +1,84 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <objidl.h>
+
+#include <systools/win32/comtools.hxx>
+
+/*--------------------------------------------------------------------------
+ To implement the lostOwnership mechanism cleanly we need this wrapper
+ object
+----------------------------------------------------------------------------*/
+
+// forward
+class CWinClipboard;
+
+class CXNotifyingDataObject final : public IDataObject
+{
+public:
+ CXNotifyingDataObject(
+ const IDataObjectPtr& aIDataObject,
+ const css::uno::Reference< css::datatransfer::XTransferable >& aXTransferable,
+ const css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner >& aXClipOwner,
+ CWinClipboard* const theWinClipoard);
+
+ virtual ~CXNotifyingDataObject() {}
+
+ // ole interface implementation
+
+ //IUnknown interface methods
+ STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override;
+ STDMETHODIMP_( ULONG ) AddRef( ) override;
+ STDMETHODIMP_( ULONG ) Release( ) override;
+
+ // IDataObject interface methods
+ STDMETHODIMP GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override;
+ STDMETHODIMP GetDataHere( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override;
+ STDMETHODIMP QueryGetData( FORMATETC * pFormatetc ) override;
+ STDMETHODIMP GetCanonicalFormatEtc( FORMATETC * pFormatectIn, FORMATETC * pFormatetcOut ) override;
+ STDMETHODIMP SetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease ) override;
+ STDMETHODIMP EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc ) override;
+ STDMETHODIMP DAdvise( FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD* pdwConnection ) override;
+ STDMETHODIMP DUnadvise( DWORD dwConnection ) override;
+ STDMETHODIMP EnumDAdvise( IEnumSTATDATA** ppenumAdvise ) override;
+
+ operator IDataObject*( );
+
+private:
+ void lostOwnership( );
+
+ sal_Int32 m_nRefCnt;
+ IDataObjectPtr m_aIDataObject;
+ const css::uno::Reference< css::datatransfer::XTransferable > m_XTransferable;
+ const css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner > m_XClipboardOwner;
+ CWinClipboard* const m_pWinClipImpl;
+
+ friend class CWinClipboard;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/XTDataObject.cxx b/vcl/win/dtrans/XTDataObject.cxx
new file mode 100644
index 0000000000..b3f5ccb303
--- /dev/null
+++ b/vcl/win/dtrans/XTDataObject.cxx
@@ -0,0 +1,751 @@
+/* -*- 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 <osl/diagnose.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/safeint.hxx>
+
+#include "XTDataObject.hxx"
+#include <com/sun/star/datatransfer/DataFlavor.hpp>
+#include "ImplHelper.hxx"
+#include "DTransHelper.hxx"
+#include "TxtCnvtHlp.hxx"
+#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp>
+#include <com/sun/star/awt/AsyncCallback.hpp>
+#include <com/sun/star/awt/XCallback.hpp>
+#include "FmtFilter.hxx"
+#include <cppuhelper/implbase.hxx>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <shlobj.h>
+
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::datatransfer::clipboard;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+
+namespace {
+
+void setupStgMedium( const FORMATETC& fetc,
+ CStgTransferHelper& stgTransHlp,
+ STGMEDIUM& stgmedium )
+{
+ stgmedium.pUnkForRelease = nullptr;
+
+ if ( fetc.cfFormat == CF_METAFILEPICT )
+ {
+ stgmedium.tymed = TYMED_MFPICT;
+ stgmedium.hMetaFilePict = static_cast< HMETAFILEPICT >( stgTransHlp.getHGlobal( ) );
+ }
+ else if ( fetc.cfFormat == CF_ENHMETAFILE )
+ {
+ stgmedium.tymed = TYMED_ENHMF;
+ stgmedium.hEnhMetaFile = static_cast< HENHMETAFILE >( stgTransHlp.getHGlobal( ) );
+ }
+ else if ( fetc.tymed & TYMED_HGLOBAL )
+ {
+ stgmedium.tymed = TYMED_HGLOBAL;
+ stgmedium.hGlobal = stgTransHlp.getHGlobal( );
+ }
+ else if ( fetc.tymed & TYMED_ISTREAM )
+ {
+ stgmedium.tymed = TYMED_ISTREAM;
+ stgTransHlp.getIStream( &stgmedium.pstm );
+ }
+ else
+ OSL_ASSERT( false );
+}
+
+/**
+ We need to destroy XTransferable in the main thread to avoid dead lock
+ when locking in the clipboard thread. So we transfer the ownership of the
+ XTransferable reference to this object and release it when the callback
+ is executed in main thread.
+*/
+class AsyncDereference : public cppu::WeakImplHelper<css::awt::XCallback>
+{
+ Reference<XTransferable> maTransferable;
+
+public:
+ AsyncDereference(css::uno::Reference<css::datatransfer::XTransferable> const & rTransferable)
+ : maTransferable(rTransferable)
+ {}
+
+ virtual void SAL_CALL notify(css::uno::Any const &) override
+ {
+ maTransferable.clear();
+ }
+};
+
+// a helper class that will be thrown by the function validateFormatEtc
+
+class CInvalidFormatEtcException
+{
+public:
+ HRESULT m_hr;
+ explicit CInvalidFormatEtcException( HRESULT hr ) : m_hr( hr ) {};
+};
+
+void validateFormatEtc( LPFORMATETC lpFormatEtc )
+{
+ OSL_ASSERT( lpFormatEtc );
+
+ if ( lpFormatEtc->lindex != -1 )
+ throw CInvalidFormatEtcException( DV_E_LINDEX );
+
+ if ( !(lpFormatEtc->dwAspect & DVASPECT_CONTENT) &&
+ !(lpFormatEtc->dwAspect & DVASPECT_SHORTNAME) )
+ throw CInvalidFormatEtcException( DV_E_DVASPECT );
+
+ if ( !(lpFormatEtc->tymed & TYMED_HGLOBAL) &&
+ !(lpFormatEtc->tymed & TYMED_ISTREAM) &&
+ !(lpFormatEtc->tymed & TYMED_MFPICT) &&
+ !(lpFormatEtc->tymed & TYMED_ENHMF) )
+ throw CInvalidFormatEtcException( DV_E_TYMED );
+
+ if ( lpFormatEtc->cfFormat == CF_METAFILEPICT &&
+ !(lpFormatEtc->tymed & TYMED_MFPICT) )
+ throw CInvalidFormatEtcException( DV_E_TYMED );
+
+ if ( lpFormatEtc->cfFormat == CF_ENHMETAFILE &&
+ !(lpFormatEtc->tymed & TYMED_ENHMF) )
+ throw CInvalidFormatEtcException( DV_E_TYMED );
+}
+
+void invalidateStgMedium( STGMEDIUM& stgmedium )
+{
+ stgmedium.tymed = TYMED_NULL;
+}
+
+HRESULT translateStgExceptionCode( HRESULT hr )
+{
+ HRESULT hrTransl;
+
+ switch( hr )
+ {
+ case STG_E_MEDIUMFULL:
+ hrTransl = hr;
+ break;
+
+ default:
+ hrTransl = E_UNEXPECTED;
+ break;
+ }
+
+ return hrTransl;
+}
+
+// inline
+void renderDataAndSetupStgMedium(
+ const sal_Int8* lpStorage, const FORMATETC& fetc, sal_uInt32 nInitStgSize,
+ sal_uInt32 nBytesToTransfer, STGMEDIUM& stgmedium )
+{
+ OSL_PRECOND( !nInitStgSize || (nInitStgSize >= nBytesToTransfer),
+ "Memory size less than number of bytes to transfer" );
+
+ CStgTransferHelper stgTransfHelper( AUTO_INIT );
+
+ // setup storage size
+ if ( nInitStgSize > 0 )
+ stgTransfHelper.init( nInitStgSize );
+
+#if OSL_DEBUG_LEVEL > 0
+ sal_uInt32 nBytesWritten = 0;
+ stgTransfHelper.write( lpStorage, nBytesToTransfer, &nBytesWritten );
+ OSL_ASSERT( nBytesWritten == nBytesToTransfer );
+#else
+ stgTransfHelper.write( lpStorage, nBytesToTransfer );
+#endif
+
+ setupStgMedium( fetc, stgTransfHelper, stgmedium );
+}
+
+}
+
+CXTDataObject::CXTDataObject( const Reference< XComponentContext >& rxContext,
+ const Reference< XTransferable >& aXTransferable )
+ : m_nRefCnt( 0 )
+ , m_XTransferable( aXTransferable )
+ , m_XComponentContext( rxContext )
+ , m_bFormatEtcContainerInitialized( false )
+ , m_DataFormatTranslator( rxContext )
+ , m_FormatRegistrar( rxContext, m_DataFormatTranslator )
+{
+}
+
+CXTDataObject::~CXTDataObject()
+{
+ css::awt::AsyncCallback::create(m_XComponentContext)->addCallback(
+ new AsyncDereference(m_XTransferable),
+ css::uno::Any());
+}
+
+// IUnknown->QueryInterface
+
+STDMETHODIMP CXTDataObject::QueryInterface( REFIID iid, void** ppvObject )
+{
+ if ( nullptr == ppvObject )
+ return E_INVALIDARG;
+
+ HRESULT hr = E_NOINTERFACE;
+
+ *ppvObject = nullptr;
+ if ( ( __uuidof( IUnknown ) == iid ) ||
+ ( __uuidof( IDataObject ) == iid ) )
+ {
+ *ppvObject = static_cast< IUnknown* >( this );
+ static_cast<LPUNKNOWN>(*ppvObject)->AddRef( );
+ hr = S_OK;
+ }
+
+ return hr;
+}
+
+// IUnknown->AddRef
+
+STDMETHODIMP_(ULONG) CXTDataObject::AddRef( )
+{
+ return static_cast< ULONG >( InterlockedIncrement( &m_nRefCnt ) );
+}
+
+// IUnknown->Release
+
+STDMETHODIMP_(ULONG) CXTDataObject::Release( )
+{
+ ULONG nRefCnt =
+ static_cast< ULONG >( InterlockedDecrement( &m_nRefCnt ) );
+
+ if ( 0 == nRefCnt )
+ delete this;
+
+ return nRefCnt;
+}
+
+STDMETHODIMP CXTDataObject::GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium )
+{
+ if ( !(pFormatetc && pmedium) )
+ return E_INVALIDARG;
+
+ try
+ {
+ // prepare data transfer
+ invalidateStgMedium( *pmedium );
+ validateFormatEtc( pFormatetc );
+
+ // handle locale request, because locale is an artificial format for us
+ if ( CF_LOCALE == pFormatetc->cfFormat )
+ renderLocaleAndSetupStgMedium( *pFormatetc, *pmedium );
+ else if ( CF_UNICODETEXT == pFormatetc->cfFormat )
+ renderUnicodeAndSetupStgMedium( *pFormatetc, *pmedium );
+ else
+ renderAnyDataAndSetupStgMedium( *pFormatetc, *pmedium );
+ }
+ catch(UnsupportedFlavorException&)
+ {
+ HRESULT hr = DV_E_FORMATETC;
+
+ CFormatEtc aFormatetc(*pFormatetc);
+ if (CFormatRegistrar::isSynthesizeableFormat(aFormatetc))
+ hr = renderSynthesizedFormatAndSetupStgMedium( *pFormatetc, *pmedium );
+
+ return hr;
+ }
+ catch( CInvalidFormatEtcException& ex )
+ {
+ return ex.m_hr;
+ }
+ catch( CStgTransferHelper::CStgTransferException& ex )
+ {
+ return translateStgExceptionCode( ex.m_hr );
+ }
+ catch(...)
+ {
+ return E_UNEXPECTED;
+ }
+
+ return S_OK;
+}
+
+//inline
+void CXTDataObject::renderLocaleAndSetupStgMedium(
+ FORMATETC const & fetc, STGMEDIUM& stgmedium )
+{
+ if ( !m_FormatRegistrar.hasSynthesizedLocale( ) )
+ throw CInvalidFormatEtcException( DV_E_FORMATETC );
+ LCID lcid = CFormatRegistrar::getSynthesizedLocale( );
+ renderDataAndSetupStgMedium(
+ reinterpret_cast< sal_Int8* >( &lcid ),
+ fetc,
+ 0,
+ sizeof( LCID ),
+ stgmedium );
+}
+
+void CXTDataObject::renderUnicodeAndSetupStgMedium(
+ FORMATETC const & fetc, STGMEDIUM& stgmedium )
+{
+ DataFlavor aFlavor = formatEtcToDataFlavor( fetc );
+
+ Any aAny = m_XTransferable->getTransferData( aFlavor );
+
+ // unfortunately not all transferables fulfill the
+ // spec. and do throw an UnsupportedFlavorException
+ // so we must check the any
+ if ( !aAny.hasValue( ) )
+ {
+ OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" );
+ throw UnsupportedFlavorException( );
+ }
+
+ OUString aText;
+ aAny >>= aText;
+
+ sal_uInt32 nBytesToTransfer = aText.getLength( ) * sizeof( sal_Unicode );
+
+ // to be sure there is an ending 0
+ sal_uInt32 nRequiredMemSize = nBytesToTransfer + sizeof( sal_Unicode );
+
+ renderDataAndSetupStgMedium(
+ reinterpret_cast< const sal_Int8* >( aText.getStr( ) ),
+ fetc,
+ nRequiredMemSize,
+ nBytesToTransfer,
+ stgmedium );
+}
+
+void CXTDataObject::renderAnyDataAndSetupStgMedium(
+ FORMATETC& fetc, STGMEDIUM& stgmedium )
+{
+ DataFlavor aFlavor = formatEtcToDataFlavor( fetc );
+
+ Any aAny = m_XTransferable->getTransferData( aFlavor );
+
+ // unfortunately not all transferables fulfill the
+ // spec. and do throw an UnsupportedFlavorException
+ // so we must check the any
+ if ( !aAny.hasValue( ) )
+ {
+ OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" );
+ throw UnsupportedFlavorException( );
+ }
+
+ Sequence< sal_Int8 > clipDataStream;
+ aAny >>= clipDataStream;
+
+ sal_uInt32 nRequiredMemSize = 0;
+ if ( CDataFormatTranslator::isOemOrAnsiTextFormat( fetc.cfFormat ) )
+ nRequiredMemSize = sizeof( sal_Int8 ) * clipDataStream.getLength( ) + 1;
+
+ // prepare data for transmission
+ // #i124085# DIBV5 should not happen for now, but keep as hint here
+ if ( CF_DIBV5 == fetc.cfFormat || CF_DIB == fetc.cfFormat )
+ {
+#ifdef DBG_UTIL
+ if(CF_DIBV5 == fetc.cfFormat)
+ {
+ OSL_ENSURE(o3tl::make_unsigned(clipDataStream.getLength()) > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPV5HEADER)), "Wrong size on CF_DIBV5 data (!)");
+ }
+ else // CF_DIB == fetc.cfFormat
+ {
+ OSL_ENSURE(o3tl::make_unsigned(clipDataStream.getLength()) > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)), "Wrong size on CF_DIB data (!)");
+ }
+#endif
+
+ // remove BITMAPFILEHEADER
+ clipDataStream = OOBmpToWinDIB( clipDataStream );
+ }
+
+ if ( CF_METAFILEPICT == fetc.cfFormat )
+ {
+ stgmedium.tymed = TYMED_MFPICT;
+ stgmedium.hMetaFilePict = OOMFPictToWinMFPict( clipDataStream );
+ stgmedium.pUnkForRelease = nullptr;
+ }
+ else if( CF_ENHMETAFILE == fetc.cfFormat )
+ {
+ stgmedium.tymed = TYMED_ENHMF;
+ stgmedium.hMetaFilePict = OOMFPictToWinENHMFPict( clipDataStream );
+ stgmedium.pUnkForRelease = nullptr;
+ }
+ else
+ renderDataAndSetupStgMedium(
+ clipDataStream.getArray( ),
+ fetc,
+ nRequiredMemSize,
+ clipDataStream.getLength( ),
+ stgmedium );
+}
+
+HRESULT CXTDataObject::renderSynthesizedFormatAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium )
+{
+ HRESULT hr = S_OK;
+
+ try
+ {
+ if ( CF_UNICODETEXT == fetc.cfFormat )
+ // the transferable seems to have only text
+ renderSynthesizedUnicodeAndSetupStgMedium( fetc, stgmedium );
+ else if ( CDataFormatTranslator::isOemOrAnsiTextFormat( fetc.cfFormat ) )
+ // the transferable seems to have only unicode text
+ renderSynthesizedTextAndSetupStgMedium( fetc, stgmedium );
+ else
+ // the transferable seems to have only text/html
+ renderSynthesizedHtmlAndSetupStgMedium( fetc, stgmedium );
+ }
+ catch(UnsupportedFlavorException&)
+ {
+ hr = DV_E_FORMATETC;
+ }
+ catch( CInvalidFormatEtcException& )
+ {
+ OSL_FAIL( "Unexpected exception" );
+ }
+ catch( CStgTransferHelper::CStgTransferException& ex )
+ {
+ return translateStgExceptionCode( ex.m_hr );
+ }
+ catch(...)
+ {
+ hr = E_UNEXPECTED;
+ }
+
+ return hr;
+}
+
+// the transferable must have only text, so we will synthesize unicode text
+
+void CXTDataObject::renderSynthesizedUnicodeAndSetupStgMedium( FORMATETC const & fetc, STGMEDIUM& stgmedium )
+{
+ OSL_ASSERT( CF_UNICODETEXT == fetc.cfFormat );
+
+ Any aAny = m_XTransferable->getTransferData( m_FormatRegistrar.getRegisteredTextFlavor( ) );
+
+ // unfortunately not all transferables fulfill the
+ // spec. and do throw an UnsupportedFlavorException
+ // so we must check the any
+ if ( !aAny.hasValue( ) )
+ {
+ OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" );
+ throw UnsupportedFlavorException( );
+ }
+
+ Sequence< sal_Int8 > aText;
+ aAny >>= aText;
+
+ CStgTransferHelper stgTransfHelper;
+
+ MultiByteToWideCharEx(
+ CFormatRegistrar::getRegisteredTextCodePage( ),
+ reinterpret_cast< char* >( aText.getArray( ) ),
+ aText.getLength( ),
+ stgTransfHelper );
+
+ setupStgMedium( fetc, stgTransfHelper, stgmedium );
+}
+
+// the transferable must have only unicode text so we will synthesize text
+
+void CXTDataObject::renderSynthesizedTextAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium )
+{
+ OSL_ASSERT( CDataFormatTranslator::isOemOrAnsiTextFormat( fetc.cfFormat ) );
+
+ DataFlavor aFlavor = formatEtcToDataFlavor(
+ CDataFormatTranslator::getFormatEtcForClipformat( CF_UNICODETEXT ) );
+
+ Any aAny = m_XTransferable->getTransferData( aFlavor );
+
+ // unfortunately not all transferables fulfill the
+ // spec. and do throw an UnsupportedFlavorException
+ // so we must check the any
+ if ( !aAny.hasValue( ) )
+ {
+ OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" );
+ throw UnsupportedFlavorException( );
+ }
+
+ OUString aUnicodeText;
+ aAny >>= aUnicodeText;
+
+ CStgTransferHelper stgTransfHelper;
+
+ WideCharToMultiByteEx(
+ GetACP( ),
+ o3tl::toW( aUnicodeText.getStr( ) ),
+ aUnicodeText.getLength( ),
+ stgTransfHelper );
+
+ setupStgMedium( fetc, stgTransfHelper, stgmedium );
+}
+
+void CXTDataObject::renderSynthesizedHtmlAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium )
+{
+ OSL_ASSERT( CDataFormatTranslator::isHTMLFormat( fetc.cfFormat ) );
+
+ DataFlavor aFlavor;
+
+ // creating a DataFlavor on the fly
+ aFlavor.MimeType = "text/html";
+ aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+
+ Any aAny = m_XTransferable->getTransferData( aFlavor );
+
+ // unfortunately not all transferables fulfill the
+ // spec. and do throw an UnsupportedFlavorException
+ // so we must check the any
+ if ( !aAny.hasValue( ) )
+ {
+ OSL_FAIL( "XTransferable should throw an exception if ask for an unsupported flavor" );
+ throw UnsupportedFlavorException( );
+ }
+
+ Sequence< sal_Int8 > aTextHtmlSequence;
+ aAny >>= aTextHtmlSequence;
+
+ Sequence< sal_Int8 > aHTMLFormatSequence = TextHtmlToHTMLFormat( aTextHtmlSequence );
+
+ sal_uInt32 nBytesToTransfer = aHTMLFormatSequence.getLength( );
+
+ renderDataAndSetupStgMedium(
+ reinterpret_cast< const sal_Int8* >( aHTMLFormatSequence.getArray( ) ),
+ fetc,
+ 0,
+ nBytesToTransfer,
+ stgmedium );
+}
+
+// IDataObject->EnumFormatEtc
+
+STDMETHODIMP CXTDataObject::EnumFormatEtc(
+ DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc )
+{
+ if ( nullptr == ppenumFormatetc )
+ return E_INVALIDARG;
+
+ if ( DATADIR_SET == dwDirection )
+ return E_NOTIMPL;
+
+ *ppenumFormatetc = nullptr;
+
+ InitializeFormatEtcContainer( );
+
+ HRESULT hr;
+ if ( DATADIR_GET == dwDirection )
+ {
+ *ppenumFormatetc = new CEnumFormatEtc( this, m_FormatEtcContainer );
+ static_cast< LPUNKNOWN >( *ppenumFormatetc )->AddRef( );
+
+ hr = S_OK;
+ }
+ else
+ hr = E_INVALIDARG;
+
+ return hr;
+}
+
+// IDataObject->QueryGetData
+
+STDMETHODIMP CXTDataObject::QueryGetData( FORMATETC * pFormatetc )
+{
+ if ( (nullptr == pFormatetc) || IsBadReadPtr( pFormatetc, sizeof( FORMATETC ) ) )
+ return E_INVALIDARG;
+
+ InitializeFormatEtcContainer( );
+
+ CFormatEtc aFormatetc(*pFormatetc);
+ return m_FormatEtcContainer.hasFormatEtc(aFormatetc) ? S_OK : S_FALSE;
+}
+
+// IDataObject->GetDataHere
+
+STDMETHODIMP CXTDataObject::GetDataHere( FORMATETC *, STGMEDIUM * )
+{
+ return E_NOTIMPL;
+}
+
+// IDataObject->GetCanonicalFormatEtc
+
+STDMETHODIMP CXTDataObject::GetCanonicalFormatEtc( FORMATETC *, FORMATETC * )
+{
+ return E_NOTIMPL;
+}
+
+// IDataObject->SetData
+
+STDMETHODIMP CXTDataObject::SetData( FORMATETC *, STGMEDIUM *, BOOL )
+{
+ return E_NOTIMPL;
+}
+
+// IDataObject->DAdvise
+
+STDMETHODIMP CXTDataObject::DAdvise( FORMATETC *, DWORD, IAdviseSink *, DWORD * )
+{
+ return E_NOTIMPL;
+}
+
+// IDataObject->DUnadvise
+
+STDMETHODIMP CXTDataObject::DUnadvise( DWORD )
+{
+ return E_NOTIMPL;
+}
+
+// IDataObject->EnumDAdvise
+
+STDMETHODIMP CXTDataObject::EnumDAdvise( IEnumSTATDATA ** )
+{
+ return E_NOTIMPL;
+}
+
+// for our convenience
+
+CXTDataObject::operator IDataObject*( )
+{
+ return static_cast< IDataObject* >( this );
+}
+
+inline
+DataFlavor CXTDataObject::formatEtcToDataFlavor( const FORMATETC& aFormatEtc ) const
+{
+ DataFlavor aFlavor;
+
+ if ( m_FormatRegistrar.hasSynthesizedLocale( ) )
+ aFlavor = m_DataFormatTranslator.getDataFlavorFromFormatEtc(
+ aFormatEtc.cfFormat, CFormatRegistrar::getSynthesizedLocale());
+ else
+ aFlavor = m_DataFormatTranslator.getDataFlavorFromFormatEtc(aFormatEtc.cfFormat);
+
+ if ( !aFlavor.MimeType.getLength( ) )
+ throw UnsupportedFlavorException( );
+
+ return aFlavor;
+}
+
+inline void CXTDataObject::InitializeFormatEtcContainer( )
+{
+ if ( !m_bFormatEtcContainerInitialized )
+ {
+ m_FormatRegistrar.RegisterFormats( m_XTransferable, m_FormatEtcContainer );
+ m_bFormatEtcContainerInitialized = true;
+ }
+}
+
+CEnumFormatEtc::CEnumFormatEtc( LPUNKNOWN lpUnkOuter, const CFormatEtcContainer& aFormatEtcContainer ) :
+ m_nRefCnt( 0 ),
+ m_lpUnkOuter( lpUnkOuter ),
+ m_FormatEtcContainer( aFormatEtcContainer )
+{
+ Reset( );
+}
+
+// IUnknown->QueryInterface
+
+STDMETHODIMP CEnumFormatEtc::QueryInterface( REFIID iid, void** ppvObject )
+{
+ if ( nullptr == ppvObject )
+ return E_INVALIDARG;
+
+ HRESULT hr = E_NOINTERFACE;
+
+ *ppvObject = nullptr;
+
+ if ( ( __uuidof( IUnknown ) == iid ) ||
+ ( __uuidof( IEnumFORMATETC ) == iid ) )
+ {
+ *ppvObject = static_cast< IUnknown* >( this );
+ static_cast< LPUNKNOWN >( *ppvObject )->AddRef( );
+ hr = S_OK;
+ }
+
+ return hr;
+}
+
+// IUnknown->AddRef
+
+STDMETHODIMP_(ULONG) CEnumFormatEtc::AddRef( )
+{
+ // keep the dataobject alive
+ m_lpUnkOuter->AddRef( );
+ return InterlockedIncrement( &m_nRefCnt );
+}
+
+// IUnknown->Release
+
+STDMETHODIMP_(ULONG) CEnumFormatEtc::Release( )
+{
+ // release the outer dataobject
+ m_lpUnkOuter->Release( );
+
+ ULONG nRefCnt = InterlockedDecrement( &m_nRefCnt );
+ if ( 0 == nRefCnt )
+ delete this;
+
+ return nRefCnt;
+}
+
+// IEnumFORMATETC->Next
+
+STDMETHODIMP CEnumFormatEtc::Next( ULONG nRequested, FORMATETC * lpDest, ULONG* lpFetched )
+{
+ if ( ( nRequested < 1 ) ||
+ (( nRequested > 1 ) && ( nullptr == lpFetched )) ||
+ IsBadWritePtr( lpDest, sizeof( FORMATETC ) * nRequested ) )
+ return E_INVALIDARG;
+
+ sal_uInt32 nFetched = m_FormatEtcContainer.nextFormatEtc( lpDest, nRequested );
+
+ if ( nullptr != lpFetched )
+ *lpFetched = nFetched;
+
+ return (nFetched == nRequested) ? S_OK : S_FALSE;
+}
+
+// IEnumFORMATETC->Skip
+
+STDMETHODIMP CEnumFormatEtc::Skip( ULONG celt )
+{
+ return m_FormatEtcContainer.skipFormatEtc( celt ) ? S_OK : S_FALSE;
+}
+
+// IEnumFORMATETC->Reset
+
+STDMETHODIMP CEnumFormatEtc::Reset( )
+{
+ m_FormatEtcContainer.beginEnumFormatEtc( );
+ return S_OK;
+}
+
+// IEnumFORMATETC->Clone
+
+STDMETHODIMP CEnumFormatEtc::Clone( IEnumFORMATETC** ppenum )
+{
+ if ( nullptr == ppenum )
+ return E_INVALIDARG;
+
+ *ppenum = new CEnumFormatEtc( m_lpUnkOuter, m_FormatEtcContainer );
+ static_cast< LPUNKNOWN >( *ppenum )->AddRef( );
+
+ return S_OK;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/XTDataObject.hxx b/vcl/win/dtrans/XTDataObject.hxx
new file mode 100644
index 0000000000..77f8c53f29
--- /dev/null
+++ b/vcl/win/dtrans/XTDataObject.hxx
@@ -0,0 +1,134 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboardOwner.hpp>
+
+#include "DataFmtTransl.hxx"
+
+#include "FetcList.hxx"
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <ole2.h>
+#include <objidl.h>
+
+/*--------------------------------------------------------------------------
+ - the function principle of the windows clipboard:
+ a data provider offers all formats he can deliver on the clipboard
+ a clipboard client ask for the available formats on the clipboard
+ and decides if there is a format he can use
+ if there is one, he requests the data in this format
+
+ - This class inherits from IDataObject and so can be placed on the
+ OleClipboard. The class wraps a transferable object which is the
+ original DataSource
+ - DataFlavors offered by this transferable will be translated into
+ appropriate clipboard formats
+ - if the transferable contains text data always text and unicodetext
+ will be offered or vice versa
+ - text data will be automatically converted between text and unicode text
+ - although the transferable may support text in different charsets
+ (codepages) only text in one codepage can be offered by the clipboard
+
+----------------------------------------------------------------------------*/
+
+class CStgTransferHelper;
+
+class CXTDataObject : public IDataObject
+{
+public:
+ CXTDataObject( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::datatransfer::XTransferable >& aXTransferable );
+ virtual ~CXTDataObject();
+
+ // ole interface implementation
+
+ //IUnknown interface methods
+ STDMETHODIMP QueryInterface(REFIID iid, void** ppvObject) override;
+ STDMETHODIMP_( ULONG ) AddRef( ) override;
+ STDMETHODIMP_( ULONG ) Release( ) override;
+
+ // IDataObject interface methods
+ STDMETHODIMP GetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override;
+ STDMETHODIMP GetDataHere( FORMATETC * pFormatetc, STGMEDIUM * pmedium ) override;
+ STDMETHODIMP QueryGetData( FORMATETC * pFormatetc ) override;
+ STDMETHODIMP GetCanonicalFormatEtc( FORMATETC * pFormatectIn, FORMATETC * pFormatetcOut ) override;
+ STDMETHODIMP SetData( FORMATETC * pFormatetc, STGMEDIUM * pmedium, BOOL fRelease ) override;
+ STDMETHODIMP EnumFormatEtc( DWORD dwDirection, IEnumFORMATETC** ppenumFormatetc ) override;
+ STDMETHODIMP DAdvise( FORMATETC * pFormatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD* pdwConnection ) override;
+ STDMETHODIMP DUnadvise( DWORD dwConnection ) override;
+ STDMETHODIMP EnumDAdvise( IEnumSTATDATA** ppenumAdvise ) override;
+
+ operator IDataObject*( );
+
+private:
+ css::datatransfer::DataFlavor formatEtcToDataFlavor( const FORMATETC& aFormatEtc ) const;
+
+ void renderLocaleAndSetupStgMedium( FORMATETC const & fetc, STGMEDIUM& stgmedium );
+ void renderUnicodeAndSetupStgMedium( FORMATETC const & fetc, STGMEDIUM& stgmedium );
+ void renderAnyDataAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium );
+
+ HRESULT renderSynthesizedFormatAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium );
+ void renderSynthesizedUnicodeAndSetupStgMedium( FORMATETC const & fetc, STGMEDIUM& stgmedium );
+ void renderSynthesizedTextAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium );
+ void renderSynthesizedHtmlAndSetupStgMedium( FORMATETC& fetc, STGMEDIUM& stgmedium );
+
+ inline void InitializeFormatEtcContainer( );
+
+private:
+ LONG m_nRefCnt;
+ css::uno::Reference< css::datatransfer::XTransferable > m_XTransferable;
+ css::uno::Reference< css::uno::XComponentContext> m_XComponentContext;
+ CFormatEtcContainer m_FormatEtcContainer;
+ bool m_bFormatEtcContainerInitialized;
+ CDataFormatTranslator m_DataFormatTranslator;
+ CFormatRegistrar m_FormatRegistrar;
+};
+
+class CEnumFormatEtc : public IEnumFORMATETC
+{
+public:
+ CEnumFormatEtc( LPUNKNOWN lpUnkOuter, const CFormatEtcContainer& aFormatEtcContainer );
+ virtual ~CEnumFormatEtc() {}
+
+ // IUnknown
+ STDMETHODIMP QueryInterface( REFIID iid, void** ppvObject ) override;
+ STDMETHODIMP_( ULONG ) AddRef( ) override;
+ STDMETHODIMP_( ULONG ) Release( ) override;
+
+ //IEnumFORMATETC
+ STDMETHODIMP Next( ULONG nRequested, FORMATETC * lpDest, ULONG* lpFetched ) override;
+ STDMETHODIMP Skip( ULONG celt ) override;
+ STDMETHODIMP Reset( ) override;
+ STDMETHODIMP Clone( IEnumFORMATETC** ppenum ) override;
+
+private:
+ LONG m_nRefCnt;
+ LPUNKNOWN m_lpUnkOuter;
+ CFormatEtcContainer m_FormatEtcContainer;
+};
+
+typedef CEnumFormatEtc *PCEnumFormatEtc;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/clipboardmanager.cxx b/vcl/win/dtrans/clipboardmanager.cxx
new file mode 100644
index 0000000000..bff5aec49f
--- /dev/null
+++ b/vcl/win/dtrans/clipboardmanager.cxx
@@ -0,0 +1,198 @@
+/* -*- 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 "clipboardmanager.hxx"
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+#include <rtl/ref.hxx>
+
+using namespace com::sun::star::container;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::datatransfer::clipboard;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace cppu;
+using namespace osl;
+
+using ::dtrans::ClipboardManager;
+
+static std::mutex g_InstanceGuard;
+static rtl::Reference<ClipboardManager> g_Instance;
+static bool g_Disposed = false;
+
+
+ClipboardManager::ClipboardManager():
+ m_aDefaultName(OUString("default"))
+{
+}
+
+ClipboardManager::~ClipboardManager()
+{
+}
+
+OUString SAL_CALL ClipboardManager::getImplementationName( )
+{
+ return "com.sun.star.comp.datatransfer.ClipboardManager";
+}
+
+sal_Bool SAL_CALL ClipboardManager::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL ClipboardManager::getSupportedServiceNames( )
+{
+ return { "com.sun.star.datatransfer.clipboard.ClipboardManager" };
+}
+
+Reference< XClipboard > SAL_CALL ClipboardManager::getClipboard( const OUString& aName )
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ // object is disposed already
+ if (m_bDisposed)
+ throw DisposedException("object is disposed.",
+ static_cast < XClipboardManager * > (this));
+
+ ClipboardMap::iterator iter =
+ m_aClipboardMap.find(aName.getLength() ? aName : m_aDefaultName);
+
+ if (iter != m_aClipboardMap.end())
+ return iter->second;
+
+ throw NoSuchElementException(aName, static_cast < XClipboardManager * > (this));
+}
+
+void SAL_CALL ClipboardManager::addClipboard( const Reference< XClipboard >& xClipboard )
+{
+ OSL_ASSERT(xClipboard.is());
+
+ // check parameter
+ if (!xClipboard.is())
+ throw IllegalArgumentException("empty reference",
+ static_cast < XClipboardManager * > (this), 1);
+
+ // the name "default" is reserved for internal use
+ OUString aName = xClipboard->getName();
+ if ( m_aDefaultName == aName )
+ throw IllegalArgumentException("name reserved",
+ static_cast < XClipboardManager * > (this), 1);
+
+ // try to add new clipboard to the list
+ std::unique_lock aGuard(m_aMutex);
+ if (!m_bDisposed)
+ {
+ std::pair< const OUString, Reference< XClipboard > > value (
+ aName.getLength() ? aName : m_aDefaultName,
+ xClipboard );
+
+ std::pair< ClipboardMap::iterator, bool > p = m_aClipboardMap.insert(value);
+ aGuard.unlock();
+
+ // insert failed, element must exist already
+ if (!p.second)
+ throw ElementExistException(aName, static_cast < XClipboardManager * > (this));
+
+ // request disposing notifications
+ Reference< XComponent > xComponent(xClipboard, UNO_QUERY);
+ if (xComponent.is())
+ xComponent->addEventListener(static_cast < XEventListener * > (this));
+ }
+}
+
+void SAL_CALL ClipboardManager::removeClipboard( const OUString& aName )
+{
+ std::unique_lock aGuard(m_aMutex);
+ if (!m_bDisposed)
+ m_aClipboardMap.erase(aName.getLength() ? aName : m_aDefaultName );
+}
+
+Sequence< OUString > SAL_CALL ClipboardManager::listClipboardNames()
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if (m_bDisposed)
+ throw DisposedException("object is disposed.",
+ static_cast < XClipboardManager * > (this));
+
+ return comphelper::mapKeysToSequence(m_aClipboardMap);
+}
+
+void ClipboardManager::disposing(std::unique_lock<std::mutex>& rGuard)
+{
+ rGuard.unlock();
+
+ {
+ std::unique_lock aGuard(g_InstanceGuard);
+ g_Instance.clear();
+ g_Disposed = true;
+ }
+
+ // removeClipboard is still allowed here, so make a copy of the
+ // list (to ensure integrity) and clear the original.
+ rGuard.lock();
+ ClipboardMap aCopy;
+ std::swap(aCopy, m_aClipboardMap);
+ rGuard.unlock();
+
+ // dispose all clipboards still in list
+ for (auto const& elem : aCopy)
+ {
+ Reference< XComponent > xComponent(elem.second, UNO_QUERY);
+ if (xComponent.is())
+ {
+ try
+ {
+ xComponent->removeEventListener(static_cast < XEventListener * > (this));
+ xComponent->dispose();
+ }
+ catch (const Exception&)
+ {
+ // exceptions can be safely ignored here.
+ }
+ }
+ }
+}
+
+void SAL_CALL ClipboardManager::disposing( const EventObject& event )
+{
+ Reference < XClipboard > xClipboard(event.Source, UNO_QUERY);
+
+ if (xClipboard.is())
+ removeClipboard(xClipboard->getName());
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+dtrans_ClipboardManager_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ std::unique_lock aGuard(g_InstanceGuard);
+ if (g_Disposed)
+ return nullptr;
+ if (!g_Instance)
+ g_Instance.set(new ClipboardManager());
+ return cppu::acquire(g_Instance.get());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/clipboardmanager.hxx b/vcl/win/dtrans/clipboardmanager.hxx
new file mode 100644
index 0000000000..27f9ddbdae
--- /dev/null
+++ b/vcl/win/dtrans/clipboardmanager.hxx
@@ -0,0 +1,89 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <comphelper/compbase.hxx>
+
+#include <com/sun/star/datatransfer/clipboard/XClipboardManager.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+
+#include <map>
+
+typedef std::map< OUString, css::uno::Reference< css::datatransfer::clipboard::XClipboard > > ClipboardMap;
+
+namespace dtrans
+{
+
+ class ClipboardManager : public ::comphelper::WeakComponentImplHelper <
+ css::datatransfer::clipboard::XClipboardManager,
+ css::lang::XEventListener,
+ css::lang::XServiceInfo >
+ {
+ ClipboardMap m_aClipboardMap;
+
+ const OUString m_aDefaultName;
+
+ virtual ~ClipboardManager() override;
+ protected:
+ using WeakComponentImplHelperBase::disposing;
+ public:
+
+ ClipboardManager();
+
+ /*
+ * XServiceInfo
+ */
+
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ /*
+ * XComponent
+ */
+
+ virtual void disposing(std::unique_lock<std::mutex>& rGuard) override;
+
+ /*
+ * XEventListener
+ */
+
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ /*
+ * XClipboardManager
+ */
+
+ virtual css::uno::Reference< css::datatransfer::clipboard::XClipboard > SAL_CALL getClipboard( const OUString& aName ) override;
+
+ virtual void SAL_CALL addClipboard( const css::uno::Reference< css::datatransfer::clipboard::XClipboard >& xClipboard ) override;
+
+ virtual void SAL_CALL removeClipboard( const OUString& aName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL listClipboardNames( ) override;
+
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/ftransl.cxx b/vcl/win/dtrans/ftransl.cxx
new file mode 100644
index 0000000000..20056bba0e
--- /dev/null
+++ b/vcl/win/dtrans/ftransl.cxx
@@ -0,0 +1,550 @@
+/* -*- 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 <string_view>
+
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+
+#include "ftransl.hxx"
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/datatransfer/XMimeContentType.hpp>
+#include <com/sun/star/datatransfer/MimeContentTypeFactory.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include "ImplHelper.hxx"
+
+#include <shlobj.h>
+
+#define CPPUTYPE_SEQSALINT8 cppu::UnoType<Sequence< sal_Int8 >>::get()
+#define CPPUTYPE_DEFAULT CPPUTYPE_SEQSALINT8
+
+constexpr OUString Windows_FormatName = u"windows_formatname"_ustr;
+const css::uno::Type CppuType_ByteSequence = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
+const css::uno::Type CppuType_String = ::cppu::UnoType<OUString>::get();
+
+using namespace osl;
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::container;
+
+namespace
+{
+
+struct FormatEntry
+{
+ FormatEntry(
+ const char *mime_content_type,
+ const char *human_presentable_name,
+ const char *native_format_name,
+ CLIPFORMAT std_clipboard_format_id,
+ css::uno::Type const & cppu_type
+ );
+
+ css::datatransfer::DataFlavor aDataFlavor;
+ OUString aNativeFormatName;
+ sal_Int32 aStandardFormatId;
+};
+
+}
+
+FormatEntry::FormatEntry(
+ const char *mime_content_type,
+ const char *human_presentable_name,
+ const char *native_format_name,
+ CLIPFORMAT std_clipboard_format_id,
+ css::uno::Type const & cppu_type)
+ : aDataFlavor( OUString::createFromAscii(mime_content_type), OUString::createFromAscii(human_presentable_name), cppu_type)
+{
+ if (native_format_name)
+ aNativeFormatName = OUString::createFromAscii(native_format_name);
+ else
+ aNativeFormatName = OUString::createFromAscii(human_presentable_name);
+
+ aStandardFormatId = std_clipboard_format_id;
+}
+
+// to optimize searching we add all entries with a
+// standard clipboard format number first, in the
+// table before the entries with CF_INVALID
+// if we are searching for a standard clipboard
+// format number we can stop if we find the first
+// CF_INVALID
+
+const std::vector< FormatEntry > g_TranslTable {
+ //SotClipboardFormatId::DIF
+ FormatEntry("application/x-openoffice-dif;windows_formatname=\"DIF\"", "DIF", "DIF", CF_DIF, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::BITMAP
+
+ // #i124085# CF_DIBV5 disabled, leads to problems at export. To fully support, using
+ // an own mime-type may be necessary. I have tried that, but saw no real advantages
+ // with different apps when exchanging bitmap-based data. So, disabled for now. At
+ // the same time increased png format exchange for better interoperability
+ // FormatEntry("application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", "Bitmap", "Bitmap", CF_DIBV5, CPPUTYPE_DEFAULT),
+
+ FormatEntry("application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", "Bitmap", "Bitmap", CF_DIB, CPPUTYPE_DEFAULT),
+ FormatEntry("application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"", "Bitmap", "Bitmap", CF_BITMAP, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::STRING
+ FormatEntry("text/plain;charset=utf-16", "Unicode-Text", "", CF_UNICODETEXT, CppuType_String),
+ // Format Locale - for internal use
+ FormatEntry("application/x-openoffice-locale;windows_formatname=\"Locale\"", "Locale", "Locale", CF_LOCALE, CPPUTYPE_DEFAULT),
+ // SOT_FORMAT_WMF
+ FormatEntry("application/x-openoffice-wmf;windows_formatname=\"Image WMF\"", "Windows MetaFile", "Image WMF", CF_METAFILEPICT, CPPUTYPE_DEFAULT),
+ // SOT_FORMAT_EMF
+ FormatEntry("application/x-openoffice-emf;windows_formatname=\"Image EMF\"", "Windows Enhanced MetaFile", "Image EMF", CF_ENHMETAFILE, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::FILE_LIST
+ FormatEntry("application/x-openoffice-filelist;windows_formatname=\"FileList\"", "FileList", "FileList", CF_HDROP, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SYLK
+ FormatEntry("application/x-openoffice-sylk;windows_formatname=\"Sylk\"", "Sylk", "Sylk", CF_SYLK, CPPUTYPE_DEFAULT ),
+ // SotClipboardFormatId::GDIMETAFILE
+ FormatEntry("application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\"", "GDIMetaFile", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::PRIVATE
+ FormatEntry("application/x-openoffice-private;windows_formatname=\"Private\"", "Private", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::SIMPLE_FILE
+ FormatEntry("application/x-openoffice-file;windows_formatname=\"FileNameW\"", "FileName", nullptr, CF_INVALID, CppuType_String),
+ // SotClipboardFormatId::RTF
+ FormatEntry("text/rtf", "Rich Text Format", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::DRAWING
+ FormatEntry("application/x-openoffice-drawing;windows_formatname=\"Drawing Format\"", "Drawing Format", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::SVXB
+ FormatEntry("application/x-openoffice-svbx;windows_formatname=\"SVXB (StarView Bitmap/Animation)\"", "SVXB (StarView Bitmap/Animation)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::SVIM
+ FormatEntry("application/x-openoffice-svim;windows_formatname=\"SVIM (StarView ImageMap)\"", "SVIM (StarView ImageMap)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::XFA
+ FormatEntry("application/x-libreoffice-xfa;windows_formatname=\"XFA (XOutDev FillAttr Any)\"", "XFA (XOutDev FillAttr Any)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT
+ FormatEntry("application/vnd.oasis.opendocument.text-flat-xml", "EditEngine ODF", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::INTERNALLINK_STATE
+ FormatEntry("application/x-openoffice-internallink-state;windows_formatname=\"StatusInfo of SvxInternalLink\"", "StatusInfo of SvxInternalLink", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::SOLK
+ FormatEntry("application/x-openoffice-solk;windows_formatname=\"SOLK (StarOffice Link)\"", "SOLK (StarOffice Link)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::NETSCAPE_BOOKMARK
+ FormatEntry("application/x-openoffice-netscape-bookmark;windows_formatname=\"Netscape Bookmark\"", "Netscape Bookmark", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::TREELISTBOX
+ FormatEntry("application/x-openoffice-treelistbox;windows_formatname=\"SV_LBOX_DD_FORMAT\"", "SV_LBOX_DD_FORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::NATIVE
+ FormatEntry("application/x-openoffice-native;windows_formatname=\"Native\"", "Native", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::OWNERLINK
+ FormatEntry("application/x-openoffice-ownerlink;windows_formatname=\"OwnerLink\"", "OwnerLink", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::STARSERVER
+ FormatEntry("application/x-openoffice-starserver;windows_formatname=\"StarServerFormat\"", "StarServerFormat", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::STAROBJECT
+ FormatEntry("application/x-openoffice-starobject;windows_formatname=\"StarObjectFormat\"", "StarObjectFormat", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::APPLETOBJECT
+ FormatEntry("application/x-openoffice-appletobject;windows_formatname=\"Applet Object\"", "Applet Object", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::PLUGIN_OBJECT
+ FormatEntry("application/x-openoffice-plugin-object;windows_formatname=\"PlugIn Object\"", "PlugIn Object", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::STARWRITER_30
+ FormatEntry("application/x-openoffice-starwriter-30;windows_formatname=\"StarWriter 3.0\"", "StarWriter 3.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARWRITER_40
+ FormatEntry("application/x-openoffice-starwriter-40;windows_formatname=\"StarWriter 4.0\"", "StarWriter 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARWRITER_50
+ FormatEntry("application/x-openoffice-starwriter-50;windows_formatname=\"StarWriter 5.0\"", "StarWriter 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARWRITERWEB_40
+ FormatEntry("application/x-openoffice-starwriterweb-40;windows_formatname=\"StarWriter/Web 4.0\"", "StarWriter/Web 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARWRITERWEB_50
+ FormatEntry("application/x-openoffice-starwriterweb-50;windows_formatname=\"StarWriter/Web 5.0\"", "StarWriter/Web 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARWRITERGLOB_40
+ FormatEntry("application/x-openoffice-starwriterglob-40;windows_formatname=\"StarWriter/Global 4.0\"", "StarWriter/Global 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::STARWRITERGLOB_50
+ FormatEntry("application/x-openoffice-starwriterglob-50;windows_formatname=\"StarWriter/Global 5.0\"", "StarWriter/Global 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARDRAW
+ FormatEntry("application/x-openoffice-stardraw;windows_formatname=\"StarDrawDocument\"", "StarDrawDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARDRAW_40
+ FormatEntry("application/x-openoffice-stardraw-40;windows_formatname=\"StarDrawDocument 4.0\"", "StarDrawDocument 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARIMPRESS_50
+ FormatEntry("application/x-openoffice-starimpress-50;windows_formatname=\"StarImpress 5.0\"", "StarImpress 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::STARDRAW_50
+ FormatEntry("application/x-openoffice-stardraw-50;windows_formatname=\"StarDraw 5.0\"", "StarDraw 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARCALC
+ FormatEntry("application/x-openoffice-starcalc;windows_formatname=\"StarCalcDocument\"", "StarCalcDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARCALC_40
+ FormatEntry("application/x-openoffice-starcalc-40;windows_formatname=\"StarCalc 4.0\"", "StarCalc 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::STARCALC_50
+ FormatEntry("application/x-openoffice-starcalc-50;windows_formatname=\"StarCalc 5.0\"", "StarCalc 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::STARCHART
+ FormatEntry("application/x-openoffice-starchart;windows_formatname=\"StarChartDocument\"", "StarChartDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::STARCHART_40
+ FormatEntry("application/x-openoffice-starchart-40;windows_formatname=\"StarChartDocument 4.0\"", "StarChartDocument 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::STARCHART_50
+ FormatEntry("application/x-openoffice-starchart-50;windows_formatname=\"StarChart 5.0\"", "StarChart 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARIMAGE
+ FormatEntry("application/x-openoffice-starimage;windows_formatname=\"StarImageDocument\"", "StarImageDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARIMAGE_40
+ FormatEntry("application/x-openoffice-starimage-40;windows_formatname=\"StarImageDocument 4.0\"", "StarImageDocument 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARIMAGE_50
+ FormatEntry("application/x-openoffice-starimage-50;windows_formatname=\"StarImage 5.0\"", "StarImage 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARMATH
+ FormatEntry("application/x-openoffice-starmath;windows_formatname=\"StarMath\"", "StarMath", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARMATH_40
+ FormatEntry("application/x-openoffice-starmath-40;windows_formatname=\"StarMathDocument 4.0\"", "StarMathDocument 4.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARMATH_50
+ FormatEntry("application/x-openoffice-starmath-50;windows_formatname=\"StarMath 5.0\"", "StarMath 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STAROBJECT_PAINTDOC
+ FormatEntry("application/x-openoffice-starobject-paintdoc;windows_formatname=\"StarObjectPaintDocument\"", "StarObjectPaintDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::FILLED_AREA
+ FormatEntry("application/x-openoffice-filled-area;windows_formatname=\"FilledArea\"", "FilledArea", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::HTML
+ FormatEntry("text/html", "HTML (HyperText Markup Language)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::HTML_SIMPLE
+ FormatEntry("application/x-openoffice-html-simple;windows_formatname=\"HTML Format\"", "HTML Format", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::CHAOS
+ FormatEntry("application/x-openoffice-chaos;windows_formatname=\"FORMAT_CHAOS\"", "FORMAT_CHAOS", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::CNT_MSGATTACHFILE
+ FormatEntry("application/x-openoffice-msgattachfile;windows_formatname=\"CNT_MSGATTACHFILE_FORMAT\"", "CNT_MSGATTACHFILE_FORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::BIFF_5
+ FormatEntry("application/x-openoffice-biff5;windows_formatname=\"Biff5\"", "Biff5", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::BIFF__5
+ FormatEntry("application/x-openoffice-biff-5;windows_formatname=\"Biff 5\"", "Biff 5", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::BIFF_8
+ FormatEntry("application/x-openoffice-biff-8;windows_formatname=\"Biff8\"", "Biff8", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SYLK_BIGCAPS
+ FormatEntry("application/x-openoffice-sylk-bigcaps;windows_formatname=\"SYLK\"", "SYLK", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::LINK
+ FormatEntry("application/x-openoffice-link;windows_formatname=\"Link\"", "Link", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARDRAW_TABBAR
+ FormatEntry("application/x-openoffice-stardraw-tabbar;windows_formatname=\"StarDraw TabBar\"", "StarDraw TabBar", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SONLK
+ FormatEntry("application/x-openoffice-sonlk;windows_formatname=\"SONLK (StarOffice Navi Link)\"", "SONLK (StarOffice Navi Link)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::MSWORD_DOC
+ FormatEntry("application/msword", "MSWordDoc", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STAR_FRAMESET_DOC
+ FormatEntry("application/x-openoffice-star-frameset-doc;windows_formatname=\"StarFrameSetDocument\"", "StarFrameSetDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::OFFICE_DOC
+ FormatEntry("application/x-openoffice-office-doc;windows_formatname=\"OfficeDocument\"", "OfficeDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::NOTES_DOCINFO
+ FormatEntry("application/x-openoffice-notes-docinfo;windows_formatname=\"NotesDocInfo\"", "NotesDocInfo", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::NOTES_HNOTE
+ FormatEntry("application/x-openoffice-notes-hnote;windows_formatname=\"NoteshNote\"", "NoteshNote", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::NOTES_NATIVE
+ FormatEntry("application/x-openoffice-notes-native;windows_formatname=\"Native\"", "Native", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SFX_DOC
+ FormatEntry("application/x-openoffice-sfx-doc;windows_formatname=\"SfxDocument\"", "SfxDocument", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::EVDF
+ FormatEntry("application/x-openoffice-evdf;windows_formatname=\"EVDF (Explorer View Dummy Format)\"", "EVDF (Explorer View Dummy Format)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::ESDF
+ FormatEntry("application/x-openoffice-esdf;windows_formatname=\"ESDF (Explorer Search Dummy Format)\"", "ESDF (Explorer Search Dummy Format)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::IDF
+ FormatEntry("application/x-openoffice-idf;windows_formatname=\"IDF (Iconview Dummy Format)\"", "IDF (Iconview Dummy Format)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::EFTP
+ FormatEntry("application/x-openoffice-eftp;windows_formatname=\"EFTP (Explorer Ftp File)\"", "EFTP (Explorer Ftp File)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::EFD
+ FormatEntry("application/x-openoffice-efd;windows_formatname=\"EFD (Explorer Ftp Dir)\"", "EFD (Explorer Ftp Dir)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SVX_FORMFIELDEXCH
+ FormatEntry("application/x-openoffice-svx-formfieldexch;windows_formatname=\"SvxFormFieldExch\"", "SvxFormFieldExch", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::EXTENDED_TABBAR
+ FormatEntry("application/x-openoffice-extended-tabbar;windows_formatname=\"ExtendedTabBar\"", "ExtendedTabBar", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SBA_DATAEXCHANGE
+ FormatEntry("application/x-openoffice-sba-dataexchange;windows_formatname=\"SBA-DATAFORMAT\"", "SBA-DATAFORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SBA_FIELDDATAEXCHANGE
+ FormatEntry("application/x-openoffice-sba-fielddataexchange;windows_formatname=\"SBA-FIELDFORMAT\"", "SBA-FIELDFORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SBA_PRIVATE_URL
+ FormatEntry("application/x-openoffice-sba-private-url;windows_formatname=\"SBA-PRIVATEURLFORMAT\"", "SBA-PRIVATEURLFORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SBA_TABED
+ FormatEntry("application/x-openoffice-sba-tabed;windows_formatname=\"Tabed\"", "Tabed", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SBA_TABID
+ FormatEntry("application/x-openoffice-sba-tabid;windows_formatname=\"Tabid\"", "Tabid", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SBA_JOIN
+ FormatEntry("application/x-openoffice-sba-join;windows_formatname=\"SBA-JOINFORMAT\"", "SBA-JOINFORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::OBJECTDESCRIPTOR
+ FormatEntry("application/x-openoffice-objectdescriptor-xml;windows_formatname=\"Star Object Descriptor (XML)\"", "Star Object Descriptor (XML)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::LINKSRCDESCRIPTOR
+ FormatEntry("application/x-openoffice-linksrcdescriptor-xml;windows_formatname=\"Star Link Source Descriptor (XML)\"", "Star Link Source Descriptor (XML)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::EMBED_SOURCE
+ FormatEntry("application/x-openoffice-embed-source-xml;windows_formatname=\"Star Embed Source (XML)\"", "Star Embed Source (XML)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::LINK_SOURCE
+ FormatEntry("application/x-openoffice-link-source-xml;windows_formatname=\"Star Link Source (XML)\"", "Star Link Source (XML)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::EMBEDDED_OBJ
+ FormatEntry("application/x-openoffice-embedded-obj-xml;windows_formatname=\"Star Embedded Object (XML)\"", "Star Embedded Object (XML)", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::FILECONTENT
+ FormatEntry("application/x-openoffice-filecontent;windows_formatname=\"" CFSTR_FILECONTENTS "\"", CFSTR_FILECONTENTS, nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::FILEGRPDESCRIPTOR
+ FormatEntry("application/x-openoffice-filegrpdescriptor;windows_formatname=\"" CFSTR_FILEDESCRIPTOR "\"", CFSTR_FILEDESCRIPTOR, nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::FILENAME
+ FormatEntry("application/x-openoffice-filename;windows_formatname=\"" CFSTR_FILENAME "\"", CFSTR_FILENAME, nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SD_OLE
+ FormatEntry("application/x-openoffice-sd-ole;windows_formatname=\"SD-OLE\"", "SD-OLE", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::EMBEDDED_OBJ_OLE
+ FormatEntry("application/x-openoffice-embedded-obj-ole;windows_formatname=\"Embedded Object\"", "Embedded Object", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::EMBED_SOURCE_OLE
+ FormatEntry("application/x-openoffice-embed-source-ole;windows_formatname=\"Embed Source\"", "Embed Source", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::OBJECTDESCRIPTOR_OLE
+ FormatEntry("application/x-openoffice-objectdescriptor-ole;windows_formatname=\"Object Descriptor\"", "Object Descriptor", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::LINKSRCDESCRIPTOR_OLE
+ FormatEntry("application/x-openoffice-linkdescriptor-ole;windows_formatname=\"Link Source Descriptor\"", "Link Source Descriptor", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::LINK_SOURCE_OLE
+ FormatEntry("application/x-openoffice-link-source-ole;windows_formatname=\"Link Source\"", "Link Source", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SBA_CTRLDATAEXCHANGE
+ FormatEntry("application/x-openoffice-sba-ctrldataexchange;windows_formatname=\"SBA-CTRLFORMAT\"", "SBA-CTRLFORMAT", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::OUTPLACE_OBJ
+ FormatEntry("application/x-openoffice-outplace-obj;windows_formatname=\"OutPlace Object\"", "OutPlace Object", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::CNT_OWN_CLIP
+ FormatEntry("application/x-openoffice-cnt-own-clip;windows_formatname=\"CntOwnClipboard\"", "CntOwnClipboard", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::INET_IMAGE
+ FormatEntry("application/x-openoffice-inet-image;windows_formatname=\"SO-INet-Image\"", "SO-INet-Image", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::NETSCAPE_IMAGE
+ FormatEntry("application/x-openoffice-netscape-image;windows_formatname=\"Netscape Image Format\"", "Netscape Image Format", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SBA_FORMEXCHANGE
+ FormatEntry("application/x-openoffice-sba-formexchange;windows_formatname=\"SBA_FORMEXCHANGE\"", "SBA_FORMEXCHANGE", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::SBA_REPORTEXCHANGE
+ FormatEntry("application/x-openoffice-sba-reportexchange;windows_formatname=\"SBA_REPORTEXCHANGE\"", "SBA_REPORTEXCHANGE", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::UNIFORMRESOURCELOCATOR
+ FormatEntry("application/x-openoffice-uniformresourcelocator;windows_formatname=\"UniformResourceLocatorW\"", "UniformResourceLocator", nullptr, CF_INVALID, CppuType_String),
+ //SotClipboardFormatId::STARCHARTDOCUMENT_50
+ FormatEntry("application/x-openoffice-starchartdocument-50;windows_formatname=\"StarChartDocument 5.0\"", "StarChartDocument 5.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::GRAPHOBJ
+ FormatEntry("application/x-openoffice-graphobj;windows_formatname=\"Graphic Object\"", "Graphic Object", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARWRITER_60
+ FormatEntry("application/vnd.sun.xml.writer", "Writer 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARWRITERWEB_60
+ FormatEntry("application/vnd.sun.xml.writer.web", "Writer/Web 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARWRITERGLOB_60
+ FormatEntry("application/vnd.sun.xml.writer.global", "Writer/Global 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARWDRAW_60
+ FormatEntry("application/vnd.sun.xml.draw", "Draw 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARIMPRESS_60
+ FormatEntry("application/vnd.sun.xml.impress", "Impress 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARCALC_60
+ FormatEntry("application/vnd.sun.xml.calc", "Calc 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARCHART_60
+ FormatEntry("application/vnd.sun.xml.chart", "Chart 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::STARMATH_60
+ FormatEntry("application/vnd.sun.xml.math", "Math 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::DIALOG_60
+ FormatEntry("application/vnd.sun.xml.dialog", "Dialog 6.0", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::BMP
+ FormatEntry("image/bmp", "Windows Bitmap", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::PNG
+ FormatEntry("image/png", "PNG", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::MATHML
+ FormatEntry("application/mathml+xml", "MathML", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::DUMMY3
+ FormatEntry("application/x-openoffice-dummy3;windows_formatname=\"SO_DUMMYFORMAT_3\"", "SO_DUMMYFORMAT_3", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ //SotClipboardFormatId::DUMMY4
+ FormatEntry("application/x-openoffice-dummy4;windows_formatname=\"SO_DUMMYFORMAT_4\"", "SO_DUMMYFORMAT_4", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ // SotClipboardFormatId::RICHTEXT
+ FormatEntry("text/richtext", "Richtext Format", nullptr, CF_INVALID, CPPUTYPE_DEFAULT),
+ };
+
+namespace {
+
+void findDataFlavorForStandardFormatId( sal_Int32 aStandardFormatId, DataFlavor& aDataFlavor )
+{
+ /*
+ we stop search if we find the first CF_INVALID
+ because in the translation table the entries with a
+ standard clipboard format id appear before the other
+ entries with CF_INVALID
+ */
+ std::vector< FormatEntry >::const_iterator citer = std::find_if(g_TranslTable.begin(), g_TranslTable.end(),
+ [&aStandardFormatId](const FormatEntry& rEntry) {
+ return rEntry.aStandardFormatId == aStandardFormatId
+ || rEntry.aStandardFormatId == CF_INVALID;
+ });
+ if (citer != g_TranslTable.end() && citer->aStandardFormatId == aStandardFormatId)
+ aDataFlavor = citer->aDataFlavor;
+}
+
+void findDataFlavorForNativeFormatName( const OUString& aNativeFormatName, DataFlavor& aDataFlavor )
+{
+ std::vector< FormatEntry >::const_iterator citer = std::find_if(g_TranslTable.begin(), g_TranslTable.end(),
+ [&aNativeFormatName](const FormatEntry& rEntry) {
+ return aNativeFormatName.equalsIgnoreAsciiCase(rEntry.aNativeFormatName); });
+ if (citer != g_TranslTable.end())
+ aDataFlavor = citer->aDataFlavor;
+}
+
+void findStandardFormatIdForCharset( const OUString& aCharset, Any& aAny )
+{
+ if ( aCharset.equalsIgnoreAsciiCase( "utf-16" ) )
+ aAny <<= static_cast< sal_Int32 >( CF_UNICODETEXT );
+ else
+ {
+ sal_Int32 wincp = getWinCPFromMimeCharset( aCharset );
+ if ( IsOEMCP ( wincp ) )
+ aAny <<= static_cast< sal_Int32 >( CF_OEMTEXT );
+ }
+}
+
+void setStandardFormatIdForNativeFormatName( const OUString& aNativeFormatName, Any& aAny )
+{
+ std::vector< FormatEntry >::const_iterator citer = std::find_if(g_TranslTable.begin(), g_TranslTable.end(),
+ [&aNativeFormatName](const FormatEntry& rEntry) {
+ return aNativeFormatName.equalsIgnoreAsciiCase(rEntry.aNativeFormatName)
+ && (CF_INVALID != rEntry.aStandardFormatId);
+ });
+ if (citer != g_TranslTable.end())
+ aAny <<= citer->aStandardFormatId;
+}
+
+void findStdFormatIdOrNativeFormatNameForFullMediaType(
+ const Reference< XMimeContentTypeFactory >& aRefXMimeFactory,
+ const OUString& aFullMediaType,
+ Any& aAny )
+{
+ std::vector< FormatEntry >::const_iterator citer = std::find_if(g_TranslTable.begin(), g_TranslTable.end(),
+ [&aRefXMimeFactory, &aFullMediaType](const FormatEntry& rEntry) {
+ Reference<XMimeContentType> refXMime( aRefXMimeFactory->createMimeContentType(rEntry.aDataFlavor.MimeType) );
+ return aFullMediaType.equalsIgnoreAsciiCase(refXMime->getFullMediaType());
+ });
+ if (citer != g_TranslTable.end())
+ {
+ sal_Int32 cf = citer->aStandardFormatId;
+ if ( CF_INVALID != cf )
+ aAny <<= cf;
+ else
+ {
+ OSL_ENSURE( citer->aNativeFormatName.getLength( ),
+ "Invalid standard format id and empty native format name in translation table" );
+ aAny <<= citer->aNativeFormatName;
+ }
+ }
+}
+
+bool isTextPlainMediaType( std::u16string_view fullMediaType )
+{
+ return o3tl::equalsIgnoreAsciiCase(fullMediaType, u"text/plain");
+}
+
+DataFlavor mkDataFlv(const OUString& cnttype, const OUString& hpname, Type dtype)
+{
+ DataFlavor dflv;
+ dflv.MimeType = cnttype;
+ dflv.HumanPresentableName = hpname;
+ dflv.DataType = dtype;
+ return dflv;
+}
+
+}
+
+CDataFormatTranslatorUNO::CDataFormatTranslatorUNO( const Reference< XComponentContext >& rxContext ) :
+ m_xContext( rxContext )
+{
+}
+
+Any SAL_CALL CDataFormatTranslatorUNO::getSystemDataTypeFromDataFlavor( const DataFlavor& aDataFlavor )
+{
+ Any aAny;
+
+ try
+ {
+ Reference< XMimeContentTypeFactory > refXMimeCntFactory = MimeContentTypeFactory::create( m_xContext );
+
+ Reference< XMimeContentType >
+ refXMimeCntType( refXMimeCntFactory->createMimeContentType( aDataFlavor.MimeType ) );
+
+ OUString fullMediaType = refXMimeCntType->getFullMediaType( );
+ if ( isTextPlainMediaType( fullMediaType ) )
+ {
+ // default is CF_TEXT
+ aAny <<= static_cast< sal_Int32 >( CF_TEXT );
+
+ if ( refXMimeCntType->hasParameter( "charset" ) )
+ {
+ // but maybe it is unicode text or oem text
+ OUString charset = refXMimeCntType->getParameterValue( "charset" );
+ findStandardFormatIdForCharset( charset, aAny );
+ }
+ }
+ else
+ {
+ if (refXMimeCntType->hasParameter(Windows_FormatName))
+ {
+ OUString winfmtname = refXMimeCntType->getParameterValue(Windows_FormatName);
+ aAny <<= winfmtname;
+
+ setStandardFormatIdForNativeFormatName( winfmtname, aAny );
+ }
+ else
+ findStdFormatIdOrNativeFormatNameForFullMediaType( refXMimeCntFactory, fullMediaType, aAny );
+ }
+ }
+ catch( IllegalArgumentException& )
+ {
+ OSL_FAIL( "Invalid content-type detected!" );
+ }
+ catch( NoSuchElementException& )
+ {
+ OSL_FAIL( "Illegal content-type parameter" );
+ }
+ catch( ... )
+ {
+ OSL_FAIL( "Unexpected error" );
+ throw;
+ }
+
+ return aAny;
+}
+
+DataFlavor SAL_CALL CDataFormatTranslatorUNO::getDataFlavorFromSystemDataType( const Any& aSysDataType )
+{
+ OSL_PRECOND( aSysDataType.hasValue( ), "Empty system data type delivered" );
+
+ DataFlavor aFlavor = mkDataFlv( OUString(), OUString(), CPPUTYPE_SEQSALINT8 );
+
+ if ( aSysDataType.getValueType( ) == cppu::UnoType<sal_Int32>::get() )
+ {
+ sal_Int32 clipformat = CF_INVALID;
+ aSysDataType >>= clipformat;
+ if ( CF_INVALID != clipformat )
+ findDataFlavorForStandardFormatId( clipformat, aFlavor );
+ }
+ else if ( aSysDataType.getValueType( ) == cppu::UnoType<OUString>::get() )
+ {
+ OUString nativeFormatName;
+ aSysDataType >>= nativeFormatName;
+
+ findDataFlavorForNativeFormatName( nativeFormatName, aFlavor );
+ }
+ else
+ OSL_FAIL( "Invalid data type received" );
+
+ return aFlavor;
+}
+
+// XServiceInfo
+
+OUString SAL_CALL CDataFormatTranslatorUNO::getImplementationName( )
+{
+ return "com.sun.star.datatransfer.DataFormatTranslator";
+}
+
+sal_Bool SAL_CALL CDataFormatTranslatorUNO::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL CDataFormatTranslatorUNO::getSupportedServiceNames( )
+{
+ return { "com.sun.star.datatransfer.DataFormatTranslator" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+dtrans_CDataFormatTranslatorUNO_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new CDataFormatTranslatorUNO(context));
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/ftransl.hxx b/vcl/win/dtrans/ftransl.hxx
new file mode 100644
index 0000000000..7a5041be5f
--- /dev/null
+++ b/vcl/win/dtrans/ftransl.hxx
@@ -0,0 +1,59 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/datatransfer/XDataFormatTranslator.hpp>
+#include <com/sun/star/datatransfer/XMimeContentTypeFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include "WinClip.hxx"
+
+#include <vector>
+
+class CDataFormatTranslatorUNO : public
+ cppu::WeakImplHelper< css::datatransfer::XDataFormatTranslator,
+ css::lang::XServiceInfo >
+{
+
+public:
+ explicit CDataFormatTranslatorUNO( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ // XDataFormatTranslator
+
+ virtual css::uno::Any SAL_CALL getSystemDataTypeFromDataFlavor( const css::datatransfer::DataFlavor& aDataFlavor ) override;
+
+ virtual css::datatransfer::DataFlavor SAL_CALL getDataFlavorFromSystemDataType( const css::uno::Any& aSysDataType ) override;
+
+ // XServiceInfo
+
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+private:
+ const css::uno::Reference< css::uno::XComponentContext > m_xContext;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/generic_clipboard.cxx b/vcl/win/dtrans/generic_clipboard.cxx
new file mode 100644
index 0000000000..fd822f091e
--- /dev/null
+++ b/vcl/win/dtrans/generic_clipboard.cxx
@@ -0,0 +1,136 @@
+/* -*- 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 "generic_clipboard.hxx"
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/datatransfer/clipboard/RenderingCapabilities.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::datatransfer::clipboard;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace cppu;
+using namespace osl;
+
+using ::dtrans::GenericClipboard;
+
+GenericClipboard::GenericClipboard() :
+ m_bInitialized(false)
+{
+}
+
+GenericClipboard::~GenericClipboard()
+{
+}
+
+void SAL_CALL GenericClipboard::initialize( const Sequence< Any >& aArguments )
+{
+ if (!m_bInitialized)
+ {
+ for (Any const & arg : aArguments)
+ if (arg.getValueType() == cppu::UnoType<OUString>::get())
+ {
+ arg >>= m_aName;
+ break;
+ }
+ }
+}
+
+OUString SAL_CALL GenericClipboard::getImplementationName( )
+{
+ return "com.sun.star.comp.datatransfer.clipboard.GenericClipboard";
+}
+
+sal_Bool SAL_CALL GenericClipboard::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL GenericClipboard::getSupportedServiceNames( )
+{
+ return { "com.sun.star.datatransfer.clipboard.GenericClipboard" };
+}
+
+Reference< XTransferable > SAL_CALL GenericClipboard::getContents()
+{
+ std::unique_lock aGuard(m_aMutex);
+ return m_aContents;
+}
+
+void SAL_CALL GenericClipboard::setContents(const Reference< XTransferable >& xTrans,
+ const Reference< XClipboardOwner >& xClipboardOwner )
+{
+ // remember old values for callbacks before setting the new ones.
+ std::unique_lock aGuard(m_aMutex);
+
+ Reference< XClipboardOwner > oldOwner(m_aOwner);
+ m_aOwner = xClipboardOwner;
+
+ Reference< XTransferable > oldContents(m_aContents);
+ m_aContents = xTrans;
+
+ aGuard.unlock();
+
+ // notify old owner on loss of ownership
+ if( oldOwner.is() )
+ oldOwner->lostOwnership(static_cast < XClipboard * > (this), oldContents);
+
+ // notify all listeners on content changes
+ aGuard.lock();
+ ClipboardEvent aEvent(static_cast < XClipboard * > (this), m_aContents);
+ maClipboardListeners.notifyEach(aGuard, &XClipboardListener::changedContents, aEvent);
+}
+
+OUString SAL_CALL GenericClipboard::getName()
+{
+ return m_aName;
+}
+
+sal_Int8 SAL_CALL GenericClipboard::getRenderingCapabilities()
+{
+ return RenderingCapabilities::Delayed;
+}
+
+void SAL_CALL GenericClipboard::addClipboardListener( const Reference< XClipboardListener >& listener )
+{
+ std::unique_lock aGuard( m_aMutex );
+ OSL_ENSURE( !m_bDisposed, "object is disposed" );
+ if (!m_bDisposed)
+ maClipboardListeners.addInterface( aGuard, listener );
+}
+
+void SAL_CALL GenericClipboard::removeClipboardListener( const Reference< XClipboardListener >& listener )
+{
+ std::unique_lock aGuard( m_aMutex );
+ OSL_ENSURE( !m_bDisposed, "object is disposed" );
+ if (!m_bDisposed)
+ maClipboardListeners.removeInterface( aGuard, listener );
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+dtrans_GenericClipboard_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new GenericClipboard());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/generic_clipboard.hxx b/vcl/win/dtrans/generic_clipboard.hxx
new file mode 100644
index 0000000000..cc1ad976b3
--- /dev/null
+++ b/vcl/win/dtrans/generic_clipboard.hxx
@@ -0,0 +1,101 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <comphelper/compbase.hxx>
+
+#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp>
+
+#include <com/sun/star/datatransfer/clipboard/XClipboardNotifier.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+
+namespace dtrans
+{
+
+ class GenericClipboard : public ::comphelper::WeakComponentImplHelper <
+ css::datatransfer::clipboard::XClipboardEx,
+ css::datatransfer::clipboard::XClipboardNotifier,
+ css::lang::XServiceInfo,
+ css::lang::XInitialization >
+ {
+ OUString m_aName;
+
+ css::uno::Reference< css::datatransfer::XTransferable > m_aContents;
+ css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner > m_aOwner;
+ comphelper::OInterfaceContainerHelper4<css::datatransfer::clipboard::XClipboardListener> maClipboardListeners;
+
+ bool m_bInitialized;
+ virtual ~GenericClipboard() override;
+
+ public:
+
+ GenericClipboard();
+
+ /*
+ * XInitialization
+ */
+
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ /*
+ * XServiceInfo
+ */
+
+ virtual OUString SAL_CALL getImplementationName( ) override;
+
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ /*
+ * XClipboard
+ */
+
+ virtual css::uno::Reference< css::datatransfer::XTransferable > SAL_CALL getContents() override;
+
+ virtual void SAL_CALL setContents(
+ const css::uno::Reference< css::datatransfer::XTransferable >& xTrans,
+ const css::uno::Reference< css::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner ) override;
+
+ virtual OUString SAL_CALL getName() override;
+
+ /*
+ * XClipboardEx
+ */
+
+ virtual sal_Int8 SAL_CALL getRenderingCapabilities() override;
+
+ /*
+ * XClipboardNotifier
+ */
+
+ virtual void SAL_CALL addClipboardListener(
+ const css::uno::Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) override;
+
+ virtual void SAL_CALL removeClipboardListener(
+ const css::uno::Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) override;
+
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/globals.cxx b/vcl/win/dtrans/globals.cxx
new file mode 100644
index 0000000000..fcddef22e6
--- /dev/null
+++ b/vcl/win/dtrans/globals.cxx
@@ -0,0 +1,130 @@
+/* -*- 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/datatransfer/dnd/DNDConstants.hpp>
+
+#include "globals.hxx"
+
+//--> TRA
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+
+// used as shortcut when drag-source and drop-target are the same
+css::uno::Reference< css::datatransfer::XTransferable > g_XTransferable;
+
+//<-- TRA
+
+using namespace com::sun::star::datatransfer::dnd::DNDConstants;
+
+sal_Int8 dndOleKeysToAction( DWORD grfKeyState, sal_Int8 nSourceActions)
+{
+ sal_Int8 ret= 0;
+
+ // no MK_ALT, MK_CONTROL, MK_SHIFT
+ if( !(grfKeyState & MK_CONTROL) &&
+ !(grfKeyState & MK_ALT) &&
+ !(grfKeyState & MK_RBUTTON) &&
+ !(grfKeyState & MK_SHIFT))
+ {
+ if( nSourceActions & ACTION_MOVE )
+ {
+ ret= ACTION_DEFAULT | ACTION_MOVE;
+ }
+
+ else if( nSourceActions & ACTION_COPY )
+ {
+ ret= ACTION_COPY;
+ }
+
+ else if( nSourceActions & ACTION_LINK )
+ {
+ ret= ACTION_LINK;
+ }
+
+ else
+ ret = 0;
+ }
+ else if( grfKeyState & MK_SHIFT &&
+ !(grfKeyState & MK_CONTROL))
+ {
+ ret= ACTION_MOVE;
+ }
+ else if ( grfKeyState & MK_CONTROL &&
+ !(grfKeyState & MK_SHIFT) )
+ {
+ ret= ACTION_COPY;
+ }
+ else if ( grfKeyState & MK_CONTROL &&
+ grfKeyState & MK_SHIFT)
+ {
+ ret= ACTION_LINK;
+ }
+ else if ( grfKeyState & MK_RBUTTON ||
+ grfKeyState & MK_ALT)
+ {
+ ret= ACTION_COPY_OR_MOVE | ACTION_LINK;
+ }
+ return ret;
+}
+
+sal_Int8 dndOleDropEffectsToActions( DWORD dwEffect)
+{
+ sal_Int8 ret= ACTION_NONE;
+ if( dwEffect & DROPEFFECT_COPY)
+ ret |= ACTION_COPY;
+ if( dwEffect & DROPEFFECT_MOVE)
+ ret |= ACTION_MOVE;
+ if( dwEffect & DROPEFFECT_LINK)
+ ret |= ACTION_LINK;
+
+ return ret;
+}
+
+DWORD dndActionsToDropEffects( sal_Int8 actions)
+{
+ DWORD ret= DROPEFFECT_NONE;
+ if( actions & ACTION_MOVE)
+ ret |= DROPEFFECT_MOVE;
+ if( actions & ACTION_COPY)
+ ret |= DROPEFFECT_COPY;
+ if( actions & ACTION_LINK)
+ ret |= DROPEFFECT_LINK;
+ if( actions & ACTION_DEFAULT)
+ ret |= DROPEFFECT_COPY;
+ return ret;
+}
+
+DWORD dndActionsToSingleDropEffect( sal_Int8 actions)
+{
+ DWORD effects= dndActionsToDropEffects( actions);
+
+ sal_Int8 countEffect= 0;
+
+ if( effects & DROPEFFECT_MOVE)
+ countEffect++;
+ if( effects & DROPEFFECT_COPY)
+ countEffect++;
+ if( effects & DROPEFFECT_LINK)
+ countEffect++;
+
+ // DROPEFFECT_MOVE is the default effect
+ DWORD retVal= countEffect > 1 ? DROPEFFECT_MOVE : effects;
+ return retVal;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/globals.hxx b/vcl/win/dtrans/globals.hxx
new file mode 100644
index 0000000000..9bb174d0b6
--- /dev/null
+++ b/vcl/win/dtrans/globals.hxx
@@ -0,0 +1,70 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <osl/mutex.hxx>
+
+namespace com::sun::star::datatransfer
+{
+class XTransferable;
+}
+
+#include <wtypes.h>
+#include <sal/types.h>
+
+// This maps key states as occur as parameter, e.g. in IDropTarget::DragEnter,
+// IDropSource::QueryContinueDrag, to actions as are declared in
+// css::datatransfer::dnd::DNDConstants ( ACTION_MOVE etc).
+// If the grfKeyState indicates the ALt or right mousebutton then the returned
+// values combines all possible actions. This is because those keys and buttons are
+// used when the user expect a menu to appear when he drops. The menu then
+// contains entries, such as move, copy, link, cancel.
+// An odd fact is that the argument grfKeyState in IDropTarget::Drop does not
+// contain mouse buttons (winnt 4 SP6). That indicates that the right mouse button
+// is not considered relevant in a drag operation. Contrarily the file explorer
+// gives that button a special meaning: the user has to select the effect from
+// a context menu on drop.
+sal_Int8 dndOleKeysToAction(DWORD grfKeyState, sal_Int8 sourceActions);
+
+// The function maps a windows DROPEFFECTs to actions
+// ( css::datatransfer::dnd::DNDConstants).
+// The argument can be a combination of different DROPEFFECTS,
+// In that case the return value is also a combination of the
+// appropriate actions.
+sal_Int8 dndOleDropEffectsToActions(DWORD dwEffect);
+
+// The function maps actions ( css::datatransfer::dnd::DNDConstants)
+// to window DROPEFFECTs.
+// The argument can be a combination of different actions
+// In that case the return value is also a combination of the
+// appropriate DROPEFFECTS.
+DWORD dndActionsToDropEffects(sal_Int8 actions);
+
+// If the argument constitutes only one action then it is mapped to the
+// corresponding DROPEFFECT otherwise DROPEFFECT_MOVE is returned. This is
+// why move is the default effect (no modifiers pressed, or right mouse button
+// or Alt).
+DWORD dndActionsToSingleDropEffect(sal_Int8 actions);
+
+extern css::uno::Reference<css::datatransfer::XTransferable> g_XTransferable;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/idroptarget.cxx b/vcl/win/dtrans/idroptarget.cxx
new file mode 100644
index 0000000000..8b403eb7a3
--- /dev/null
+++ b/vcl/win/dtrans/idroptarget.cxx
@@ -0,0 +1,96 @@
+/* -*- 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 "idroptarget.hxx"
+
+IDropTargetImpl::IDropTargetImpl( DropTarget& pTarget): m_nRefCount( 0),
+ m_rDropTarget( pTarget)
+{
+}
+
+IDropTargetImpl::~IDropTargetImpl()
+{
+}
+
+//IDropTarget
+HRESULT STDMETHODCALLTYPE IDropTargetImpl::QueryInterface( REFIID riid, void **ppvObject)
+{
+ if( !ppvObject)
+ return E_POINTER;
+ *ppvObject= nullptr;
+
+ if( riid == __uuidof( IUnknown))
+ *ppvObject= static_cast<IUnknown*>( this);
+ else if ( riid == __uuidof( IDropTarget))
+ *ppvObject= static_cast<IDropTarget*>( this);
+
+ if(*ppvObject)
+ {
+ AddRef();
+ return S_OK;
+ }
+ else
+ return E_NOINTERFACE;
+
+}
+
+ULONG STDMETHODCALLTYPE IDropTargetImpl::AddRef()
+{
+ return InterlockedIncrement( &m_nRefCount);
+}
+
+ULONG STDMETHODCALLTYPE IDropTargetImpl::Release()
+{
+ LONG count= InterlockedDecrement( &m_nRefCount);
+ if( m_nRefCount == 0 )
+ delete this;
+ return count;
+}
+
+STDMETHODIMP IDropTargetImpl::DragEnter( IDataObject __RPC_FAR *pDataObj,
+ DWORD grfKeyState,
+ POINTL pt,
+ DWORD *pdwEffect)
+{
+ return m_rDropTarget.DragEnter( pDataObj, grfKeyState,
+ pt, pdwEffect);
+}
+
+STDMETHODIMP IDropTargetImpl::DragOver( DWORD grfKeyState,
+ POINTL pt,
+ DWORD *pdwEffect)
+{
+ return m_rDropTarget.DragOver( grfKeyState, pt, pdwEffect);
+}
+
+STDMETHODIMP IDropTargetImpl::DragLeave()
+{
+ return m_rDropTarget.DragLeave();
+}
+
+STDMETHODIMP IDropTargetImpl::Drop( IDataObject *pDataObj,
+ DWORD grfKeyState,
+ POINTL pt,
+ DWORD __RPC_FAR *pdwEffect)
+{
+ return m_rDropTarget.Drop( pDataObj, grfKeyState,
+ pt, pdwEffect);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/idroptarget.hxx b/vcl/win/dtrans/idroptarget.hxx
new file mode 100644
index 0000000000..5871373c6c
--- /dev/null
+++ b/vcl/win/dtrans/idroptarget.hxx
@@ -0,0 +1,65 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <win/dnd_target.hxx>
+
+class IDropTargetImpl: public IDropTarget
+{
+ LONG m_nRefCount;
+ // Calls to IDropTarget functions are delegated to a DropTarget.
+ DropTarget& m_rDropTarget;
+
+ virtual ~IDropTargetImpl(); // delete is only called by IUnknown::Release
+ IDropTargetImpl( const IDropTargetImpl& );
+ IDropTargetImpl& operator=( const IDropTargetImpl& );
+public:
+ explicit IDropTargetImpl(DropTarget& rTarget);
+
+ // IDropTarget
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(
+ /* [in] */ REFIID riid,
+ /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject) override;
+
+ virtual ULONG STDMETHODCALLTYPE AddRef( ) override;
+
+ virtual ULONG STDMETHODCALLTYPE Release( ) override;
+
+ virtual HRESULT STDMETHODCALLTYPE DragEnter(
+ /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
+ /* [in] */ DWORD grfKeyState,
+ /* [in] */ POINTL pt,
+ /* [out][in] */ DWORD __RPC_FAR *pdwEffect) override;
+
+ virtual HRESULT STDMETHODCALLTYPE DragOver(
+ /* [in] */ DWORD grfKeyState,
+ /* [in] */ POINTL pt,
+ /* [out][in] */ DWORD __RPC_FAR *pdwEffect) override;
+
+ virtual HRESULT STDMETHODCALLTYPE DragLeave( ) override;
+
+ virtual HRESULT STDMETHODCALLTYPE Drop(
+ /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
+ /* [in] */ DWORD grfKeyState,
+ /* [in] */ POINTL pt,
+ /* [out][in] */ DWORD __RPC_FAR *pdwEffect) override;
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/source.cxx b/vcl/win/dtrans/source.cxx
new file mode 100644
index 0000000000..94124b23dd
--- /dev/null
+++ b/vcl/win/dtrans/source.cxx
@@ -0,0 +1,373 @@
+/* -*- 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/datatransfer/dnd/DNDConstants.hpp>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/awt/MouseEvent.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/any.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <process.h>
+#include <memory>
+
+#include <win/dnd_source.hxx>
+#include "globals.hxx"
+#include "sourcecontext.hxx"
+#include "DtObjFactory.hxx"
+
+#include <rtl/ustring.h>
+#include <osl/thread.h>
+#include <winuser.h>
+#include <stdio.h>
+
+using namespace cppu;
+using namespace osl;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::datatransfer::dnd;
+using namespace com::sun::star::datatransfer::dnd::DNDConstants;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::awt::MouseButton;
+using namespace com::sun::star::awt;
+using namespace com::sun::star::lang;
+
+static unsigned __stdcall DndOleSTAFunc(void* pParams);
+
+DragSource::DragSource( const Reference<XComponentContext>& rxContext):
+ WeakComponentImplHelper< XDragSource, XInitialization, XServiceInfo >(m_aMutex),
+ m_xContext( rxContext ),
+// m_pcurrentContext_impl(0),
+ m_hAppWindow(nullptr),
+ m_MouseButton(0),
+ m_RunningDndOperationCount(0)
+{
+}
+
+DragSource::~DragSource()
+{
+}
+
+/** First start a new drag and drop thread if
+ the last one has finished
+
+ ????
+ Do we really need a separate thread for
+ every Dnd operation or only if the source
+ thread is an MTA thread
+ ????
+*/
+void DragSource::StartDragImpl(
+ const DragGestureEvent& trigger,
+ sal_Int8 sourceActions,
+ sal_Int32 /*cursor*/,
+ sal_Int32 /*image*/,
+ const Reference<XTransferable >& trans,
+ const Reference<XDragSourceListener >& listener )
+{
+ // The actions supported by the drag source
+ m_sourceActions= sourceActions;
+ // We need to know which mouse button triggered the operation.
+ // If it was the left one, then the drop occurs when that button
+ // has been released and if it was the right one then the drop
+ // occurs when the right button has been released. If the event is not
+ // set then we assume that the left button is pressed.
+ MouseEvent evtMouse;
+ trigger.Event >>= evtMouse;
+ m_MouseButton= evtMouse.Buttons;
+
+ // The SourceContext class administers the XDragSourceListener s and
+ // fires events to them. An instance only exists in the scope of this
+ // function. However, the drag and drop operation causes callbacks
+ // to the IDropSource interface implemented in this class (but only
+ // while this function executes). The source context is also used
+ // in DragSource::QueryContinueDrag.
+ m_currentContext = new SourceContext(this, listener);
+
+ // Convert the XTransferable data object into an IDataObject object;
+
+ //--> TRA
+ g_XTransferable = trans;
+ //<-- TRA
+
+ m_spDataObject= CDTransObjFactory::createDataObjFromTransferable(
+ m_xContext, trans);
+
+ // Obtain the id of the thread that created the window
+ DWORD processId;
+ m_threadIdWindow= GetWindowThreadProcessId( m_hAppWindow, &processId);
+
+ // hold the instance for the DnD thread, it's too late
+ // to acquire at the start of the thread procedure
+ // the thread procedure is responsible for the release
+ acquire();
+
+ // The thread accesses members of this instance but does not call acquire.
+ // Hopefully this instance is not destroyed before the thread has terminated.
+ HANDLE hThread
+ = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, DndOleSTAFunc, this, 0, nullptr));
+
+ // detach from thread
+ CloseHandle(hThread);
+}
+
+// XInitialization
+/** aArguments contains a machine id */
+void SAL_CALL DragSource::initialize( const Sequence< Any >& aArguments )
+{
+ if( aArguments.getLength() >=2)
+ m_hAppWindow= reinterpret_cast<HWND>(static_cast<sal_uIntPtr>(*o3tl::doAccess<sal_uInt64>(aArguments[1])));
+ OSL_ASSERT( IsWindow( m_hAppWindow) );
+}
+
+/** XDragSource */
+sal_Bool SAL_CALL DragSource::isDragImageSupported( )
+{
+ return false;
+}
+
+sal_Int32 SAL_CALL DragSource::getDefaultCursor( sal_Int8 /*dragAction*/ )
+{
+ return 0;
+}
+
+/** Notifies the XDragSourceListener by
+ calling dragDropEnd */
+void SAL_CALL DragSource::startDrag(
+ const DragGestureEvent& trigger,
+ sal_Int8 sourceActions,
+ sal_Int32 cursor,
+ sal_Int32 image,
+ const Reference<XTransferable >& trans,
+ const Reference<XDragSourceListener >& listener )
+{
+ // Allow only one running dnd operation at a time,
+ // see XDragSource documentation
+
+ LONG cnt = InterlockedIncrement(&m_RunningDndOperationCount);
+
+ if (1 == cnt)
+ {
+ StartDragImpl(trigger, sourceActions, cursor, image, trans, listener);
+ }
+ else
+ {
+ cnt = InterlockedDecrement(&m_RunningDndOperationCount);
+
+ DragSourceDropEvent dsde;
+
+ dsde.DropAction = ACTION_NONE;
+ dsde.DropSuccess = false;
+
+ try
+ {
+ listener->dragDropEnd(dsde);
+ }
+ catch(RuntimeException&)
+ {
+ TOOLS_WARN_EXCEPTION( "vcl", "Runtime exception during event dispatching");
+ }
+ }
+}
+
+/** IDropTarget */
+HRESULT STDMETHODCALLTYPE DragSource::QueryInterface( REFIID riid, void **ppvObject)
+{
+ if( !ppvObject)
+ return E_POINTER;
+ *ppvObject= nullptr;
+
+ if( riid == __uuidof( IUnknown) )
+ *ppvObject= static_cast<IUnknown*>( this);
+ else if ( riid == __uuidof( IDropSource) )
+ *ppvObject= static_cast<IDropSource*>( this);
+
+ if(*ppvObject)
+ {
+ AddRef();
+ return S_OK;
+ }
+ else
+ return E_NOINTERFACE;
+
+}
+
+ULONG STDMETHODCALLTYPE DragSource::AddRef()
+{
+ acquire();
+ return static_cast<ULONG>(m_refCount);
+}
+
+ULONG STDMETHODCALLTYPE DragSource::Release()
+{
+ ULONG ref= m_refCount;
+ release();
+ return --ref;
+}
+
+/** IDropSource */
+HRESULT STDMETHODCALLTYPE DragSource::QueryContinueDrag(
+/* [in] */ BOOL fEscapePressed,
+/* [in] */ DWORD grfKeyState)
+{
+#if defined DBG_CONSOLE_OUT
+ printf("\nDragSource::QueryContinueDrag");
+#endif
+
+ HRESULT retVal= S_OK; // default continue DnD
+
+ if (fEscapePressed)
+ {
+ retVal= DRAGDROP_S_CANCEL;
+ }
+ else
+ {
+ if( ( m_MouseButton == MouseButton::RIGHT && !(grfKeyState & MK_RBUTTON) ) ||
+ ( m_MouseButton == MouseButton::MIDDLE && !(grfKeyState & MK_MBUTTON) ) ||
+ ( m_MouseButton == MouseButton::LEFT && !(grfKeyState & MK_LBUTTON) ) ||
+ ( m_MouseButton == 0 && !(grfKeyState & MK_LBUTTON) ) )
+ {
+ retVal= DRAGDROP_S_DROP;
+ }
+ }
+
+ // fire dropActionChanged event.
+ // this is actually done by the context, which also detects whether the action
+ // changed at all
+ sal_Int8 dropAction= fEscapePressed ? ACTION_NONE :
+ dndOleKeysToAction( grfKeyState, m_sourceActions);
+
+ sal_Int8 userAction= fEscapePressed ? ACTION_NONE :
+ dndOleKeysToAction( grfKeyState, -1 );
+
+ static_cast<SourceContext*>(m_currentContext.get())->fire_dropActionChanged(
+ dropAction, userAction);
+
+ return retVal;
+}
+
+HRESULT STDMETHODCALLTYPE DragSource::GiveFeedback(
+/* [in] */ DWORD
+#if defined DBG_CONSOLE_OUT
+dwEffect
+#endif
+)
+{
+#if defined DBG_CONSOLE_OUT
+ printf("\nDragSource::GiveFeedback %d", dwEffect);
+#endif
+
+ return DRAGDROP_S_USEDEFAULTCURSORS;
+}
+
+// XServiceInfo
+OUString SAL_CALL DragSource::getImplementationName( )
+{
+ return "com.sun.star.comp.datatransfer.dnd.OleDragSource_V1";
+}
+// XServiceInfo
+sal_Bool SAL_CALL DragSource::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL DragSource::getSupportedServiceNames( )
+{
+ return { "com.sun.star.datatransfer.dnd.OleDragSource" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+dtrans_DragSource_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new DragSource(context));
+}
+
+/** This function is called as extra thread from
+ DragSource::executeDrag. The function
+ carries out a drag and drop operation by calling
+ DoDragDrop. The thread also notifies all
+ XSourceListener. */
+unsigned __stdcall DndOleSTAFunc(void* pParams)
+{
+ osl_setThreadName("DragSource DndOleSTAFunc");
+
+ // The structure contains all arguments for DoDragDrop and other
+ DragSource *pSource= static_cast<DragSource*>(pParams);
+
+ // Drag and drop only works in a thread in which OleInitialize is called.
+ HRESULT hr= OleInitialize( nullptr);
+
+ if(SUCCEEDED(hr))
+ {
+ // We force the creation of a thread message queue. This is necessary
+ // for a later call to AttachThreadInput
+ MSG msgtemp;
+ PeekMessageW( &msgtemp, nullptr, WM_USER, WM_USER, PM_NOREMOVE);
+
+ DWORD threadId= GetCurrentThreadId();
+
+ // This thread is attached to the thread that created the window. Hence
+ // this thread also receives all mouse and keyboard messages which are
+ // needed by DoDragDrop
+ AttachThreadInput( threadId , pSource->m_threadIdWindow, TRUE );
+
+ DWORD dwEffect= 0;
+ hr= DoDragDrop(
+ pSource->m_spDataObject.get(),
+ static_cast<IDropSource*>(pSource),
+ dndActionsToDropEffects( pSource->m_sourceActions),
+ &dwEffect);
+
+ // #105428 detach my message queue from the other threads
+ // message queue before calling fire_dragDropEnd else
+ // the office may appear to hang sometimes
+ AttachThreadInput( threadId, pSource->m_threadIdWindow, FALSE);
+
+ //--> TRA
+ // clear the global transferable again
+ g_XTransferable.clear();
+ //<-- TRA
+
+ OSL_ENSURE( hr != E_INVALIDARG, "IDataObject impl does not contain valid data");
+
+ //Fire event
+ sal_Int8 action= hr == DRAGDROP_S_DROP ? dndOleDropEffectsToActions( dwEffect) : ACTION_NONE;
+
+ static_cast<SourceContext*>(pSource->m_currentContext.get())->fire_dragDropEnd(
+ hr == DRAGDROP_S_DROP, action);
+
+ // Destroy SourceContextslkfgj
+ pSource->m_currentContext= nullptr;
+ // Destroy the XTransferable wrapper
+ pSource->m_spDataObject=nullptr;
+
+ OleUninitialize();
+ }
+
+ InterlockedDecrement(&pSource->m_RunningDndOperationCount);
+
+ // the DragSource was manually acquired by
+ // thread starting method DelayedStartDrag
+ pSource->release();
+
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/sourcecontext.cxx b/vcl/win/dtrans/sourcecontext.cxx
new file mode 100644
index 0000000000..c0e6371c5b
--- /dev/null
+++ b/vcl/win/dtrans/sourcecontext.cxx
@@ -0,0 +1,134 @@
+/* -*- 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/datatransfer/dnd/DNDConstants.hpp>
+
+#include "sourcecontext.hxx"
+
+using namespace com::sun::star::datatransfer::dnd;
+using namespace com::sun::star::datatransfer::dnd::DNDConstants;
+
+SourceContext::SourceContext( DragSource* pSource,
+ const Reference<XDragSourceListener>& listener):
+ WeakComponentImplHelper<XDragSourceContext>(m_aMutex),
+ m_pDragSource( pSource),
+ m_dragSource( static_cast<XDragSource*>( m_pDragSource) )
+{
+#if OSL_DEBUG_LEVEL > 1
+ if( listener.is())
+#endif
+ rBHelper.addListener( cppu::UnoType<decltype(listener)>::get(), listener );
+}
+
+SourceContext::~SourceContext()
+{
+}
+
+void SourceContext::addDragSourceListener(
+ const Reference<XDragSourceListener >& )
+{
+}
+
+void SourceContext::removeDragSourceListener(
+ const Reference<XDragSourceListener >& )
+{
+}
+
+sal_Int32 SAL_CALL SourceContext::getCurrentCursor( )
+{
+ return 0;
+}
+
+void SAL_CALL SourceContext::setCursor( sal_Int32 /*cursorId*/ )
+{
+}
+
+void SAL_CALL SourceContext::setImage( sal_Int32 /*imageId*/ )
+{
+}
+
+void SAL_CALL SourceContext::transferablesFlavorsChanged( )
+{
+}
+
+// non -interface functions
+// Fires XDragSourceListener::dragDropEnd events.
+void SourceContext::fire_dragDropEnd( bool success, sal_Int8 effect)
+{
+
+ DragSourceDropEvent e;
+
+ if( success )
+ {
+ e.DropAction= effect;
+ e.DropSuccess= true;
+ }
+ else
+ {
+ e.DropAction= ACTION_NONE;
+ e.DropSuccess= false;
+ }
+ e.DragSource= m_dragSource;
+ e.DragSourceContext= static_cast<XDragSourceContext*>( this);
+ e.Source.set( static_cast<XDragSourceContext*>( this), UNO_QUERY);
+
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer(
+ cppu::UnoType<XDragSourceListener>::get());
+
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer);
+ while( iter.hasMoreElements())
+ {
+ Reference<XDragSourceListener> listener(
+ static_cast<XDragSourceListener*>( iter.next()));
+ listener->dragDropEnd( e);
+ }
+ }
+}
+
+void SourceContext::fire_dropActionChanged( sal_Int8 dropAction, sal_Int8 userAction)
+{
+ if( m_currentAction != dropAction)
+ {
+ m_currentAction= dropAction;
+ DragSourceDragEvent e;
+ e.DropAction= dropAction;
+ e.UserAction= userAction;
+ e.DragSource= m_dragSource;
+ e.DragSourceContext= static_cast<XDragSourceContext*>( this);
+ e.Source.set( static_cast<XDragSourceContext*>( this), UNO_QUERY);
+
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer(
+ cppu::UnoType<XDragSourceListener>::get());
+
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer);
+ while( iter.hasMoreElements())
+ {
+ Reference<XDragSourceListener> listener(
+ static_cast<XDragSourceListener*>( iter.next()));
+ listener->dropActionChanged( e);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/sourcecontext.hxx b/vcl/win/dtrans/sourcecontext.hxx
new file mode 100644
index 0000000000..4471747956
--- /dev/null
+++ b/vcl/win/dtrans/sourcecontext.hxx
@@ -0,0 +1,66 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <com/sun/star/datatransfer/dnd/XDragSourceContext.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+#include <win/dnd_source.hxx>
+
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::datatransfer::dnd;
+using namespace ::cppu;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+// This class fires events to XDragSourceListener implementations.
+// Of that interface only dragDropEnd and dropActionChanged are called.
+// The functions dragEnter, dragExit and dragOver are not supported
+// currently.
+// An instance of SourceContext only lives as long as the drag and drop
+// operation lasts.
+class SourceContext : public cppu::BaseMutex, public WeakComponentImplHelper<XDragSourceContext>
+{
+ DragSource* m_pDragSource;
+ Reference<XDragSource> m_dragSource;
+ // the action ( copy, move etc)
+ sal_Int8 m_currentAction;
+
+public:
+ SourceContext(DragSource* pSource, const Reference<XDragSourceListener>& listener);
+ ~SourceContext() override;
+ SourceContext(const SourceContext&) = delete;
+ SourceContext& operator=(const SourceContext&) = delete;
+
+ /// @throws RuntimeException
+ virtual void addDragSourceListener(const Reference<XDragSourceListener>& dsl);
+ /// @throws RuntimeException
+ virtual void removeDragSourceListener(const Reference<XDragSourceListener>& dsl);
+ virtual sal_Int32 SAL_CALL getCurrentCursor() override;
+ virtual void SAL_CALL setCursor(sal_Int32 cursorId) override;
+ virtual void SAL_CALL setImage(sal_Int32 imageId) override;
+ virtual void SAL_CALL transferablesFlavorsChanged() override;
+
+ // non - interface functions
+ void fire_dragDropEnd(bool success, sal_Int8 byte);
+ void fire_dropActionChanged(sal_Int8 dropAction, sal_Int8 userAction);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/target.cxx b/vcl/win/dtrans/target.cxx
new file mode 100644
index 0000000000..abe53379ea
--- /dev/null
+++ b/vcl/win/dtrans/target.cxx
@@ -0,0 +1,646 @@
+/* -*- 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/datatransfer/dnd/DNDConstants.hpp>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/any.hxx>
+
+#include <stdio.h>
+#include <win/dnd_target.hxx>
+#include "idroptarget.hxx"
+#include "globals.hxx"
+#include "targetdropcontext.hxx"
+#include "targetdragcontext.hxx"
+#include <rtl/ustring.h>
+#include <osl/thread.h>
+#include <sal/log.hxx>
+#include <comphelper/windowserrorstring.hxx>
+
+#include "DOTransferable.hxx"
+
+using namespace cppu;
+using namespace osl;
+using namespace com::sun::star::datatransfer;
+using namespace com::sun::star::datatransfer::dnd;
+using namespace com::sun::star::datatransfer::dnd::DNDConstants;
+
+#define WM_REGISTERDRAGDROP WM_USER + 1
+#define WM_REVOKEDRAGDROP WM_USER + 2
+
+unsigned __stdcall DndTargetOleSTAFunc(void* pParams);
+
+DropTarget::DropTarget( const Reference<XComponentContext>& rxContext):
+ WeakComponentImplHelper<XInitialization,XDropTarget, XServiceInfo>(m_aMutex),
+ m_hWnd( nullptr),
+ m_threadIdWindow(0),
+ m_threadIdTarget(0),
+ m_hOleThread(nullptr),
+ m_oleThreadId( 0),
+ m_pDropTarget( nullptr),
+ m_xContext( rxContext ),
+ m_bActive(true),
+ m_nDefaultActions(ACTION_COPY|ACTION_MOVE|ACTION_LINK|ACTION_DEFAULT),
+ m_nCurrentDropAction( ACTION_NONE),
+ m_nLastDropAction(0),
+ m_bDropComplete(false)
+{
+}
+
+DropTarget::~DropTarget()
+{
+}
+// called from WeakComponentImplHelperX::dispose
+// WeakComponentImplHelper calls disposing before it destroys
+// itself.
+// NOTE: RevokeDragDrop decrements the ref count on the IDropTarget
+// interface. (m_pDropTarget)
+// If the HWND is invalid then it doesn't decrement and
+// the IDropTarget object will live on. MEMORY LEAK
+void SAL_CALL DropTarget::disposing()
+{
+ if( m_threadIdTarget)
+ {
+ // Call RevokeDragDrop and wait for the OLE thread to die;
+ PostThreadMessageW( m_threadIdTarget, WM_REVOKEDRAGDROP, reinterpret_cast<WPARAM>(this), 0);
+ WaitForSingleObject( m_hOleThread, INFINITE);
+ CloseHandle( m_hOleThread);
+ //OSL_ENSURE( SUCCEEDED( hr), "HWND not valid!" );
+ }
+ else
+ {
+ RevokeDragDrop( m_hWnd);
+ m_hWnd= nullptr;
+ }
+ if( m_pDropTarget)
+ {
+ CoLockObjectExternal( m_pDropTarget, FALSE, TRUE);
+ m_pDropTarget->Release();
+ m_pDropTarget = nullptr;
+ }
+
+ if( m_oleThreadId)
+ {
+ if( m_oleThreadId == CoGetCurrentProcess() )
+ OleUninitialize();
+ }
+
+}
+
+void SAL_CALL DropTarget::initialize( const Sequence< Any >& aArguments )
+{
+ // The window must be registered for Dnd by RegisterDragDrop. We must ensure
+ // that RegisterDragDrop is called from an STA ( OleInitialize) thread.
+ // As long as the window is registered we need to receive OLE messages in
+ // an OLE thread. That is to say, if DropTarget::initialize was called from an
+ // MTA thread then we create an OLE thread in which the window is registered.
+ // The thread will stay alive until aver RevokeDragDrop has been called.
+
+ // Additionally even if RegisterDragDrop is called from an STA thread we have
+ // to ensure that it is called from the same thread that created the Window
+ // otherwise messages sent during DND won't reach the windows message queue.
+ // Calling AttachThreadInput first would resolve this problem but would block
+ // the message queue of the calling thread. So if the current thread
+ // (even if it's an STA thread) and the thread that created the window are not
+ // identical we need to create a new thread as we do when the calling thread is
+ // an MTA thread.
+
+ if( aArguments.getLength() > 0)
+ {
+ // Get the window handle from aArgument. It is needed for RegisterDragDrop.
+ m_hWnd= reinterpret_cast<HWND>(static_cast<sal_uIntPtr>(*o3tl::doAccess<sal_uInt64>(aArguments[0])));
+ OSL_ASSERT( IsWindow( m_hWnd) );
+
+ // Obtain the id of the thread that created the window
+ m_threadIdWindow= GetWindowThreadProcessId( m_hWnd, nullptr);
+
+ HRESULT hr= OleInitialize( nullptr);
+
+ // Current thread is MTA or Current thread and Window thread are not identical
+ if( hr == RPC_E_CHANGED_MODE || GetCurrentThreadId() != m_threadIdWindow )
+ {
+ OSL_ENSURE( ! m_threadIdTarget,"initialize was called twice");
+ // create the IDropTargetImplementation
+ m_pDropTarget= new IDropTargetImpl( *this );
+ m_pDropTarget->AddRef();
+
+ // Obtain the id of the thread that created the window
+ m_threadIdWindow= GetWindowThreadProcessId( m_hWnd, nullptr);
+ // The event is set by the thread that we will create momentarily.
+ // It indicates that the thread is ready to receive messages.
+ HANDLE m_evtThreadReady= CreateEventW( nullptr, FALSE, FALSE, nullptr);
+
+ m_hOleThread = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, DndTargetOleSTAFunc,
+ &m_evtThreadReady, 0, &m_threadIdTarget));
+ WaitForSingleObject( m_evtThreadReady, INFINITE);
+ CloseHandle( m_evtThreadReady);
+ PostThreadMessageW( m_threadIdTarget, WM_REGISTERDRAGDROP, reinterpret_cast<WPARAM>(this), 0);
+ }
+ else if( hr == S_OK || hr == S_FALSE)
+ {
+ // current thread is STA
+ // If OleInitialize has been called by the caller then we must not call
+ // OleUninitialize
+ if( hr == S_OK)
+ {
+ // caller did not call OleInitialize, so we call OleUninitialize
+ // remember the thread that will call OleUninitialize
+ m_oleThreadId= CoGetCurrentProcess(); // get a unique thread id
+ }
+
+ // Get the window handle from aArgument. It is needed for RegisterDragDrop.
+ // create the IDropTargetImplementation
+ m_pDropTarget= new IDropTargetImpl( *this );
+ m_pDropTarget->AddRef();
+ // CoLockObjectExternal is prescribed by the protocol. It bumps up the ref count
+ if( SUCCEEDED( CoLockObjectExternal( m_pDropTarget, TRUE, FALSE)))
+ {
+ if( FAILED( RegisterDragDrop( m_hWnd, m_pDropTarget) ) )
+ {
+ // do clean up if drag and drop is not possible
+ CoLockObjectExternal( m_pDropTarget, FALSE, FALSE);
+ m_pDropTarget->Release();
+ m_pDropTarget = nullptr;
+ m_hWnd= nullptr;
+ }
+ }
+ }
+ else
+ throw Exception("OleInitialize failed with " + OUString::number(hr), nullptr);
+
+ }
+}
+
+// This function is called as extra thread from DragSource::startDrag.
+// The function carries out a drag and drop operation by calling
+// DoDragDrop. The thread also notifies all XSourceListener.
+unsigned __stdcall DndTargetOleSTAFunc(void* pParams)
+{
+ osl_setThreadName("DropTarget DndTargetOleSTAFunc");
+
+ HRESULT hr= OleInitialize( nullptr);
+ if( SUCCEEDED( hr) )
+ {
+ MSG msg;
+ // force the creation of a message queue
+ PeekMessageW( &msg, nullptr, 0, 0, PM_NOREMOVE);
+ // Signal the creator ( DropTarget::initialize) that the thread is
+ // ready to receive messages.
+ SetEvent( *static_cast<HANDLE*>(pParams));
+ // Thread id is needed for attaching this message queue to the one of the
+ // thread where the window was created.
+ DWORD threadId= GetCurrentThreadId();
+ // We force the creation of a thread message queue. This is necessary
+ // for a later call to AttachThreadInput
+ for (;;)
+ {
+ int const bRet = GetMessageW(&msg, nullptr, 0, 0);
+ if (bRet == 0)
+ {
+ break;
+ }
+ if (-1 == bRet)
+ {
+ SAL_WARN("vcl.win.dtrans", "GetMessageW failed: " << WindowsErrorString(GetLastError()));
+ break;
+ }
+ if( msg.message == WM_REGISTERDRAGDROP)
+ {
+ DropTarget *pTarget= reinterpret_cast<DropTarget*>(msg.wParam);
+ // This thread is attached to the thread that created the window. Hence
+ // this thread also receives all mouse and keyboard messages which are
+ // needed
+ AttachThreadInput( threadId , pTarget->m_threadIdWindow, TRUE );
+
+ if( SUCCEEDED( CoLockObjectExternal(pTarget-> m_pDropTarget, TRUE, FALSE)))
+ {
+ if( FAILED( RegisterDragDrop( pTarget-> m_hWnd, pTarget-> m_pDropTarget) ) )
+ {
+ // do clean up if drag and drop is not possible
+ CoLockObjectExternal( pTarget->m_pDropTarget, FALSE, FALSE);
+ pTarget->m_pDropTarget->Release();
+ pTarget->m_pDropTarget = nullptr;
+ pTarget->m_hWnd= nullptr;
+ }
+ }
+ }
+ else if( msg.message == WM_REVOKEDRAGDROP)
+ {
+ DropTarget *pTarget= reinterpret_cast<DropTarget*>(msg.wParam);
+ RevokeDragDrop( pTarget-> m_hWnd);
+ // Detach this thread from the window thread
+ AttachThreadInput( threadId, pTarget->m_threadIdWindow, FALSE);
+ pTarget->m_hWnd= nullptr;
+ break;
+ }
+ TranslateMessage( &msg);
+ DispatchMessageW( &msg);
+ }
+ OleUninitialize();
+ }
+ return 0;
+}
+
+// XServiceInfo
+OUString SAL_CALL DropTarget::getImplementationName( )
+{
+ return "com.sun.star.comp.datatransfer.dnd.OleDropTarget_V1";
+}
+// XServiceInfo
+sal_Bool SAL_CALL DropTarget::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL DropTarget::getSupportedServiceNames( )
+{
+ return { "com.sun.star.datatransfer.dnd.OleDropTarget" };
+}
+
+// XDropTarget
+void SAL_CALL DropTarget::addDropTargetListener( const Reference< XDropTargetListener >& dtl )
+{
+ rBHelper.addListener( cppu::UnoType<decltype(dtl)>::get(), dtl );
+}
+
+void SAL_CALL DropTarget::removeDropTargetListener( const Reference< XDropTargetListener >& dtl )
+{
+ rBHelper.removeListener( cppu::UnoType<decltype(dtl)>::get(), dtl );
+}
+
+sal_Bool SAL_CALL DropTarget::isActive( )
+{
+ return m_bActive; //m_bDropTargetRegistered;
+}
+
+void SAL_CALL DropTarget::setActive( sal_Bool _b )
+{
+ MutexGuard g(m_aMutex);
+ m_bActive= _b;
+}
+
+sal_Int8 SAL_CALL DropTarget::getDefaultActions( )
+{
+ return m_nDefaultActions;
+}
+
+void SAL_CALL DropTarget::setDefaultActions( sal_Int8 actions )
+{
+ OSL_ENSURE( actions < 8, "No valid default actions");
+ m_nDefaultActions= actions;
+}
+
+HRESULT DropTarget::DragEnter( IDataObject *pDataObj,
+ DWORD grfKeyState,
+ POINTL pt,
+ DWORD *pdwEffect)
+{
+#if defined DBG_CONSOLE_OUT
+ printf("\nDropTarget::DragEnter state: %x effect %d", grfKeyState, *pdwEffect);
+#endif
+ if( m_bActive )
+ {
+ // Intersection of pdwEffect and the allowed actions ( setDefaultActions)
+ m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect);
+ // m_nLastDropAction has to be set by a listener. If no listener calls
+ //XDropTargetDragContext::acceptDrag and specifies an action then pdwEffect
+ // will be DROPEFFECT_NONE throughout
+ m_nLastDropAction= ACTION_DEFAULT | ACTION_MOVE;
+
+ m_currentDragContext = new TargetDragContext(this);
+
+ //--> TRA
+
+ // shortcut
+ if ( g_XTransferable.is( ) )
+ m_currentData = g_XTransferable;
+ else
+ {
+ // Convert the IDataObject to a XTransferable
+ m_currentData = new CDOTransferable(m_xContext, IDataObjectPtr(pDataObj));
+ }
+
+ //<-- TRA
+
+ if( m_nCurrentDropAction != ACTION_NONE)
+ {
+ DropTargetDragEnterEvent e;
+ e.SupportedDataFlavors= m_currentData->getTransferDataFlavors();
+ e.DropAction= m_nCurrentDropAction;
+ e.Source.set( static_cast<XDropTarget*>(this),UNO_QUERY);
+ e.Context= m_currentDragContext;
+ POINT point={ pt.x, pt.y};
+ ScreenToClient( m_hWnd, &point);
+ e.LocationX= point.x;
+ e.LocationY= point.y;
+ e.SourceActions= dndOleDropEffectsToActions( *pdwEffect);
+
+ fire_dragEnter( e);
+ // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set
+ // by the listener (m_nCurrentDropAction) is allowed by the source. Only an allowed action is set
+ // in pdwEffect. The listener notification is asynchronous, that is we cannot expect that the listener
+ // has already reacted to the notification.
+ // If there is more than one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed
+ // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed.
+ // On drop the target should present the user a dialog from which the user may change the action.
+ sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect);
+ *pdwEffect= dndActionsToSingleDropEffect( m_nLastDropAction & allowedActions);
+ }
+ else
+ {
+ *pdwEffect= DROPEFFECT_NONE;
+ }
+ }
+ return S_OK;
+}
+
+HRESULT DropTarget::DragOver( DWORD grfKeyState,
+ POINTL pt,
+ DWORD *pdwEffect)
+{
+ if( m_bActive)
+ {
+ m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect);
+
+ if( m_nCurrentDropAction)
+ {
+ DropTargetDragEvent e;
+ e.DropAction= m_nCurrentDropAction;
+ e.Source.set(static_cast<XDropTarget*>(this),UNO_QUERY);
+ e.Context= m_currentDragContext;
+ POINT point={ pt.x, pt.y};
+ ScreenToClient( m_hWnd, &point);
+ e.LocationX= point.x;
+ e.LocationY= point.y;
+ e.SourceActions= dndOleDropEffectsToActions( *pdwEffect);
+
+ // if grfKeyState has changed since the last DragOver then fire events.
+ // A listener might change m_nCurrentDropAction by calling the
+ // XDropTargetDragContext::acceptDrag function. But this is not important
+ // because in the afterwards fired dragOver event the action reflects
+ // grgKeyState again.
+ if( m_nLastDropAction != m_nCurrentDropAction)
+ fire_dropActionChanged( e);
+
+ // The Event contains a XDropTargetDragContext implementation.
+ fire_dragOver( e);
+ // Check if the action derived from grfKeyState (m_nCurrentDropAction) or the action set
+ // by the listener (m_nCurrentDropAction) is allowed by the source. Only an allowed action is set
+ // in pdwEffect. The listener notification is asynchronous, that is we cannot expect that the listener
+ // has already reacted to the notification.
+ // If there is more than one valid action which is the case when ALT or RIGHT MOUSE BUTTON is pressed
+ // then getDropEffect returns DROPEFFECT_MOVE which is the default value if no other modifier is pressed.
+ // On drop the target should present the user a dialog from which the user may change the action.
+ sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect);
+ // set the last action to the current if listener has not changed the value yet
+ *pdwEffect= dndActionsToSingleDropEffect( m_nLastDropAction & allowedActions);
+ }
+ else
+ {
+ *pdwEffect= DROPEFFECT_NONE;
+ }
+ }
+#if defined DBG_CONSOLE_OUT
+ printf("\nDropTarget::DragOver %d", *pdwEffect );
+#endif
+ return S_OK;
+}
+
+HRESULT DropTarget::DragLeave()
+{
+#if defined DBG_CONSOLE_OUT
+ printf("\nDropTarget::DragLeave");
+#endif
+ if( m_bActive)
+ {
+
+ m_currentData=nullptr;
+ m_currentDragContext= nullptr;
+ m_currentDropContext= nullptr;
+ m_nLastDropAction= 0;
+
+ if( m_nDefaultActions != ACTION_NONE)
+ {
+ DropTargetEvent e;
+ e.Source= static_cast<XDropTarget*>(this);
+
+ fire_dragExit( e);
+ }
+ }
+ return S_OK;
+}
+
+HRESULT DropTarget::Drop( IDataObject * /*pDataObj*/,
+ DWORD grfKeyState,
+ POINTL pt,
+ DWORD *pdwEffect)
+{
+#if defined DBG_CONSOLE_OUT
+ printf("\nDropTarget::Drop");
+#endif
+ if( m_bActive)
+ {
+
+ m_bDropComplete= false;
+
+ m_nCurrentDropAction= getFilteredActions( grfKeyState, *pdwEffect);
+ m_currentDropContext = new TargetDropContext(this);
+ if( m_nCurrentDropAction)
+ {
+ DropTargetDropEvent e;
+ e.DropAction= m_nCurrentDropAction;
+ e.Source.set( static_cast<XDropTarget*>(this), UNO_QUERY);
+ e.Context= m_currentDropContext;
+ POINT point={ pt.x, pt.y};
+ ScreenToClient( m_hWnd, &point);
+ e.LocationX= point.x;
+ e.LocationY= point.y;
+ e.SourceActions= dndOleDropEffectsToActions( *pdwEffect);
+ e.Transferable= m_currentData;
+ fire_drop( e);
+
+ //if fire_drop returns than a listener might have modified m_nCurrentDropAction
+ if( m_bDropComplete )
+ {
+ sal_Int8 allowedActions= dndOleDropEffectsToActions( *pdwEffect);
+ *pdwEffect= dndActionsToSingleDropEffect( m_nCurrentDropAction & allowedActions);
+ }
+ else
+ *pdwEffect= DROPEFFECT_NONE;
+ }
+ else
+ *pdwEffect= DROPEFFECT_NONE;
+
+ m_currentData= nullptr;
+ m_currentDragContext= nullptr;
+ m_currentDropContext= nullptr;
+ m_nLastDropAction= 0;
+ }
+ return S_OK;
+}
+
+void DropTarget::fire_drop( const DropTargetDropEvent& dte)
+{
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer);
+ while( iter.hasMoreElements())
+ {
+ Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
+ listener->drop( dte);
+ }
+ }
+}
+
+void DropTarget::fire_dragEnter( const DropTargetDragEnterEvent& e )
+{
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer);
+ while( iter.hasMoreElements())
+ {
+ Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
+ listener->dragEnter( e);
+ }
+ }
+}
+
+void DropTarget::fire_dragExit( const DropTargetEvent& dte )
+{
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
+
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer);
+ while( iter.hasMoreElements())
+ {
+ Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
+ listener->dragExit( dte);
+ }
+ }
+}
+
+void DropTarget::fire_dragOver( const DropTargetDragEvent& dtde )
+{
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer );
+ while( iter.hasMoreElements())
+ {
+ Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
+ listener->dragOver( dtde);
+ }
+ }
+}
+
+void DropTarget::fire_dropActionChanged( const DropTargetDragEvent& dtde )
+{
+ OInterfaceContainerHelper* pContainer= rBHelper.getContainer( cppu::UnoType<XDropTargetListener>::get());
+ if( pContainer)
+ {
+ OInterfaceIteratorHelper iter( *pContainer);
+ while( iter.hasMoreElements())
+ {
+ Reference<XDropTargetListener> listener( static_cast<XDropTargetListener*>( iter.next()));
+ listener->dropActionChanged( dtde);
+ }
+ }
+}
+
+// Non - interface functions
+// DropTarget fires events to XDropTargetListeners. The event object contains an
+// XDropTargetDropContext implementation. When the listener calls on that interface
+// then the calls are delegated from DropContext (XDropTargetDropContext) to these
+// functions.
+// Only one listener which visible area is affected is allowed to call on
+// XDropTargetDropContext
+// Returning sal_False would cause the XDropTargetDropContext or ..DragContext implementation
+// to throw an InvalidDNDOperationException, meaning that a Drag is not currently performed.
+// return sal_False results in throwing an InvalidDNDOperationException in the caller.
+
+void DropTarget::_acceptDrop(sal_Int8 dropOperation, const Reference<XDropTargetDropContext>& context)
+{
+ if( context == m_currentDropContext)
+ {
+ m_nCurrentDropAction= dropOperation;
+ }
+}
+
+void DropTarget::_rejectDrop( const Reference<XDropTargetDropContext>& context)
+{
+ if( context == m_currentDropContext)
+ {
+ m_nCurrentDropAction= ACTION_NONE;
+ }
+}
+
+void DropTarget::_dropComplete(bool success, const Reference<XDropTargetDropContext>& context)
+{
+ if(context == m_currentDropContext)
+ {
+ m_bDropComplete= success;
+ }
+}
+
+// DropTarget fires events to XDropTargetListeners. The event object can contains an
+// XDropTargetDragContext implementation. When the listener calls on that interface
+// then the calls are delegated from DragContext (XDropTargetDragContext) to these
+// functions.
+// Only one listener which visible area is affected is allowed to call on
+// XDropTargetDragContext
+void DropTarget::_acceptDrag( sal_Int8 dragOperation, const Reference<XDropTargetDragContext>& context)
+{
+ if( context == m_currentDragContext)
+ {
+ m_nLastDropAction= dragOperation;
+ }
+}
+
+void DropTarget::_rejectDrag( const Reference<XDropTargetDragContext>& context)
+{
+ if(context == m_currentDragContext)
+ {
+ m_nLastDropAction= ACTION_NONE;
+ }
+}
+
+// This function determines the action dependent on the pressed
+// key modifiers ( CTRL, SHIFT, ALT, Right Mouse Button). The result
+// is then checked against the allowed actions which can be set through
+// XDropTarget::setDefaultActions. Only those values which are also
+// default actions are returned. If setDefaultActions has not been called
+// beforehand the default actions comprise all possible actions.
+// params: grfKeyState - the modifier keys and mouse buttons currently pressed
+inline sal_Int8 DropTarget::getFilteredActions( DWORD grfKeyState, DWORD dwEffect)
+{
+ sal_Int8 actions= dndOleKeysToAction( grfKeyState, dndOleDropEffectsToActions( dwEffect));
+ return actions & m_nDefaultActions;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+dtrans_DropTarget_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new DropTarget(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/targetdragcontext.cxx b/vcl/win/dtrans/targetdragcontext.cxx
new file mode 100644
index 0000000000..71e345b74c
--- /dev/null
+++ b/vcl/win/dtrans/targetdragcontext.cxx
@@ -0,0 +1,39 @@
+/* -*- 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 "targetdragcontext.hxx"
+
+TargetDragContext::TargetDragContext(DropTarget* p)
+{
+ m_pDropTarget = p;
+ p->acquire();
+}
+
+TargetDragContext::~TargetDragContext() { m_pDropTarget->release(); }
+
+void SAL_CALL TargetDragContext::acceptDrag(sal_Int8 dragOperation)
+{
+ m_pDropTarget->_acceptDrag(dragOperation, static_cast<XDropTargetDragContext*>(this));
+}
+void SAL_CALL TargetDragContext::rejectDrag()
+{
+ m_pDropTarget->_rejectDrag(static_cast<XDropTargetDragContext*>(this));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/targetdragcontext.hxx b/vcl/win/dtrans/targetdragcontext.hxx
new file mode 100644
index 0000000000..a64ab6a30b
--- /dev/null
+++ b/vcl/win/dtrans/targetdragcontext.hxx
@@ -0,0 +1,50 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/datatransfer/dnd/XDropTargetDragContext.hpp>
+#include <com/sun/star/datatransfer/DataFlavor.hpp>
+
+#include <win/dnd_target.hxx>
+
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::datatransfer::dnd;
+using namespace ::cppu;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+class TargetDragContext : public WeakImplHelper<XDropTargetDragContext>
+{
+ // some calls to the functions of XDropTargetDragContext are delegated
+ // to non-interface functions of m_pDropTarget
+ DropTarget* m_pDropTarget;
+
+public:
+ explicit TargetDragContext(DropTarget* pTarget);
+ ~TargetDragContext() override;
+ TargetDragContext(const TargetDragContext&) = delete;
+ TargetDragContext& operator=(const TargetDragContext&) = delete;
+
+ virtual void SAL_CALL acceptDrag(sal_Int8 dragOperation) override;
+ virtual void SAL_CALL rejectDrag() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/targetdropcontext.cxx b/vcl/win/dtrans/targetdropcontext.cxx
new file mode 100644
index 0000000000..fe65389431
--- /dev/null
+++ b/vcl/win/dtrans/targetdropcontext.cxx
@@ -0,0 +1,50 @@
+/* -*- 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 "targetdropcontext.hxx"
+
+using namespace ::com::sun::star::datatransfer::dnd;
+using namespace ::cppu;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+TargetDropContext::TargetDropContext(DropTarget* p)
+{
+ m_pDropTarget = p;
+ p->acquire();
+}
+
+TargetDropContext::~TargetDropContext() { m_pDropTarget->release(); }
+
+void SAL_CALL TargetDropContext::acceptDrop(sal_Int8 dropOperation)
+{
+ m_pDropTarget->_acceptDrop(dropOperation, static_cast<XDropTargetDropContext*>(this));
+}
+
+void SAL_CALL TargetDropContext::rejectDrop()
+{
+ m_pDropTarget->_rejectDrop(static_cast<XDropTargetDropContext*>(this));
+}
+
+void SAL_CALL TargetDropContext::dropComplete(sal_Bool success)
+{
+ m_pDropTarget->_dropComplete(success, static_cast<XDropTargetDropContext*>(this));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/dtrans/targetdropcontext.hxx b/vcl/win/dtrans/targetdropcontext.hxx
new file mode 100644
index 0000000000..938a23f850
--- /dev/null
+++ b/vcl/win/dtrans/targetdropcontext.hxx
@@ -0,0 +1,52 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/datatransfer/dnd/XDropTargetDropContext.hpp>
+
+#include <win/dnd_target.hxx>
+
+using namespace ::com::sun::star::datatransfer::dnd;
+using namespace ::cppu;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+class TargetDropContext : public WeakImplHelper<XDropTargetDropContext>
+{
+ // calls to the functions of XDropTargetDropContext are delegated
+ // to non-interface functions of m_pDropTarget
+ DropTarget* m_pDropTarget;
+
+public:
+ explicit TargetDropContext(DropTarget* pTarget);
+ ~TargetDropContext() override;
+ TargetDropContext(const TargetDropContext&) = delete;
+ TargetDropContext& operator=(const TargetDropContext&) = delete;
+
+ // XDropTargetDragContext
+ virtual void SAL_CALL acceptDrop(sal_Int8 dropOperation) override;
+ virtual void SAL_CALL rejectDrop() override;
+
+ // XDropTargetDropContext (inherits XDropTargetDragContext)
+ virtual void SAL_CALL dropComplete(sal_Bool success) override;
+};
+
+/* 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 0000000000..3d3dac83c6
--- /dev/null
+++ b/vcl/win/gdi/DWriteTextRenderer.cxx
@@ -0,0 +1,341 @@
+/* -*- 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 <win/salgdi.h>
+#include <win/saldata.hxx>
+#include <ImplOutDevData.hxx>
+
+#include <win/DWriteTextRenderer.hxx>
+
+#include <sft.hxx>
+#include <sallayout.hxx>
+
+#include <shlwapi.h>
+#include <winver.h>
+
+#include <comphelper/windowserrorstring.hxx>
+#include <o3tl/safeint.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(bool bRenderingModeNatural)
+ : mpD2DFactory(nullptr),
+ mpRT(nullptr),
+ mRTProps(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
+ D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
+ 0, 0)),
+ mbRenderingModeNatural(bRenderingModeNatural),
+ meTextAntiAliasMode(D2DTextAntiAliasMode::Default)
+{
+ WinSalGraphics::getDWriteFactory(&mpDWriteFactory);
+ HRESULT hr = S_OK;
+ hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, __uuidof(ID2D1Factory), nullptr, reinterpret_cast<void **>(&mpD2DFactory));
+ if (SUCCEEDED(hr))
+ hr = CreateRenderTarget(bRenderingModeNatural);
+ meTextAntiAliasMode = lclGetSystemTextAntiAliasMode();
+}
+
+D2DWriteTextOutRenderer::~D2DWriteTextOutRenderer()
+{
+ if (mpRT)
+ mpRT->Release();
+ if (mpD2DFactory)
+ mpD2DFactory->Release();
+}
+
+void D2DWriteTextOutRenderer::applyTextAntiAliasMode(bool bRenderingModeNatural)
+{
+ 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;
+ }
+
+ if (bRenderingModeNatural)
+ eRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
+
+ mpRT->SetTextRenderingParams(lclSetRenderingMode(mpDWriteFactory, eRenderingMode));
+ mpRT->SetTextAntialiasMode(eTextAAMode);
+}
+
+HRESULT D2DWriteTextOutRenderer::CreateRenderTarget(bool bRenderingModeNatural)
+{
+ if (mpRT)
+ {
+ mpRT->Release();
+ mpRT = nullptr;
+ }
+ HRESULT hr = CHECKHR(mpD2DFactory->CreateDCRenderTarget(&mRTProps, &mpRT));
+ if (SUCCEEDED(hr))
+ applyTextAntiAliasMode(bRenderingModeNatural);
+ return hr;
+}
+
+bool D2DWriteTextOutRenderer::Ready() const
+{
+ return mpRT;
+}
+
+HRESULT D2DWriteTextOutRenderer::BindDC(HDC hDC, tools::Rectangle const & rRect)
+{
+ RECT const rc = {
+ o3tl::narrowing<LONG>(rRect.Left()), o3tl::narrowing<LONG>(rRect.Top()),
+ o3tl::narrowing<LONG>(rRect.Right()), o3tl::narrowing<LONG>(rRect.Bottom()) };
+ return CHECKHR(mpRT->BindDC(hDC, &rc));
+}
+
+bool D2DWriteTextOutRenderer::operator()(GenericSalLayout const & rLayout, SalGraphics& rGraphics, HDC hDC, bool bRenderingModeNatural)
+{
+ bool bRetry = false;
+ bool bResult = false;
+ int nCount = 0;
+ do
+ {
+ bRetry = false;
+ bResult = performRender(rLayout, rGraphics, hDC, bRetry, bRenderingModeNatural);
+ nCount++;
+ } while (bRetry && nCount < 3);
+ return bResult;
+}
+
+bool D2DWriteTextOutRenderer::performRender(GenericSalLayout const & rLayout, SalGraphics& rGraphics, HDC hDC, bool& bRetry, bool bRenderingModeNatural)
+{
+ if (!Ready())
+ return false;
+
+ HRESULT hr = S_OK;
+ hr = BindDC(hDC);
+
+ if (hr == D2DERR_RECREATE_TARGET)
+ {
+ CreateRenderTarget(bRenderingModeNatural);
+ bRetry = true;
+ return false;
+ }
+ if (FAILED(hr))
+ {
+ // If for any reason we can't bind fallback to legacy APIs.
+ return ExTextOutRenderer()(rLayout, rGraphics, hDC, bRenderingModeNatural);
+ }
+
+ const WinFontInstance& rWinFont = static_cast<const WinFontInstance&>(rLayout.GetFont());
+ float fHScale = rWinFont.getHScale();
+
+ float lfEmHeight = 0;
+ IDWriteFontFace* pFontFace = GetDWriteFace(rWinFont, &lfEmHeight);
+ if (!pFontFace)
+ return false;
+
+ 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;
+ basegfx::B2DPoint aPos;
+ const GlyphItem* pGlyph;
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ UINT16 glyphIndices[] = { static_cast<UINT16>(pGlyph->glyphId()) };
+ FLOAT glyphAdvances[] = { static_cast<FLOAT>(pGlyph->newWidth()) / fHScale };
+ DWRITE_GLYPH_OFFSET glyphOffsets[] = { { 0.0f, 0.0f }, };
+ D2D1_POINT_2F baseline = { static_cast<FLOAT>(aPos.getX() - bounds.Left()) / fHScale,
+ static_cast<FLOAT>(aPos.getY() - bounds.Top()) };
+ WinFontTransformGuard aTransformGuard(mpRT, fHScale, rLayout, baseline, pGlyph->IsVertical());
+ DWRITE_GLYPH_RUN glyphs = {
+ pFontFace,
+ lfEmHeight,
+ 1,
+ glyphIndices,
+ glyphAdvances,
+ glyphOffsets,
+ false,
+ 0
+ };
+
+ mpRT->DrawGlyphRun(baseline, &glyphs, pBrush);
+ }
+
+ hr = CHECKHR(mpRT->EndDraw());
+ }
+
+ if (pBrush)
+ pBrush->Release();
+
+ if (hr == D2DERR_RECREATE_TARGET)
+ {
+ CreateRenderTarget(bRenderingModeNatural);
+ bRetry = true;
+ }
+
+ return succeeded;
+}
+
+IDWriteFontFace* D2DWriteTextOutRenderer::GetDWriteFace(const WinFontInstance& rWinFont,
+ float* lfSize) const
+{
+ auto pFontFace = rWinFont.GetDWFontFace();
+ if (pFontFace)
+ {
+ LOGFONTW aLogFont;
+ HFONT hFont = rWinFont.GetHFONT();
+
+ GetObjectW(hFont, sizeof(LOGFONTW), &aLogFont);
+ float dpix, dpiy;
+ mpRT->GetDpi(&dpix, &dpiy);
+ *lfSize = aLogFont.lfHeight * 96.0f / dpiy;
+
+ assert(*lfSize < 0);
+ *lfSize *= -1;
+ }
+
+ return pFontFace;
+}
+
+WinFontTransformGuard::WinFontTransformGuard(ID2D1RenderTarget* pRenderTarget, float fHScale,
+ const GenericSalLayout& rLayout,
+ const D2D1_POINT_2F& rBaseline,
+ bool bIsVertical)
+ : 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));
+ }
+
+ Degree10 angle = rLayout.GetOrientation();
+
+ if (bIsVertical)
+ angle += 900_deg10;
+
+ if (angle)
+ {
+ // DWrite angle is in clockwise degrees, our orientation is in counter-clockwise 10th
+ // degrees.
+ aTransform = aTransform
+ * D2D1::Matrix3x2F::Rotation(
+ -toDegrees(angle), rBaseline);
+ }
+ mpRenderTarget->SetTransform(aTransform);
+}
+
+WinFontTransformGuard::~WinFontTransformGuard() { mpRenderTarget->SetTransform(maTransform); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/win/gdi/dw-extra.h b/vcl/win/gdi/dw-extra.h
new file mode 100644
index 0000000000..4c07d81d21
--- /dev/null
+++ b/vcl/win/gdi/dw-extra.h
@@ -0,0 +1,141 @@
+//
+// copied from:
+// https://hg.mozilla.org/mozilla-central/file/704f09a557a4dfc9057f1672b711789f64f74a82/gfx/2d/dw-extra.h
+//
+
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * 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/. */
+
+/*
+ * New DirectWrite interfaces based on Win10 Fall Creators Update versions
+ * of dwrite_3.h and dcommon.h (from SDK 10.0.17061.0). This particular
+ * subset of declarations is intended to be just sufficient to compile the
+ * Gecko DirectWrite font code; it omits many other new interfaces, etc.
+ */
+
+#ifndef DWRITE_EXTRA_H
+#define DWRITE_EXTRA_H
+
+#pragma once
+
+interface IDWriteFontResource;
+interface IDWriteFontFaceReference1;
+
+enum DWRITE_GLYPH_IMAGE_FORMATS {
+ DWRITE_GLYPH_IMAGE_FORMATS_NONE = 0x00000000,
+ DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE = 0x00000001,
+ DWRITE_GLYPH_IMAGE_FORMATS_CFF = 0x00000002,
+ DWRITE_GLYPH_IMAGE_FORMATS_COLR = 0x00000004,
+ DWRITE_GLYPH_IMAGE_FORMATS_SVG = 0x00000008,
+ DWRITE_GLYPH_IMAGE_FORMATS_PNG = 0x00000010,
+ DWRITE_GLYPH_IMAGE_FORMATS_JPEG = 0x00000020,
+ DWRITE_GLYPH_IMAGE_FORMATS_TIFF = 0x00000040,
+ DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 = 0x00000080,
+};
+
+#ifdef DEFINE_ENUM_FLAG_OPERATORS
+DEFINE_ENUM_FLAG_OPERATORS(DWRITE_GLYPH_IMAGE_FORMATS);
+#endif
+
+#define DWRITE_MAKE_FONT_AXIS_TAG(a, b, c, d) \
+ (static_cast<DWRITE_FONT_AXIS_TAG>(DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d)))
+
+enum DWRITE_FONT_AXIS_TAG : UINT32 {
+ DWRITE_FONT_AXIS_TAG_WEIGHT = DWRITE_MAKE_FONT_AXIS_TAG('w', 'g', 'h', 't'),
+ DWRITE_FONT_AXIS_TAG_WIDTH = DWRITE_MAKE_FONT_AXIS_TAG('w', 'd', 't', 'h'),
+ DWRITE_FONT_AXIS_TAG_SLANT = DWRITE_MAKE_FONT_AXIS_TAG('s', 'l', 'n', 't'),
+ DWRITE_FONT_AXIS_TAG_OPTICAL_SIZE =
+ DWRITE_MAKE_FONT_AXIS_TAG('o', 'p', 's', 'z'),
+ DWRITE_FONT_AXIS_TAG_ITALIC = DWRITE_MAKE_FONT_AXIS_TAG('i', 't', 'a', 'l'),
+};
+
+enum DWRITE_FONT_AXIS_ATTRIBUTES {
+ DWRITE_FONT_AXIS_ATTRIBUTES_NONE = 0x0000,
+ DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE = 0x0001,
+ DWRITE_FONT_AXIS_ATTRIBUTES_HIDDEN = 0x0002,
+};
+
+struct DWRITE_FONT_AXIS_VALUE {
+ DWRITE_FONT_AXIS_TAG axisTag;
+ FLOAT value;
+};
+
+struct DWRITE_FONT_AXIS_RANGE {
+ DWRITE_FONT_AXIS_TAG axisTag;
+ FLOAT minValue;
+ FLOAT maxValue;
+};
+
+struct DWRITE_GLYPH_IMAGE_DATA {
+ const void* imageData;
+ UINT32 imageDataSize;
+ UINT32 uniqueDataId;
+ UINT32 pixelsPerEm;
+ D2D1_SIZE_U pixelSize;
+ D2D1_POINT_2L horizontalLeftOrigin;
+ D2D1_POINT_2L horizontalRightOrigin;
+ D2D1_POINT_2L verticalTopOrigin;
+ D2D1_POINT_2L verticalBottomOrigin;
+};
+
+interface DWRITE_DECLARE_INTERFACE("27F2A904-4EB8-441D-9678-0563F53E3E2F")
+ IDWriteFontFace4 : public IDWriteFontFace3 {
+ STDMETHOD_(DWRITE_GLYPH_IMAGE_FORMATS, GetGlyphImageFormats)() PURE;
+ STDMETHOD(GetGlyphImageFormats)
+ (UINT16 glyphId, UINT32 pixelsPerEmFirst, UINT32 pixelsPerEmLast,
+ _Out_ DWRITE_GLYPH_IMAGE_FORMATS* glyphImageFormats) PURE;
+ STDMETHOD(GetGlyphImageData)
+ (_In_ UINT16 glyphId, UINT32 pixelsPerEm,
+ DWRITE_GLYPH_IMAGE_FORMATS glyphImageFormat,
+ _Out_ DWRITE_GLYPH_IMAGE_DATA* glyphData,
+ _Outptr_result_maybenull_ void** glyphDataContext) PURE;
+ STDMETHOD_(void, ReleaseGlyphImageData)(void* glyphDataContext) PURE;
+};
+
+interface DWRITE_DECLARE_INTERFACE("98EFF3A5-B667-479A-B145-E2FA5B9FDC29")
+ IDWriteFontFace5 : public IDWriteFontFace4 {
+ STDMETHOD_(UINT32, GetFontAxisValueCount)() PURE;
+ STDMETHOD(GetFontAxisValues)
+ (_Out_writes_(fontAxisValueCount) DWRITE_FONT_AXIS_VALUE* fontAxisValues,
+ UINT32 fontAxisValueCount) PURE;
+ STDMETHOD_(BOOL, HasVariations)() PURE;
+ STDMETHOD(GetFontResource)
+ (_COM_Outptr_ IDWriteFontResource** fontResource) PURE;
+ STDMETHOD_(BOOL, Equals)(IDWriteFontFace* fontFace) PURE;
+};
+
+interface DWRITE_DECLARE_INTERFACE("1F803A76-6871-48E8-987F-B975551C50F2")
+ IDWriteFontResource : public IUnknown {
+ STDMETHOD(GetFontFile)(_COM_Outptr_ IDWriteFontFile** fontFile) PURE;
+ STDMETHOD_(UINT32, GetFontFaceIndex)() PURE;
+ STDMETHOD_(UINT32, GetFontAxisCount)() PURE;
+ STDMETHOD(GetDefaultFontAxisValues)
+ (_Out_writes_(fontAxisValueCount) DWRITE_FONT_AXIS_VALUE* fontAxisValues,
+ UINT32 fontAxisValueCount) PURE;
+ STDMETHOD(GetFontAxisRanges)
+ (_Out_writes_(fontAxisRangeCount) DWRITE_FONT_AXIS_RANGE* fontAxisRanges,
+ UINT32 fontAxisRangeCount) PURE;
+ STDMETHOD_(DWRITE_FONT_AXIS_ATTRIBUTES, GetFontAxisAttributes)
+ (UINT32 axisIndex) PURE;
+ STDMETHOD(GetAxisNames)
+ (UINT32 axisIndex, _COM_Outptr_ IDWriteLocalizedStrings** names) PURE;
+ STDMETHOD_(UINT32, GetAxisValueNameCount)(UINT32 axisIndex) PURE;
+ STDMETHOD(GetAxisValueNames)
+ (UINT32 axisIndex, UINT32 axisValueIndex,
+ _Out_ DWRITE_FONT_AXIS_RANGE* fontAxisRange,
+ _COM_Outptr_ IDWriteLocalizedStrings** names) PURE;
+ STDMETHOD_(BOOL, HasVariations)() PURE;
+ STDMETHOD(CreateFontFace)
+ (DWRITE_FONT_SIMULATIONS fontSimulations,
+ _In_reads_(fontAxisValueCount) DWRITE_FONT_AXIS_VALUE const* fontAxisValues,
+ UINT32 fontAxisValueCount, _COM_Outptr_ IDWriteFontFace5** fontFace) PURE;
+ STDMETHOD(CreateFontFaceReference)
+ (DWRITE_FONT_SIMULATIONS fontSimulations,
+ _In_reads_(fontAxisValueCount) DWRITE_FONT_AXIS_VALUE const* fontAxisValues,
+ UINT32 fontAxisValueCount,
+ _COM_Outptr_ IDWriteFontFaceReference1** fontFaceReference) PURE;
+};
+
+#endif /* DWRITE_EXTRA_H */
diff --git a/vcl/win/gdi/gdiimpl.cxx b/vcl/win/gdi/gdiimpl.cxx
new file mode 100644
index 0000000000..5cb6a05bda
--- /dev/null
+++ b/vcl/win/gdi/gdiimpl.cxx
@@ -0,0 +1,2731 @@
+/* -*- 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 <cstdlib>
+#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 <win/salids.hrc>
+#include <ControlCacheKey.hxx>
+
+#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 Point* const* pPtAry,
+ const PolyFlags* const* pFlgAry,
+ POINT* pWinPointAry,
+ BYTE* pWinFlagAry )
+{
+ sal_uLong nCurrPoly;
+ for( nCurrPoly=0; nCurrPoly<nPoly; ++nCurrPoly )
+ {
+ const Point* pCurrPoint = *pPtAry++;
+ const PolyFlags* pCurrFlag = *pFlgAry++;
+ const sal_uInt32 nCurrPoints = *pPoints++;
+ const bool bHaveFlagArray( pCurrFlag );
+ sal_uLong nCurrPoint;
+
+ if( nCurrPoints )
+ {
+ // start figure
+ *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
+ 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++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
+ pCurrPoint++;
+ *pWinFlagAry++ = PT_BEZIERTO;
+
+ // control point two
+ *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
+ pCurrPoint++;
+ *pWinFlagAry++ = PT_BEZIERTO;
+
+ // end point
+ *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
+ pCurrPoint++;
+ *pWinFlagAry++ = PT_BEZIERTO;
+
+ nCurrPoint += 3;
+ pCurrFlag += 3;
+ continue;
+ }
+ }
+
+ // regular line point
+ *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
+ 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(tools::Long, tools::Long, tools::Long, tools::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( tools::Long nDestX, tools::Long nDestY,
+ tools::Long nSrcX, tools::Long nSrcY,
+ tools::Long nSrcWidth, tools::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, vcl::bitDepthToPixelFormat(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 on top
+ }
+ 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( tools::Long nX, tools::Long nY, tools::Long nWidth,
+ tools::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( tools::Long nX, tools::Long nY, tools::Long nDX, tools::Long nDY )
+{
+ SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No ::GetBitmap() from printer possible!" );
+
+ std::shared_ptr<WinSalBitmap> pSalBitmap;
+
+ nDX = std::abs( nDX );
+ nDY = std::abs( 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 ) )
+ {
+ 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( tools::Long nX, tools::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( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::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 Point* 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 );
+
+ std::unique_ptr<POINT[]> pWinPtAry(new POINT[nPoints]);
+ for (sal_uInt32 i=0; i<nPoints; ++i)
+ pWinPtAry[i] = POINT { static_cast<LONG>(pPtAry[i].getX()), static_cast<LONG>(pPtAry[i].getY()) };
+
+ // for Windows 95 and its maximum number of points
+ if ( nSalFlags & SalInvert::TrackFrame )
+ {
+ if ( !Polyline( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
+ Polyline( mrParent.getHDC(), pWinPtAry.get(), MAX_64KSALPOINTS );
+ }
+ else
+ {
+ if ( !Polygon( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
+ Polygon( mrParent.getHDC(), pWinPtAry.get(), 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 ));
+}
+
+tools::Long WinSalGraphicsImpl::GetGraphicsWidth() const
+{
+ if( mrParent.gethWnd() && IsWindow( mrParent.gethWnd() ) )
+ {
+ WinSalFrame* pFrame = GetWindowPtr( mrParent.gethWnd() );
+ if( pFrame )
+ {
+ if (pFrame->maGeometry.width())
+ return pFrame->maGeometry.width();
+ 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;
+}
+
+void 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 tools::Long nW(rectangle.GetWidth());
+ const tools::Long nH(rectangle.GetHeight());
+
+ if(nW && nH)
+ {
+ const tools::Long nRight(rectangle.Left() + nW);
+ const tools::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 );
+ }
+}
+
+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( tools::Long nX, tools::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( tools::Long nX, tools::Long nY )
+{
+ DrawPixelImpl( nX, nY, mnPenColor );
+}
+
+void WinSalGraphicsImpl::drawPixel( tools::Long nX, tools::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( tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::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( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::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 Point* pPtAry )
+{
+ std::unique_ptr<POINT[]> pWinPtAry(new POINT[nPoints]);
+ for (sal_uInt32 i=0; i<nPoints; ++i)
+ pWinPtAry[i] = POINT { static_cast<LONG>(pPtAry[i].getX()), static_cast<LONG>(pPtAry[i].getY()) };
+
+ // for Windows 95 and its maximum number of points
+ if ( !Polyline( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
+ Polyline( mrParent.getHDC(), pWinPtAry.get(), 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 Point* pPtAry )
+{
+ std::unique_ptr<POINT[]> pWinPtAry(new POINT[nPoints]);
+ for (sal_uInt32 i=0; i<nPoints; ++i)
+ pWinPtAry[i] = POINT { static_cast<LONG>(pPtAry[i].getX()), static_cast<LONG>(pPtAry[i].getY()) };
+
+ // for Windows 95 and its maximum number of points
+ if ( !Polygon( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
+ Polygon( mrParent.getHDC(), pWinPtAry.get(), MAX_64KSALPOINTS );
+}
+
+void WinSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ const Point** 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];
+ UINT n = 0;
+ for ( i = 0; i < static_cast<UINT>(nPoly); i++ )
+ {
+ nPoints = pWinPointAry[i];
+ const Point* pPolyAry = pPtAry[i];
+ for (sal_uInt32 j=0; j<nPoints-1; ++j)
+ pWinPointAryAry[n+j] = POINT { static_cast<LONG>(pPolyAry[j].getX()), static_cast<LONG>(pPolyAry[j].getY()) };
+ 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 Point* pPtAry, const PolyFlags* pFlgAry )
+{
+ // #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, static_cast<LONG>(pPtAry->getX()), static_cast<LONG>(pPtAry->getY()), nullptr);
+ ++pPtAry;
+ ++pFlgAry;
+
+ for(sal_uInt32 i = 1; i < nPoints; ++i)
+ {
+ if(*pFlgAry != PolyFlags::Control)
+ {
+ LineTo(hdc, pPtAry->getX(), pPtAry->getY());
+ }
+ else if(nPoints - i > 2)
+ {
+ POINT bezierPoints[] = {
+ POINT { static_cast<LONG>(pPtAry[0].getX()), static_cast<LONG>(pPtAry[0].getY()) },
+ POINT { static_cast<LONG>(pPtAry[1].getX()), static_cast<LONG>(pPtAry[1].getY()) },
+ POINT { static_cast<LONG>(pPtAry[2].getX()), static_cast<LONG>(pPtAry[2].getY()) },
+ };
+ PolyBezierTo(hdc, bezierPoints, 3);
+ i += 2;
+ pPtAry += 2;
+ pFlgAry += 2;
+ }
+
+ ++pPtAry;
+ ++pFlgAry;
+ }
+
+ return true;
+}
+
+bool WinSalGraphicsImpl::drawPolygonBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
+{
+ 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 Point* const* pPtAry, const PolyFlags* const* pFlgAry )
+{
+ 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(
+ 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(
+ std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
+ bool bNoLineJoin,
+ const std::vector< double >* pStroke)
+: basegfx::SystemDependentData(Application::GetSystemDependentDataManager()),
+ 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;
+}
+
+void 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;
+ }
+
+ 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>(
+ pGraphicsPath,
+ false,
+ nullptr);
+ }
+
+ if(mrParent.getAntiAlias())
+ {
+ 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));
+}
+
+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);
+
+ // tdf#134128. ODF adds caps to the dashes and dots, but GDI makes caps from the
+ // dash or dot themselves. We tweak aDashArray to look the same in GDI (e.g. Impress edit mode)
+ // and other renders (e.g. Impress slide show), while keeping the total length of the
+ // pattern.
+ // Patterns are always a sequence dash space dash space ...
+ if (eLineCap != css::drawing::LineCap_BUTT)
+ {
+ size_t nSize = pStroke->size();
+ // We want to treat dash and space in pairs. There should be no odd size. If so, we ignore
+ // last item.
+ nSize /= 2;
+ for(size_t a(0); a < nSize; a++)
+ {
+ double fDashLengthRel = (*pStroke)[2 * a] * fFactor;
+ double fSpaceLengthRel = (*pStroke)[2 * a + 1] * fFactor;
+ // GDI allows only positive lengths for space, Skia negative lengths too. Thus the
+ // appearance is different, in case space is too small.
+ double fCorrect = fSpaceLengthRel - 1.0 <= 0 ? fSpaceLengthRel - 0.01 : 1.0;
+ aDashArray[2 * a] = Gdiplus::REAL(fDashLengthRel + fCorrect);
+ aDashArray[2 * a + 1] = Gdiplus::REAL(fSpaceLengthRel - fCorrect);
+ }
+ }
+ else
+ {
+ for(size_t a(0); a < pStroke->size(); a++)
+ {
+ aDashArray[a] = Gdiplus::REAL((*pStroke)[a] * fFactor);
+ }
+ }
+ if (eLineCap == css::drawing::LineCap_ROUND)
+ aPen.SetDashCap(Gdiplus::DashCapRound);
+ else
+ aPen.SetDashCap(Gdiplus::DashCapFlat); // "square" doesn't exist in Gdiplus
+ 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>(
+ pGraphicsPath,
+ bNoLineJoin,
+ pStroke);
+ }
+ }
+
+ if(mrParent.getAntiAlias())
+ {
+ 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,
+ tools::Long rSrcWidth,
+ tools::Long rDestWidth,
+ tools::Long rSrcHeight,
+ tools::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)
+ {
+ 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)
+ {
+ 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,
+ double fAlpha)
+{
+ assert(dynamic_cast<const WinSalBitmap*>(&rSourceBitmap));
+ assert(!pAlphaBitmap || dynamic_cast<const WinSalBitmap*>(pAlphaBitmap));
+
+ if( fAlpha != 1.0 )
+ return false;
+
+ 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)
+ {
+ const tools::Long nSrcWidth(aARGB->GetWidth());
+ const tools::Long nSrcHeight(aARGB->GetHeight());
+
+ if(nSrcWidth && nSrcHeight)
+ {
+ const tools::Long nDestWidth(basegfx::fround(basegfx::B2DVector(rX - rNull).getLength()));
+ const tools::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::hasFastDrawTransformedBitmap() const
+{
+ return false;
+}
+
+bool WinSalGraphicsImpl::drawGradient(const tools::PolyPolygon& /*rPolygon*/,
+ const Gradient& /*rGradient*/)
+{
+ return false;
+}
+
+bool WinSalGraphicsImpl::implDrawGradient(basegfx::B2DPolyPolygon const & /*rPolyPolygon*/,
+ SalGradient const & /*rGradient*/)
+{
+ return false;
+}
+
+bool WinSalGraphicsImpl::supportsOperation(OutDevSupportType eType) const
+{
+ bool bRet = false;
+
+ switch (eType)
+ {
+ case OutDevSupportType::TransparentRect:
+ bRet = mrParent.mbVirDev || mrParent.mbWindow;
+ 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 0000000000..9d4fa32233
--- /dev/null
+++ b/vcl/win/gdi/gdiimpl.hxx
@@ -0,0 +1,253 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <salgdiimpl.hxx>
+#include <tools/long.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(tools::Long nX, tools::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 void setClipRegion( const vcl::Region& ) override;
+ //
+ // get the depth of the device
+ virtual sal_uInt16 GetBitCount() const override;
+
+ // get the width of the device
+ virtual tools::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( tools::Long nX, tools::Long nY ) override;
+ virtual void drawPixel( tools::Long nX, tools::Long nY, Color nColor ) override;
+
+ virtual void drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2 ) override;
+
+ virtual void drawRect( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) override;
+
+ virtual void drawPolyLine( sal_uInt32 nPoints, const Point* pPtAry ) override;
+
+ virtual void drawPolygon( sal_uInt32 nPoints, const Point* pPtAry ) override;
+
+ virtual void drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints, const Point** pPtAry ) override;
+
+ virtual void 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 Point* pPtAry,
+ const PolyFlags* pFlgAry ) override;
+
+ virtual bool drawPolygonBezier(
+ sal_uInt32 nPoints,
+ const Point* pPtAry,
+ const PolyFlags* pFlgAry ) override;
+
+ virtual bool drawPolyPolygonBezier(
+ sal_uInt32 nPoly,
+ const sal_uInt32* pPoints,
+ const Point* const* pPtAry,
+ const PolyFlags* const* pFlgAry ) override;
+
+ // CopyArea --> No RasterOp, but ClipRegion
+ virtual void copyArea(
+ tools::Long nDestX, tools::Long nDestY,
+ tools::Long nSrcX, tools::Long nSrcY,
+ tools::Long nSrcWidth, tools::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( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight ) override;
+
+ virtual Color getPixel( tools::Long nX, tools::Long nY ) override;
+
+ // invert --> ClipRegion (only Windows or VirDevs)
+ virtual void invert(
+ tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight,
+ SalInvert nFlags) override;
+
+ virtual void invert( sal_uInt32 nPoints, const Point* pPtAry, SalInvert nFlags ) override;
+
+ virtual bool drawEPS(
+ tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::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,
+ double fAlpha) override;
+
+ virtual bool hasFastDrawTransformedBitmap() const 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(
+ tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight,
+ sal_uInt8 nTransparency ) override;
+
+
+ virtual bool drawGradient(const tools::PolyPolygon& rPolygon,
+ const Gradient& rGradient) override;
+ virtual bool implDrawGradient(basegfx::B2DPolyPolygon const & rPolyPolygon,
+ SalGradient const & rGradient) override;
+
+ virtual bool supportsOperation(OutDevSupportType eType) const override;
+};
+
+/* 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 0000000000..71c099e952
--- /dev/null
+++ b/vcl/win/gdi/salbmp.cxx
@@ -0,0 +1,910 @@
+/*
+ * 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>
+
+#include <prewin.h>
+#include <gdiplus.h>
+#include <postwin.h>
+
+#if defined _MSC_VER
+#undef min
+#undef max
+#endif
+
+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(
+ 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(
+ const std::shared_ptr<Gdiplus::Bitmap>& rGdiPlusBitmap,
+ const WinSalBitmap* pAssociatedAlpha)
+: basegfx::SystemDependentData(Application::GetSystemDependentDataManager()),
+ 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 = const_cast< WinSalBitmap* >(this)->ImplCreateGdiPlusBitmap(*pAlphaSource);
+ pAssociatedAlpha = pAlphaSource;
+ }
+ else
+ {
+ aRetval = const_cast< WinSalBitmap* >(this)->ImplCreateGdiPlusBitmap();
+ pAssociatedAlpha = nullptr;
+ }
+
+ // add to buffering mechanism
+ addOrReplaceSystemDependentData<SystemDependentData_GdiPlusBitmap>(
+ aRetval,
+ pAssociatedAlpha);
+ }
+
+ return aRetval;
+}
+
+std::shared_ptr<Gdiplus::Bitmap> WinSalBitmap::ImplCreateGdiPlusBitmap()
+{
+ std::shared_ptr<Gdiplus::Bitmap> pRetval;
+ WinSalBitmap* pSalRGB = this;
+ std::unique_ptr<WinSalBitmap> pExtraWinSalRGB;
+
+ if(!pSalRGB->ImplGethDIB())
+ {
+ // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap
+ pExtraWinSalRGB.reset(new WinSalBitmap());
+ pExtraWinSalRGB->Create(*pSalRGB, vcl::bitDepthToPixelFormat(pSalRGB->GetBitCount()));
+ pSalRGB = pExtraWinSalRGB.get();
+ }
+
+ BitmapBuffer* pRGB = pSalRGB->AcquireBuffer(BitmapAccessMode::Read);
+ std::optional<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 ? &*pExtraRGB : nullptr;
+ }
+
+ 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 = std::make_shared<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
+ {
+ pRetval.reset();
+ }
+ }
+
+ 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);
+ }
+
+ return pRetval;
+}
+
+std::shared_ptr<Gdiplus::Bitmap> WinSalBitmap::ImplCreateGdiPlusBitmap(const WinSalBitmap& rAlphaSource)
+{
+ std::shared_ptr<Gdiplus::Bitmap> pRetval;
+ WinSalBitmap* pSalRGB = this;
+ std::unique_ptr<WinSalBitmap> pExtraWinSalRGB;
+
+ if(!pSalRGB->ImplGethDIB())
+ {
+ // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap
+ pExtraWinSalRGB.reset(new WinSalBitmap());
+ pExtraWinSalRGB->Create(*pSalRGB, vcl::bitDepthToPixelFormat(pSalRGB->GetBitCount()));
+ pSalRGB = pExtraWinSalRGB.get();
+ }
+
+ BitmapBuffer* pRGB = pSalRGB->AcquireBuffer(BitmapAccessMode::Read);
+ std::optional<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 ? &*pExtraRGB : nullptr;
+ }
+
+ WinSalBitmap* pSalA = const_cast< WinSalBitmap* >(&rAlphaSource);
+ std::unique_ptr<WinSalBitmap> pExtraWinSalA;
+
+ if(!pSalA->ImplGethDIB())
+ {
+ // we need DIB for success with AcquireBuffer, create a replacement WinSalBitmap
+ pExtraWinSalA.reset(new WinSalBitmap());
+ pExtraWinSalA->Create(*pSalA, vcl::bitDepthToPixelFormat(pSalA->GetBitCount()));
+ pSalA = pExtraWinSalA.get();
+ }
+
+ BitmapBuffer* pA = pSalA->AcquireBuffer(BitmapAccessMode::Read);
+ std::optional<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 ? &*pExtraA : nullptr;
+ }
+
+ 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 = std::make_shared<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++ = *pSrcA++;
+ }
+
+ pSrcRGB += nExtraRGB;
+ pSrcA += nExtraA;
+ }
+
+ pRetval->UnlockBits(&aGdiPlusBitmapData);
+ }
+ else
+ {
+ pRetval.reset();
+ }
+ }
+
+ 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);
+ }
+
+ pExtraWinSalA.reset();
+
+ 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);
+ }
+
+ pExtraWinSalRGB.reset();
+
+ return pRetval;
+}
+
+bool WinSalBitmap::Create( HANDLE hBitmap )
+{
+ bool bRet = true;
+
+ mhDDB = static_cast<HBITMAP>( 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, vcl::PixelFormat ePixelFormat, const BitmapPalette& rPal)
+{
+ bool bRet = false;
+
+ mhDIB = ImplCreateDIB(rSize, ePixelFormat, rPal);
+
+ if( mhDIB )
+ {
+ maSize = rSize;
+ mnBitCount = vcl::pixelFormatBitCount(ePixelFormat);
+ 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, vcl::PixelFormat eNewPixelFormat)
+{
+ bool bRet = false;
+
+ const WinSalBitmap& rSalBmp = static_cast<const WinSalBitmap&>(rSSalBmp);
+
+ if( rSalBmp.mhDDB )
+ {
+ mhDIB = ImplCreateDIB( rSalBmp.maSize, eNewPixelFormat, 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 = vcl::pixelFormatBitCount(eNewPixelFormat);
+ 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 ) {
+ css::uno::Sequence< css::uno::Any > args;
+
+ if( xFastPropertySet->getFastPropertyValue(bMask ? 2 : 1) >>= args ) {
+ sal_Int64 aHBmp64;
+
+ if( args[0] >>= aHBmp64 ) {
+ return Create( reinterpret_cast<HANDLE>(aHBmp64) );
+ }
+ }
+ }
+ 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, vcl::PixelFormat ePixelFormat, const BitmapPalette& rPal)
+{
+ HGLOBAL hDIB = nullptr;
+
+ if( rSize.IsEmpty() )
+ return hDIB;
+
+ const auto nBits = vcl::pixelFormatBitCount(ePixelFormat);
+
+ // 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
+ sal_uInt16 nColors = 0;
+ if (ePixelFormat <= vcl::PixelFormat::N8_BPP)
+ nColors = vcl::numberOfColors(ePixelFormat);
+
+ 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*/ )
+{
+ std::unique_ptr<BitmapBuffer> pBuffer;
+
+ if( mhDIB )
+ {
+ PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( mhDIB ));
+ PBITMAPINFOHEADER pBIH = &pBI->bmiHeader;
+
+ if( pBIH->biPlanes == 1 )
+ {
+ pBuffer.reset(new BitmapBuffer);
+
+ pBuffer->mnFormat = pBIH->biBitCount == 1 ? ScanlineFormat::N1BitMsbPal :
+ 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 );
+ pBuffer.reset();
+ }
+ }
+ else
+ GlobalUnlock( mhDIB );
+ }
+
+ return pBuffer.release();
+}
+
+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();
+}
+
+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;
+}
+
+const basegfx::SystemDependentDataHolder* WinSalBitmap::accessSystemDependentDataHolder() const
+{
+ return this;
+}
+
+/* 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 0000000000..51ddcce741
--- /dev/null
+++ b/vcl/win/gdi/salfont.cxx
@@ -0,0 +1,1377 @@
+/* -*- 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/types.h>
+#include <config_folders.h>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+#include <string.h>
+#include <string_view>
+#include <svsys.h>
+#include <vector>
+
+#include <dwrite_3.h>
+// Currently, we build with _WIN32_WINNT=0x0601 (Windows 7), which means newer
+// declarations in dwrite_3.h will not be visible.
+#if WINVER < 0x0A00
+# include "dw-extra.h"
+#endif
+
+#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 <comphelper/scopeguard.hxx>
+#include <comphelper/windowserrorstring.hxx>
+
+#include <font/FontSelectPattern.hxx>
+#include <font/PhysicalFontCollection.hxx>
+#include <font/PhysicalFontFaceCollection.hxx>
+#include <font/PhysicalFontFace.hxx>
+#include <font/fontsubstitution.hxx>
+#include <sft.hxx>
+#include <win/saldata.hxx>
+#include <win/salgdi.h>
+#include <win/winlayout.hxx>
+#include <win/wingdiimpl.hxx>
+#include <impfontcharmap.hxx>
+#include <font/FontMetricData.hxx>
+#include <impglyphitem.hxx>
+
+#if HAVE_FEATURE_SKIA
+#include <vcl/skia/SkiaHelper.hxx>
+#include <skia/win/font.hxx>
+#endif
+
+using namespace vcl;
+
+static FIXED FixedFromDouble( double d )
+{
+ const tools::Long l = static_cast<tools::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;
+}
+
+// platform specific font substitution hooks for glyph fallback enhancement
+
+namespace {
+
+class WinPreMatchFontSubstititution
+: public vcl::font::PreMatchFontSubstitution
+{
+public:
+ bool FindFontSubstitute(vcl::font::FontSelectPattern&) const override;
+};
+
+class WinGlyphFallbackSubstititution
+: public vcl::font::GlyphFallbackFontSubstitution
+{
+public:
+ bool FindFontSubstitute(vcl::font::FontSelectPattern&, LogicalFontInstance* pLogicalFont, OUString& rMissingChars) const override;
+};
+
+// does a font face hold the given missing characters?
+bool HasMissingChars(vcl::font::PhysicalFontFace* pFace, OUString& rMissingChars)
+{
+ FontCharMapRef xFontCharMap = pFace->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;
+}
+
+ //used by 2-level font fallback
+ vcl::font::PhysicalFontFamily* findDevFontListByLocale(const vcl::font::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
+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(vcl::font::FontSelectPattern& rFontSelData) const
+{
+ if (rFontSelData.IsMicrosoftSymbolEncoded() || IsOpenSymbol(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(vcl::font::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 vcl::font::PhysicalFontCollection* pFontCollection = ImplGetSVData()->maGDIData.mxScreenFontList.get();
+ vcl::font::PhysicalFontFamily* pFontFamily = findDevFontListByLocale(*pFontCollection, aLanguageTag);
+ if( pFontFamily )
+ {
+ vcl::font::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 )
+ {
+ vcl::font::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<vcl::font::PhysicalFontFaceCollection> pTestFontList = pFontCollection->GetFontFaceCollection();
+ // 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 )
+ {
+ vcl::font::PhysicalFontFace* pFace = pTestFontList->Get( i );
+ bFound = HasMissingChars( pFace, rMissingChars );
+ if( !bFound )
+ continue;
+ rFontSelData.maSearchName = pFace->GetFamilyName();
+ break;
+ }
+
+ return bFound;
+}
+
+namespace {
+
+struct ImplEnumInfo
+{
+ HDC mhDC;
+ vcl::font::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.SetMicrosoftSymbolEncoded(rLogFont.lfCharSet == SYMBOL_CHARSET);
+
+ // get the font face name
+ aDFA.SetFamilyName(OUString(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(OUString(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;
+}
+
+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 ) );
+
+ tools::Long nFontHeight = rLogFont.lfHeight;
+ if ( nFontHeight < 0 )
+ nFontHeight = -nFontHeight;
+ tools::Long nDPIY = GetDeviceCaps( hDC, LOGPIXELSY );
+ if( !nDPIY )
+ nDPIY = 600;
+ nFontHeight *= 72;
+ nFontHeight += nDPIY/2;
+ nFontHeight /= nDPIY;
+ rFont.SetFontSize( Size( 0, nFontHeight ) );
+ rFont.SetOrientation( Degree10(static_cast<sal_Int16>(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 ENUMLOGFONTEXW& rEnumFont, const NEWTEXTMETRICW& rMetric)
+: vcl::font::PhysicalFontFace(WinFont2DevFontAttributes(rEnumFont, rMetric)),
+ mnId( 0 ),
+ meWinCharSet(rEnumFont.elfLogFont.lfCharSet),
+ mnPitchAndFamily(rMetric.tmPitchAndFamily),
+ maLogFont(rEnumFont.elfLogFont)
+{
+}
+
+WinFontFace::~WinFontFace()
+{
+}
+
+sal_IntPtr WinFontFace::GetFontId() const
+{
+ return mnId;
+}
+
+rtl::Reference<LogicalFontInstance> WinFontFace::CreateFontInstance(const vcl::font::FontSelectPattern& rFSD) const
+{
+#if HAVE_FEATURE_SKIA
+ if (SkiaHelper::isVCLSkiaEnabled())
+ return new SkiaWinFontInstance(*this, rFSD);
+#endif
+ return new WinFontInstance(*this, rFSD);
+}
+
+const std::vector<hb_variation_t>&
+WinFontFace::GetVariations(const LogicalFontInstance& rFont) const
+{
+ if (!mxVariations)
+ {
+ mxVariations.emplace();
+ auto pDWFontFace = static_cast<const WinFontInstance&>(rFont).GetDWFontFace();
+ if (pDWFontFace)
+ {
+ sal::systools::COMReference<IDWriteFontFace5> xDWFontFace5;
+ auto hr = pDWFontFace->QueryInterface(__uuidof(IDWriteFontFace5),
+ reinterpret_cast<void**>(&xDWFontFace5));
+ if (SUCCEEDED(hr) && xDWFontFace5->HasVariations())
+ {
+ std::vector<DWRITE_FONT_AXIS_VALUE> aAxisValues(
+ xDWFontFace5->GetFontAxisValueCount());
+ hr = xDWFontFace5->GetFontAxisValues(aAxisValues.data(), aAxisValues.size());
+ if (SUCCEEDED(hr))
+ {
+ mxVariations->reserve(aAxisValues.size());
+ for (auto& rAxisValue : aAxisValues)
+ mxVariations->push_back(
+ { OSL_NETDWORD(rAxisValue.axisTag), rAxisValue.value });
+ }
+ }
+ }
+ }
+
+ return *mxVariations;
+}
+
+namespace
+{
+struct BlobReference
+{
+ hb_blob_t* mpBlob;
+ BlobReference(hb_blob_t* pBlob)
+ : mpBlob(pBlob)
+ {
+ hb_blob_reference(mpBlob);
+ }
+ BlobReference(BlobReference&& other) noexcept
+ : mpBlob(other.mpBlob)
+ {
+ other.mpBlob = nullptr;
+ }
+ BlobReference& operator=(BlobReference&& other)
+ {
+ std::swap(mpBlob, other.mpBlob);
+ return *this;
+ }
+ BlobReference(const BlobReference& other) = delete;
+ BlobReference& operator=(BlobReference& other) = delete;
+ ~BlobReference() { hb_blob_destroy(mpBlob); }
+};
+}
+
+using BlobCacheKey = std::pair<sal_IntPtr, hb_tag_t>;
+
+namespace
+{
+struct BlobCacheKeyHash
+{
+ std::size_t operator()(BlobCacheKey const& rKey) const
+ {
+ std::size_t seed = 0;
+ o3tl::hash_combine(seed, rKey.first);
+ o3tl::hash_combine(seed, rKey.second);
+ return seed;
+ }
+};
+}
+
+hb_blob_t* WinFontFace::GetHbTable(hb_tag_t nTag) const
+{
+ static o3tl::lru_map<BlobCacheKey, BlobReference, BlobCacheKeyHash> gCache(50);
+ BlobCacheKey aCacheKey{ GetFontId(), nTag };
+ auto it = gCache.find(aCacheKey);
+ if (it != gCache.end())
+ {
+ hb_blob_reference(it->second.mpBlob);
+ return it->second.mpBlob;
+ }
+
+ sal_uLong nLength = 0;
+ unsigned char* pBuffer = nullptr;
+
+ HDC hDC(::GetDC(nullptr));
+ HFONT hFont = ::CreateFontIndirectW(&maLogFont);
+ HFONT hOldFont = ::SelectFont(hDC, hFont);
+
+ nLength = ::GetFontData(hDC, OSL_NETDWORD(nTag), 0, nullptr, 0);
+ if (nLength > 0 && nLength != GDI_ERROR)
+ {
+ pBuffer = new unsigned char[nLength];
+ ::GetFontData(hDC, OSL_NETDWORD(nTag), 0, pBuffer, nLength);
+ }
+
+ ::SelectFont(hDC, hOldFont);
+ ::DeleteFont(hFont);
+ ::ReleaseDC(nullptr, hDC);
+
+ hb_blob_t* pBlob = nullptr;
+
+ if (pBuffer)
+ pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY,
+ pBuffer, [](void* data) { delete[] static_cast<unsigned char*>(data); });
+
+ gCache.insert({ aCacheKey, BlobReference(pBlob) });
+ return pBlob;
+}
+
+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( const vcl::font::FontSelectPattern& rFont,
+ const vcl::font::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.IsMicrosoftSymbolEncoded() ? SYMBOL_CHARSET : DEFAULT_CHARSET;
+ rLogFont.lfPitchAndFamily = ImplPitchToWin( rFont.GetPitch() )
+ | ImplFamilyToWin( rFont.GetFamilyType() );
+ }
+
+ 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.get();
+ rLogFont.lfOrientation = rLogFont.lfEscapement;
+ rLogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+ rLogFont.lfQuality = DEFAULT_QUALITY;
+ rLogFont.lfOutPrecision = OUT_TT_PRECIS;
+ if ( rFont.mnOrientation )
+ rLogFont.lfClipPrecision |= CLIP_LH_ANGLES;
+
+ // disable antialiasing if requested
+ if ( rFont.mbNonAntialiased )
+ rLogFont.lfQuality = NONANTIALIASED_QUALITY;
+
+}
+
+std::tuple<HFONT,bool,sal_Int32> WinSalGraphics::ImplDoSetFont(HDC hDC, vcl::font::FontSelectPattern const & i_rFont,
+ const vcl::font::PhysicalFontFace * i_pFontFace,
+ HFONT& o_rOldFont)
+{
+ HFONT hNewFont = nullptr;
+
+ LOGFONTW aLogFont;
+ ImplGetLogFontFromFontSelect( i_rFont, i_pFontFace, aLogFont );
+
+ bool bIsCJKVerticalFont = false;
+ // select vertical mode for printing if requested and available
+ if ( i_rFont.mbVertical && mbPrinter )
+ {
+ constexpr size_t nLen = sizeof(aLogFont.lfFaceName) - sizeof(aLogFont.lfFaceName[0]);
+ // vertical fonts start with an '@'
+ memmove( &aLogFont.lfFaceName[1], &aLogFont.lfFaceName[0], nLen );
+ aLogFont.lfFaceName[0] = '@';
+ aLogFont.lfFaceName[LF_FACESIZE - 1] = 0;
+
+ // check availability of vertical mode for this font
+ EnumFontFamiliesExW( getHDC(), &aLogFont, SalEnumQueryFontProcExW,
+ reinterpret_cast<LPARAM>(&bIsCJKVerticalFont), 0 );
+ if( !bIsCJKVerticalFont )
+ {
+ // restore non-vertical name if not vertical mode isn't available
+ memcpy( &aLogFont.lfFaceName[0], &aLogFont.lfFaceName[1], nLen );
+ aLogFont.lfFaceName[LF_FACESIZE - 1] = 0;
+ }
+ }
+
+ 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(hDC, hNewFont);
+
+ TEXTMETRICW aTextMetricW;
+ if (!::GetTextMetricsW(hDC, &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(hDC, hNewFont2);
+ DeleteFont( hNewFont );
+ hNewFont = hNewFont2;
+ bIsCJKVerticalFont = false;
+ }
+
+ if( hdcScreen )
+ ::ReleaseDC( nullptr, hdcScreen );
+
+ return std::make_tuple(hNewFont, bIsCJKVerticalFont, static_cast<sal_Int32>(aTextMetricW.tmDescent));
+}
+
+void WinSalGraphics::SetFont(LogicalFontInstance* pFont, int nFallbackLevel)
+{
+ assert(nFallbackLevel >= 0 && nFallbackLevel < MAX_FALLBACK);
+
+ // return early if there is no new font
+ if( !pFont )
+ {
+ if (!mpWinFontEntry[nFallbackLevel].is())
+ return;
+
+ // DeInitGraphics doesn't free the cached fonts, so mhDefFont might be nullptr
+ if (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;
+ }
+}
+
+void WinSalGraphics::GetFontMetric( FontMetricDataRef& 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(OUString(o3tl::toU(aFaceName)));
+
+ rxFontMetric->SetMinKashida(pFontInstance->GetKashidaWidth());
+ rxFontMetric->ImplCalcLineSpacing(pFontInstance.get());
+ rxFontMetric->ImplInitBaselines(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->SetMicrosoftSymbolEncoded(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(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 = new WinFontFace(*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(std::u16string_view 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(vcl::font::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( vcl::font::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()
+{
+ mWinSalGraphicsImplBase->ClearDevFontCache();
+ ImplReleaseTempFonts(*GetSalData(), false);
+}
+
+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;
+
+ tools::Long nX = IntTimes256FromFixed( pHeader->pfxStart.x );
+ tools::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(1.0f/256);
+ rB2DPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(fFactor, fFactor));
+ }
+
+ return true;
+}
+
+IDWriteFontFace* WinFontInstance::GetDWFontFace() const
+{
+ if (!mxDWFontFace)
+ {
+ 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);
+ });
+
+ IDWriteGdiInterop* pDWriteGdiInterop;
+ WinSalGraphics::getDWriteFactory(nullptr, &pDWriteGdiInterop);
+
+ HRESULT hr = pDWriteGdiInterop->CreateFontFaceFromHdc(hDC, &mxDWFontFace);
+ if (FAILED(hr))
+ {
+ SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << ": "
+ << WindowsErrorStringFromHRESULT(hr));
+ mxDWFontFace = nullptr;
+ }
+ }
+
+ return mxDWFontFace;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/vcl/win/gdi/salgdi.cxx b/vcl/win/gdi/salgdi.cxx
new file mode 100644
index 0000000000..cb4b500a4a
--- /dev/null
+++ b/vcl/win/gdi/salgdi.cxx
@@ -0,0 +1,1139 @@
+/* -*- 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 <comphelper/windowserrorstring.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 <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 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
+ tools::Long n;
+
+ pSalData->mhDitherDIB = GlobalAlloc( GMEM_FIXED, sizeof( BITMAPINFOHEADER ) + 192 );
+ pSalData->mpDitherDIB = static_cast<BYTE*>(GlobalLock( pSalData->mhDitherDIB ));
+ pSalData->mpDitherDiff.reset(new tools::Long[ 256 ]);
+ pSalData->mpDitherLow.reset(new BYTE[ 256 ]);
+ pSalData->mpDitherHigh.reset(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
+ tools::Long const nSize = sizeof( BITMAPINFOHEADER ) + ( 256 * sizeof( short ) ) + 64;
+ tools::Long n;
+
+ pSalData->mhDitherDIB = GlobalAlloc( GMEM_FIXED, nSize );
+ pSalData->mpDitherDIB = static_cast<BYTE*>(GlobalLock( pSalData->mhDitherDIB ));
+ pSalData->mpDitherDiff.reset(new tools::Long[ 256 ]);
+ pSalData->mpDitherLow.reset(new BYTE[ 256 ]);
+ pSalData->mpDitherHigh.reset(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 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;
+ pSalData->mpDitherDiff.reset();
+ pSalData->mpDitherLow.reset();
+ pSalData->mpDitherHigh.reset();
+ }
+
+ 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()
+{
+ if (!getHDC())
+ return;
+
+ // 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()
+{
+ if (!getHDC())
+ return;
+
+ // clear clip region
+ SelectClipRgn( getHDC(), nullptr );
+ // select default objects
+ if ( mhDefPen )
+ {
+ SelectPen( getHDC(), mhDefPen );
+ mhDefPen = nullptr;
+ }
+ if ( mhDefBrush )
+ {
+ SelectBrush( getHDC(), mhDefBrush );
+ mhDefBrush = nullptr;
+ }
+ if ( mhDefFont )
+ {
+ SelectFont( getHDC(), mhDefFont );
+ mhDefFont = nullptr;
+ }
+ setPalette(nullptr);
+
+ mpImpl->DeInit();
+}
+
+void WinSalGraphics::setHDC(HDC aNew)
+{
+ DeInitGraphics();
+ mhLocalDC = aNew;
+ InitGraphics();
+}
+
+HDC ImplGetCachedDC( sal_uLong nID, HBITMAP hBmp )
+{
+ SalData* pSalData = GetSalData();
+ HDCCache* pC = &pSalData->maHDCCache[ 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->maHDCCache[ 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->maHDCCache[ 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
+ 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 = rWinGraphics.getWinSalGraphicsImplBase();
+ 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, [[maybe_unused]] 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)
+ {
+ auto const impl = new WinSkiaSalGraphicsImpl(*this, pProvider);
+ mpImpl.reset(impl);
+ mWinSalGraphicsImplBase = impl;
+ }
+ else
+#endif
+ {
+ auto const impl = new WinSalGraphicsImpl(*this);
+ mpImpl.reset(impl);
+ mWinSalGraphicsImplBase = impl;
+ }
+}
+
+WinSalGraphics::~WinSalGraphics()
+{
+ // free obsolete GDI objects
+ ReleaseFonts();
+
+ if ( mhRegion )
+ {
+ DeleteRegion( mhRegion );
+ mhRegion = nullptr;
+ }
+
+ // delete cache data
+ delete [] reinterpret_cast<BYTE*>(mpStdClipRgnData);
+
+ setHDC(nullptr);
+}
+
+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
+{
+ assert(getHDC() || !mhDefPal);
+ return mhDefPal;
+}
+
+UINT WinSalGraphics::setPalette(HPALETTE hNewPal, BOOL bForceBkgd)
+{
+ UINT res = GDI_ERROR;
+
+ if (!getHDC())
+ {
+ assert(!mhDefPal);
+ return res;
+ }
+
+ if (hNewPal)
+ {
+ HPALETTE hOldPal = SelectPalette(getHDC(), hNewPal, bForceBkgd);
+ if (hOldPal)
+ {
+ if (!mhDefPal)
+ mhDefPal = hOldPal;
+ res = RealizePalette(getHDC());
+ }
+ }
+ else
+ {
+ res = 0;
+ if (mhDefPal)
+ {
+ SelectPalette(getHDC(), mhDefPal, bForceBkgd);
+ mhDefPal = nullptr;
+ }
+ }
+
+ return res;
+}
+
+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;
+}
+
+void WinSalGraphics::getDWriteFactory(IDWriteFactory** pFactory, IDWriteGdiInterop** pInterop)
+{
+ if (!bDWriteDone)
+ {
+ HRESULT hr = S_OK;
+ hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
+ reinterpret_cast<IUnknown**>(&mxDWriteFactory));
+ if (FAILED(hr))
+ {
+ SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << ": "
+ << WindowsErrorStringFromHRESULT(hr));
+ abort();
+ }
+
+ hr = mxDWriteFactory->GetGdiInterop(&mxDWriteGdiInterop);
+ if (FAILED(hr))
+ {
+ SAL_WARN("vcl.fonts", "HRESULT 0x" << OUString::number(hr, 16) << ": "
+ << WindowsErrorStringFromHRESULT(hr));
+ abort();
+ }
+
+ bDWriteDone = true;
+ }
+
+ if (pFactory)
+ *pFactory = mxDWriteFactory.get();
+ if (pInterop)
+ *pInterop = mxDWriteGdiInterop.get();
+}
+
+sal_uInt16 WinSalGraphics::GetBitCount() const
+{
+ return mpImpl->GetBitCount();
+}
+
+tools::Long WinSalGraphics::GetGraphicsWidth() const
+{
+ return mpImpl->GetGraphicsWidth();
+}
+
+void WinSalGraphics::Flush()
+{
+ mWinSalGraphicsImplBase->Flush();
+}
+
+void WinSalGraphics::ResetClipRegion()
+{
+ mpImpl->ResetClipRegion();
+}
+
+void WinSalGraphics::setClipRegion( const vcl::Region& i_rClip )
+{
+ 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( tools::Long nX, tools::Long nY )
+{
+ mpImpl->drawPixel( nX, nY );
+}
+
+void WinSalGraphics::drawPixel( tools::Long nX, tools::Long nY, Color nColor )
+{
+ mpImpl->drawPixel( nX, nY, nColor );
+}
+
+void WinSalGraphics::drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2 )
+{
+ mpImpl->drawLine( nX1, nY1, nX2, nY2 );
+}
+
+void WinSalGraphics::drawRect( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight )
+{
+ mpImpl->drawRect( nX, nY, nWidth, nHeight );
+}
+
+void WinSalGraphics::drawPolyLine( sal_uInt32 nPoints, const Point* pPtAry )
+{
+ mpImpl->drawPolyLine( nPoints, pPtAry );
+}
+
+void WinSalGraphics::drawPolygon( sal_uInt32 nPoints, const Point* pPtAry )
+{
+ mpImpl->drawPolygon( nPoints, pPtAry );
+}
+
+void WinSalGraphics::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ const Point** pPtAry )
+{
+ mpImpl->drawPolyPolygon( nPoly, pPoints, pPtAry );
+}
+
+bool WinSalGraphics::drawPolyLineBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
+{
+ return mpImpl->drawPolyLineBezier( nPoints, pPtAry, pFlgAry );
+}
+
+bool WinSalGraphics::drawPolygonBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
+{
+ return mpImpl->drawPolygonBezier( nPoints, pPtAry, pFlgAry );
+}
+
+bool WinSalGraphics::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints,
+ const Point* const* pPtAry, const PolyFlags* const* pFlgAry )
+{
+ return mpImpl->drawPolyPolygonBezier( nPoly, pPoints, pPtAry, pFlgAry );
+}
+
+bool WinSalGraphics::drawGradient(const tools::PolyPolygon& rPoly, const Gradient& rGradient)
+{
+ return mpImpl->drawGradient(rPoly, rGradient);
+}
+
+bool WinSalGraphics::implDrawGradient(basegfx::B2DPolyPolygon const & rPolyPolygon, SalGradient const & rGradient)
+{
+ return mpImpl->implDrawGradient(rPolyPolygon, rGradient);
+}
+
+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( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::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"
+ + OString::number(pRect->left) + " " + OString::number(pRect->top)
+ + " moveto\n"
+ + OString::number(pRect->right) + " " + OString::number(pRect->top)
+ + " lineto\n"
+ + OString::number(pRect->right) + " "
+ + OString::number(pRect->bottom) + " lineto\n"
+ + OString::number(pRect->left) + " "
+ + OString::number(pRect->bottom) + " 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[" + OString::number(dM11) + " 0 0 " + OString::number(dM22) + " "
+ + OString::number(nX - ( dM11 * nBoundingBox[0] )) + " "
+ + OString::number(nY - ( dM22 * nBoundingBox[3] )) + "] 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 0000000000..409fcc74bd
--- /dev/null
+++ b/vcl/win/gdi/salgdi2.cxx
@@ -0,0 +1,240 @@
+/* -*- 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 <vcl/BitmapAccessMode.hxx>
+#include <vcl/BitmapBuffer.hxx>
+#include <vcl/BitmapPalette.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/Scanline.hxx>
+#include <salgdiimpl.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( tools::Long nDestX, tools::Long nDestY,
+ tools::Long nSrcX, tools::Long nSrcY,
+ tools::Long nSrcWidth, tools::Long nSrcHeight,
+ bool bWindowInvalidate )
+{
+ mpImpl->copyArea( nDestX, nDestY, nSrcX, nSrcY,
+ nSrcWidth, nSrcHeight, bWindowInvalidate );
+}
+
+namespace
+{
+
+class ColorScanlineConverter
+{
+public:
+ ScanlineFormat meSourceFormat;
+
+ int mnComponentSize;
+ int mnComponentExchangeIndex;
+
+ tools::Long mnScanlineSize;
+
+ ColorScanlineConverter(ScanlineFormat eSourceFormat, int nComponentSize, tools::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 (tools::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;
+#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(), vcl::bitDepthToPixelFormat(rSalBitmap.GetBitCount()), aBitmapPalette);
+ BitmapBuffer* pWrite = rWinSalBitmap.AcquireBuffer(BitmapAccessMode::Write);
+
+ sal_uInt8* pSource(pRead->mpBits);
+ sal_uInt8* pDestination(pWrite->mpBits);
+ tools::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 (tools::Long y = 0; y < pRead->mnHeight; y++)
+ {
+ pConverter->convertScanline(pSource, pDestination);
+ pSource += readRowChange;
+ pDestination += pWrite->mnScanlineSize;
+ }
+ }
+ else
+ {
+ for (tools::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<const WinSalBitmap*>(&rSalBitmap) == nullptr
+#if HAVE_FEATURE_SKIA
+ && dynamic_cast<WinSkiaSalGraphicsImpl*>(mpImpl.get()) == nullptr
+#endif
+ )
+ {
+ 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<const WinSalBitmap*>(&rSSalBitmap) == nullptr
+#if HAVE_FEATURE_SKIA
+ && dynamic_cast<WinSkiaSalGraphicsImpl*>(mpImpl.get()) == nullptr
+#endif
+ )
+ {
+ 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( tools::Long nX, tools::Long nY, tools::Long nWidth,
+ tools::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( tools::Long nX, tools::Long nY, tools::Long nDX, tools::Long nDY )
+{
+ return mpImpl->getBitmap( nX, nY, nDX, nDY );
+}
+
+Color WinSalGraphics::getPixel( tools::Long nX, tools::Long nY )
+{
+ return mpImpl->getPixel( nX, nY );
+}
+
+void WinSalGraphics::invert( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, SalInvert nFlags )
+{
+ mpImpl->invert( nX, nY, nWidth, nHeight, nFlags );
+}
+
+void WinSalGraphics::invert( sal_uInt32 nPoints, const Point* 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 0000000000..198f755b23
--- /dev/null
+++ b/vcl/win/gdi/salgdi_gdiplus.cxx
@@ -0,0 +1,104 @@
+/* -*- 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"
+
+void WinSalGraphics::drawPolyPolygon(
+ const basegfx::B2DHomMatrix& rObjectToDevice,
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ double fTransparency)
+{
+ 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,
+ double fAlpha)
+{
+ return mpImpl->drawTransformedBitmap(rNull, rX, rY,
+ rSourceBitmap, pAlphaBitmap, fAlpha);
+}
+
+bool WinSalGraphics::hasFastDrawTransformedBitmap() const
+{
+ return mpImpl->hasFastDrawTransformedBitmap();
+}
+
+/* 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 0000000000..318009acad
--- /dev/null
+++ b/vcl/win/gdi/salnativewidgets-luna.cxx
@@ -0,0 +1,1634 @@
+/* -*- 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 <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <toolbarvalue.hxx>
+#include <menubarvalue.hxx>
+
+#include <win/svsys.h>
+#include <win/salgdi.h>
+#include <win/saldata.hxx>
+#include <win/salframe.h>
+#include <win/scoped_gdi.hxx>
+#include <win/wingdiimpl.hxx>
+
+#include <uxtheme.h>
+#include <vssym32.h>
+
+#include <map>
+#include <string>
+#include <optional>
+#include <ControlCacheKey.hxx>
+
+typedef std::map< std::wstring, HTHEME > ThemeMap;
+static ThemeMap aThemeMap;
+
+/*********************************************************
+ * 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 )
+ CloseThemeData(rEntry.second);
+ aThemeMap.clear();
+}
+
+static HTHEME getThemeHandle(HWND hWnd, LPCWSTR name, WinSalGraphicsImplBase* pGraphicsImpl)
+{
+ if( GetSalData()->mbThemeChanged )
+ {
+ // throw away invalid theme handles
+ SalData::deInitNWF();
+ // throw away native control cache
+ pGraphicsImpl->ClearNativeControlCache();
+ 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 = 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", mWinSalGraphicsImplBase);
+ 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", mWinSalGraphicsImplBase);
+ 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", mWinSalGraphicsImplBase);
+ else if( nPart == ControlPart::ButtonDown )
+ hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Spinbox:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
+ else if( nPart == ControlPart::AllButtons ||
+ nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonDown ||
+ nPart == ControlPart::ButtonLeft|| nPart == ControlPart::ButtonRight )
+ hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::SpinButtons:
+ if( nPart == ControlPart::Entire || nPart == ControlPart::AllButtons )
+ hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
+ 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", mWinSalGraphicsImplBase);
+ 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", mWinSalGraphicsImplBase);
+ else if( nPart == ControlPart::ButtonDown )
+ hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::TabPane:
+ case ControlType::TabBody:
+ case ControlType::TabItem:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle(mhWnd, L"Tab", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Toolbar:
+ if( nPart == ControlPart::Entire || nPart == ControlPart::Button )
+ hTheme = getThemeHandle(mhWnd, L"Toolbar", mWinSalGraphicsImplBase);
+ else
+ // use rebar theme for grip and background
+ hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Menubar:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
+ else if( GetSalData()->mbThemeMenuSupport )
+ {
+ if( nPart == ControlPart::MenuItem )
+ hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
+ }
+ 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", mWinSalGraphicsImplBase);
+ }
+ break;
+ case ControlType::Progress:
+ case ControlType::LevelBar:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle(mhWnd, L"Progress", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Slider:
+ if( nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea )
+ hTheme = getThemeHandle(mhWnd, L"Trackbar", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::ListNode:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle(mhWnd, L"TreeView", mWinSalGraphicsImplBase);
+ 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 = DrawThemeBackground( hTheme, hDC, iPart, iState, &rc, nullptr);
+
+ if( aStr.getLength() )
+ {
+ RECT rcContent;
+ hr = GetThemeBackgroundContentRect( hTheme, hDC, iPart, iState, &rc, &rcContent);
+ hr = DrawThemeText( hTheme, hDC, iPart, iState,
+ o3tl::toW(aStr.getStr()), -1,
+ DT_CENTER | DT_VCENTER | DT_SINGLELINE,
+ 0, &rcContent);
+ }
+ return (hr == S_OK);
+}
+
+// TS_TRUE returns optimal size
+static std::optional<Size> ImplGetThemeSize(HTHEME hTheme, HDC hDC, int iPart, int iState, LPCRECT pRect, THEMESIZE eTS = TS_TRUE)
+{
+ if (SIZE aSz; SUCCEEDED(GetThemePartSize(hTheme, hDC, iPart, iState, pRect, eTS, &aSz)))
+ return Size(aSz.cx, aSz.cy);
+ return {};
+}
+
+static tools::Rectangle ImplGetThemeRect( HTHEME hTheme, HDC hDC, int iPart, int iState, const tools::Rectangle& /* aRect */, THEMESIZE eTS = TS_TRUE )
+{
+ if (const std::optional<Size> oSz = ImplGetThemeSize(hTheme, hDC, iPart, iState, nullptr, eTS))
+ return tools::Rectangle( 0, 0, oSz->Width(), oSz->Height() );
+ 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 int 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 );
+ }
+ }
+}
+
+static bool implDrawNativeMenuMark(HDC hDC, HTHEME hTheme, RECT rc, ControlPart nPart,
+ ControlState nState, OUString const& aCaption)
+{
+ int iState = (nState & ControlState::ENABLED) ? MCB_NORMAL : MCB_DISABLED;
+ ImplDrawTheme(hTheme, hDC, MENU_POPUPCHECKBACKGROUND, iState, rc, aCaption);
+ if (nPart == ControlPart::MenuItemCheckMark)
+ iState = (nState & ControlState::ENABLED) ? MC_CHECKMARKNORMAL : MC_CHECKMARKDISABLED;
+ else
+ iState = (nState & ControlState::ENABLED) ? MC_BULLETNORMAL : MC_BULLETDISABLED;
+ // tdf#133697: Get true size of mark, to avoid stretching
+ if (auto oSize = ImplGetThemeSize(hTheme, hDC, MENU_POPUPCHECK, iState, &rc))
+ {
+ // center the mark inside the passed rectangle
+ if (const auto dx = (rc.right - rc.left - oSize->Width() + 1) / 2; dx > 0)
+ {
+ rc.left += dx;
+ rc.right = rc.left + oSize->Width();
+ }
+ if (const auto dy = (rc.bottom - rc.top - oSize->Height() + 1) / 2; dy > 0)
+ {
+ rc.top += dy;
+ rc.bottom = rc.top + oSize->Height();
+ }
+ }
+ return ImplDrawTheme(hTheme, hDC, MENU_POPUPCHECK, iState, rc, aCaption);
+}
+
+bool UseDarkMode()
+{
+ static bool bOSSupportsDarkMode = OSSupportsDarkMode();
+ if (!bOSSupportsDarkMode)
+ return false;
+
+ bool bRet(false);
+ switch (MiscSettings::GetDarkMode())
+ {
+ case 0: // auto
+ default:
+ {
+ HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (!hUxthemeLib)
+ return false;
+
+ typedef bool(WINAPI* ShouldAppsUseDarkMode_t)();
+ if (auto ShouldAppsUseDarkMode = reinterpret_cast<ShouldAppsUseDarkMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(132))))
+ bRet = ShouldAppsUseDarkMode();
+
+ FreeLibrary(hUxthemeLib);
+ break;
+ }
+ case 1: // light
+ bRet = false;
+ break;
+ case 2: // dark
+ bRet = true;
+ break;
+ }
+
+ return bRet;
+}
+
+static bool ImplDrawNativeControl( HDC hDC, HTHEME hTheme, RECT rc,
+ ControlType nType,
+ ControlPart nPart,
+ ControlState nState,
+ const ImplControlValue& aValue,
+ OUString const & aCaption,
+ bool bUseDarkMode )
+{
+ // 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 = 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 = 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 = 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 = 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;
+ GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_MIN, &sz);
+ GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_TRUE, &sz);
+ GetThemePartSize(hTheme, hDC, iPart, iState, nullptr, TS_DRAW, &sz);
+
+ hr = 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;
+ 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 = 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
+ //GetThemePartSize( hTheme, hDC, iPart, iState, &rc, eSize, &sz);
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if (nType == ControlType::Editbox)
+ {
+ iPart = EP_EDITBORDER_NOSCROLL;
+ if( !(nState & ControlState::ENABLED) )
+ iState = EPSN_DISABLED;
+ else if( nState & ControlState::FOCUSED )
+ iState = EPSN_FOCUSED;
+ else if( nState & ControlState::ROLLOVER )
+ iState = EPSN_HOT;
+ else
+ iState = EPSN_NORMAL;
+
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if (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 )
+ {
+ // tabpane in tabcontrols gets drawn in "darkmode" as if it was a
+ // a "light" theme, so bodge this by drawing a frame directly
+ if (bUseDarkMode)
+ {
+ Color aColor(Application::GetSettings().GetStyleSettings().GetDisableColor());
+ ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
+ aColor.GetGreen(),
+ aColor.GetBlue())));
+ FrameRect(hDC, &rc, hbrush.get());
+ return true;
+ }
+ iPart = TABP_PANE;
+ return ImplDrawTheme( hTheme, hDC, iPart, iState, rc, aCaption);
+ }
+
+ if( nType == ControlType::TabBody )
+ {
+ // tabbody in main window gets drawn in white in "darkmode", so bodge this here
+ if (bUseDarkMode)
+ {
+ Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
+ ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
+ aColor.GetGreen(),
+ aColor.GetBlue())));
+ FillRect(hDC, &rc, hbrush.get());
+ return true;
+ }
+
+ 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;
+
+ // tabitem in tabcontrols gets drawn in "darkmode" as if it was a
+ // a "light" theme, so bodge this by drawing with a button instead
+ if (bUseDarkMode)
+ {
+ Color aColor;
+ if (iState == TILES_SELECTED)
+ aColor = Application::GetSettings().GetStyleSettings().GetActiveTabColor();
+ else
+ aColor = Application::GetSettings().GetStyleSettings().GetInactiveTabColor();
+ ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
+ aColor.GetGreen(),
+ aColor.GetBlue())));
+ FillRect(hDC, &rc, hbrush.get());
+
+ aColor = Application::GetSettings().GetStyleSettings().GetDisableColor();
+ ScopedSelectedHPEN hPen(hDC, CreatePen(PS_SOLID, 1, RGB(aColor.GetRed(),
+ aColor.GetGreen(),
+ aColor.GetBlue())));
+ POINT apt[4];
+ apt[0].x = rc.left;
+ apt[0].y = rc.bottom - (iPart == TABP_TABITEMLEFTEDGE ? 1 : 2);
+ apt[1].x = rc.left;
+ apt[1].y = rc.top;
+ apt[2].x = rc.right;
+ apt[2].y = rc.top;
+ apt[3].x = rc.right;
+ apt[3].y = rc.bottom - 1;
+ Polyline(hDC, apt, SAL_N_ELEMENTS(apt));
+ return true;
+ }
+
+ 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
+ }
+
+ // toolbar in main window gets drawn in white in "darkmode", so bodge this here
+ if (bUseDarkMode)
+ {
+ Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
+ ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
+ aColor.GetGreen(),
+ aColor.GetBlue())));
+ FillRect(hDC, &rc, hbrush.get());
+ return true;
+ }
+
+ // make it more compatible with Aero
+ if (ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames &&
+ !Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ 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
+
+ // menubar in main window gets drawn in white in "darkmode", so bodge this here
+ if (bUseDarkMode)
+ {
+ Color aColor(Application::GetSettings().GetStyleSettings().GetWindowColor());
+ ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
+ aColor.GetGreen(),
+ aColor.GetBlue())));
+ FillRect(hDC, &rc, hbrush.get());
+ return true;
+ }
+
+ // make it more compatible with Aero
+ if (ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames &&
+ !Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ 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;
+
+ if(GetSalData()->mbThemeMenuSupport && Application::GetSettings().GetStyleSettings().GetHighContrastMode()
+ && ( nState & (ControlState::SELECTED | nState & ControlState::ROLLOVER )))
+ {
+ Color aColor(Application::GetSettings().GetStyleSettings().GetHighlightColor());
+ ScopedHBRUSH hbrush(CreateSolidBrush(RGB(aColor.GetRed(),
+ aColor.GetGreen(),
+ aColor.GetBlue())));
+ FillRect(hDC, &rc, hbrush.get());
+ return true;
+ }
+ }
+ 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 || nType == ControlType::LevelBar )
+ {
+ if( nPart != ControlPart::Entire )
+ return false;
+
+ int nPartIdBackground = PP_BAR;
+ if( nType == ControlType::LevelBar )
+ {
+ nPartIdBackground = PP_TRANSPARENTBAR;
+ iState = PBBS_PARTIAL;
+ }
+
+ if( ! ImplDrawTheme( hTheme, hDC, nPartIdBackground, iState, rc, aCaption) )
+ return false;
+ RECT aProgressRect = rc;
+ if( GetThemeBackgroundContentRect( hTheme, hDC, PP_BAR, iState, &rc, &aProgressRect) != S_OK )
+ return false;
+
+ tools::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;
+
+ if (nType == ControlType::LevelBar)
+ {
+ const auto nPercentage
+ = aValue.getNumericVal() * 100 / std::max(LONG{ 1 }, (rc.right - rc.left));
+
+ COLORREF aBrushColor{};
+ if (nPercentage < 25)
+ aBrushColor = RGB(255, 0, 0);
+ else if (nPercentage < 50)
+ aBrushColor = RGB(255, 255, 0);
+ else if (nPercentage < 75)
+ aBrushColor = RGB(0, 0, 255);
+ else
+ aBrushColor = RGB(0, 255, 0);
+
+ ScopedHBRUSH hBrush(CreateSolidBrush(aBrushColor));
+ FillRect(hDC, &aProgressRect, hBrush.get());
+ return true;
+ }
+
+ 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 )
+ {
+ tools::Long nH = aTrackRect.GetHeight();
+ aTRect.top += (rc.bottom - rc.top - nH)/2;
+ aTRect.bottom = aTRect.top + nH;
+ }
+ else
+ {
+ tools::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)
+ return implDrawNativeMenuMark(hDC, hTheme, rc, nPart, nState, 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
+ auto const 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 = mWinSalGraphicsImplBase;
+ 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();
+ }
+ }
+
+
+ ControlCacheKey aControlCacheKey(nType, nPart, nState, keySize);
+ if (pImpl != nullptr && pImpl->TryRenderCachedNativeControl(aControlCacheKey, buttonRect.Left(), buttonRect.Top()))
+ {
+ return true;
+ }
+
+ const bool bUseDarkMode = UseDarkMode();
+ if (bUseDarkMode)
+ SetWindowTheme(mhWnd, L"Explorer", nullptr);
+
+ switch( nType )
+ {
+ case ControlType::Pushbutton:
+ case ControlType::Radiobutton:
+ case ControlType::Checkbox:
+ hTheme = getThemeHandle(mhWnd, L"Button", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Scrollbar:
+ if (bUseDarkMode)
+ {
+ // tdf#153273 undo the earlier SetWindowTheme, and use an explicit Explorer::Scrollbar
+ // a) with "Scrollbar" and SetWindowTheme(... "Explorer" ...) then scrollbars in dialog
+ // and main windows are dark, but dropdowns are light
+ // b) with "Explorer::Scrollbar" and SetWindowTheme(... "Explorer" ...) then scrollbars
+ // in dropdowns are dark, but scrollbars in dialogs and main windows are sort of "extra
+ // dark"
+ // c) with "Explorer::Scrollbar" and no SetWindowTheme both cases are dark
+ SetWindowTheme(mhWnd, nullptr, nullptr);
+ hTheme = getThemeHandle(mhWnd, L"Explorer::Scrollbar", mWinSalGraphicsImplBase);
+ }
+ else
+ hTheme = getThemeHandle(mhWnd, L"Scrollbar", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Combobox:
+ if( nPart == ControlPart::Entire )
+ {
+ if (bUseDarkMode && !(nState & ControlState::FOCUSED))
+ SetWindowTheme(mhWnd, L"CFD", nullptr);
+ hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
+ }
+ else if( nPart == ControlPart::ButtonDown )
+ {
+ if (bUseDarkMode)
+ SetWindowTheme(mhWnd, L"CFD", nullptr);
+ hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
+ }
+ break;
+ case ControlType::Spinbox:
+ if( nPart == ControlPart::Entire )
+ {
+ if (bUseDarkMode && !(nState & ControlState::FOCUSED))
+ SetWindowTheme(mhWnd, L"CFD", nullptr);
+ hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
+ }
+ else
+ hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::SpinButtons:
+ hTheme = getThemeHandle(mhWnd, L"Spin", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Editbox:
+ if (bUseDarkMode && !(nState & ControlState::FOCUSED))
+ SetWindowTheme(mhWnd, L"CFD", nullptr);
+ hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::MultilineEditbox:
+ hTheme = getThemeHandle(mhWnd, L"Edit", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Listbox:
+ if( nPart == ControlPart::Entire || nPart == ControlPart::ListboxWindow )
+ hTheme = getThemeHandle(mhWnd, L"Listview", mWinSalGraphicsImplBase);
+ else if( nPart == ControlPart::ButtonDown )
+ {
+ if (bUseDarkMode)
+ SetWindowTheme(mhWnd, L"CFD", nullptr);
+ hTheme = getThemeHandle(mhWnd, L"Combobox", mWinSalGraphicsImplBase);
+ }
+ break;
+ case ControlType::TabBody:
+ hTheme = getThemeHandle(mhWnd, L"Tab", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::TabPane:
+ case ControlType::TabItem:
+ hTheme = getThemeHandle(mhWnd, L"Tab", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Toolbar:
+ if( nPart == ControlPart::Entire || nPart == ControlPart::Button )
+ hTheme = getThemeHandle(mhWnd, L"Toolbar", mWinSalGraphicsImplBase);
+ else
+ // use rebar for grip and background
+ hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Menubar:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle(mhWnd, L"Rebar", mWinSalGraphicsImplBase);
+ else if( GetSalData()->mbThemeMenuSupport )
+ {
+ if( nPart == ControlPart::MenuItem )
+ hTheme = getThemeHandle(mhWnd, L"Menu", mWinSalGraphicsImplBase);
+ }
+ break;
+ case ControlType::Progress:
+ case ControlType::LevelBar:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle(mhWnd, L"Progress", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::ListNode:
+ if( nPart == ControlPart::Entire )
+ hTheme = getThemeHandle(mhWnd, L"TreeView", mWinSalGraphicsImplBase);
+ break;
+ case ControlType::Slider:
+ if( nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea )
+ hTheme = getThemeHandle(mhWnd, L"Trackbar", mWinSalGraphicsImplBase);
+ 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", mWinSalGraphicsImplBase);
+ }
+ break;
+ default:
+ hTheme = nullptr;
+ break;
+ }
+
+ if( !hTheme )
+ {
+ if (bUseDarkMode)
+ SetWindowTheme(mhWnd, nullptr, nullptr);
+ 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, bUseDarkMode);
+
+ // 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, bUseDarkMode) &&
+ ImplDrawNativeControl(aWhiteDC->getCompatibleHDC(), hTheme, rc, nType, nPart, nState, aValue, aCaptionStr, bUseDarkMode))
+ {
+ bOk = pImpl->RenderAndCacheNativeControl(*aWhiteDC, *aBlackDC, cacheRect.Left(), cacheRect.Top(), aControlCacheKey);
+ }
+ }
+
+ if (bUseDarkMode)
+ SetWindowTheme(mhWnd, nullptr, nullptr);
+ 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", mWinSalGraphicsImplBase);
+ 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", mWinSalGraphicsImplBase);
+ 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", mWinSalGraphicsImplBase);
+ 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", mWinSalGraphicsImplBase);
+ 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", mWinSalGraphicsImplBase);
+ 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", mWinSalGraphicsImplBase);
+ tools::Rectangle aBoxRect( rControlRegion );
+ tools::Rectangle aRect( ImplGetThemeRect( hTheme, hDC,
+ MENU_POPUPCHECK,
+ MC_CHECKMARKNORMAL,
+ aBoxRect ) );
+ if (!aRect.IsEmpty())
+ {
+ if (MARGINS mg;
+ SUCCEEDED(GetThemeMargins(hTheme, hDC, MENU_POPUPCHECK, MC_CHECKMARKNORMAL,
+ TMT_CONTENTMARGINS, nullptr, &mg)))
+ {
+ aRect.AdjustLeft(-mg.cxLeftWidth);
+ aRect.AdjustRight(mg.cxRightWidth);
+ aRect.AdjustTop(-mg.cyTopHeight);
+ aRect.AdjustBottom(mg.cyBottomHeight);
+ }
+ rNativeContentRegion = rNativeBoundingRegion = aRect;
+ bRet = true;
+ }
+ }
+ }
+ }
+
+ if( nType == ControlType::Slider && ( (nPart == ControlPart::ThumbHorz) || (nPart == ControlPart::ThumbVert) ) )
+ {
+ HTHEME hTheme = getThemeHandle(mhWnd, L"Trackbar", mWinSalGraphicsImplBase);
+ 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 )
+ {
+ tools::Long nW = aThumbRect.GetWidth();
+ tools::Rectangle aRect( rControlRegion );
+ aRect.SetRight( aRect.Left() + nW - 1 );
+ rNativeContentRegion = aRect;
+ rNativeBoundingRegion = rNativeContentRegion;
+ }
+ else
+ {
+ tools::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 ( !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
+ // high contrast highlight color is not related to persona and not apply blur or transparency
+ if( !aStyleSettings.GetHighContrastMode() )
+ {
+ 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 0000000000..3302efa2d9
--- /dev/null
+++ b/vcl/win/gdi/salprn.cxx
@@ -0,0 +1,1604 @@
+/* -*- 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 <vector>
+#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 <vcl/QueueInfo.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 <comphelper/windowsdebugoutput.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 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->moPortName )
+ pInfo->moPortName = aPortName;
+ }
+ std::free(pWinInfo2);
+ }
+ ClosePrinter( hPrinter );
+ }
+}
+
+OUString WinSalInstance::GetDefaultPrinter()
+{
+ DWORD nChars = 0;
+ GetDefaultPrinterW( nullptr, &nChars );
+ if( nChars )
+ {
+ std::vector<WCHAR> pStr(nChars);
+ if (GetDefaultPrinterW(pStr.data(), &nChars))
+ return OUString(o3tl::toU(pStr.data()));
+ }
+ 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<tools::Long>(pSetupData->GetDriverDataLen() - pSetupDriverData->mnDriverOffset) == nSysJobSize &&
+ pSetupDriverData->mnSysSignature == SAL_DRIVERDATA_SYSSIGN )
+ {
+ if( pDevModeW &&
+ (dmSpecVersion == pDevModeW->dmSpecVersion) &&
+ (dmDriverVersion == pDevModeW->dmDriverVersion) )
+ return true;
+ }
+ if ( bDelete )
+ {
+ pSetupData->SetDriverData( nullptr, 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;
+ std::unique_ptr<sal_uInt8[]> pDriverData;
+ 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;
+ pDriverData = std::make_unique<sal_uInt8[]>( nDriverDataLen );
+ memset(pDriverData.get(), 0, nDriverDataLen);
+ pOutBuffer = reinterpret_cast<SalDriverData*>(pDriverData.get());
+ 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)) )
+ 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
+ pSetupData->SetDriverData(std::move(pDriverData), nDriverDataLen);
+ 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);
+ return pGraphics;
+}
+
+static bool ImplUpdateSalPrnIC( WinSalInfoPrinter* pPrinter, const ImplJobSetup* pSetupData )
+{
+ HDC hNewDC = ImplCreateSalPrnIC( pPrinter, pSetupData );
+ if ( !hNewDC )
+ return false;
+
+ pPrinter->setHDC(hNewDC);
+ return true;
+}
+
+
+SalInfoPrinter* WinSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
+ ImplJobSetup* pSetupData )
+{
+ WinSalInfoPrinter* pPrinter = new WinSalInfoPrinter;
+ if( ! pQueueInfo->moPortName )
+ GetPrinterQueueState( pQueueInfo );
+ pPrinter->maDriverName = pQueueInfo->maDriver;
+ pPrinter->maDeviceName = pQueueInfo->maPrinterName;
+ pPrinter->maPortName = pQueueInfo->moPortName ? *pQueueInfo->moPortName : 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->setHDC(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() :
+ m_hDC(nullptr),
+ m_pGraphics(nullptr),
+ m_bGraphics(false)
+{
+ m_bPapersInit = false;
+}
+
+WinSalInfoPrinter::~WinSalInfoPrinter()
+{
+ setHDC(nullptr);
+}
+
+void WinSalInfoPrinter::setHDC(HDC hNewDC)
+{
+ assert(!m_bGraphics);
+
+ if (m_hDC)
+ {
+ assert(!m_pGraphics || m_hDC == m_pGraphics->getHDC());
+ delete m_pGraphics;
+ m_pGraphics = nullptr;
+ DeleteDC(m_hDC);
+ }
+
+ m_hDC = hNewDC;
+}
+
+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 );
+
+ SAL_INFO("vcl.print", "DC_PAPERSIZE sizes (mm) from printer: " << DC_PAPERSIZE_array_to_string(pPaperSizes, nCount));
+
+ 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()
+{
+ assert(m_hDC);
+ if (m_bGraphics)
+ return nullptr;
+
+ if (!m_pGraphics)
+ m_pGraphics = ImplCreateSalPrnGraphics(m_hDC);
+ if (m_pGraphics)
+ m_bGraphics = true;
+
+ return m_pGraphics;
+}
+
+void WinSalInfoPrinter::ReleaseGraphics( SalGraphics* )
+{
+ m_bGraphics = 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*,
+ tools::Long& rOutWidth, tools::Long& rOutHeight,
+ Point& rPageOffset,
+ Size& rPaperSize )
+{
+ HDC hDC = m_hDC;
+
+ 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_uInt32 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() :
+ 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 )
+ {
+ // explicitly reset(), so the mxGraphics's borrowed HDC defaults are
+ // restored and WinSalGraphics's destructor won't work on a deleted HDC.
+ mxGraphics.reset();
+ 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;
+ }
+}
+
+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_StartDocW1( 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;
+}
+
+static int lcl_StartDocW( HDC hDC, DOCINFOW const * pInfo, WinSalPrinter* pPrt )
+{
+ //tdf#127547 - Freeze/crash in Microsoft Print to PDF dialog, if we try to paste while
+ // executing the StartDocW method, Windows will call back into us on a separate thread,
+ // where we will attempt to take the SolarMutex.
+ SolarMutexReleaser aReleaser;
+
+ return lcl_StartDocW1(hDC, pInfo, pPrt);
+}
+
+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 )
+ {
+ DWORD 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())
+ {
+ mxGraphics.reset();
+
+ // #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())
+ 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 );
+
+ mxGraphics.reset(ImplCreateSalPrnGraphics( hDC ));
+ return mxGraphics.get();
+}
+
+void WinSalPrinter::EndPage()
+{
+ mxGraphics.reset();
+
+ if (!isValid())
+ return;
+
+ HDC hDC = mhDC;
+ 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 0000000000..7b3e7e11fc
--- /dev/null
+++ b/vcl/win/gdi/salvd.cxx
@@ -0,0 +1,223 @@
+/* -*- 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 <sal/log.hxx>
+#include <o3tl/temporary.hxx>
+
+HBITMAP WinSalVirtualDevice::ImplCreateVirDevBitmap(HDC hDC, tools::Long nDX, tools::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& rSGraphics,
+ tools::Long &nDX, tools::Long &nDY,
+ DeviceFormat /*eFormat*/,
+ const SystemGraphicsData* pData )
+{
+ WinSalGraphics& rGraphics = static_cast<WinSalGraphics&>(rSGraphics);
+ 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( rGraphics.getHDC() );
+ SAL_WARN_IF( !hDC, "vcl", "CreateCompatibleDC failed: " << WindowsErrorString( GetLastError() ) );
+ }
+
+ if (!hDC)
+ return nullptr;
+
+ sal_uInt16 nBitCount = 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(rGraphics.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,
+ rGraphics.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->setPalette(pSalData->mhDitherPal);
+ RealizePalette( hDC );
+ }
+
+ pVDev->setGraphics(pVirGraphics);
+
+ return std::unique_ptr<SalVirtualDevice>(pVDev);
+}
+
+WinSalVirtualDevice::WinSalVirtualDevice(HDC hDC, HBITMAP hBMP, sal_uInt16 nBitCount, bool bForeignDC, tools::Long nWidth, tools::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;
+
+ HDC hDC = mpGraphics->getHDC();
+ // restore the mpGraphics' original HDC values, so the HDC can be deleted in the !mbForeignDC case
+ mpGraphics->setHDC(nullptr);
+
+ if( mhDefBmp )
+ SelectBitmap(hDC, mhDefBmp);
+ if( !mbForeignDC )
+ DeleteDC(hDC);
+}
+
+SalGraphics* WinSalVirtualDevice::AcquireGraphics()
+{
+ if ( mbGraphics )
+ return nullptr;
+
+ if ( mpGraphics )
+ mbGraphics = true;
+
+ return mpGraphics.get();
+}
+
+void WinSalVirtualDevice::ReleaseGraphics( SalGraphics* )
+{
+ mbGraphics = false;
+}
+
+bool WinSalVirtualDevice::SetSize( tools::Long nDX, tools::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 0000000000..eb5c740580
--- /dev/null
+++ b/vcl/win/gdi/winlayout.cxx
@@ -0,0 +1,235 @@
+/* -*- 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 <config_features.h>
+
+#include <memory>
+
+#include <o3tl/safeint.hxx>
+#include <osl/module.h>
+#include <osl/file.h>
+#include <sal/log.hxx>
+
+#include <comphelper/windowserrorstring.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <win/salgdi.h>
+#include <win/saldata.hxx>
+#include <win/wingdiimpl.hxx>
+#include <ImplOutDevData.hxx>
+
+#include <win/DWriteTextRenderer.hxx>
+#include <win/scoped_gdi.hxx>
+
+#include <sallayout.hxx>
+
+#include <cstdio>
+#include <cstdlib>
+
+#include <rtl/character.hxx>
+
+#include <algorithm>
+
+#include <shlwapi.h>
+#include <winver.h>
+
+TextOutRenderer& TextOutRenderer::get(bool bUseDWrite, bool bRenderingModeNatural)
+{
+ 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
+ || static_cast<D2DWriteTextOutRenderer*>(pSalData->m_pD2DWriteTextOutRenderer.get())
+ ->GetRenderingModeNatural()
+ != bRenderingModeNatural)
+ {
+ pSalData->m_pD2DWriteTextOutRenderer.reset(
+ new D2DWriteTextOutRenderer(bRenderingModeNatural));
+ }
+ 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, bool /*bRenderingModeNatural*/)
+{
+ int nStart = 0;
+ basegfx::B2DPoint aPos;
+ const GlyphItem* pGlyph;
+ const WinFontInstance* pWinFont = static_cast<const WinFontInstance*>(&rLayout.GetFont());
+ UINT nTextAlign = GetTextAlign(hDC);
+ UINT nCurTextAlign = nTextAlign;
+ sal_Int32 nGlyphOffset = -pWinFont->GetTmDescent();
+
+ while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+ {
+ wchar_t glyphWStr = pGlyph->glyphId();
+ UINT32 nNewTextAlign = nCurTextAlign;
+ sal_Int32 nYOffset = 0;
+
+ if (pWinFont->IsCJKVerticalFont() && pGlyph->IsVertical())
+ {
+ nNewTextAlign = VTA_CENTER | TA_BOTTOM;
+ nYOffset = nGlyphOffset;
+ }
+ else
+ nNewTextAlign = nTextAlign;
+
+ if (nCurTextAlign != nNewTextAlign)
+ SetTextAlign(hDC, nNewTextAlign);
+
+ ExtTextOutW(hDC, aPos.getX(), aPos.getY() + nYOffset, ETO_GLYPH_INDEX, nullptr, &glyphWStr,
+ 1, nullptr);
+
+ nCurTextAlign = nNewTextAlign;
+ }
+
+ if (nCurTextAlign != nTextAlign)
+ SetTextAlign(hDC, nTextAlign);
+
+ 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 vcl::font::FontSelectPattern& rFSP)
+ : LogicalFontInstance(rPFF, rFSP)
+ , m_pGraphics(nullptr)
+ , m_hFont(nullptr)
+ , m_bIsCJKVerticalFont(false)
+ , m_nTmDescent(0)
+{
+}
+
+WinFontInstance::~WinFontInstance()
+{
+ if (m_hFont)
+ ::DeleteFont(m_hFont);
+}
+
+bool WinFontInstance::hasHScale() const
+{
+ const vcl::font::FontSelectPattern& rPattern = GetFontSelectPattern();
+ int nHeight(rPattern.mnHeight);
+ int nWidth(rPattern.mnWidth ? rPattern.mnWidth * GetAverageWidthFactor() : nHeight);
+ return nWidth != nHeight;
+}
+
+float WinFontInstance::getHScale() const
+{
+ const vcl::font::FontSelectPattern& rPattern = GetFontSelectPattern();
+ int nHeight(rPattern.mnHeight);
+ if (!nHeight)
+ return 1.0;
+ float nWidth(rPattern.mnWidth ? rPattern.mnWidth * GetAverageWidthFactor() : nHeight);
+ return nWidth / nHeight;
+}
+
+void WinFontInstance::ImplInitHbFont(hb_font_t* /*pHbFont*/)
+{
+ assert(m_pGraphics);
+ // Calculate the AverageWidthFactor, see LogicalFontInstance::GetScale().
+ if (GetFontSelectPattern().mnWidth)
+ {
+ double nUPEM = GetFontFace()->UnitsPerEm();
+
+ 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);
+ }
+}
+
+void WinFontInstance::SetGraphics(WinSalGraphics* pGraphics)
+{
+ m_pGraphics = pGraphics;
+ if (m_hFont)
+ return;
+ HFONT hOrigFont;
+ HDC hDC = m_pGraphics->getHDC();
+ std::tie(m_hFont, m_bIsCJKVerticalFont, m_nTmDescent)
+ = m_pGraphics->ImplDoSetFont(hDC, GetFontSelectPattern(), GetFontFace(), hOrigFont);
+ SelectObject(hDC, hOrigFont);
+}
+
+void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout, HDC hDC, bool bUseDWrite,
+ bool bRenderingModeNatural)
+{
+ TextOutRenderer& render = TextOutRenderer::get(bUseDWrite, bRenderingModeNatural);
+ render(rLayout, *this, hDC, bRenderingModeNatural);
+}
+
+void WinSalGraphics::DrawTextLayout(const GenericSalLayout& rLayout)
+{
+ if (!mbPrinter && mWinSalGraphicsImplBase->DrawTextLayout(rLayout))
+ return; // handled by mWinSalGraphicsImplBase
+
+ HDC hDC = getHDC();
+ const WinFontInstance* pWinFont = static_cast<const WinFontInstance*>(&rLayout.GetFont());
+ const HFONT hLayoutFont = pWinFont->GetHFONT();
+
+ const HFONT hOrigFont = ::SelectFont(hDC, hLayoutFont);
+
+ // DWrite text renderer performs vertical writing better except printing.
+ const bool bVerticalScreenText
+ = !mbPrinter && rLayout.GetFont().GetFontSelectPattern().mbVertical;
+ const bool bRenderingModeNatural = rLayout.GetSubpixelPositioning();
+ const bool bUseDWrite = bVerticalScreenText || bRenderingModeNatural;
+ DrawTextLayout(rLayout, hDC, bUseDWrite, bRenderingModeNatural);
+
+ ::SelectFont(hDC, hOrigFont);
+}
+
+/* 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 0000000000..b9d56fcd14
--- /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 0000000000..8517d965f0
--- /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 0000000000..7634a7d34a
--- /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 0000000000..e444e42bf3
--- /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 0000000000..e92cc65e7e
--- /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 0000000000..04d0b09c35
--- /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 0000000000..a0e25b16de
--- /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 0000000000..20322bc97b
--- /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 0000000000..7166636a1a
--- /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 0000000000..8cb71234b0
--- /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 0000000000..46ee06d168
--- /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 0000000000..0ccac50f45
--- /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 0000000000..c238b7e10a
--- /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 0000000000..02abb7ab71
--- /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 0000000000..938ece03f3
--- /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 0000000000..25fe85b760
--- /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 0000000000..d3c4bc93af
--- /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 0000000000..495fd5e177
--- /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 0000000000..450c09443a
--- /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 0000000000..ac8de5da6b
--- /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 0000000000..e67f0539fa
--- /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 0000000000..c40cf591e2
--- /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 0000000000..327fb06976
--- /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 0000000000..38504fa23c
--- /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 0000000000..f630b837dd
--- /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 0000000000..10dd5ba0d6
--- /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 0000000000..b19d3f8257
--- /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 0000000000..5318d8f22d
--- /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 0000000000..c489a64033
--- /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 0000000000..30e5685b64
--- /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 0000000000..3ff56d0076
--- /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 0000000000..623c33ac23
--- /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 0000000000..3b911cd01e
--- /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 0000000000..9467f1e286
--- /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 0000000000..60a5242c20
--- /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 0000000000..01e7d31eae
--- /dev/null
+++ b/vcl/win/src/dtext.cur
Binary files differ
diff --git a/vcl/win/src/fatcross.cur b/vcl/win/src/fatcross.cur
new file mode 100644
index 0000000000..9f92d04029
--- /dev/null
+++ b/vcl/win/src/fatcross.cur
Binary files differ
diff --git a/vcl/win/src/fill.cur b/vcl/win/src/fill.cur
new file mode 100644
index 0000000000..78f5fad87a
--- /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 0000000000..5cf2211458
--- /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 0000000000..e47c1dea2c
--- /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 0000000000..6cc498a026
--- /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 0000000000..1e32b92351
--- /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 0000000000..e05eb836eb
--- /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 0000000000..d079eb9fe2
--- /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 0000000000..4d67cbe471
--- /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 0000000000..1bb7b03064
--- /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 0000000000..6abee2381d
--- /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 0000000000..d044981a3f
--- /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 0000000000..630fa1bc3e
--- /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 0000000000..81d3af5a05
--- /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 0000000000..28dbb2a903
--- /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 0000000000..061b3ba926
--- /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 0000000000..4497dacd99
--- /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 0000000000..efbbead893
--- /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 0000000000..649444e9e1
--- /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 0000000000..43c2a54a10
--- /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 0000000000..b23ac149a7
--- /dev/null
+++ b/vcl/win/src/salsrc.rc
@@ -0,0 +1,90 @@
+/* -*- 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_POINTER_FATCROSS CURSOR fatcross.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 0000000000..b2a0a07a67
--- /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 0000000000..3683e20df1
--- /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 0000000000..007182d734
--- /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 0000000000..986f013950
--- /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 0000000000..adabba1a2a
--- /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 0000000000..a95eb85af4
--- /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 0000000000..a4bbf7e8eb
--- /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 0000000000..776177901e
--- /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 0000000000..bfa8fdfdba
--- /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 0000000000..e0c2106034
--- /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 0000000000..e30f7284ca
--- /dev/null
+++ b/vcl/win/window/keynames.cxx
@@ -0,0 +1,225 @@
+/* -*- 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 <o3tl/string_view.hxx>
+#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!
+
+ 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" },
+ };
+
+ 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" },
+ };
+
+ 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" },
+ };
+
+ 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" },
+ };
+
+ 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" },
+ };
+
+ 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" },
+ };
+
+ 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" },
+ };
+
+ const struct KeyboardReplacements aKeyboards[] =
+ {
+ { "ast",aImplReplacements_Asturian, std::size(aImplReplacements_Asturian) },
+ { "ca", aImplReplacements_Catalan, std::size(aImplReplacements_Catalan) },
+ { "et", aImplReplacements_Estonian, std::size(aImplReplacements_Estonian) },
+ { "hu", aImplReplacements_Hungarian, std::size(aImplReplacements_Hungarian) },
+ { "lt", aImplReplacements_Lithuanian, std::size(aImplReplacements_Lithuanian) },
+ { "sl", aImplReplacements_Slovenian, std::size(aImplReplacements_Slovenian) },
+ { "es", aImplReplacements_Spanish, std::size(aImplReplacements_Spanish) },
+ };
+
+ // translate keycodes, used within the displayed menu shortcuts
+ OUString getKeysReplacementName( std::u16string_view pLang, LONG nSymbol )
+ {
+ for( unsigned int n = 0; n < SAL_N_ELEMENTS(aKeyboards); n++ )
+ {
+ if( o3tl::equalsAscii( pLang, 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 0000000000..cf2c8c6f8b
--- /dev/null
+++ b/vcl/win/window/salframe.cxx
@@ -0,0 +1,6099 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+
+#include <officecfg/Office/Common.hxx>
+
+#include <memory>
+#include <string.h>
+#include <limits.h>
+
+#include <svsys.h>
+
+#include <comphelper/windowserrorstring.hxx>
+
+#include <fstream>
+#include <boost/property_tree/ptree.hpp>
+#include <boost/property_tree/ini_parser.hpp>
+#include <osl/file.hxx>
+#include <osl/process.h>
+
+#include <rtl/character.hxx>
+#include <rtl/string.h>
+#include <rtl/ustring.h>
+#include <sal/log.hxx>
+
+#include <osl/module.h>
+#include <comphelper/scopeguard.hxx>
+#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>
+
+#include <vector>
+
+#include <com/sun/star/uno/Exception.hpp>
+
+#include <oleacc.h>
+#include <com/sun/star/accessibility/XMSAAService.hpp>
+
+#include <time.h>
+
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <dwmapi.h>
+#include <shobjidl.h>
+#include <propkey.h>
+#include <propvarutil.h>
+#include <shellapi.h>
+#include <uxtheme.h>
+#include <Vssym32.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+
+#ifndef IDC_PEN
+# define IDC_PEN MAKEINTRESOURCE(32631)
+#endif
+
+const unsigned int WM_USER_SYSTEM_WINDOW_ACTIVATED = RegisterWindowMessageW(L"SYSTEM_WINDOW_ACTIVATED");
+
+bool WinSalFrame::mbInReparent = false;
+
+// Macros for support of WM_UNICHAR & Keyman 6.0
+#define Uni_SupplementaryPlanesStart 0x10000
+
+static void UpdateFrameGeometry(WinSalFrame* pFrame);
+static void SetMaximizedFrameGeometry( HWND hWnd, WinSalFrame* pFrame, RECT* pParentRect = nullptr );
+
+static void SetGeometrySize(vcl::WindowPosSize& rWinPosSize, const Size& rSize)
+{
+ rWinPosSize.setWidth(rSize.Width() < 0 ? 0 : rSize.Width());
+ rWinPosSize.setHeight(rSize.Height() < 0 ? 0 : rSize.Height());
+}
+
+// If called with UpdateFrameGeometry, it must be called after it, as UpdateFrameGeometry
+// updates the geometry depending on the old state!
+void WinSalFrame::UpdateFrameState()
+{
+ // don't overwrite restore state in fullscreen mode
+ if (isFullScreen())
+ return;
+
+ const bool bVisible = (GetWindowStyle(mhWnd) & WS_VISIBLE);
+ if (IsIconic(mhWnd))
+ {
+ m_eState &= ~vcl::WindowState(vcl::WindowState::Normal | vcl::WindowState::Maximized);
+ m_eState |= vcl::WindowState::Minimized;
+ if (bVisible)
+ mnShowState = SW_SHOWMINIMIZED;
+ }
+ else if (IsZoomed(mhWnd))
+ {
+ m_eState &= ~vcl::WindowState(vcl::WindowState::Minimized | vcl::WindowState::Normal);
+ m_eState |= vcl::WindowState::Maximized;
+ if (bVisible)
+ mnShowState = SW_SHOWMAXIMIZED;
+ mbRestoreMaximize = true;
+ }
+ else
+ {
+ m_eState &= ~vcl::WindowState(vcl::WindowState::Minimized | vcl::WindowState::Maximized);
+ m_eState |= vcl::WindowState::Normal;
+ if (bVisible)
+ mnShowState = SW_SHOWNORMAL;
+ 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 )
+{
+ if (Application::IsHeadlessModeEnabled()) {
+ pRect->left = 0;
+ pRect->top = 0;
+ pRect->right = VIRTUAL_DESKTOP_WIDTH;
+ pRect->bottom = VIRTUAL_DESKTOP_HEIGHT;
+ return;
+ }
+ // check if we or our parent is fullscreen, then the taskbar should be ignored
+ bool bIgnoreTaskbar = false;
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if( pFrame )
+ {
+ vcl::Window *pWin = pFrame->GetWindow();
+ while( pWin )
+ {
+ WorkWindow *pWorkWin = (pWin->GetType() == WindowType::WORKWINDOW) ? static_cast<WorkWindow *>(pWin) : nullptr;
+ if( pWorkWin && pWorkWin->ImplGetWindowImpl()->mbReallyVisible && pWorkWin->IsFullScreenMode() )
+ {
+ bIgnoreTaskbar = true;
+ break;
+ }
+ else
+ pWin = pWin->ImplGetWindowImpl()->mpParent;
+ }
+ }
+
+ // calculates the work area taking multiple monitors into account
+ 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;
+ }
+ }
+ }
+}
+
+namespace {
+
+enum PreferredAppMode
+{
+ AllowDark = 1,
+ ForceDark = 2,
+ ForceLight = 3
+};
+
+}
+
+static void UpdateDarkMode(HWND hWnd)
+{
+ static bool bOSSupportsDarkMode = OSSupportsDarkMode();
+ if (!bOSSupportsDarkMode)
+ return;
+
+ HINSTANCE hUxthemeLib = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ if (!hUxthemeLib)
+ return;
+
+ typedef PreferredAppMode(WINAPI* SetPreferredAppMode_t)(PreferredAppMode);
+ auto SetPreferredAppMode = reinterpret_cast<SetPreferredAppMode_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(135)));
+ if (SetPreferredAppMode)
+ {
+ switch (MiscSettings::GetDarkMode())
+ {
+ case 0:
+ SetPreferredAppMode(AllowDark);
+ break;
+ case 1:
+ SetPreferredAppMode(ForceLight);
+ break;
+ case 2:
+ SetPreferredAppMode(ForceDark);
+ break;
+ }
+ }
+
+ BOOL bDarkMode = UseDarkMode();
+
+ typedef void(WINAPI* AllowDarkModeForWindow_t)(HWND, BOOL);
+ auto AllowDarkModeForWindow = reinterpret_cast<AllowDarkModeForWindow_t>(GetProcAddress(hUxthemeLib, MAKEINTRESOURCEA(133)));
+ if (AllowDarkModeForWindow)
+ AllowDarkModeForWindow(hWnd, bDarkMode);
+
+ FreeLibrary(hUxthemeLib);
+
+ if (!AllowDarkModeForWindow)
+ return;
+
+ DwmSetWindowAttribute(hWnd, 20, &bDarkMode, sizeof(bDarkMode));
+}
+
+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->mbDefPos = true;
+
+ UpdateFrameGeometry(pFrame);
+ pFrame->UpdateFrameState();
+
+ 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 168
+
+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
+ 0, // 146
+ 0, // 147
+ 0, // 148
+ 0, // 149
+ 0, // 150
+ 0, // 151
+ 0, // 152
+ 0, // 153
+ 0, // 154
+ 0, // 155
+ 0, // 156
+ 0, // 157
+ 0, // 158
+ 0, // 159
+ 0, // 160
+ 0, // 161
+ 0, // 162
+ 0, // 163
+ 0, // 164
+ 0, // 165
+ KEY_XF86BACK, // VK_BROWSER_BACK 166
+ KEY_XF86FORWARD // VK_BROWSER_FORWARD 167
+};
+
+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
+ {
+ AbsoluteScreenPixelRectangle aRect;
+ sal_Int32 nMonitors = Application::GetScreenCount();
+ if( (pFrame->mnDisplay >= 0) && (pFrame->mnDisplay < nMonitors) )
+ {
+ aRect = Application::GetScreenPosSizePixel(pFrame->mnDisplay);
+ }
+ else
+ {
+ for (sal_Int32 i = 0; i < nMonitors; i++)
+ aRect.Union(Application::GetScreenPosSizePixel(i));
+ }
+ nScreenX = aRect.Left();
+ nScreenY = aRect.Top();
+ nScreenDX = aRect.GetWidth();
+ nScreenDY = aRect.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;
+ m_eState = vcl::WindowState::Normal;
+ mnShowState = SW_SHOWNORMAL;
+ 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;
+ 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();
+ AbsoluteScreenPixelPoint aPoint(maGeometry.pos());
+ size_t nMon = rMonitors.size();
+ for( size_t i = 0; i < nMon; i++ )
+ {
+ if( rMonitors[i].m_aArea.Contains( aPoint ) )
+ {
+ mnDisplay = static_cast<sal_Int32>(i);
+ maGeometry.setScreen(static_cast<unsigned int>(i));
+ }
+ }
+ }
+}
+
+bool WinSalFrame::ReleaseFrameGraphicsDC( WinSalGraphics* pGraphics )
+{
+ assert( pGraphics );
+ SalData* pSalData = GetSalData();
+ HDC hDC = pGraphics->getHDC();
+ if ( !hDC )
+ return false;
+ pGraphics->setHDC(nullptr);
+ SendMessageW( pSalData->mpInstance->mhComWnd, SAL_MSG_RELEASEDC,
+ reinterpret_cast<WPARAM>(mhWnd), reinterpret_cast<LPARAM>(hDC) );
+ if ( pGraphics == mpThreadGraphics )
+ pSalData->mnCacheDCInUse--;
+ return true;
+}
+
+WinSalFrame::~WinSalFrame()
+{
+ SalData* pSalData = GetSalData();
+
+ if( mpClipRgnData )
+ delete [] reinterpret_cast<BYTE*>(mpClipRgnData);
+
+ // remove frame from framelist
+ WinSalFrame** ppFrame = &pSalData->mpFirstFrame;
+ for(; (*ppFrame != this) && *ppFrame; ppFrame = &(*ppFrame)->mpNextFrame );
+ if( *ppFrame )
+ *ppFrame = mpNextFrame;
+ mpNextFrame = nullptr;
+
+ // destroy the thread SalGraphics
+ if ( mpThreadGraphics )
+ {
+ ReleaseFrameGraphicsDC( mpThreadGraphics );
+ delete mpThreadGraphics;
+ mpThreadGraphics = nullptr;
+ }
+
+ // destroy the local SalGraphics
+ if ( mpLocalGraphics )
+ {
+ ReleaseFrameGraphicsDC( mpLocalGraphics );
+ delete mpLocalGraphics;
+ mpLocalGraphics = nullptr;
+ }
+
+ if ( mhWnd )
+ {
+ // reset mouse leave data
+ if ( pSalData->mhWantLeaveMsg == mhWnd )
+ {
+ pSalData->mhWantLeaveMsg = nullptr;
+ }
+
+ // remove windows properties
+ if ( mbPropertiesStored )
+ SetApplicationID( OUString() );
+
+ // destroy system frame
+ if ( !DestroyWindow( mhWnd ) )
+ SetWindowPtr( mhWnd, nullptr );
+
+ mhWnd = nullptr;
+ }
+}
+
+bool WinSalFrame::InitFrameGraphicsDC( WinSalGraphics *pGraphics, HDC hDC, HWND hWnd )
+{
+ SalData* pSalData = GetSalData();
+ assert( pGraphics );
+ pGraphics->setHWND( hWnd );
+
+ HDC hCurrentDC = pGraphics->getHDC();
+ assert( !hCurrentDC || (hCurrentDC == hDC) );
+ if ( hCurrentDC )
+ return true;
+ pGraphics->setHDC( hDC );
+
+ if ( !hDC )
+ return false;
+
+ if ( pSalData->mhDitherPal )
+ pGraphics->setPalette(pSalData->mhDitherPal);
+
+ if ( pGraphics == mpThreadGraphics )
+ pSalData->mnCacheDCInUse++;
+ return true;
+}
+
+SalGraphics* WinSalFrame::AcquireGraphics()
+{
+ if ( mbGraphics || !mhWnd )
+ return nullptr;
+
+ SalData* pSalData = GetSalData();
+ WinSalGraphics *pGraphics = nullptr;
+ HDC hDC = nullptr;
+
+ // Other threads get an own DC, because Windows modify in the
+ // other case our DC (changing clip region), when they send a
+ // WM_ERASEBACKGROUND message
+ if ( !pSalData->mpInstance->IsMainThread() )
+ {
+ // We use only three CacheDC's for all threads, because W9x is limited
+ // to max. 5 Cache DC's per thread
+ if ( pSalData->mnCacheDCInUse >= 3 )
+ return nullptr;
+
+ if ( !mpThreadGraphics )
+ mpThreadGraphics = new WinSalGraphics(WinSalGraphics::WINDOW, true, mhWnd, this);
+ pGraphics = mpThreadGraphics;
+ assert( !pGraphics->getHDC() );
+ hDC = reinterpret_cast<HDC>(static_cast<sal_IntPtr>(SendMessageW( pSalData->mpInstance->mhComWnd,
+ SAL_MSG_GETCACHEDDC, reinterpret_cast<WPARAM>(mhWnd), 0 )));
+ }
+ else
+ {
+ if ( !mpLocalGraphics )
+ mpLocalGraphics = new WinSalGraphics(WinSalGraphics::WINDOW, true, mhWnd, this);
+ pGraphics = mpLocalGraphics;
+ hDC = pGraphics->getHDC();
+ if ( !hDC )
+ hDC = GetDC( mhWnd );
+ }
+
+ mbGraphics = InitFrameGraphicsDC( pGraphics, hDC, mhWnd );
+ return mbGraphics ? pGraphics : nullptr;
+}
+
+void WinSalFrame::ReleaseGraphics( SalGraphics* pGraphics )
+{
+ if ( mpThreadGraphics == pGraphics )
+ ReleaseFrameGraphicsDC( mpThreadGraphics );
+ mbGraphics = false;
+}
+
+bool WinSalFrame::PostEvent(std::unique_ptr<ImplSVEvent> pData)
+{
+ bool const ret = PostMessageW(mhWnd, SAL_MSG_USEREVENT, 0, reinterpret_cast<LPARAM>(pData.get()));
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ if (ret)
+ pData.release();
+ 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 );
+}
+
+static HWND ImplGetParentHwnd( HWND hWnd )
+{
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if( !pFrame || !pFrame->GetWindow())
+ return ::GetParent( hWnd );
+ vcl::Window *pRealParent = pFrame->GetWindow()->ImplGetWindowImpl()->mpRealParent;
+ if( pRealParent )
+ return static_cast<WinSalFrame*>(pRealParent->ImplGetWindowImpl()->mpFrame)->mhWnd;
+ else
+ return ::GetParent( hWnd );
+
+}
+
+SalFrame* WinSalFrame::GetParent() const
+{
+ return GetWindowPtr( ImplGetParentHwnd( mhWnd ) );
+}
+
+static void ImplSalShow( HWND hWnd, bool bVisible, bool bNoActivate )
+{
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( !pFrame )
+ return;
+
+ if ( bVisible )
+ {
+ pFrame->mbDefPos = false;
+ pFrame->mbOverwriteState = true;
+ pFrame->mbInShow = true;
+
+ // #i4715, save position
+ RECT aRectPreMatrox, aRectPostMatrox;
+ GetWindowRect( hWnd, &aRectPreMatrox );
+
+ vcl::DeletionListener aDogTag( pFrame );
+ if( bNoActivate )
+ ShowWindow( hWnd, SW_SHOWNOACTIVATE );
+ else
+ ShowWindow( hWnd, pFrame->mnShowState );
+ if( aDogTag.isDeleted() )
+ return;
+
+ if (pFrame->mbFloatWin && !(pFrame->mnStyle & SalFrameStyleFlags::NOSHADOW))
+ {
+ // erase the window immediately to improve XP shadow effect
+ // otherwise the shadow may appears long time before the rest of the window
+ // especially when accessibility is on
+ HDC dc = GetDC( hWnd );
+ RECT aRect;
+ GetClientRect( hWnd, &aRect );
+ FillRect( dc, &aRect, reinterpret_cast<HBRUSH>(COLOR_MENU+1) ); // choose the menucolor, because its mostly noticeable for menus
+ ReleaseDC( hWnd, dc );
+ }
+
+ // #i4715, matrox centerpopup might have changed our position
+ // reposition popups without caption (menus, dropdowns, tooltips)
+ GetWindowRect( hWnd, &aRectPostMatrox );
+ if( (GetWindowStyle( hWnd ) & WS_POPUP) &&
+ !pFrame->mbCaption &&
+ (aRectPreMatrox.left != aRectPostMatrox.left || aRectPreMatrox.top != aRectPostMatrox.top) )
+ SetWindowPos( hWnd, nullptr, aRectPreMatrox.left, aRectPreMatrox.top, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE );
+
+ if( aDogTag.isDeleted() )
+ return;
+ vcl::Window *pClientWin = pFrame->GetWindow()->ImplGetClientWindow();
+ if ( pFrame->mbFloatWin || ( pClientWin && (pClientWin->GetStyle() & WB_SYSTEMFLOATWIN) ) )
+ pFrame->mnShowState = SW_SHOWNOACTIVATE;
+ else
+ pFrame->mnShowState = SW_SHOW;
+ // hide toolbar for W98
+ if ( pFrame->mbPresentation )
+ {
+ HWND hWndParent = ::GetParent( hWnd );
+ if ( hWndParent )
+ SetForegroundWindow_Impl( hWndParent );
+ SetForegroundWindow_Impl( hWnd );
+ }
+
+ pFrame->mbInShow = false;
+ pFrame->updateScreenNumber();
+
+ // Direct Paint only, if we get the SolarMutex
+ if ( ImplSalYieldMutexTryToAcquire() )
+ {
+ UpdateWindow( hWnd );
+ ImplSalYieldMutexRelease();
+ }
+ }
+ else
+ {
+ ShowWindow( hWnd, SW_HIDE );
+ }
+}
+
+void WinSalFrame::SetExtendedFrameStyle( SalExtStyle )
+{
+}
+
+void WinSalFrame::Show( bool bVisible, bool bNoActivate )
+{
+ // Post this Message to the window, because this only works
+ // in the thread of the window, which has create this window.
+ // We post this message to avoid deadlocks
+ if ( GetSalData()->mnAppThreadId != GetCurrentThreadId() )
+ {
+ bool const ret = PostMessageW(mhWnd, SAL_MSG_SHOW, WPARAM(bVisible), LPARAM(bNoActivate));
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ else
+ ImplSalShow( mhWnd, bVisible, bNoActivate );
+}
+
+void WinSalFrame::SetMinClientSize( tools::Long nWidth, tools::Long nHeight )
+{
+ mnMinWidth = nWidth;
+ mnMinHeight = nHeight;
+}
+
+void WinSalFrame::SetMaxClientSize( tools::Long nWidth, tools::Long nHeight )
+{
+ mnMaxWidth = nWidth;
+ mnMaxHeight = nHeight;
+}
+
+void WinSalFrame::SetPosSize( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
+ sal_uInt16 nFlags )
+{
+ bool bVisible = (GetWindowStyle( mhWnd ) & WS_VISIBLE) != 0;
+ if ( !bVisible )
+ {
+ vcl::Window *pClientWin = GetWindow()->ImplGetClientWindow();
+ if ( mbFloatWin || ( pClientWin && (pClientWin->GetStyle() & WB_SYSTEMFLOATWIN) ) )
+ mnShowState = SW_SHOWNOACTIVATE;
+ else
+ mnShowState = SW_SHOWNORMAL;
+ }
+ else
+ {
+ if ( IsIconic( mhWnd ) || IsZoomed( mhWnd ) )
+ ShowWindow( mhWnd, SW_RESTORE );
+ }
+
+ SalEvent nEvent = SalEvent::NONE;
+ UINT nPosSize = 0;
+ RECT aClientRect, aWindowRect;
+ GetClientRect( mhWnd, &aClientRect ); // x,y always 0,0, but width and height without border
+ GetWindowRect( mhWnd, &aWindowRect ); // x,y in screen coordinates, width and height with border
+
+ if ( !(nFlags & (SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y)) )
+ nPosSize |= SWP_NOMOVE;
+ else
+ {
+ //SAL_WARN_IF( !nX || !nY, "vcl", " Windowposition of (0,0) requested!" );
+ nEvent = SalEvent::Move;
+ }
+ if ( !(nFlags & (SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT)) )
+ nPosSize |= SWP_NOSIZE;
+ else
+ nEvent = (nEvent == SalEvent::Move) ? SalEvent::MoveResize : SalEvent::Resize;
+
+ if ( !(nFlags & SAL_FRAME_POSSIZE_X) )
+ nX = aWindowRect.left;
+ if ( !(nFlags & SAL_FRAME_POSSIZE_Y) )
+ nY = aWindowRect.top;
+ if ( !(nFlags & SAL_FRAME_POSSIZE_WIDTH) )
+ nWidth = aClientRect.right-aClientRect.left;
+ if ( !(nFlags & SAL_FRAME_POSSIZE_HEIGHT) )
+ nHeight = aClientRect.bottom-aClientRect.top;
+
+ // Calculate window size including the border
+ RECT aWinRect;
+ aWinRect.left = 0;
+ aWinRect.right = static_cast<int>(nWidth)-1;
+ aWinRect.top = 0;
+ aWinRect.bottom = static_cast<int>(nHeight)-1;
+ AdjustWindowRectEx( &aWinRect, GetWindowStyle( mhWnd ),
+ FALSE, GetWindowExStyle( mhWnd ) );
+ nWidth = aWinRect.right - aWinRect.left + 1;
+ nHeight = aWinRect.bottom - aWinRect.top + 1;
+
+ HWND hWndParent = ImplGetParentHwnd(mhWnd);
+ // For dialogs (WS_POPUP && WS_DLGFRAME), we need to find the "real" parent,
+ // in case multiple dialogs are stacked on each other
+ // (we don't want to position the second dialog relative to the first one, but relative to the main window)
+ if ( (GetWindowStyle( mhWnd ) & WS_POPUP) && (GetWindowStyle( mhWnd ) & WS_DLGFRAME) ) // mhWnd is a dialog
+ {
+ while ( hWndParent && (GetWindowStyle( hWndParent ) & WS_POPUP) && (GetWindowStyle( hWndParent ) & WS_DLGFRAME) )
+ {
+ hWndParent = ::ImplGetParentHwnd( hWndParent );
+ }
+ }
+
+ if ( !(nPosSize & SWP_NOMOVE) && hWndParent )
+ {
+ RECT aParentRect;
+ GetClientRect( hWndParent, &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;
+
+ WinSalFrame* pParentFrame = GetWindowPtr( hWndParent );
+ if ( pParentFrame && pParentFrame->mnShowState == SW_SHOWMAXIMIZED )
+ {
+ // #i42485#: parent will be shown maximized in which case
+ // a ClientToScreen uses the wrong coordinates (i.e. those from the restore pos)
+ // so use the (already updated) frame geometry for the transformation
+ aPt.x += pParentFrame->maGeometry.x();
+ aPt.y += pParentFrame->maGeometry.y();
+ }
+ else
+ ClientToScreen( hWndParent, &aPt );
+
+ nX = aPt.x;
+ nY = aPt.y;
+
+ // the position is set
+ mbDefPos = false;
+ }
+ }
+
+ // #i3338# to be conformant to UNIX we must position the client window, ie without the decoration
+ // #i43250# if the position was read from the system (GetWindowRect(), see above), it must not be modified
+ if ( nFlags & SAL_FRAME_POSSIZE_X )
+ nX += aWinRect.left;
+ if ( nFlags & SAL_FRAME_POSSIZE_Y )
+ nY += aWinRect.top;
+
+ int nScreenX;
+ int nScreenY;
+ int nScreenWidth;
+ int nScreenHeight;
+
+ 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 hWndParent2 = ::GetParent( mhWnd );
+ // Search for TopLevel Frame
+ while ( hWndParent2 && (GetWindowStyle( hWndParent2 ) & WS_CHILD) )
+ hWndParent2 = ::GetParent( hWndParent2 );
+ // if the Window has a Parent, then center the window to
+ // the parent, in the other case to the screen
+ if ( hWndParent2 && !IsIconic( hWndParent2 ) &&
+ (GetWindowStyle( hWndParent2 ) & WS_VISIBLE) )
+ {
+ RECT aParentRect;
+ GetWindowRect( hWndParent2, &aParentRect );
+ int nParentWidth = aParentRect.right-aParentRect.left;
+ int nParentHeight = aParentRect.bottom-aParentRect.top;
+
+ // We don't center, when Parent is smaller than our window
+ if ( (nParentWidth-GetSystemMetrics( SM_CXFIXEDFRAME ) <= nWidth) &&
+ (nParentHeight-GetSystemMetrics( SM_CYFIXEDFRAME ) <= nHeight) )
+ {
+ int nOff = GetSystemMetrics( SM_CYSIZEFRAME ) + GetSystemMetrics( SM_CYCAPTION );
+ nX = aParentRect.left+nOff;
+ nY = aParentRect.top+nOff;
+ }
+ else
+ {
+ nX = (nParentWidth-nWidth)/2 + aParentRect.left;
+ nY = (nParentHeight-nHeight)/2 + aParentRect.top;
+ }
+ }
+ else
+ {
+ POINT pt;
+ GetCursorPos( &pt );
+ RECT aRect2;
+ aRect2.left = pt.x;
+ aRect2.top = pt.y;
+ aRect2.right = pt.x+2;
+ aRect2.bottom = pt.y+2;
+
+ // dualmonitor support:
+ // Get screensize of the monitor with the mouse pointer
+ ImplSalGetWorkArea( mhWnd, &aRect2, &aRect2 );
+
+ 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(this);
+
+ // Notification -- really ???
+ if( nEvent != SalEvent::NONE )
+ CallCallback( nEvent, nullptr );
+}
+
+void WinSalFrame::ImplSetParentFrame( HWND hNewParentWnd, bool bAsChild )
+{
+ // save hwnd, will be overwritten in WM_CREATE during createwindow
+ HWND hWndOld = mhWnd;
+ HWND hWndOldParent = ::GetParent( hWndOld );
+ SalData* pSalData = GetSalData();
+
+ if( hNewParentWnd == hWndOldParent )
+ return;
+
+ ::std::vector< WinSalFrame* > children;
+ ::std::vector< WinSalObject* > systemChildren;
+
+ // search child windows
+ WinSalFrame *pFrame = pSalData->mpFirstFrame;
+ while( pFrame )
+ {
+ HWND hWndParent = ::GetParent( pFrame->mhWnd );
+ if( mhWnd == hWndParent )
+ children.push_back( pFrame );
+ pFrame = pFrame->mpNextFrame;
+ }
+
+ // search system child windows (plugins etc.)
+ WinSalObject *pObject = pSalData->mpFirstObject;
+ while( pObject )
+ {
+ HWND hWndParent = ::GetParent( pObject->mhWnd );
+ if( mhWnd == hWndParent )
+ systemChildren.push_back( pObject );
+ pObject = pObject->mpNextObject;
+ }
+
+ // to recreate the DCs, if they were destroyed
+ bool bHadLocalGraphics = false, bHadThreadGraphics = false;
+
+ HFONT hFont = nullptr;
+ HPEN hPen = nullptr;
+ HBRUSH hBrush = nullptr;
+
+ int oldCount = pSalData->mnCacheDCInUse;
+
+ // release the thread DC
+ if ( mpThreadGraphics )
+ {
+ // save current gdi objects before hdc is gone
+ HDC hDC = mpThreadGraphics->getHDC();
+ if ( hDC )
+ {
+ hFont = static_cast<HFONT>(GetCurrentObject( hDC, OBJ_FONT ));
+ hPen = static_cast<HPEN>(GetCurrentObject( hDC, OBJ_PEN ));
+ hBrush = static_cast<HBRUSH>(GetCurrentObject( hDC, OBJ_BRUSH ));
+ }
+
+ bHadThreadGraphics = ReleaseFrameGraphicsDC( mpThreadGraphics );
+ assert( (bHadThreadGraphics && hDC) || (!bHadThreadGraphics && !hDC) );
+ }
+
+ // release the local DC
+ if ( mpLocalGraphics )
+ bHadLocalGraphics = ReleaseFrameGraphicsDC( mpLocalGraphics );
+
+ // create a new hwnd with the same styles
+ HWND hWndParent = hNewParentWnd;
+ // forward to main thread
+ HWND hWnd = reinterpret_cast<HWND>(static_cast<sal_IntPtr>(SendMessageW( pSalData->mpInstance->mhComWnd,
+ bAsChild ? SAL_MSG_RECREATECHILDHWND : SAL_MSG_RECREATEHWND,
+ reinterpret_cast<WPARAM>(hWndParent), reinterpret_cast<LPARAM>(mhWnd) )));
+
+ // succeeded ?
+ SAL_WARN_IF( !IsWindow( hWnd ), "vcl", "WinSalFrame::SetParent not successful");
+
+ // re-create thread DC
+ if( bHadThreadGraphics )
+ {
+ HDC hDC = reinterpret_cast<HDC>(static_cast<sal_IntPtr>(
+ SendMessageW( pSalData->mpInstance->mhComWnd,
+ SAL_MSG_GETCACHEDDC, reinterpret_cast<WPARAM>(hWnd), 0 )));
+ InitFrameGraphicsDC( mpThreadGraphics, hDC, hWnd );
+ if ( hDC )
+ {
+ // re-select saved gdi objects
+ if( hFont )
+ SelectObject( hDC, hFont );
+ if( hPen )
+ SelectObject( hDC, hPen );
+ if( hBrush )
+ SelectObject( hDC, hBrush );
+
+ SAL_WARN_IF( oldCount != pSalData->mnCacheDCInUse, "vcl", "WinSalFrame::SetParent() hDC count corrupted");
+ }
+ }
+
+ // re-create local DC
+ if( bHadLocalGraphics )
+ InitFrameGraphicsDC( mpLocalGraphics, GetDC( hWnd ), hWnd );
+
+ // TODO: add SetParent() call for SalObjects
+ SAL_WARN_IF( !systemChildren.empty(), "vcl", "WinSalFrame::SetParent() parent of living system child window will be destroyed!");
+
+ // reparent children before old parent is destroyed
+ for (auto & child : children)
+ child->ImplSetParentFrame( hWnd, false );
+
+ children.clear();
+ systemChildren.clear();
+
+ // Now destroy original HWND in the thread where it was created.
+ SendMessageW( GetSalData()->mpInstance->mhComWnd,
+ SAL_MSG_DESTROYHWND, WPARAM(0), reinterpret_cast<LPARAM>(hWndOld));
+}
+
+void WinSalFrame::SetParent( SalFrame* pNewParent )
+{
+ WinSalFrame::mbInReparent = true;
+ ImplSetParentFrame( static_cast<WinSalFrame*>(pNewParent)->mhWnd, false );
+ WinSalFrame::mbInReparent = false;
+}
+
+void WinSalFrame::SetPluginParent( SystemParentData* pNewParent )
+{
+ if ( pNewParent->hWnd == nullptr )
+ {
+ pNewParent->hWnd = GetDesktopWindow();
+ }
+
+ WinSalFrame::mbInReparent = true;
+ ImplSetParentFrame( pNewParent->hWnd, true );
+ WinSalFrame::mbInReparent = false;
+}
+
+void WinSalFrame::GetWorkArea( AbsoluteScreenPixelRectangle &rRect )
+{
+ RECT aRect;
+
+ // pass cursor's position to ImplSalGetWorkArea to determine work area on
+ // multi monitor setups correctly.
+ POINT aPoint;
+ GetCursorPos(&aPoint);
+ RECT aRect2{ aPoint.x, aPoint.y, aPoint.x + 2, aPoint.y + 2 };
+
+ ImplSalGetWorkArea( mhWnd, &aRect, &aRect2 );
+ rRect.SetLeft( aRect.left );
+ rRect.SetRight( aRect.right-1 );
+ rRect.SetTop( aRect.top );
+ rRect.SetBottom( aRect.bottom-1 );
+}
+
+void WinSalFrame::GetClientSize( tools::Long& rWidth, tools::Long& rHeight )
+{
+ rWidth = maGeometry.width();
+ rHeight = maGeometry.height();
+}
+
+void WinSalFrame::SetWindowState(const vcl::WindowData* pState)
+{
+ // Check if the window fits into the screen, in case the screen
+ // resolution changed
+ int nX;
+ int nY;
+ int nWidth;
+ int nHeight;
+ int nScreenX;
+ int nScreenY;
+ int nScreenWidth;
+ int nScreenHeight;
+
+ RECT aRect;
+ ImplSalGetWorkArea( mhWnd, &aRect, nullptr );
+ // #102500# allow some overlap, the window could have been made a little larger than the physical screen
+ nScreenX = aRect.left-10;
+ nScreenY = aRect.top-10;
+ nScreenWidth = aRect.right-aRect.left+20;
+ nScreenHeight = aRect.bottom-aRect.top+20;
+
+ UINT nPosSize = 0;
+ RECT aWinRect;
+ GetWindowRect( mhWnd, &aWinRect );
+
+ // to be consistent with Unix, the frame state is without(!) decoration
+ // ->add the decoration
+ RECT aRect2 = aWinRect;
+ AdjustWindowRectEx( &aRect2, GetWindowStyle( mhWnd ),
+ FALSE, GetWindowExStyle( mhWnd ) );
+ tools::Long nTopDeco = abs( aWinRect.top - aRect2.top );
+ tools::Long nLeftDeco = abs( aWinRect.left - aRect2.left );
+ tools::Long nBottomDeco = abs( aWinRect.bottom - aRect2.bottom );
+ tools::Long nRightDeco = abs( aWinRect.right - aRect2.right );
+
+ // adjust window position/size to fit the screen
+ if ( !(pState->mask() & vcl::WindowDataMask::Pos) )
+ nPosSize |= SWP_NOMOVE;
+ if ( !(pState->mask() & vcl::WindowDataMask::Size) )
+ nPosSize |= SWP_NOSIZE;
+ if ( pState->mask() & vcl::WindowDataMask::X )
+ nX = static_cast<int>(pState->x()) - nLeftDeco;
+ else
+ nX = aWinRect.left;
+ if ( pState->mask() & vcl::WindowDataMask::Y )
+ nY = static_cast<int>(pState->y()) - nTopDeco;
+ else
+ nY = aWinRect.top;
+ if ( pState->mask() & vcl::WindowDataMask::Width )
+ nWidth = static_cast<int>(pState->width()) + nLeftDeco + nRightDeco;
+ else
+ nWidth = aWinRect.right-aWinRect.left;
+ if ( pState->mask() & vcl::WindowDataMask::Height )
+ nHeight = static_cast<int>(pState->height()) + nTopDeco + nBottomDeco;
+ else
+ nHeight = aWinRect.bottom-aWinRect.top;
+
+ // Adjust Window in the screen:
+ // if it does not fit into the screen do nothing, ie default pos/size will be used
+ // if there is an overlap with the screen border move the window while keeping its size
+
+ if( 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
+ const bool bIsMinimized = IsIconic(mhWnd);
+ const bool bIsMaximized = IsZoomed(mhWnd);
+ const bool bVisible = (GetWindowStyle(mhWnd) & WS_VISIBLE);
+ bool bUpdateHiddenFramePos = false;
+ if ( !bVisible )
+ {
+ aPlacement.showCmd = SW_HIDE;
+
+ if (mbOverwriteState && (pState->mask() & vcl::WindowDataMask::State))
+ {
+ if (pState->state() & vcl::WindowState::Minimized)
+ mnShowState = SW_SHOWMINIMIZED;
+ else if (pState->state() & vcl::WindowState::Maximized)
+ {
+ mnShowState = SW_SHOWMAXIMIZED;
+ bUpdateHiddenFramePos = true;
+ }
+ else if (pState->state() & vcl::WindowState::Normal)
+ mnShowState = SW_SHOWNORMAL;
+ }
+ }
+ else
+ {
+ if ( pState->mask() & vcl::WindowDataMask::State )
+ {
+ if ( pState->state() & vcl::WindowState::Minimized )
+ {
+ if ( pState->state() & vcl::WindowState::Maximized )
+ aPlacement.flags |= WPF_RESTORETOMAXIMIZED;
+ aPlacement.showCmd = SW_SHOWMINIMIZED;
+ }
+ else if ( pState->state() & vcl::WindowState::Maximized )
+ aPlacement.showCmd = SW_SHOWMAXIMIZED;
+ else if ( pState->state() & vcl::WindowState::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 (!bIsMinimized && !bIsMaximized && (!bVisible || (aPlacement.showCmd == SW_RESTORE)))
+ {
+ if( bUpdateHiddenFramePos )
+ {
+ RECT aStateRect;
+ aStateRect.left = nX;
+ aStateRect.top = nY;
+ aStateRect.right = nX+nWidth;
+ aStateRect.bottom = nY+nHeight;
+ // #96084 set a useful internal window size because
+ // the window will not be maximized (and the size updated) before show()
+ SetMaximizedFrameGeometry( mhWnd, this, &aStateRect );
+ SetWindowPos( mhWnd, nullptr,
+ maGeometry.x(), maGeometry.y(), maGeometry.width(), maGeometry.height(),
+ SWP_NOZORDER | SWP_NOACTIVATE | nPosSize );
+ }
+ else
+ SetWindowPos( mhWnd, nullptr,
+ nX, nY, nWidth, nHeight,
+ SWP_NOZORDER | SWP_NOACTIVATE | nPosSize );
+ }
+ else
+ {
+ if( !(nPosSize & (SWP_NOMOVE|SWP_NOSIZE)) )
+ {
+ aPlacement.rcNormalPosition.left = nX-nScreenX;
+ aPlacement.rcNormalPosition.top = nY-nScreenY;
+ aPlacement.rcNormalPosition.right = nX+nWidth-nScreenX;
+ aPlacement.rcNormalPosition.bottom = nY+nHeight-nScreenY;
+ }
+ SetWindowPlacement( mhWnd, &aPlacement );
+ }
+
+ if( !(nPosSize & SWP_NOMOVE) )
+ mbDefPos = false; // window was positioned
+}
+
+bool WinSalFrame::GetWindowState(vcl::WindowData* pState)
+{
+ pState->setPosSize(maGeometry.posSize());
+ pState->setState(m_eState);
+ pState->setMask(vcl::WindowDataMask::PosSizeState);
+ return true;
+}
+
+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 )
+ {
+ AbsoluteScreenPixelPoint aOldMonPos, aNewMonPos( rMonitors[nNewScreen].m_aArea.TopLeft() );
+ AbsoluteScreenPixelPoint aCurPos(maGeometry.pos());
+ for( size_t i = 0; i < nMon; i++ )
+ {
+ if( rMonitors[i].m_aArea.Contains( aCurPos ) )
+ {
+ aOldMonPos = rMonitors[i].m_aArea.TopLeft();
+ break;
+ }
+ }
+ mnDisplay = nNewScreen;
+ maGeometry.setScreen(nNewScreen);
+ SetPosSize( aNewMonPos.X() + (maGeometry.x() - aOldMonPos.X()),
+ aNewMonPos.Y() + (maGeometry.y() - 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(const bool bFullScreen, const sal_Int32 nDisplay)
+{
+ if ((isFullScreen() == bFullScreen) && (!bFullScreen || (mnDisplay == nDisplay)))
+ return;
+
+ mnDisplay = nDisplay;
+
+ if ( bFullScreen )
+ {
+ m_eState |= vcl::WindowState::FullScreen;
+ // 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
+ {
+ m_eState &= ~vcl::WindowState::FullScreen;
+ // when the ShowState has to be reset, hide the window first to
+ // reduce flicker
+ bool bVisible = (GetWindowStyle( mhWnd ) & WS_VISIBLE) != 0;
+ if ( bVisible && (mnShowState != mnFullScreenShowState) )
+ ShowWindow( mhWnd, SW_HIDE );
+
+ if ( 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;
+
+ if ( bStart )
+ {
+ // turn off screen-saver / power saving when in Presentation mode
+ SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
+ }
+ else
+ {
+ // turn on screen-saver / power saving back
+ SetThreadExecutionState(ES_CONTINUOUS);
+ }
+}
+
+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 =
+ {
+ // [-loplugin:redundantfcast]:
+ ImplPtrData{ nullptr, IDC_ARROW, 0 }, // POINTER_ARROW
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_NULL }, // POINTER_NULL
+ ImplPtrData{ nullptr, IDC_WAIT, 0 }, // POINTER_WAIT
+ ImplPtrData{ nullptr, IDC_IBEAM, 0 }, // POINTER_TEXT
+ ImplPtrData{ nullptr, IDC_HELP, 0 }, // POINTER_HELP
+ ImplPtrData{ nullptr, IDC_CROSS, 0 }, // POINTER_CROSS
+ ImplPtrData{ nullptr, IDC_SIZEALL, 0 }, // POINTER_MOVE
+ ImplPtrData{ nullptr, IDC_SIZENS, 0 }, // POINTER_NSIZE
+ ImplPtrData{ nullptr, IDC_SIZENS, 0 }, // POINTER_SSIZE
+ ImplPtrData{ nullptr, IDC_SIZEWE, 0 }, // POINTER_WSIZE
+ ImplPtrData{ nullptr, IDC_SIZEWE, 0 }, // POINTER_ESIZE
+ ImplPtrData{ nullptr, IDC_SIZENWSE, 0 }, // POINTER_NWSIZE
+ ImplPtrData{ nullptr, IDC_SIZENESW, 0 }, // POINTER_NESIZE
+ ImplPtrData{ nullptr, IDC_SIZENESW, 0 }, // POINTER_SWSIZE
+ ImplPtrData{ nullptr, IDC_SIZENWSE, 0 }, // POINTER_SESIZE
+ ImplPtrData{ nullptr, IDC_SIZENS, 0 }, // POINTER_WINDOW_NSIZE
+ ImplPtrData{ nullptr, IDC_SIZENS, 0 }, // POINTER_WINDOW_SSIZE
+ ImplPtrData{ nullptr, IDC_SIZEWE, 0 }, // POINTER_WINDOW_WSIZE
+ ImplPtrData{ nullptr, IDC_SIZEWE, 0 }, // POINTER_WINDOW_ESIZE
+ ImplPtrData{ nullptr, IDC_SIZENWSE, 0 }, // POINTER_WINDOW_NWSIZE
+ ImplPtrData{ nullptr, IDC_SIZENESW, 0 }, // POINTER_WINDOW_NESIZE
+ ImplPtrData{ nullptr, IDC_SIZENESW, 0 }, // POINTER_WINDOW_SWSIZE
+ ImplPtrData{ nullptr, IDC_SIZENWSE, 0 }, // POINTER_WINDOW_SESIZE
+ ImplPtrData{ nullptr, IDC_SIZEWE, 0 }, // POINTER_HSPLIT
+ ImplPtrData{ nullptr, IDC_SIZENS, 0 }, // POINTER_VSPLIT
+ ImplPtrData{ nullptr, IDC_SIZEWE, 0 }, // POINTER_HSIZEBAR
+ ImplPtrData{ nullptr, IDC_SIZENS, 0 }, // POINTER_VSIZEBAR
+ ImplPtrData{ nullptr, IDC_HAND, 0 }, // POINTER_HAND
+ ImplPtrData{ nullptr, IDC_HAND, 0 }, // POINTER_REFHAND
+ ImplPtrData{ nullptr, IDC_PEN, 0 }, // POINTER_PEN
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_MAGNIFY }, // POINTER_MAGNIFY
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_FILL }, // POINTER_FILL
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_ROTATE }, // POINTER_ROTATE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_HSHEAR }, // POINTER_HSHEAR
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_VSHEAR }, // POINTER_VSHEAR
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_MIRROR }, // POINTER_MIRROR
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_CROOK }, // POINTER_CROOK
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_CROP }, // POINTER_CROP
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_MOVEPOINT }, // POINTER_MOVEPOINT
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_MOVEBEZIERWEIGHT }, // POINTER_MOVEBEZIERWEIGHT
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_MOVEDATA }, // POINTER_MOVEDATA
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_COPYDATA }, // POINTER_COPYDATA
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_LINKDATA }, // POINTER_LINKDATA
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_MOVEDATALINK }, // POINTER_MOVEDATALINK
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_COPYDATALINK }, // POINTER_COPYDATALINK
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_MOVEFILE }, // POINTER_MOVEFILE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_COPYFILE }, // POINTER_COPYFILE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_LINKFILE }, // POINTER_LINKFILE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_MOVEFILELINK }, // POINTER_MOVEFILELINK
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_COPYFILELINK }, // POINTER_COPYFILELINK
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_MOVEFILES }, // POINTER_MOVEFILES
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_COPYFILES }, // POINTER_COPYFILES
+ ImplPtrData{ nullptr, IDC_NO, 0 }, // POINTER_NOTALLOWED
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_LINE }, // POINTER_DRAW_LINE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_RECT }, // POINTER_DRAW_RECT
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_POLYGON }, // POINTER_DRAW_POLYGON
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_BEZIER }, // POINTER_DRAW_BEZIER
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_ARC }, // POINTER_DRAW_ARC
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_PIE }, // POINTER_DRAW_PIE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_CIRCLECUT }, // POINTER_DRAW_CIRCLECUT
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_ELLIPSE }, // POINTER_DRAW_ELLIPSE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_FREEHAND }, // POINTER_DRAW_FREEHAND
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_CONNECT }, // POINTER_DRAW_CONNECT
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_TEXT }, // POINTER_DRAW_TEXT
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DRAW_CAPTION }, // POINTER_DRAW_CAPTION
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_CHART }, // POINTER_CHART
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_DETECTIVE }, // POINTER_DETECTIVE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_PIVOT_COL }, // POINTER_PIVOT_COL
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_PIVOT_ROW }, // POINTER_PIVOT_ROW
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_PIVOT_FIELD }, // POINTER_PIVOT_FIELD
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_CHAIN }, // POINTER_CHAIN
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_CHAIN_NOTALLOWED }, // POINTER_CHAIN_NOTALLOWED
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_N }, // POINTER_AUTOSCROLL_N
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_S }, // POINTER_AUTOSCROLL_S
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_W }, // POINTER_AUTOSCROLL_W
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_E }, // POINTER_AUTOSCROLL_E
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NW }, // POINTER_AUTOSCROLL_NW
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NE }, // POINTER_AUTOSCROLL_NE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_SW }, // POINTER_AUTOSCROLL_SW
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_SE }, // POINTER_AUTOSCROLL_SE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NS }, // POINTER_AUTOSCROLL_NS
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_WE }, // POINTER_AUTOSCROLL_WE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_AUTOSCROLL_NSWE }, // POINTER_AUTOSCROLL_NSWE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_TEXT_VERTICAL }, // POINTER_TEXT_VERTICAL
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_PIVOT_DELETE }, // POINTER_PIVOT_DELETE
+
+ // #i32329#
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_S }, // POINTER_TAB_SELECT_S
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_E }, // POINTER_TAB_SELECT_E
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_SE }, // POINTER_TAB_SELECT_SE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_W }, // POINTER_TAB_SELECT_W
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_TAB_SELECT_SW }, // POINTER_TAB_SELECT_SW
+
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_HIDEWHITESPACE }, // POINTER_HIDEWHITESPACE
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_SHOWWHITESPACE }, // POINTER_UNHIDEWHITESPACE
+
+ ImplPtrData{ nullptr, nullptr, SAL_RESID_POINTER_FATCROSS } // POINTER_FATCROSS
+ };
+
+ // Mousepointer loaded ?
+ if ( !aImplPtrTab[ePointerStyle].mhCursor )
+ {
+ if ( aImplPtrTab[ePointerStyle].mnOwnId )
+ aImplPtrTab[ePointerStyle].mhCursor = ImplLoadSalCursor( aImplPtrTab[ePointerStyle].mnOwnId );
+ else
+ aImplPtrTab[ePointerStyle].mhCursor = LoadCursor( nullptr, aImplPtrTab[ePointerStyle].mnSysId );
+ }
+
+ // change the mouse pointer if different
+ if ( mhCursor != aImplPtrTab[ePointerStyle].mhCursor )
+ {
+ mhCursor = aImplPtrTab[ePointerStyle].mhCursor;
+ SetCursor( mhCursor );
+ }
+}
+
+void WinSalFrame::CaptureMouse( bool bCapture )
+{
+ // Send this Message to the window, because CaptureMouse() only work
+ // in the thread of the window, which has create this window
+ int nMsg;
+ if ( bCapture )
+ nMsg = SAL_MSG_CAPTUREMOUSE;
+ else
+ nMsg = SAL_MSG_RELEASEMOUSE;
+ SendMessageW( mhWnd, nMsg, 0, 0 );
+}
+
+void WinSalFrame::SetPointerPos( tools::Long nX, tools::Long nY )
+{
+ POINT aPt;
+ aPt.x = static_cast<int>(nX);
+ aPt.y = static_cast<int>(nY);
+ ClientToScreen( mhWnd, &aPt );
+ SetCursorPos( aPt.x, aPt.y );
+}
+
+void WinSalFrame::Flush()
+{
+ if(mpLocalGraphics)
+ mpLocalGraphics->Flush();
+ if(mpThreadGraphics)
+ mpThreadGraphics->Flush();
+ GdiFlush();
+}
+
+static void ImplSalFrameSetInputContext( HWND hWnd, const SalInputContext* pContext )
+{
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ bool bIME(pContext->mnOptions & InputContextFlags::Text);
+ if ( bIME )
+ {
+ if ( !pFrame->mbIME )
+ {
+ pFrame->mbIME = true;
+
+ if ( pFrame->mhDefIMEContext )
+ {
+ ImmAssociateContext( pFrame->mhWnd, pFrame->mhDefIMEContext );
+ UINT nImeProps = ImmGetProperty( GetKeyboardLayout( 0 ), IGP_PROPERTY );
+ pFrame->mbSpezIME = (nImeProps & IME_PROP_SPECIAL_UI) != 0;
+ pFrame->mbAtCursorIME = (nImeProps & IME_PROP_AT_CARET) != 0;
+ pFrame->mbHandleIME = !pFrame->mbSpezIME;
+ }
+ }
+
+ // When the application can't handle IME messages, then the
+ // System should handle the IME handling
+ if ( !(pContext->mnOptions & InputContextFlags::ExtText) )
+ pFrame->mbHandleIME = false;
+
+ // Set the Font for IME Handling
+ if ( pContext->mpFont )
+ {
+ HIMC hIMC = ImmGetContext( pFrame->mhWnd );
+ if ( hIMC )
+ {
+ LOGFONTW aLogFont;
+ ImplGetLogFontFromFontSelect(pContext->mpFont->GetFontSelectPattern(),
+ nullptr, aLogFont);
+ ImmSetCompositionFontW( hIMC, &aLogFont );
+ ImmReleaseContext( pFrame->mhWnd, hIMC );
+ }
+ }
+ }
+ else
+ {
+ if ( pFrame->mbIME )
+ {
+ pFrame->mbIME = false;
+ pFrame->mbHandleIME = false;
+ ImmAssociateContext( pFrame->mhWnd, nullptr );
+ }
+ }
+}
+
+void WinSalFrame::SetInputContext( SalInputContext* pContext )
+{
+ // Must be called in the main thread!
+ SendMessageW( mhWnd, SAL_MSG_SETINPUTCONTEXT, 0, reinterpret_cast<LPARAM>(pContext) );
+}
+
+static void ImplSalFrameEndExtTextInput( HWND hWnd, EndExtTextInputFlags nFlags )
+{
+ HIMC hIMC = ImmGetContext( hWnd );
+ if ( hIMC )
+ {
+ DWORD nIndex;
+ if ( nFlags & EndExtTextInputFlags::Complete )
+ nIndex = CPS_COMPLETE;
+ else
+ nIndex = CPS_CANCEL;
+
+ ImmNotifyIME( hIMC, NI_COMPOSITIONSTR, nIndex, 0 );
+ ImmReleaseContext( hWnd, hIMC );
+ }
+}
+
+void WinSalFrame::EndExtTextInput( EndExtTextInputFlags nFlags )
+{
+ // Must be called in the main thread!
+ SendMessageW( mhWnd, SAL_MSG_ENDEXTTEXTINPUT, static_cast<WPARAM>(nFlags), 0 );
+}
+
+static void ImplGetKeyNameText( LONG lParam, sal_Unicode* pBuf,
+ UINT& rCount, UINT nMaxSize,
+ const char* pReplace )
+{
+ static_assert( sizeof( WCHAR ) == sizeof( sal_Unicode ), "must be the same size" );
+
+ static const int nMaxKeyLen = 350;
+ WCHAR aKeyBuf[ nMaxKeyLen ];
+ int nKeyLen = 0;
+ if ( lParam )
+ {
+ OUString aLang = Application::GetSettings().GetUILanguageTag().getLanguage();
+ OUString aRet;
+
+ aRet = ::vcl_sal::getKeysReplacementName( aLang, lParam );
+ if( aRet.isEmpty() )
+ {
+ nKeyLen = GetKeyNameTextW( lParam, aKeyBuf, nMaxKeyLen );
+ SAL_WARN_IF( nKeyLen > nMaxKeyLen, "vcl", "Invalid key name length!" );
+ if( nKeyLen > nMaxKeyLen )
+ nKeyLen = 0;
+ else if( nKeyLen > 0 )
+ {
+ // Capitalize just the first letter of key names
+ CharLowerBuffW( aKeyBuf, nKeyLen );
+
+ bool bUpper = true;
+ for( WCHAR *pW=aKeyBuf, *pE=pW+nKeyLen; pW < pE; ++pW )
+ {
+ if( bUpper )
+ CharUpperBuffW( pW, 1 );
+ bUpper = (*pW=='+') || (*pW=='-') || (*pW==' ') || (*pW=='.');
+ }
+ }
+ }
+ else
+ {
+ nKeyLen = aRet.getLength();
+ wcscpy( aKeyBuf, o3tl::toW( aRet.getStr() ));
+ }
+ }
+
+ if ( (nKeyLen > 0) || pReplace )
+ {
+ if( (rCount > 0) && (rCount < nMaxSize) )
+ {
+ pBuf[rCount] = '+';
+ rCount++;
+ }
+
+ if( nKeyLen > 0 )
+ {
+ WCHAR *pW = aKeyBuf, *pE = aKeyBuf + nKeyLen;
+ while ((pW < pE) && *pW && (rCount < nMaxSize))
+ pBuf[rCount++] = *pW++;
+ }
+ else // fall back to provided default name
+ {
+ while( *pReplace && (rCount < nMaxSize) )
+ {
+ pBuf[rCount] = *pReplace;
+ rCount++;
+ pReplace++;
+ }
+ }
+ }
+ else
+ rCount = 0;
+}
+
+OUString WinSalFrame::GetKeyName( sal_uInt16 nKeyCode )
+{
+ static const UINT nMaxKeyLen = 350;
+ sal_Unicode aKeyBuf[ nMaxKeyLen ];
+ UINT nKeyBufLen = 0;
+ UINT nSysCode = 0;
+
+ if ( nKeyCode & KEY_MOD1 )
+ {
+ nSysCode = MapVirtualKeyW( VK_CONTROL, 0 );
+ nSysCode = (nSysCode << 16) | ((sal_uLong(1)) << 25);
+ ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, "Ctrl" );
+ }
+
+ if ( nKeyCode & KEY_MOD2 )
+ {
+ nSysCode = MapVirtualKeyW( VK_MENU, 0 );
+ nSysCode = (nSysCode << 16) | ((sal_uLong(1)) << 25);
+ ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, "Alt" );
+ }
+
+ if ( nKeyCode & KEY_SHIFT )
+ {
+ nSysCode = MapVirtualKeyW( VK_SHIFT, 0 );
+ nSysCode = (nSysCode << 16) | ((sal_uLong(1)) << 25);
+ ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, "Shift" );
+ }
+
+ sal_uInt16 nCode = nKeyCode & 0x0FFF;
+ sal_uLong nSysCode2 = 0;
+ const char* pReplace = nullptr;
+ sal_Unicode cSVCode = 0;
+ char aFBuf[4];
+ nSysCode = 0;
+
+ if ( (nCode >= KEY_0) && (nCode <= KEY_9) )
+ cSVCode = '0' + (nCode - KEY_0);
+ else if ( (nCode >= KEY_A) && (nCode <= KEY_Z) )
+ cSVCode = 'A' + (nCode - KEY_A);
+ else if ( (nCode >= KEY_F1) && (nCode <= KEY_F26) )
+ {
+ nSysCode = VK_F1 + (nCode - KEY_F1);
+ aFBuf[0] = 'F';
+ if (nCode <= KEY_F9)
+ {
+ aFBuf[1] = sal::static_int_cast<char>('1' + (nCode - KEY_F1));
+ aFBuf[2] = 0;
+ }
+ else if (nCode <= KEY_F19)
+ {
+ aFBuf[1] = '1';
+ aFBuf[2] = sal::static_int_cast<char>('0' + (nCode - KEY_F10));
+ aFBuf[3] = 0;
+ }
+ else
+ {
+ aFBuf[1] = '2';
+ aFBuf[2] = sal::static_int_cast<char>('0' + (nCode - KEY_F20));
+ aFBuf[3] = 0;
+ }
+ pReplace = aFBuf;
+ }
+ else
+ {
+ switch ( nCode )
+ {
+ case KEY_DOWN:
+ nSysCode = VK_DOWN;
+ nSysCode2 = ((sal_uLong(1)) << 24);
+ pReplace = "Down";
+ break;
+ case KEY_UP:
+ nSysCode = VK_UP;
+ nSysCode2 = ((sal_uLong(1)) << 24);
+ pReplace = "Up";
+ break;
+ case KEY_LEFT:
+ nSysCode = VK_LEFT;
+ nSysCode2 = ((sal_uLong(1)) << 24);
+ pReplace = "Left";
+ break;
+ case KEY_RIGHT:
+ nSysCode = VK_RIGHT;
+ nSysCode2 = ((sal_uLong(1)) << 24);
+ pReplace = "Right";
+ break;
+ case KEY_HOME:
+ nSysCode = VK_HOME;
+ nSysCode2 = ((sal_uLong(1)) << 24);
+ pReplace = "Home";
+ break;
+ case KEY_END:
+ nSysCode = VK_END;
+ nSysCode2 = ((sal_uLong(1)) << 24);
+ pReplace = "End";
+ break;
+ case KEY_PAGEUP:
+ nSysCode = VK_PRIOR;
+ nSysCode2 = ((sal_uLong(1)) << 24);
+ pReplace = "Page Up";
+ break;
+ case KEY_PAGEDOWN:
+ nSysCode = VK_NEXT;
+ nSysCode2 = ((sal_uLong(1)) << 24);
+ pReplace = "Page Down";
+ break;
+ case KEY_RETURN:
+ nSysCode = VK_RETURN;
+ pReplace = "Enter";
+ break;
+ case KEY_ESCAPE:
+ nSysCode = VK_ESCAPE;
+ pReplace = "Escape";
+ break;
+ case KEY_TAB:
+ nSysCode = VK_TAB;
+ pReplace = "Tab";
+ break;
+ case KEY_BACKSPACE:
+ nSysCode = VK_BACK;
+ pReplace = "Backspace";
+ break;
+ case KEY_SPACE:
+ nSysCode = VK_SPACE;
+ pReplace = "Space";
+ break;
+ case KEY_INSERT:
+ nSysCode = VK_INSERT;
+ nSysCode2 = ((sal_uLong(1)) << 24);
+ pReplace = "Insert";
+ break;
+ case KEY_DELETE:
+ nSysCode = VK_DELETE;
+ nSysCode2 = ((sal_uLong(1)) << 24);
+ pReplace = "Delete";
+ break;
+
+ case KEY_ADD:
+ cSVCode = '+';
+ break;
+ case KEY_SUBTRACT:
+ cSVCode = '-';
+ break;
+ case KEY_MULTIPLY:
+ cSVCode = '*';
+ break;
+ case KEY_DIVIDE:
+ cSVCode = '/';
+ break;
+ case KEY_POINT:
+ cSVCode = '.';
+ break;
+ case KEY_COMMA:
+ cSVCode = ',';
+ break;
+ case KEY_LESS:
+ cSVCode = '<';
+ break;
+ case KEY_GREATER:
+ cSVCode = '>';
+ break;
+ case KEY_EQUAL:
+ cSVCode = '=';
+ break;
+ case KEY_NUMBERSIGN:
+ cSVCode = '#';
+ break;
+ case KEY_XF86FORWARD:
+ cSVCode = VK_BROWSER_FORWARD;
+ break;
+ case KEY_XF86BACK:
+ cSVCode = VK_BROWSER_BACK;
+ break;
+ case KEY_COLON:
+ cSVCode = ':';
+ break;
+ case KEY_SEMICOLON:
+ cSVCode = ';';
+ break;
+ case KEY_QUOTERIGHT:
+ cSVCode = '\'';
+ break;
+ case KEY_BRACKETLEFT:
+ cSVCode = '[';
+ break;
+ case KEY_BRACKETRIGHT:
+ cSVCode = ']';
+ break;
+ case KEY_QUOTELEFT:
+ cSVCode = '`';
+ break;
+ case KEY_RIGHTCURLYBRACKET:
+ cSVCode = '}';
+ break;
+ }
+ }
+
+ if ( nSysCode )
+ {
+ nSysCode = MapVirtualKeyW( nSysCode, 0 );
+ if ( nSysCode )
+ nSysCode = (nSysCode << 16) | nSysCode2;
+ ImplGetKeyNameText( nSysCode, aKeyBuf, nKeyBufLen, nMaxKeyLen, pReplace );
+ }
+ else
+ {
+ if ( cSVCode )
+ {
+ if ( nKeyBufLen > 0 )
+ aKeyBuf[ nKeyBufLen++ ] = '+';
+ if( nKeyBufLen < nMaxKeyLen )
+ aKeyBuf[ nKeyBufLen++ ] = cSVCode;
+ }
+ }
+
+ if( !nKeyBufLen )
+ return OUString();
+
+ return OUString( aKeyBuf, sal::static_int_cast< sal_uInt16 >(nKeyBufLen) );
+}
+
+static Color ImplWinColorToSal( COLORREF nColor )
+{
+ return Color( GetRValue( nColor ), GetGValue( nColor ), GetBValue( nColor ) );
+}
+
+static void ImplSalUpdateStyleFontW( HDC hDC, const LOGFONTW& rLogFont, vcl::Font& rFont )
+{
+ ImplSalLogFontToFontW( hDC, rLogFont, rFont );
+
+ // On Windows 9x, Windows NT we get sometimes very small sizes
+ // (for example for the small Caption height).
+ // So if it is MS Sans Serif, a none scalable font we use
+ // 8 Point as the minimum control height, in all other cases
+ // 6 Point is the smallest one
+ if ( rFont.GetFontHeight() < 8 )
+ {
+ if ( rtl_ustr_compareIgnoreAsciiCase( o3tl::toU(rLogFont.lfFaceName), o3tl::toU(L"MS Sans Serif") ) == 0 )
+ rFont.SetFontHeight( 8 );
+ else if ( rFont.GetFontHeight() < 6 )
+ rFont.SetFontHeight( 6 );
+ }
+}
+
+static tools::Long ImplW2I( const wchar_t* pStr )
+{
+ tools::Long n = 0;
+ int nSign = 1;
+
+ if ( *pStr == '-' )
+ {
+ nSign = -1;
+ pStr++;
+ }
+
+ while( (*pStr >= 48) && (*pStr <= 57) )
+ {
+ n *= 10;
+ n += ((*pStr) - 48);
+ pStr++;
+ }
+
+ n *= nSign;
+
+ return n;
+}
+
+void WinSalFrame::UpdateSettings( AllSettings& rSettings )
+{
+ MouseSettings aMouseSettings = rSettings.GetMouseSettings();
+ aMouseSettings.SetDoubleClickTime( GetDoubleClickTime() );
+ aMouseSettings.SetDoubleClickWidth( GetSystemMetrics( SM_CXDOUBLECLK ) );
+ aMouseSettings.SetDoubleClickHeight( GetSystemMetrics( SM_CYDOUBLECLK ) );
+ tools::Long nDragWidth = GetSystemMetrics( SM_CXDRAG );
+ tools::Long nDragHeight = GetSystemMetrics( SM_CYDRAG );
+ if ( nDragWidth )
+ aMouseSettings.SetStartDragWidth( nDragWidth );
+ if ( nDragHeight )
+ aMouseSettings.SetStartDragHeight( nDragHeight );
+ HKEY hRegKey;
+ if ( RegOpenKeyW( HKEY_CURRENT_USER,
+ L"Control Panel\\Desktop",
+ &hRegKey ) == ERROR_SUCCESS )
+ {
+ wchar_t aValueBuf[10];
+ DWORD nValueSize = sizeof( aValueBuf );
+ DWORD nType;
+ if ( RegQueryValueExW( hRegKey, L"MenuShowDelay", nullptr,
+ &nType, reinterpret_cast<LPBYTE>(aValueBuf), &nValueSize ) == ERROR_SUCCESS )
+ {
+ if ( nType == REG_SZ )
+ aMouseSettings.SetMenuDelay( static_cast<sal_uLong>(ImplW2I( aValueBuf )) );
+ }
+
+ RegCloseKey( hRegKey );
+ }
+
+ StyleSettings aStyleSettings = rSettings.GetStyleSettings();
+
+ // High contrast
+ HIGHCONTRAST hc;
+ hc.cbSize = sizeof( HIGHCONTRAST );
+ if( SystemParametersInfoW( SPI_GETHIGHCONTRAST, hc.cbSize, &hc, 0 )
+ && (hc.dwFlags & HCF_HIGHCONTRASTON) )
+ aStyleSettings.SetHighContrastMode( true );
+ else
+ aStyleSettings.SetHighContrastMode( false );
+
+ aStyleSettings.SetScrollBarSize( GetSystemMetrics( SM_CXVSCROLL ) );
+ aStyleSettings.SetSpinSize( GetSystemMetrics( SM_CXVSCROLL ) );
+ UINT blinkTime = GetCaretBlinkTime();
+ aStyleSettings.SetCursorBlinkTime(
+ blinkTime == 0 || blinkTime == INFINITE // 0 indicates error
+ ? STYLE_CURSOR_NOBLINKTIME : blinkTime );
+ aStyleSettings.SetFloatTitleHeight( GetSystemMetrics( SM_CYSMCAPTION ) );
+ aStyleSettings.SetTitleHeight( GetSystemMetrics( SM_CYCAPTION ) );
+ aStyleSettings.SetActiveBorderColor( ImplWinColorToSal( GetSysColor( COLOR_ACTIVEBORDER ) ) );
+ aStyleSettings.SetDeactiveBorderColor( ImplWinColorToSal( GetSysColor( COLOR_INACTIVEBORDER ) ) );
+ aStyleSettings.SetDeactiveColor( ImplWinColorToSal( GetSysColor( COLOR_GRADIENTINACTIVECAPTION ) ) );
+
+ Color aControlTextColor;
+ Color aMenuBarTextColor;
+ Color aMenuBarRolloverTextColor;
+ Color aHighlightTextColor = ImplWinColorToSal(GetSysColor(COLOR_HIGHLIGHTTEXT));
+
+ BOOL bFlatMenus = FALSE;
+ SystemParametersInfoW( SPI_GETFLATMENU, 0, &bFlatMenus, 0);
+ if( bFlatMenus )
+ {
+ aStyleSettings.SetUseFlatMenus( true );
+ // flat borders for our controls etc. as well in this mode (ie, no 3d borders)
+ // this is not active in the classic style appearance
+ aStyleSettings.SetUseFlatBorders( true );
+ }
+ else
+ {
+ aStyleSettings.SetUseFlatMenus( false );
+ aStyleSettings.SetUseFlatBorders( false );
+ }
+
+ if( bFlatMenus )
+ {
+ aStyleSettings.SetMenuHighlightColor( ImplWinColorToSal( GetSysColor( COLOR_MENUHILIGHT ) ) );
+ aStyleSettings.SetMenuBarRolloverColor( ImplWinColorToSal( GetSysColor( COLOR_MENUHILIGHT ) ) );
+ aStyleSettings.SetMenuBorderColor( ImplWinColorToSal( GetSysColor( COLOR_3DSHADOW ) ) );
+ }
+ else
+ {
+ aStyleSettings.SetMenuHighlightColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHT ) ) );
+ aStyleSettings.SetMenuBarRolloverColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHT ) ) );
+ aStyleSettings.SetMenuBorderColor( ImplWinColorToSal( GetSysColor( COLOR_3DLIGHT ) ) );
+ }
+
+ const bool bUseDarkMode(UseDarkMode());
+
+ OUString sThemeName(!bUseDarkMode ? u"colibre" : u"colibre_dark");
+ aStyleSettings.SetPreferredIconTheme(sThemeName, bUseDarkMode);
+
+ if (bUseDarkMode)
+ {
+ SetWindowTheme(mhWnd, L"Explorer", nullptr);
+
+ HTHEME hTheme = OpenThemeData(nullptr, L"ItemsView");
+ COLORREF color;
+ GetThemeColor(hTheme, 0, 0, TMT_FILLCOLOR, &color);
+ aStyleSettings.SetFaceColor( ImplWinColorToSal( color ) );
+ aStyleSettings.SetWindowColor( ImplWinColorToSal( color ) );
+
+ // tdf#156040 in the absence of a better idea, do like
+ // StyleSettings::Set3DColors does
+ Color aLightColor(ImplWinColorToSal(color));
+ aLightColor.DecreaseLuminance(64);
+ aStyleSettings.SetLightColor(aLightColor);
+
+ GetThemeColor(hTheme, 0, 0, TMT_TEXTCOLOR, &color);
+ aStyleSettings.SetWindowTextColor( ImplWinColorToSal( color ) );
+ aStyleSettings.SetToolTextColor( ImplWinColorToSal( color ) );
+ GetThemeColor(hTheme, 0, 0, TMT_SHADOWCOLOR, &color);
+ aStyleSettings.SetShadowColor( ImplWinColorToSal( color ) );
+ GetThemeColor(hTheme, 0, 0, TMT_DKSHADOW3D, &color);
+ aStyleSettings.SetDarkShadowColor( ImplWinColorToSal( color ) );
+ CloseThemeData(hTheme);
+
+ hTheme = OpenThemeData(mhWnd, L"Button");
+ GetThemeColor(hTheme, BP_PUSHBUTTON, PBS_NORMAL, TMT_TEXTCOLOR, &color);
+ aControlTextColor = ImplWinColorToSal(color);
+ GetThemeColor(hTheme, BP_CHECKBOX, CBS_CHECKEDNORMAL, TMT_TEXTCOLOR, &color);
+ aStyleSettings.SetRadioCheckTextColor( ImplWinColorToSal( color ) );
+ CloseThemeData(hTheme);
+
+ SetWindowTheme(mhWnd, nullptr, nullptr);
+
+ hTheme = OpenThemeData(mhWnd, L"Menu");
+ GetThemeColor(hTheme, MENU_POPUPITEM, MBI_NORMAL, TMT_TEXTCOLOR, &color);
+ aStyleSettings.SetMenuTextColor( ImplWinColorToSal( color ) );
+ aMenuBarTextColor = ImplWinColorToSal( color );
+ aMenuBarRolloverTextColor = ImplWinColorToSal( color );
+ CloseThemeData(hTheme);
+
+ aStyleSettings.SetActiveTabColor( aStyleSettings.GetWindowColor() );
+ hTheme = OpenThemeData(mhWnd, L"Toolbar");
+ GetThemeColor(hTheme, 0, 0, TMT_FILLCOLOR, &color);
+ aStyleSettings.SetInactiveTabColor( ImplWinColorToSal( color ) );
+ // see ImplDrawNativeControl for dark mode
+ aStyleSettings.SetMenuBarColor( aStyleSettings.GetWindowColor() );
+ CloseThemeData(hTheme);
+
+ hTheme = OpenThemeData(mhWnd, L"Textstyle");
+ if (hTheme)
+ {
+ GetThemeColor(hTheme, TEXT_HYPERLINKTEXT, TS_HYPERLINK_NORMAL, TMT_TEXTCOLOR, &color);
+ aStyleSettings.SetLinkColor(ImplWinColorToSal(color));
+ CloseThemeData(hTheme);
+ }
+
+ // tdf#148448 pick a warning color more likely to be readable as a
+ // background in a dark theme
+ aStyleSettings.SetWarningColor(Color(0xf5, 0x79, 0x00));
+ }
+ else
+ {
+ aStyleSettings.SetFaceColor( ImplWinColorToSal( GetSysColor( COLOR_3DFACE ) ) );
+ aStyleSettings.SetWindowColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOW ) ) );
+ aStyleSettings.SetWindowTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) );
+ aStyleSettings.SetToolTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) );
+ aStyleSettings.SetLightColor( ImplWinColorToSal( GetSysColor( COLOR_3DHILIGHT ) ) );
+ aStyleSettings.SetShadowColor( ImplWinColorToSal( GetSysColor( COLOR_3DSHADOW ) ) );
+ aStyleSettings.SetDarkShadowColor( ImplWinColorToSal( GetSysColor( COLOR_3DDKSHADOW ) ) );
+ aControlTextColor = ImplWinColorToSal(GetSysColor(COLOR_BTNTEXT));
+ aStyleSettings.SetRadioCheckTextColor( ImplWinColorToSal( GetSysColor( COLOR_WINDOWTEXT ) ) );
+ aStyleSettings.SetMenuTextColor( ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) ) );
+ aMenuBarTextColor = ImplWinColorToSal( GetSysColor( COLOR_MENUTEXT ) );
+ aMenuBarRolloverTextColor = aHighlightTextColor;
+ if( bFlatMenus )
+ aStyleSettings.SetMenuBarColor( ImplWinColorToSal( GetSysColor( COLOR_MENUBAR ) ) );
+ else
+ aStyleSettings.SetMenuBarColor( ImplWinColorToSal( GetSysColor( COLOR_MENU ) ) );
+ aStyleSettings.SetActiveTabColor( aStyleSettings.GetWindowColor() );
+ aStyleSettings.SetInactiveTabColor( aStyleSettings.GetFaceColor() );
+ }
+
+ if ( std::optional<Color> aColor = aStyleSettings.GetPersonaMenuBarTextColor() )
+ {
+ aMenuBarTextColor = *aColor;
+ if (!aStyleSettings.GetHighContrastMode())
+ aMenuBarRolloverTextColor = *aColor;
+ }
+
+ aStyleSettings.SetMenuBarTextColor( aMenuBarTextColor );
+ aStyleSettings.SetMenuBarRolloverTextColor( aMenuBarRolloverTextColor );
+
+ aStyleSettings.SetLightBorderColor( ImplWinColorToSal( GetSysColor( COLOR_3DLIGHT ) ) );
+ aStyleSettings.SetHelpColor( ImplWinColorToSal( GetSysColor( COLOR_INFOBK ) ) );
+ aStyleSettings.SetHelpTextColor( ImplWinColorToSal( GetSysColor( COLOR_INFOTEXT ) ) );
+
+ aStyleSettings.SetWorkspaceColor(aStyleSettings.GetFaceColor());
+ aStyleSettings.SetDialogColor(aStyleSettings.GetFaceColor());
+ aStyleSettings.SetDialogTextColor(aControlTextColor);
+
+ Color aHighlightButtonTextColor = aStyleSettings.GetHighContrastMode() ?
+ aHighlightTextColor : aControlTextColor;
+
+ if (aStyleSettings.GetHighContrastMode())
+ {
+ Color aLinkColor(ImplWinColorToSal(GetSysColor(COLOR_HOTLIGHT)));
+ aStyleSettings.SetLinkColor(aLinkColor);
+ aStyleSettings.SetVisitedLinkColor(aLinkColor);
+ }
+
+ aStyleSettings.SetDefaultButtonTextColor(aHighlightButtonTextColor);
+ aStyleSettings.SetButtonTextColor(aControlTextColor);
+ aStyleSettings.SetDefaultActionButtonTextColor(aHighlightButtonTextColor);
+ aStyleSettings.SetActionButtonTextColor(aControlTextColor);
+ aStyleSettings.SetFlatButtonTextColor(aControlTextColor);
+ aStyleSettings.SetDefaultButtonRolloverTextColor(aHighlightButtonTextColor);
+ aStyleSettings.SetButtonRolloverTextColor(aHighlightButtonTextColor);
+ aStyleSettings.SetDefaultActionButtonRolloverTextColor(aHighlightButtonTextColor);
+ aStyleSettings.SetActionButtonRolloverTextColor(aHighlightButtonTextColor);
+ aStyleSettings.SetFlatButtonRolloverTextColor(aHighlightButtonTextColor);
+ aStyleSettings.SetDefaultButtonPressedRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetButtonPressedRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetDefaultActionButtonPressedRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetActionButtonPressedRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetFlatButtonPressedRolloverTextColor(aControlTextColor);
+
+ aStyleSettings.SetTabTextColor(aControlTextColor);
+ aStyleSettings.SetTabRolloverTextColor(aControlTextColor);
+ aStyleSettings.SetTabHighlightTextColor(aControlTextColor);
+
+ aStyleSettings.SetGroupTextColor( aStyleSettings.GetRadioCheckTextColor() );
+ aStyleSettings.SetLabelTextColor( aStyleSettings.GetRadioCheckTextColor() );
+ aStyleSettings.SetFieldColor( aStyleSettings.GetWindowColor() );
+ aStyleSettings.SetListBoxWindowBackgroundColor( aStyleSettings.GetWindowColor() );
+ aStyleSettings.SetFieldTextColor( aStyleSettings.GetWindowTextColor() );
+ aStyleSettings.SetFieldRolloverTextColor( aStyleSettings.GetFieldTextColor() );
+ aStyleSettings.SetListBoxWindowTextColor( aStyleSettings.GetFieldTextColor() );
+
+ aStyleSettings.SetAccentColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHT ) ) );
+ // https://devblogs.microsoft.com/oldnewthing/20170405-00/?p=95905
+
+ aStyleSettings.SetHighlightColor( ImplWinColorToSal( GetSysColor( COLOR_HIGHLIGHT ) ) );
+ aStyleSettings.SetHighlightTextColor(aHighlightTextColor);
+ aStyleSettings.SetListBoxWindowHighlightColor( aStyleSettings.GetHighlightColor() );
+ aStyleSettings.SetListBoxWindowHighlightTextColor( aStyleSettings.GetHighlightTextColor() );
+ aStyleSettings.SetMenuHighlightTextColor( aStyleSettings.GetHighlightTextColor() );
+
+ ImplSVData* pSVData = ImplGetSVData();
+ pSVData->maNWFData.mnMenuFormatBorderX = 0;
+ pSVData->maNWFData.mnMenuFormatBorderY = 0;
+ pSVData->maNWFData.maMenuBarHighlightTextColor = COL_TRANSPARENT;
+ GetSalData()->mbThemeMenuSupport = false;
+ aStyleSettings.SetMenuColor( ImplWinColorToSal( GetSysColor( COLOR_MENU ) ) );
+ 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 ) ) );
+
+ aStyleSettings.SetCheckedColorSpecialCase( );
+
+ // caret width
+ DWORD nCaretWidth = 2;
+ if( SystemParametersInfoW( SPI_GETCARETWIDTH, 0, &nCaretWidth, 0 ) )
+ aStyleSettings.SetCursorSize( nCaretWidth );
+
+ // Query Fonts
+ vcl::Font aMenuFont = aStyleSettings.GetMenuFont();
+ vcl::Font aTitleFont = aStyleSettings.GetTitleFont();
+ vcl::Font aFloatTitleFont = aStyleSettings.GetFloatTitleFont();
+ vcl::Font aHelpFont = aStyleSettings.GetHelpFont();
+ vcl::Font aAppFont = aStyleSettings.GetAppFont();
+ vcl::Font aIconFont = aStyleSettings.GetIconFont();
+ HDC hDC = GetDC( nullptr );
+ NONCLIENTMETRICSW aNonClientMetrics;
+ aNonClientMetrics.cbSize = sizeof( aNonClientMetrics );
+ if ( SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, sizeof( aNonClientMetrics ), &aNonClientMetrics, 0 ) )
+ {
+ ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfMenuFont, aMenuFont );
+ ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfCaptionFont, aTitleFont );
+ ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfSmCaptionFont, aFloatTitleFont );
+ ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfStatusFont, aHelpFont );
+ ImplSalUpdateStyleFontW( hDC, aNonClientMetrics.lfMessageFont, aAppFont );
+
+ LOGFONTW aLogFont;
+ if ( SystemParametersInfoW( SPI_GETICONTITLELOGFONT, 0, &aLogFont, 0 ) )
+ ImplSalUpdateStyleFontW( hDC, aLogFont, aIconFont );
+ }
+
+ ReleaseDC( nullptr, hDC );
+
+ aStyleSettings.SetToolbarIconSize(ToolbarIconSize::Large);
+
+ aStyleSettings.BatchSetFonts( aAppFont, aAppFont );
+
+ aStyleSettings.SetMenuFont( aMenuFont );
+ aStyleSettings.SetTitleFont( aTitleFont );
+ aStyleSettings.SetFloatTitleFont( aFloatTitleFont );
+ aStyleSettings.SetHelpFont( aHelpFont );
+ aStyleSettings.SetIconFont( aIconFont );
+
+ if ( aAppFont.GetWeight() > WEIGHT_NORMAL )
+ aAppFont.SetWeight( WEIGHT_NORMAL );
+ aStyleSettings.SetToolFont( aAppFont );
+ aStyleSettings.SetTabFont( aAppFont );
+
+ BOOL bDragFull;
+ if ( SystemParametersInfoW( SPI_GETDRAGFULLWINDOWS, 0, &bDragFull, 0 ) )
+ {
+ DragFullOptions nDragFullOptions = aStyleSettings.GetDragFullOptions();
+ if ( bDragFull )
+ nDragFullOptions |= DragFullOptions::WindowMove | DragFullOptions::WindowSize | DragFullOptions::Docking | DragFullOptions::Split;
+ else
+ nDragFullOptions &= ~DragFullOptions(DragFullOptions::WindowMove | DragFullOptions::WindowSize | DragFullOptions::Docking | DragFullOptions::Split);
+ aStyleSettings.SetDragFullOptions( nDragFullOptions );
+ }
+
+ if ( RegOpenKeyW( HKEY_CURRENT_USER,
+ L"Control Panel\\International\\Calendars\\TwoDigitYearMax",
+ &hRegKey ) == ERROR_SUCCESS )
+ {
+ wchar_t aValueBuf[10];
+ DWORD nValue;
+ DWORD nValueSize = sizeof( aValueBuf );
+ DWORD nType;
+ if ( RegQueryValueExW( hRegKey, L"1", nullptr,
+ &nType, reinterpret_cast<LPBYTE>(aValueBuf), &nValueSize ) == ERROR_SUCCESS )
+ {
+ if ( nType == REG_SZ )
+ {
+ nValue = static_cast<sal_uLong>(ImplW2I( aValueBuf ));
+ if ( (nValue > 1000) && (nValue < 10000) )
+ {
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::DateFormat::TwoDigitYear::set(static_cast<sal_Int32>(nValue-99), batch);
+ batch->commit();
+ }
+ }
+ }
+
+ RegCloseKey( hRegKey );
+ }
+
+ rSettings.SetMouseSettings( aMouseSettings );
+ rSettings.SetStyleSettings( aStyleSettings );
+
+ // now apply the values from theming, if available
+ WinSalGraphics::updateSettingsNative( rSettings );
+}
+
+const SystemEnvData* WinSalFrame::GetSystemData() const
+{
+ return &maSysData;
+}
+
+void WinSalFrame::Beep()
+{
+ // a simple beep
+ MessageBeep( 0 );
+}
+
+SalFrame::SalPointerState WinSalFrame::GetPointerState()
+{
+ SalPointerState aState;
+ aState.mnState = 0;
+
+ if ( GetKeyState( VK_LBUTTON ) & 0x8000 )
+ aState.mnState |= MOUSE_LEFT;
+ if ( GetKeyState( VK_MBUTTON ) & 0x8000 )
+ aState.mnState |= MOUSE_MIDDLE;
+ if ( GetKeyState( VK_RBUTTON ) & 0x8000 )
+ aState.mnState |= MOUSE_RIGHT;
+ if ( GetKeyState( VK_SHIFT ) & 0x8000 )
+ aState.mnState |= KEY_SHIFT;
+ if ( GetKeyState( VK_CONTROL ) & 0x8000 )
+ aState.mnState |= KEY_MOD1;
+ if ( GetKeyState( VK_MENU ) & 0x8000 )
+ aState.mnState |= KEY_MOD2;
+
+ POINT pt;
+ GetCursorPos( &pt );
+
+ aState.maPos = Point(pt.x - maGeometry.x(), pt.y - maGeometry.y());
+ return aState;
+}
+
+KeyIndicatorState WinSalFrame::GetIndicatorState()
+{
+ KeyIndicatorState aState = KeyIndicatorState::NONE;
+ if (::GetKeyState(VK_CAPITAL))
+ aState |= KeyIndicatorState::CAPSLOCK;
+
+ if (::GetKeyState(VK_NUMLOCK))
+ aState |= KeyIndicatorState::NUMLOCK;
+
+ if (::GetKeyState(VK_SCROLL))
+ aState |= KeyIndicatorState::SCROLLLOCK;
+
+ return aState;
+}
+
+void WinSalFrame::SimulateKeyPress( sal_uInt16 nKeyCode )
+{
+ BYTE nVKey = 0;
+ switch (nKeyCode)
+ {
+ case KEY_CAPSLOCK:
+ nVKey = VK_CAPITAL;
+ break;
+ }
+
+ if (nVKey > 0 && nVKey < 255)
+ {
+ ::keybd_event(nVKey, 0x45, KEYEVENTF_EXTENDEDKEY, 0);
+ ::keybd_event(nVKey, 0x45, KEYEVENTF_EXTENDEDKEY|KEYEVENTF_KEYUP, 0);
+ }
+}
+
+void WinSalFrame::ResetClipRegion()
+{
+ SetWindowRgn( mhWnd, nullptr, TRUE );
+}
+
+void WinSalFrame::BeginSetClipRegion( sal_uInt32 nRects )
+{
+ if( mpClipRgnData )
+ delete [] reinterpret_cast<BYTE*>(mpClipRgnData);
+ sal_uLong nRectBufSize = sizeof(RECT)*nRects;
+ mpClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+nRectBufSize]);
+ mpClipRgnData->rdh.dwSize = sizeof( RGNDATAHEADER );
+ mpClipRgnData->rdh.iType = RDH_RECTANGLES;
+ mpClipRgnData->rdh.nCount = nRects;
+ mpClipRgnData->rdh.nRgnSize = nRectBufSize;
+ SetRectEmpty( &(mpClipRgnData->rdh.rcBound) );
+ mpNextClipRect = reinterpret_cast<RECT*>(&(mpClipRgnData->Buffer));
+ mbFirstClipRect = true;
+}
+
+void WinSalFrame::UnionClipRegion( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight )
+{
+ if( ! mpClipRgnData )
+ return;
+
+ RECT* pRect = mpNextClipRect;
+ RECT* pBoundRect = &(mpClipRgnData->rdh.rcBound);
+ tools::Long nRight = nX + nWidth;
+ tools::Long nBottom = nY + nHeight;
+
+ if ( mbFirstClipRect )
+ {
+ pBoundRect->left = nX;
+ pBoundRect->top = nY;
+ pBoundRect->right = nRight;
+ pBoundRect->bottom = nBottom;
+ mbFirstClipRect = false;
+ }
+ else
+ {
+ if ( nX < pBoundRect->left )
+ pBoundRect->left = static_cast<int>(nX);
+
+ if ( nY < pBoundRect->top )
+ pBoundRect->top = static_cast<int>(nY);
+
+ if ( nRight > pBoundRect->right )
+ pBoundRect->right = static_cast<int>(nRight);
+
+ if ( nBottom > pBoundRect->bottom )
+ pBoundRect->bottom = static_cast<int>(nBottom);
+ }
+
+ pRect->left = static_cast<int>(nX);
+ pRect->top = static_cast<int>(nY);
+ pRect->right = static_cast<int>(nRight);
+ pRect->bottom = static_cast<int>(nBottom);
+ if( (mpNextClipRect - reinterpret_cast<RECT*>(&mpClipRgnData->Buffer)) < static_cast<int>(mpClipRgnData->rdh.nCount) )
+ mpNextClipRect++;
+}
+
+void WinSalFrame::EndSetClipRegion()
+{
+ if( ! mpClipRgnData )
+ return;
+
+ HRGN hRegion;
+
+ // create region from accumulated rectangles
+ if ( mpClipRgnData->rdh.nCount == 1 )
+ {
+ RECT* pRect = &(mpClipRgnData->rdh.rcBound);
+ hRegion = CreateRectRgn( pRect->left, pRect->top,
+ pRect->right, pRect->bottom );
+ }
+ else
+ {
+ sal_uLong nSize = mpClipRgnData->rdh.nRgnSize+sizeof(RGNDATAHEADER);
+ hRegion = ExtCreateRegion( nullptr, nSize, mpClipRgnData );
+ }
+ delete [] reinterpret_cast<BYTE*>(mpClipRgnData);
+ mpClipRgnData = nullptr;
+
+ SAL_WARN_IF( !hRegion, "vcl", "WinSalFrame::EndSetClipRegion() - Can't create ClipRegion" );
+ if( hRegion )
+ {
+ RECT aWindowRect;
+ GetWindowRect( mhWnd, &aWindowRect );
+ POINT aPt;
+ aPt.x=0;
+ aPt.y=0;
+ ClientToScreen( mhWnd, &aPt );
+ OffsetRgn( hRegion, aPt.x - aWindowRect.left, aPt.y - aWindowRect.top );
+
+ if( SetWindowRgn( mhWnd, hRegion, TRUE ) == 0 )
+ DeleteObject( hRegion );
+ }
+}
+
+void WinSalFrame::UpdateDarkMode()
+{
+ ::UpdateDarkMode(mhWnd);
+}
+
+bool WinSalFrame::GetUseDarkMode() const
+{
+ return UseDarkMode();
+}
+
+bool WinSalFrame::GetUseReducedAnimation() const
+{
+ BOOL bEnableAnimation = FALSE;
+ SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &bEnableAnimation, 0);
+ return !bEnableAnimation;
+}
+
+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;
+ aMouseEvt.mnButton = 0;
+ nEvent = SalEvent::MouseMove;
+ }
+ break;
+
+ case WM_NCMOUSEMOVE:
+ case SAL_MSG_MOUSELEAVE:
+ {
+ SalData* pSalData = GetSalData();
+ if ( pSalData->mhWantLeaveMsg == hWnd )
+ {
+ // Mouse-Coordinates are relative to the screen
+ POINT aPt;
+ aPt.x = static_cast<short>(LOWORD(lParam));
+ aPt.y = static_cast<short>(HIWORD(lParam));
+ ScreenToClient(hWnd, &aPt);
+ if (const auto& pHelpWin = ImplGetSVHelpData().mpHelpWin)
+ {
+ const tools::Rectangle& rHelpRect = pHelpWin->GetHelpArea();
+ if (rHelpRect.Contains(Point(aPt.x, aPt.y)))
+ {
+ // We have entered a tooltip (help window). Don't call the handler here; it
+ // would launch the sequence "Mouse leaves the Control->Control redraws->
+ // Help window gets destroyed->Mouse enters the Control->Control redraws",
+ // which takes CPU and may flicker. Just destroy the help window and pretend
+ // we are still over the original window.
+ ImplDestroyHelpWindow(true);
+ bCall = false;
+ break;
+ }
+ }
+ pSalData->mhWantLeaveMsg = nullptr;
+ 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.width() - 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.width() - 1 - aWheelEvt.mnX;
+
+ nRet = pFrame->CallCallback( SalEvent::WheelMouse, &aWheelEvt );
+ }
+
+ ImplSalYieldMutexRelease();
+
+ return nRet;
+}
+
+static sal_uInt16 ImplSalGetKeyCode( WPARAM wParam )
+{
+ sal_uInt16 nKeyCode;
+
+ // convert KeyCode
+ if ( wParam < KEY_TAB_SIZE )
+ nKeyCode = aImplTranslateKeyTab[wParam];
+ else
+ {
+ SalData* pSalData = GetSalData();
+ std::map< UINT, sal_uInt16 >::const_iterator it = pSalData->maVKMap.find( static_cast<UINT>(wParam) );
+ if( it != pSalData->maVKMap.end() )
+ nKeyCode = it->second;
+ else
+ nKeyCode = 0;
+ }
+
+ return nKeyCode;
+}
+
+static void ImplUpdateInputLang( WinSalFrame* pFrame )
+{
+ UINT nLang = LOWORD( GetKeyboardLayout( 0 ) );
+ if ( nLang && nLang != pFrame->mnInputLang )
+ {
+ // keep input lang up-to-date
+ pFrame->mnInputLang = nLang;
+ }
+
+ // We are on Windows NT so we use Unicode FrameProcs and get
+ // Unicode charcodes directly from Windows no need to set up a
+ // code page
+ return;
+}
+
+static sal_Unicode ImplGetCharCode( WinSalFrame* pFrame, WPARAM nCharCode )
+{
+ ImplUpdateInputLang( pFrame );
+
+ // We are on Windows NT so we use Unicode FrameProcs and we
+ // get Unicode charcodes directly from Windows
+ return static_cast<sal_Unicode>(nCharCode);
+}
+
+LanguageType WinSalFrame::GetInputLanguage()
+{
+ if( !mnInputLang )
+ ImplUpdateInputLang( this );
+
+ if( !mnInputLang )
+ return LANGUAGE_DONTKNOW;
+ else
+ return LanguageType(mnInputLang);
+}
+
+bool WinSalFrame::MapUnicodeToKeyCode( sal_Unicode aUnicode, LanguageType aLangType, vcl::KeyCode& rKeyCode )
+{
+ bool bRet = false;
+ sal_IntPtr nLangType = static_cast<sal_uInt16>(aLangType);
+ // just use the passed language identifier, do not try to load additional keyboard support
+ HKL hkl = reinterpret_cast<HKL>(nLangType);
+
+ if( hkl )
+ {
+ SHORT scan = VkKeyScanExW( aUnicode, hkl );
+ if( LOWORD(scan) == 0xFFFF )
+ // keyboard not loaded or key cannot be mapped
+ bRet = false;
+ else
+ {
+ BYTE vkeycode = LOBYTE(scan);
+ BYTE shiftstate = HIBYTE(scan);
+
+ // Last argument is set to false, because there's no decision made
+ // yet which key should be assigned to MOD3 modifier on Windows.
+ // Windows key - user's can be confused, because it should display
+ // Windows menu (applies to both left/right key)
+ // Menu key - this key is used to display context menu
+ // AltGr key - probably it has no sense
+ rKeyCode = vcl::KeyCode( ImplSalGetKeyCode( vkeycode ),
+ (shiftstate & 0x01) != 0, // shift
+ (shiftstate & 0x02) != 0, // ctrl
+ (shiftstate & 0x04) != 0, // alt
+ false );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+static void UnsetAltIfAltGr(SalKeyEvent& rKeyEvt, sal_uInt16 nModCode)
+{
+ if ((nModCode & (KEY_MOD1 | KEY_MOD2)) == (KEY_MOD1 | KEY_MOD2) &&
+ rKeyEvt.mnCharCode)
+ {
+ // this is actually AltGr and should not be handled as Alt
+ rKeyEvt.mnCode &= ~(KEY_MOD1 | KEY_MOD2);
+ }
+}
+
+// tdf#152404 Commit uncommitted text before dispatching key shortcuts. In
+// certain cases such as pressing Control-Alt-C in a Writer document while
+// there is uncommitted text will call WinSalFrame::EndExtTextInput() which
+// will dispatch a SalEvent::EndExtTextInput event. Writer's handler for that
+// event will delete the uncommitted text and then insert the committed text
+// but LibreOffice will crash when deleting the uncommitted text because
+// deletion of the text also removes and deletes the newly inserted comment.
+static void FlushIMBeforeShortCut(WinSalFrame* pFrame, SalEvent nEvent, sal_uInt16 nModCode)
+{
+ if (pFrame->mbCandidateMode && nEvent == SalEvent::KeyInput
+ && (nModCode & (KEY_MOD1 | KEY_MOD2)))
+ {
+ pFrame->EndExtTextInput(EndExtTextInputFlags::Complete);
+ }
+}
+
+static bool ImplHandleKeyMsg( HWND hWnd, UINT nMsg,
+ WPARAM wParam, LPARAM lParam, LRESULT& rResult )
+{
+ static bool bIgnoreCharMsg = false;
+ static WPARAM nDeadChar = 0;
+ static WPARAM nLastVKChar = 0;
+ static sal_uInt16 nLastChar = 0;
+ static ModKeyFlags nLastModKeyCode = ModKeyFlags::NONE;
+ static bool bWaitForModKeyRelease = false;
+ sal_uInt16 nRepeat = LOWORD( lParam )-1;
+ sal_uInt16 nModCode = 0;
+
+ // this key might have been relayed by SysChild and thus
+ // may not be processed twice
+ GetSalData()->mnSalObjWantKeyEvt = 0;
+
+ if ( nMsg == WM_DEADCHAR )
+ {
+ nDeadChar = wParam;
+ return false;
+ }
+
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( !pFrame )
+ return false;
+
+ // reset the background mode for each text input,
+ // as some tools such as RichWin may have changed it
+ if ( pFrame->mpLocalGraphics &&
+ pFrame->mpLocalGraphics->getHDC() )
+ SetBkMode( pFrame->mpLocalGraphics->getHDC(), TRANSPARENT );
+
+ // determine modifiers
+ if ( GetKeyState( VK_SHIFT ) & 0x8000 )
+ nModCode |= KEY_SHIFT;
+ if ( GetKeyState( VK_CONTROL ) & 0x8000 )
+ nModCode |= KEY_MOD1;
+ if (GetKeyState(VK_MENU) & 0x8000)
+ nModCode |= KEY_MOD2;
+
+ if ( (nMsg == WM_CHAR) || (nMsg == WM_SYSCHAR) )
+ {
+ nDeadChar = 0;
+
+ if ( bIgnoreCharMsg )
+ {
+ bIgnoreCharMsg = false;
+ // #101635# if zero is returned here for WM_SYSCHAR (ALT+<key>) Windows will beep
+ // because this 'hotkey' was not processed -> better return 1
+ // except for Alt-SPACE which should always open the sysmenu (#104616#)
+
+ // also return zero if a system menubar is available that might process this hotkey
+ // this also applies to the OLE inplace embedding where we are a child window
+ if( (GetWindowStyle( hWnd ) & WS_CHILD) || GetMenu( hWnd ) || (wParam == 0x20) )
+ return false;
+ else
+ return true;
+ }
+
+ // ignore backspace as a single key, so that
+ // we do not get problems for combinations w/ a DeadKey
+ if ( wParam == 0x08 ) // BACKSPACE
+ return false;
+
+ // only "free flying" WM_CHAR messages arrive here, that are
+ // created by typing an ALT-NUMPAD combination
+ SalKeyEvent aKeyEvt;
+
+ if ( (wParam >= '0') && (wParam <= '9') )
+ aKeyEvt.mnCode = sal::static_int_cast<sal_uInt16>(KEYGROUP_NUM + wParam - '0');
+ else if ( (wParam >= 'A') && (wParam <= 'Z') )
+ aKeyEvt.mnCode = sal::static_int_cast<sal_uInt16>(KEYGROUP_ALPHA + wParam - 'A');
+ else if ( (wParam >= 'a') && (wParam <= 'z') )
+ aKeyEvt.mnCode = sal::static_int_cast<sal_uInt16>(KEYGROUP_ALPHA + wParam - 'a');
+ else if ( wParam == 0x0D ) // RETURN
+ aKeyEvt.mnCode = KEY_RETURN;
+ else if ( wParam == 0x1B ) // ESCAPE
+ aKeyEvt.mnCode = KEY_ESCAPE;
+ else if ( wParam == 0x09 ) // TAB
+ aKeyEvt.mnCode = KEY_TAB;
+ else if ( wParam == 0x20 ) // SPACE
+ aKeyEvt.mnCode = KEY_SPACE;
+ else
+ aKeyEvt.mnCode = 0;
+
+ aKeyEvt.mnCode |= nModCode;
+ aKeyEvt.mnCharCode = ImplGetCharCode( pFrame, wParam );
+ aKeyEvt.mnRepeat = nRepeat;
+
+ UnsetAltIfAltGr(aKeyEvt, nModCode);
+ FlushIMBeforeShortCut(pFrame, SalEvent::KeyInput, nModCode);
+
+ nLastChar = 0;
+ nLastVKChar = 0;
+
+ bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
+ pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
+ return nRet;
+ }
+ // #i11583#, MCD, 2003-01-13, Support for WM_UNICHAR & Keyman 6.0; addition begins
+ else if( nMsg == WM_UNICHAR )
+ {
+ // If Windows is asking if we accept WM_UNICHAR, return TRUE
+ if(wParam == UNICODE_NOCHAR)
+ {
+ rResult = TRUE; // ssa: this will actually return TRUE to windows
+ return true; // ...but this will only avoid calling the defwindowproc
+ }
+
+ if (!rtl::isUnicodeCodePoint(wParam))
+ return false;
+
+ SalKeyEvent aKeyEvt;
+ aKeyEvt.mnCode = nModCode; // Or should it be 0? - as this is always a character returned
+ aKeyEvt.mnRepeat = 0;
+
+ if( wParam >= Uni_SupplementaryPlanesStart )
+ {
+ // character is supplementary char in UTF-32 format - must be converted to UTF-16 supplementary pair
+ aKeyEvt.mnCharCode = rtl::getHighSurrogate(wParam);
+ nLastChar = 0;
+ nLastVKChar = 0;
+ pFrame->CallCallback(SalEvent::KeyInput, &aKeyEvt);
+ pFrame->CallCallback(SalEvent::KeyUp, &aKeyEvt);
+ wParam = rtl::getLowSurrogate(wParam);
+ }
+
+ aKeyEvt.mnCharCode = static_cast<sal_Unicode>(wParam);
+
+ nLastChar = 0;
+ nLastVKChar = 0;
+ bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
+ pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
+
+ return nRet;
+ }
+ // MCD, 2003-01-13, Support for WM_UNICHAR & Keyman 6.0; addition ends
+ else
+ {
+ // for shift, control and menu we issue a KeyModChange event
+ if ( (wParam == VK_SHIFT) || (wParam == VK_CONTROL) || (wParam == VK_MENU) )
+ {
+ SalKeyModEvent aModEvt;
+ aModEvt.mbDown = false; // auto-accelerator feature not supported here.
+ aModEvt.mnCode = nModCode;
+ aModEvt.mnModKeyCode = ModKeyFlags::NONE; // no command events will be sent if this member is 0
+
+ ModKeyFlags tmpCode = ModKeyFlags::NONE;
+ if( GetKeyState( VK_LSHIFT ) & 0x8000 )
+ tmpCode |= ModKeyFlags::LeftShift;
+ if( GetKeyState( VK_RSHIFT ) & 0x8000 )
+ tmpCode |= ModKeyFlags::RightShift;
+ if( GetKeyState( VK_LCONTROL ) & 0x8000 )
+ tmpCode |= ModKeyFlags::LeftMod1;
+ if( GetKeyState( VK_RCONTROL ) & 0x8000 )
+ tmpCode |= ModKeyFlags::RightMod1;
+ if( GetKeyState( VK_LMENU ) & 0x8000 )
+ tmpCode |= ModKeyFlags::LeftMod2;
+ if( GetKeyState( VK_RMENU ) & 0x8000 )
+ tmpCode |= ModKeyFlags::RightMod2;
+
+ if( tmpCode < nLastModKeyCode )
+ {
+ aModEvt.mnModKeyCode = nLastModKeyCode;
+ nLastModKeyCode = ModKeyFlags::NONE;
+ bWaitForModKeyRelease = true;
+ }
+ else
+ {
+ if( !bWaitForModKeyRelease )
+ nLastModKeyCode = tmpCode;
+ }
+
+ if( tmpCode == ModKeyFlags::NONE )
+ bWaitForModKeyRelease = false;
+
+ return pFrame->CallCallback( SalEvent::KeyModChange, &aModEvt );
+ }
+ else
+ {
+ SalKeyEvent aKeyEvt;
+ SalEvent nEvent;
+ MSG aCharMsg;
+ bool bCharPeek = false;
+ UINT nCharMsg = WM_CHAR;
+ bool bKeyUp = (nMsg == WM_KEYUP) || (nMsg == WM_SYSKEYUP);
+
+ nLastModKeyCode = ModKeyFlags::NONE; // make sure no modkey messages are sent if they belong to a hotkey (see above)
+ aKeyEvt.mnCharCode = 0;
+ aKeyEvt.mnCode = ImplSalGetKeyCode( wParam );
+ if ( !bKeyUp )
+ {
+ // check for charcode
+ // Get the related WM_CHAR message using PeekMessage, if available.
+ // The WM_CHAR message is always at the beginning of the
+ // message queue. Also it is made certain that there is always only
+ // one WM_CHAR message in the queue.
+ bCharPeek = PeekMessageW( &aCharMsg, hWnd,
+ WM_CHAR, WM_CHAR, PM_NOREMOVE | PM_NOYIELD );
+ if ( bCharPeek && (nDeadChar == aCharMsg.wParam) )
+ {
+ bCharPeek = false;
+ nDeadChar = 0;
+
+ if ( wParam == VK_BACK )
+ {
+ PeekMessageW( &aCharMsg, hWnd,
+ nCharMsg, nCharMsg, PM_REMOVE | PM_NOYIELD );
+ return false;
+ }
+ }
+ else
+ {
+ if ( !bCharPeek )
+ {
+ bCharPeek = PeekMessageW( &aCharMsg, hWnd,
+ WM_SYSCHAR, WM_SYSCHAR, PM_NOREMOVE | PM_NOYIELD );
+ nCharMsg = WM_SYSCHAR;
+ }
+ }
+ if ( bCharPeek )
+ aKeyEvt.mnCharCode = ImplGetCharCode( pFrame, aCharMsg.wParam );
+ else
+ aKeyEvt.mnCharCode = 0;
+
+ nLastChar = aKeyEvt.mnCharCode;
+ nLastVKChar = wParam;
+ }
+ else
+ {
+ if ( wParam == nLastVKChar )
+ {
+ aKeyEvt.mnCharCode = nLastChar;
+ nLastChar = 0;
+ nLastVKChar = 0;
+ }
+ }
+
+ if ( aKeyEvt.mnCode || aKeyEvt.mnCharCode )
+ {
+ if ( bKeyUp )
+ nEvent = SalEvent::KeyUp;
+ else
+ nEvent = SalEvent::KeyInput;
+
+ aKeyEvt.mnCode |= nModCode;
+ aKeyEvt.mnRepeat = nRepeat;
+
+ UnsetAltIfAltGr(aKeyEvt, nModCode);
+ FlushIMBeforeShortCut(pFrame, nEvent, nModCode);
+
+ bIgnoreCharMsg = bCharPeek;
+ bool nRet = pFrame->CallCallback( nEvent, &aKeyEvt );
+ // independent part only reacts on keyup but Windows does not send
+ // keyup for VK_HANJA
+ if( aKeyEvt.mnCode == KEY_HANGUL_HANJA )
+ nRet = pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
+
+ bIgnoreCharMsg = false;
+
+ // char-message, then remove or ignore
+ if ( bCharPeek )
+ {
+ nDeadChar = 0;
+ if ( nRet )
+ {
+ PeekMessageW( &aCharMsg, hWnd,
+ nCharMsg, nCharMsg, PM_REMOVE | PM_NOYIELD );
+ }
+ else
+ bIgnoreCharMsg = true;
+ }
+
+ return nRet;
+ }
+ else
+ return false;
+ }
+ }
+}
+
+bool ImplHandleSalObjKeyMsg( HWND hWnd, UINT nMsg,
+ WPARAM wParam, LPARAM lParam )
+{
+ if ( (nMsg == WM_KEYDOWN) || (nMsg == WM_KEYUP) )
+ {
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( !pFrame )
+ return false;
+
+ sal_uInt16 nRepeat = LOWORD( lParam )-1;
+ sal_uInt16 nModCode = 0;
+
+ // determine modifiers
+ if ( GetKeyState( VK_SHIFT ) & 0x8000 )
+ nModCode |= KEY_SHIFT;
+ if ( GetKeyState( VK_CONTROL ) & 0x8000 )
+ nModCode |= KEY_MOD1;
+ if ( GetKeyState( VK_MENU ) & 0x8000 )
+ nModCode |= KEY_MOD2;
+
+ if ( (wParam != VK_SHIFT) && (wParam != VK_CONTROL) && (wParam != VK_MENU) )
+ {
+ SalKeyEvent aKeyEvt;
+ SalEvent nEvent;
+
+ // convert KeyCode
+ aKeyEvt.mnCode = ImplSalGetKeyCode( wParam );
+ aKeyEvt.mnCharCode = 0;
+
+ if ( aKeyEvt.mnCode )
+ {
+ if (nMsg == WM_KEYUP)
+ nEvent = SalEvent::KeyUp;
+ else
+ nEvent = SalEvent::KeyInput;
+
+ aKeyEvt.mnCode |= nModCode;
+ aKeyEvt.mnRepeat = nRepeat;
+ bool nRet = pFrame->CallCallback( nEvent, &aKeyEvt );
+ return nRet;
+ }
+ else
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool ImplHandleSalObjSysCharMsg( HWND hWnd, WPARAM wParam, LPARAM lParam )
+{
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( !pFrame )
+ return false;
+
+ sal_uInt16 nRepeat = LOWORD( lParam )-1;
+ sal_uInt16 nModCode = 0;
+ sal_uInt16 cKeyCode = static_cast<sal_uInt16>(wParam);
+
+ // determine modifiers
+ if ( GetKeyState( VK_SHIFT ) & 0x8000 )
+ nModCode |= KEY_SHIFT;
+ if ( GetKeyState( VK_CONTROL ) & 0x8000 )
+ nModCode |= KEY_MOD1;
+ nModCode |= KEY_MOD2;
+
+ // assemble KeyEvent
+ SalKeyEvent aKeyEvt;
+ if ( (cKeyCode >= 48) && (cKeyCode <= 57) )
+ aKeyEvt.mnCode = KEY_0+(cKeyCode-48);
+ else if ( (cKeyCode >= 65) && (cKeyCode <= 90) )
+ aKeyEvt.mnCode = KEY_A+(cKeyCode-65);
+ else if ( (cKeyCode >= 97) && (cKeyCode <= 122) )
+ aKeyEvt.mnCode = KEY_A+(cKeyCode-97);
+ else
+ aKeyEvt.mnCode = 0;
+ aKeyEvt.mnCode |= nModCode;
+ aKeyEvt.mnCharCode = ImplGetCharCode( pFrame, cKeyCode );
+ aKeyEvt.mnRepeat = nRepeat;
+ bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
+ pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
+ return nRet;
+}
+
+namespace {
+
+enum class DeferPolicy
+{
+ Blocked,
+ Allowed
+};
+
+}
+
+// Remember to release the solar mutex on success!
+static WinSalFrame* ProcessOrDeferMessage( HWND hWnd, INT nMsg, WPARAM pWParam = 0,
+ DeferPolicy eCanDefer = DeferPolicy::Allowed )
+{
+ bool bFailedCondition = false, bGotMutex = false;
+ WinSalFrame* pFrame = nullptr;
+
+ if ( DeferPolicy::Blocked == eCanDefer )
+ assert( (DeferPolicy::Blocked == eCanDefer) && (nMsg == 0) && (pWParam == 0) );
+ else
+ assert( (DeferPolicy::Allowed == eCanDefer) && (nMsg != 0) );
+
+ if ( DeferPolicy::Blocked == eCanDefer )
+ {
+ ImplSalYieldMutexAcquireWithWait();
+ bGotMutex = true;
+ }
+ else if ( !(bGotMutex = ImplSalYieldMutexTryToAcquire()) )
+ bFailedCondition = true;
+
+ if ( !bFailedCondition )
+ {
+ pFrame = GetWindowPtr( hWnd );
+ bFailedCondition = pFrame == nullptr;
+ }
+
+ if ( bFailedCondition )
+ {
+ if ( bGotMutex )
+ ImplSalYieldMutexRelease();
+ if ( DeferPolicy::Allowed == eCanDefer )
+ {
+ bool const ret = PostMessageW(hWnd, nMsg, pWParam, 0);
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ }
+
+ return pFrame;
+}
+
+namespace {
+
+enum class PostedState
+{
+ IsPosted,
+ IsInitial
+};
+
+}
+
+static bool ImplHandlePostPaintMsg( HWND hWnd, RECT* pRect,
+ PostedState eProcessed = PostedState::IsPosted )
+{
+ RECT* pMsgRect;
+ if ( PostedState::IsInitial == eProcessed )
+ {
+ pMsgRect = new RECT;
+ CopyRect( pMsgRect, pRect );
+ }
+ else
+ pMsgRect = pRect;
+
+ WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_POSTPAINT,
+ reinterpret_cast<WPARAM>(pMsgRect) );
+ if ( pFrame )
+ {
+ SalPaintEvent aPEvt( pRect->left, pRect->top, pRect->right-pRect->left, pRect->bottom-pRect->top );
+ pFrame->CallCallback( SalEvent::Paint, &aPEvt );
+ ImplSalYieldMutexRelease();
+ if ( PostedState::IsPosted == eProcessed )
+ delete pRect;
+ }
+
+ return (pFrame != nullptr);
+}
+
+static bool ImplHandlePaintMsg( HWND hWnd )
+{
+ bool bPaintSuccessful = false;
+
+ // even without the Yield mutex, we can still change the clip region,
+ // because other threads don't use the Yield mutex
+ // --> see AcquireGraphics()
+
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( pFrame )
+ {
+ // clip region must be set, as we don't get a proper
+ // bounding rectangle otherwise
+ WinSalGraphics *pGraphics = pFrame->mpLocalGraphics;
+ bool bHasClipRegion = pGraphics &&
+ pGraphics->getHDC() && pGraphics->getRegion();
+ if ( bHasClipRegion )
+ SelectClipRgn( pGraphics->getHDC(), nullptr );
+
+ // according to Windows documentation one shall check first if
+ // there really is a paint-region
+ RECT aUpdateRect;
+ PAINTSTRUCT aPs;
+ bool bHasPaintRegion = GetUpdateRect( hWnd, nullptr, FALSE );
+ if ( bHasPaintRegion )
+ {
+ // call BeginPaint/EndPaint to query the paint rect and use
+ // this information in the (deferred) paint
+ BeginPaint( hWnd, &aPs );
+ CopyRect( &aUpdateRect, &aPs.rcPaint );
+ }
+
+ // reset clip region
+ if ( bHasClipRegion )
+ SelectClipRgn( pGraphics->getHDC(), pGraphics->getRegion() );
+
+ // try painting
+ if ( bHasPaintRegion )
+ {
+ bPaintSuccessful = ImplHandlePostPaintMsg(
+ hWnd, &aUpdateRect, PostedState::IsInitial );
+ EndPaint( hWnd, &aPs );
+ }
+ else // if there is nothing to paint, the paint is successful
+ bPaintSuccessful = true;
+ }
+
+ return bPaintSuccessful;
+}
+
+static void SetMaximizedFrameGeometry( HWND hWnd, WinSalFrame* pFrame, RECT* pParentRect )
+{
+ // calculate and set frame geometry of a maximized window - useful if the window is still hidden
+
+ // dualmonitor support:
+ // Get screensize of the monitor with the mouse pointer
+
+ RECT aRectMouse;
+ if( ! pParentRect )
+ {
+ POINT pt;
+ GetCursorPos( &pt );
+ aRectMouse.left = pt.x;
+ aRectMouse.top = pt.y;
+ aRectMouse.right = pt.x+2;
+ aRectMouse.bottom = pt.y+2;
+ pParentRect = &aRectMouse;
+ }
+
+ RECT aRect;
+ ImplSalGetWorkArea( hWnd, &aRect, pParentRect );
+
+ // a maximized window has no other borders than the caption
+ pFrame->maGeometry.setDecorations(0, pFrame->mbCaption ? GetSystemMetrics(SM_CYCAPTION) : 0, 0, 0);
+
+ aRect.top += pFrame->maGeometry.topDecoration();
+ pFrame->maGeometry.setPos({ aRect.left, aRect.top });
+ SetGeometrySize(pFrame->maGeometry, { aRect.right - aRect.left, aRect.bottom - aRect.top });
+}
+
+static void UpdateFrameGeometry(WinSalFrame* pFrame)
+{
+ if( !pFrame )
+ return;
+ const HWND hWnd = pFrame->mhWnd;
+
+ RECT aRect;
+ GetWindowRect( hWnd, &aRect );
+ pFrame->maGeometry.setPosSize({ 0, 0 }, { 0, 0 });
+ pFrame->maGeometry.setDecorations(0, 0, 0, 0);
+ pFrame->maGeometry.setScreen(0);
+
+ if ( IsIconic( hWnd ) )
+ return;
+
+ POINT aPt;
+ aPt.x=0;
+ aPt.y=0;
+ ClientToScreen(hWnd, &aPt);
+ int cx = aPt.x - aRect.left;
+
+ pFrame->maGeometry.setDecorations(cx, aPt.y - aRect.top, cx, 0);
+ pFrame->maGeometry.setPos({ aPt.x, 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.setRightDecoration(aRect.right - aPt.x);
+ }
+ if( aInnerRect.bottom ) // may be zero if window was not shown yet
+ pFrame->maGeometry.setBottomDecoration(aRect.bottom - aPt.y - aInnerRect.bottom);
+ else
+ // bottom border is typically the same as left/right
+ pFrame->maGeometry.setBottomDecoration(pFrame->maGeometry.leftDecoration());
+
+ int nWidth = aRect.right - aRect.left
+ - pFrame->maGeometry.rightDecoration() - pFrame->maGeometry.leftDecoration();
+ int nHeight = aRect.bottom - aRect.top
+ - pFrame->maGeometry.bottomDecoration() - pFrame->maGeometry.topDecoration();
+ SetGeometrySize(pFrame->maGeometry, { nWidth, nHeight });
+ pFrame->updateScreenNumber();
+}
+
+static void ImplCallClosePopupsHdl( HWND hWnd )
+{
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( pFrame )
+ {
+ pFrame->CallCallback( SalEvent::ClosePopups, nullptr );
+ }
+}
+
+static void ImplCallMoveHdl(HWND hWnd)
+{
+ WinSalFrame* pFrame = ProcessOrDeferMessage(hWnd, SAL_MSG_POSTMOVE);
+ if (!pFrame)
+ return;
+
+ pFrame->CallCallback(SalEvent::Move, nullptr);
+
+ ImplSalYieldMutexRelease();
+}
+
+static void ImplHandleMoveMsg(HWND hWnd, LPARAM lParam)
+{
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if (!pFrame)
+ return;
+
+ UpdateFrameGeometry(pFrame);
+
+#ifdef NDEBUG
+ (void) lParam;
+#endif
+ assert(IsIconic(hWnd) || (pFrame->maGeometry.x() == static_cast<sal_Int16>(LOWORD(lParam))));
+ assert(IsIconic(hWnd) || (pFrame->maGeometry.y() == static_cast<sal_Int16>(HIWORD(lParam))));
+
+ if (GetWindowStyle(hWnd) & WS_VISIBLE)
+ pFrame->mbDefPos = false;
+
+ // protect against recursion
+ if (!pFrame->mbInMoveMsg)
+ {
+ // adjust window again for FullScreenMode
+ pFrame->mbInMoveMsg = true;
+ if (pFrame->isFullScreen())
+ ImplSalFrameFullScreenPos(pFrame);
+ pFrame->mbInMoveMsg = false;
+ }
+
+ pFrame->UpdateFrameState();
+
+ ImplCallMoveHdl(hWnd);
+}
+
+static void ImplCallSizeHdl( HWND hWnd )
+{
+ WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, SAL_MSG_POSTCALLSIZE );
+ if (!pFrame)
+ return;
+
+ 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))
+ return;
+
+ WinSalFrame* pFrame = GetWindowPtr(hWnd);
+ if (!pFrame)
+ return;
+
+ UpdateFrameGeometry(pFrame);
+
+#ifdef NDEBUG
+ (void) lParam;
+#endif
+ assert(pFrame->maGeometry.width() == static_cast<sal_Int16>(LOWORD(lParam)));
+ assert(pFrame->maGeometry.height() == static_cast<sal_Int16>(HIWORD(lParam)));
+
+ pFrame->UpdateFrameState();
+
+ 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)
+ return;
+ const ::comphelper::ScopeGuard aScopeGuard([](){ ImplSalYieldMutexRelease(); });
+
+ if (WinSalFrame::mbInReparent)
+ return;
+
+ const 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);
+}
+
+static void ImplHandleCloseMsg( HWND hWnd )
+{
+ WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, WM_CLOSE );
+ if ( pFrame )
+ {
+ pFrame->CallCallback( SalEvent::Close, nullptr );
+ ImplSalYieldMutexRelease();
+ }
+}
+
+static bool ImplHandleShutDownMsg( HWND hWnd )
+{
+ bool nRet = false;
+ WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, 0, 0, DeferPolicy::Blocked );
+ if ( pFrame )
+ {
+ nRet = pFrame->CallCallback( SalEvent::Shutdown, nullptr );
+ ImplSalYieldMutexRelease();
+ }
+ return nRet;
+}
+
+static void ImplHandleSettingsChangeMsg( HWND hWnd, UINT nMsg,
+ WPARAM wParam, LPARAM lParam )
+{
+ SalEvent nSalEvent = SalEvent::SettingsChanged;
+
+ if ( nMsg == WM_DEVMODECHANGE )
+ nSalEvent = SalEvent::PrinterChanged;
+ else if ( nMsg == WM_DISPLAYCHANGE )
+ nSalEvent = SalEvent::DisplayChanged;
+ else if ( nMsg == WM_FONTCHANGE )
+ nSalEvent = SalEvent::FontChanged;
+ else if ( nMsg == WM_WININICHANGE )
+ {
+ if ( lParam )
+ {
+ if ( ImplSalWICompareAscii( reinterpret_cast<const wchar_t*>(lParam), "devices" ) == 0 )
+ nSalEvent = SalEvent::PrinterChanged;
+ }
+ }
+
+ if ( nMsg == WM_SETTINGCHANGE )
+ {
+ if ( wParam == SPI_SETWHEELSCROLLLINES )
+ aSalShlData.mnWheelScrollLines = ImplSalGetWheelScrollLines();
+ else if( wParam == SPI_SETWHEELSCROLLCHARS )
+ aSalShlData.mnWheelScrollChars = ImplSalGetWheelScrollChars();
+ UpdateDarkMode(hWnd);
+ GetSalData()->mbThemeChanged = true;
+ }
+
+ if ( WM_SYSCOLORCHANGE == nMsg && GetSalData()->mhDitherPal )
+ ImplUpdateSysColorEntries();
+
+ WinSalFrame* pFrame = ProcessOrDeferMessage( hWnd, 0, 0, DeferPolicy::Blocked );
+ if (!pFrame)
+ return;
+
+ if (((nMsg == WM_DISPLAYCHANGE) || (nMsg == WM_WININICHANGE)) && pFrame->isFullScreen())
+ 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)
+ return;
+
+ WinSalFrame* pFrame = ProcessOrDeferMessage(hWnd, SAL_MSG_FORCEPALETTE);
+ if (!pFrame)
+ return;
+ const ::comphelper::ScopeGuard aScopeGuard([](){ ImplSalYieldMutexRelease(); });
+
+ WinSalGraphics* pGraphics = pFrame->mpLocalGraphics;
+ if (!pGraphics || !pGraphics->getHDC() || !pGraphics->getDefPal()
+ || (pGraphics->setPalette(hPal, FALSE) == GDI_ERROR))
+ return;
+
+ InvalidateRect(hWnd, nullptr, FALSE);
+ UpdateWindow(hWnd);
+ pFrame->CallCallback(SalEvent::DisplayChanged, nullptr);
+}
+
+static LRESULT ImplHandlePalette( bool bFrame, HWND hWnd, UINT nMsg,
+ WPARAM wParam, LPARAM lParam, bool& rDef )
+{
+ SalData* pSalData = GetSalData();
+ HPALETTE hPal = pSalData->mhDitherPal;
+ if ( !hPal )
+ return 0;
+
+ rDef = false;
+ if ( pSalData->mbInPalChange )
+ return 0;
+
+ if ( (nMsg == WM_PALETTECHANGED) || (nMsg == SAL_MSG_POSTPALCHANGED) )
+ {
+ if ( reinterpret_cast<HWND>(wParam) == hWnd )
+ return 0;
+ }
+
+ bool bReleaseMutex = false;
+ if ( (nMsg == WM_QUERYNEWPALETTE) || (nMsg == WM_PALETTECHANGED) )
+ {
+ // as Windows can send these messages also, we have to use
+ // the Solar semaphore
+ if ( ImplSalYieldMutexTryToAcquire() )
+ bReleaseMutex = true;
+ else if ( nMsg == WM_QUERYNEWPALETTE )
+ {
+ bool const ret = PostMessageW(hWnd, SAL_MSG_POSTQUERYNEWPAL, wParam, lParam);
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ else /* ( nMsg == WM_PALETTECHANGED ) */
+ {
+ bool const ret = PostMessageW(hWnd, SAL_MSG_POSTPALCHANGED, wParam, lParam);
+ SAL_WARN_IF(!ret, "vcl", "ERROR: PostMessage() failed!");
+ }
+ }
+
+ WinSalVirtualDevice*pTempVD;
+ WinSalFrame* pTempFrame;
+ WinSalGraphics* pGraphics;
+ HDC hDC;
+ HPALETTE hOldPal = nullptr;
+ UINT nCols = GDI_ERROR;
+ bool bUpdate;
+
+ pSalData->mbInPalChange = true;
+
+ // reset all palettes in VirDevs and Frames
+ pTempVD = pSalData->mpFirstVD;
+ while ( pTempVD )
+ {
+ pGraphics = pTempVD->getGraphics();
+ pGraphics->setPalette(nullptr);
+ pTempVD = pTempVD->getNext();
+ }
+ pTempFrame = pSalData->mpFirstFrame;
+ while ( pTempFrame )
+ {
+ pGraphics = pTempFrame->mpLocalGraphics;
+ pGraphics->setPalette(nullptr);
+ pTempFrame = pTempFrame->mpNextFrame;
+ }
+
+ // re-initialize palette
+ WinSalFrame* pFrame = nullptr;
+ if ( bFrame )
+ pFrame = GetWindowPtr( hWnd );
+
+ UnrealizeObject(hPal);
+ const bool bStdDC = pFrame && pFrame->mpLocalGraphics && pFrame->mpLocalGraphics->getHDC();
+ if (!bStdDC)
+ {
+ hDC = GetDC(hWnd);
+ hOldPal = SelectPalette(hDC, hPal, TRUE);
+ if (hOldPal)
+ nCols = RealizePalette(hDC);
+ }
+ else
+ {
+ hDC = pFrame->mpLocalGraphics->getHDC();
+ nCols = pFrame->mpLocalGraphics->setPalette(hPal);
+ }
+
+ bUpdate = nCols != 0 && nCols != GDI_ERROR;
+
+ if ( !bStdDC )
+ {
+ if (hOldPal)
+ SelectPalette(hDC, hOldPal, TRUE);
+ ReleaseDC( hWnd, hDC );
+ }
+
+ // reset all palettes in VirDevs and Frames
+ pTempVD = pSalData->mpFirstVD;
+ while ( pTempVD )
+ {
+ pGraphics = pTempVD->getGraphics();
+ if ( pGraphics->getDefPal() )
+ pGraphics->setPalette(hPal);
+ pTempVD = pTempVD->getNext();
+ }
+
+ pTempFrame = pSalData->mpFirstFrame;
+ while ( pTempFrame )
+ {
+ if ( pTempFrame != pFrame )
+ {
+ pGraphics = pTempFrame->mpLocalGraphics;
+ if (pGraphics && pGraphics->getDefPal())
+ {
+ UINT nRes = pGraphics->setPalette(hPal);
+ if (nRes != 0 && nRes != GDI_ERROR)
+ bUpdate = true;
+ }
+ }
+ pTempFrame = pTempFrame->mpNextFrame;
+ }
+
+ // if colors changed, update the window
+ if ( bUpdate )
+ {
+ pTempFrame = pSalData->mpFirstFrame;
+ while ( pTempFrame )
+ {
+ pGraphics = pTempFrame->mpLocalGraphics;
+ if (pGraphics && pGraphics->getDefPal())
+ {
+ InvalidateRect( pTempFrame->mhWnd, nullptr, FALSE );
+ UpdateWindow( pTempFrame->mhWnd );
+ pTempFrame->CallCallback( SalEvent::DisplayChanged, nullptr );
+ }
+ pTempFrame = pTempFrame->mpNextFrame;
+ }
+ }
+
+ pSalData->mbInPalChange = false;
+
+ if ( bReleaseMutex )
+ ImplSalYieldMutexRelease();
+
+ if ( nMsg == WM_PALETTECHANGED )
+ return 0;
+ else
+ return nCols;
+}
+
+static bool ImplHandleMinMax( HWND hWnd, LPARAM lParam )
+{
+ bool bRet = false;
+
+ if ( ImplSalYieldMutexTryToAcquire() )
+ {
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( pFrame )
+ {
+ MINMAXINFO* pMinMax = reinterpret_cast<MINMAXINFO*>(lParam);
+
+ if (pFrame->isFullScreen())
+ {
+ int nX;
+ int nY;
+ int nDX;
+ int nDY;
+ ImplSalCalcFullScreenSize( pFrame, nX, nY, nDX, nDY );
+
+ if ( pMinMax->ptMaxPosition.x > nX )
+ pMinMax->ptMaxPosition.x = nX;
+ if ( pMinMax->ptMaxPosition.y > nY )
+ pMinMax->ptMaxPosition.y = nY;
+
+ if ( pMinMax->ptMaxSize.x < nDX )
+ pMinMax->ptMaxSize.x = nDX;
+ if ( pMinMax->ptMaxSize.y < nDY )
+ pMinMax->ptMaxSize.y = nDY;
+ if ( pMinMax->ptMaxTrackSize.x < nDX )
+ pMinMax->ptMaxTrackSize.x = nDX;
+ if ( pMinMax->ptMaxTrackSize.y < nDY )
+ pMinMax->ptMaxTrackSize.y = nDY;
+
+ pMinMax->ptMinTrackSize.x = nDX;
+ pMinMax->ptMinTrackSize.y = nDY;
+
+ bRet = true;
+ }
+
+ if ( pFrame->mnMinWidth || pFrame->mnMinHeight )
+ {
+ int nWidth = pFrame->mnMinWidth;
+ int nHeight = pFrame->mnMinHeight;
+
+ ImplSalAddBorder( pFrame, nWidth, nHeight );
+
+ if ( pMinMax->ptMinTrackSize.x < nWidth )
+ pMinMax->ptMinTrackSize.x = nWidth;
+ if ( pMinMax->ptMinTrackSize.y < nHeight )
+ pMinMax->ptMinTrackSize.y = nHeight;
+ }
+
+ if ( pFrame->mnMaxWidth || pFrame->mnMaxHeight )
+ {
+ int nWidth = pFrame->mnMaxWidth;
+ int nHeight = pFrame->mnMaxHeight;
+
+ ImplSalAddBorder( pFrame, nWidth, nHeight );
+
+ if( nWidth > 0 && nHeight > 0 ) // protect against int overflow due to INT_MAX initialisation
+ {
+ if ( pMinMax->ptMaxTrackSize.x > nWidth )
+ pMinMax->ptMaxTrackSize.x = nWidth;
+ if ( pMinMax->ptMaxTrackSize.y > nHeight )
+ pMinMax->ptMaxTrackSize.y = nHeight;
+ }
+ }
+ }
+
+ ImplSalYieldMutexRelease();
+ }
+
+ return bRet;
+}
+
+// retrieves the SalMenuItem pointer from a hMenu
+// the pointer is stored in every item, so if no position
+// is specified we just use the first item (ie, pos=0)
+// if bByPosition is false then nPos denotes a menu id instead of a position
+static WinSalMenuItem* ImplGetSalMenuItem( HMENU hMenu, UINT nPos, bool bByPosition=true )
+{
+ MENUITEMINFOW mi = {};
+ mi.cbSize = sizeof( mi );
+ mi.fMask = MIIM_DATA;
+ if( !GetMenuItemInfoW( hMenu, nPos, bByPosition, &mi) )
+ SAL_WARN("vcl", "GetMenuItemInfoW failed: " << WindowsErrorString(GetLastError()));
+
+ return reinterpret_cast<WinSalMenuItem *>(mi.dwItemData);
+}
+
+// returns the index of the currently selected item if any or -1
+static int ImplGetSelectedIndex( HMENU hMenu )
+{
+ MENUITEMINFOW mi = {};
+ mi.cbSize = sizeof( mi );
+ mi.fMask = MIIM_STATE;
+ int n = GetMenuItemCount( hMenu );
+ if( n != -1 )
+ {
+ for(int i=0; i<n; i++ )
+ {
+ if( !GetMenuItemInfoW( hMenu, i, TRUE, &mi) )
+ SAL_WARN( "vcl", "GetMenuItemInfoW failed: " << WindowsErrorString( GetLastError() ) );
+ else
+ {
+ if( mi.fState & MFS_HILITE )
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+static LRESULT ImplMenuChar( HWND, WPARAM wParam, LPARAM lParam )
+{
+ LRESULT nRet = MNC_IGNORE;
+ HMENU hMenu = reinterpret_cast<HMENU>(lParam);
+ OUString aMnemonic( "&" + OUStringChar(static_cast<sal_Unicode>(LOWORD(wParam))) );
+ aMnemonic = aMnemonic.toAsciiLowerCase(); // we only have ascii mnemonics
+
+ // search the mnemonic in the current menu
+ int nItemCount = GetMenuItemCount( hMenu );
+ int nFound = 0;
+ int idxFound = -1;
+ int idxSelected = ImplGetSelectedIndex( hMenu );
+ int idx = idxSelected != -1 ? idxSelected+1 : 0; // if duplicate mnemonics cycle through menu
+ for( int i=0; i< nItemCount; i++, idx++ )
+ {
+ WinSalMenuItem* pSalMenuItem = ImplGetSalMenuItem( hMenu, idx % nItemCount );
+ if( !pSalMenuItem )
+ continue;
+ OUString aStr = pSalMenuItem->mText;
+ aStr = aStr.toAsciiLowerCase();
+ if( aStr.indexOf( aMnemonic ) != -1 )
+ {
+ if( idxFound == -1 )
+ idxFound = idx % nItemCount;
+ if( nFound++ )
+ break; // duplicate found
+ }
+ }
+ if( nFound == 1 )
+ nRet = MAKELRESULT( idxFound, MNC_EXECUTE );
+ else
+ // duplicate mnemonics, just select the next occurrence
+ nRet = MAKELRESULT( idxFound, MNC_SELECT );
+
+ return nRet;
+}
+
+static LRESULT ImplMeasureItem( HWND hWnd, WPARAM wParam, LPARAM lParam )
+{
+ LRESULT nRet = 0;
+ if( !wParam )
+ {
+ // request was sent by a menu
+ nRet = 1;
+ MEASUREITEMSTRUCT *pMI = reinterpret_cast<LPMEASUREITEMSTRUCT>(lParam);
+ if( pMI->CtlType != ODT_MENU )
+ return 0;
+
+ WinSalMenuItem *pSalMenuItem = reinterpret_cast<WinSalMenuItem *>(pMI->itemData);
+ if( !pSalMenuItem )
+ return 0;
+
+ HDC hdc = GetDC( hWnd );
+ SIZE strSize;
+
+ NONCLIENTMETRICSW ncm = {};
+ ncm.cbSize = sizeof( ncm );
+ SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
+
+ // Assume every menu item can be default and printed bold
+ //ncm.lfMenuFont.lfWeight = FW_BOLD;
+
+ HFONT hfntOld = static_cast<HFONT>(SelectObject(hdc, CreateFontIndirectW( &ncm.lfMenuFont )));
+
+ // menu text and accelerator
+ OUString aStr(pSalMenuItem->mText);
+ if( pSalMenuItem->mAccelText.getLength() )
+ {
+ aStr += " " + pSalMenuItem->mAccelText;
+ }
+ GetTextExtentPoint32W( hdc, o3tl::toW(aStr.getStr()),
+ aStr.getLength(), &strSize );
+
+ // image
+ Size bmpSize( 16, 16 );
+ //if( pSalMenuItem->maBitmap )
+ // bmpSize = pSalMenuItem->maBitmap.GetSizePixel();
+
+ // checkmark
+ Size checkSize( GetSystemMetrics( SM_CXMENUCHECK ), GetSystemMetrics( SM_CYMENUCHECK ) );
+
+ pMI->itemWidth = checkSize.Width() + 3 + bmpSize.Width() + 3 + strSize.cx;
+ pMI->itemHeight = std::max( std::max( checkSize.Height(), bmpSize.Height() ), tools::Long(strSize.cy) );
+ pMI->itemHeight += 4;
+
+ DeleteObject( SelectObject(hdc, hfntOld) );
+ ReleaseDC( hWnd, hdc );
+ }
+
+ return nRet;
+}
+
+static LRESULT ImplDrawItem(HWND, WPARAM wParam, LPARAM lParam )
+{
+ LRESULT nRet = 0;
+ if( !wParam )
+ {
+ // request was sent by a menu
+ nRet = 1;
+ DRAWITEMSTRUCT *pDI = reinterpret_cast<LPDRAWITEMSTRUCT>(lParam);
+ if( pDI->CtlType != ODT_MENU )
+ return 0;
+
+ WinSalMenuItem *pSalMenuItem = reinterpret_cast<WinSalMenuItem *>(pDI->itemData);
+ if( !pSalMenuItem )
+ return 0;
+
+ COLORREF clrPrevText, clrPrevBkgnd;
+ HFONT hfntOld;
+ HBRUSH hbrOld;
+ bool fChecked = (pDI->itemState & ODS_CHECKED);
+ bool fSelected = (pDI->itemState & ODS_SELECTED);
+ bool fDisabled = (pDI->itemState & (ODS_DISABLED | ODS_GRAYED));
+
+ // Set the appropriate foreground and background colors.
+ RECT aRect = pDI->rcItem;
+
+ if ( fDisabled )
+ clrPrevText = SetTextColor( pDI->hDC, GetSysColor( COLOR_GRAYTEXT ) );
+ else
+ clrPrevText = SetTextColor( pDI->hDC, GetSysColor( fSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT ) );
+
+ DWORD colBackground = GetSysColor( fSelected ? COLOR_HIGHLIGHT : COLOR_MENU );
+ clrPrevBkgnd = SetBkColor( pDI->hDC, colBackground );
+
+ hbrOld = static_cast<HBRUSH>(SelectObject( pDI->hDC, CreateSolidBrush( GetBkColor( pDI->hDC ) ) ));
+
+ // Fill background
+ if(!PatBlt( pDI->hDC, aRect.left, aRect.top, aRect.right-aRect.left, aRect.bottom-aRect.top, PATCOPY ))
+ SAL_WARN("vcl", "PatBlt failed: " << WindowsErrorString(GetLastError()));
+
+ int lineHeight = aRect.bottom-aRect.top;
+
+ int x = aRect.left;
+ int y = aRect.top;
+
+ int checkWidth = GetSystemMetrics( SM_CXMENUCHECK );
+ int checkHeight = GetSystemMetrics( SM_CYMENUCHECK );
+ if( fChecked )
+ {
+ RECT r;
+ r.left = 0;
+ r.top = 0;
+ r.right = checkWidth;
+ r.bottom = checkWidth;
+ HDC memDC = CreateCompatibleDC( pDI->hDC );
+ HBITMAP memBmp = CreateCompatibleBitmap( pDI->hDC, checkWidth, checkHeight );
+ HBITMAP hOldBmp = static_cast<HBITMAP>(SelectObject( memDC, memBmp ));
+ DrawFrameControl( memDC, &r, DFC_MENU, DFCS_MENUCHECK );
+ BitBlt( pDI->hDC, x, y+(lineHeight-checkHeight)/2, checkWidth, checkHeight, memDC, 0, 0, SRCAND );
+ DeleteObject( SelectObject( memDC, hOldBmp ) );
+ DeleteDC( memDC );
+ }
+ x += checkWidth+3;
+
+ //Size bmpSize = aBitmap.GetSizePixel();
+ Size bmpSize(16, 16);
+ if( !pSalMenuItem->maBitmap.IsEmpty() )
+ {
+ Bitmap aBitmap( pSalMenuItem->maBitmap );
+
+ // set transparent pixels to background color
+ if( fDisabled )
+ colBackground = RGB(255,255,255);
+ aBitmap.Replace( COL_LIGHTMAGENTA,
+ Color( GetRValue(colBackground),GetGValue(colBackground),GetBValue(colBackground) ));
+
+ WinSalBitmap* pSalBmp = static_cast<WinSalBitmap*>(aBitmap.ImplGetSalBitmap().get());
+ HGLOBAL hDrawDIB = pSalBmp->ImplGethDIB();
+
+ if( hDrawDIB )
+ {
+ PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( hDrawDIB ));
+ PBYTE pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize +
+ WinSalBitmap::ImplGetDIBColorCount( hDrawDIB ) * sizeof( RGBQUAD );
+
+ HBITMAP hBmp = CreateDIBitmap( pDI->hDC, &pBI->bmiHeader, CBM_INIT, pBits, pBI, DIB_RGB_COLORS );
+ GlobalUnlock( hDrawDIB );
+
+ HBRUSH hbrIcon = CreateSolidBrush( GetSysColor( COLOR_GRAYTEXT ) );
+ DrawStateW( pDI->hDC, hbrIcon, nullptr, reinterpret_cast<LPARAM>(hBmp), WPARAM(0),
+ x, y+(lineHeight-bmpSize.Height())/2, bmpSize.Width(), bmpSize.Height(),
+ DST_BITMAP | (fDisabled ? (fSelected ? DSS_MONO : DSS_DISABLED) : DSS_NORMAL) );
+
+ DeleteObject( hbrIcon );
+ DeleteObject( hBmp );
+ }
+
+ }
+ x += bmpSize.Width() + 3;
+ aRect.left = x;
+
+ NONCLIENTMETRICSW ncm = {};
+ ncm.cbSize = sizeof( ncm );
+ SystemParametersInfoW( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
+
+ // Print default menu entry with bold font
+ //if ( pDI->itemState & ODS_DEFAULT )
+ // ncm.lfMenuFont.lfWeight = FW_BOLD;
+
+ hfntOld = static_cast<HFONT>(SelectObject(pDI->hDC, CreateFontIndirectW( &ncm.lfMenuFont )));
+
+ SIZE strSize;
+ OUString aStr( pSalMenuItem->mText );
+ GetTextExtentPoint32W( pDI->hDC, o3tl::toW(aStr.getStr()),
+ aStr.getLength(), &strSize );
+
+ if(!DrawStateW( pDI->hDC, nullptr, nullptr,
+ reinterpret_cast<LPARAM>(aStr.getStr()),
+ WPARAM(0), aRect.left, aRect.top + (lineHeight - strSize.cy)/2, 0, 0,
+ DST_PREFIXTEXT | (fDisabled && !fSelected ? DSS_DISABLED : DSS_NORMAL) ) )
+ SAL_WARN("vcl", "DrawStateW failed: " << WindowsErrorString(GetLastError()));
+
+ if( pSalMenuItem->mAccelText.getLength() )
+ {
+ SIZE strSizeA;
+ aStr = pSalMenuItem->mAccelText;
+ GetTextExtentPoint32W( pDI->hDC, o3tl::toW(aStr.getStr()),
+ aStr.getLength(), &strSizeA );
+ TEXTMETRICW tm;
+ GetTextMetricsW( pDI->hDC, &tm );
+
+ // position the accelerator string to the right but leave space for the
+ // (potential) submenu arrow (tm.tmMaxCharWidth)
+ if(!DrawStateW( pDI->hDC, nullptr, nullptr,
+ reinterpret_cast<LPARAM>(aStr.getStr()),
+ WPARAM(0), aRect.right-strSizeA.cx-tm.tmMaxCharWidth, aRect.top + (lineHeight - strSizeA.cy)/2, 0, 0,
+ DST_TEXT | (fDisabled && !fSelected ? DSS_DISABLED : DSS_NORMAL) ) )
+ SAL_WARN("vcl", "DrawStateW failed: " << WindowsErrorString(GetLastError()));
+ }
+
+ // Restore the original font and colors.
+ DeleteObject( SelectObject( pDI->hDC, hbrOld ) );
+ DeleteObject( SelectObject( pDI->hDC, hfntOld) );
+ SetTextColor(pDI->hDC, clrPrevText);
+ SetBkColor(pDI->hDC, clrPrevBkgnd);
+ }
+ return nRet;
+}
+
+static bool ImplHandleMenuActivate( HWND hWnd, WPARAM wParam, LPARAM )
+{
+ // Menu activation
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( !pFrame )
+ return false;
+
+ HMENU hMenu = reinterpret_cast<HMENU>(wParam);
+ // WORD nPos = LOWORD (lParam);
+ // bool bWindowMenu = (bool) HIWORD(lParam);
+
+ // Send activate and deactivate together, so we have not keep track of opened menus
+ // this will be enough to have the menus updated correctly
+ SalMenuEvent aMenuEvt;
+ WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( hMenu, 0 );
+ if( pSalMenuItem )
+ aMenuEvt.mpMenu = pSalMenuItem->mpMenu;
+ else
+ aMenuEvt.mpMenu = nullptr;
+
+ bool nRet = pFrame->CallCallback( SalEvent::MenuActivate, &aMenuEvt );
+ if( nRet )
+ nRet = pFrame->CallCallback( SalEvent::MenuDeactivate, &aMenuEvt );
+ if( nRet )
+ pFrame->mLastActivatedhMenu = hMenu;
+
+ return nRet;
+}
+
+static bool ImplHandleMenuSelect( HWND hWnd, WPARAM wParam, LPARAM lParam )
+{
+ // Menu selection
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( !pFrame )
+ return false;
+
+ WORD nId = LOWORD(wParam); // menu item or submenu index
+ WORD nFlags = HIWORD(wParam);
+ HMENU hMenu = reinterpret_cast<HMENU>(lParam);
+
+ // check if we have to process the message
+ if( !GetSalData()->IsKnownMenuHandle( hMenu ) )
+ return false;
+
+ bool bByPosition = false;
+ if( nFlags & MF_POPUP )
+ bByPosition = true;
+
+ bool nRet = false;
+ if ( hMenu && !pFrame->mLastActivatedhMenu )
+ {
+ // we never activated a menu (ie, no WM_INITMENUPOPUP has occurred yet)
+ // which means this must be the menubar -> send activation/deactivation
+ SalMenuEvent aMenuEvt;
+ WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( hMenu, nId, bByPosition );
+ if( pSalMenuItem )
+ aMenuEvt.mpMenu = pSalMenuItem->mpMenu;
+ else
+ aMenuEvt.mpMenu = nullptr;
+
+ nRet = pFrame->CallCallback( SalEvent::MenuActivate, &aMenuEvt );
+ if( nRet )
+ nRet = pFrame->CallCallback( SalEvent::MenuDeactivate, &aMenuEvt );
+ if( nRet )
+ pFrame->mLastActivatedhMenu = hMenu;
+ }
+
+ if( !hMenu && nFlags == 0xFFFF )
+ {
+ // all menus are closed, reset activation logic
+ pFrame->mLastActivatedhMenu = nullptr;
+ }
+
+ if( hMenu )
+ {
+ // hMenu must be saved, as it is not passed in WM_COMMAND which always occurs after a selection
+ // if a menu is closed due to a command selection then hMenu is NULL, but WM_COMMAND comes later
+ // so we must not overwrite it in this case
+ pFrame->mSelectedhMenu = hMenu;
+
+ // send highlight event
+ if( nFlags & MF_POPUP )
+ {
+ // submenu selected
+ // wParam now carries an index instead of an id -> retrieve id
+ MENUITEMINFOW mi = {};
+ mi.cbSize = sizeof( mi );
+ mi.fMask = MIIM_ID;
+ if( GetMenuItemInfoW( hMenu, LOWORD(wParam), TRUE, &mi) )
+ nId = sal::static_int_cast<WORD>(mi.wID);
+ }
+
+ SalMenuEvent aMenuEvt;
+ aMenuEvt.mnId = nId;
+ WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( hMenu, nId, false );
+ if( pSalMenuItem )
+ aMenuEvt.mpMenu = pSalMenuItem->mpMenu;
+ else
+ aMenuEvt.mpMenu = nullptr;
+
+ nRet = pFrame->CallCallback( SalEvent::MenuHighlight, &aMenuEvt );
+ }
+
+ return nRet;
+}
+
+static bool ImplHandleCommand( HWND hWnd, WPARAM wParam, LPARAM )
+{
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( !pFrame )
+ return false;
+
+ bool nRet = false;
+ if( !HIWORD(wParam) )
+ {
+ // Menu command
+ WORD nId = LOWORD(wParam);
+ if( nId ) // zero for separators
+ {
+ SalMenuEvent aMenuEvt;
+ aMenuEvt.mnId = nId;
+ WinSalMenuItem *pSalMenuItem = ImplGetSalMenuItem( pFrame->mSelectedhMenu, nId, false );
+ if( pSalMenuItem )
+ aMenuEvt.mpMenu = pSalMenuItem->mpMenu;
+ else
+ aMenuEvt.mpMenu = nullptr;
+
+ nRet = pFrame->CallCallback( SalEvent::MenuCommand, &aMenuEvt );
+ }
+ }
+ return nRet;
+}
+
+static bool ImplHandleSysCommand( HWND hWnd, WPARAM wParam, LPARAM lParam )
+{
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( !pFrame )
+ return false;
+
+ WPARAM nCommand = wParam & 0xFFF0;
+
+ if (pFrame->isFullScreen())
+ {
+ bool bMaximize = IsZoomed( pFrame->mhWnd );
+ bool bMinimize = IsIconic( pFrame->mhWnd );
+ if ( (nCommand == SC_SIZE) ||
+ (!bMinimize && (nCommand == SC_MOVE)) ||
+ (!bMaximize && (nCommand == SC_MAXIMIZE)) ||
+ (bMaximize && (nCommand == SC_RESTORE)) )
+ {
+ return true;
+ }
+ }
+
+ if ( nCommand == SC_MOVE )
+ {
+ WinSalTimer* pTimer = static_cast<WinSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
+ if ( pTimer )
+ pTimer->SetForceRealTimer( true );
+ }
+
+ if ( nCommand == SC_KEYMENU )
+ {
+ // do not process SC_KEYMENU if we have a native menu
+ // Windows should handle this
+ if( GetMenu( hWnd ) )
+ return false;
+
+ // Process here KeyMenu events only for Alt to activate the MenuBar,
+ // or if a SysChild window is in focus, as Alt-key-combinations are
+ // only processed via this event
+ if ( !LOWORD( lParam ) )
+ {
+ // Only trigger if no other key is pressed.
+ // Contrary to Docu the CharCode is delivered with the x-coordinate
+ // that is pressed in addition.
+ // Also 32 for space, 99 for c, 100 for d, ...
+ // As this is not documented, we check the state of the space-bar
+ if ( GetKeyState( VK_SPACE ) & 0x8000 )
+ return false;
+
+ // to avoid activating the MenuBar for Alt+MouseKey
+ if ( (GetKeyState( VK_LBUTTON ) & 0x8000) ||
+ (GetKeyState( VK_RBUTTON ) & 0x8000) ||
+ (GetKeyState( VK_MBUTTON ) & 0x8000) ||
+ (GetKeyState( VK_SHIFT ) & 0x8000) )
+ return true;
+
+ SalKeyEvent aKeyEvt;
+ aKeyEvt.mnCode = KEY_MENU;
+ aKeyEvt.mnCharCode = 0;
+ aKeyEvt.mnRepeat = 0;
+ bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
+ pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
+ return nRet;
+ }
+ else
+ {
+ // check if a SysChild is in focus
+ HWND hFocusWnd = ::GetFocus();
+ if ( hFocusWnd && ImplFindSalObject( hFocusWnd ) )
+ {
+ char cKeyCode = static_cast<char>(static_cast<unsigned char>(LOWORD( lParam )));
+ // LowerCase
+ if ( (cKeyCode >= 65) && (cKeyCode <= 90) )
+ cKeyCode += 32;
+ // We only accept 0-9 and A-Z; all other keys have to be
+ // processed by the SalObj hook
+ if ( ((cKeyCode >= 48) && (cKeyCode <= 57)) ||
+ ((cKeyCode >= 97) && (cKeyCode <= 122)) )
+ {
+ sal_uInt16 nModCode = 0;
+ if ( GetKeyState( VK_SHIFT ) & 0x8000 )
+ nModCode |= KEY_SHIFT;
+ if ( GetKeyState( VK_CONTROL ) & 0x8000 )
+ nModCode |= KEY_MOD1;
+ nModCode |= KEY_MOD2;
+
+ SalKeyEvent aKeyEvt;
+ if ( (cKeyCode >= 48) && (cKeyCode <= 57) )
+ aKeyEvt.mnCode = KEY_0+(cKeyCode-48);
+ else
+ aKeyEvt.mnCode = KEY_A+(cKeyCode-97);
+ aKeyEvt.mnCode |= nModCode;
+ aKeyEvt.mnCharCode = cKeyCode;
+ aKeyEvt.mnRepeat = 0;
+ bool nRet = pFrame->CallCallback( SalEvent::KeyInput, &aKeyEvt );
+ pFrame->CallCallback( SalEvent::KeyUp, &aKeyEvt );
+ return nRet;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+static void ImplHandleInputLangChange( HWND hWnd, WPARAM, LPARAM lParam )
+{
+ ImplSalYieldMutexAcquireWithWait();
+
+ // check if we support IME
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+
+ if ( !pFrame )
+ return;
+
+ if ( pFrame->mbIME && pFrame->mhDefIMEContext )
+ {
+ HKL hKL = reinterpret_cast<HKL>(lParam);
+ UINT nImeProps = ImmGetProperty( hKL, IGP_PROPERTY );
+
+ pFrame->mbSpezIME = (nImeProps & IME_PROP_SPECIAL_UI) != 0;
+ pFrame->mbAtCursorIME = (nImeProps & IME_PROP_AT_CARET) != 0;
+ pFrame->mbHandleIME = !pFrame->mbSpezIME;
+ }
+
+ // trigger input language and codepage update
+ UINT nLang = pFrame->mnInputLang;
+ ImplUpdateInputLang( pFrame );
+
+ // notify change
+ if( nLang != pFrame->mnInputLang )
+ pFrame->CallCallback( SalEvent::InputLanguageChange, nullptr );
+
+ // reinit spec. keys
+ GetSalData()->initKeyCodeMap();
+
+ ImplSalYieldMutexRelease();
+}
+
+static void ImplUpdateIMECursorPos( WinSalFrame* pFrame, HIMC hIMC )
+{
+ COMPOSITIONFORM aForm = {};
+
+ // get cursor position and from it calculate default position
+ // for the composition window
+ SalExtTextInputPosEvent aPosEvt;
+ pFrame->CallCallback( SalEvent::ExtTextInputPos, &aPosEvt );
+ if ( (aPosEvt.mnX == -1) && (aPosEvt.mnY == -1) )
+ aForm.dwStyle |= CFS_DEFAULT;
+ else
+ {
+ aForm.dwStyle |= CFS_POINT;
+ aForm.ptCurrentPos.x = aPosEvt.mnX;
+ aForm.ptCurrentPos.y = aPosEvt.mnY;
+ }
+ ImmSetCompositionWindow( hIMC, &aForm );
+
+ // Because not all IME's use this values, we create
+ // a Windows caret to force the Position from the IME
+ if ( GetFocus() == pFrame->mhWnd )
+ {
+ CreateCaret( pFrame->mhWnd, nullptr,
+ aPosEvt.mnWidth, aPosEvt.mnHeight );
+ SetCaretPos( aPosEvt.mnX, aPosEvt.mnY );
+ }
+}
+
+static bool ImplHandleIMEStartComposition( HWND hWnd )
+{
+ bool bDef = true;
+
+ ImplSalYieldMutexAcquireWithWait();
+
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( pFrame )
+ {
+ HIMC hIMC = ImmGetContext( hWnd );
+ if ( hIMC )
+ {
+ ImplUpdateIMECursorPos( pFrame, hIMC );
+ ImmReleaseContext( hWnd, hIMC );
+ }
+
+ if ( pFrame->mbHandleIME )
+ {
+ if ( pFrame->mbAtCursorIME )
+ bDef = false;
+ }
+ }
+
+ ImplSalYieldMutexRelease();
+
+ return bDef;
+}
+
+static bool ImplHandleIMECompositionInput( WinSalFrame* pFrame,
+ HIMC hIMC, LPARAM lParam )
+{
+ bool bDef = true;
+
+ // Init Event
+ SalExtTextInputEvent aEvt;
+ aEvt.mpTextAttr = nullptr;
+ aEvt.mnCursorPos = 0;
+ aEvt.mnCursorFlags = 0;
+
+ // If we get a result string, then we handle this input
+ if ( lParam & GCS_RESULTSTR )
+ {
+ bDef = false;
+
+ LONG nTextLen = ImmGetCompositionStringW( hIMC, GCS_RESULTSTR, nullptr, 0 ) / sizeof( WCHAR );
+ if ( nTextLen >= 0 )
+ {
+ auto pTextBuf = std::make_unique<WCHAR[]>(nTextLen);
+ ImmGetCompositionStringW( hIMC, GCS_RESULTSTR, pTextBuf.get(), nTextLen*sizeof( WCHAR ) );
+ aEvt.maText = OUString( o3tl::toU(pTextBuf.get()), static_cast<sal_Int32>(nTextLen) );
+ }
+
+ aEvt.mnCursorPos = aEvt.maText.getLength();
+ pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt );
+ pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
+ ImplUpdateIMECursorPos( pFrame, hIMC );
+ }
+
+ // If the IME doesn't support OnSpot input, then there is nothing to do
+ if ( !pFrame->mbAtCursorIME )
+ return !bDef;
+
+ // If we get new Composition data, then we handle this new input
+ if ( (lParam & (GCS_COMPSTR | GCS_COMPATTR)) ||
+ ((lParam & GCS_CURSORPOS) && !(lParam & GCS_RESULTSTR)) )
+ {
+ bDef = false;
+
+ std::unique_ptr<ExtTextInputAttr[]> pSalAttrAry;
+ LONG nTextLen = ImmGetCompositionStringW( hIMC, GCS_COMPSTR, nullptr, 0 ) / sizeof( WCHAR );
+ if ( nTextLen > 0 )
+ {
+ {
+ auto pTextBuf = std::make_unique<WCHAR[]>(nTextLen);
+ ImmGetCompositionStringW( hIMC, GCS_COMPSTR, pTextBuf.get(), nTextLen*sizeof( WCHAR ) );
+ aEvt.maText = OUString( o3tl::toU(pTextBuf.get()), static_cast<sal_Int32>(nTextLen) );
+ }
+
+ std::unique_ptr<BYTE[]> pAttrBuf;
+ LONG nAttrLen = ImmGetCompositionStringW( hIMC, GCS_COMPATTR, nullptr, 0 );
+ if ( nAttrLen > 0 )
+ {
+ pAttrBuf.reset(new BYTE[nAttrLen]);
+ ImmGetCompositionStringW( hIMC, GCS_COMPATTR, pAttrBuf.get(), nAttrLen );
+ }
+
+ if ( pAttrBuf )
+ {
+ sal_Int32 nTextLen2 = aEvt.maText.getLength();
+ pSalAttrAry.reset(new ExtTextInputAttr[nTextLen2]);
+ memset( pSalAttrAry.get(), 0, nTextLen2*sizeof( sal_uInt16 ) );
+ for( sal_Int32 i = 0; (i < nTextLen2) && (i < nAttrLen); i++ )
+ {
+ BYTE nWinAttr = pAttrBuf.get()[i];
+ ExtTextInputAttr nSalAttr;
+ if ( nWinAttr == ATTR_TARGET_CONVERTED )
+ {
+ nSalAttr = ExtTextInputAttr::BoldUnderline;
+ aEvt.mnCursorFlags |= EXTTEXTINPUT_CURSOR_INVISIBLE;
+ }
+ else if ( nWinAttr == ATTR_CONVERTED )
+ nSalAttr = ExtTextInputAttr::DashDotUnderline;
+ else if ( nWinAttr == ATTR_TARGET_NOTCONVERTED )
+ nSalAttr = ExtTextInputAttr::Highlight;
+ else if ( nWinAttr == ATTR_INPUT_ERROR )
+ nSalAttr = ExtTextInputAttr::RedText | ExtTextInputAttr::DottedUnderline;
+ else /* ( nWinAttr == ATTR_INPUT ) */
+ nSalAttr = ExtTextInputAttr::DottedUnderline;
+ pSalAttrAry[i] = nSalAttr;
+ }
+
+ aEvt.mpTextAttr = pSalAttrAry.get();
+ }
+ }
+
+ // Only when we get new composition data, we must send this event
+ if ( (nTextLen > 0) || !(lParam & GCS_RESULTSTR) )
+ {
+ // End the mode, if the last character is deleted
+ if ( !nTextLen )
+ {
+ pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt );
+ pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
+ }
+ else
+ {
+ // Because Cursor-Position and DeltaStart never updated
+ // from the korean input engine, we must handle this here
+ if ( lParam & CS_INSERTCHAR )
+ {
+ aEvt.mnCursorPos = nTextLen;
+ if ( aEvt.mnCursorPos && (lParam & CS_NOMOVECARET) )
+ aEvt.mnCursorPos--;
+ }
+ else
+ aEvt.mnCursorPos = LOWORD( ImmGetCompositionStringW( hIMC, GCS_CURSORPOS, nullptr, 0 ) );
+
+ if ( pFrame->mbCandidateMode )
+ aEvt.mnCursorFlags |= EXTTEXTINPUT_CURSOR_INVISIBLE;
+ if ( lParam & CS_NOMOVECARET )
+ aEvt.mnCursorFlags |= EXTTEXTINPUT_CURSOR_OVERWRITE;
+
+ pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt );
+ }
+ ImplUpdateIMECursorPos( pFrame, hIMC );
+ }
+ }
+
+ return !bDef;
+}
+
+static bool ImplHandleIMEComposition( HWND hWnd, LPARAM lParam )
+{
+ bool bDef = true;
+ ImplSalYieldMutexAcquireWithWait();
+
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( pFrame && (!lParam || (lParam & GCS_RESULTSTR)) )
+ {
+ // reset the background mode for each text input,
+ // as some tools such as RichWin may have changed it
+ if ( pFrame->mpLocalGraphics &&
+ pFrame->mpLocalGraphics->getHDC() )
+ SetBkMode( pFrame->mpLocalGraphics->getHDC(), TRANSPARENT );
+ }
+
+ if ( pFrame && pFrame->mbHandleIME )
+ {
+ if ( !lParam )
+ {
+ SalExtTextInputEvent aEvt;
+ aEvt.mpTextAttr = nullptr;
+ aEvt.mnCursorPos = 0;
+ aEvt.mnCursorFlags = 0;
+ pFrame->CallCallback( SalEvent::ExtTextInput, &aEvt );
+ pFrame->CallCallback( SalEvent::EndExtTextInput, nullptr );
+ }
+ else if ( lParam & (GCS_RESULTSTR | GCS_COMPSTR | GCS_COMPATTR | GCS_CURSORPOS) )
+ {
+ HIMC hIMC = ImmGetContext( hWnd );
+ if ( hIMC )
+ {
+ if ( ImplHandleIMECompositionInput( pFrame, hIMC, lParam ) )
+ bDef = false;
+
+ ImmReleaseContext( hWnd, hIMC );
+ }
+ }
+ }
+
+ ImplSalYieldMutexRelease();
+ return bDef;
+}
+
+static bool ImplHandleIMEEndComposition( HWND hWnd )
+{
+ bool bDef = true;
+
+ ImplSalYieldMutexAcquireWithWait();
+
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ if ( pFrame && pFrame->mbHandleIME )
+ {
+ if ( pFrame->mbAtCursorIME )
+ {
+ pFrame->mbCandidateMode = false;
+ bDef = false;
+ }
+ }
+
+ ImplSalYieldMutexRelease();
+
+ return bDef;
+}
+
+static bool ImplHandleAppCommand( HWND hWnd, LPARAM lParam, LRESULT & nRet )
+{
+ MediaCommand nCommand;
+ switch( GET_APPCOMMAND_LPARAM(lParam) )
+ {
+ case APPCOMMAND_MEDIA_CHANNEL_DOWN: nCommand = MediaCommand::ChannelDown; break;
+ case APPCOMMAND_MEDIA_CHANNEL_UP: nCommand = MediaCommand::ChannelUp; break;
+ case APPCOMMAND_MEDIA_NEXTTRACK: nCommand = MediaCommand::NextTrack; break;
+ case APPCOMMAND_MEDIA_PAUSE: nCommand = MediaCommand::Pause; break;
+ case APPCOMMAND_MEDIA_PLAY: nCommand = MediaCommand::Play; break;
+ case APPCOMMAND_MEDIA_PLAY_PAUSE: nCommand = MediaCommand::PlayPause; break;
+ case APPCOMMAND_MEDIA_PREVIOUSTRACK: nCommand = MediaCommand::PreviousTrack; break;
+ case APPCOMMAND_MEDIA_RECORD: nCommand = MediaCommand::Record; break;
+ case APPCOMMAND_MEDIA_REWIND: nCommand = MediaCommand::Rewind; break;
+ case APPCOMMAND_MEDIA_STOP: nCommand = MediaCommand::Stop; break;
+ case APPCOMMAND_MIC_ON_OFF_TOGGLE: nCommand = MediaCommand::MicOnOffToggle; break;
+ case APPCOMMAND_MICROPHONE_VOLUME_DOWN: nCommand = MediaCommand::MicrophoneVolumeDown; break;
+ case APPCOMMAND_MICROPHONE_VOLUME_MUTE: nCommand = MediaCommand::MicrophoneVolumeMute; break;
+ case APPCOMMAND_MICROPHONE_VOLUME_UP: nCommand = MediaCommand::MicrophoneVolumeUp; break;
+ case APPCOMMAND_VOLUME_DOWN: nCommand = MediaCommand::VolumeDown; break;
+ case APPCOMMAND_VOLUME_MUTE: nCommand = MediaCommand::VolumeMute; break;
+ case APPCOMMAND_VOLUME_UP: nCommand = MediaCommand::VolumeUp; break;
+ default:
+ return false;
+ }
+
+ WinSalFrame* pFrame = GetWindowPtr( hWnd );
+ vcl::Window *pWindow = pFrame ? pFrame->GetWindow() : nullptr;
+
+ if( pWindow )
+ {
+ const Point aPoint;
+ CommandMediaData aMediaData(nCommand);
+ CommandEvent aCEvt( aPoint, CommandEventId::Media, false, &aMediaData );
+ NotifyEvent aNCmdEvt( NotifyEventType::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)
+{
+ uno::Reference<accessibility::XMSAAService> xMSAA;
+ if (ImplSalYieldMutexTryToAcquire())
+ {
+ if (!Application::GetSettings().GetMiscSettings().GetEnableATToolSupport())
+ {
+ // IA2 should be enabled automatically
+ AllSettings aSettings = Application::GetSettings();
+ MiscSettings aMisc = aSettings.GetMiscSettings();
+ aMisc.SetEnableATToolSupport(true);
+ // The above is enough, since aMisc changes the same shared ImplMiscData as used in global
+ // settings, so no need to call aSettings.SetMiscSettings and Application::SetSettings
+
+ if (!Application::GetSettings().GetMiscSettings().GetEnableATToolSupport())
+ return false; // locked down somehow ?
+ }
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // Make sure to launch Accessibility only the following criteria are satisfied
+ // to avoid RFT interrupts regular accessibility processing
+ if ( !pSVData->mxAccessBridge.is() )
+ {
+ if( !InitAccessBridge() )
+ return false;
+ }
+ xMSAA.set(pSVData->mxAccessBridge, uno::UNO_QUERY);
+ ImplSalYieldMutexRelease();
+ }
+ else
+ { // tdf#155794: access without locking: hopefully this should be fine
+ // as the bridge is typically inited in Desktop::Main() already and the
+ // WM_GETOBJECT is received only on the main thread and by the time in
+ // VCL shutdown when ImplSvData dies there should not be Windows any
+ // more that could receive messages.
+ xMSAA.set(ImplGetSVData()->mxAccessBridge, uno::UNO_QUERY);
+ }
+
+ 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.maCursorBound.getX() + aEvt.maCursorBound.GetWidth();
+ pQueryCharPosition->pt.y = aEvt.maCursorBound.getY();
+ pQueryCharPosition->cLineHeight = aEvt.maCursorBound.GetWidth();
+ }
+ else
+ {
+ // For horizontal writing, the base line is the bottom edge of the rectangle.
+ // and the target position is top-left corner.
+ pQueryCharPosition->pt.x = aEvt.maCursorBound.getX();
+ pQueryCharPosition->pt.y = aEvt.maCursorBound.getY();
+ pQueryCharPosition->cLineHeight = aEvt.maCursorBound.GetHeight();
+ }
+
+ // Currently not supported but many IMEs usually ignore them.
+ pQueryCharPosition->rcDocument.left = 0;
+ pQueryCharPosition->rcDocument.top = 0;
+ pQueryCharPosition->rcDocument.right = 0;
+ pQueryCharPosition->rcDocument.bottom = 0;
+
+ return TRUE;
+}
+
+void SalTestMouseLeave()
+{
+ SalData* pSalData = GetSalData();
+
+ if ( pSalData->mhWantLeaveMsg && !::GetCapture() )
+ {
+ POINT aPt;
+ GetCursorPos( &aPt );
+
+ // a one item cache, because this function is sometimes hot - if the cursor has not moved, then
+ // no need to call WindowFromPoint
+ static POINT cachedPoint;
+ if (cachedPoint.x == aPt.x && cachedPoint.y == aPt.y)
+ return;
+ cachedPoint = aPt;
+
+ if ( pSalData->mhWantLeaveMsg != WindowFromPoint( aPt ) )
+ SendMessageW( pSalData->mhWantLeaveMsg, SAL_MSG_MOUSELEAVE, 0, MAKELPARAM( aPt.x, aPt.y ) );
+ }
+}
+
+static bool ImplSalWheelMousePos( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ,
+ LRESULT& rResult )
+{
+ POINT aPt;
+ POINT aScreenPt;
+ aScreenPt.x = static_cast<short>(LOWORD( lParam ));
+ aScreenPt.y = static_cast<short>(HIWORD( lParam ));
+ // find child window that is at this position
+ HWND hChildWnd;
+ HWND hWheelWnd = hWnd;
+ do
+ {
+ hChildWnd = hWheelWnd;
+ aPt = aScreenPt;
+ ScreenToClient( hChildWnd, &aPt );
+ hWheelWnd = ChildWindowFromPointEx( hChildWnd, aPt, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT );
+ }
+ while ( hWheelWnd && (hWheelWnd != hChildWnd) );
+ if ( hWheelWnd && (hWheelWnd != hWnd) &&
+ (hWheelWnd != ::GetFocus()) && IsWindowEnabled( hWheelWnd ) )
+ {
+ rResult = SendMessageW( hWheelWnd, nMsg, wParam, lParam );
+ return false;
+ }
+
+ return true;
+}
+
+static LRESULT CALLBACK SalFrameWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, bool& rDef )
+{
+ LRESULT nRet = 0;
+ static bool bInWheelMsg = false;
+ static bool bInQueryEnd = false;
+
+ SAL_INFO("vcl.gdi.wndproc", "SalFrameWndProc(nMsg=" << nMsg << ", wParam=" << wParam << ", lParam=" << lParam << ")");
+
+ // By WM_CREATE we connect the frame with the window handle
+ if ( nMsg == WM_CREATE )
+ {
+ // Save Window-Instance in Windowhandle
+ // Can also be used for the A-Version, because the struct
+ // to access lpCreateParams is the same structure
+ CREATESTRUCTW* pStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
+ WinSalFrame* pFrame = static_cast<WinSalFrame*>(pStruct->lpCreateParams);
+ if ( pFrame != nullptr )
+ {
+ SetWindowPtr( hWnd, pFrame );
+
+ UpdateDarkMode(hWnd);
+
+ // Set HWND already here, as data might be used already
+ // when messages are being sent by CreateWindow()
+ pFrame->mhWnd = hWnd;
+ pFrame->maSysData.hWnd = hWnd;
+ }
+ return 0;
+ }
+
+ ImplSVData* pSVData = ImplGetSVData();
+ // #i72707# TODO: the mbDeInit check will not be needed
+ // once all windows that are not properly closed on exit got fixed
+ if( pSVData->mbDeInit )
+ return 0;
+
+ if ( WM_USER_SYSTEM_WINDOW_ACTIVATED == nMsg )
+ {
+ ImplHideSplash();
+ return 0;
+ }
+
+ switch( nMsg )
+ {
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_NCMOUSEMOVE:
+ case SAL_MSG_MOUSELEAVE:
+ ImplSalYieldMutexAcquireWithWait();
+ rDef = !ImplHandleMouseMsg( hWnd, nMsg, wParam, lParam );
+ ImplSalYieldMutexRelease();
+ break;
+
+ case WM_NCLBUTTONDOWN:
+ case WM_NCMBUTTONDOWN:
+ case WM_NCRBUTTONDOWN:
+ ImplSalYieldMutexAcquireWithWait();
+ ImplCallClosePopupsHdl( hWnd ); // close popups...
+ ImplSalYieldMutexRelease();
+ break;
+
+ case WM_MOUSEACTIVATE:
+ if ( LOWORD( lParam ) == HTCLIENT )
+ {
+ ImplSalYieldMutexAcquireWithWait();
+ nRet = LRESULT(ImplHandleMouseActivateMsg( hWnd ));
+ ImplSalYieldMutexRelease();
+ if ( nRet )
+ {
+ nRet = MA_NOACTIVATE;
+ rDef = false;
+ }
+ }
+ break;
+
+ case WM_KEYDOWN:
+ case WM_KEYUP:
+ case WM_DEADCHAR:
+ case WM_CHAR:
+ case WM_UNICHAR: // MCD, 2003-01-13, Support for WM_UNICHAR & Keyman 6.0
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ case WM_SYSCHAR:
+ ImplSalYieldMutexAcquireWithWait();
+ rDef = !ImplHandleKeyMsg( hWnd, nMsg, wParam, lParam, nRet );
+ ImplSalYieldMutexRelease();
+ break;
+
+ case WM_MOUSEWHEEL:
+ case WM_MOUSEHWHEEL:
+ // protect against recursion, in case the message is returned
+ // by IE or the external window
+ if ( !bInWheelMsg )
+ {
+ bInWheelMsg = true;
+ rDef = !ImplHandleWheelMsg( hWnd, nMsg, wParam, lParam );
+ // If we did not process the message, re-check if here is a
+ // connected (?) window that we have to notify.
+ if ( rDef )
+ rDef = ImplSalWheelMousePos( hWnd, nMsg, wParam, lParam, nRet );
+ bInWheelMsg = false;
+ }
+ break;
+
+ case WM_COMMAND:
+ ImplSalYieldMutexAcquireWithWait();
+ rDef = !ImplHandleCommand( hWnd, wParam, lParam );
+ ImplSalYieldMutexRelease();
+ break;
+
+ case WM_INITMENUPOPUP:
+ ImplSalYieldMutexAcquireWithWait();
+ rDef = !ImplHandleMenuActivate( hWnd, wParam, lParam );
+ ImplSalYieldMutexRelease();
+ break;
+
+ case WM_MENUSELECT:
+ ImplSalYieldMutexAcquireWithWait();
+ rDef = !ImplHandleMenuSelect( hWnd, wParam, lParam );
+ ImplSalYieldMutexRelease();
+ break;
+
+ case WM_SYSCOMMAND:
+ ImplSalYieldMutexAcquireWithWait();
+ nRet = LRESULT(ImplHandleSysCommand( hWnd, wParam, lParam ));
+ ImplSalYieldMutexRelease();
+ if ( nRet )
+ rDef = false;
+ break;
+
+ case WM_MENUCHAR:
+ nRet = ImplMenuChar( hWnd, wParam, lParam );
+ if( nRet )
+ rDef = false;
+ break;
+
+ case WM_MEASUREITEM:
+ nRet = ImplMeasureItem(hWnd, wParam, lParam);
+ if( nRet )
+ rDef = false;
+ break;
+
+ case WM_DRAWITEM:
+ nRet = ImplDrawItem(hWnd, wParam, lParam);
+ if( nRet )
+ rDef = false;
+ break;
+
+ case WM_MOVE:
+ ImplHandleMoveMsg(hWnd, lParam);
+ rDef = false;
+ break;
+ case SAL_MSG_POSTMOVE:
+ ImplCallMoveHdl(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:
+ // tdf#155794: this must complete without taking SolarMutex
+ if ( ImplHandleGetObject( hWnd, lParam, wParam, nRet ) )
+ {
+ rDef = false;
+ }
+ 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 0000000000..91a15284ae
--- /dev/null
+++ b/vcl/win/window/salmenu.cxx
@@ -0,0 +1,329 @@
+/* -*- 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);
+}
+
+/*
+ * 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;
+ }
+}
+
+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;
+ }
+ }
+}
+
+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;
+ }
+}
+
+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();
+ }
+}
+
+void WinSalMenu::CheckItem( unsigned nPos, bool bCheck )
+{
+ ::CheckMenuItem(mhMenu, nPos, MF_BYPOSITION|(bCheck ? MF_CHECKED : MF_UNCHECKED));
+}
+
+void WinSalMenu::EnableItem( unsigned nPos, bool bEnable )
+{
+ ::EnableMenuItem(mhMenu, nPos, MF_BYPOSITION|(bEnable ? MF_ENABLED : (MF_DISABLED|MF_GRAYED)));
+}
+
+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();
+ }
+}
+
+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();
+ }
+}
+
+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 0000000000..05ad16b0d4
--- /dev/null
+++ b/vcl/win/window/salobj.cxx
@@ -0,0 +1,679 @@
+/* -*- 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 SalSysObjWndProcW(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch( nMsg )
+ {
+ case WM_ERASEBKGND:
+ return 1;
+ case WM_PAINT:
+ {
+ PAINTSTRUCT aPs;
+ BeginPaint( hWnd, &aPs );
+ EndPaint( hWnd, &aPs );
+ }
+ return 0;
+
+ case WM_PARENTNOTIFY:
+ if (UINT nNotifyMsg = LOWORD(wParam);
+ (nNotifyMsg == WM_LBUTTONDOWN) ||
+ (nNotifyMsg == WM_RBUTTONDOWN) ||
+ (nNotifyMsg == WM_MBUTTONDOWN) )
+ {
+ ImplSalYieldMutexAcquireWithWait();
+ WinSalObject* pSysObj = GetSalObjWindowPtr(hWnd);
+ if ( pSysObj && !pSysObj->IsMouseTransparent() )
+ pSysObj->CallCallback( SalObjEvent::ToTop );
+ ImplSalYieldMutexRelease();
+ }
+ break;
+
+ case WM_MOUSEACTIVATE:
+ ImplSalYieldMutexAcquireWithWait();
+ if (WinSalObject* pSysObj = GetSalObjWindowPtr(hWnd);
+ 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() )
+ {
+ if (WinSalObject* pSysObj = GetSalObjWindowPtr(hWnd))
+ pSysObj->CallCallback(SalObjEvent::ToTop);
+ ImplSalYieldMutexRelease();
+ return 0;
+ }
+ 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() )
+ {
+ WinSalObject* 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!");
+ }
+ return 0;
+
+ case WM_SIZE:
+ if (HWND hWndChild = GetWindow(hWnd, GW_CHILD))
+ {
+ SetWindowPos( hWndChild,
+ nullptr, 0, 0, static_cast<int>(LOWORD( lParam )), static_cast<int>(HIWORD( lParam )),
+ SWP_NOZORDER | SWP_NOACTIVATE );
+ }
+ return 0;
+
+ case WM_CREATE:
+ {
+ // Save the window instance at the window handle.
+ CREATESTRUCTW* pStruct = reinterpret_cast<CREATESTRUCTW*>(lParam);
+ WinSalObject* 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;
+ return 0;
+ }
+ }
+
+ return DefWindowProcW(hWnd, nMsg, wParam, lParam);
+}
+
+static LRESULT CALLBACK SalSysObjChildWndProcW(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
+{
+ switch( nMsg )
+ {
+ // clear background for plugins
+ case WM_ERASEBKGND:
+ if (WinSalObject* pSysObj = GetSalObjWindowPtr(GetParent(hWnd));
+ pSysObj && !pSysObj->IsEraseBackgroundEnabled())
+ {
+ // do not erase background
+ return 1;
+ }
+ break;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT aPs;
+ BeginPaint( hWnd, &aPs );
+ EndPaint( hWnd, &aPs );
+ }
+ return 0;
+
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ if (WinSalObject* pSysObj = GetSalObjWindowPtr(GetParent(hWnd));
+ pSysObj && pSysObj->IsMouseTransparent())
+ {
+ // forward mouse events to parent frame
+ HWND hWndParent = GetParent(pSysObj->mhWnd);
+
+ // transform coordinates
+ POINT pt;
+ pt.x = static_cast<tools::Long>(LOWORD(lParam));
+ pt.y = static_cast<tools::Long>(HIWORD(lParam));
+ MapWindowPoints(hWnd, hWndParent, &pt, 1);
+ lParam = MAKELPARAM(static_cast<WORD>(pt.x), static_cast<WORD>(pt.y));
+
+ return SendMessageW(hWndParent, nMsg, wParam, lParam);
+ }
+ break;
+ }
+
+ return DefWindowProcW(hWnd, nMsg, wParam, lParam);
+}
+
+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( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight )
+{
+ RECT* pRect = mpNextClipRect;
+ RECT* pBoundRect = &(mpClipRgnData->rdh.rcBound);
+ tools::Long nRight = nX + nWidth;
+ tools::Long nBottom = nY + nHeight;
+
+ if ( mbFirstClipRect )
+ {
+ pBoundRect->left = nX;
+ pBoundRect->top = nY;
+ pBoundRect->right = nRight;
+ pBoundRect->bottom = nBottom;
+ mbFirstClipRect = false;
+ }
+ else
+ {
+ if ( nX < pBoundRect->left )
+ pBoundRect->left = static_cast<int>(nX);
+
+ if ( nY < pBoundRect->top )
+ pBoundRect->top = static_cast<int>(nY);
+
+ if ( nRight > pBoundRect->right )
+ pBoundRect->right = static_cast<int>(nRight);
+
+ if ( nBottom > pBoundRect->bottom )
+ pBoundRect->bottom = static_cast<int>(nBottom);
+ }
+
+ pRect->left = static_cast<int>(nX);
+ pRect->top = static_cast<int>(nY);
+ pRect->right = static_cast<int>(nRight);
+ pRect->bottom = static_cast<int>(nBottom);
+ 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( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::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: */