summaryrefslogtreecommitdiffstats
path: root/vcl/source/window
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/source/window
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 '')
-rw-r--r--vcl/source/window/DocWindow.cxx39
-rw-r--r--vcl/source/window/EnumContext.cxx220
-rw-r--r--vcl/source/window/NotebookBarAddonsMerger.cxx183
-rw-r--r--vcl/source/window/OptionalBox.cxx62
-rw-r--r--vcl/source/window/abstdlg.cxx85
-rw-r--r--vcl/source/window/accel.cxx279
-rw-r--r--vcl/source/window/accessibility.cxx594
-rw-r--r--vcl/source/window/accmgr.cxx228
-rw-r--r--vcl/source/window/brdwin.cxx2003
-rw-r--r--vcl/source/window/bubblewindow.cxx591
-rw-r--r--vcl/source/window/bufferdevice.cxx45
-rw-r--r--vcl/source/window/bufferdevice.hxx35
-rw-r--r--vcl/source/window/builder.cxx4399
-rw-r--r--vcl/source/window/clipping.cxx709
-rw-r--r--vcl/source/window/commandevent.cxx214
-rw-r--r--vcl/source/window/cursor.cxx485
-rw-r--r--vcl/source/window/debug.cxx48
-rw-r--r--vcl/source/window/debugevent.cxx271
-rw-r--r--vcl/source/window/decoview.cxx1020
-rw-r--r--vcl/source/window/dialog.cxx1736
-rw-r--r--vcl/source/window/dlgctrl.cxx1168
-rw-r--r--vcl/source/window/dlgctrl.hxx34
-rw-r--r--vcl/source/window/dndeventdispatcher.cxx412
-rw-r--r--vcl/source/window/dndlistenercontainer.cxx455
-rw-r--r--vcl/source/window/dockingarea.cxx257
-rw-r--r--vcl/source/window/dockmgr.cxx1083
-rw-r--r--vcl/source/window/dockwin.cxx1148
-rw-r--r--vcl/source/window/errinf.cxx222
-rw-r--r--vcl/source/window/event.cxx673
-rw-r--r--vcl/source/window/floatwin.cxx974
-rw-r--r--vcl/source/window/globalization.cxx44
-rw-r--r--vcl/source/window/impldockingwrapper.hxx131
-rw-r--r--vcl/source/window/introwin.cxx49
-rw-r--r--vcl/source/window/keycod.cxx95
-rw-r--r--vcl/source/window/keyevent.cxx67
-rw-r--r--vcl/source/window/layout.cxx3128
-rw-r--r--vcl/source/window/legacyaccessibility.cxx241
-rw-r--r--vcl/source/window/menu.cxx3143
-rw-r--r--vcl/source/window/menubarwindow.cxx1220
-rw-r--r--vcl/source/window/menubarwindow.hxx142
-rw-r--r--vcl/source/window/menufloatingwindow.cxx1318
-rw-r--r--vcl/source/window/menufloatingwindow.hxx128
-rw-r--r--vcl/source/window/menuitemlist.cxx316
-rw-r--r--vcl/source/window/menuitemlist.hxx151
-rw-r--r--vcl/source/window/menuwindow.cxx118
-rw-r--r--vcl/source/window/menuwindow.hxx56
-rw-r--r--vcl/source/window/mnemonic.cxx351
-rw-r--r--vcl/source/window/mouse.cxx744
-rw-r--r--vcl/source/window/paint.cxx1808
-rw-r--r--vcl/source/window/printdlg.cxx2254
-rw-r--r--vcl/source/window/scrwnd.cxx379
-rw-r--r--vcl/source/window/seleng.cxx422
-rw-r--r--vcl/source/window/settings.cxx255
-rw-r--r--vcl/source/window/split.cxx698
-rw-r--r--vcl/source/window/splitwin.cxx2717
-rw-r--r--vcl/source/window/stacking.cxx1152
-rw-r--r--vcl/source/window/status.cxx1494
-rw-r--r--vcl/source/window/syschild.cxx200
-rw-r--r--vcl/source/window/syswin.cxx1171
-rw-r--r--vcl/source/window/tabdlg.cxx184
-rw-r--r--vcl/source/window/tabpage.cxx309
-rw-r--r--vcl/source/window/taskpanelist.cxx287
-rw-r--r--vcl/source/window/toolbox.cxx4810
-rw-r--r--vcl/source/window/toolbox2.cxx1757
-rw-r--r--vcl/source/window/window.cxx3979
-rw-r--r--vcl/source/window/window2.cxx2059
-rw-r--r--vcl/source/window/window3.cxx220
-rw-r--r--vcl/source/window/winproc.cxx2948
-rw-r--r--vcl/source/window/wrkwin.cxx271
69 files changed, 60488 insertions, 0 deletions
diff --git a/vcl/source/window/DocWindow.cxx b/vcl/source/window/DocWindow.cxx
new file mode 100644
index 0000000000..fe2ce61dfb
--- /dev/null
+++ b/vcl/source/window/DocWindow.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/.
+ */
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <sal/log.hxx>
+#include <vcl/DocWindow.hxx>
+#include <vcl/ITiledRenderable.hxx>
+
+namespace vcl
+{
+void DocWindow::SetPointer(PointerStyle nPointer)
+{
+ Window::SetPointer(nPointer);
+
+ VclPtr<vcl::Window> pWin = GetParentWithLOKNotifier();
+ if (!pWin)
+ return;
+
+ PointerStyle aPointer = GetPointer();
+ // We don't map all possible pointers hence we need a default
+ OString aPointerString = "default"_ostr;
+ auto aIt = vcl::gaLOKPointerMap.find(aPointer);
+ if (aIt != vcl::gaLOKPointerMap.end())
+ {
+ aPointerString = aIt->second;
+ }
+
+ pWin->GetLOKNotifier()->libreOfficeKitViewCallback(LOK_CALLBACK_MOUSE_POINTER, aPointerString);
+}
+} // namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/EnumContext.cxx b/vcl/source/window/EnumContext.cxx
new file mode 100644
index 0000000000..ffe0655682
--- /dev/null
+++ b/vcl/source/window/EnumContext.cxx
@@ -0,0 +1,220 @@
+/* -*- 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 <vcl/EnumContext.hxx>
+
+#include <osl/diagnose.h>
+#include <o3tl/enumarray.hxx>
+
+#include <map>
+
+namespace vcl {
+
+namespace {
+
+typedef ::std::map<OUString,EnumContext::Application> ApplicationMap;
+
+ApplicationMap maApplicationMap;
+o3tl::enumarray<EnumContext::Application, OUString> maApplicationVector;
+
+typedef ::std::map<OUString,EnumContext::Context> ContextMap;
+
+ContextMap maContextMap;
+o3tl::enumarray<EnumContext::Context, OUString> maContextVector;
+
+}
+
+const sal_Int32 EnumContext::NoMatch = 4;
+
+EnumContext::EnumContext()
+ : meApplication(Application::NONE),
+ meContext(Context::Unknown)
+{
+}
+
+EnumContext::EnumContext (
+ const Application eApplication,
+ const Context eContext)
+ : meApplication(eApplication),
+ meContext(eContext)
+{
+}
+
+sal_Int32 EnumContext::GetCombinedContext_DI() const
+{
+ return CombinedEnumContext(GetApplication_DI(), meContext);
+}
+
+EnumContext::Application EnumContext::GetApplication() const
+{
+ return meApplication;
+}
+
+EnumContext::Application EnumContext::GetApplication_DI() const
+{
+ switch (meApplication)
+ {
+ case Application::Draw:
+ case Application::Impress:
+ return Application::DrawImpress;
+
+ case Application::Writer:
+ case Application::WriterGlobal:
+ case Application::WriterWeb:
+ case Application::WriterXML:
+ case Application::WriterForm:
+ case Application::WriterReport:
+ return Application::WriterVariants;
+
+ default:
+ return meApplication;
+ }
+}
+
+bool EnumContext::operator== (const EnumContext& rOther) const
+{
+ return meApplication==rOther.meApplication
+ && meContext==rOther.meContext;
+}
+
+bool EnumContext::operator!= (const EnumContext& rOther) const
+{
+ return meApplication!=rOther.meApplication
+ || meContext!=rOther.meContext;
+}
+
+void EnumContext::AddEntry (const OUString& rsName, const Application eApplication)
+{
+ maApplicationMap[rsName] = eApplication;
+ OSL_ASSERT(eApplication<=Application::LAST);
+ maApplicationVector[eApplication]=rsName;
+}
+
+void EnumContext::ProvideApplicationContainers()
+{
+ if (!maApplicationMap.empty())
+ return;
+
+ AddEntry("com.sun.star.text.TextDocument", EnumContext::Application::Writer);
+ AddEntry("com.sun.star.text.GlobalDocument", EnumContext::Application::WriterGlobal);
+ AddEntry("com.sun.star.text.WebDocument", EnumContext::Application::WriterWeb);
+ AddEntry("com.sun.star.xforms.XMLFormDocument", EnumContext::Application::WriterXML);
+ AddEntry("com.sun.star.sdb.FormDesign", EnumContext::Application::WriterForm);
+ AddEntry("com.sun.star.sdb.TextReportDesign", EnumContext::Application::WriterReport);
+ AddEntry("com.sun.star.sheet.SpreadsheetDocument", EnumContext::Application::Calc);
+ AddEntry("com.sun.star.chart2.ChartDocument", EnumContext::Application::Chart);
+ AddEntry("com.sun.star.drawing.DrawingDocument", EnumContext::Application::Draw);
+ AddEntry("com.sun.star.presentation.PresentationDocument", EnumContext::Application::Impress);
+ AddEntry("com.sun.star.formula.FormulaProperties", EnumContext::Application::Formula);
+ AddEntry("com.sun.star.sdb.OfficeDatabaseDocument", EnumContext::Application::Base);
+ AddEntry("any", EnumContext::Application::Any);
+ AddEntry("none", EnumContext::Application::NONE);
+
+}
+
+EnumContext::Application EnumContext::GetApplicationEnum (const OUString& rsApplicationName)
+{
+ ProvideApplicationContainers();
+
+ ApplicationMap::const_iterator iApplication(
+ maApplicationMap.find(rsApplicationName));
+ if (iApplication != maApplicationMap.end())
+ return iApplication->second;
+ else
+ return EnumContext::Application::NONE;
+}
+
+const OUString& EnumContext::GetApplicationName (const Application eApplication)
+{
+ ProvideApplicationContainers();
+ return maApplicationVector[eApplication];
+}
+
+void EnumContext::AddEntry (const OUString& rsName, const Context eContext)
+{
+ maContextMap[rsName] = eContext;
+ maContextVector[eContext] = rsName;
+}
+
+void EnumContext::ProvideContextContainers()
+{
+ if (!maContextMap.empty())
+ return;
+
+ AddEntry("3DObject", Context::ThreeDObject);
+ AddEntry("Annotation", Context::Annotation);
+ AddEntry("Auditing", Context::Auditing);
+ AddEntry("Axis", Context::Axis);
+ AddEntry("Cell", Context::Cell);
+ AddEntry("Chart", Context::Chart);
+ AddEntry("ChartElements", Context::ChartElements);
+ AddEntry("Draw", Context::Draw);
+ AddEntry("DrawFontwork", Context::DrawFontwork);
+ AddEntry("DrawLine", Context::DrawLine);
+ AddEntry("DrawPage", Context::DrawPage);
+ AddEntry("DrawText", Context::DrawText);
+ AddEntry("EditCell", Context::EditCell);
+ AddEntry("ErrorBar", Context::ErrorBar);
+ AddEntry("Form", Context::Form);
+ AddEntry("Frame", Context::Frame);
+ AddEntry("Graphic", Context::Graphic);
+ AddEntry("Grid", Context::Grid);
+ AddEntry("HandoutPage", Context::HandoutPage);
+ AddEntry("MasterPage", Context::MasterPage);
+ AddEntry("Math", Context::Math);
+ AddEntry("Media", Context::Media);
+ AddEntry("MultiObject", Context::MultiObject);
+ AddEntry("NotesPage", Context::NotesPage);
+ AddEntry("OLE", Context::OLE);
+ AddEntry("OutlineText", Context::OutlineText);
+ AddEntry("Pivot", Context::Pivot);
+ AddEntry("Printpreview", Context::Printpreview);
+ AddEntry("Series", Context::Series);
+ AddEntry("SlidesorterPage", Context::SlidesorterPage);
+ AddEntry("Table", Context::Table);
+ AddEntry("Text", Context::Text);
+ AddEntry("TextObject", Context::TextObject);
+ AddEntry("Trendline", Context::Trendline);
+ AddEntry("Sparkline", Context::Sparkline);
+
+ // other general contexts
+ AddEntry("any", Context::Any);
+ AddEntry("default", Context::Default);
+ AddEntry("empty", Context::Empty);
+}
+
+EnumContext::Context EnumContext::GetContextEnum (const OUString& rsContextName)
+{
+ ProvideContextContainers();
+
+ ContextMap::const_iterator iContext( maContextMap.find(rsContextName) );
+ if (iContext != maContextMap.end())
+ return iContext->second;
+ else
+ return EnumContext::Context::Unknown;
+}
+
+const OUString& EnumContext::GetContextName (const Context eContext)
+{
+ ProvideContextContainers();
+ return maContextVector[eContext];
+}
+
+} // end of namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/NotebookBarAddonsMerger.cxx b/vcl/source/window/NotebookBarAddonsMerger.cxx
new file mode 100644
index 0000000000..d8cdfebf85
--- /dev/null
+++ b/vcl/source/window/NotebookBarAddonsMerger.cxx
@@ -0,0 +1,183 @@
+/* -*- 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 <cstddef>
+
+#include <vcl/notebookbar/NotebookBarAddonsMerger.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/vclenum.hxx>
+#include <vcl/toolbox.hxx>
+#include <IPrioritable.hxx>
+#include <OptionalBox.hxx>
+
+const char STYLE_TEXT[] = "Text";
+const char STYLE_ICON[] = "Icon";
+
+const char MERGE_NOTEBOOKBAR_URL[] = "URL";
+const char MERGE_NOTEBOOKBAR_TITLE[] = "Title";
+const char MERGE_NOTEBOOKBAR_CONTEXT[] = "Context";
+const char MERGE_NOTEBOOKBAR_TARGET[] = "Target";
+const char MERGE_NOTEBOOKBAR_CONTROLTYPE[] = "ControlType";
+const char MERGE_NOTEBOOKBAR_WIDTH[] = "Width";
+const char MERGE_NOTEBOOKBAR_STYLE[] = "Style";
+
+static void GetAddonNotebookBarItem(const css::uno::Sequence<css::beans::PropertyValue>& pExtension,
+ AddonNotebookBarItem& aAddonNotebookBarItem)
+{
+ for (const auto& i : pExtension)
+ {
+ if (i.Name == MERGE_NOTEBOOKBAR_URL)
+ i.Value >>= aAddonNotebookBarItem.sCommandURL;
+ else if (i.Name == MERGE_NOTEBOOKBAR_TITLE)
+ i.Value >>= aAddonNotebookBarItem.sLabel;
+ else if (i.Name == MERGE_NOTEBOOKBAR_CONTEXT)
+ i.Value >>= aAddonNotebookBarItem.sContext;
+ else if (i.Name == MERGE_NOTEBOOKBAR_TARGET)
+ i.Value >>= aAddonNotebookBarItem.sTarget;
+ else if (i.Name == MERGE_NOTEBOOKBAR_CONTROLTYPE)
+ i.Value >>= aAddonNotebookBarItem.sControlType;
+ else if (i.Name == MERGE_NOTEBOOKBAR_WIDTH)
+ i.Value >>= aAddonNotebookBarItem.nWidth;
+ else if (i.Name == MERGE_NOTEBOOKBAR_STYLE)
+ i.Value >>= aAddonNotebookBarItem.sStyle;
+ }
+}
+
+static void CreateNotebookBarToolBox(vcl::Window* pNotebookbarToolBox,
+ const css::uno::Reference<css::frame::XFrame>& m_xFrame,
+ const AddonNotebookBarItem& aAddonNotebookBarItem,
+ const std::vector<Image>& aImageVec, const tools::ULong nIter)
+{
+ ToolBoxItemId nItemId(0);
+ ToolBox* pToolbox = dynamic_cast<ToolBox*>(pNotebookbarToolBox);
+ if (!pToolbox)
+ return;
+
+ pToolbox->InsertSeparator();
+ pToolbox->Show();
+ Size aSize(0, 0);
+ Image sImage;
+ pToolbox->InsertItem(aAddonNotebookBarItem.sCommandURL, m_xFrame, ToolBoxItemBits::NONE, aSize);
+ nItemId = pToolbox->GetItemId(aAddonNotebookBarItem.sCommandURL);
+ pToolbox->SetQuickHelpText(nItemId, aAddonNotebookBarItem.sLabel);
+
+ if (nIter < aImageVec.size())
+ {
+ sImage = aImageVec[nIter];
+ if (!sImage)
+ {
+ sImage = vcl::CommandInfoProvider::GetImageForCommand(aAddonNotebookBarItem.sCommandURL,
+ m_xFrame);
+ }
+ }
+
+ if (aAddonNotebookBarItem.sStyle == STYLE_TEXT)
+ pToolbox->SetItemText(nItemId, aAddonNotebookBarItem.sLabel);
+ else if (aAddonNotebookBarItem.sStyle == STYLE_ICON)
+ pToolbox->SetItemImage(nItemId, sImage);
+ else
+ {
+ pToolbox->SetItemText(nItemId, aAddonNotebookBarItem.sLabel);
+ pToolbox->SetItemImage(nItemId, sImage);
+ }
+ pToolbox->Show();
+}
+
+namespace NotebookBarAddonsMerger
+{
+void MergeNotebookBarAddons(vcl::Window* pParent, const VclBuilder::customMakeWidget& pFunction,
+ const css::uno::Reference<css::frame::XFrame>& m_xFrame,
+ const NotebookBarAddonsItem& aNotebookBarAddonsItem,
+ VclBuilder::stringmap& rMap)
+{
+ std::vector<Image> aImageVec = aNotebookBarAddonsItem.aImageValues;
+ tools::ULong nIter = 0;
+ sal_uInt16 nPriorityIdx = aImageVec.size();
+ css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>> aExtension;
+ for (std::size_t nIdx = 0; nIdx < aNotebookBarAddonsItem.aAddonValues.size(); nIdx++)
+ {
+ aExtension = aNotebookBarAddonsItem.aAddonValues[nIdx];
+
+ for (const css::uno::Sequence<css::beans::PropertyValue>& pExtension :
+ std::as_const(aExtension))
+ {
+ VclPtr<vcl::Window> pOptionalParent;
+ pOptionalParent = VclPtr<OptionalBox>::Create(pParent);
+ pOptionalParent->Show();
+
+ vcl::IPrioritable* pPrioritable
+ = dynamic_cast<vcl::IPrioritable*>(pOptionalParent.get());
+ if (pPrioritable)
+ pPrioritable->SetPriority(nPriorityIdx - nIter);
+
+ VclPtr<vcl::Window> pNotebookbarToolBox;
+ pFunction(pNotebookbarToolBox, pOptionalParent, rMap);
+
+ AddonNotebookBarItem aAddonNotebookBarItem;
+ GetAddonNotebookBarItem(pExtension, aAddonNotebookBarItem);
+
+ CreateNotebookBarToolBox(pNotebookbarToolBox, m_xFrame, aAddonNotebookBarItem,
+ aImageVec, nIter);
+ nIter++;
+ }
+ }
+}
+
+void MergeNotebookBarMenuAddons(Menu* pPopupMenu, sal_Int16 nItemId, const OUString& sItemIdName,
+ NotebookBarAddonsItem& aNotebookBarAddonsItem)
+{
+ std::vector<Image> aImageVec = aNotebookBarAddonsItem.aImageValues;
+ tools::ULong nIter = 0;
+ css::uno::Sequence<css::uno::Sequence<css::beans::PropertyValue>> aExtension;
+ for (std::size_t nIdx = 0; nIdx < aNotebookBarAddonsItem.aAddonValues.size(); nIdx++)
+ {
+ aExtension = aNotebookBarAddonsItem.aAddonValues[nIdx];
+
+ for (int nSecIdx = 0; nSecIdx < aExtension.getLength(); nSecIdx++)
+ {
+ AddonNotebookBarItem aAddonNotebookBarItem;
+ Image sImage;
+ MenuItemBits nBits = MenuItemBits::ICON;
+ const css::uno::Sequence<css::beans::PropertyValue> pExtension = aExtension[nSecIdx];
+
+ GetAddonNotebookBarItem(pExtension, aAddonNotebookBarItem);
+
+ pPopupMenu->InsertItem(nItemId, aAddonNotebookBarItem.sLabel, nBits, sItemIdName);
+ pPopupMenu->SetItemCommand(nItemId, aAddonNotebookBarItem.sCommandURL);
+
+ if (nIter < aImageVec.size())
+ {
+ sImage = aImageVec[nIter];
+ nIter++;
+ }
+ pPopupMenu->SetItemImage(nItemId, sImage);
+
+ if (nSecIdx == aExtension.getLength() - 1)
+ pPopupMenu->InsertSeparator();
+
+ ++nItemId;
+ }
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/OptionalBox.cxx b/vcl/source/window/OptionalBox.cxx
new file mode 100644
index 0000000000..28055f7e21
--- /dev/null
+++ b/vcl/source/window/OptionalBox.cxx
@@ -0,0 +1,62 @@
+/* -*- 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 <vcl/layout.hxx>
+#include <OptionalBox.hxx>
+
+/*
+ * OptionalBox - shows or hides the content. To use with PriorityHBox
+ * or PriorityMergedHBox
+ */
+
+OptionalBox::OptionalBox(vcl::Window* pParent)
+ : VclHBox(pParent)
+ , m_bInFullView(true)
+{
+}
+
+OptionalBox::~OptionalBox() { disposeOnce(); }
+
+void OptionalBox::HideContent()
+{
+ if (m_bInFullView)
+ {
+ m_bInFullView = false;
+
+ for (int i = 0; i < GetChildCount(); i++)
+ GetChild(i)->Hide();
+
+ SetOutputSizePixel(Size(10, GetSizePixel().Height()));
+ }
+}
+
+void OptionalBox::ShowContent()
+{
+ if (!m_bInFullView)
+ {
+ m_bInFullView = true;
+
+ for (int i = 0; i < GetChildCount(); i++)
+ GetChild(i)->Show();
+ }
+}
+
+bool OptionalBox::IsHidden() { return !m_bInFullView; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/abstdlg.cxx b/vcl/source/window/abstdlg.cxx
new file mode 100644
index 0000000000..5bacc91b37
--- /dev/null
+++ b/vcl/source/window/abstdlg.cxx
@@ -0,0 +1,85 @@
+/* -*- 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/module.hxx>
+#include <vcl/abstdlg.hxx>
+#include <vcl/bitmapex.hxx>
+
+typedef VclAbstractDialogFactory*(SAL_CALL* FuncPtrCreateDialogFactory)();
+
+#ifndef DISABLE_DYNLOADING
+extern "C" {
+static void thisModule() {}
+}
+#else
+extern "C" VclAbstractDialogFactory* CreateDialogFactory();
+#endif
+
+VclAbstractDialogFactory* VclAbstractDialogFactory::Create()
+{
+ static auto fp = []() -> FuncPtrCreateDialogFactory {
+#ifndef DISABLE_DYNLOADING
+ ::osl::Module aDialogLibrary;
+ if (aDialogLibrary.loadRelative(&thisModule, CUI_DLL_NAME,
+ SAL_LOADMODULE_GLOBAL | SAL_LOADMODULE_LAZY))
+ {
+ auto const p = reinterpret_cast<FuncPtrCreateDialogFactory>(
+ aDialogLibrary.getFunctionSymbol("CreateDialogFactory"));
+ aDialogLibrary.release();
+ return p;
+ }
+ return nullptr;
+#else
+ return CreateDialogFactory;
+#endif
+ }();
+ if (fp)
+ return fp();
+ return nullptr;
+}
+
+VclAbstractDialog::~VclAbstractDialog() {}
+
+bool VclAbstractDialog::StartExecuteAsync(AsyncContext&)
+{
+ assert(false);
+ return false;
+}
+
+std::vector<OUString> VclAbstractDialog::getAllPageUIXMLDescriptions() const
+{
+ // default has no pages
+ return {};
+}
+
+bool VclAbstractDialog::selectPageByUIXMLDescription(const OUString& /*rUIXMLDescription*/)
+{
+ // default cannot select a page (which is okay, return true)
+ return true;
+}
+
+BitmapEx VclAbstractDialog::createScreenshot() const
+{
+ // default returns empty bitmap
+ return BitmapEx();
+}
+
+VclAbstractDialogFactory::~VclAbstractDialogFactory() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/accel.cxx b/vcl/source/window/accel.cxx
new file mode 100644
index 0000000000..a20c289f20
--- /dev/null
+++ b/vcl/source/window/accel.cxx
@@ -0,0 +1,279 @@
+/* -*- 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/log.hxx>
+#include <osl/diagnose.h>
+#include <accel.hxx>
+
+#define ACCELENTRY_NOTFOUND (sal_uInt16(0xFFFF))
+
+static sal_uInt16 ImplAccelEntryGetIndex( const ImplAccelList* pList, sal_uInt16 nId,
+ sal_uInt16* pIndex = nullptr )
+{
+ size_t nLow;
+ size_t nHigh;
+ size_t nMid;
+ size_t nCount = pList->size();
+ sal_uInt16 nCompareId;
+
+ // check if first key is larger then the key to compare
+ if ( !nCount || (nId < (*pList)[ 0 ]->mnId) )
+ {
+ if ( pIndex )
+ *pIndex = 0;
+ return ACCELENTRY_NOTFOUND;
+ }
+
+ // Binary search
+ nLow = 0;
+ nHigh = nCount-1;
+ do
+ {
+ nMid = (nLow + nHigh) / 2;
+ nCompareId = (*pList)[ nMid ]->mnId;
+ if ( nId < nCompareId )
+ nHigh = nMid-1;
+ else
+ {
+ if ( nId > nCompareId )
+ nLow = nMid + 1;
+ else
+ return static_cast<sal_uInt16>(nMid);
+ }
+ }
+ while ( nLow <= nHigh );
+
+ if ( pIndex )
+ {
+ if ( nId > nCompareId )
+ *pIndex = static_cast<sal_uInt16>(nMid+1);
+ else
+ *pIndex = static_cast<sal_uInt16>(nMid);
+ }
+
+ return ACCELENTRY_NOTFOUND;
+}
+
+static void ImplAccelEntryInsert( ImplAccelList* pList, std::unique_ptr<ImplAccelEntry> pEntry )
+{
+ sal_uInt16 nInsIndex(0);
+ std::vector<ImplAccelEntry *>::size_type nIndex = ImplAccelEntryGetIndex( pList, pEntry->mnId, &nInsIndex );
+
+ if ( nIndex != ACCELENTRY_NOTFOUND )
+ {
+ do
+ {
+ nIndex++;
+ ImplAccelEntry* pTempEntry = nullptr;
+ if ( nIndex < pList->size() )
+ pTempEntry = (*pList)[ nIndex ].get();
+ if ( !pTempEntry || (pTempEntry->mnId != pEntry->mnId) )
+ break;
+ }
+ while ( nIndex < pList->size() );
+
+ if ( nIndex < pList->size() ) {
+ pList->insert( pList->begin() + nIndex, std::move(pEntry) );
+ } else {
+ pList->push_back( std::move(pEntry) );
+ }
+ }
+ else {
+ if ( nInsIndex < pList->size() ) {
+ pList->insert( pList->begin() + nInsIndex, std::move(pEntry) );
+ } else {
+ pList->push_back( std::move(pEntry) );
+ }
+ }
+}
+
+void Accelerator::ImplInit()
+{
+ mnCurId = 0;
+ mpDel = nullptr;
+}
+
+ImplAccelEntry* Accelerator::ImplGetAccelData( const vcl::KeyCode& rKeyCode ) const
+{
+ auto it = maKeyMap.find( rKeyCode.GetFullCode() );
+ if( it != maKeyMap.end() )
+ return it->second;
+ else
+ return nullptr;
+}
+
+void Accelerator::ImplCopyData( const Accelerator& rAccelData )
+{
+ // copy table
+ for (const std::unique_ptr<ImplAccelEntry>& i : rAccelData.maIdList)
+ {
+ std::unique_ptr<ImplAccelEntry> pEntry(new ImplAccelEntry( *i ));
+
+ // sequence accelerator, then copy also
+ if ( pEntry->mpAccel )
+ {
+ pEntry->mpAccel = new Accelerator( *(pEntry->mpAccel) );
+ pEntry->mpAutoAccel = pEntry->mpAccel;
+ }
+ else
+ pEntry->mpAutoAccel = nullptr;
+
+ maKeyMap.insert( std::make_pair( pEntry->maKeyCode.GetFullCode(), pEntry.get() ) );
+ maIdList.push_back( std::move(pEntry) );
+ }
+}
+
+void Accelerator::ImplDeleteData()
+{
+ // delete accelerator-entries using the id-table
+ for (const std::unique_ptr<ImplAccelEntry>& pEntry : maIdList) {
+ delete pEntry->mpAutoAccel;
+ }
+ maIdList.clear();
+}
+
+void Accelerator::ImplInsertAccel( sal_uInt16 nItemId, const vcl::KeyCode& rKeyCode,
+ bool bEnable, Accelerator* pAutoAccel )
+{
+ SAL_WARN_IF( !nItemId, "vcl", "Accelerator::InsertItem(): ItemId == 0" );
+
+ if ( rKeyCode.IsFunction() )
+ {
+ sal_uInt16 nCode1;
+ sal_uInt16 nCode2;
+ sal_uInt16 nCode3;
+ sal_uInt16 nCode4;
+ ImplGetKeyCode( rKeyCode.GetFunction(), nCode1, nCode2, nCode3, nCode4 );
+ if ( nCode1 )
+ ImplInsertAccel( nItemId, vcl::KeyCode( nCode1, nCode1 ), bEnable, pAutoAccel );
+ if ( nCode2 )
+ {
+ if ( pAutoAccel )
+ pAutoAccel = new Accelerator( *pAutoAccel );
+ ImplInsertAccel( nItemId, vcl::KeyCode( nCode2, nCode2 ), bEnable, pAutoAccel );
+ if ( nCode3 )
+ {
+ if ( pAutoAccel )
+ pAutoAccel = new Accelerator( *pAutoAccel );
+ ImplInsertAccel( nItemId, vcl::KeyCode( nCode3, nCode3 ), bEnable, pAutoAccel );
+ }
+ }
+ return;
+ }
+
+ // fetch and fill new entries
+ std::unique_ptr<ImplAccelEntry> pEntry(new ImplAccelEntry);
+ pEntry->mnId = nItemId;
+ pEntry->maKeyCode = rKeyCode;
+ pEntry->mpAccel = pAutoAccel;
+ pEntry->mpAutoAccel = pAutoAccel;
+ pEntry->mbEnabled = bEnable;
+
+ // now into the tables
+ sal_uLong nCode = rKeyCode.GetFullCode();
+ if ( !nCode )
+ {
+ OSL_FAIL( "Accelerator::InsertItem(): KeyCode with KeyCode 0 not allowed" );
+ }
+ else if ( !maKeyMap.insert( std::make_pair( nCode, pEntry.get() ) ).second )
+ {
+ SAL_WARN( "vcl", "Accelerator::InsertItem(): KeyCode (Key: " << nCode << ") already exists" );
+ }
+ else
+ ImplAccelEntryInsert( &maIdList, std::move(pEntry) );
+}
+
+Accelerator::Accelerator()
+{
+ ImplInit();
+}
+
+Accelerator::Accelerator(const Accelerator& rAccel)
+{
+ ImplInit();
+ ImplCopyData(rAccel);
+}
+
+Accelerator::~Accelerator()
+{
+
+ // inform AccelManager about deleting the Accelerator
+ if ( mpDel )
+ *mpDel = true;
+
+ ImplDeleteData();
+}
+
+void Accelerator::Activate()
+{
+ maActivateHdl.Call( *this );
+}
+
+void Accelerator::Select()
+{
+ maSelectHdl.Call( *this );
+}
+
+void Accelerator::InsertItem( sal_uInt16 nItemId, const vcl::KeyCode& rKeyCode )
+{
+ ImplInsertAccel( nItemId, rKeyCode, true, nullptr );
+}
+
+sal_uInt16 Accelerator::GetItemCount() const
+{
+ return static_cast<sal_uInt16>(maIdList.size());
+}
+
+sal_uInt16 Accelerator::GetItemId( sal_uInt16 nPos ) const
+{
+
+ ImplAccelEntry* pEntry = ( nPos < maIdList.size() ) ? maIdList[ nPos ].get() : nullptr;
+ if ( pEntry )
+ return pEntry->mnId;
+ else
+ return 0;
+}
+
+Accelerator* Accelerator::GetAccel( sal_uInt16 nItemId ) const
+{
+
+ sal_uInt16 nIndex = ImplAccelEntryGetIndex( &maIdList, nItemId );
+ if ( nIndex != ACCELENTRY_NOTFOUND )
+ return maIdList[ nIndex ]->mpAccel;
+ else
+ return nullptr;
+}
+
+Accelerator& Accelerator::operator=( const Accelerator& rAccel )
+{
+ if(this == &rAccel)
+ return *this;
+
+ // assign new data
+ mnCurId = 0;
+
+ // delete and copy tables
+ ImplDeleteData();
+ maKeyMap.clear();
+ ImplCopyData(rAccel);
+
+ return *this;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/accessibility.cxx b/vcl/source/window/accessibility.cxx
new file mode 100644
index 0000000000..3c6103ac31
--- /dev/null
+++ b/vcl/source/window/accessibility.cxx
@@ -0,0 +1,594 @@
+/* -*- 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 <vcl/layout.hxx>
+#include <vcl/toolkit/fixed.hxx>
+#include <vcl/window.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/wrkwin.hxx>
+
+#include <window.h>
+#include <brdwin.hxx>
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::datatransfer::clipboard;
+using namespace ::com::sun::star::datatransfer::dnd;
+using namespace ::com::sun::star;
+
+
+ImplAccessibleInfos::ImplAccessibleInfos()
+{
+ nAccessibleRole = 0xFFFF;
+ pLabeledByWindow = nullptr;
+ pLabelForWindow = nullptr;
+}
+
+ImplAccessibleInfos::~ImplAccessibleInfos()
+{
+}
+
+namespace vcl {
+
+css::uno::Reference< css::accessibility::XAccessible > Window::GetAccessible( bool bCreate )
+{
+ // do not optimize hierarchy for the top level border win (ie, when there is no parent)
+ /* // do not optimize accessible hierarchy at all to better reflect real VCL hierarchy
+ if ( GetParent() && ( GetType() == WindowType::BORDERWINDOW ) && ( GetChildCount() == 1 ) )
+ //if( !ImplIsAccessibleCandidate() )
+ {
+ vcl::Window* pChild = GetAccessibleChildWindow( 0 );
+ if ( pChild )
+ return pChild->GetAccessible();
+ }
+ */
+ if ( !mpWindowImpl )
+ return css::uno::Reference< css::accessibility::XAccessible >();
+ if ( !mpWindowImpl->mxAccessible.is() && bCreate )
+ mpWindowImpl->mxAccessible = CreateAccessible();
+
+ return mpWindowImpl->mxAccessible;
+}
+
+css::uno::Reference< css::accessibility::XAccessible > Window::CreateAccessible()
+{
+ css::uno::Reference< css::accessibility::XAccessible > xAcc( GetComponentInterface(), css::uno::UNO_QUERY );
+ return xAcc;
+}
+
+void Window::SetAccessible( const css::uno::Reference< css::accessibility::XAccessible >& x )
+{
+ if (!mpWindowImpl)
+ return;
+
+ mpWindowImpl->mxAccessible = x;
+}
+
+// skip all border windows that are not top level frames
+bool Window::ImplIsAccessibleCandidate() const
+{
+ if( !mpWindowImpl->mbBorderWin )
+ return true;
+
+ return IsNativeFrame();
+}
+
+vcl::Window* Window::GetAccessibleParentWindow() const
+{
+ if (!mpWindowImpl || IsNativeFrame())
+ return nullptr;
+
+ vcl::Window* pParent = mpWindowImpl->mpParent;
+ if( GetType() == WindowType::MENUBARWINDOW )
+ {
+ // report the menubar as a child of THE workwindow
+ vcl::Window *pWorkWin = GetParent()->mpWindowImpl->mpFirstChild;
+ while( pWorkWin && (pWorkWin == this) )
+ pWorkWin = pWorkWin->mpWindowImpl->mpNext;
+ pParent = pWorkWin;
+ }
+ // If this is a floating window which has a native border window, then that border should be reported as
+ // the accessible parent
+ else if( GetType() == WindowType::FLOATINGWINDOW &&
+ mpWindowImpl->mpBorderWindow && mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame )
+ {
+ pParent = mpWindowImpl->mpBorderWindow;
+ }
+ else if( pParent && !pParent->ImplIsAccessibleCandidate() )
+ {
+ pParent = pParent->mpWindowImpl->mpParent;
+ }
+ return pParent;
+}
+
+sal_uInt16 Window::GetAccessibleChildWindowCount()
+{
+ if (!mpWindowImpl)
+ return 0;
+
+ sal_uInt16 nChildren = 0;
+ vcl::Window* pChild = mpWindowImpl->mpFirstChild;
+ while( pChild )
+ {
+ if( pChild->IsVisible() )
+ nChildren++;
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+
+ // report the menubarwindow as a child of THE workwindow
+ if( GetType() == WindowType::BORDERWINDOW )
+ {
+ ImplBorderWindow *pBorderWindow = static_cast<ImplBorderWindow*>(this);
+ if( pBorderWindow->mpMenuBarWindow &&
+ pBorderWindow->mpMenuBarWindow->IsVisible()
+ )
+ --nChildren;
+ }
+ else if( GetType() == WindowType::WORKWINDOW )
+ {
+ WorkWindow *pWorkWindow = static_cast<WorkWindow*>(this);
+ if( pWorkWindow->GetMenuBar() &&
+ pWorkWindow->GetMenuBar()->GetWindow() &&
+ pWorkWindow->GetMenuBar()->GetWindow()->IsVisible()
+ )
+ ++nChildren;
+ }
+
+ return nChildren;
+}
+
+vcl::Window* Window::GetAccessibleChildWindow( sal_uInt16 n )
+{
+ // report the menubarwindow as the first child of THE workwindow
+ if( GetType() == WindowType::WORKWINDOW && static_cast<WorkWindow *>(this)->GetMenuBar() )
+ {
+ if( n == 0 )
+ {
+ MenuBar *pMenuBar = static_cast<WorkWindow *>(this)->GetMenuBar();
+ if( pMenuBar->GetWindow() && pMenuBar->GetWindow()->IsVisible() )
+ return pMenuBar->GetWindow();
+ }
+ else
+ --n;
+ }
+
+ // transform n to child number including invisible children
+ sal_uInt16 nChildren = n;
+ vcl::Window* pChild = mpWindowImpl->mpFirstChild;
+ while( pChild )
+ {
+ if( pChild->IsVisible() )
+ {
+ if( ! nChildren )
+ break;
+ nChildren--;
+ }
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+
+ if( GetType() == WindowType::BORDERWINDOW && pChild && pChild->GetType() == WindowType::MENUBARWINDOW )
+ {
+ do pChild = pChild->mpWindowImpl->mpNext; while( pChild && ! pChild->IsVisible() );
+ SAL_WARN_IF( !pChild, "vcl", "GetAccessibleChildWindow(): wrong index in border window");
+ }
+
+ if ( pChild && ( pChild->GetType() == WindowType::BORDERWINDOW ) && ( pChild->GetChildCount() == 1 ) )
+ {
+ pChild = pChild->GetChild( 0 );
+ }
+ return pChild;
+}
+
+void Window::SetAccessibleRole( sal_uInt16 nRole )
+{
+ if ( !mpWindowImpl->mpAccessibleInfos )
+ mpWindowImpl->mpAccessibleInfos.reset( new ImplAccessibleInfos );
+
+ SAL_WARN_IF( mpWindowImpl->mpAccessibleInfos->nAccessibleRole != 0xFFFF, "vcl", "AccessibleRole already set!" );
+ mpWindowImpl->mpAccessibleInfos->nAccessibleRole = nRole;
+}
+
+sal_uInt16 Window::getDefaultAccessibleRole() const
+{
+ sal_uInt16 nRole = 0xFFFF;
+ switch ( GetType() )
+ {
+ case WindowType::MESSBOX: // MT: Would be nice to have special roles!
+ case WindowType::INFOBOX:
+ case WindowType::WARNINGBOX:
+ case WindowType::ERRORBOX:
+ case WindowType::QUERYBOX: nRole = accessibility::AccessibleRole::ALERT; break;
+
+ case WindowType::MODELESSDIALOG:
+ case WindowType::TABDIALOG:
+ case WindowType::BUTTONDIALOG:
+ case WindowType::DIALOG: nRole = accessibility::AccessibleRole::DIALOG; break;
+
+ case WindowType::PUSHBUTTON:
+ case WindowType::OKBUTTON:
+ case WindowType::CANCELBUTTON:
+ case WindowType::HELPBUTTON:
+ case WindowType::IMAGEBUTTON:
+ case WindowType::MOREBUTTON: nRole = accessibility::AccessibleRole::PUSH_BUTTON; break;
+ case WindowType::MENUBUTTON: nRole = accessibility::AccessibleRole::BUTTON_MENU; break;
+
+ case WindowType::RADIOBUTTON: nRole = accessibility::AccessibleRole::RADIO_BUTTON; break;
+ case WindowType::TRISTATEBOX:
+ case WindowType::CHECKBOX: nRole = accessibility::AccessibleRole::CHECK_BOX; break;
+
+ case WindowType::MULTILINEEDIT: nRole = accessibility::AccessibleRole::SCROLL_PANE; break;
+
+ case WindowType::PATTERNFIELD:
+ case WindowType::EDIT: nRole = static_cast<Edit const *>(this)->IsPassword() ? accessibility::AccessibleRole::PASSWORD_TEXT : accessibility::AccessibleRole::TEXT; break;
+
+ case WindowType::PATTERNBOX:
+ case WindowType::NUMERICBOX:
+ case WindowType::METRICBOX:
+ case WindowType::CURRENCYBOX:
+ case WindowType::LONGCURRENCYBOX:
+ case WindowType::COMBOBOX: nRole = accessibility::AccessibleRole::COMBO_BOX; break;
+
+ case WindowType::LISTBOX:
+ case WindowType::MULTILISTBOX: nRole = accessibility::AccessibleRole::LIST; break;
+
+ case WindowType::TREELISTBOX: nRole = accessibility::AccessibleRole::TREE; break;
+
+ case WindowType::FIXEDTEXT: nRole = accessibility::AccessibleRole::LABEL; break;
+ case WindowType::FIXEDLINE:
+ if( !GetText().isEmpty() )
+ nRole = accessibility::AccessibleRole::LABEL;
+ else
+ nRole = accessibility::AccessibleRole::SEPARATOR;
+ break;
+
+ case WindowType::FIXEDBITMAP:
+ case WindowType::FIXEDIMAGE: nRole = accessibility::AccessibleRole::ICON; break;
+ case WindowType::GROUPBOX: nRole = accessibility::AccessibleRole::GROUP_BOX; break;
+ case WindowType::SCROLLBAR: nRole = accessibility::AccessibleRole::SCROLL_BAR; break;
+
+ case WindowType::SLIDER:
+ case WindowType::SPLITTER:
+ case WindowType::SPLITWINDOW: nRole = accessibility::AccessibleRole::SPLIT_PANE; break;
+
+ case WindowType::DATEBOX:
+ case WindowType::TIMEBOX:
+ case WindowType::DATEFIELD:
+ case WindowType::TIMEFIELD: nRole = accessibility::AccessibleRole::DATE_EDITOR; break;
+
+ case WindowType::METRICFIELD:
+ case WindowType::CURRENCYFIELD:
+ case WindowType::SPINBUTTON:
+ case WindowType::SPINFIELD:
+ case WindowType::FORMATTEDFIELD: nRole = accessibility::AccessibleRole::SPIN_BOX; break;
+
+ case WindowType::TOOLBOX: nRole = accessibility::AccessibleRole::TOOL_BAR; break;
+ case WindowType::STATUSBAR: nRole = accessibility::AccessibleRole::STATUS_BAR; break;
+
+ case WindowType::TABPAGE: nRole = accessibility::AccessibleRole::PANEL; break;
+ case WindowType::TABCONTROL: nRole = accessibility::AccessibleRole::PAGE_TAB_LIST; break;
+
+ case WindowType::DOCKINGWINDOW: nRole = (mpWindowImpl->mbFrame) ? accessibility::AccessibleRole::FRAME :
+ accessibility::AccessibleRole::PANEL; break;
+
+ case WindowType::FLOATINGWINDOW: nRole = ( mpWindowImpl->mbFrame ||
+ (mpWindowImpl->mpBorderWindow && mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame) ||
+ (GetStyle() & WB_OWNERDRAWDECORATION) ) ? accessibility::AccessibleRole::FRAME :
+ accessibility::AccessibleRole::WINDOW; break;
+
+ case WindowType::WORKWINDOW: nRole = accessibility::AccessibleRole::ROOT_PANE; break;
+
+ case WindowType::SCROLLBARBOX: nRole = accessibility::AccessibleRole::FILLER; break;
+
+ case WindowType::HELPTEXTWINDOW: nRole = accessibility::AccessibleRole::TOOL_TIP; break;
+
+ case WindowType::RULER: nRole = accessibility::AccessibleRole::RULER; break;
+
+ case WindowType::SCROLLWINDOW: nRole = accessibility::AccessibleRole::SCROLL_PANE; break;
+
+ case WindowType::WINDOW:
+ case WindowType::CONTROL:
+ case WindowType::BORDERWINDOW:
+ case WindowType::SYSTEMCHILDWINDOW:
+ default:
+ if (IsNativeFrame() )
+ nRole = accessibility::AccessibleRole::FRAME;
+ else if( IsScrollable() )
+ nRole = accessibility::AccessibleRole::SCROLL_PANE;
+ else if( this->ImplGetWindow()->IsMenuFloatingWindow() )
+ nRole = accessibility::AccessibleRole::WINDOW; // #106002#, contextmenus are windows (i.e. toplevel)
+ else
+ // #104051# WINDOW seems to be a bad default role, use LAYEREDPANE instead
+ // a WINDOW is interpreted as a top-level window, which is typically not the case
+ //nRole = accessibility::AccessibleRole::WINDOW;
+ nRole = accessibility::AccessibleRole::PANEL;
+ }
+ return nRole;
+}
+
+sal_uInt16 Window::GetAccessibleRole() const
+{
+ if (!mpWindowImpl)
+ return 0;
+
+ sal_uInt16 nRole = mpWindowImpl->mpAccessibleInfos ? mpWindowImpl->mpAccessibleInfos->nAccessibleRole : 0xFFFF;
+ if ( nRole == 0xFFFF )
+ nRole = getDefaultAccessibleRole();
+ return nRole;
+}
+
+void Window::SetAccessibleName( const OUString& rName )
+{
+ if ( !mpWindowImpl->mpAccessibleInfos )
+ mpWindowImpl->mpAccessibleInfos.reset( new ImplAccessibleInfos );
+
+ OUString oldName = GetAccessibleName();
+
+ mpWindowImpl->mpAccessibleInfos->pAccessibleName = rName;
+
+ CallEventListeners( VclEventId::WindowFrameTitleChanged, &oldName );
+}
+
+OUString Window::GetAccessibleName() const
+{
+ if (!mpWindowImpl)
+ return OUString();
+
+ if (mpWindowImpl->mpAccessibleInfos && mpWindowImpl->mpAccessibleInfos->pAccessibleName)
+ return *mpWindowImpl->mpAccessibleInfos->pAccessibleName;
+ return getDefaultAccessibleName();
+}
+
+OUString Window::getDefaultAccessibleName() const
+{
+ OUString aAccessibleName;
+ switch ( GetType() )
+ {
+ case WindowType::MULTILINEEDIT:
+ case WindowType::PATTERNFIELD:
+ case WindowType::METRICFIELD:
+ case WindowType::CURRENCYFIELD:
+ case WindowType::EDIT:
+
+ case WindowType::DATEBOX:
+ case WindowType::TIMEBOX:
+ case WindowType::CURRENCYBOX:
+ case WindowType::LONGCURRENCYBOX:
+ case WindowType::DATEFIELD:
+ case WindowType::TIMEFIELD:
+ case WindowType::SPINFIELD:
+ case WindowType::FORMATTEDFIELD:
+
+ case WindowType::COMBOBOX:
+ case WindowType::LISTBOX:
+ case WindowType::MULTILISTBOX:
+ case WindowType::TREELISTBOX:
+ case WindowType::METRICBOX:
+ {
+ vcl::Window *pLabel = GetAccessibleRelationLabeledBy();
+ if ( pLabel && pLabel != this )
+ aAccessibleName = pLabel->GetText();
+ if (aAccessibleName.isEmpty())
+ aAccessibleName = GetQuickHelpText();
+ if (aAccessibleName.isEmpty())
+ aAccessibleName = GetText();
+ }
+ break;
+
+ case WindowType::IMAGEBUTTON:
+ case WindowType::PUSHBUTTON:
+ aAccessibleName = GetText();
+ if (aAccessibleName.isEmpty())
+ {
+ aAccessibleName = GetQuickHelpText();
+ if (aAccessibleName.isEmpty())
+ aAccessibleName = GetHelpText();
+ }
+ break;
+
+ case WindowType::TOOLBOX:
+ aAccessibleName = GetText();
+ break;
+
+ case WindowType::MOREBUTTON:
+ aAccessibleName = mpWindowImpl->maText;
+ break;
+
+ default:
+ aAccessibleName = GetText();
+ break;
+ }
+
+ return removeMnemonicFromString( aAccessibleName );
+}
+
+void Window::SetAccessibleDescription( const OUString& rDescription )
+{
+ if ( ! mpWindowImpl->mpAccessibleInfos )
+ mpWindowImpl->mpAccessibleInfos.reset( new ImplAccessibleInfos );
+
+ std::optional<OUString>& rCurrentDescription = mpWindowImpl->mpAccessibleInfos->pAccessibleDescription;
+ SAL_WARN_IF( rCurrentDescription && *rCurrentDescription != rDescription, "vcl", "AccessibleDescription already set" );
+ rCurrentDescription = rDescription;
+}
+
+OUString Window::GetAccessibleDescription() const
+{
+ if (!mpWindowImpl)
+ return OUString();
+
+ OUString aAccessibleDescription;
+ if ( mpWindowImpl->mpAccessibleInfos && mpWindowImpl->mpAccessibleInfos->pAccessibleDescription )
+ {
+ aAccessibleDescription = *mpWindowImpl->mpAccessibleInfos->pAccessibleDescription;
+ }
+ else
+ {
+ // Special code for help text windows. ZT asks the border window for the
+ // description so we have to forward this request to our inner window.
+ const vcl::Window* pWin = this->ImplGetWindow();
+ if ( pWin->GetType() == WindowType::HELPTEXTWINDOW )
+ aAccessibleDescription = pWin->GetHelpText();
+ else
+ aAccessibleDescription = GetHelpText();
+ }
+
+ return aAccessibleDescription;
+}
+
+void Window::SetAccessibleRelationLabeledBy( vcl::Window* pLabeledBy )
+{
+ if ( !mpWindowImpl->mpAccessibleInfos )
+ mpWindowImpl->mpAccessibleInfos.reset( new ImplAccessibleInfos );
+ mpWindowImpl->mpAccessibleInfos->pLabeledByWindow = pLabeledBy;
+}
+
+void Window::SetAccessibleRelationLabelFor( vcl::Window* pLabelFor )
+{
+ if ( !mpWindowImpl->mpAccessibleInfos )
+ mpWindowImpl->mpAccessibleInfos.reset( new ImplAccessibleInfos );
+ mpWindowImpl->mpAccessibleInfos->pLabelForWindow = pLabelFor;
+}
+
+vcl::Window* Window::GetAccessibleRelationMemberOf() const
+{
+ if (!isContainerWindow(this) && !isContainerWindow(GetParent()))
+ return getLegacyNonLayoutAccessibleRelationMemberOf();
+
+ return nullptr;
+}
+
+vcl::Window* Window::getAccessibleRelationLabelFor() const
+{
+ if (mpWindowImpl->mpAccessibleInfos && mpWindowImpl->mpAccessibleInfos->pLabelForWindow)
+ return mpWindowImpl->mpAccessibleInfos->pLabelForWindow;
+
+ return nullptr;
+}
+
+vcl::Window* Window::GetAccessibleRelationLabelFor() const
+{
+ vcl::Window* pWindow = getAccessibleRelationLabelFor();
+
+ if (pWindow)
+ return pWindow;
+
+ if (!isContainerWindow(this) && !isContainerWindow(GetParent()))
+ return getLegacyNonLayoutAccessibleRelationLabelFor();
+
+ return nullptr;
+}
+
+vcl::Window* Window::GetAccessibleRelationLabeledBy() const
+{
+ if (mpWindowImpl->mpAccessibleInfos && mpWindowImpl->mpAccessibleInfos->pLabeledByWindow)
+ return mpWindowImpl->mpAccessibleInfos->pLabeledByWindow;
+
+ std::vector<VclPtr<FixedText> > aMnemonicLabels(list_mnemonic_labels());
+ if (!aMnemonicLabels.empty())
+ {
+ //if we have multiple labels, then prefer the first that is visible
+ for (auto const & rCandidate : aMnemonicLabels)
+ {
+ if (rCandidate->IsVisible())
+ return rCandidate;
+ }
+ return aMnemonicLabels[0];
+ }
+
+ if (!isContainerWindow(this) && !isContainerWindow(GetParent()))
+ return getLegacyNonLayoutAccessibleRelationLabeledBy();
+
+ return nullptr;
+}
+
+bool Window::IsAccessibilityEventsSuppressed( bool bTraverseParentPath )
+{
+ if( !bTraverseParentPath )
+ return mpWindowImpl->mbSuppressAccessibilityEvents;
+ else
+ {
+ vcl::Window *pParent = this;
+ while ( pParent && pParent->mpWindowImpl)
+ {
+ if( pParent->mpWindowImpl->mbSuppressAccessibilityEvents )
+ return true;
+ else
+ pParent = pParent->mpWindowImpl->mpParent; // do not use GetParent() to find borderwindows that are frames
+ }
+ return false;
+ }
+}
+
+void Window::SetAccessibilityEventsSuppressed(bool bSuppressed)
+{
+ mpWindowImpl->mbSuppressAccessibilityEvents = bSuppressed;
+}
+
+} /* namespace vcl */
+
+uno::Reference<accessibility::XAccessibleEditableText>
+FindFocusedEditableText(uno::Reference<accessibility::XAccessibleContext> const& xContext)
+{
+ if (!xContext.is())
+ return uno::Reference<accessibility::XAccessibleEditableText>();
+
+ sal_Int64 nState = xContext->getAccessibleStateSet();
+ if (nState & accessibility::AccessibleStateType::FOCUSED)
+ {
+ uno::Reference<accessibility::XAccessibleEditableText> xText(xContext, uno::UNO_QUERY);
+ if (xText.is())
+ return xText;
+ if (nState & accessibility::AccessibleStateType::MANAGES_DESCENDANTS)
+ return uno::Reference<accessibility::XAccessibleEditableText>();
+ }
+
+ bool bSafeToIterate = true;
+ sal_Int64 nCount = xContext->getAccessibleChildCount();
+ if (nCount < 0 || nCount > SAL_MAX_UINT16 /* slow enough for anyone */)
+ bSafeToIterate = false;
+ if (!bSafeToIterate)
+ return uno::Reference<accessibility::XAccessibleEditableText>();
+
+ for (sal_Int64 i = 0; i < xContext->getAccessibleChildCount(); ++i)
+ {
+ uno::Reference<accessibility::XAccessible> xChild = xContext->getAccessibleChild(i);
+ if (!xChild.is())
+ continue;
+ uno::Reference<accessibility::XAccessibleContext> xChildContext
+ = xChild->getAccessibleContext();
+ if (!xChildContext.is())
+ continue;
+ uno::Reference<accessibility::XAccessibleEditableText> xText
+ = FindFocusedEditableText(xChildContext);
+ if (xText.is())
+ return xText;
+ }
+ return uno::Reference<accessibility::XAccessibleEditableText>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/accmgr.cxx b/vcl/source/window/accmgr.cxx
new file mode 100644
index 0000000000..26ea9846e8
--- /dev/null
+++ b/vcl/source/window/accmgr.cxx
@@ -0,0 +1,228 @@
+/* -*- 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 <accel.hxx>
+#include <accmgr.hxx>
+
+#include <algorithm>
+
+ImplAccelManager::~ImplAccelManager()
+{
+}
+
+bool ImplAccelManager::InsertAccel( Accelerator* pAccel )
+{
+ if ( !mxAccelList ) {
+ mxAccelList.emplace();
+ } else {
+ for (Accelerator* i : *mxAccelList) {
+ if ( i == pAccel ) {
+ return false;
+ }
+ }
+ }
+
+ mxAccelList->insert( mxAccelList->begin(), pAccel );
+ return true;
+}
+
+void ImplAccelManager::RemoveAccel( Accelerator const * pAccel )
+{
+ // do we have a list ?
+ if ( !mxAccelList )
+ return;
+
+ //e.g. #i90599#. Someone starts typing a sequence in a dialog, but doesn't
+ //end it, and then closes the dialog, deleting the accelerators. So if
+ //we're removing an accelerator that a sub-accelerator which is in the
+ //sequence list, throw away the entire sequence
+ if ( mxSequenceList ) {
+ for (sal_uInt16 i = 0; i < pAccel->GetItemCount(); ++i) {
+ Accelerator* pSubAccel = pAccel->GetAccel( pAccel->GetItemId(i) );
+ for (Accelerator* j : *mxSequenceList) {
+ if ( j == pSubAccel ) {
+ EndSequence();
+ i = pAccel->GetItemCount();
+ break;
+ }
+ }
+ }
+ }
+
+ // throw it away
+ auto it = std::find(mxAccelList->begin(), mxAccelList->end(), pAccel);
+ if (it != mxAccelList->end())
+ mxAccelList->erase( it );
+}
+
+void ImplAccelManager::EndSequence()
+{
+ // are we in a list ?
+ if ( !mxSequenceList )
+ return;
+
+ for (Accelerator* pTempAccel : *mxSequenceList)
+ {
+ pTempAccel->mpDel = nullptr;
+ }
+
+ // delete sequence-list
+ mxSequenceList.reset();
+}
+
+bool ImplAccelManager::IsAccelKey( const vcl::KeyCode& rKeyCode )
+{
+ Accelerator* pAccel;
+
+ // do we have accelerators ??
+ if ( !mxAccelList )
+ return false;
+ if ( mxAccelList->empty() )
+ return false;
+
+ // are we in a sequence ?
+ if ( mxSequenceList )
+ {
+ pAccel = mxSequenceList->empty() ? nullptr : (*mxSequenceList)[ 0 ];
+
+ // not found ?
+ if ( !pAccel )
+ {
+ // abort sequence
+ FlushAccel();
+ return false;
+ }
+
+ // can the entry be found ?
+ ImplAccelEntry* pEntry = pAccel->ImplGetAccelData( rKeyCode );
+ if ( pEntry )
+ {
+ Accelerator* pNextAccel = pEntry->mpAccel;
+
+ // is an accelerator coupled ?
+ if ( pNextAccel )
+ {
+
+ mxSequenceList->insert( mxSequenceList->begin(), pNextAccel );
+
+ // call Activate-Handler of the new one
+ pNextAccel->Activate();
+ return true;
+ }
+ else
+ {
+ // it is there already !
+ if ( pEntry->mbEnabled )
+ {
+ // stop sequence (first call deactivate-handler)
+ EndSequence();
+
+ // set accelerator of the actual item
+ // and call the handler
+ bool bDel = false;
+ pAccel->mnCurId = pEntry->mnId;
+ pAccel->mpDel = &bDel;
+ pAccel->Select();
+
+ // did the accelerator survive the call
+ if ( !bDel )
+ {
+ pAccel->mnCurId = 0;
+ pAccel->mpDel = nullptr;
+ }
+
+ return true;
+ }
+ else
+ {
+ // stop sequence as the accelerator was disabled
+ // transfer the key (to the system)
+ FlushAccel();
+ return false;
+ }
+ }
+ }
+ else
+ {
+ // wrong key => stop sequence
+ FlushAccel();
+ return false;
+ }
+ }
+
+ // step through the list of accelerators
+ for (Accelerator* i : *mxAccelList)
+ {
+ pAccel = i;
+
+ // is the entry contained ?
+ ImplAccelEntry* pEntry = pAccel->ImplGetAccelData( rKeyCode );
+ if ( pEntry )
+ {
+ Accelerator* pNextAccel = pEntry->mpAccel;
+
+ // is an accelerator assigned ?
+ if ( pNextAccel )
+ {
+
+ // create sequence list
+ mxSequenceList.emplace();
+ mxSequenceList->insert( mxSequenceList->begin(), pAccel );
+ mxSequenceList->insert( mxSequenceList->begin(), pNextAccel );
+
+ // call activate-Handler of the new one
+ pNextAccel->Activate();
+
+ return true;
+ }
+ else
+ {
+ // already assigned !
+ if ( pEntry->mbEnabled )
+ {
+ // first call activate/deactivate-Handler
+ pAccel->Activate();
+
+ // define accelerator of the actual item
+ // and call the handler
+ bool bDel = false;
+ pAccel->mnCurId = pEntry->mnId;
+ pAccel->mpDel = &bDel;
+ pAccel->Select();
+
+ // if the accelerator did survive the call
+ if ( !bDel )
+ {
+ pAccel->mnCurId = 0;
+ pAccel->mpDel = nullptr;
+ }
+
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/brdwin.cxx b/vcl/source/window/brdwin.cxx
new file mode 100644
index 0000000000..e7b5693640
--- /dev/null
+++ b/vcl/source/window/brdwin.cxx
@@ -0,0 +1,2003 @@
+/* -*- 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 <strings.hrc>
+#include <svdata.hxx>
+#include <brdwin.hxx>
+#include <window.h>
+
+#include <vcl/textrectinfo.hxx>
+#include <vcl/event.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/syswin.hxx>
+#include <vcl/dockwin.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/help.hxx>
+#include <vcl/toolkit/edit.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/ptrstyle.hxx>
+
+using namespace ::com::sun::star::uno;
+
+// useful caption height for title bar buttons
+#define MIN_CAPTION_HEIGHT 18
+
+namespace vcl {
+
+void Window::ImplCalcSymbolRect( tools::Rectangle& rRect )
+{
+ // Add border, not shown in the non-default representation,
+ // as we want to use it for small buttons
+ rRect.AdjustLeft( -1 );
+ rRect.AdjustTop( -1 );
+ rRect.AdjustRight( 1 );
+ rRect.AdjustBottom( 1 );
+
+ // we leave 5% room between the symbol and the button border
+ tools::Long nExtraWidth = ((rRect.GetWidth()*50)+500)/1000;
+ tools::Long nExtraHeight = ((rRect.GetHeight()*50)+500)/1000;
+ rRect.AdjustLeft(nExtraWidth );
+ rRect.AdjustRight( -nExtraWidth );
+ rRect.AdjustTop(nExtraHeight );
+ rRect.AdjustBottom( -nExtraHeight );
+}
+
+} /* namespace vcl */
+
+static void ImplDrawBrdWinSymbol( vcl::RenderContext* pDev,
+ const tools::Rectangle& rRect, SymbolType eSymbol )
+{
+ // we leave 5% room between the symbol and the button border
+ DecorationView aDecoView( pDev );
+ tools::Rectangle aTempRect = rRect;
+ vcl::Window::ImplCalcSymbolRect( aTempRect );
+ aDecoView.DrawSymbol( aTempRect, eSymbol,
+ pDev->GetSettings().GetStyleSettings().GetButtonTextColor() );
+}
+
+static void ImplDrawBrdWinSymbolButton( vcl::RenderContext* pDev,
+ const tools::Rectangle& rRect,
+ SymbolType eSymbol, DrawButtonFlags nState )
+{
+ bool bMouseOver(nState & DrawButtonFlags::Highlight);
+ nState &= ~DrawButtonFlags::Highlight;
+
+ tools::Rectangle aTempRect;
+ vcl::Window *pWin = pDev->GetOwnerWindow();
+ if( pWin )
+ {
+ if( bMouseOver )
+ {
+ // provide a bright background for selection effect
+ pDev->SetFillColor( pDev->GetSettings().GetStyleSettings().GetWindowColor() );
+ pDev->SetLineColor();
+ pDev->DrawRect( rRect );
+ pWin->DrawSelectionBackground( rRect, 2, bool(nState & DrawButtonFlags::Pressed),
+ true );
+ }
+ aTempRect = rRect;
+ aTempRect.AdjustLeft(3 );
+ aTempRect.AdjustRight( -4 );
+ aTempRect.AdjustTop(3 );
+ aTempRect.AdjustBottom( -4 );
+ }
+ else
+ {
+ DecorationView aDecoView( pDev );
+ aTempRect = aDecoView.DrawButton( rRect, nState|DrawButtonFlags::Flat );
+ }
+ ImplDrawBrdWinSymbol( pDev, aTempRect, eSymbol );
+}
+
+
+ImplBorderWindowView::~ImplBorderWindowView()
+{
+}
+
+bool ImplBorderWindowView::MouseMove( const MouseEvent& )
+{
+ return false;
+}
+
+bool ImplBorderWindowView::MouseButtonDown( const MouseEvent& )
+{
+ return false;
+}
+
+bool ImplBorderWindowView::Tracking( const TrackingEvent& )
+{
+ return false;
+}
+
+OUString ImplBorderWindowView::RequestHelp( const Point&, tools::Rectangle& )
+{
+ return OUString();
+}
+
+tools::Rectangle ImplBorderWindowView::GetMenuRect() const
+{
+ return tools::Rectangle();
+}
+
+void ImplBorderWindowView::ImplInitTitle(ImplBorderFrameData* pData)
+{
+ ImplBorderWindow* pBorderWindow = pData->mpBorderWindow;
+
+ if ( !(pBorderWindow->GetStyle() & (WB_MOVEABLE | WB_POPUP)) ||
+ (pData->mnTitleType == BorderWindowTitleType::NONE) )
+ {
+ pData->mnTitleType = BorderWindowTitleType::NONE;
+ pData->mnTitleHeight = 0;
+ }
+ else
+ {
+ const StyleSettings& rStyleSettings = pData->mpOutDev->GetSettings().GetStyleSettings();
+ if (pData->mnTitleType == BorderWindowTitleType::Tearoff)
+ pData->mnTitleHeight = ToolBox::ImplGetDragWidth(*pData->mpBorderWindow, false) + 2;
+ else
+ {
+ if (pData->mnTitleType == BorderWindowTitleType::Small)
+ {
+ pBorderWindow->SetPointFont(*pBorderWindow->GetOutDev(), rStyleSettings.GetFloatTitleFont() );
+ pData->mnTitleHeight = rStyleSettings.GetFloatTitleHeight();
+ }
+ else // pData->mnTitleType == BorderWindowTitleType::Normal
+ {
+ // FIXME RenderContext
+ pBorderWindow->SetPointFont(*pBorderWindow->GetOutDev(), rStyleSettings.GetTitleFont());
+ pData->mnTitleHeight = rStyleSettings.GetTitleHeight();
+ }
+ tools::Long nTextHeight = pBorderWindow->GetTextHeight();
+ if (nTextHeight > pData->mnTitleHeight)
+ pData->mnTitleHeight = nTextHeight;
+ }
+ }
+}
+
+BorderWindowHitTest ImplBorderWindowView::ImplHitTest( ImplBorderFrameData const * pData, const Point& rPos )
+{
+ ImplBorderWindow* pBorderWindow = pData->mpBorderWindow;
+
+ if ( pData->maTitleRect.Contains( rPos ) )
+ {
+ if ( pData->maCloseRect.Contains( rPos ) )
+ return BorderWindowHitTest::Close;
+ else if ( pData->maMenuRect.Contains( rPos ) )
+ return BorderWindowHitTest::Menu;
+ else if ( pData->maDockRect.Contains( rPos ) )
+ return BorderWindowHitTest::Dock;
+ else if ( pData->maHideRect.Contains( rPos ) )
+ return BorderWindowHitTest::Hide;
+ else if ( pData->maHelpRect.Contains( rPos ) )
+ return BorderWindowHitTest::Help;
+ else
+ return BorderWindowHitTest::Title;
+ }
+
+ if (pBorderWindow->GetStyle() & WB_SIZEABLE)
+ {
+ tools::Long nSizeWidth = pData->mnNoTitleTop+pData->mnTitleHeight;
+ if ( nSizeWidth < 16 )
+ nSizeWidth = 16;
+
+ // no corner resize for floating toolbars, which would lead to jumps while formatting
+ // setting nSizeWidth = 0 will only return pure left,top,right,bottom
+ if( pBorderWindow->GetStyle() & (WB_OWNERDRAWDECORATION | WB_POPUP) )
+ nSizeWidth = 0;
+
+ if ( rPos.X() < pData->mnLeftBorder )
+ {
+ if ( rPos.Y() < nSizeWidth )
+ return BorderWindowHitTest::TopLeft;
+ else if ( rPos.Y() >= pData->mnHeight-nSizeWidth )
+ return BorderWindowHitTest::BottomLeft;
+ else
+ return BorderWindowHitTest::Left;
+ }
+ else if ( rPos.X() >= pData->mnWidth-pData->mnRightBorder )
+ {
+ if ( rPos.Y() < nSizeWidth )
+ return BorderWindowHitTest::TopRight;
+ else if ( rPos.Y() >= pData->mnHeight-nSizeWidth )
+ return BorderWindowHitTest::BottomRight;
+ else
+ return BorderWindowHitTest::Right;
+ }
+ else if ( rPos.Y() < pData->mnNoTitleTop )
+ {
+ if ( rPos.X() < nSizeWidth )
+ return BorderWindowHitTest::TopLeft;
+ else if ( rPos.X() >= pData->mnWidth-nSizeWidth )
+ return BorderWindowHitTest::TopRight;
+ else
+ return BorderWindowHitTest::Top;
+ }
+ else if ( rPos.Y() >= pData->mnHeight-pData->mnBottomBorder )
+ {
+ if ( rPos.X() < nSizeWidth )
+ return BorderWindowHitTest::BottomLeft;
+ else if ( rPos.X() >= pData->mnWidth-nSizeWidth )
+ return BorderWindowHitTest::BottomRight;
+ else
+ return BorderWindowHitTest::Bottom;
+ }
+ }
+
+ return BorderWindowHitTest::NONE;
+}
+
+void ImplBorderWindowView::ImplMouseMove( ImplBorderFrameData* pData, const MouseEvent& rMEvt )
+{
+ DrawButtonFlags oldCloseState = pData->mnCloseState;
+ DrawButtonFlags oldMenuState = pData->mnMenuState;
+ pData->mnCloseState &= ~DrawButtonFlags::Highlight;
+ pData->mnMenuState &= ~DrawButtonFlags::Highlight;
+
+ Point aMousePos = rMEvt.GetPosPixel();
+ BorderWindowHitTest nHitTest = ImplHitTest( pData, aMousePos );
+ PointerStyle ePtrStyle = PointerStyle::Arrow;
+ if ( nHitTest & BorderWindowHitTest::Left )
+ ePtrStyle = PointerStyle::WindowWSize;
+ else if ( nHitTest & BorderWindowHitTest::Right )
+ ePtrStyle = PointerStyle::WindowESize;
+ else if ( nHitTest & BorderWindowHitTest::Top )
+ ePtrStyle = PointerStyle::WindowNSize;
+ else if ( nHitTest & BorderWindowHitTest::Bottom )
+ ePtrStyle = PointerStyle::WindowSSize;
+ else if ( nHitTest & BorderWindowHitTest::TopLeft )
+ ePtrStyle = PointerStyle::WindowNWSize;
+ else if ( nHitTest & BorderWindowHitTest::BottomRight )
+ ePtrStyle = PointerStyle::WindowSESize;
+ else if ( nHitTest & BorderWindowHitTest::TopRight )
+ ePtrStyle = PointerStyle::WindowNESize;
+ else if ( nHitTest & BorderWindowHitTest::BottomLeft )
+ ePtrStyle = PointerStyle::WindowSWSize;
+ else if ( nHitTest & BorderWindowHitTest::Close )
+ pData->mnCloseState |= DrawButtonFlags::Highlight;
+ else if ( nHitTest & BorderWindowHitTest::Menu )
+ pData->mnMenuState |= DrawButtonFlags::Highlight;
+ else if ( nHitTest & BorderWindowHitTest::Title &&
+ pData->mnTitleType == BorderWindowTitleType::Tearoff && !rMEvt.IsLeaveWindow() )
+ ePtrStyle = PointerStyle::Move;
+ pData->mpBorderWindow->SetPointer( ePtrStyle );
+
+ if( pData->mnCloseState != oldCloseState )
+ pData->mpBorderWindow->Invalidate( pData->maCloseRect );
+ if( pData->mnMenuState != oldMenuState )
+ pData->mpBorderWindow->Invalidate( pData->maMenuRect );
+}
+
+OUString ImplBorderWindowView::ImplRequestHelp( ImplBorderFrameData const * pData,
+ const Point& rPos,
+ tools::Rectangle& rHelpRect )
+{
+ TranslateId pHelpId;
+ OUString aHelpStr;
+ BorderWindowHitTest nHitTest = ImplHitTest( pData, rPos );
+ if ( nHitTest != BorderWindowHitTest::NONE )
+ {
+ if ( nHitTest & BorderWindowHitTest::Close )
+ {
+ pHelpId = SV_HELPTEXT_CLOSE;
+ rHelpRect = pData->maCloseRect;
+ }
+ else if ( nHitTest & BorderWindowHitTest::Dock )
+ {
+ pHelpId = SV_HELPTEXT_MAXIMIZE;
+ rHelpRect = pData->maDockRect;
+ }
+ else if ( nHitTest & BorderWindowHitTest::Hide )
+ {
+ pHelpId = SV_HELPTEXT_MINIMIZE;
+ rHelpRect = pData->maHideRect;
+ }
+ else if ( nHitTest & BorderWindowHitTest::Help )
+ {
+ pHelpId = SV_HELPTEXT_HELP;
+ rHelpRect = pData->maHelpRect;
+ }
+ else if ( nHitTest & BorderWindowHitTest::Title )
+ {
+ if( !pData->maTitleRect.IsEmpty() )
+ {
+ // tooltip only if title truncated
+ if( pData->mbTitleClipped )
+ {
+ rHelpRect = pData->maTitleRect;
+ // no help id, use window title as help string
+ aHelpStr = pData->mpBorderWindow->GetText();
+ }
+ }
+ }
+ }
+
+ if (pHelpId)
+ aHelpStr = VclResId(pHelpId);
+
+ return aHelpStr;
+}
+
+tools::Long ImplBorderWindowView::ImplCalcTitleWidth( const ImplBorderFrameData* pData )
+{
+ // title is not visible therefore no width
+ if ( !pData->mnTitleHeight )
+ return 0;
+
+ ImplBorderWindow* pBorderWindow = pData->mpBorderWindow;
+ tools::Long nTitleWidth = pBorderWindow->GetTextWidth( pBorderWindow->GetText() )+6;
+ nTitleWidth += pData->maCloseRect.GetWidth();
+ nTitleWidth += pData->maDockRect.GetWidth();
+ nTitleWidth += pData->maMenuRect.GetWidth();
+ nTitleWidth += pData->maHideRect.GetWidth();
+ nTitleWidth += pData->maHelpRect.GetWidth();
+ nTitleWidth += pData->mnLeftBorder+pData->mnRightBorder;
+ return nTitleWidth;
+}
+
+
+ImplNoBorderWindowView::ImplNoBorderWindowView()
+{
+}
+
+void ImplNoBorderWindowView::Init( OutputDevice*, tools::Long, tools::Long )
+{
+}
+
+void ImplNoBorderWindowView::GetBorder( sal_Int32& rLeftBorder, sal_Int32& rTopBorder,
+ sal_Int32& rRightBorder, sal_Int32& rBottomBorder ) const
+{
+ rLeftBorder = 0;
+ rTopBorder = 0;
+ rRightBorder = 0;
+ rBottomBorder = 0;
+}
+
+tools::Long ImplNoBorderWindowView::CalcTitleWidth() const
+{
+ return 0;
+}
+
+void ImplNoBorderWindowView::DrawWindow(vcl::RenderContext&, const Point*)
+{
+}
+
+ImplSmallBorderWindowView::ImplSmallBorderWindowView( ImplBorderWindow* pBorderWindow )
+ : mpBorderWindow(pBorderWindow)
+ , mpOutDev(nullptr)
+ , mnWidth(0)
+ , mnHeight(0)
+ , mnLeftBorder(0)
+ , mnTopBorder(0)
+ , mnRightBorder(0)
+ , mnBottomBorder(0)
+ , mbNWFBorder(false)
+{
+}
+
+void ImplSmallBorderWindowView::Init( OutputDevice* pDev, tools::Long nWidth, tools::Long nHeight )
+{
+ mpOutDev = pDev;
+ mnWidth = nWidth;
+ mnHeight = nHeight;
+ mbNWFBorder = false;
+
+ vcl::Window *pWin = mpOutDev->GetOwnerWindow();
+ vcl::Window *pCtrl = nullptr;
+ vcl::Window *pSubEdit = nullptr;
+ if (pWin)
+ pCtrl = mpBorderWindow->GetWindow(GetWindowType::Client);
+
+ tools::Long nOrigLeftBorder = mnLeftBorder;
+ tools::Long nOrigTopBorder = mnTopBorder;
+ tools::Long nOrigRightBorder = mnRightBorder;
+ tools::Long nOrigBottomBorder = mnBottomBorder;
+
+ WindowBorderStyle nBorderStyle = mpBorderWindow->GetBorderStyle();
+ if ( nBorderStyle & WindowBorderStyle::NOBORDER )
+ {
+ mnLeftBorder = 0;
+ mnTopBorder = 0;
+ mnRightBorder = 0;
+ mnBottomBorder = 0;
+ }
+ else
+ {
+ // FIXME: this is currently only on macOS, check with other
+ // platforms
+ if( ImplGetSVData()->maNWFData.mbNoFocusRects && !( nBorderStyle & WindowBorderStyle::NWF ) )
+ {
+ // for native widget drawing we must find out what
+ // control this border belongs to
+ ControlType aCtrlType = ControlType::Generic;
+ ControlPart aCtrlPart = ControlPart::Entire;
+ if (pCtrl)
+ {
+ switch( pCtrl->GetType() )
+ {
+ case WindowType::LISTBOX:
+ if( pCtrl->GetStyle() & WB_DROPDOWN )
+ {
+ aCtrlType = ControlType::Listbox;
+ mbNWFBorder = true;
+ pSubEdit = static_cast<Edit*>(pCtrl)->GetSubEdit();
+ }
+ break;
+ case WindowType::LISTBOXWINDOW:
+ aCtrlType = ControlType::Listbox;
+ aCtrlPart = ControlPart::ListboxWindow;
+ mbNWFBorder = true;
+ break;
+ case WindowType::COMBOBOX:
+ if( pCtrl->GetStyle() & WB_DROPDOWN )
+ {
+ aCtrlType = ControlType::Combobox;
+ mbNWFBorder = true;
+ pSubEdit = static_cast<Edit*>(pCtrl)->GetSubEdit();
+ }
+ break;
+ case WindowType::MULTILINEEDIT:
+ aCtrlType = ControlType::MultilineEditbox;
+ mbNWFBorder = true;
+ break;
+ case WindowType::EDIT:
+ case WindowType::PATTERNFIELD:
+ case WindowType::METRICFIELD:
+ case WindowType::CURRENCYFIELD:
+ case WindowType::DATEFIELD:
+ case WindowType::TIMEFIELD:
+ case WindowType::SPINFIELD:
+ case WindowType::FORMATTEDFIELD:
+ mbNWFBorder = true;
+ if (pCtrl->GetStyle() & WB_SPIN)
+ aCtrlType = ControlType::Spinbox;
+ else
+ aCtrlType = ControlType::Editbox;
+ pSubEdit = static_cast<Edit*>(pCtrl)->GetSubEdit();
+ break;
+ default:
+ break;
+ }
+ }
+ if( mbNWFBorder )
+ {
+ ImplControlValue aControlValue;
+ Size aMinSize( mnWidth, mnHeight );
+ if( aMinSize.Width() < 10 ) aMinSize.setWidth( 10 );
+ if( aMinSize.Height() < 10 ) aMinSize.setHeight( 10 );
+ tools::Rectangle aCtrlRegion( Point(), aMinSize );
+ tools::Rectangle aBounds, aContent;
+ if( pWin->GetNativeControlRegion( aCtrlType, aCtrlPart, aCtrlRegion,
+ ControlState::ENABLED, aControlValue,
+ aBounds, aContent ) )
+ {
+ aBounds.AdjustLeft(mnLeftBorder);
+ aBounds.AdjustRight(-mnRightBorder);
+ aBounds.AdjustTop(mnTopBorder);
+ aBounds.AdjustBottom(-mnBottomBorder);
+ aContent.AdjustLeft(mnLeftBorder);
+ aContent.AdjustRight(-mnRightBorder);
+ aContent.AdjustTop(mnTopBorder);
+ aContent.AdjustBottom(-mnBottomBorder);
+ mnLeftBorder = aContent.Left() - aBounds.Left();
+ mnRightBorder = aBounds.Right() - aContent.Right();
+ mnTopBorder = aContent.Top() - aBounds.Top();
+ mnBottomBorder = aBounds.Bottom() - aContent.Bottom();
+ if( mnWidth && mnHeight )
+ {
+
+ mpBorderWindow->SetPaintTransparent( true );
+ mpBorderWindow->SetBackground();
+ if (!pCtrl->IsControlBackground())
+ {
+ pCtrl->SetPaintTransparent(true);
+ if (pSubEdit)
+ pSubEdit->SetPaintTransparent(true);
+ }
+
+ vcl::Window* pCompoundParent = nullptr;
+ if( pWin->GetParent() && pWin->GetParent()->IsCompoundControl() )
+ pCompoundParent = pWin->GetParent();
+
+ if( pCompoundParent )
+ pCompoundParent->SetPaintTransparent( true );
+
+ if( mnWidth < aBounds.GetWidth() || mnHeight < aBounds.GetHeight() )
+ {
+ if( ! pCompoundParent ) // compound controls have to fix themselves
+ {
+ Point aPos( mpBorderWindow->GetPosPixel() );
+ if( mnWidth < aBounds.GetWidth() )
+ aPos.AdjustX( -((aBounds.GetWidth() - mnWidth) / 2) );
+ if( mnHeight < aBounds.GetHeight() )
+ aPos.AdjustY( -((aBounds.GetHeight() - mnHeight) / 2) );
+ mpBorderWindow->SetPosSizePixel( aPos, aBounds.GetSize() );
+ }
+ }
+ }
+ }
+ else
+ mbNWFBorder = false;
+ }
+ }
+
+ if( ! mbNWFBorder )
+ {
+ DrawFrameStyle nStyle = DrawFrameStyle::NONE;
+ DrawFrameFlags nFlags = DrawFrameFlags::NoDraw;
+ // move border outside if border was converted or if the BorderWindow is a frame window,
+ if ( mpBorderWindow->mbSmallOutBorder )
+ nStyle = DrawFrameStyle::DoubleOut;
+ else if ( nBorderStyle & WindowBorderStyle::NWF )
+ nStyle = DrawFrameStyle::NWF;
+ else
+ nStyle = DrawFrameStyle::DoubleIn;
+ if ( nBorderStyle & WindowBorderStyle::MONO )
+ nFlags |= DrawFrameFlags::Mono;
+
+ DecorationView aDecoView( mpOutDev );
+ tools::Rectangle aRect( 0, 0, 10, 10 );
+ tools::Rectangle aCalcRect = aDecoView.DrawFrame( aRect, nStyle, nFlags );
+ mnLeftBorder = aCalcRect.Left();
+ mnTopBorder = aCalcRect.Top();
+ mnRightBorder = aRect.Right()-aCalcRect.Right();
+ mnBottomBorder = aRect.Bottom()-aCalcRect.Bottom();
+ }
+ }
+
+ if (pCtrl)
+ {
+ //fdo#57090 If the borders have changed, then trigger a queue_resize on
+ //the bordered window, which will resync its borders at that point
+ if (nOrigLeftBorder != mnLeftBorder ||
+ nOrigTopBorder != mnTopBorder ||
+ nOrigRightBorder != mnRightBorder ||
+ nOrigBottomBorder != mnBottomBorder)
+ {
+ pCtrl->queue_resize();
+ }
+ }
+}
+
+void ImplSmallBorderWindowView::GetBorder( sal_Int32& rLeftBorder, sal_Int32& rTopBorder,
+ sal_Int32& rRightBorder, sal_Int32& rBottomBorder ) const
+{
+ rLeftBorder = mnLeftBorder;
+ rTopBorder = mnTopBorder;
+ rRightBorder = mnRightBorder;
+ rBottomBorder = mnBottomBorder;
+}
+
+tools::Long ImplSmallBorderWindowView::CalcTitleWidth() const
+{
+ return 0;
+}
+
+void ImplSmallBorderWindowView::DrawWindow(vcl::RenderContext& rRenderContext, const Point*)
+{
+ WindowBorderStyle nBorderStyle = mpBorderWindow->GetBorderStyle();
+ if (nBorderStyle & WindowBorderStyle::NOBORDER)
+ return;
+
+ bool bNativeOK = false;
+ // for native widget drawing we must find out what
+ // control this border belongs to
+ vcl::Window* pCtrl = mpBorderWindow->GetWindow(GetWindowType::Client);
+
+ ControlType aCtrlType = ControlType::Generic;
+ ControlPart aCtrlPart = ControlPart::Entire;
+ if (pCtrl)
+ {
+ switch (pCtrl->GetType())
+ {
+ case WindowType::MULTILINEEDIT:
+ aCtrlType = ControlType::MultilineEditbox;
+ break;
+ case WindowType::EDIT:
+ case WindowType::PATTERNFIELD:
+ case WindowType::METRICFIELD:
+ case WindowType::CURRENCYFIELD:
+ case WindowType::DATEFIELD:
+ case WindowType::TIMEFIELD:
+ case WindowType::SPINFIELD:
+ case WindowType::FORMATTEDFIELD:
+ if (pCtrl->GetStyle() & WB_SPIN)
+ aCtrlType = ControlType::Spinbox;
+ else
+ aCtrlType = ControlType::Editbox;
+ break;
+
+ case WindowType::LISTBOX:
+ case WindowType::MULTILISTBOX:
+ case WindowType::TREELISTBOX:
+ aCtrlType = ControlType::Listbox;
+ if (pCtrl->GetStyle() & WB_DROPDOWN)
+ aCtrlPart = ControlPart::Entire;
+ else
+ aCtrlPart = ControlPart::ListboxWindow;
+ break;
+
+ case WindowType::LISTBOXWINDOW:
+ aCtrlType = ControlType::Listbox;
+ aCtrlPart = ControlPart::ListboxWindow;
+ break;
+
+ case WindowType::COMBOBOX:
+ case WindowType::PATTERNBOX:
+ case WindowType::NUMERICBOX:
+ case WindowType::METRICBOX:
+ case WindowType::CURRENCYBOX:
+ case WindowType::DATEBOX:
+ case WindowType::TIMEBOX:
+ case WindowType::LONGCURRENCYBOX:
+ if (pCtrl->GetStyle() & WB_DROPDOWN)
+ {
+ aCtrlType = ControlType::Combobox;
+ aCtrlPart = ControlPart::Entire;
+ }
+ else
+ {
+ aCtrlType = ControlType::Listbox;
+ aCtrlPart = ControlPart::ListboxWindow;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (aCtrlType != ControlType::Generic && pCtrl->IsNativeControlSupported(aCtrlType, aCtrlPart))
+ {
+ ImplControlValue aControlValue;
+ ControlState nState = ControlState::ENABLED;
+
+ if (!mpBorderWindow->IsEnabled())
+ nState &= ~ControlState::ENABLED;
+ if (mpBorderWindow->HasFocus() || pCtrl->HasFocus() || pCtrl->HasChildPathFocus())
+ nState |= ControlState::FOCUSED;
+
+ bool bMouseOver = pCtrl->IsMouseOver();
+ if (!bMouseOver)
+ {
+ vcl::Window *pCtrlChild = pCtrl->GetWindow(GetWindowType::FirstChild);
+ while(pCtrlChild)
+ {
+ bMouseOver = pCtrlChild->IsMouseOver();
+ if (bMouseOver)
+ break;
+ pCtrlChild = pCtrlChild->GetWindow(GetWindowType::Next);
+ }
+ }
+
+ if (bMouseOver)
+ nState |= ControlState::ROLLOVER;
+
+ Point aPoint;
+ tools::Rectangle aCtrlRegion(aPoint, Size(mnWidth, mnHeight));
+
+ tools::Rectangle aBoundingRgn(aPoint, Size(mnWidth, mnHeight));
+ tools::Rectangle aContentRgn(aCtrlRegion);
+ if (!ImplGetSVData()->maNWFData.mbCanDrawWidgetAnySize &&
+ rRenderContext.GetNativeControlRegion(aCtrlType, aCtrlPart, aCtrlRegion,
+ nState, aControlValue,
+ aBoundingRgn, aContentRgn))
+ {
+ aCtrlRegion=aContentRgn;
+ }
+
+ Color aBackgroundColor = COL_AUTO;
+ if (pCtrl->IsControlBackground())
+ aBackgroundColor = pCtrl->GetBackgroundColor();
+ bNativeOK = rRenderContext.DrawNativeControl(aCtrlType, aCtrlPart, aCtrlRegion, nState, aControlValue, OUString(), aBackgroundColor);
+
+ // if the native theme draws the spinbuttons in one call, make sure the proper settings
+ // are passed, this might force a redraw though... (TODO: improve)
+ if ((aCtrlType == ControlType::Spinbox) && !pCtrl->IsNativeControlSupported(ControlType::Spinbox, ControlPart::ButtonUp))
+ {
+ Edit* pEdit = static_cast<Edit*>(pCtrl)->GetSubEdit();
+ if (pEdit && !pEdit->SupportsDoubleBuffering())
+ pCtrl->Paint(*pCtrl->GetOutDev(), tools::Rectangle()); // make sure the buttons are also drawn as they might overwrite the border
+ }
+ }
+
+ if (bNativeOK)
+ return;
+
+ DrawFrameStyle nStyle = DrawFrameStyle::NONE;
+ DrawFrameFlags nFlags = DrawFrameFlags::NONE;
+ // move border outside if border was converted or if the border window is a frame window,
+ if (mpBorderWindow->mbSmallOutBorder)
+ nStyle = DrawFrameStyle::DoubleOut;
+ else if (nBorderStyle & WindowBorderStyle::NWF)
+ nStyle = DrawFrameStyle::NWF;
+ else
+ nStyle = DrawFrameStyle::DoubleIn;
+ if (nBorderStyle & WindowBorderStyle::MONO)
+ nFlags |= DrawFrameFlags::Mono;
+ if (nBorderStyle & WindowBorderStyle::MENU)
+ nFlags |= DrawFrameFlags::Menu;
+ // tell DrawFrame that we're drawing a window border of a frame window to avoid round corners
+ if (mpBorderWindow == mpBorderWindow->ImplGetFrameWindow())
+ nFlags |= DrawFrameFlags::WindowBorder;
+
+ DecorationView aDecoView(&rRenderContext);
+ tools::Rectangle aInRect(Point(), Size(mnWidth, mnHeight));
+ aDecoView.DrawFrame(aInRect, nStyle, nFlags);
+}
+
+
+ImplStdBorderWindowView::ImplStdBorderWindowView( ImplBorderWindow* pBorderWindow )
+{
+ maFrameData.mpBorderWindow = pBorderWindow;
+ maFrameData.mbDragFull = false;
+ maFrameData.mnHitTest = BorderWindowHitTest::NONE;
+ maFrameData.mnCloseState = DrawButtonFlags::NONE;
+ maFrameData.mnDockState = DrawButtonFlags::NONE;
+ maFrameData.mnMenuState = DrawButtonFlags::NONE;
+ maFrameData.mnHideState = DrawButtonFlags::NONE;
+ maFrameData.mnHelpState = DrawButtonFlags::NONE;
+ maFrameData.mbTitleClipped = false;
+}
+
+ImplStdBorderWindowView::~ImplStdBorderWindowView()
+{
+}
+
+bool ImplStdBorderWindowView::MouseMove( const MouseEvent& rMEvt )
+{
+ ImplMouseMove( &maFrameData, rMEvt );
+ return true;
+}
+
+bool ImplStdBorderWindowView::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ ImplBorderWindow* pBorderWindow = maFrameData.mpBorderWindow;
+
+ if ( rMEvt.IsLeft() || rMEvt.IsRight() )
+ {
+ maFrameData.maMouseOff = rMEvt.GetPosPixel();
+ maFrameData.mnHitTest = ImplHitTest( &maFrameData, maFrameData.maMouseOff );
+ if ( maFrameData.mnHitTest != BorderWindowHitTest::NONE )
+ {
+ DragFullOptions nDragFullTest = DragFullOptions::NONE;
+ bool bTracking = true;
+ bool bHitTest = true;
+
+ if ( maFrameData.mnHitTest & BorderWindowHitTest::Close )
+ {
+ maFrameData.mnCloseState |= DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ else if ( maFrameData.mnHitTest & BorderWindowHitTest::Dock )
+ {
+ maFrameData.mnDockState |= DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ else if ( maFrameData.mnHitTest & BorderWindowHitTest::Menu )
+ {
+ maFrameData.mnMenuState |= DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+
+ // call handler already on mouse down
+ if ( pBorderWindow->ImplGetClientWindow()->IsSystemWindow() )
+ {
+ SystemWindow* pClientWindow = static_cast<SystemWindow*>(pBorderWindow->ImplGetClientWindow());
+ pClientWindow->TitleButtonClick( TitleButton::Menu );
+ }
+
+ maFrameData.mnMenuState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+
+ bTracking = false;
+ }
+ else if ( maFrameData.mnHitTest & BorderWindowHitTest::Hide )
+ {
+ maFrameData.mnHideState |= DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ else if ( maFrameData.mnHitTest & BorderWindowHitTest::Help )
+ {
+ maFrameData.mnHelpState |= DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ else
+ {
+ if ( rMEvt.GetClicks() == 1 )
+ {
+ Point aPos = pBorderWindow->GetPosPixel();
+ Size aSize = pBorderWindow->GetOutputSizePixel();
+ maFrameData.mnTrackX = aPos.X();
+ maFrameData.mnTrackY = aPos.Y();
+ maFrameData.mnTrackWidth = aSize.Width();
+ maFrameData.mnTrackHeight = aSize.Height();
+
+ if (maFrameData.mnHitTest & BorderWindowHitTest::Title)
+ nDragFullTest = DragFullOptions::WindowMove;
+ else
+ nDragFullTest = DragFullOptions::WindowSize;
+ }
+ else
+ {
+ bTracking = false;
+
+ if ( (maFrameData.mnHitTest & BorderWindowHitTest::Title) &&
+ ((rMEvt.GetClicks() % 2) == 0) )
+ {
+ maFrameData.mnHitTest = BorderWindowHitTest::NONE;
+ bHitTest = false;
+
+ if ( pBorderWindow->ImplGetClientWindow()->IsSystemWindow() )
+ {
+ SystemWindow* pClientWindow = static_cast<SystemWindow*>(pBorderWindow->ImplGetClientWindow());
+ // always perform docking on double click, no button required
+ pClientWindow->TitleButtonClick( TitleButton::Docking );
+ }
+ }
+ }
+ }
+
+ if ( bTracking )
+ {
+ maFrameData.mbDragFull = false;
+ if ( nDragFullTest != DragFullOptions::NONE )
+ maFrameData.mbDragFull = true; // always fulldrag for proper docking, ignore system settings
+ pBorderWindow->StartTracking();
+ }
+ else if ( bHitTest )
+ maFrameData.mnHitTest = BorderWindowHitTest::NONE;
+ }
+ }
+
+ return true;
+}
+
+bool ImplStdBorderWindowView::Tracking( const TrackingEvent& rTEvt )
+{
+ ImplBorderWindow* pBorderWindow = maFrameData.mpBorderWindow;
+
+ if ( rTEvt.IsTrackingEnded() )
+ {
+ BorderWindowHitTest nHitTest = maFrameData.mnHitTest;
+ maFrameData.mnHitTest = BorderWindowHitTest::NONE;
+
+ if ( nHitTest & BorderWindowHitTest::Close )
+ {
+ if ( maFrameData.mnCloseState & DrawButtonFlags::Pressed )
+ {
+ maFrameData.mnCloseState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+
+ // do not call a Click-Handler when aborting
+ if ( !rTEvt.IsTrackingCanceled() )
+ {
+ // dispatch to correct window type (why is Close() not virtual ??? )
+ // TODO: make Close() virtual
+ VclPtr<vcl::Window> pWin = pBorderWindow->ImplGetClientWindow()->ImplGetWindow();
+ SystemWindow *pSysWin = dynamic_cast<SystemWindow* >(pWin.get());
+ DockingWindow *pDockWin = dynamic_cast<DockingWindow*>(pWin.get());
+ if ( pSysWin )
+ pSysWin->Close();
+ else if ( pDockWin )
+ pDockWin->Close();
+ }
+ }
+ }
+ else if ( nHitTest & BorderWindowHitTest::Dock )
+ {
+ if ( maFrameData.mnDockState & DrawButtonFlags::Pressed )
+ {
+ maFrameData.mnDockState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+
+ // do not call a Click-Handler when aborting
+ if ( !rTEvt.IsTrackingCanceled() )
+ {
+ if ( pBorderWindow->ImplGetClientWindow()->IsSystemWindow() )
+ {
+ SystemWindow* pClientWindow = static_cast<SystemWindow*>(pBorderWindow->ImplGetClientWindow());
+ pClientWindow->TitleButtonClick( TitleButton::Docking );
+ }
+ }
+ }
+ }
+ else if ( nHitTest & BorderWindowHitTest::Menu )
+ {
+ if ( maFrameData.mnMenuState & DrawButtonFlags::Pressed )
+ {
+ maFrameData.mnMenuState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+
+ // handler already called on mouse down
+ }
+ }
+ else if ( nHitTest & BorderWindowHitTest::Hide )
+ {
+ if ( maFrameData.mnHideState & DrawButtonFlags::Pressed )
+ {
+ maFrameData.mnHideState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+
+ // do not call a Click-Handler when aborting
+ if ( !rTEvt.IsTrackingCanceled() )
+ {
+ if ( pBorderWindow->ImplGetClientWindow()->IsSystemWindow() )
+ {
+ SystemWindow* pClientWindow = static_cast<SystemWindow*>(pBorderWindow->ImplGetClientWindow());
+ pClientWindow->TitleButtonClick( TitleButton::Hide );
+ }
+ }
+ }
+ }
+ else if ( nHitTest & BorderWindowHitTest::Help )
+ {
+ if ( maFrameData.mnHelpState & DrawButtonFlags::Pressed )
+ {
+ maFrameData.mnHelpState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ else
+ {
+ if ( maFrameData.mbDragFull )
+ {
+ // restore old state when aborting
+ if ( rTEvt.IsTrackingCanceled() )
+ pBorderWindow->SetPosSizePixel( Point( maFrameData.mnTrackX, maFrameData.mnTrackY ), Size( maFrameData.mnTrackWidth, maFrameData.mnTrackHeight ) );
+ }
+ else
+ {
+ pBorderWindow->HideTracking();
+ if ( !rTEvt.IsTrackingCanceled() )
+ pBorderWindow->SetPosSizePixel( Point( maFrameData.mnTrackX, maFrameData.mnTrackY ), Size( maFrameData.mnTrackWidth, maFrameData.mnTrackHeight ) );
+ }
+
+ if ( !rTEvt.IsTrackingCanceled() )
+ {
+ if ( pBorderWindow->ImplGetClientWindow()->ImplIsFloatingWindow() )
+ {
+ if ( static_cast<FloatingWindow*>(pBorderWindow->ImplGetClientWindow())->IsInPopupMode() )
+ static_cast<FloatingWindow*>(pBorderWindow->ImplGetClientWindow())->EndPopupMode( FloatWinPopupEndFlags::TearOff );
+ }
+ }
+ }
+ }
+ else if ( !rTEvt.GetMouseEvent().IsSynthetic() )
+ {
+ Point aMousePos = rTEvt.GetMouseEvent().GetPosPixel();
+
+ if ( maFrameData.mnHitTest & BorderWindowHitTest::Close )
+ {
+ if ( maFrameData.maCloseRect.Contains( aMousePos ) )
+ {
+ if ( !(maFrameData.mnCloseState & DrawButtonFlags::Pressed) )
+ {
+ maFrameData.mnCloseState |= DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ else
+ {
+ if ( maFrameData.mnCloseState & DrawButtonFlags::Pressed )
+ {
+ maFrameData.mnCloseState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ }
+ else if ( maFrameData.mnHitTest & BorderWindowHitTest::Dock )
+ {
+ if ( maFrameData.maDockRect.Contains( aMousePos ) )
+ {
+ if ( !(maFrameData.mnDockState & DrawButtonFlags::Pressed) )
+ {
+ maFrameData.mnDockState |= DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ else
+ {
+ if ( maFrameData.mnDockState & DrawButtonFlags::Pressed )
+ {
+ maFrameData.mnDockState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ }
+ else if ( maFrameData.mnHitTest & BorderWindowHitTest::Menu )
+ {
+ if ( maFrameData.maMenuRect.Contains( aMousePos ) )
+ {
+ if ( !(maFrameData.mnMenuState & DrawButtonFlags::Pressed) )
+ {
+ maFrameData.mnMenuState |= DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ else
+ {
+ if ( maFrameData.mnMenuState & DrawButtonFlags::Pressed )
+ {
+ maFrameData.mnMenuState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ }
+ else if ( maFrameData.mnHitTest & BorderWindowHitTest::Hide )
+ {
+ if ( maFrameData.maHideRect.Contains( aMousePos ) )
+ {
+ if ( !(maFrameData.mnHideState & DrawButtonFlags::Pressed) )
+ {
+ maFrameData.mnHideState |= DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ else
+ {
+ if ( maFrameData.mnHideState & DrawButtonFlags::Pressed )
+ {
+ maFrameData.mnHideState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ }
+ else if ( maFrameData.mnHitTest & BorderWindowHitTest::Help )
+ {
+ if ( maFrameData.maHelpRect.Contains( aMousePos ) )
+ {
+ if ( !(maFrameData.mnHelpState & DrawButtonFlags::Pressed) )
+ {
+ maFrameData.mnHelpState |= DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ else
+ {
+ if ( maFrameData.mnHelpState & DrawButtonFlags::Pressed )
+ {
+ maFrameData.mnHelpState &= ~DrawButtonFlags::Pressed;
+ pBorderWindow->InvalidateBorder();
+ }
+ }
+ }
+ else
+ {
+ aMousePos.AdjustX( -(maFrameData.maMouseOff.X()) );
+ aMousePos.AdjustY( -(maFrameData.maMouseOff.Y()) );
+
+ if ( maFrameData.mnHitTest & BorderWindowHitTest::Title )
+ {
+ maFrameData.mpBorderWindow->SetPointer( PointerStyle::Move );
+
+ Point aPos = pBorderWindow->GetPosPixel();
+ aPos.AdjustX(aMousePos.X() );
+ aPos.AdjustY(aMousePos.Y() );
+ if ( maFrameData.mbDragFull )
+ {
+ pBorderWindow->SetPosPixel( aPos );
+ pBorderWindow->ImplUpdateAll();
+ pBorderWindow->ImplGetFrameWindow()->ImplUpdateAll();
+ }
+ else
+ {
+ maFrameData.mnTrackX = aPos.X();
+ maFrameData.mnTrackY = aPos.Y();
+ pBorderWindow->ShowTracking( tools::Rectangle( pBorderWindow->ScreenToOutputPixel( aPos ), pBorderWindow->GetOutputSizePixel() ), ShowTrackFlags::Big );
+ }
+ }
+ else
+ {
+ Point aOldPos = pBorderWindow->GetPosPixel();
+ Size aSize = pBorderWindow->GetSizePixel();
+ tools::Rectangle aNewRect( aOldPos, aSize );
+ tools::Long nOldWidth = aSize.Width();
+ tools::Long nOldHeight = aSize.Height();
+ tools::Long nBorderWidth = maFrameData.mnLeftBorder+maFrameData.mnRightBorder;
+ tools::Long nBorderHeight = maFrameData.mnTopBorder+maFrameData.mnBottomBorder;
+ tools::Long nMinWidth = pBorderWindow->mnMinWidth+nBorderWidth;
+ tools::Long nMinHeight = pBorderWindow->mnMinHeight+nBorderHeight;
+ tools::Long nMinWidth2 = nBorderWidth;
+ tools::Long nMaxWidth = pBorderWindow->mnMaxWidth+nBorderWidth;
+ tools::Long nMaxHeight = pBorderWindow->mnMaxHeight+nBorderHeight;
+
+ if ( maFrameData.mnTitleHeight )
+ {
+ nMinWidth2 += 4;
+
+ if ( pBorderWindow->GetStyle() & WB_CLOSEABLE )
+ nMinWidth2 += maFrameData.maCloseRect.GetWidth();
+ }
+ if ( nMinWidth2 > nMinWidth )
+ nMinWidth = nMinWidth2;
+ if ( maFrameData.mnHitTest & (BorderWindowHitTest::Left | BorderWindowHitTest::TopLeft | BorderWindowHitTest::BottomLeft) )
+ {
+ aNewRect.AdjustLeft(aMousePos.X() );
+ if ( aNewRect.GetWidth() < nMinWidth )
+ aNewRect.SetLeft( aNewRect.Right()-nMinWidth+1 );
+ else if ( aNewRect.GetWidth() > nMaxWidth )
+ aNewRect.SetLeft( aNewRect.Right()-nMaxWidth+1 );
+ }
+ else if ( maFrameData.mnHitTest & (BorderWindowHitTest::Right | BorderWindowHitTest::TopRight | BorderWindowHitTest::BottomRight) )
+ {
+ aNewRect.AdjustRight(aMousePos.X() );
+ if ( aNewRect.GetWidth() < nMinWidth )
+ aNewRect.SetRight( aNewRect.Left()+nMinWidth+1 );
+ else if ( aNewRect.GetWidth() > nMaxWidth )
+ aNewRect.SetRight( aNewRect.Left()+nMaxWidth+1 );
+ }
+ if ( maFrameData.mnHitTest & (BorderWindowHitTest::Top | BorderWindowHitTest::TopLeft | BorderWindowHitTest::TopRight) )
+ {
+ aNewRect.AdjustTop(aMousePos.Y() );
+ if ( aNewRect.GetHeight() < nMinHeight )
+ aNewRect.SetTop( aNewRect.Bottom()-nMinHeight+1 );
+ else if ( aNewRect.GetHeight() > nMaxHeight )
+ aNewRect.SetTop( aNewRect.Bottom()-nMaxHeight+1 );
+ }
+ else if ( maFrameData.mnHitTest & (BorderWindowHitTest::Bottom | BorderWindowHitTest::BottomLeft | BorderWindowHitTest::BottomRight) )
+ {
+ aNewRect.AdjustBottom(aMousePos.Y() );
+ if ( aNewRect.GetHeight() < nMinHeight )
+ aNewRect.SetBottom( aNewRect.Top()+nMinHeight+1 );
+ else if ( aNewRect.GetHeight() > nMaxHeight )
+ aNewRect.SetBottom( aNewRect.Top()+nMaxHeight+1 );
+ }
+
+ // call Resizing-Handler for SystemWindows
+ if ( pBorderWindow->ImplGetClientWindow()->IsSystemWindow() )
+ {
+ // adjust size for Resizing-call
+ aSize = aNewRect.GetSize();
+ aSize.AdjustWidth( -nBorderWidth );
+ aSize.AdjustHeight( -nBorderHeight );
+ static_cast<SystemWindow*>(pBorderWindow->ImplGetClientWindow())->Resizing( aSize );
+ aSize.AdjustWidth(nBorderWidth );
+ aSize.AdjustHeight(nBorderHeight );
+ if ( aSize.Width() < nMinWidth )
+ aSize.setWidth( nMinWidth );
+ if ( aSize.Height() < nMinHeight )
+ aSize.setHeight( nMinHeight );
+ if ( aSize.Width() > nMaxWidth )
+ aSize.setWidth( nMaxWidth );
+ if ( aSize.Height() > nMaxHeight )
+ aSize.setHeight( nMaxHeight );
+ if ( maFrameData.mnHitTest & (BorderWindowHitTest::Left | BorderWindowHitTest::TopLeft | BorderWindowHitTest::BottomLeft) )
+ aNewRect.SetLeft( aNewRect.Right()-aSize.Width()+1 );
+ else
+ aNewRect.SetRight( aNewRect.Left()+aSize.Width()-1 );
+ if ( maFrameData.mnHitTest & (BorderWindowHitTest::Top | BorderWindowHitTest::TopLeft | BorderWindowHitTest::TopRight) )
+ aNewRect.SetTop( aNewRect.Bottom()-aSize.Height()+1 );
+ else
+ aNewRect.SetBottom( aNewRect.Top()+aSize.Height()-1 );
+ }
+
+ if ( maFrameData.mbDragFull )
+ {
+ // no move (only resize) if position did not change
+ if( aOldPos != aNewRect.TopLeft() )
+ pBorderWindow->setPosSizePixel( aNewRect.Left(), aNewRect.Top(),
+ aNewRect.GetWidth(), aNewRect.GetHeight() );
+ else
+ pBorderWindow->setPosSizePixel( aNewRect.Left(), aNewRect.Top(),
+ aNewRect.GetWidth(), aNewRect.GetHeight(), PosSizeFlags::Size );
+
+ pBorderWindow->ImplUpdateAll();
+ pBorderWindow->ImplGetFrameWindow()->ImplUpdateAll();
+ if ( maFrameData.mnHitTest & (BorderWindowHitTest::Right | BorderWindowHitTest::TopRight | BorderWindowHitTest::BottomRight) )
+ maFrameData.maMouseOff.AdjustX(aNewRect.GetWidth()-nOldWidth );
+ if ( maFrameData.mnHitTest & (BorderWindowHitTest::Bottom | BorderWindowHitTest::BottomLeft | BorderWindowHitTest::BottomRight) )
+ maFrameData.maMouseOff.AdjustY(aNewRect.GetHeight()-nOldHeight );
+ }
+ else
+ {
+ maFrameData.mnTrackX = aNewRect.Left();
+ maFrameData.mnTrackY = aNewRect.Top();
+ maFrameData.mnTrackWidth = aNewRect.GetWidth();
+ maFrameData.mnTrackHeight = aNewRect.GetHeight();
+ pBorderWindow->ShowTracking( tools::Rectangle( pBorderWindow->ScreenToOutputPixel( aNewRect.TopLeft() ), aNewRect.GetSize() ), ShowTrackFlags::Big );
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+OUString ImplStdBorderWindowView::RequestHelp( const Point& rPos, tools::Rectangle& rHelpRect )
+{
+ return ImplRequestHelp( &maFrameData, rPos, rHelpRect );
+}
+
+tools::Rectangle ImplStdBorderWindowView::GetMenuRect() const
+{
+ return maFrameData.maMenuRect;
+}
+
+void ImplStdBorderWindowView::Init( OutputDevice* pDev, tools::Long nWidth, tools::Long nHeight )
+{
+ ImplBorderFrameData* pData = &maFrameData;
+ ImplBorderWindow* pBorderWindow = maFrameData.mpBorderWindow;
+ const StyleSettings& rStyleSettings = pDev->GetSettings().GetStyleSettings();
+ DecorationView aDecoView( pDev );
+ tools::Rectangle aRect( 0, 0, 10, 10 );
+ tools::Rectangle aCalcRect = aDecoView.DrawFrame( aRect, DrawFrameStyle::DoubleOut, DrawFrameFlags::NoDraw );
+
+ pData->mpOutDev = pDev;
+ pData->mnWidth = nWidth;
+ pData->mnHeight = nHeight;
+
+ pData->mnTitleType = pBorderWindow->mnTitleType;
+
+ if ( !(pBorderWindow->GetStyle() & (WB_MOVEABLE | WB_POPUP)) || (pData->mnTitleType == BorderWindowTitleType::NONE) )
+ pData->mnBorderSize = 0;
+ else if ( pData->mnTitleType == BorderWindowTitleType::Tearoff )
+ pData->mnBorderSize = 0;
+ else
+ pData->mnBorderSize = StyleSettings::GetBorderSize();
+ pData->mnLeftBorder = aCalcRect.Left();
+ pData->mnTopBorder = aCalcRect.Top();
+ pData->mnRightBorder = aRect.Right()-aCalcRect.Right();
+ pData->mnBottomBorder = aRect.Bottom()-aCalcRect.Bottom();
+ pData->mnLeftBorder += pData->mnBorderSize;
+ pData->mnTopBorder += pData->mnBorderSize;
+ pData->mnRightBorder += pData->mnBorderSize;
+ pData->mnBottomBorder += pData->mnBorderSize;
+ pData->mnNoTitleTop = pData->mnTopBorder;
+
+ ImplInitTitle(&maFrameData);
+ if (pData->mnTitleHeight)
+ {
+ // to improve symbol display force a minimum title height
+ if (pData->mnTitleType != BorderWindowTitleType::Tearoff &&
+ pData->mnTitleHeight < MIN_CAPTION_HEIGHT)
+ pData->mnTitleHeight = MIN_CAPTION_HEIGHT;
+
+ // set a proper background for drawing
+ // highlighted buttons in the title
+ pBorderWindow->SetBackground( rStyleSettings.GetFaceColor() );
+
+ pData->maTitleRect.SetLeft( pData->mnLeftBorder );
+ pData->maTitleRect.SetRight( nWidth-pData->mnRightBorder-1 );
+ pData->maTitleRect.SetTop( pData->mnTopBorder );
+ pData->maTitleRect.SetBottom( pData->maTitleRect.Top()+pData->mnTitleHeight-1 );
+
+ if ( pData->mnTitleType & (BorderWindowTitleType::Normal | BorderWindowTitleType::Small) )
+ {
+ tools::Long nRight = pData->maTitleRect.Right() - 3;
+ tools::Long const nItemTop = pData->maTitleRect.Top() + 2;
+ tools::Long const nItemBottom = pData->maTitleRect.Bottom() - 2;
+
+ auto addSquareOnRight = [&nRight, nItemTop, nItemBottom](
+ tools::Rectangle & rect, tools::Long gap)
+ {
+ rect.SetTop( nItemTop );
+ rect.SetBottom( nItemBottom );
+ rect.SetRight( nRight );
+ rect.SetLeft( rect.Right() - rect.GetHeight() + 1 );
+ nRight -= rect.GetWidth() + gap;
+ };
+
+ if ( pBorderWindow->GetStyle() & WB_CLOSEABLE )
+ {
+ addSquareOnRight(pData->maCloseRect, 3);
+ }
+
+ if ( pBorderWindow->mbMenuBtn )
+ {
+ addSquareOnRight(pData->maMenuRect, 0);
+ }
+
+ if ( pBorderWindow->mbDockBtn )
+ {
+ addSquareOnRight(pData->maDockRect, 0);
+ }
+
+ if ( pBorderWindow->mbHideBtn )
+ {
+ addSquareOnRight(pData->maHideRect, 0);
+ }
+ }
+ else
+ {
+ pData->maCloseRect.SetEmpty();
+ pData->maDockRect.SetEmpty();
+ pData->maMenuRect.SetEmpty();
+ pData->maHideRect.SetEmpty();
+ pData->maHelpRect.SetEmpty();
+ }
+
+ pData->mnTopBorder += pData->mnTitleHeight;
+ }
+ else
+ {
+ pData->maTitleRect.SetEmpty();
+ pData->maCloseRect.SetEmpty();
+ pData->maDockRect.SetEmpty();
+ pData->maMenuRect.SetEmpty();
+ pData->maHideRect.SetEmpty();
+ pData->maHelpRect.SetEmpty();
+ }
+}
+
+void ImplStdBorderWindowView::GetBorder( sal_Int32& rLeftBorder, sal_Int32& rTopBorder,
+ sal_Int32& rRightBorder, sal_Int32& rBottomBorder ) const
+{
+ rLeftBorder = maFrameData.mnLeftBorder;
+ rTopBorder = maFrameData.mnTopBorder;
+ rRightBorder = maFrameData.mnRightBorder;
+ rBottomBorder = maFrameData.mnBottomBorder;
+}
+
+tools::Long ImplStdBorderWindowView::CalcTitleWidth() const
+{
+ return ImplCalcTitleWidth( &maFrameData );
+}
+
+void ImplStdBorderWindowView::DrawWindow(vcl::RenderContext& rRenderContext, const Point* pOffset)
+{
+ ImplBorderFrameData* pData = &maFrameData;
+ ImplBorderWindow* pBorderWindow = pData->mpBorderWindow;
+ Point aTmpPoint = pOffset ? *pOffset : Point();
+ tools::Rectangle aInRect( aTmpPoint, Size( pData->mnWidth, pData->mnHeight ) );
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ Color aFaceColor(rStyleSettings.GetFaceColor());
+ Color aFrameColor(aFaceColor);
+
+ aFrameColor.DecreaseContrast(sal_uInt8(0.5 * 255));
+
+ // Draw Frame
+ vcl::Region oldClipRgn(rRenderContext.GetClipRegion());
+
+ // for popups, don't draw part of the frame
+ const bool bShowJunctionToLauncher = !(pData->mnTitleType & (BorderWindowTitleType::Normal | BorderWindowTitleType::Small));
+ if (bShowJunctionToLauncher && !ImplGetSVData()->maNWFData.mbNoFrameJunctionForPopups)
+ {
+ FloatingWindow* pWin = dynamic_cast<FloatingWindow*>(pData->mpBorderWindow->GetWindow(GetWindowType::Client));
+ if (pWin)
+ {
+ vcl::Region aClipRgn(aInRect);
+ AbsoluteScreenPixelRectangle aItemClipRect(pWin->ImplGetItemEdgeClipRect());
+ if (!aItemClipRect.IsEmpty())
+ {
+ tools::Rectangle aTmp(pData->mpBorderWindow->AbsoluteScreenToOutputPixel(aItemClipRect.TopLeft()), aItemClipRect.GetSize());
+ aClipRgn.Exclude(aTmp);
+ rRenderContext.SetClipRegion(aClipRgn);
+ }
+ }
+ }
+
+ // single line frame
+ rRenderContext.SetLineColor(aFrameColor);
+ rRenderContext.SetFillColor();
+ rRenderContext.DrawRect(aInRect);
+ aInRect.AdjustLeft( 1 );
+ aInRect.AdjustRight( -1 );
+ aInRect.AdjustTop( 1 );
+ aInRect.AdjustBottom( -1 );
+
+ // restore
+ if (!(pData->mnTitleType & (BorderWindowTitleType::Normal | BorderWindowTitleType::Small)))
+ rRenderContext.SetClipRegion(oldClipRgn);
+
+ // Draw Border
+ rRenderContext.SetLineColor();
+ tools::Long nBorderSize = pData->mnBorderSize;
+ if (nBorderSize)
+ {
+ rRenderContext.SetFillColor(rStyleSettings.GetFaceColor());
+ rRenderContext.DrawRect(tools::Rectangle(Point(aInRect.Left(), aInRect.Top()),
+ Size(aInRect.GetWidth(), nBorderSize)));
+ rRenderContext.DrawRect(tools::Rectangle(Point(aInRect.Left(), aInRect.Top() + nBorderSize),
+ Size(nBorderSize, aInRect.GetHeight() - nBorderSize)));
+ rRenderContext.DrawRect(tools::Rectangle(Point(aInRect.Left(), aInRect.Bottom() - nBorderSize + 1),
+ Size(aInRect.GetWidth(), nBorderSize)));
+ rRenderContext.DrawRect(tools::Rectangle(Point(aInRect.Right()-nBorderSize + 1, aInRect.Top() + nBorderSize),
+ Size(nBorderSize, aInRect.GetHeight() - nBorderSize)));
+ }
+
+ // Draw Title
+ if (!pData->maTitleRect.IsEmpty())
+ {
+ aInRect = pData->maTitleRect;
+
+ // use no gradient anymore, just a static titlecolor
+ if (pData->mnTitleType == BorderWindowTitleType::Tearoff)
+ rRenderContext.SetFillColor(rStyleSettings.GetFaceGradientColor());
+ else if (pData->mnTitleType == BorderWindowTitleType::Popup)
+ rRenderContext.SetFillColor(aFaceColor);
+ else
+ rRenderContext.SetFillColor(aFrameColor);
+
+ rRenderContext.SetTextColor(rStyleSettings.GetButtonTextColor());
+ tools::Rectangle aTitleRect(pData->maTitleRect);
+ if(pOffset)
+ aTitleRect.Move(pOffset->X(), pOffset->Y());
+ rRenderContext.DrawRect(aTitleRect);
+
+ if (pData->mnTitleType != BorderWindowTitleType::Tearoff)
+ {
+ aInRect.AdjustLeft(2 );
+ aInRect.AdjustRight( -2 );
+
+ if (!pData->maHelpRect.IsEmpty())
+ aInRect.SetRight( pData->maHelpRect.Left() - 2 );
+ else if (!pData->maHideRect.IsEmpty())
+ aInRect.SetRight( pData->maHideRect.Left() - 2 );
+ else if (!pData->maDockRect.IsEmpty())
+ aInRect.SetRight( pData->maDockRect.Left() - 2 );
+ else if (!pData->maMenuRect.IsEmpty())
+ aInRect.SetRight( pData->maMenuRect.Left() - 2 );
+ else if (!pData->maCloseRect.IsEmpty())
+ aInRect.SetRight( pData->maCloseRect.Left() - 2 );
+
+ if (pOffset)
+ aInRect.Move(pOffset->X(), pOffset->Y());
+
+ DrawTextFlags nTextStyle = DrawTextFlags::Left | DrawTextFlags::VCenter | DrawTextFlags::EndEllipsis | DrawTextFlags::Clip;
+
+ // must show tooltip ?
+ TextRectInfo aInfo;
+ rRenderContext.GetTextRect(aInRect, pBorderWindow->GetText(), nTextStyle, &aInfo);
+ pData->mbTitleClipped = aInfo.IsEllipses();
+
+ rRenderContext.DrawText(aInRect, pBorderWindow->GetText(), nTextStyle);
+ }
+ else
+ {
+ ToolBox::ImplDrawGrip(rRenderContext, aTitleRect, ToolBox::ImplGetDragWidth(rRenderContext, false),
+ WindowAlign::Left, false);
+ }
+ }
+
+ if (!pData->maCloseRect.IsEmpty())
+ {
+ tools::Rectangle aSymbolRect(pData->maCloseRect);
+ if (pOffset)
+ aSymbolRect.Move(pOffset->X(), pOffset->Y());
+ ImplDrawBrdWinSymbolButton(&rRenderContext, aSymbolRect, SymbolType::CLOSE, pData->mnCloseState);
+ }
+ if (!pData->maDockRect.IsEmpty())
+ {
+ tools::Rectangle aSymbolRect(pData->maDockRect);
+ if (pOffset)
+ aSymbolRect.Move(pOffset->X(), pOffset->Y());
+ ImplDrawBrdWinSymbolButton(&rRenderContext, aSymbolRect, SymbolType::DOCK, pData->mnDockState);
+ }
+ if (!pData->maMenuRect.IsEmpty())
+ {
+ tools::Rectangle aSymbolRect(pData->maMenuRect);
+ if (pOffset)
+ aSymbolRect.Move(pOffset->X(), pOffset->Y());
+ ImplDrawBrdWinSymbolButton(&rRenderContext, aSymbolRect, SymbolType::MENU, pData->mnMenuState);
+ }
+ if (!pData->maHideRect.IsEmpty())
+ {
+ tools::Rectangle aSymbolRect(pData->maHideRect);
+ if (pOffset)
+ aSymbolRect.Move(pOffset->X(), pOffset->Y());
+ ImplDrawBrdWinSymbolButton(&rRenderContext, aSymbolRect, SymbolType::HIDE, pData->mnHideState);
+ }
+
+ if (!pData->maHelpRect.IsEmpty())
+ {
+ tools::Rectangle aSymbolRect(pData->maHelpRect);
+ if (pOffset)
+ aSymbolRect.Move(pOffset->X(), pOffset->Y());
+ ImplDrawBrdWinSymbolButton(&rRenderContext, aSymbolRect, SymbolType::HELP, pData->mnHelpState);
+ }
+}
+
+void ImplBorderWindow::ImplInit( vcl::Window* pParent,
+ WinBits nStyle, BorderWindowStyle nTypeStyle,
+ SystemParentData* pSystemParentData
+ )
+{
+ // remove all unwanted WindowBits
+ WinBits nOrgStyle = nStyle;
+ WinBits nTestStyle = (WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE | WB_STANDALONE | WB_DIALOGCONTROL | WB_NODIALOGCONTROL | WB_SYSTEMFLOATWIN | WB_INTROWIN | WB_DEFAULTWIN | WB_TOOLTIPWIN | WB_NOSHADOW | WB_OWNERDRAWDECORATION | WB_SYSTEMCHILDWINDOW | WB_POPUP);
+ if ( nTypeStyle & BorderWindowStyle::App )
+ nTestStyle |= WB_APP;
+ nStyle &= nTestStyle;
+
+ mpWindowImpl->mbBorderWin = true;
+ mbSmallOutBorder = false;
+ if ( nTypeStyle & BorderWindowStyle::Frame )
+ {
+ mpWindowImpl->mbOverlapWin = true;
+ mpWindowImpl->mbFrame = true;
+
+ if( nStyle & WB_SYSTEMCHILDWINDOW )
+ {
+ mbFrameBorder = false;
+ }
+ else if( nStyle & (WB_OWNERDRAWDECORATION | WB_POPUP) )
+ {
+ mbFrameBorder = (nOrgStyle & WB_NOBORDER) == 0;
+ }
+ else
+ {
+ mbFrameBorder = false;
+ // closeable windows may have a border as well, eg. system floating windows without caption
+ if ( (nOrgStyle & (WB_BORDER | WB_NOBORDER | WB_MOVEABLE | WB_SIZEABLE/* | WB_CLOSEABLE*/)) == WB_BORDER )
+ mbSmallOutBorder = true;
+ }
+ }
+ else if ( nTypeStyle & BorderWindowStyle::Overlap )
+ {
+ mpWindowImpl->mbOverlapWin = true;
+ mbFrameBorder = true;
+ }
+ else
+ mbFrameBorder = false;
+
+ if ( nTypeStyle & BorderWindowStyle::Float )
+ mbFloatWindow = true;
+ else
+ mbFloatWindow = false;
+
+ Window::ImplInit( pParent, nStyle, pSystemParentData );
+ SetBackground();
+ SetTextFillColor();
+
+ mpMenuBarWindow = nullptr;
+ mnMinWidth = 0;
+ mnMinHeight = 0;
+ mnMaxWidth = SHRT_MAX;
+ mnMaxHeight = SHRT_MAX;
+ mnOrgMenuHeight = 0;
+ mbMenuHide = false;
+ mbDockBtn = false;
+ mbMenuBtn = false;
+ mbHideBtn = false;
+ mbDisplayActive = IsActive();
+
+ if ( nTypeStyle & BorderWindowStyle::Float )
+ mnTitleType = BorderWindowTitleType::Small;
+ else
+ mnTitleType = BorderWindowTitleType::Normal;
+ mnBorderStyle = WindowBorderStyle::NORMAL;
+ InitView();
+}
+
+ImplBorderWindow::ImplBorderWindow( vcl::Window* pParent,
+ SystemParentData* pSystemParentData,
+ WinBits nStyle, BorderWindowStyle nTypeStyle
+ ) : Window( WindowType::BORDERWINDOW )
+{
+ ImplInit( pParent, nStyle, nTypeStyle, pSystemParentData );
+}
+
+ImplBorderWindow::ImplBorderWindow( vcl::Window* pParent, WinBits nStyle ,
+ BorderWindowStyle nTypeStyle ) :
+ Window( WindowType::BORDERWINDOW )
+{
+ ImplInit( pParent, nStyle, nTypeStyle, nullptr );
+}
+
+ImplBorderWindow::~ImplBorderWindow()
+{
+ disposeOnce();
+}
+
+void ImplBorderWindow::dispose()
+{
+ mpBorderView.reset();
+ mpMenuBarWindow.clear();
+ mpNotebookBar.disposeAndClear();
+ vcl::Window::dispose();
+}
+
+void ImplBorderWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ if (mpBorderView)
+ mpBorderView->MouseMove( rMEvt );
+}
+
+void ImplBorderWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (mpBorderView)
+ mpBorderView->MouseButtonDown( rMEvt );
+}
+
+void ImplBorderWindow::Tracking( const TrackingEvent& rTEvt )
+{
+ if (mpBorderView)
+ mpBorderView->Tracking( rTEvt );
+}
+
+void ImplBorderWindow::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
+{
+ if (mpBorderView)
+ mpBorderView->DrawWindow(rRenderContext);
+}
+
+void ImplBorderWindow::Draw( OutputDevice* pOutDev, const Point& rPos )
+{
+ if (mpBorderView)
+ mpBorderView->DrawWindow(*pOutDev, &rPos);
+}
+
+void ImplBorderWindow::Activate()
+{
+ SetDisplayActive( true );
+ Window::Activate();
+}
+
+void ImplBorderWindow::Deactivate()
+{
+ // remove active windows from the ruler, also ignore the Deactivate
+ // if a menu becomes active
+ if (GetActivateMode() != ActivateModeFlags::NONE && !ImplGetSVData()->mpWinData->mbNoDeactivate)
+ SetDisplayActive( false );
+ Window::Deactivate();
+}
+
+void ImplBorderWindow::RequestHelp( const HelpEvent& rHEvt )
+{
+ // no keyboard help for border window
+ if ( rHEvt.GetMode() & (HelpEventMode::BALLOON | HelpEventMode::QUICK) && !rHEvt.KeyboardActivated() )
+ {
+ Point aMousePosPixel = ScreenToOutputPixel( rHEvt.GetMousePosPixel() );
+ tools::Rectangle aHelpRect;
+ OUString aHelpStr( mpBorderView->RequestHelp( aMousePosPixel, aHelpRect ) );
+
+ // retrieve rectangle
+ if ( !aHelpStr.isEmpty() )
+ {
+ aHelpRect.SetPos( OutputToScreenPixel( aHelpRect.TopLeft() ) );
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ Help::ShowBalloon( this, aHelpRect.Center(), aHelpRect, aHelpStr );
+ else
+ Help::ShowQuickHelp( this, aHelpRect, aHelpStr );
+ return;
+ }
+ }
+
+ Window::RequestHelp( rHEvt );
+}
+
+void ImplBorderWindow::Resize()
+{
+ Size aSize = GetOutputSizePixel();
+
+ vcl::Window* pClientWindow = ImplGetClientWindow();
+
+ sal_Int32 nLeftBorder;
+ sal_Int32 nTopBorder;
+ sal_Int32 nRightBorder;
+ sal_Int32 nBottomBorder;
+ mpBorderView->GetBorder( nLeftBorder, nTopBorder, nRightBorder, nBottomBorder );
+
+ if (mpMenuBarWindow)
+ {
+ tools::Long nMenuHeight = mpMenuBarWindow->GetSizePixel().Height();
+ if ( mbMenuHide )
+ {
+ if ( nMenuHeight )
+ mnOrgMenuHeight = nMenuHeight;
+ nMenuHeight = 0;
+ }
+ else
+ {
+ if ( !nMenuHeight )
+ nMenuHeight = mnOrgMenuHeight;
+ }
+ mpMenuBarWindow->setPosSizePixel(
+ nLeftBorder, nTopBorder,
+ aSize.Width()-nLeftBorder-nRightBorder,
+ nMenuHeight);
+
+ // shift the notebookbar down accordingly
+ nTopBorder += nMenuHeight;
+ }
+
+ if (mpNotebookBar)
+ {
+ tools::Long nNotebookBarHeight = mpNotebookBar->GetSizePixel().Height();
+
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ const BitmapEx& aPersona = rStyleSettings.GetPersonaHeader();
+ // since size of notebookbar changes, to make common persona for menubar
+ // and notebookbar persona should be set again with changed coordinates
+ if (!aPersona.IsEmpty())
+ {
+ Wallpaper aWallpaper(aPersona);
+ aWallpaper.SetStyle(WallpaperStyle::TopRight);
+ aWallpaper.SetRect(tools::Rectangle(Point(0, -nTopBorder),
+ Size(aSize.Width() - nLeftBorder - nRightBorder,
+ nNotebookBarHeight + nTopBorder)));
+ mpNotebookBar->SetBackground(aWallpaper);
+ }
+
+ mpNotebookBar->setPosSizePixel(
+ nLeftBorder, nTopBorder,
+ aSize.Width() - nLeftBorder - nRightBorder,
+ nNotebookBarHeight);
+ }
+
+ GetBorder( pClientWindow->mpWindowImpl->mnLeftBorder, pClientWindow->mpWindowImpl->mnTopBorder,
+ pClientWindow->mpWindowImpl->mnRightBorder, pClientWindow->mpWindowImpl->mnBottomBorder );
+ pClientWindow->ImplPosSizeWindow( pClientWindow->mpWindowImpl->mnLeftBorder,
+ pClientWindow->mpWindowImpl->mnTopBorder,
+ aSize.Width()-pClientWindow->mpWindowImpl->mnLeftBorder-pClientWindow->mpWindowImpl->mnRightBorder,
+ aSize.Height()-pClientWindow->mpWindowImpl->mnTopBorder-pClientWindow->mpWindowImpl->mnBottomBorder,
+ PosSizeFlags::X | PosSizeFlags::Y |
+ PosSizeFlags::Width | PosSizeFlags::Height );
+
+ // UpdateView
+ mpBorderView->Init( GetOutDev(), aSize.Width(), aSize.Height() );
+ InvalidateBorder();
+
+ Window::Resize();
+}
+
+void ImplBorderWindow::StateChanged( StateChangedType nType )
+{
+ if ( (nType == StateChangedType::Text) ||
+ (nType == StateChangedType::Data) )
+ {
+ if (IsReallyVisible() && mbFrameBorder)
+ InvalidateBorder();
+ }
+
+ Window::StateChanged( nType );
+}
+
+void ImplBorderWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
+ ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
+ {
+ if ( !mpWindowImpl->mbFrame || (GetStyle() & (WB_OWNERDRAWDECORATION | WB_POPUP)) )
+ UpdateView( true, ImplGetWindow()->GetOutputSizePixel() );
+ }
+
+ Window::DataChanged( rDCEvt );
+}
+
+void ImplBorderWindow::InitView()
+{
+ if ( mbSmallOutBorder )
+ mpBorderView.reset(new ImplSmallBorderWindowView( this ));
+ else if ( mpWindowImpl->mbFrame )
+ {
+ if( mbFrameBorder )
+ mpBorderView.reset(new ImplStdBorderWindowView( this ));
+ else
+ mpBorderView.reset(new ImplNoBorderWindowView);
+ }
+ else if ( !mbFrameBorder )
+ mpBorderView.reset(new ImplSmallBorderWindowView( this ));
+ else
+ mpBorderView.reset(new ImplStdBorderWindowView( this ));
+ Size aSize = GetOutputSizePixel();
+ mpBorderView->Init( GetOutDev(), aSize.Width(), aSize.Height() );
+}
+
+void ImplBorderWindow::UpdateView( bool bNewView, const Size& rNewOutSize )
+{
+ sal_Int32 nLeftBorder;
+ sal_Int32 nTopBorder;
+ sal_Int32 nRightBorder;
+ sal_Int32 nBottomBorder;
+ Size aOldSize = GetSizePixel();
+ Size aOutputSize = rNewOutSize;
+
+ if ( bNewView )
+ {
+ mpBorderView.reset();
+ InitView();
+ }
+ else
+ {
+ Size aSize = aOutputSize;
+ mpBorderView->GetBorder( nLeftBorder, nTopBorder, nRightBorder, nBottomBorder );
+ aSize.AdjustWidth(nLeftBorder+nRightBorder );
+ aSize.AdjustHeight(nTopBorder+nBottomBorder );
+ mpBorderView->Init( GetOutDev(), aSize.Width(), aSize.Height() );
+ }
+
+ vcl::Window* pClientWindow = ImplGetClientWindow();
+ if ( pClientWindow )
+ {
+ GetBorder( pClientWindow->mpWindowImpl->mnLeftBorder, pClientWindow->mpWindowImpl->mnTopBorder,
+ pClientWindow->mpWindowImpl->mnRightBorder, pClientWindow->mpWindowImpl->mnBottomBorder );
+ }
+ GetBorder( nLeftBorder, nTopBorder, nRightBorder, nBottomBorder );
+ if ( aOldSize.Width() || aOldSize.Height() )
+ {
+ aOutputSize.AdjustWidth(nLeftBorder+nRightBorder );
+ aOutputSize.AdjustHeight(nTopBorder+nBottomBorder );
+ if ( aOutputSize == GetSizePixel() )
+ InvalidateBorder();
+ else
+ SetSizePixel( aOutputSize );
+ }
+}
+
+void ImplBorderWindow::InvalidateBorder()
+{
+ if ( !IsReallyVisible() )
+ return;
+
+ // invalidate only if we have a border
+ sal_Int32 nLeftBorder;
+ sal_Int32 nTopBorder;
+ sal_Int32 nRightBorder;
+ sal_Int32 nBottomBorder;
+ mpBorderView->GetBorder( nLeftBorder, nTopBorder, nRightBorder, nBottomBorder );
+ if ( !(nLeftBorder || nTopBorder || nRightBorder || nBottomBorder) )
+ return;
+
+ tools::Rectangle aWinRect( Point( 0, 0 ), GetOutputSizePixel() );
+ vcl::Region aRegion( aWinRect );
+ aWinRect.AdjustLeft(nLeftBorder );
+ aWinRect.AdjustTop(nTopBorder );
+ aWinRect.AdjustRight( -nRightBorder );
+ aWinRect.AdjustBottom( -nBottomBorder );
+ // no output area anymore, now invalidate all
+ if ( (aWinRect.Right() < aWinRect.Left()) ||
+ (aWinRect.Bottom() < aWinRect.Top()) )
+ Invalidate( InvalidateFlags::NoChildren );
+ else
+ {
+ aRegion.Exclude( aWinRect );
+ Invalidate( aRegion, InvalidateFlags::NoChildren );
+ }
+}
+
+void ImplBorderWindow::SetDisplayActive( bool bActive )
+{
+ if ( mbDisplayActive != bActive )
+ {
+ mbDisplayActive = bActive;
+ if ( mbFrameBorder )
+ InvalidateBorder();
+ }
+}
+
+void ImplBorderWindow::SetTitleType( BorderWindowTitleType nTitleType, const Size& rSize )
+{
+ mnTitleType = nTitleType;
+ UpdateView( false, rSize );
+}
+
+void ImplBorderWindow::SetBorderStyle( WindowBorderStyle nStyle )
+{
+ if ( !mbFrameBorder && (mnBorderStyle != nStyle) )
+ {
+ mnBorderStyle = nStyle;
+ UpdateView( false, ImplGetWindow()->GetOutputSizePixel() );
+ }
+}
+
+void ImplBorderWindow::SetCloseButton()
+{
+ SetStyle( GetStyle() | WB_CLOSEABLE );
+ Size aSize = GetOutputSizePixel();
+ mpBorderView->Init( GetOutDev(), aSize.Width(), aSize.Height() );
+ InvalidateBorder();
+}
+
+void ImplBorderWindow::SetDockButton( bool bDockButton )
+{
+ mbDockBtn = bDockButton;
+ Size aSize = GetOutputSizePixel();
+ mpBorderView->Init( GetOutDev(), aSize.Width(), aSize.Height() );
+ InvalidateBorder();
+}
+
+void ImplBorderWindow::SetHideButton( bool bHideButton )
+{
+ mbHideBtn = bHideButton;
+ Size aSize = GetOutputSizePixel();
+ mpBorderView->Init( GetOutDev(), aSize.Width(), aSize.Height() );
+ InvalidateBorder();
+}
+
+void ImplBorderWindow::SetMenuButton( bool bMenuButton )
+{
+ mbMenuBtn = bMenuButton;
+ Size aSize = GetOutputSizePixel();
+ mpBorderView->Init( GetOutDev(), aSize.Width(), aSize.Height() );
+ InvalidateBorder();
+}
+
+void ImplBorderWindow::UpdateMenuHeight()
+{
+ Resize();
+}
+
+void ImplBorderWindow::SetMenuBarWindow( vcl::Window* pWindow )
+{
+ mpMenuBarWindow = pWindow;
+ UpdateMenuHeight();
+ if ( pWindow )
+ pWindow->Show();
+}
+
+void ImplBorderWindow::SetMenuBarMode( bool bHide )
+{
+ mbMenuHide = bHide;
+ UpdateMenuHeight();
+}
+
+void ImplBorderWindow::SetNotebookBar(const OUString& rUIXMLDescription,
+ const css::uno::Reference<css::frame::XFrame>& rFrame,
+ const NotebookBarAddonsItem& aNotebookBarAddonsItem)
+{
+ if (mpNotebookBar)
+ mpNotebookBar.disposeAndClear();
+ mpNotebookBar = VclPtr<NotebookBar>::Create(this, "NotebookBar", rUIXMLDescription, rFrame,
+ aNotebookBarAddonsItem);
+ Resize();
+}
+
+void ImplBorderWindow::CloseNotebookBar()
+{
+ if (mpNotebookBar)
+ mpNotebookBar.disposeAndClear();
+ mpNotebookBar = nullptr;
+ Resize();
+}
+
+void ImplBorderWindow::GetBorder( sal_Int32& rLeftBorder, sal_Int32& rTopBorder,
+ sal_Int32& rRightBorder, sal_Int32& rBottomBorder ) const
+{
+ mpBorderView->GetBorder(rLeftBorder, rTopBorder, rRightBorder, rBottomBorder);
+
+ if (mpMenuBarWindow && !mbMenuHide)
+ rTopBorder += mpMenuBarWindow->GetSizePixel().Height();
+
+ if (mpNotebookBar && mpNotebookBar->IsVisible())
+ rTopBorder += mpNotebookBar->GetSizePixel().Height();
+}
+
+tools::Long ImplBorderWindow::CalcTitleWidth() const
+{
+ return mpBorderView->CalcTitleWidth();
+}
+
+tools::Rectangle ImplBorderWindow::GetMenuRect() const
+{
+ return mpBorderView->GetMenuRect();
+}
+
+Size ImplBorderWindow::GetOptimalSize() const
+{
+ const vcl::Window* pClientWindow = ImplGetClientWindow();
+ if (pClientWindow)
+ return pClientWindow->GetOptimalSize();
+ return Size(mnMinWidth, mnMinHeight);
+}
+
+void ImplBorderWindow::queue_resize(StateChangedType eReason)
+{
+ //if we are floating, then we don't want to inform our parent that it needs
+ //to calculate a new layout allocation. Because while we are a child
+ //of our parent we are not embedded into the parent so it doesn't care
+ //about us.
+ if (mbFloatWindow)
+ return;
+ vcl::Window::queue_resize(eReason);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/bubblewindow.cxx b/vcl/source/window/bubblewindow.cxx
new file mode 100644
index 0000000000..aa9e51dfa3
--- /dev/null
+++ b/vcl/source/window/bubblewindow.cxx
@@ -0,0 +1,591 @@
+/* -*- 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/ustrbuf.hxx>
+#include <utility>
+#include <vcl/menubarupdateicon.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/resmgr.hxx>
+#include <bubblewindow.hxx>
+#include <bitmaps.hlst>
+
+#define TIP_HEIGHT 15
+#define TIP_WIDTH 7
+#define TIP_RIGHT_OFFSET 18
+#define BUBBLE_BORDER 10
+#define TEXT_MAX_WIDTH 300
+#define TEXT_MAX_HEIGHT 200
+
+BubbleWindow::BubbleWindow( vcl::Window* pParent, OUString aTitle,
+ OUString aText, Image aImage )
+ : FloatingWindow( pParent, WB_SYSTEMWINDOW
+ | WB_OWNERDRAWDECORATION
+ | WB_NOBORDER
+ )
+ , maBubbleTitle(std::move( aTitle ))
+ , maBubbleText(std::move( aText ))
+ , maBubbleImage(std::move( aImage ))
+ , maMaxTextSize( TEXT_MAX_WIDTH, TEXT_MAX_HEIGHT )
+ , mnTipOffset( 0 )
+{
+ SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetHelpColor() ) );
+}
+
+void BubbleWindow::Resize()
+{
+ FloatingWindow::Resize();
+
+ Size aSize = GetSizePixel();
+
+ if ( ( aSize.Height() < 20 ) || ( aSize.Width() < 60 ) )
+ return;
+
+ tools::Rectangle aRect( 0, TIP_HEIGHT, aSize.Width(), aSize.Height() - TIP_HEIGHT );
+ maRectPoly = tools::Polygon( aRect, 6, 6 );
+ vcl::Region aRegion( maRectPoly );
+ tools::Long nTipOffset = aSize.Width() - TIP_RIGHT_OFFSET + mnTipOffset;
+
+ Point aPointArr[4];
+ aPointArr[0] = Point( nTipOffset, TIP_HEIGHT );
+ aPointArr[1] = Point( nTipOffset, 0 );
+ aPointArr[2] = Point( nTipOffset + TIP_WIDTH , TIP_HEIGHT );
+ aPointArr[3] = Point( nTipOffset, TIP_HEIGHT );
+ maTriPoly = tools::Polygon( 4, aPointArr );
+ vcl::Region aTriRegion( maTriPoly );
+
+ aRegion.Union( aTriRegion);
+ maBounds = aRegion;
+
+ SetWindowRegionPixel( maBounds );
+}
+
+void BubbleWindow::SetTitleAndText( const OUString& rTitle,
+ const OUString& rText,
+ const Image& rImage )
+{
+ maBubbleTitle = rTitle;
+ maBubbleText = rText;
+ maBubbleImage = rImage;
+
+ Resize();
+}
+
+void BubbleWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
+{
+ LineInfo aThickLine( LineStyle::Solid, 2 );
+
+ rRenderContext.DrawPolyLine( maRectPoly, aThickLine );
+ rRenderContext.DrawPolyLine( maTriPoly );
+
+ Color aOldLine = rRenderContext.GetLineColor();
+ Size aSize = GetSizePixel();
+ tools::Long nTipOffset = aSize.Width() - TIP_RIGHT_OFFSET + mnTipOffset;
+
+ rRenderContext.SetLineColor( GetSettings().GetStyleSettings().GetHelpColor() );
+ rRenderContext.DrawLine( Point( nTipOffset+2, TIP_HEIGHT ),
+ Point( nTipOffset + TIP_WIDTH -1 , TIP_HEIGHT ),
+ aThickLine );
+ rRenderContext.SetLineColor( aOldLine );
+
+ Size aImgSize = maBubbleImage.GetSizePixel();
+
+ rRenderContext.DrawImage( Point( BUBBLE_BORDER, BUBBLE_BORDER + TIP_HEIGHT ), maBubbleImage );
+
+ vcl::Font aOldFont = GetFont();
+ vcl::Font aBoldFont = aOldFont;
+ aBoldFont.SetWeight( WEIGHT_BOLD );
+
+ SetFont( aBoldFont );
+ tools::Rectangle aTitleRect = maTitleRect;
+ aTitleRect.Move( aImgSize.Width(), 0 );
+ rRenderContext.DrawText( aTitleRect, maBubbleTitle, DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
+
+ SetFont( aOldFont );
+ tools::Rectangle aTextRect = maTextRect;
+ aTextRect.Move( aImgSize.Width(), 0 );
+ rRenderContext.DrawText( aTextRect, maBubbleText, DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
+}
+
+void BubbleWindow::MouseButtonDown( const MouseEvent& )
+{
+ Show( false );
+}
+
+void BubbleWindow::Show( bool bVisible )
+{
+ if ( !bVisible )
+ {
+ FloatingWindow::Show( bVisible );
+ return;
+ }
+
+ // don't show bubbles without a text
+ if ( ( maBubbleTitle.isEmpty() ) && ( maBubbleText.isEmpty() ) )
+ return;
+
+ Size aWindowSize = GetSizePixel();
+
+ Size aImgSize = maBubbleImage.GetSizePixel();
+
+ RecalcTextRects();
+
+ aWindowSize.setHeight( maTitleRect.GetHeight() * 7 / 4+ maTextRect.GetHeight() +
+ 3 * BUBBLE_BORDER + TIP_HEIGHT );
+
+ if ( maTitleRect.GetWidth() > maTextRect.GetWidth() )
+ aWindowSize.setWidth( maTitleRect.GetWidth() );
+ else
+ aWindowSize.setWidth( maTextRect.GetWidth() );
+
+ aWindowSize.setWidth( aWindowSize.Width() + 3 * BUBBLE_BORDER + aImgSize.Width() );
+
+ if ( aWindowSize.Height() < aImgSize.Height() + TIP_HEIGHT + 2 * BUBBLE_BORDER )
+ aWindowSize.setHeight( aImgSize.Height() + TIP_HEIGHT + 2 * BUBBLE_BORDER );
+
+ Point aPos;
+ aPos.setX( maTipPos.X() - aWindowSize.Width() + TIP_RIGHT_OFFSET );
+ aPos.setY( maTipPos.Y() );
+ AbsoluteScreenPixelPoint aScreenPos = GetParent()->OutputToAbsoluteScreenPixel( aPos );
+ if ( aScreenPos.X() < 0 )
+ {
+ mnTipOffset = aScreenPos.X();
+ aPos.AdjustX( -mnTipOffset );
+ }
+ SetPosSizePixel( aPos, aWindowSize );
+
+ FloatingWindow::Show( bVisible, ShowFlags::NoActivate );
+}
+
+void BubbleWindow::RecalcTextRects()
+{
+ Size aTotalSize;
+ bool bFinished = false;
+ vcl::Font aOldFont = GetFont();
+ vcl::Font aBoldFont = aOldFont;
+
+ aBoldFont.SetWeight( WEIGHT_BOLD );
+
+ while ( !bFinished )
+ {
+ SetFont( aBoldFont );
+
+ maTitleRect = GetTextRect( tools::Rectangle( Point( 0, 0 ), maMaxTextSize ),
+ maBubbleTitle,
+ DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
+
+ SetFont( aOldFont );
+ maTextRect = GetTextRect( tools::Rectangle( Point( 0, 0 ), maMaxTextSize ),
+ maBubbleText,
+ DrawTextFlags::MultiLine | DrawTextFlags::WordBreak );
+
+ if ( maTextRect.GetHeight() < 10 )
+ maTextRect.setHeight( 10 );
+
+ aTotalSize.setHeight( maTitleRect.GetHeight() +
+ aBoldFont.GetFontHeight() * 3 / 4 +
+ maTextRect.GetHeight() +
+ 3 * BUBBLE_BORDER + TIP_HEIGHT );
+ if ( aTotalSize.Height() > maMaxTextSize.Height() )
+ {
+ maMaxTextSize.setWidth( maMaxTextSize.Width() * 3 / 2 );
+ maMaxTextSize.setHeight( maMaxTextSize.Height() * 3 / 2 );
+ }
+ else
+ bFinished = true;
+ }
+ maTitleRect.Move( 2*BUBBLE_BORDER, BUBBLE_BORDER + TIP_HEIGHT );
+ maTextRect.Move( 2*BUBBLE_BORDER, BUBBLE_BORDER + TIP_HEIGHT + maTitleRect.GetHeight() + aBoldFont.GetFontHeight() * 3 / 4 );
+}
+
+MenuBarUpdateIconManager::MenuBarUpdateIconManager()
+ : maTimeoutTimer("MenuBarUpdateIconManager")
+ , maWaitIdle("vcl MenuBarUpdateIconManager maWaitIdle")
+ , mbShowMenuIcon(false)
+ , mbShowBubble(false)
+ , mbBubbleChanged( false )
+{
+ maTimeoutTimer.SetTimeout( 10000 );
+ maTimeoutTimer.SetInvokeHandler(LINK(this, MenuBarUpdateIconManager, TimeOutHdl));
+
+ maWaitIdle.SetPriority( TaskPriority::LOWEST );
+ maWaitIdle.SetInvokeHandler(LINK(this, MenuBarUpdateIconManager, WaitTimeOutHdl));
+
+ maApplicationEventHdl = LINK(this, MenuBarUpdateIconManager, ApplicationEventHdl);
+ Application::AddEventListener( maApplicationEventHdl );
+
+ maWindowEventHdl = LINK(this, MenuBarUpdateIconManager, WindowEventHdl);
+}
+
+sal_uInt16 MenuBarUpdateIconManager::GetIconID(MenuBar* pMenuBar) const
+{
+ auto aI = std::find(maIconMBars.begin(), maIconMBars.end(), pMenuBar);
+ if (aI == maIconMBars.end())
+ return 0;
+ return maIconIDs[std::distance(maIconMBars.begin(), aI)];
+}
+
+VclPtr<BubbleWindow> MenuBarUpdateIconManager::GetBubbleWindow()
+{
+ if (!mpActiveSysWin)
+ return nullptr;
+
+ tools::Rectangle aIconRect = mpActiveMBar->GetMenuBarButtonRectPixel(GetIconID(mpActiveMBar));
+ if( aIconRect.IsEmpty() )
+ return nullptr;
+
+ auto pBubbleWin = mpBubbleWin;
+
+ if ( !pBubbleWin ) {
+ pBubbleWin = VclPtr<BubbleWindow>::Create( mpActiveSysWin, maBubbleTitle,
+ maBubbleText, maBubbleImage );
+ mbBubbleChanged = false;
+ }
+ else if ( mbBubbleChanged ) {
+ pBubbleWin->SetTitleAndText( maBubbleTitle, maBubbleText,
+ maBubbleImage );
+ mbBubbleChanged = false;
+ }
+
+ Point aWinPos = aIconRect.BottomCenter();
+
+ pBubbleWin->SetTipPosPixel( aWinPos );
+
+ return pBubbleWin;
+}
+
+IMPL_LINK_NOARG(MenuBarUpdateIconManager, TimeOutHdl, Timer *, void)
+{
+ RemoveBubbleWindow();
+}
+
+IMPL_LINK(MenuBarUpdateIconManager, WindowEventHdl, VclWindowEvent&, rEvent, void)
+{
+ VclEventId nEventID = rEvent.GetId();
+
+ if ( VclEventId::ObjectDying == nEventID )
+ {
+ if (mpActiveSysWin == rEvent.GetWindow())
+ {
+ RemoveBubbleWindow();
+ mpActiveSysWin = nullptr;
+ mpActiveMBar = nullptr;
+ }
+ }
+ else if ( VclEventId::WindowMenubarAdded == nEventID )
+ {
+ vcl::Window *pWindow = rEvent.GetWindow();
+ if ( pWindow )
+ {
+ SystemWindow *pSysWin = pWindow->GetSystemWindow();
+ if (pSysWin)
+ AddMenuBarIcon(*pSysWin, false);
+ }
+ }
+ else if ( VclEventId::WindowMenubarRemoved == nEventID )
+ {
+ MenuBar *pMBar = static_cast<MenuBar*>(rEvent.GetData());
+ if (pMBar)
+ {
+ if (pMBar == mpActiveMBar)
+ {
+ RemoveBubbleWindow();
+ mpActiveMBar = nullptr;
+ }
+ RemoveMenuBarIcon(pMBar);
+ }
+ }
+ else if ( ( nEventID == VclEventId::WindowMove ) ||
+ ( nEventID == VclEventId::WindowResize ) )
+ {
+ if ( mpActiveSysWin == rEvent.GetWindow() &&
+ mpBubbleWin && mpActiveMBar )
+ {
+ tools::Rectangle aIconRect = mpActiveMBar->GetMenuBarButtonRectPixel(GetIconID(mpActiveMBar));
+ Point aWinPos = aIconRect.BottomCenter();
+ mpBubbleWin->SetTipPosPixel( aWinPos );
+ if ( mpBubbleWin->IsVisible() )
+ mpBubbleWin->Show(); // This will recalc the screen position of the bubble
+ }
+ }
+}
+
+IMPL_LINK(MenuBarUpdateIconManager, ApplicationEventHdl, VclSimpleEvent&, rEvent, void)
+{
+ switch (rEvent.GetId())
+ {
+ case VclEventId::WindowShow:
+ case VclEventId::WindowActivate:
+ case VclEventId::WindowGetFocus: {
+
+ vcl::Window *pWindow = static_cast< VclWindowEvent * >(&rEvent)->GetWindow();
+ if ( pWindow && pWindow->IsTopWindow() )
+ {
+ SystemWindow *pSysWin = pWindow->GetSystemWindow();
+ MenuBar *pMBar = pSysWin ? pSysWin->GetMenuBar() : nullptr;
+ if (pMBar)
+ AddMenuBarIcon(*pSysWin, true);
+ }
+ break;
+ }
+ default: break;
+ }
+}
+
+IMPL_LINK_NOARG(MenuBarUpdateIconManager, UserEventHdl, void*, void)
+{
+ vcl::Window *pTopWin = Application::GetFirstTopLevelWindow();
+ vcl::Window *pActiveWin = Application::GetActiveTopWindow();
+ SystemWindow *pActiveSysWin = nullptr;
+
+ vcl::Window *pBubbleWin = nullptr;
+ if ( mpBubbleWin )
+ pBubbleWin = mpBubbleWin;
+
+ if ( pActiveWin && ( pActiveWin != pBubbleWin ) && pActiveWin->IsTopWindow() )
+ pActiveSysWin = pActiveWin->GetSystemWindow();
+
+ if ( pActiveWin == pBubbleWin )
+ pActiveSysWin = nullptr;
+
+ while ( !pActiveSysWin && pTopWin )
+ {
+ if ( ( pTopWin != pBubbleWin ) && pTopWin->IsTopWindow() )
+ pActiveSysWin = pTopWin->GetSystemWindow();
+ if ( !pActiveSysWin )
+ pTopWin = Application::GetNextTopLevelWindow( pTopWin );
+ }
+
+ if ( pActiveSysWin )
+ AddMenuBarIcon(*pActiveSysWin, true);
+}
+
+IMPL_LINK_NOARG(MenuBarUpdateIconManager, ClickHdl, MenuBarButtonCallbackArg&, bool)
+{
+ maWaitIdle.Stop();
+ if ( mpBubbleWin )
+ mpBubbleWin->Show( false );
+
+ maClickHdl.Call(nullptr);
+
+ return false;
+}
+
+IMPL_LINK(MenuBarUpdateIconManager, HighlightHdl, MenuBarButtonCallbackArg&, rData, bool)
+{
+ if ( rData.bHighlight )
+ maWaitIdle.Start();
+ else
+ RemoveBubbleWindow();
+
+ return false;
+}
+
+IMPL_LINK_NOARG(MenuBarUpdateIconManager, WaitTimeOutHdl, Timer *, void)
+{
+ mpBubbleWin = GetBubbleWindow();
+
+ if ( mpBubbleWin )
+ {
+ mpBubbleWin->Show();
+ }
+}
+
+MenuBarUpdateIconManager::~MenuBarUpdateIconManager()
+{
+ Application::RemoveEventListener( maApplicationEventHdl );
+
+ RemoveBubbleWindow();
+ RemoveMenuBarIcons();
+}
+
+void MenuBarUpdateIconManager::RemoveMenuBarIcons()
+{
+ while (!maIconMBars.empty())
+ RemoveMenuBarIcon(maIconMBars[0]);
+}
+
+void MenuBarUpdateIconManager::SetShowMenuIcon(bool bShowMenuIcon)
+{
+ if ( bShowMenuIcon != mbShowMenuIcon )
+ {
+ mbShowMenuIcon = bShowMenuIcon;
+ if ( bShowMenuIcon )
+ Application::PostUserEvent(LINK(this, MenuBarUpdateIconManager, UserEventHdl));
+ else
+ {
+ RemoveBubbleWindow();
+ RemoveMenuBarIcons();
+ }
+ }
+}
+
+void MenuBarUpdateIconManager::SetShowBubble(bool bShowBubble)
+{
+ mbShowBubble = bShowBubble;
+ if ( mbShowBubble )
+ Application::PostUserEvent(LINK(this, MenuBarUpdateIconManager, UserEventHdl));
+ else if ( mpBubbleWin )
+ mpBubbleWin->Show( false );
+}
+
+void MenuBarUpdateIconManager::SetBubbleChanged()
+{
+ mbBubbleChanged = true;
+ if (mbBubbleChanged && mpBubbleWin)
+ mpBubbleWin->Show( false );
+}
+
+void MenuBarUpdateIconManager::SetBubbleImage(const Image& rImage)
+{
+ maBubbleImage = rImage;
+ SetBubbleChanged();
+}
+
+void MenuBarUpdateIconManager::SetBubbleTitle(const OUString& rTitle)
+{
+ if (rTitle != maBubbleTitle)
+ {
+ maBubbleTitle = rTitle;
+ SetBubbleChanged();
+ }
+}
+
+void MenuBarUpdateIconManager::SetBubbleText(const OUString& rText)
+{
+ if (rText != maBubbleText)
+ {
+ maBubbleText = rText;
+ SetBubbleChanged();
+ }
+}
+
+namespace {
+Image GetMenuBarIcon( MenuBar const * pMBar )
+{
+ OUString sResID;
+ vcl::Window *pMBarWin = pMBar->GetWindow();
+ sal_uInt32 nMBarHeight = 20;
+
+ if ( pMBarWin )
+ nMBarHeight = pMBarWin->GetOutputSizePixel().getHeight();
+
+ if (nMBarHeight >= 35)
+ sResID = RID_UPDATE_AVAILABLE_26;
+ else
+ sResID = RID_UPDATE_AVAILABLE_16;
+
+ return Image(StockImage::Yes, sResID);
+}
+}
+
+void MenuBarUpdateIconManager::AddMenuBarIcon(SystemWindow& rSysWin, bool bAddEventHdl)
+{
+ if (!mbShowMenuIcon)
+ return;
+
+ MenuBar *pActiveMBar = rSysWin.GetMenuBar();
+ if (&rSysWin != mpActiveSysWin || pActiveMBar != mpActiveMBar)
+ RemoveBubbleWindow();
+
+ auto aI = std::find(maIconMBars.begin(), maIconMBars.end(), pActiveMBar);
+ if (aI == maIconMBars.end())
+ {
+ if (pActiveMBar)
+ {
+ OUStringBuffer aBuf;
+ if( !maBubbleTitle.isEmpty() )
+ aBuf.append( maBubbleTitle );
+ if( !maBubbleText.isEmpty() )
+ {
+ if( !maBubbleTitle.isEmpty() )
+ aBuf.append( "\n\n" );
+ aBuf.append( maBubbleText );
+ }
+
+ Image aImage = GetMenuBarIcon( pActiveMBar );
+ sal_uInt16 nIconID = pActiveMBar->AddMenuBarButton( aImage,
+ LINK( this, MenuBarUpdateIconManager, ClickHdl ),
+ aBuf.makeStringAndClear() );
+ maIconMBars.push_back(pActiveMBar);
+ maIconIDs.push_back(nIconID);
+ }
+
+ if (bAddEventHdl)
+ rSysWin.AddEventListener( maWindowEventHdl );
+ }
+
+ if (mpActiveMBar != pActiveMBar)
+ {
+ if (mpActiveMBar)
+ {
+ mpActiveMBar->SetMenuBarButtonHighlightHdl(GetIconID(mpActiveMBar),
+ Link<MenuBarButtonCallbackArg&,bool>());
+ }
+ mpActiveMBar = pActiveMBar;
+ if (mpActiveMBar)
+ {
+ mpActiveMBar->SetMenuBarButtonHighlightHdl(GetIconID(mpActiveMBar),
+ LINK(this, MenuBarUpdateIconManager, HighlightHdl));
+ }
+ }
+
+ mpActiveSysWin = &rSysWin;
+
+ if (mbShowBubble && pActiveMBar)
+ {
+ mpBubbleWin = GetBubbleWindow();
+ if ( mpBubbleWin )
+ {
+ mpBubbleWin->Show();
+ maTimeoutTimer.Start();
+ }
+ mbShowBubble = false;
+ }
+}
+
+void MenuBarUpdateIconManager::RemoveMenuBarIcon(MenuBar* pMenuBar)
+{
+ auto aI = std::find(maIconMBars.begin(), maIconMBars.end(), pMenuBar);
+ if (aI == maIconMBars.end())
+ return;
+
+ auto aIconI = maIconIDs.begin() + std::distance(maIconMBars.begin(), aI);
+
+ try
+ {
+ pMenuBar->RemoveMenuBarButton(*aIconI);
+ }
+ catch (...)
+ {
+ }
+
+ maIconMBars.erase(aI);
+ maIconIDs.erase(aIconI);
+}
+
+void MenuBarUpdateIconManager::RemoveBubbleWindow()
+{
+ maWaitIdle.Stop();
+ maTimeoutTimer.Stop();
+ mpBubbleWin.disposeAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/bufferdevice.cxx b/vcl/source/window/bufferdevice.cxx
new file mode 100644
index 0000000000..188fbb1acc
--- /dev/null
+++ b/vcl/source/window/bufferdevice.cxx
@@ -0,0 +1,45 @@
+/* -*- 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/.
+ */
+
+#include "bufferdevice.hxx"
+
+namespace vcl
+{
+BufferDevice::BufferDevice(const VclPtr<vcl::Window>& pWindow, vcl::RenderContext& rRenderContext)
+ : m_pBuffer(VclPtr<VirtualDevice>::Create(rRenderContext))
+ , m_pWindow(pWindow)
+ , m_rRenderContext(rRenderContext)
+{
+ m_pBuffer->SetOutputSizePixel(pWindow->GetOutputSizePixel(), false);
+ m_pBuffer->SetTextColor(rRenderContext.GetTextColor());
+ m_pBuffer->DrawOutDev(Point(0, 0), pWindow->GetOutputSizePixel(), Point(0, 0),
+ pWindow->GetOutputSizePixel(), rRenderContext);
+ m_pBuffer->EnableRTL(rRenderContext.IsRTLEnabled());
+}
+
+void BufferDevice::Dispose()
+{
+ if (m_bDisposed)
+ {
+ return;
+ }
+
+ m_rRenderContext.DrawOutDev(Point(0, 0), m_pWindow->GetOutputSizePixel(), Point(0, 0),
+ m_pWindow->GetOutputSizePixel(), *m_pBuffer);
+ m_bDisposed = true;
+}
+
+BufferDevice::~BufferDevice() { Dispose(); }
+
+vcl::RenderContext* BufferDevice::operator->() { return m_pBuffer.get(); }
+
+vcl::RenderContext& BufferDevice::operator*() { return *m_pBuffer; }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/bufferdevice.hxx b/vcl/source/window/bufferdevice.hxx
new file mode 100644
index 0000000000..eafc829e59
--- /dev/null
+++ b/vcl/source/window/bufferdevice.hxx
@@ -0,0 +1,35 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <vcl/virdev.hxx>
+#include <vcl/window.hxx>
+
+namespace vcl
+{
+/// Buffers drawing on a vcl::RenderContext using a VirtualDevice.
+class VCL_DLLPUBLIC BufferDevice
+{
+ ScopedVclPtr<VirtualDevice> m_pBuffer;
+ VclPtr<vcl::Window> m_pWindow;
+ vcl::RenderContext& m_rRenderContext;
+ bool m_bDisposed = false;
+
+public:
+ BufferDevice(const VclPtr<vcl::Window>& pWindow, vcl::RenderContext& rRenderContext);
+ ~BufferDevice();
+ void Dispose();
+
+ vcl::RenderContext* operator->();
+ vcl::RenderContext& operator*();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx
new file mode 100644
index 0000000000..bc307ba855
--- /dev/null
+++ b/vcl/source/window/builder.cxx
@@ -0,0 +1,4399 @@
+/* -*- 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/.
+ */
+
+#include <config_feature_desktop.h>
+#include <config_options.h>
+#include <config_vclplug.h>
+
+#include <memory>
+#include <string_view>
+#include <unordered_map>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+
+#include <comphelper/lok.hxx>
+#include <i18nutil/unicode.hxx>
+#include <jsdialog/enabled.hxx>
+#include <o3tl/string_view.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <osl/module.hxx>
+#include <sal/log.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/resmgr.hxx>
+#include <utility>
+#include <vcl/builder.hxx>
+#include <vcl/dialoghelper.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/toolkit/button.hxx>
+#include <vcl/toolkit/dialog.hxx>
+#include <vcl/toolkit/edit.hxx>
+#include <vcl/toolkit/field.hxx>
+#include <vcl/fieldvalues.hxx>
+#include <vcl/toolkit/fmtfield.hxx>
+#include <vcl/toolkit/fixed.hxx>
+#include <vcl/toolkit/fixedhyper.hxx>
+#include <vcl/headbar.hxx>
+#include <vcl/notebookbar/NotebookBarAddonsMerger.hxx>
+#include <vcl/toolkit/ivctrl.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/toolkit/lstbox.hxx>
+#include <vcl/toolkit/menubtn.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/toolkit/prgsbar.hxx>
+#include <vcl/toolkit/scrbar.hxx>
+#include <vcl/split.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolkit/svtabbx.hxx>
+#include <vcl/tabctrl.hxx>
+#include <vcl/tabpage.hxx>
+#include <vcl/toolkit/throbber.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/toolkit/treelistentry.hxx>
+#include <vcl/toolkit/vclmedit.hxx>
+#include <vcl/settings.hxx>
+#include <slider.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <iconview.hxx>
+#include <svdata.hxx>
+#include <bitmaps.hlst>
+#include <managedmenubutton.hxx>
+#include <messagedialog.hxx>
+#include <ContextVBox.hxx>
+#include <DropdownBox.hxx>
+#include <IPrioritable.hxx>
+#include <OptionalBox.hxx>
+#include <PriorityMergedHBox.hxx>
+#include <PriorityHBox.hxx>
+#include <window.h>
+#include <xmlreader/xmlreader.hxx>
+#include <desktop/crashreport.hxx>
+#include <calendar.hxx>
+#include <menutogglebutton.hxx>
+#include <salinst.hxx>
+#include <strings.hrc>
+#include <treeglue.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <verticaltabctrl.hxx>
+#include <wizdlg.hxx>
+#include <tools/svlibrary.h>
+#include <jsdialog/jsdialogbuilder.hxx>
+
+#if defined(DISABLE_DYNLOADING) || defined(LINUX)
+#include <dlfcn.h>
+#endif
+
+static bool toBool(std::string_view rValue)
+{
+ return (!rValue.empty() && (rValue[0] == 't' || rValue[0] == 'T' || rValue[0] == '1'));
+}
+
+namespace
+{
+ OUString mapStockToImageResource(std::u16string_view sType)
+ {
+ if (sType == u"view-refresh")
+ return SV_RESID_BITMAP_REFRESH;
+ else if (sType == u"dialog-error")
+ return IMG_ERROR;
+ else if (sType == u"list-add")
+ return IMG_ADD;
+ else if (sType == u"list-remove")
+ return IMG_REMOVE;
+ else if (sType == u"edit-copy")
+ return IMG_COPY;
+ else if (sType == u"edit-paste")
+ return IMG_PASTE;
+ else if (sType == u"document-open")
+ return IMG_OPEN;
+ else if (sType == u"open-menu-symbolic")
+ return IMG_MENU;
+ else if (sType == u"window-close-symbolic")
+ return SV_RESID_BITMAP_CLOSEDOC;
+ else if (sType == u"x-office-calendar")
+ return IMG_CALENDAR;
+ return OUString();
+ }
+
+}
+
+SymbolType VclBuilder::mapStockToSymbol(std::u16string_view sType)
+{
+ SymbolType eRet = SymbolType::DONTKNOW;
+ if (sType == u"media-skip-forward")
+ eRet = SymbolType::NEXT;
+ else if (sType == u"media-skip-backward")
+ eRet = SymbolType::PREV;
+ else if (sType == u"media-playback-start")
+ eRet = SymbolType::PLAY;
+ else if (sType == u"media-playback-stop")
+ eRet = SymbolType::STOP;
+ else if (sType == u"go-first")
+ eRet = SymbolType::FIRST;
+ else if (sType == u"go-last")
+ eRet = SymbolType::LAST;
+ else if (sType == u"go-previous")
+ eRet = SymbolType::ARROW_LEFT;
+ else if (sType == u"go-next")
+ eRet = SymbolType::ARROW_RIGHT;
+ else if (sType == u"go-up")
+ eRet = SymbolType::ARROW_UP;
+ else if (sType == u"go-down")
+ eRet = SymbolType::ARROW_DOWN;
+ else if (sType == u"missing-image")
+ eRet = SymbolType::IMAGE;
+ else if (sType == u"help-browser" || sType == u"help-browser-symbolic")
+ eRet = SymbolType::HELP;
+ else if (sType == u"window-close")
+ eRet = SymbolType::CLOSE;
+ else if (sType == u"document-new")
+ eRet = SymbolType::PLUS;
+ else if (sType == u"pan-down-symbolic")
+ eRet = SymbolType::SPIN_DOWN;
+ else if (sType == u"pan-up-symbolic")
+ eRet = SymbolType::SPIN_UP;
+ else if (!mapStockToImageResource(sType).isEmpty())
+ eRet = SymbolType::IMAGE;
+ return eRet;
+}
+
+namespace
+{
+ void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame);
+
+#if defined SAL_LOG_WARN
+ bool isButtonType(WindowType nType)
+ {
+ return nType == WindowType::PUSHBUTTON ||
+ nType == WindowType::OKBUTTON ||
+ nType == WindowType::CANCELBUTTON ||
+ nType == WindowType::HELPBUTTON ||
+ nType == WindowType::IMAGEBUTTON ||
+ nType == WindowType::MENUBUTTON ||
+ nType == WindowType::MOREBUTTON ||
+ nType == WindowType::SPINBUTTON;
+ }
+#endif
+
+}
+
+std::unique_ptr<weld::Builder> Application::CreateBuilder(weld::Widget* pParent, const OUString &rUIFile, bool bMobile, sal_uInt64 nLOKWindowId)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (jsdialog::isBuilderEnabledForSidebar(rUIFile))
+ return JSInstanceBuilder::CreateSidebarBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, nLOKWindowId);
+ else if (jsdialog::isBuilderEnabledForPopup(rUIFile))
+ return JSInstanceBuilder::CreatePopupBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile);
+ else if (jsdialog::isBuilderEnabled(rUIFile, bMobile))
+ return JSInstanceBuilder::CreateDialogBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile);
+ }
+
+ return ImplGetSVData()->mpDefInst->CreateBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile);
+}
+
+std::unique_ptr<weld::Builder> Application::CreateInterimBuilder(vcl::Window* pParent, const OUString &rUIFile, bool bAllowCycleFocusOut, sal_uInt64 nLOKWindowId)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Notebookbar sub controls
+ if (jsdialog::isInterimBuilderEnabledForNotebookbar(rUIFile))
+ return JSInstanceBuilder::CreateNotebookbarBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, css::uno::Reference<css::frame::XFrame>(), nLOKWindowId);
+ else if (rUIFile == u"modules/scalc/ui/inputbar.ui")
+ return JSInstanceBuilder::CreateFormulabarBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, nLOKWindowId);
+ }
+
+ return ImplGetSVData()->mpDefInst->CreateInterimBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, bAllowCycleFocusOut, nLOKWindowId);
+}
+
+weld::MessageDialog* Application::CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType,
+ VclButtonsType eButtonType, const OUString& rPrimaryMessage,
+ const ILibreOfficeKitNotifier* pNotifier)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return JSInstanceBuilder::CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage, pNotifier);
+ else
+ return ImplGetSVData()->mpDefInst->CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage);
+}
+
+weld::Window* Application::GetFrameWeld(const css::uno::Reference<css::awt::XWindow>& rWindow)
+{
+ return ImplGetSVData()->mpDefInst->GetFrameWeld(rWindow);
+}
+
+namespace weld
+{
+ OUString MetricSpinButton::MetricToString(FieldUnit rUnit)
+ {
+ const FieldUnitStringList& rList = ImplGetFieldUnits();
+ // return unit's default string (ie, the first one )
+ auto it = std::find_if(
+ rList.begin(), rList.end(),
+ [&rUnit](const std::pair<OUString, FieldUnit>& rItem) { return rItem.second == rUnit; });
+ if (it != rList.end())
+ return it->first;
+
+ return OUString();
+ }
+
+ IMPL_LINK_NOARG(MetricSpinButton, spin_button_value_changed, SpinButton&, void)
+ {
+ signal_value_changed();
+ }
+
+ IMPL_LINK(MetricSpinButton, spin_button_output, SpinButton&, rSpinButton, void)
+ {
+ OUString sNewText(format_number(rSpinButton.get_value()));
+ if (sNewText != rSpinButton.get_text())
+ rSpinButton.set_text(sNewText);
+ }
+
+ void MetricSpinButton::update_width_chars()
+ {
+ sal_Int64 min, max;
+ m_xSpinButton->get_range(min, max);
+ auto width = std::max(m_xSpinButton->get_pixel_size(format_number(min)).Width(),
+ m_xSpinButton->get_pixel_size(format_number(max)).Width());
+ int chars = ceil(width / m_xSpinButton->get_approximate_digit_width());
+ m_xSpinButton->set_width_chars(chars);
+ }
+
+ unsigned int SpinButton::Power10(unsigned int n)
+ {
+ unsigned int nValue = 1;
+ for (unsigned int i = 0; i < n; ++i)
+ nValue *= 10;
+ return nValue;
+ }
+
+ sal_Int64 SpinButton::denormalize(sal_Int64 nValue) const
+ {
+ const int nFactor = Power10(get_digits());
+
+ if ((nValue < (std::numeric_limits<sal_Int64>::min() + nFactor)) ||
+ (nValue > (std::numeric_limits<sal_Int64>::max() - nFactor)))
+ {
+ return nValue / nFactor;
+ }
+
+ const int nHalf = nFactor / 2;
+
+ if (nValue < 0)
+ return (nValue - nHalf) / nFactor;
+ return (nValue + nHalf) / nFactor;
+ }
+
+ OUString MetricSpinButton::format_number(sal_Int64 nValue) const
+ {
+ OUString aStr;
+
+ const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
+
+ unsigned int nDecimalDigits = m_xSpinButton->get_digits();
+ //pawn percent off to icu to decide whether percent is separated from its number for this locale
+ if (m_eSrcUnit == FieldUnit::PERCENT)
+ {
+ double fValue = nValue;
+ fValue /= SpinButton::Power10(nDecimalDigits);
+ aStr = unicode::formatPercent(fValue, rLocaleData.getLanguageTag());
+ }
+ else
+ {
+ aStr = rLocaleData.getNum(nValue, nDecimalDigits, true, true);
+ OUString aSuffix = MetricToString(m_eSrcUnit);
+ if (m_eSrcUnit != FieldUnit::NONE && m_eSrcUnit != FieldUnit::DEGREE && m_eSrcUnit != FieldUnit::INCH && m_eSrcUnit != FieldUnit::FOOT)
+ aStr += " ";
+ if (m_eSrcUnit == FieldUnit::INCH)
+ {
+ OUString sDoublePrime = u"\u2033"_ustr;
+ if (aSuffix != "\"" && aSuffix != sDoublePrime)
+ aStr += " ";
+ else
+ aSuffix = sDoublePrime;
+ }
+ else if (m_eSrcUnit == FieldUnit::FOOT)
+ {
+ OUString sPrime = u"\u2032"_ustr;
+ if (aSuffix != "'" && aSuffix != sPrime)
+ aStr += " ";
+ else
+ aSuffix = sPrime;
+ }
+
+ assert(m_eSrcUnit != FieldUnit::PERCENT);
+ aStr += aSuffix;
+ }
+
+ return aStr;
+ }
+
+ void MetricSpinButton::set_digits(unsigned int digits)
+ {
+ int step, page;
+ get_increments(step, page, m_eSrcUnit);
+ sal_Int64 value = get_value(m_eSrcUnit);
+ m_xSpinButton->set_digits(digits);
+ set_increments(step, page, m_eSrcUnit);
+ set_value(value, m_eSrcUnit);
+ update_width_chars();
+ }
+
+ void MetricSpinButton::set_unit(FieldUnit eUnit)
+ {
+ if (eUnit != m_eSrcUnit)
+ {
+ int step, page;
+ get_increments(step, page, m_eSrcUnit);
+ sal_Int64 value = get_value(m_eSrcUnit);
+ m_eSrcUnit = eUnit;
+ set_increments(step, page, m_eSrcUnit);
+ set_value(value, m_eSrcUnit);
+ spin_button_output(*m_xSpinButton);
+ update_width_chars();
+ }
+ }
+
+ sal_Int64 MetricSpinButton::ConvertValue(sal_Int64 nValue, FieldUnit eInUnit, FieldUnit eOutUnit) const
+ {
+ return vcl::ConvertValue(nValue, 0, m_xSpinButton->get_digits(), eInUnit, eOutUnit);
+ }
+
+ IMPL_LINK(MetricSpinButton, spin_button_input, int*, result, bool)
+ {
+ const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
+ double fResult(0.0);
+ bool bRet = vcl::TextToValue(get_text(), fResult, 0, m_xSpinButton->get_digits(), rLocaleData, m_eSrcUnit);
+ if (bRet)
+ {
+ if (fResult > SAL_MAX_INT32)
+ fResult = SAL_MAX_INT32;
+ else if (fResult < SAL_MIN_INT32)
+ fResult = SAL_MIN_INT32;
+ *result = fResult;
+ }
+ return bRet;
+ }
+
+ EntryTreeView::EntryTreeView(std::unique_ptr<Entry> xEntry, std::unique_ptr<TreeView> xTreeView)
+ : m_xEntry(std::move(xEntry))
+ , m_xTreeView(std::move(xTreeView))
+ {
+ m_xTreeView->connect_changed(LINK(this, EntryTreeView, ClickHdl));
+ m_xEntry->connect_changed(LINK(this, EntryTreeView, ModifyHdl));
+ }
+
+ IMPL_LINK(EntryTreeView, ClickHdl, weld::TreeView&, rView, void)
+ {
+ m_xEntry->set_text(rView.get_selected_text());
+ m_aChangeHdl.Call(*this);
+ }
+
+ IMPL_LINK_NOARG(EntryTreeView, ModifyHdl, weld::Entry&, void)
+ {
+ m_aChangeHdl.Call(*this);
+ }
+
+ void EntryTreeView::set_height_request_by_rows(int nRows)
+ {
+ int nHeight = nRows == -1 ? -1 : m_xTreeView->get_height_rows(nRows);
+ m_xTreeView->set_size_request(m_xTreeView->get_size_request().Width(), nHeight);
+ }
+
+ size_t GetAbsPos(const weld::TreeView& rTreeView, const weld::TreeIter& rIter)
+ {
+ size_t nAbsPos = 0;
+
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator(&rIter));
+ if (!rTreeView.get_iter_first(*xEntry))
+ xEntry.reset();
+
+ while (xEntry && rTreeView.iter_compare(*xEntry, rIter) != 0)
+ {
+ if (!rTreeView.iter_next(*xEntry))
+ xEntry.reset();
+ nAbsPos++;
+ }
+
+ return nAbsPos;
+ }
+
+ bool IsEntryVisible(const weld::TreeView& rTreeView, const weld::TreeIter& rIter)
+ {
+ // short circuit for the common case
+ if (rTreeView.get_iter_depth(rIter) == 0)
+ return true;
+
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator(&rIter));
+ bool bRetVal = false;
+ do
+ {
+ if (rTreeView.get_iter_depth(*xEntry) == 0)
+ {
+ bRetVal = true;
+ break;
+ }
+ } while (rTreeView.iter_parent(*xEntry) && rTreeView.get_row_expanded(*xEntry));
+ return bRetVal;
+ }
+}
+
+VclBuilder::VclBuilder(vcl::Window* pParent, const OUString& sUIDir, const OUString& sUIFile,
+ OUString sID, css::uno::Reference<css::frame::XFrame> xFrame,
+ bool bLegacy, const NotebookBarAddonsItem* pNotebookBarAddonsItem)
+ : m_pNotebookBarAddonsItem(pNotebookBarAddonsItem
+ ? new NotebookBarAddonsItem(*pNotebookBarAddonsItem)
+ : new NotebookBarAddonsItem{})
+ , m_sID(std::move(sID))
+ , m_sHelpRoot(sUIFile)
+ , m_pStringReplace(Translate::GetReadStringHook())
+ , m_pParent(pParent)
+ , m_bToplevelParentFound(false)
+ , m_bLegacy(bLegacy)
+ , m_pParserState(new ParserState)
+ , m_xFrame(std::move(xFrame))
+{
+ m_bToplevelHasDeferredInit = pParent &&
+ ((pParent->IsSystemWindow() && static_cast<SystemWindow*>(pParent)->isDeferredInit()) ||
+ (pParent->IsDockingWindow() && static_cast<DockingWindow*>(pParent)->isDeferredInit()));
+ m_bToplevelHasDeferredProperties = m_bToplevelHasDeferredInit;
+
+ sal_Int32 nIdx = m_sHelpRoot.lastIndexOf('.');
+ if (nIdx != -1)
+ m_sHelpRoot = m_sHelpRoot.copy(0, nIdx);
+ m_sHelpRoot += "/";
+
+ try
+ {
+ xmlreader::XmlReader reader(sUIDir + sUIFile);
+
+ handleChild(pParent, nullptr, reader);
+ }
+ catch (const css::uno::Exception &rExcept)
+ {
+ DBG_UNHANDLED_EXCEPTION("vcl.builder", "Unable to read .ui file");
+ CrashReporter::addKeyValue("VclBuilderException", "Unable to read .ui file: " + rExcept.Message, CrashReporter::Write);
+ throw;
+ }
+
+ //Set Mnemonic widgets when everything has been imported
+ for (auto const& mnemonicWidget : m_pParserState->m_aMnemonicWidgetMaps)
+ {
+ FixedText *pOne = get<FixedText>(mnemonicWidget.m_sID);
+ vcl::Window *pOther = get(mnemonicWidget.m_sValue);
+ SAL_WARN_IF(!pOne || !pOther, "vcl", "missing either source " << mnemonicWidget.m_sID
+ << " or target " << mnemonicWidget.m_sValue << " member of Mnemonic Widget Mapping");
+ if (pOne && pOther)
+ pOne->set_mnemonic_widget(pOther);
+ }
+
+ //Set a11y relations and role when everything has been imported
+ for (auto const& elemAtk : m_pParserState->m_aAtkInfo)
+ {
+ vcl::Window *pSource = elemAtk.first;
+ const stringmap &rMap = elemAtk.second;
+
+ for (auto const& [ rType, rParam ] : rMap)
+ {
+ if (rType == "role")
+ {
+ sal_Int16 role = BuilderUtils::getRoleFromName(rParam);
+ if (role != com::sun::star::accessibility::AccessibleRole::UNKNOWN)
+ pSource->SetAccessibleRole(role);
+ }
+ else
+ {
+ vcl::Window *pTarget = get(rParam);
+ SAL_WARN_IF(!pTarget, "vcl", "missing parameter of a11y relation: " << rParam);
+ if (!pTarget)
+ continue;
+ if (rType == "labelled-by")
+ pSource->SetAccessibleRelationLabeledBy(pTarget);
+ else if (rType == "label-for")
+ pSource->SetAccessibleRelationLabelFor(pTarget);
+ else
+ {
+ SAL_WARN("vcl.builder", "unhandled a11y relation :" << rType);
+ }
+ }
+ }
+ }
+
+ //Set radiobutton groups when everything has been imported
+ for (auto const& elem : m_pParserState->m_aGroupMaps)
+ {
+ RadioButton *pOne = get<RadioButton>(elem.m_sID);
+ RadioButton *pOther = get<RadioButton>(elem.m_sValue);
+ SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton group");
+ if (pOne && pOther)
+ {
+ if (m_bLegacy)
+ pOne->group(*pOther);
+ else
+ {
+ pOther->group(*pOne);
+ std::stable_sort(pOther->m_xGroup->begin(), pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this));
+ }
+ }
+ }
+
+#ifndef NDEBUG
+ o3tl::sorted_vector<OUString> models;
+#endif
+ //Set ComboBox models when everything has been imported
+ for (auto const& elem : m_pParserState->m_aModelMaps)
+ {
+ assert(models.insert(elem.m_sValue).second && "a liststore or treestore is used in duplicate widgets");
+ vcl::Window* pTarget = get(elem.m_sID);
+ ListBox *pListBoxTarget = dynamic_cast<ListBox*>(pTarget);
+ ComboBox *pComboBoxTarget = dynamic_cast<ComboBox*>(pTarget);
+ SvTabListBox *pTreeBoxTarget = dynamic_cast<SvTabListBox*>(pTarget);
+ // pStore may be empty
+ const ListStore *pStore = get_model_by_name(elem.m_sValue);
+ SAL_WARN_IF(!pListBoxTarget && !pComboBoxTarget && !pTreeBoxTarget && !dynamic_cast<IconView*>(pTarget), "vcl", "missing elements of combobox");
+ if (pListBoxTarget && pStore)
+ mungeModel(*pListBoxTarget, *pStore, elem.m_nActiveId);
+ else if (pComboBoxTarget && pStore)
+ mungeModel(*pComboBoxTarget, *pStore, elem.m_nActiveId);
+ else if (pTreeBoxTarget && pStore)
+ mungeModel(*pTreeBoxTarget, *pStore, elem.m_nActiveId);
+ }
+
+ //Set TextView buffers when everything has been imported
+ for (auto const& elem : m_pParserState->m_aTextBufferMaps)
+ {
+ VclMultiLineEdit *pTarget = get<VclMultiLineEdit>(elem.m_sID);
+ const TextBuffer *pBuffer = get_buffer_by_name(elem.m_sValue);
+ SAL_WARN_IF(!pTarget || !pBuffer, "vcl", "missing elements of textview/textbuffer");
+ if (pTarget && pBuffer)
+ mungeTextBuffer(*pTarget, *pBuffer);
+ }
+
+ //Set SpinButton adjustments when everything has been imported
+ for (auto const& elem : m_pParserState->m_aNumericFormatterAdjustmentMaps)
+ {
+ NumericFormatter *pTarget = dynamic_cast<NumericFormatter*>(get(elem.m_sID));
+ const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue);
+ SAL_WARN_IF(!pTarget, "vcl", "missing NumericFormatter element of spinbutton/adjustment");
+ SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
+ if (pTarget && pAdjustment)
+ mungeAdjustment(*pTarget, *pAdjustment);
+ }
+
+ for (auto const& elem : m_pParserState->m_aFormattedFormatterAdjustmentMaps)
+ {
+ FormattedField *pTarget = dynamic_cast<FormattedField*>(get(elem.m_sID));
+ const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue);
+ SAL_WARN_IF(!pTarget, "vcl", "missing FormattedField element of spinbutton/adjustment");
+ SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
+ if (pTarget && pAdjustment)
+ mungeAdjustment(*pTarget, *pAdjustment);
+ }
+
+ //Set ScrollBar adjustments when everything has been imported
+ for (auto const& elem : m_pParserState->m_aScrollAdjustmentMaps)
+ {
+ ScrollBar *pTarget = get<ScrollBar>(elem.m_sID);
+ const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue);
+ SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scrollbar/adjustment");
+ if (pTarget && pAdjustment)
+ mungeAdjustment(*pTarget, *pAdjustment);
+ }
+
+ //Set Scale(Slider) adjustments
+ for (auto const& elem : m_pParserState->m_aSliderAdjustmentMaps)
+ {
+ Slider* pTarget = dynamic_cast<Slider*>(get(elem.m_sID));
+ const Adjustment* pAdjustment = get_adjustment_by_name(elem.m_sValue);
+ SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scale(slider)/adjustment");
+ if (pTarget && pAdjustment)
+ {
+ mungeAdjustment(*pTarget, *pAdjustment);
+ }
+ }
+
+ //Set size-groups when all widgets have been imported
+ for (auto const& sizeGroup : m_pParserState->m_aSizeGroups)
+ {
+ std::shared_ptr<VclSizeGroup> xGroup(std::make_shared<VclSizeGroup>());
+
+ for (auto const& [ rKey, rValue ] : sizeGroup.m_aProperties)
+ xGroup->set_property(rKey, rValue);
+
+ for (auto const& elem : sizeGroup.m_aWidgets)
+ {
+ vcl::Window* pWindow = get(elem);
+ pWindow->add_to_size_group(xGroup);
+ }
+ }
+
+ //Set button images when everything has been imported
+ std::set<OUString> aImagesToBeRemoved;
+ for (auto const& elem : m_pParserState->m_aButtonImageWidgetMaps)
+ {
+ PushButton *pTargetButton = nullptr;
+ RadioButton *pTargetRadio = nullptr;
+ Button *pTarget = nullptr;
+
+ if (!elem.m_bRadio)
+ {
+ pTargetButton = get<PushButton>(elem.m_sID);
+ pTarget = pTargetButton;
+ }
+ else
+ {
+ pTargetRadio = get<RadioButton>(elem.m_sID);
+ pTarget = pTargetRadio;
+ }
+
+ FixedImage *pImage = get<FixedImage>(elem.m_sValue);
+ SAL_WARN_IF(!pTarget || !pImage,
+ "vcl", "missing elements of button/image/stock");
+ if (!pTarget || !pImage)
+ continue;
+ aImagesToBeRemoved.insert(elem.m_sValue);
+
+ if (!elem.m_bRadio)
+ {
+ const Image& rImage = pImage->GetImage();
+ SymbolType eSymbol = mapStockToSymbol(rImage.GetStock());
+ if (eSymbol != SymbolType::IMAGE && eSymbol != SymbolType::DONTKNOW)
+ {
+ pTargetButton->SetSymbol(eSymbol);
+ //fdo#76457 keep symbol images small e.g. tools->customize->menu
+ //but images the right size. Really the PushButton::CalcMinimumSize
+ //and PushButton::ImplDrawPushButton are the better place to handle
+ //this, but its such a train-wreck
+ pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE);
+ }
+ else
+ {
+ pTargetButton->SetModeImage(rImage);
+ if (pImage->GetStyle() & WB_SMALLSTYLE)
+ {
+ Size aSz(rImage.GetSizePixel());
+ aSz.AdjustWidth(6);
+ aSz.AdjustHeight(6);
+ if (pTargetButton->get_width_request() == -1)
+ pTargetButton->set_width_request(aSz.Width());
+ if (pTargetButton->get_height_request() == -1)
+ pTargetButton->set_height_request(aSz.Height());
+ }
+ }
+ }
+ else
+ pTargetRadio->SetModeRadioImage(pImage->GetImage());
+
+ auto aFind = m_pParserState->m_aImageSizeMap.find(elem.m_sValue);
+ if (aFind != m_pParserState->m_aImageSizeMap.end())
+ {
+ switch (aFind->second)
+ {
+ case 1:
+ pTarget->SetSmallSymbol();
+ break;
+ case 2:
+ assert(pImage->GetStyle() & WB_SMALLSTYLE);
+ pTarget->SetStyle(pTarget->GetStyle() | WB_SMALLSTYLE);
+ break;
+ case 3:
+ pTarget->SetStyle(pTarget->GetStyle() | WB_SMALLSTYLE);
+ // large toolbar, make bigger than normal (4)
+ pTarget->set_width_request(pTarget->GetOptimalSize().Width() * 1.5);
+ pTarget->set_height_request(pTarget->GetOptimalSize().Height() * 1.5);
+ break;
+ case 4:
+ break;
+ default:
+ SAL_WARN("vcl.builder", "unsupported image size " << aFind->second);
+ break;
+ }
+ m_pParserState->m_aImageSizeMap.erase(aFind);
+ }
+ }
+
+ //There may be duplicate use of an Image, so we used a set to collect and
+ //now we can remove them from the tree after their final munge
+ for (auto const& elem : aImagesToBeRemoved)
+ {
+ delete_by_name(elem);
+ }
+
+ //Set button menus when everything has been imported
+ for (auto const& elem : m_pParserState->m_aButtonMenuMaps)
+ {
+ MenuButton *pTarget = get<MenuButton>(elem.m_sID);
+ PopupMenu *pMenu = get_menu(elem.m_sValue);
+ SAL_WARN_IF(!pTarget || !pMenu,
+ "vcl", "missing elements of button/menu");
+ if (!pTarget || !pMenu)
+ continue;
+ pTarget->SetPopupMenu(pMenu);
+ }
+
+ //Remove ScrollWindow parent widgets whose children in vcl implement scrolling
+ //internally.
+ for (auto const& elem : m_pParserState->m_aRedundantParentWidgets)
+ {
+ delete_by_window(elem.first);
+ }
+
+ //fdo#67378 merge the label into the disclosure button
+ for (auto const& elem : m_pParserState->m_aExpanderWidgets)
+ {
+ vcl::Window *pChild = elem->get_child();
+ vcl::Window* pLabel = elem->GetWindow(GetWindowType::LastChild);
+ if (pLabel && pLabel != pChild && pLabel->GetType() == WindowType::FIXEDTEXT)
+ {
+ FixedText *pLabelWidget = static_cast<FixedText*>(pLabel);
+ elem->set_label(pLabelWidget->GetText());
+ if (pLabelWidget->IsControlFont())
+ elem->get_label_widget()->SetControlFont(pLabelWidget->GetControlFont());
+ delete_by_window(pLabel);
+ }
+ }
+
+ // create message dialog message area now
+ for (auto const& elem : m_pParserState->m_aMessageDialogs)
+ elem->create_message_area();
+
+ //drop maps, etc. that we don't need again
+ m_pParserState.reset();
+
+ SAL_WARN_IF(!m_sID.isEmpty() && (!m_bToplevelParentFound && !get_by_name(m_sID)), "vcl.builder",
+ "Requested top level widget \"" << m_sID << "\" not found in " << sUIFile);
+
+#if defined SAL_LOG_WARN
+ if (m_bToplevelParentFound && m_pParent->IsDialog())
+ {
+ int nButtons = 0;
+ bool bHasDefButton = false;
+ for (auto const& child : m_aChildren)
+ {
+ if (isButtonType(child.m_pWindow->GetType()))
+ {
+ ++nButtons;
+ if (child.m_pWindow->GetStyle() & WB_DEFBUTTON)
+ {
+ bHasDefButton = true;
+ break;
+ }
+ }
+ }
+ SAL_WARN_IF(nButtons && !bHasDefButton, "vcl.builder", "No default button defined in " << sUIFile);
+ }
+#endif
+
+ const bool bHideHelp = comphelper::LibreOfficeKit::isActive() &&
+ officecfg::Office::Common::Help::HelpRootURL::get().isEmpty();
+ if (bHideHelp)
+ {
+ if (vcl::Window *pHelpButton = get("help"))
+ pHelpButton->Hide();
+ }
+}
+
+VclBuilder::~VclBuilder()
+{
+ disposeBuilder();
+}
+
+void VclBuilder::disposeBuilder()
+{
+ for (std::vector<WinAndId>::reverse_iterator aI = m_aChildren.rbegin(),
+ aEnd = m_aChildren.rend(); aI != aEnd; ++aI)
+ {
+ aI->m_pWindow.disposeAndClear();
+ }
+ m_aChildren.clear();
+
+ for (std::vector<MenuAndId>::reverse_iterator aI = m_aMenus.rbegin(),
+ aEnd = m_aMenus.rend(); aI != aEnd; ++aI)
+ {
+ aI->m_pMenu.disposeAndClear();
+ }
+ m_aMenus.clear();
+ m_pParent.clear();
+}
+
+namespace
+{
+ bool extractHasFrame(VclBuilder::stringmap& rMap)
+ {
+ bool bHasFrame = true;
+ VclBuilder::stringmap::iterator aFind = rMap.find("has-frame");
+ if (aFind != rMap.end())
+ {
+ bHasFrame = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bHasFrame;
+ }
+
+ bool extractDrawValue(VclBuilder::stringmap& rMap)
+ {
+ bool bDrawValue = true;
+ VclBuilder::stringmap::iterator aFind = rMap.find("draw-value");
+ if (aFind != rMap.end())
+ {
+ bDrawValue = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bDrawValue;
+ }
+
+ OUString extractPopupMenu(VclBuilder::stringmap& rMap)
+ {
+ OUString sRet;
+ VclBuilder::stringmap::iterator aFind = rMap.find("popup");
+ if (aFind != rMap.end())
+ {
+ sRet = aFind->second;
+ rMap.erase(aFind);
+ }
+ return sRet;
+ }
+
+ OUString extractWidgetName(VclBuilder::stringmap& rMap)
+ {
+ OUString sRet;
+ VclBuilder::stringmap::iterator aFind = rMap.find("name");
+ if (aFind != rMap.end())
+ {
+ sRet = aFind->second;
+ rMap.erase(aFind);
+ }
+ return sRet;
+ }
+
+ OUString extractValuePos(VclBuilder::stringmap& rMap)
+ {
+ OUString sRet("top");
+ VclBuilder::stringmap::iterator aFind = rMap.find("value-pos");
+ if (aFind != rMap.end())
+ {
+ sRet = aFind->second;
+ rMap.erase(aFind);
+ }
+ return sRet;
+ }
+
+ OUString extractTypeHint(VclBuilder::stringmap &rMap)
+ {
+ OUString sRet("normal");
+ VclBuilder::stringmap::iterator aFind = rMap.find("type-hint");
+ if (aFind != rMap.end())
+ {
+ sRet = aFind->second;
+ rMap.erase(aFind);
+ }
+ return sRet;
+ }
+
+ bool extractResizable(VclBuilder::stringmap &rMap)
+ {
+ bool bResizable = true;
+ VclBuilder::stringmap::iterator aFind = rMap.find("resizable");
+ if (aFind != rMap.end())
+ {
+ bResizable = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bResizable;
+ }
+
+#if HAVE_FEATURE_DESKTOP
+ bool extractModal(VclBuilder::stringmap &rMap)
+ {
+ bool bModal = false;
+ VclBuilder::stringmap::iterator aFind = rMap.find("modal");
+ if (aFind != rMap.end())
+ {
+ bModal = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bModal;
+ }
+#endif
+
+ bool extractDecorated(VclBuilder::stringmap &rMap)
+ {
+ bool bDecorated = true;
+ VclBuilder::stringmap::iterator aFind = rMap.find("decorated");
+ if (aFind != rMap.end())
+ {
+ bDecorated = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bDecorated;
+ }
+
+ bool extractCloseable(VclBuilder::stringmap &rMap)
+ {
+ bool bCloseable = true;
+ VclBuilder::stringmap::iterator aFind = rMap.find("deletable");
+ if (aFind != rMap.end())
+ {
+ bCloseable = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bCloseable;
+ }
+
+ bool extractEntry(VclBuilder::stringmap &rMap)
+ {
+ bool bHasEntry = false;
+ VclBuilder::stringmap::iterator aFind = rMap.find("has-entry");
+ if (aFind != rMap.end())
+ {
+ bHasEntry = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bHasEntry;
+ }
+
+ bool extractOrientation(VclBuilder::stringmap &rMap)
+ {
+ bool bVertical = false;
+ VclBuilder::stringmap::iterator aFind = rMap.find("orientation");
+ if (aFind != rMap.end())
+ {
+ bVertical = aFind->second.equalsIgnoreAsciiCase("vertical");
+ rMap.erase(aFind);
+ }
+ return bVertical;
+ }
+
+ bool extractVerticalTabPos(VclBuilder::stringmap &rMap)
+ {
+ bool bVertical = false;
+ VclBuilder::stringmap::iterator aFind = rMap.find("tab-pos");
+ if (aFind != rMap.end())
+ {
+ bVertical = aFind->second.equalsIgnoreAsciiCase("left") ||
+ aFind->second.equalsIgnoreAsciiCase("right");
+ rMap.erase(aFind);
+ }
+ return bVertical;
+ }
+
+ bool extractInconsistent(VclBuilder::stringmap &rMap)
+ {
+ bool bInconsistent = false;
+ VclBuilder::stringmap::iterator aFind = rMap.find("inconsistent");
+ if (aFind != rMap.end())
+ {
+ bInconsistent = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bInconsistent;
+ }
+
+ OUString extractIconName(VclBuilder::stringmap &rMap)
+ {
+ OUString sIconName;
+ // allow pixbuf, but prefer icon-name
+ {
+ VclBuilder::stringmap::iterator aFind = rMap.find("pixbuf");
+ if (aFind != rMap.end())
+ {
+ sIconName = aFind->second;
+ rMap.erase(aFind);
+ }
+ }
+ {
+ VclBuilder::stringmap::iterator aFind = rMap.find("icon-name");
+ if (aFind != rMap.end())
+ {
+ sIconName = aFind->second;
+ rMap.erase(aFind);
+ }
+ }
+ if (sIconName == "missing-image")
+ return OUString();
+ OUString sReplace = mapStockToImageResource(sIconName);
+ return !sReplace.isEmpty() ? sReplace : sIconName;
+ }
+
+ WinBits extractRelief(VclBuilder::stringmap &rMap)
+ {
+ WinBits nBits = WB_3DLOOK;
+ VclBuilder::stringmap::iterator aFind = rMap.find("relief");
+ if (aFind != rMap.end())
+ {
+ assert(aFind->second != "half" && "relief of 'half' unsupported");
+ if (aFind->second == "none")
+ nBits = WB_FLATBUTTON;
+ rMap.erase(aFind);
+ }
+ return nBits;
+ }
+
+ OUString extractLabel(VclBuilder::stringmap &rMap)
+ {
+ OUString sType;
+ VclBuilder::stringmap::iterator aFind = rMap.find("label");
+ if (aFind != rMap.end())
+ {
+ sType = aFind->second;
+ rMap.erase(aFind);
+ }
+ return sType;
+ }
+
+ OUString extractActionName(VclBuilder::stringmap &rMap)
+ {
+ OUString sActionName;
+ VclBuilder::stringmap::iterator aFind = rMap.find("action-name");
+ if (aFind != rMap.end())
+ {
+ sActionName = aFind->second;
+ rMap.erase(aFind);
+ }
+ return sActionName;
+ }
+
+ bool extractVisible(VclBuilder::stringmap &rMap)
+ {
+ bool bRet = false;
+ VclBuilder::stringmap::iterator aFind = rMap.find("visible");
+ if (aFind != rMap.end())
+ {
+ bRet = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bRet;
+ }
+
+ Size extractSizeRequest(VclBuilder::stringmap &rMap)
+ {
+ OUString sWidthRequest("0");
+ OUString sHeightRequest("0");
+ VclBuilder::stringmap::iterator aFind = rMap.find("width-request");
+ if (aFind != rMap.end())
+ {
+ sWidthRequest = aFind->second;
+ rMap.erase(aFind);
+ }
+ aFind = rMap.find("height-request");
+ if (aFind != rMap.end())
+ {
+ sHeightRequest = aFind->second;
+ rMap.erase(aFind);
+ }
+ return Size(sWidthRequest.toInt32(), sHeightRequest.toInt32());
+ }
+
+ OUString extractTooltipText(VclBuilder::stringmap &rMap)
+ {
+ OUString sTooltipText;
+ VclBuilder::stringmap::iterator aFind = rMap.find("tooltip-text");
+ if (aFind == rMap.end())
+ aFind = rMap.find("tooltip-markup");
+ if (aFind != rMap.end())
+ {
+ sTooltipText = aFind->second;
+ rMap.erase(aFind);
+ }
+ return sTooltipText;
+ }
+
+ float extractAlignment(VclBuilder::stringmap &rMap)
+ {
+ float f = 0.0;
+ VclBuilder::stringmap::iterator aFind = rMap.find("alignment");
+ if (aFind != rMap.end())
+ {
+ f = aFind->second.toFloat();
+ rMap.erase(aFind);
+ }
+ return f;
+ }
+
+ OUString extractTitle(VclBuilder::stringmap &rMap)
+ {
+ OUString sTitle;
+ VclBuilder::stringmap::iterator aFind = rMap.find("title");
+ if (aFind != rMap.end())
+ {
+ sTitle = aFind->second;
+ rMap.erase(aFind);
+ }
+ return sTitle;
+ }
+
+ bool extractHeadersVisible(VclBuilder::stringmap &rMap)
+ {
+ bool bHeadersVisible = true;
+ VclBuilder::stringmap::iterator aFind = rMap.find("headers-visible");
+ if (aFind != rMap.end())
+ {
+ bHeadersVisible = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bHeadersVisible;
+ }
+
+ bool extractSortIndicator(VclBuilder::stringmap &rMap)
+ {
+ bool bSortIndicator = false;
+ VclBuilder::stringmap::iterator aFind = rMap.find("sort-indicator");
+ if (aFind != rMap.end())
+ {
+ bSortIndicator = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bSortIndicator;
+ }
+
+ bool extractClickable(VclBuilder::stringmap &rMap)
+ {
+ bool bClickable = false;
+ VclBuilder::stringmap::iterator aFind = rMap.find("clickable");
+ if (aFind != rMap.end())
+ {
+ bClickable = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bClickable;
+ }
+
+ void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame)
+ {
+ if (!rFrame.is())
+ return;
+
+ OUString aCommand(extractActionName(rMap));
+ if (aCommand.isEmpty())
+ return;
+
+ OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, aModuleName);
+ OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
+ if (!aLabel.isEmpty())
+ pButton->SetText(aLabel);
+
+ OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(aCommand, aProperties, rFrame));
+ if (!aTooltip.isEmpty())
+ pButton->SetQuickHelpText(aTooltip);
+
+ Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommand, rFrame));
+ pButton->SetModeImage(aImage);
+
+ pButton->SetCommandHandler(aCommand, rFrame);
+ }
+
+ VclPtr<Button> extractStockAndBuildPushButton(vcl::Window *pParent, VclBuilder::stringmap &rMap, bool bToggle)
+ {
+ WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER;
+ if (bToggle)
+ nBits |= WB_TOGGLE;
+
+ nBits |= extractRelief(rMap);
+
+ VclPtr<Button> xWindow = VclPtr<PushButton>::Create(pParent, nBits);
+ return xWindow;
+ }
+
+ VclPtr<MenuButton> extractStockAndBuildMenuButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
+ {
+ WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
+
+ nBits |= extractRelief(rMap);
+
+ VclPtr<MenuButton> xWindow = VclPtr<MenuButton>::Create(pParent, nBits);
+ return xWindow;
+ }
+
+ VclPtr<MenuButton> extractStockAndBuildMenuToggleButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
+ {
+ WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
+
+ nBits |= extractRelief(rMap);
+
+ VclPtr<MenuButton> xWindow = VclPtr<MenuToggleButton>::Create(pParent, nBits);
+ return xWindow;
+ }
+
+ WinBits extractDeferredBits(VclBuilder::stringmap &rMap)
+ {
+ WinBits nBits = WB_3DLOOK|WB_HIDE;
+ if (extractResizable(rMap))
+ nBits |= WB_SIZEABLE;
+ if (extractCloseable(rMap))
+ nBits |= WB_CLOSEABLE;
+ if (!extractDecorated(rMap))
+ nBits |= WB_OWNERDRAWDECORATION;
+ OUString sType(extractTypeHint(rMap));
+ if (sType == "utility")
+ nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_MOVEABLE;
+ else if (sType == "popup-menu")
+ nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_POPUP;
+ else if (sType == "dock")
+ nBits |= WB_DOCKABLE | WB_MOVEABLE;
+ else
+ nBits |= WB_MOVEABLE;
+ return nBits;
+ }
+}
+
+void VclBuilder::extractGroup(const OUString &id, stringmap &rMap)
+{
+ VclBuilder::stringmap::iterator aFind = rMap.find("group");
+ if (aFind != rMap.end())
+ {
+ OUString sID = aFind->second;
+ sal_Int32 nDelim = sID.indexOf(':');
+ if (nDelim != -1)
+ sID = sID.copy(0, nDelim);
+ m_pParserState->m_aGroupMaps.emplace_back(id, sID);
+ rMap.erase(aFind);
+ }
+}
+
+void VclBuilder::connectNumericFormatterAdjustment(const OUString &id, const OUString &rAdjustment)
+{
+ if (!rAdjustment.isEmpty())
+ m_pParserState->m_aNumericFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
+}
+
+void VclBuilder::connectFormattedFormatterAdjustment(const OUString &id, const OUString &rAdjustment)
+{
+ if (!rAdjustment.isEmpty())
+ m_pParserState->m_aFormattedFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
+}
+
+bool VclBuilder::extractAdjustmentToMap(const OUString& id, VclBuilder::stringmap& rMap, std::vector<WidgetAdjustmentMap>& rAdjustmentMap)
+{
+ VclBuilder::stringmap::iterator aFind = rMap.find("adjustment");
+ if (aFind != rMap.end())
+ {
+ rAdjustmentMap.emplace_back(id, aFind->second);
+ rMap.erase(aFind);
+ return true;
+ }
+ return false;
+}
+
+namespace
+{
+ sal_Int32 extractActive(VclBuilder::stringmap &rMap)
+ {
+ sal_Int32 nActiveId = 0;
+ VclBuilder::stringmap::iterator aFind = rMap.find("active");
+ if (aFind != rMap.end())
+ {
+ nActiveId = aFind->second.toInt32();
+ rMap.erase(aFind);
+ }
+ return nActiveId;
+ }
+
+ bool extractSelectable(VclBuilder::stringmap &rMap)
+ {
+ bool bSelectable = false;
+ VclBuilder::stringmap::iterator aFind = rMap.find("selectable");
+ if (aFind != rMap.end())
+ {
+ bSelectable = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bSelectable;
+ }
+
+ OUString extractAdjustment(VclBuilder::stringmap &rMap)
+ {
+ OUString sAdjustment;
+ VclBuilder::stringmap::iterator aFind = rMap.find("adjustment");
+ if (aFind != rMap.end())
+ {
+ sAdjustment= aFind->second;
+ rMap.erase(aFind);
+ return sAdjustment;
+ }
+ return sAdjustment;
+ }
+
+ bool extractDrawIndicator(VclBuilder::stringmap &rMap)
+ {
+ bool bDrawIndicator = false;
+ VclBuilder::stringmap::iterator aFind = rMap.find("draw-indicator");
+ if (aFind != rMap.end())
+ {
+ bDrawIndicator = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bDrawIndicator;
+ }
+}
+
+void VclBuilder::extractModel(const OUString &id, stringmap &rMap)
+{
+ VclBuilder::stringmap::iterator aFind = rMap.find("model");
+ if (aFind != rMap.end())
+ {
+ m_pParserState->m_aModelMaps.emplace_back(id, aFind->second,
+ extractActive(rMap));
+ rMap.erase(aFind);
+ }
+}
+
+void VclBuilder::extractBuffer(const OUString &id, stringmap &rMap)
+{
+ VclBuilder::stringmap::iterator aFind = rMap.find("buffer");
+ if (aFind != rMap.end())
+ {
+ m_pParserState->m_aTextBufferMaps.emplace_back(id, aFind->second);
+ rMap.erase(aFind);
+ }
+}
+
+int VclBuilder::getImageSize(const stringmap &rMap)
+{
+ int nSize = 4;
+ auto aFind = rMap.find("icon-size");
+ if (aFind != rMap.end())
+ nSize = aFind->second.toInt32();
+ return nSize;
+}
+
+void VclBuilder::extractButtonImage(const OUString &id, stringmap &rMap, bool bRadio)
+{
+ VclBuilder::stringmap::iterator aFind = rMap.find("image");
+ if (aFind != rMap.end())
+ {
+ m_pParserState->m_aButtonImageWidgetMaps.emplace_back(id, aFind->second, bRadio);
+ rMap.erase(aFind);
+ }
+}
+
+void VclBuilder::extractMnemonicWidget(const OUString &rLabelID, stringmap &rMap)
+{
+ VclBuilder::stringmap::iterator aFind = rMap.find("mnemonic-widget");
+ if (aFind != rMap.end())
+ {
+ OUString sID = aFind->second;
+ sal_Int32 nDelim = sID.indexOf(':');
+ if (nDelim != -1)
+ sID = sID.copy(0, nDelim);
+ m_pParserState->m_aMnemonicWidgetMaps.emplace_back(rLabelID, sID);
+ rMap.erase(aFind);
+ }
+}
+
+vcl::Window* VclBuilder::prepareWidgetOwnScrolling(vcl::Window *pParent, WinBits &rWinStyle)
+{
+ //For Widgets that manage their own scrolling, if one appears as a child of
+ //a scrolling window shoehorn that scrolling settings to this widget and
+ //return the real parent to use
+ if (pParent && pParent->GetType() == WindowType::SCROLLWINDOW)
+ {
+ WinBits nScrollBits = pParent->GetStyle();
+ nScrollBits &= (WB_AUTOHSCROLL|WB_HSCROLL|WB_AUTOVSCROLL|WB_VSCROLL);
+ rWinStyle |= nScrollBits;
+ if (static_cast<VclScrolledWindow*>(pParent)->HasVisibleBorder())
+ rWinStyle |= WB_BORDER;
+ pParent = pParent->GetParent();
+ }
+
+ return pParent;
+}
+
+void VclBuilder::cleanupWidgetOwnScrolling(vcl::Window *pScrollParent, vcl::Window *pWindow, stringmap &rMap)
+{
+ //remove the redundant scrolling parent
+ sal_Int32 nWidthReq = pScrollParent->get_width_request();
+ rMap["width-request"] = OUString::number(nWidthReq);
+ sal_Int32 nHeightReq = pScrollParent->get_height_request();
+ rMap["height-request"] = OUString::number(nHeightReq);
+
+ m_pParserState->m_aRedundantParentWidgets[pScrollParent] = pWindow;
+}
+
+#ifndef DISABLE_DYNLOADING
+
+extern "C" { static void thisModule() {} }
+
+namespace {
+
+// Don't unload the module on destruction
+class NoAutoUnloadModule : public osl::Module
+{
+public:
+ ~NoAutoUnloadModule() { release(); }
+};
+
+}
+
+typedef std::map<OUString, std::shared_ptr<NoAutoUnloadModule>> ModuleMap;
+static ModuleMap g_aModuleMap;
+
+#if ENABLE_MERGELIBS
+static std::shared_ptr<NoAutoUnloadModule> g_pMergedLib = std::make_shared<NoAutoUnloadModule>();
+#endif
+
+#ifndef SAL_DLLPREFIX
+# define SAL_DLLPREFIX ""
+#endif
+
+#endif
+
+namespace vcl {
+
+void VclBuilderPreload()
+{
+#ifndef DISABLE_DYNLOADING
+
+#if ENABLE_MERGELIBS
+ g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
+#else
+// find -name '*ui*' | xargs grep 'class=".*lo-' |
+// sed 's/.*class="//' | sed 's/-.*$//' | sort | uniq
+ static const char *aWidgetLibs[] = {
+ "sfxlo", "svtlo"
+ };
+ for (const auto & lib : aWidgetLibs)
+ {
+ std::unique_ptr<NoAutoUnloadModule> pModule(new NoAutoUnloadModule);
+ OUString sModule = SAL_DLLPREFIX + OUString::createFromAscii(lib) + SAL_DLLEXTENSION;
+ if (pModule->loadRelative(&thisModule, sModule))
+ g_aModuleMap.insert(std::make_pair(sModule, std::move(pModule)));
+ }
+#endif // ENABLE_MERGELIBS
+#endif // DISABLE_DYNLOADING
+}
+
+}
+
+#if defined DISABLE_DYNLOADING && !HAVE_FEATURE_DESKTOP
+
+// This ifdef branch is mainly for building for the Collabora Online
+// -based mobile apps for Android and iOS.
+
+extern "C" VclBuilder::customMakeWidget lo_get_custom_widget_func(const char* name);
+
+#elif defined EMSCRIPTEN && !ENABLE_QT5
+
+// This branch is mainly for building for WASM, and especially for
+// Collabora Online in the browser, where code from core and Collabora
+// Online is compiled to WASM and linked into a single WASM binary.
+// (Not for Allotropia's Qt-based LibreOffice in the browser.)
+
+// When building core for WASM it doesn't use the same
+// solenv/bin/native-code.py thing as the mobile apps, even if in both
+// cases everything is linked statically. So there is no generated
+// native-code.h, and we can't use lo_get_custom_widget_func() from
+// that. So cheat and duplicate the code from an existing generated
+// native-code.h. It's just a handful of lines anyway.
+
+extern "C" void makeNotebookbarTabControl(VclPtr<vcl::Window> &rRet, const VclPtr<vcl::Window> &pParent, VclBuilder::stringmap &rVec);
+extern "C" void makeNotebookbarToolBox(VclPtr<vcl::Window> &rRet, const VclPtr<vcl::Window> &pParent, VclBuilder::stringmap &rVec);
+
+static struct { const char *name; VclBuilder::customMakeWidget func; } custom_widgets[] = {
+ { "makeNotebookbarTabControl", makeNotebookbarTabControl },
+ { "makeNotebookbarToolBox", makeNotebookbarToolBox },
+};
+
+static VclBuilder::customMakeWidget lo_get_custom_widget_func(const char* name)
+{
+ for (size_t i = 0; i < sizeof(custom_widgets) / sizeof(custom_widgets[0]); i++)
+ if (strcmp(name, custom_widgets[i].name) == 0)
+ return custom_widgets[i].func;
+ return nullptr;
+}
+
+#endif
+
+namespace
+{
+// Takes a string like "sfxlo-NotebookbarToolBox"
+VclBuilder::customMakeWidget GetCustomMakeWidget(const OUString& rName)
+{
+ const OUString name = rName == "sfxlo-SidebarToolBox" ? "sfxlo-NotebookbarToolBox" : rName;
+ VclBuilder::customMakeWidget pFunction = nullptr;
+ if (sal_Int32 nDelim = name.indexOf('-'); nDelim != -1)
+ {
+ const OUString sFunction(OUString::Concat("make") + name.subView(nDelim + 1));
+
+#ifndef DISABLE_DYNLOADING
+ const OUString sModule = OUString::Concat(SAL_DLLPREFIX)
+ + name.subView(0, nDelim)
+ + SAL_DLLEXTENSION;
+ ModuleMap::iterator aI = g_aModuleMap.find(sModule);
+ if (aI == g_aModuleMap.end())
+ {
+ std::shared_ptr<NoAutoUnloadModule> pModule;
+#if ENABLE_MERGELIBS
+ if (!g_pMergedLib->is())
+ g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
+ if ((pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
+ g_pMergedLib->getFunctionSymbol(sFunction))))
+ pModule = g_pMergedLib;
+#endif
+ if (!pFunction)
+ {
+ pModule = std::make_shared<NoAutoUnloadModule>();
+ bool ok = pModule->loadRelative(&thisModule, sModule);
+ if (!ok)
+ {
+#ifdef LINUX
+ // in the case of preloading, we don't have eg. the
+ // libcuilo.so, but still need to dlsym the symbols -
+ // which are already in-process
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(dlsym(RTLD_DEFAULT, OUStringToOString(sFunction, RTL_TEXTENCODING_UTF8).getStr()));
+ ok = !!pFunction;
+ assert(ok && "couldn't even directly dlsym the sFunction (available via preload)");
+ }
+#endif
+ assert(ok && "bad module name in .ui");
+ }
+ else
+ {
+ pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
+ pModule->getFunctionSymbol(sFunction));
+ }
+ }
+ g_aModuleMap.insert(std::make_pair(sModule, pModule));
+ }
+ else
+ pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
+ aI->second->getFunctionSymbol(sFunction));
+#elif !HAVE_FEATURE_DESKTOP || (defined EMSCRIPTEN && !ENABLE_QT5)
+ // This ifdef branch is mainly for building for either the
+ // Android or iOS apps, or the Collabora Online as WASM thing.
+ pFunction = lo_get_custom_widget_func(sFunction.toUtf8().getStr());
+ SAL_WARN_IF(!pFunction, "vcl.builder", "Could not find " << sFunction);
+ assert(pFunction);
+#else
+ pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
+ osl_getFunctionSymbol((oslModule)RTLD_DEFAULT, sFunction.pData));
+#endif
+ }
+ return pFunction;
+}
+}
+
+VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OUString &name, const OUString &id,
+ stringmap &rMap)
+{
+ bool bIsPlaceHolder = name.isEmpty();
+ bool bVertical = false;
+
+ if (pParent && (pParent->GetType() == WindowType::TABCONTROL ||
+ pParent->GetType() == WindowType::VERTICALTABCONTROL))
+ {
+ bool bTopLevel(name == "GtkDialog" || name == "GtkMessageDialog" ||
+ name == "GtkWindow" || name == "GtkPopover" || name == "GtkAssistant");
+ if (!bTopLevel)
+ {
+ if (pParent->GetType() == WindowType::TABCONTROL)
+ {
+ //We have to add a page
+ //make default pageid == position
+ TabControl *pTabControl = static_cast<TabControl*>(pParent);
+ sal_uInt16 nNewPageCount = pTabControl->GetPageCount()+1;
+ sal_uInt16 nNewPageId = nNewPageCount;
+ pTabControl->InsertPage(nNewPageId, OUString());
+ pTabControl->SetCurPageId(nNewPageId);
+ SAL_WARN_IF(bIsPlaceHolder, "vcl.builder", "we should have no placeholders for tabpages");
+ if (!bIsPlaceHolder)
+ {
+ VclPtrInstance<TabPage> pPage(pTabControl);
+ pPage->Show();
+
+ //Make up a name for it
+ OUString sTabPageId = get_by_window(pParent) +
+ "-page" +
+ OUString::number(nNewPageCount);
+ m_aChildren.emplace_back(sTabPageId, pPage, false);
+ pPage->SetHelpId(m_sHelpRoot + sTabPageId);
+
+ pParent = pPage;
+
+ pTabControl->SetTabPage(nNewPageId, pPage);
+ }
+ }
+ else
+ {
+ VerticalTabControl *pTabControl = static_cast<VerticalTabControl*>(pParent);
+ SAL_WARN_IF(bIsPlaceHolder, "vcl.builder", "we should have no placeholders for tabpages");
+ if (!bIsPlaceHolder)
+ pParent = pTabControl->GetPageParent();
+ }
+ }
+ }
+
+ if (bIsPlaceHolder || name == "GtkTreeSelection")
+ return nullptr;
+
+ ToolBox *pToolBox = (pParent && pParent->GetType() == WindowType::TOOLBOX) ? static_cast<ToolBox*>(pParent) : nullptr;
+
+ extractButtonImage(id, rMap, name == "GtkRadioButton");
+
+ VclPtr<vcl::Window> xWindow;
+ if (name == "GtkDialog" || name == "GtkAssistant")
+ {
+ // WB_ALLOWMENUBAR because we don't know in advance if we will encounter
+ // a menubar, and menubars need a BorderWindow in the toplevel, and
+ // such border windows need to be in created during the dialog ctor
+ WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_ALLOWMENUBAR;
+ if (extractResizable(rMap))
+ nBits |= WB_SIZEABLE;
+ if (extractCloseable(rMap))
+ nBits |= WB_CLOSEABLE;
+ Dialog::InitFlag eInit = !pParent ? Dialog::InitFlag::NoParent : Dialog::InitFlag::Default;
+ if (name == "GtkAssistant")
+ xWindow = VclPtr<vcl::RoadmapWizard>::Create(pParent, nBits, eInit);
+ else
+ xWindow = VclPtr<Dialog>::Create(pParent, nBits, eInit);
+#if HAVE_FEATURE_DESKTOP
+ if (!extractModal(rMap))
+ xWindow->SetType(WindowType::MODELESSDIALOG);
+#endif
+ }
+ else if (name == "GtkMessageDialog")
+ {
+ WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_CLOSEABLE;
+ if (extractResizable(rMap))
+ nBits |= WB_SIZEABLE;
+ VclPtr<MessageDialog> xDialog(VclPtr<MessageDialog>::Create(pParent, nBits));
+ m_pParserState->m_aMessageDialogs.push_back(xDialog);
+ xWindow = xDialog;
+#if defined _WIN32
+ xWindow->set_border_width(3);
+#else
+ xWindow->set_border_width(12);
+#endif
+ }
+ else if (name == "GtkBox" || name == "GtkStatusbar")
+ {
+ bVertical = extractOrientation(rMap);
+ if (bVertical)
+ xWindow = VclPtr<VclVBox>::Create(pParent);
+ else
+ xWindow = VclPtr<VclHBox>::Create(pParent);
+
+ if (name == "GtkStatusbar")
+ xWindow->SetAccessibleRole(css::accessibility::AccessibleRole::STATUS_BAR);
+ }
+ else if (name == "GtkPaned")
+ {
+ bVertical = extractOrientation(rMap);
+ if (bVertical)
+ xWindow = VclPtr<VclVPaned>::Create(pParent);
+ else
+ xWindow = VclPtr<VclHPaned>::Create(pParent);
+ }
+ else if (name == "GtkHBox")
+ xWindow = VclPtr<VclHBox>::Create(pParent);
+ else if (name == "GtkVBox")
+ xWindow = VclPtr<VclVBox>::Create(pParent);
+ else if (name == "GtkButtonBox")
+ {
+ bVertical = extractOrientation(rMap);
+ if (bVertical)
+ xWindow = VclPtr<VclVButtonBox>::Create(pParent);
+ else
+ xWindow = VclPtr<VclHButtonBox>::Create(pParent);
+ }
+ else if (name == "GtkHButtonBox")
+ xWindow = VclPtr<VclHButtonBox>::Create(pParent);
+ else if (name == "GtkVButtonBox")
+ xWindow = VclPtr<VclVButtonBox>::Create(pParent);
+ else if (name == "GtkGrid")
+ xWindow = VclPtr<VclGrid>::Create(pParent);
+ else if (name == "GtkFrame")
+ xWindow = VclPtr<VclFrame>::Create(pParent);
+ else if (name == "GtkExpander")
+ {
+ VclPtrInstance<VclExpander> pExpander(pParent);
+ m_pParserState->m_aExpanderWidgets.push_back(pExpander);
+ xWindow = pExpander;
+ }
+ else if (name == "GtkButton" || (!m_bLegacy && name == "GtkToggleButton"))
+ {
+ VclPtr<Button> xButton;
+ OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
+ if (sMenu.isEmpty())
+ xButton = extractStockAndBuildPushButton(pParent, rMap, name == "GtkToggleButton");
+ else
+ {
+ assert(m_bLegacy && "use GtkMenuButton");
+ xButton = extractStockAndBuildMenuButton(pParent, rMap);
+ m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
+ }
+ xButton->SetImageAlign(ImageAlign::Left); //default to left
+ setupFromActionName(xButton, rMap, m_xFrame);
+ xWindow = xButton;
+ }
+ else if (name == "GtkMenuButton")
+ {
+ VclPtr<MenuButton> xButton;
+
+ OUString sMenu = extractPopupMenu(rMap);
+ if (!sMenu.isEmpty())
+ m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
+
+ OUString sType = extractWidgetName(rMap);
+ if (sType.isEmpty())
+ {
+ xButton = extractStockAndBuildMenuButton(pParent, rMap);
+ xButton->SetAccessibleRole(css::accessibility::AccessibleRole::BUTTON_MENU);
+ }
+ else
+ {
+ xButton = extractStockAndBuildMenuToggleButton(pParent, rMap);
+ }
+
+ xButton->SetImageAlign(ImageAlign::Left); //default to left
+
+ if (!extractDrawIndicator(rMap))
+ xButton->SetDropDown(PushButtonDropdownStyle::NONE);
+
+ setupFromActionName(xButton, rMap, m_xFrame);
+ xWindow = xButton;
+ }
+ else if (name == "GtkToggleButton" && m_bLegacy)
+ {
+ VclPtr<Button> xButton;
+ OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
+ assert(sMenu.getLength() && "not implemented yet");
+ xButton = extractStockAndBuildMenuToggleButton(pParent, rMap);
+ m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
+ xButton->SetImageAlign(ImageAlign::Left); //default to left
+ setupFromActionName(xButton, rMap, m_xFrame);
+ xWindow = xButton;
+ }
+ else if (name == "GtkRadioButton")
+ {
+ extractGroup(id, rMap);
+ WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
+ VclPtr<RadioButton> xButton = VclPtr<RadioButton>::Create(pParent, true, nBits);
+ xButton->SetImageAlign(ImageAlign::Left); //default to left
+ xWindow = xButton;
+ }
+ else if (name == "GtkCheckButton")
+ {
+ WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
+ bool bIsTriState = extractInconsistent(rMap);
+ VclPtr<CheckBox> xCheckBox = VclPtr<CheckBox>::Create(pParent, nBits);
+ if (bIsTriState)
+ {
+ xCheckBox->EnableTriState(true);
+ xCheckBox->SetState(TRISTATE_INDET);
+ }
+ xCheckBox->SetImageAlign(ImageAlign::Left); //default to left
+
+ xWindow = xCheckBox;
+ }
+ else if (name == "GtkSpinButton")
+ {
+ OUString sAdjustment = extractAdjustment(rMap);
+
+ WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_3DLOOK|WB_SPIN|WB_REPEAT;
+ if (extractHasFrame(rMap))
+ nBits |= WB_BORDER;
+
+ connectFormattedFormatterAdjustment(id, sAdjustment);
+ VclPtrInstance<FormattedField> xField(pParent, nBits);
+ xField->GetFormatter().SetMinValue(0);
+ xWindow = xField;
+ }
+ else if (name == "GtkLinkButton")
+ xWindow = VclPtr<FixedHyperlink>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_NOLABEL);
+ else if (name == "GtkComboBox" || name == "GtkComboBoxText")
+ {
+ extractModel(id, rMap);
+
+ WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
+
+ bool bDropdown = BuilderUtils::extractDropdown(rMap);
+
+ if (bDropdown)
+ nBits |= WB_DROPDOWN;
+
+ if (extractEntry(rMap))
+ {
+ VclPtrInstance<ComboBox> xComboBox(pParent, nBits);
+ xComboBox->EnableAutoSize(true);
+ xWindow = xComboBox;
+ }
+ else
+ {
+ VclPtrInstance<ListBox> xListBox(pParent, nBits|WB_SIMPLEMODE);
+ xListBox->EnableAutoSize(true);
+ xWindow = xListBox;
+ }
+ }
+ else if (name == "VclOptionalBox" || name == "sfxlo-OptionalBox")
+ {
+ // tdf#135495 fallback sfxlo-OptionalBox to VclOptionalBox as a stopgap
+ xWindow = VclPtr<OptionalBox>::Create(pParent);
+ }
+ else if (name == "svtlo-ManagedMenuButton")
+ {
+ // like tdf#135495 keep the name svtlo-ManagedMenuButton even though it's a misnomer
+ // and is not dlsymed from the svt library
+ xWindow = VclPtr<ManagedMenuButton>::Create(pParent, WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_FLATBUTTON);
+ OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
+ if (!sMenu.isEmpty())
+ m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
+ setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
+ }
+ else if (name == "sfxlo-PriorityMergedHBox")
+ {
+ // like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
+ xWindow = VclPtr<PriorityMergedHBox>::Create(pParent);
+ }
+ else if (name == "sfxlo-PriorityHBox")
+ {
+ // like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
+ xWindow = VclPtr<PriorityHBox>::Create(pParent);
+ }
+ else if (name == "sfxlo-DropdownBox")
+ {
+ // like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
+ xWindow = VclPtr<DropdownBox>::Create(pParent);
+ }
+ else if (name == "sfxlo-ContextVBox")
+ {
+ // like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
+ xWindow = VclPtr<ContextVBox>::Create(pParent);
+ }
+ else if (name == "GtkIconView")
+ {
+ assert(rMap.find("model") != rMap.end() && "GtkIconView must have a model");
+
+ //window we want to apply the packing props for this GtkIconView to
+ VclPtr<vcl::Window> xWindowForPackingProps;
+ extractModel(id, rMap);
+ WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
+ //IconView manages its own scrolling,
+ vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
+
+ VclPtr<IconView> xBox = VclPtr<IconView>::Create(pRealParent, nWinStyle);
+ xWindowForPackingProps = xBox;
+
+ xWindow = xBox;
+ xBox->SetNoAutoCurEntry(true);
+ xBox->SetQuickSearch(true);
+
+ if (pRealParent != pParent)
+ cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
+ }
+ else if (name == "GtkTreeView")
+ {
+ if (!m_bLegacy)
+ {
+ assert(rMap.find("model") != rMap.end() && "GtkTreeView must have a model");
+ }
+
+ //window we want to apply the packing props for this GtkTreeView to
+ VclPtr<vcl::Window> xWindowForPackingProps;
+ //To-Do
+ //a) make SvHeaderTabListBox/SvTabListBox the default target for GtkTreeView
+ //b) remove the non-drop down mode of ListBox and convert
+ // everything over to SvHeaderTabListBox/SvTabListBox
+ extractModel(id, rMap);
+ WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
+ if (m_bLegacy)
+ {
+ OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
+ if (!sBorder.isEmpty())
+ nWinStyle |= WB_BORDER;
+ }
+ else
+ {
+ nWinStyle |= WB_HASBUTTONS | WB_HASBUTTONSATROOT;
+ }
+ //ListBox/SvHeaderTabListBox manages its own scrolling,
+ vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
+ if (m_bLegacy)
+ {
+ xWindow = VclPtr<ListBox>::Create(pRealParent, nWinStyle | WB_SIMPLEMODE);
+ xWindowForPackingProps = xWindow;
+ }
+ else
+ {
+ VclPtr<SvTabListBox> xBox;
+ bool bHeadersVisible = extractHeadersVisible(rMap);
+ if (bHeadersVisible)
+ {
+ VclPtr<VclVBox> xContainer = VclPtr<VclVBox>::Create(pRealParent);
+ OUString containerid(id + "-container");
+ xContainer->SetHelpId(m_sHelpRoot + containerid);
+ m_aChildren.emplace_back(containerid, xContainer, true);
+
+ VclPtrInstance<HeaderBar> xHeader(xContainer, WB_BUTTONSTYLE | WB_BORDER | WB_TABSTOP | WB_3DLOOK);
+ xHeader->set_width_request(0); // let the headerbar width not affect the size request
+ OUString headerid(id + "-header");
+ xHeader->SetHelpId(m_sHelpRoot + headerid);
+ m_aChildren.emplace_back(headerid, xHeader, true);
+
+ VclPtr<LclHeaderTabListBox> xHeaderBox = VclPtr<LclHeaderTabListBox>::Create(xContainer, nWinStyle);
+ xHeaderBox->InitHeaderBar(xHeader);
+ xContainer->set_expand(true);
+ xHeader->Show();
+ xContainer->Show();
+ xBox = xHeaderBox;
+ xWindowForPackingProps = xContainer;
+ }
+ else
+ {
+ xBox = VclPtr<LclTabListBox>::Create(pRealParent, nWinStyle);
+ xWindowForPackingProps = xBox;
+ }
+ xWindow = xBox;
+ xBox->SetNoAutoCurEntry(true);
+ xBox->SetQuickSearch(true);
+ xBox->SetSpaceBetweenEntries(3);
+ xBox->SetEntryHeight(16);
+ xBox->SetHighlightRange(); // select over the whole width
+ }
+ if (pRealParent != pParent)
+ cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
+ }
+ else if (name == "GtkTreeViewColumn")
+ {
+ if (!m_bLegacy)
+ {
+ SvHeaderTabListBox* pTreeView = dynamic_cast<SvHeaderTabListBox*>(pParent);
+ if (HeaderBar* pHeaderBar = pTreeView ? pTreeView->GetHeaderBar() : nullptr)
+ {
+ HeaderBarItemBits nBits = HeaderBarItemBits::LEFTIMAGE;
+ if (extractClickable(rMap))
+ nBits |= HeaderBarItemBits::CLICKABLE;
+ if (extractSortIndicator(rMap))
+ nBits |= HeaderBarItemBits::DOWNARROW;
+ float fAlign = extractAlignment(rMap);
+ if (fAlign == 0.0)
+ nBits |= HeaderBarItemBits::LEFT;
+ else if (fAlign == 1.0)
+ nBits |= HeaderBarItemBits::RIGHT;
+ else if (fAlign == 0.5)
+ nBits |= HeaderBarItemBits::CENTER;
+ auto nItemId = pHeaderBar->GetItemCount() + 1;
+ OUString sTitle(extractTitle(rMap));
+ pHeaderBar->InsertItem(nItemId, sTitle, 100, nBits);
+ }
+ }
+ }
+ else if (name == "GtkLabel")
+ {
+ WinBits nWinStyle = WB_CENTER|WB_VCENTER|WB_3DLOOK;
+ extractMnemonicWidget(id, rMap);
+ if (extractSelectable(rMap))
+ xWindow = VclPtr<SelectableFixedText>::Create(pParent, nWinStyle);
+ else
+ xWindow = VclPtr<FixedText>::Create(pParent, nWinStyle);
+ }
+ else if (name == "GtkImage")
+ {
+ VclPtr<FixedImage> xFixedImage = VclPtr<FixedImage>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_SCALE);
+ OUString sIconName = extractIconName(rMap);
+ if (!sIconName.isEmpty())
+ xFixedImage->SetImage(FixedImage::loadThemeImage(sIconName));
+ m_pParserState->m_aImageSizeMap[id] = getImageSize(rMap);
+ xWindow = xFixedImage;
+ //such parentless GtkImages are temps used to set icons on buttons
+ //default them to hidden to stop e.g. insert->index entry flicking temp
+ //full screen windows
+ if (!pParent)
+ {
+ rMap["visible"] = "false";
+ }
+ }
+ else if (name == "GtkSeparator")
+ {
+ bVertical = extractOrientation(rMap);
+ xWindow = VclPtr<FixedLine>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
+ }
+ else if (name == "GtkScrollbar")
+ {
+ extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
+ bVertical = extractOrientation(rMap);
+ xWindow = VclPtr<ScrollBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
+ }
+ else if (name == "GtkProgressBar")
+ {
+ extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
+ bVertical = extractOrientation(rMap);
+ xWindow = VclPtr<ProgressBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ, ProgressBar::BarStyle::Progress);
+ }
+ else if (name == "GtkLevelBar")
+ {
+ extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
+ bVertical = extractOrientation(rMap);
+ xWindow = VclPtr<ProgressBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ, ProgressBar::BarStyle::Level);
+ }
+ else if (name == "GtkScrolledWindow")
+ {
+ xWindow = VclPtr<VclScrolledWindow>::Create(pParent);
+ }
+ else if (name == "GtkViewport")
+ {
+ xWindow = VclPtr<VclViewport>::Create(pParent);
+ }
+ else if (name == "GtkEventBox")
+ {
+ xWindow = VclPtr<VclEventBox>::Create(pParent);
+ }
+ else if (name == "GtkEntry")
+ {
+ WinBits nWinStyle = WB_LEFT|WB_VCENTER|WB_3DLOOK;
+ if (extractHasFrame(rMap))
+ nWinStyle |= WB_BORDER;
+ xWindow = VclPtr<Edit>::Create(pParent, nWinStyle);
+ BuilderUtils::ensureDefaultWidthChars(rMap);
+ }
+ else if (name == "GtkNotebook")
+ {
+ if (!extractVerticalTabPos(rMap))
+ xWindow = VclPtr<TabControl>::Create(pParent, WB_STDTABCONTROL|WB_3DLOOK);
+ else
+ xWindow = VclPtr<VerticalTabControl>::Create(pParent);
+ }
+ else if (name == "GtkDrawingArea")
+ {
+ xWindow = VclPtr<VclDrawingArea>::Create(pParent, WB_TABSTOP);
+ }
+ else if (name == "GtkTextView")
+ {
+ extractBuffer(id, rMap);
+
+ WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT;
+ //VclMultiLineEdit manages its own scrolling,
+ vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
+ xWindow = VclPtr<VclMultiLineEdit>::Create(pRealParent, nWinStyle);
+ if (pRealParent != pParent)
+ cleanupWidgetOwnScrolling(pParent, xWindow, rMap);
+ }
+ else if (name == "GtkSpinner")
+ {
+ xWindow = VclPtr<Throbber>::Create(pParent, WB_3DLOOK);
+ }
+ else if (name == "GtkScale")
+ {
+ extractAdjustmentToMap(id, rMap, m_pParserState->m_aSliderAdjustmentMaps);
+ bool bDrawValue = extractDrawValue(rMap);
+ if (bDrawValue)
+ {
+ OUString sValuePos = extractValuePos(rMap);
+ (void)sValuePos;
+ }
+ bVertical = extractOrientation(rMap);
+
+ WinBits nWinStyle = bVertical ? WB_VERT : WB_HORZ;
+
+ xWindow = VclPtr<Slider>::Create(pParent, nWinStyle);
+ }
+ else if (name == "GtkToolbar")
+ {
+ xWindow = VclPtr<ToolBox>::Create(pParent, WB_3DLOOK | WB_TABSTOP);
+ }
+ else if(name == "NotebookBarAddonsToolMergePoint")
+ {
+ customMakeWidget pFunction = GetCustomMakeWidget("sfxlo-NotebookbarToolBox");
+ if(pFunction != nullptr)
+ NotebookBarAddonsMerger::MergeNotebookBarAddons(pParent, pFunction, m_xFrame, *m_pNotebookBarAddonsItem, rMap);
+ return nullptr;
+ }
+ else if (name == "GtkToolButton" || name == "GtkMenuToolButton" ||
+ name == "GtkToggleToolButton" || name == "GtkRadioToolButton" || name == "GtkToolItem")
+ {
+ if (pToolBox)
+ {
+ OUString aCommand(extractActionName(rMap));
+
+ ToolBoxItemId nItemId(0);
+ ToolBoxItemBits nBits = ToolBoxItemBits::NONE;
+ if (name == "GtkMenuToolButton")
+ nBits |= ToolBoxItemBits::DROPDOWN;
+ else if (name == "GtkToggleToolButton")
+ nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::CHECKABLE;
+ else if (name == "GtkRadioToolButton")
+ nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::RADIOCHECK;
+
+ if (!aCommand.isEmpty() && m_xFrame.is())
+ {
+ pToolBox->InsertItem(aCommand, m_xFrame, nBits, extractSizeRequest(rMap));
+ nItemId = pToolBox->GetItemId(aCommand);
+ }
+ else
+ {
+ nItemId = ToolBoxItemId(pToolBox->GetItemCount() + 1);
+ //TODO: ImplToolItems::size_type -> sal_uInt16!
+ if (aCommand.isEmpty() && !m_bLegacy)
+ aCommand = id;
+ pToolBox->InsertItem(nItemId, extractLabel(rMap), aCommand, nBits);
+ }
+
+ pToolBox->SetHelpId(nItemId, m_sHelpRoot + id);
+ OUString sTooltip(extractTooltipText(rMap));
+ if (!sTooltip.isEmpty())
+ pToolBox->SetQuickHelpText(nItemId, sTooltip);
+
+ OUString sIconName(extractIconName(rMap));
+ if (!sIconName.isEmpty())
+ pToolBox->SetItemImage(nItemId, FixedImage::loadThemeImage(sIconName));
+
+ if (!extractVisible(rMap))
+ pToolBox->HideItem(nItemId);
+
+ m_pParserState->m_nLastToolbarId = nItemId;
+
+ return nullptr; // no widget to be created
+ }
+ }
+ else if (name == "GtkSeparatorToolItem")
+ {
+ if (pToolBox)
+ {
+ pToolBox->InsertSeparator();
+ return nullptr; // no widget to be created
+ }
+ }
+ else if (name == "GtkWindow")
+ {
+ WinBits nBits = extractDeferredBits(rMap);
+ if (nBits & WB_DOCKABLE)
+ xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_MOVEABLE);
+ else
+ xWindow = VclPtr<FloatingWindow>::Create(pParent, nBits|WB_MOVEABLE);
+ }
+ else if (name == "GtkPopover")
+ {
+ WinBits nBits = extractDeferredBits(rMap);
+ xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_DOCKABLE|WB_MOVEABLE);
+ }
+ else if (name == "GtkCalendar")
+ {
+ WinBits nBits = extractDeferredBits(rMap);
+ xWindow = VclPtr<Calendar>::Create(pParent, nBits);
+ }
+ else
+ {
+ if (customMakeWidget pFunction = GetCustomMakeWidget(name))
+ {
+ pFunction(xWindow, pParent, rMap);
+ if (xWindow->GetType() == WindowType::PUSHBUTTON)
+ setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
+ else if (xWindow->GetType() == WindowType::MENUBUTTON)
+ {
+ OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
+ if (!sMenu.isEmpty())
+ m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
+ setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
+ }
+ }
+ }
+
+ SAL_INFO_IF(!xWindow, "vcl.builder", "probably need to implement " << name << " or add a make" << name << " function");
+ if (xWindow)
+ {
+ // child windows of disabled windows are made disabled by vcl by default, we don't want that
+ WindowImpl *pWindowImpl = xWindow->ImplGetWindowImpl();
+ pWindowImpl->mbDisabled = false;
+
+ xWindow->SetHelpId(m_sHelpRoot + id);
+ SAL_INFO("vcl.builder", "for name '" << name << "' and id '" << id <<
+ "', created " << xWindow.get() << " child of " <<
+ pParent << "(" << xWindow->ImplGetWindowImpl()->mpParent.get() << "/" <<
+ xWindow->ImplGetWindowImpl()->mpRealParent.get() << "/" <<
+ xWindow->ImplGetWindowImpl()->mpBorderWindow.get() << ") with helpid " <<
+ xWindow->GetHelpId());
+ m_aChildren.emplace_back(id, xWindow, bVertical);
+
+ // if the parent was a toolbox set it as an itemwindow for the latest itemid
+ if (pToolBox)
+ {
+ Size aSize(xWindow->GetSizePixel());
+ aSize.setHeight(xWindow->get_preferred_size().Height());
+ xWindow->SetSizePixel(aSize);
+ pToolBox->SetItemWindow(m_pParserState->m_nLastToolbarId, xWindow);
+ pToolBox->SetItemExpand(m_pParserState->m_nLastToolbarId, true);
+ }
+ }
+ return xWindow;
+}
+
+namespace
+{
+ //return true for window types which exist in vcl but are not themselves
+ //represented in the .ui format, i.e. only their children exist.
+ bool isConsideredGtkPseudo(vcl::Window const *pWindow)
+ {
+ return pWindow->GetType() == WindowType::TABPAGE;
+ }
+}
+
+//Any properties from .ui load we couldn't set because of potential virtual methods
+//during ctor are applied here
+void VclBuilder::setDeferredProperties()
+{
+ if (!m_bToplevelHasDeferredProperties)
+ return;
+ stringmap aDeferredProperties;
+ aDeferredProperties.swap(m_aDeferredProperties);
+ m_bToplevelHasDeferredProperties = false;
+ BuilderUtils::set_properties(m_pParent, aDeferredProperties);
+}
+
+namespace BuilderUtils
+{
+ void set_properties(vcl::Window *pWindow, const VclBuilder::stringmap &rProps)
+ {
+ for (auto const& [rKey, rValue] : rProps)
+ pWindow->set_property(rKey, rValue);
+ }
+
+ OUString convertMnemonicMarkup(std::u16string_view rIn)
+ {
+ OUStringBuffer aRet(rIn);
+ for (sal_Int32 nI = 0; nI < aRet.getLength(); ++nI)
+ {
+ if (aRet[nI] == '_' && nI+1 < aRet.getLength())
+ {
+ if (aRet[nI+1] != '_')
+ aRet[nI] = MNEMONIC_CHAR;
+ else
+ aRet.remove(nI, 1);
+ ++nI;
+ }
+ }
+ return aRet.makeStringAndClear();
+ }
+
+ OUString extractCustomProperty(VclBuilder::stringmap &rMap)
+ {
+ OUString sCustomProperty;
+ VclBuilder::stringmap::iterator aFind = rMap.find("customproperty");
+ if (aFind != rMap.end())
+ {
+ sCustomProperty = aFind->second;
+ rMap.erase(aFind);
+ }
+ return sCustomProperty;
+ }
+
+ void ensureDefaultWidthChars(VclBuilder::stringmap &rMap)
+ {
+ OUString sWidthChars("width-chars");
+ VclBuilder::stringmap::iterator aFind = rMap.find(sWidthChars);
+ if (aFind == rMap.end())
+ rMap[sWidthChars] = "20";
+ }
+
+ bool extractDropdown(VclBuilder::stringmap &rMap)
+ {
+ bool bDropdown = true;
+ VclBuilder::stringmap::iterator aFind = rMap.find("dropdown");
+ if (aFind != rMap.end())
+ {
+ bDropdown = toBool(aFind->second);
+ rMap.erase(aFind);
+ }
+ return bDropdown;
+ }
+
+ void reorderWithinParent(vcl::Window &rWindow, sal_uInt16 nNewPosition)
+ {
+ WindowImpl *pWindowImpl = rWindow.ImplGetWindowImpl();
+ if (pWindowImpl->mpParent != pWindowImpl->mpRealParent)
+ {
+ assert(pWindowImpl->mpBorderWindow == pWindowImpl->mpParent);
+ assert(pWindowImpl->mpBorderWindow->ImplGetWindowImpl()->mpParent == pWindowImpl->mpRealParent);
+ reorderWithinParent(*pWindowImpl->mpBorderWindow, nNewPosition);
+ return;
+ }
+ rWindow.reorderWithinParent(nNewPosition);
+ }
+
+ void reorderWithinParent(std::vector<vcl::Window*>& rChilds, bool bIsButtonBox)
+ {
+ for (size_t i = 0; i < rChilds.size(); ++i)
+ {
+ reorderWithinParent(*rChilds[i], i);
+
+ if (!bIsButtonBox)
+ continue;
+
+ //The first member of the group for legacy code needs WB_GROUP set and the
+ //others not
+ WinBits nBits = rChilds[i]->GetStyle();
+ nBits &= ~WB_GROUP;
+ if (i == 0)
+ nBits |= WB_GROUP;
+ rChilds[i]->SetStyle(nBits);
+ }
+ }
+
+ sal_Int16 getRoleFromName(const OUString& roleName)
+ {
+ using namespace com::sun::star::accessibility;
+
+ static const std::unordered_map<OUString, sal_Int16> aAtkRoleToAccessibleRole = {
+ /* This is in atkobject.h's AtkRole order */
+ { "invalid", AccessibleRole::UNKNOWN },
+ { "accelerator label", AccessibleRole::UNKNOWN },
+ { "alert", AccessibleRole::ALERT },
+ { "animation", AccessibleRole::UNKNOWN },
+ { "arrow", AccessibleRole::UNKNOWN },
+ { "calendar", AccessibleRole::UNKNOWN },
+ { "canvas", AccessibleRole::CANVAS },
+ { "check box", AccessibleRole::CHECK_BOX },
+ { "check menu item", AccessibleRole::CHECK_MENU_ITEM },
+ { "color chooser", AccessibleRole::COLOR_CHOOSER },
+ { "column header", AccessibleRole::COLUMN_HEADER },
+ { "combo box", AccessibleRole::COMBO_BOX },
+ { "date editor", AccessibleRole::DATE_EDITOR },
+ { "desktop icon", AccessibleRole::DESKTOP_ICON },
+ { "desktop frame", AccessibleRole::DESKTOP_PANE }, // ?
+ { "dial", AccessibleRole::UNKNOWN },
+ { "dialog", AccessibleRole::DIALOG },
+ { "directory pane", AccessibleRole::DIRECTORY_PANE },
+ { "drawing area", AccessibleRole::UNKNOWN },
+ { "file chooser", AccessibleRole::FILE_CHOOSER },
+ { "filler", AccessibleRole::FILLER },
+ { "font chooser", AccessibleRole::FONT_CHOOSER },
+ { "frame", AccessibleRole::FRAME },
+ { "glass pane", AccessibleRole::GLASS_PANE },
+ { "html container", AccessibleRole::UNKNOWN },
+ { "icon", AccessibleRole::ICON },
+ { "image", AccessibleRole::GRAPHIC },
+ { "internal frame", AccessibleRole::INTERNAL_FRAME },
+ { "label", AccessibleRole::LABEL },
+ { "layered pane", AccessibleRole::LAYERED_PANE },
+ { "list", AccessibleRole::LIST },
+ { "list item", AccessibleRole::LIST_ITEM },
+ { "menu", AccessibleRole::MENU },
+ { "menu bar", AccessibleRole::MENU_BAR },
+ { "menu item", AccessibleRole::MENU_ITEM },
+ { "option pane", AccessibleRole::OPTION_PANE },
+ { "page tab", AccessibleRole::PAGE_TAB },
+ { "page tab list", AccessibleRole::PAGE_TAB_LIST },
+ { "panel", AccessibleRole::PANEL }, // or SHAPE or TEXT_FRAME ?
+ { "password text", AccessibleRole::PASSWORD_TEXT },
+ { "popup menu", AccessibleRole::POPUP_MENU },
+ { "progress bar", AccessibleRole::PROGRESS_BAR },
+ { "push button", AccessibleRole::PUSH_BUTTON }, // or BUTTON_DROPDOWN or BUTTON_MENU
+ { "radio button", AccessibleRole::RADIO_BUTTON },
+ { "radio menu item", AccessibleRole::RADIO_MENU_ITEM },
+ { "root pane", AccessibleRole::ROOT_PANE },
+ { "row header", AccessibleRole::ROW_HEADER },
+ { "scroll bar", AccessibleRole::SCROLL_BAR },
+ { "scroll pane", AccessibleRole::SCROLL_PANE },
+ { "separator", AccessibleRole::SEPARATOR },
+ { "slider", AccessibleRole::SLIDER },
+ { "split pane", AccessibleRole::SPLIT_PANE },
+ { "spin button", AccessibleRole::SPIN_BOX }, // ?
+ { "statusbar", AccessibleRole::STATUS_BAR },
+ { "table", AccessibleRole::TABLE },
+ { "table cell", AccessibleRole::TABLE_CELL },
+ { "table column header", AccessibleRole::COLUMN_HEADER }, // approximate
+ { "table row header", AccessibleRole::ROW_HEADER }, // approximate
+ { "tear off menu item", AccessibleRole::UNKNOWN },
+ { "terminal", AccessibleRole::UNKNOWN },
+ { "text", AccessibleRole::TEXT },
+ { "toggle button", AccessibleRole::TOGGLE_BUTTON },
+ { "tool bar", AccessibleRole::TOOL_BAR },
+ { "tool tip", AccessibleRole::TOOL_TIP },
+ { "tree", AccessibleRole::TREE },
+ { "tree table", AccessibleRole::TREE_TABLE },
+ { "unknown", AccessibleRole::UNKNOWN },
+ { "viewport", AccessibleRole::VIEW_PORT },
+ { "window", AccessibleRole::WINDOW },
+ { "header", AccessibleRole::HEADER },
+ { "footer", AccessibleRole::FOOTER },
+ { "paragraph", AccessibleRole::PARAGRAPH },
+ { "ruler", AccessibleRole::RULER },
+ { "application", AccessibleRole::UNKNOWN },
+ { "autocomplete", AccessibleRole::UNKNOWN },
+ { "edit bar", AccessibleRole::EDIT_BAR },
+ { "embedded", AccessibleRole::EMBEDDED_OBJECT },
+ { "entry", AccessibleRole::UNKNOWN },
+ { "chart", AccessibleRole::CHART },
+ { "caption", AccessibleRole::CAPTION },
+ { "document frame", AccessibleRole::DOCUMENT },
+ { "heading", AccessibleRole::HEADING },
+ { "page", AccessibleRole::PAGE },
+ { "section", AccessibleRole::SECTION },
+ { "redundant object", AccessibleRole::UNKNOWN },
+ { "form", AccessibleRole::FORM },
+ { "link", AccessibleRole::HYPER_LINK },
+ { "input method window", AccessibleRole::UNKNOWN },
+ { "table row", AccessibleRole::UNKNOWN },
+ { "tree item", AccessibleRole::TREE_ITEM },
+ { "document spreadsheet", AccessibleRole::DOCUMENT_SPREADSHEET },
+ { "document presentation", AccessibleRole::DOCUMENT_PRESENTATION },
+ { "document text", AccessibleRole::DOCUMENT_TEXT },
+ { "document web", AccessibleRole::DOCUMENT }, // approximate
+ { "document email", AccessibleRole::DOCUMENT }, // approximate
+ { "comment", AccessibleRole::COMMENT }, // or NOTE or END_NOTE or FOOTNOTE or SCROLL_PANE
+ { "list box", AccessibleRole::UNKNOWN },
+ { "grouping", AccessibleRole::GROUP_BOX },
+ { "image map", AccessibleRole::IMAGE_MAP },
+ { "notification", AccessibleRole::NOTIFICATION },
+ { "info bar", AccessibleRole::UNKNOWN },
+ { "level bar", AccessibleRole::UNKNOWN },
+ { "title bar", AccessibleRole::UNKNOWN },
+ { "block quote", AccessibleRole::BLOCK_QUOTE },
+ { "audio", AccessibleRole::UNKNOWN },
+ { "video", AccessibleRole::UNKNOWN },
+ { "definition", AccessibleRole::UNKNOWN },
+ { "article", AccessibleRole::UNKNOWN },
+ { "landmark", AccessibleRole::UNKNOWN },
+ { "log", AccessibleRole::UNKNOWN },
+ { "marquee", AccessibleRole::UNKNOWN },
+ { "math", AccessibleRole::UNKNOWN },
+ { "rating", AccessibleRole::UNKNOWN },
+ { "timer", AccessibleRole::UNKNOWN },
+ { "description list", AccessibleRole::UNKNOWN },
+ { "description term", AccessibleRole::UNKNOWN },
+ { "description value", AccessibleRole::UNKNOWN },
+ { "static", AccessibleRole::STATIC },
+ { "math fraction", AccessibleRole::UNKNOWN },
+ { "math root", AccessibleRole::UNKNOWN },
+ { "subscript", AccessibleRole::UNKNOWN },
+ { "superscript", AccessibleRole::UNKNOWN },
+ { "footnote", AccessibleRole::FOOTNOTE },
+ };
+
+ auto it = aAtkRoleToAccessibleRole.find(roleName);
+ if (it == aAtkRoleToAccessibleRole.end())
+ return AccessibleRole::UNKNOWN;
+ return it->second;
+ }
+}
+
+VclPtr<vcl::Window> VclBuilder::insertObject(vcl::Window *pParent, const OUString &rClass,
+ const OUString &rID, stringmap &rProps, stringmap &rPango, stringmap &rAtk)
+{
+ VclPtr<vcl::Window> pCurrentChild;
+
+ if (m_pParent && !isConsideredGtkPseudo(m_pParent) && !m_sID.isEmpty() && rID == m_sID)
+ {
+ pCurrentChild = m_pParent;
+
+ //toplevels default to resizable and apparently you can't change them
+ //afterwards, so we need to wait until now before we can truly
+ //initialize the dialog.
+ if (pParent && pParent->IsSystemWindow())
+ {
+ SystemWindow *pSysWin = static_cast<SystemWindow*>(pCurrentChild.get());
+ pSysWin->doDeferredInit(extractDeferredBits(rProps));
+ m_bToplevelHasDeferredInit = false;
+ }
+ else if (pParent && pParent->IsDockingWindow())
+ {
+ DockingWindow *pDockWin = static_cast<DockingWindow*>(pCurrentChild.get());
+ pDockWin->doDeferredInit(extractDeferredBits(rProps));
+ m_bToplevelHasDeferredInit = false;
+ }
+
+ if (pCurrentChild->GetHelpId().isEmpty())
+ {
+ pCurrentChild->SetHelpId(m_sHelpRoot + m_sID);
+ SAL_INFO("vcl.builder", "for toplevel dialog " << this << " " <<
+ rID << ", set helpid " << pCurrentChild->GetHelpId());
+ }
+ m_bToplevelParentFound = true;
+ }
+ else
+ {
+ //if we're being inserting under a toplevel dialog whose init is
+ //deferred due to waiting to encounter it in this .ui, and it hasn't
+ //been seen yet, then make unattached widgets parent-less toplevels
+ if (pParent == m_pParent.get() && m_bToplevelHasDeferredInit)
+ pParent = nullptr;
+ pCurrentChild = makeObject(pParent, rClass, rID, rProps);
+ }
+
+ if (pCurrentChild)
+ {
+ pCurrentChild->set_id(rID);
+ if (pCurrentChild == m_pParent.get() && m_bToplevelHasDeferredProperties)
+ m_aDeferredProperties = rProps;
+ else
+ BuilderUtils::set_properties(pCurrentChild, rProps);
+
+ // tdf#119827 handle size before scale so we can trivially
+ // scale on the current font size whether size is present
+ // or not.
+ VclBuilder::stringmap::iterator aSize = rPango.find("size");
+ if (aSize != rPango.end())
+ {
+ pCurrentChild->set_font_attribute(aSize->first, aSize->second);
+ rPango.erase(aSize);
+ }
+ for (auto const& [ rKey, rValue ] : rPango)
+ pCurrentChild->set_font_attribute(rKey, rValue);
+
+ m_pParserState->m_aAtkInfo[pCurrentChild] = rAtk;
+ }
+
+ rProps.clear();
+ rPango.clear();
+ rAtk.clear();
+
+ if (!pCurrentChild)
+ {
+ bool bToolbarParent = (pParent && pParent->GetType() == WindowType::TOOLBOX);
+ pCurrentChild = (m_aChildren.empty() || bToolbarParent) ? pParent : m_aChildren.back().m_pWindow.get();
+ }
+ return pCurrentChild;
+}
+
+void VclBuilder::handleTabChild(vcl::Window *pParent, xmlreader::XmlReader &reader)
+{
+ TabControl *pTabControl = pParent && pParent->GetType() == WindowType::TABCONTROL ?
+ static_cast<TabControl*>(pParent) : nullptr;
+
+ std::vector<OUString> sIDs;
+
+ int nLevel = 1;
+ stringmap aProperties;
+ stringmap aAtkProperties;
+ std::vector<vcl::EnumContext::Context> context;
+
+ while(true)
+ {
+ xmlreader::Span name;
+ int nsId;
+
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ ++nLevel;
+ if (name == "object")
+ {
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "id")
+ {
+ name = reader.getAttributeValue(false);
+ OUString sID(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ sal_Int32 nDelim = sID.indexOf(':');
+ if (nDelim != -1)
+ {
+ aProperties["customproperty"] = sID.copy(nDelim + 1);
+ sID = sID.copy(0, nDelim);
+ }
+ sIDs.push_back(sID);
+ }
+ }
+ }
+ else if (name == "style")
+ {
+ int nPriority = 0;
+ context = handleStyle(reader, nPriority);
+ --nLevel;
+ }
+ else if (name == "property")
+ collectProperty(reader, aProperties);
+ else if (pTabControl && name == "child")
+ {
+ // just to collect the atk properties (if any) for the label
+ handleChild(nullptr, &aAtkProperties, reader);
+ --nLevel;
+ }
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ --nLevel;
+
+ if (!nLevel)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+ }
+
+ if (!pParent)
+ return;
+
+ VerticalTabControl *pVerticalTabControl = pParent->GetType() == WindowType::VERTICALTABCONTROL ?
+ static_cast<VerticalTabControl*>(pParent) : nullptr;
+ assert(pTabControl || pVerticalTabControl);
+ VclBuilder::stringmap::iterator aFind = aProperties.find("label");
+ if (aFind != aProperties.end())
+ {
+ OUString sTooltip(extractTooltipText(aProperties));
+ if (pTabControl)
+ {
+ sal_uInt16 nPageId = pTabControl->GetCurPageId();
+ pTabControl->SetPageText(nPageId, aFind->second);
+ pTabControl->SetPageName(nPageId, sIDs.back());
+ pTabControl->SetHelpText(nPageId, sTooltip);
+ if (!context.empty())
+ {
+ TabPage* pPage = pTabControl->GetTabPage(nPageId);
+ pPage->SetContext(std::move(context));
+ }
+
+ for (auto const& [ rKey, rValue ] : aAtkProperties)
+ {
+ if (rKey == "AtkObject::accessible-name")
+ pTabControl->SetAccessibleName(nPageId, rValue);
+ else if (rKey == "AtkObject::accessible-description")
+ pTabControl->SetAccessibleDescription(nPageId, rValue);
+ else
+ SAL_INFO("vcl.builder", "unhandled atk property: " << rKey);
+ }
+
+ }
+ else
+ {
+ OUString sLabel(BuilderUtils::convertMnemonicMarkup(aFind->second));
+ OUString sIconName(extractIconName(aProperties));
+ pVerticalTabControl->InsertPage(sIDs.front(), sLabel, FixedImage::loadThemeImage(sIconName), sTooltip,
+ pVerticalTabControl->GetPageParent()->GetWindow(GetWindowType::LastChild));
+ }
+ }
+ else
+ {
+ if (pTabControl)
+ pTabControl->RemovePage(pTabControl->GetCurPageId());
+ }
+}
+
+//so that tabbing between controls goes in a visually sensible sequence
+//we sort these into a best-tab-order sequence
+bool VclBuilder::sortIntoBestTabTraversalOrder::operator()(const vcl::Window *pA, const vcl::Window *pB) const
+{
+ //sort child order within parent list by grid position
+ sal_Int32 nTopA = pA->get_grid_top_attach();
+ sal_Int32 nTopB = pB->get_grid_top_attach();
+ if (nTopA < nTopB)
+ return true;
+ if (nTopA > nTopB)
+ return false;
+ sal_Int32 nLeftA = pA->get_grid_left_attach();
+ sal_Int32 nLeftB = pB->get_grid_left_attach();
+ if (nLeftA < nLeftB)
+ return true;
+ if (nLeftA > nLeftB)
+ return false;
+ //sort into two groups of pack start and pack end
+ VclPackType ePackA = pA->get_pack_type();
+ VclPackType ePackB = pB->get_pack_type();
+ if (ePackA < ePackB)
+ return true;
+ if (ePackA > ePackB)
+ return false;
+ bool bVerticalContainer = m_pBuilder->get_window_packing_data(pA->GetParent()).m_bVerticalOrient;
+ bool bPackA = pA->get_secondary();
+ bool bPackB = pB->get_secondary();
+ if (!bVerticalContainer)
+ {
+ //for horizontal boxes group secondaries before primaries
+ if (bPackA > bPackB)
+ return true;
+ if (bPackA < bPackB)
+ return false;
+ }
+ else
+ {
+ //for vertical boxes group secondaries after primaries
+ if (bPackA < bPackB)
+ return true;
+ if (bPackA > bPackB)
+ return false;
+ }
+ //honour relative box positions with pack group, (numerical order is reversed
+ //for VclPackType::End, they are packed from the end back, but here we need
+ //them in visual layout order so that tabbing works as expected)
+ sal_Int32 nPackA = m_pBuilder->get_window_packing_data(pA).m_nPosition;
+ sal_Int32 nPackB = m_pBuilder->get_window_packing_data(pB).m_nPosition;
+ if (nPackA < nPackB)
+ return ePackA == VclPackType::Start;
+ if (nPackA > nPackB)
+ return ePackA != VclPackType::Start;
+ //sort labels of Frames before body
+ if (pA->GetParent() == pB->GetParent())
+ {
+ const VclFrame *pFrameParent = dynamic_cast<const VclFrame*>(pA->GetParent());
+ if (pFrameParent)
+ {
+ const vcl::Window *pLabel = pFrameParent->get_label_widget();
+ int nFramePosA = (pA == pLabel) ? 0 : 1;
+ int nFramePosB = (pB == pLabel) ? 0 : 1;
+ return nFramePosA < nFramePosB;
+ }
+ }
+ return false;
+}
+
+void VclBuilder::handleChild(vcl::Window *pParent, stringmap* pAtkProps, xmlreader::XmlReader &reader)
+{
+ vcl::Window *pCurrentChild = nullptr;
+
+ xmlreader::Span name;
+ int nsId;
+ OString sType, sInternalChild;
+
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "type")
+ {
+ name = reader.getAttributeValue(false);
+ sType = OString(name.begin, name.length);
+ }
+ else if (name == "internal-child")
+ {
+ name = reader.getAttributeValue(false);
+ sInternalChild = OString(name.begin, name.length);
+ }
+ }
+
+ if (sType == "tab")
+ {
+ handleTabChild(pParent, reader);
+ return;
+ }
+
+ int nLevel = 1;
+ while(true)
+ {
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ if (name == "object" || name == "placeholder")
+ {
+ pCurrentChild = handleObject(pParent, pAtkProps, reader).get();
+
+ bool bObjectInserted = pCurrentChild && pParent != pCurrentChild;
+
+ if (bObjectInserted)
+ {
+ //Internal-children default in glade to not having their visible bits set
+ //even though they are visible (generally anyway)
+ if (!sInternalChild.isEmpty())
+ pCurrentChild->Show();
+
+ //Select the first page if it's a notebook
+ if (pCurrentChild->GetType() == WindowType::TABCONTROL)
+ {
+ TabControl *pTabControl = static_cast<TabControl*>(pCurrentChild);
+ pTabControl->SetCurPageId(pTabControl->GetPageId(0));
+
+ //To-Do add reorder capability to the TabControl
+ }
+ else
+ {
+ // We want to sort labels before contents of frames
+ // for keyboard traversal, especially if there
+ // are multiple widgets using the same mnemonic
+ if (sType == "label")
+ {
+ if (VclFrame *pFrameParent = dynamic_cast<VclFrame*>(pParent))
+ pFrameParent->designate_label(pCurrentChild);
+ }
+ if (sInternalChild.startsWith("vbox") || sInternalChild.startsWith("messagedialog-vbox"))
+ {
+ if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pParent))
+ pBoxParent->set_content_area(static_cast<VclBox*>(pCurrentChild)); // FIXME-VCLPTR
+ }
+ else if (sInternalChild.startsWith("action_area") || sInternalChild.startsWith("messagedialog-action_area"))
+ {
+ vcl::Window *pContentArea = pCurrentChild->GetParent();
+ if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pContentArea ? pContentArea->GetParent() : nullptr))
+ {
+ pBoxParent->set_action_area(static_cast<VclButtonBox*>(pCurrentChild)); // FIXME-VCLPTR
+ }
+ }
+
+ bool bIsButtonBox = dynamic_cast<VclButtonBox*>(pCurrentChild) != nullptr;
+
+ //To-Do make reorder a virtual in Window, move this foo
+ //there and see above
+ std::vector<vcl::Window*> aChilds;
+ for (vcl::Window* pChild = pCurrentChild->GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (bIsButtonBox)
+ {
+ if (PushButton* pPushButton = dynamic_cast<PushButton*>(pChild))
+ pPushButton->setAction(true);
+ }
+
+ aChilds.push_back(pChild);
+ }
+
+ //sort child order within parent so that tabbing
+ //between controls goes in a visually sensible sequence
+ std::stable_sort(aChilds.begin(), aChilds.end(), sortIntoBestTabTraversalOrder(this));
+ BuilderUtils::reorderWithinParent(aChilds, bIsButtonBox);
+ }
+ }
+ }
+ else if (name == "packing")
+ {
+ handlePacking(pCurrentChild, pParent, reader);
+ }
+ else if (name == "interface")
+ {
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "domain")
+ {
+ name = reader.getAttributeValue(false);
+ sType = OString(name.begin, name.length);
+ m_pParserState->m_aResLocale = Translate::Create(sType);
+ }
+ }
+ ++nLevel;
+ }
+ else
+ ++nLevel;
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ --nLevel;
+
+ if (!nLevel)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+ }
+}
+
+void VclBuilder::collectPangoAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
+{
+ xmlreader::Span span;
+ int nsId;
+
+ OUString sProperty;
+ OUString sValue;
+
+ while (reader.nextAttribute(&nsId, &span))
+ {
+ if (span == "name")
+ {
+ span = reader.getAttributeValue(false);
+ sProperty = OUString(span.begin, span.length, RTL_TEXTENCODING_UTF8);
+ }
+ else if (span == "value")
+ {
+ span = reader.getAttributeValue(false);
+ sValue = OUString(span.begin, span.length, RTL_TEXTENCODING_UTF8);
+ }
+ }
+
+ if (!sProperty.isEmpty())
+ rMap[sProperty] = sValue;
+}
+
+void VclBuilder::collectAtkRelationAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
+{
+ xmlreader::Span span;
+ int nsId;
+
+ OUString sProperty;
+ OUString sValue;
+
+ while (reader.nextAttribute(&nsId, &span))
+ {
+ if (span == "type")
+ {
+ span = reader.getAttributeValue(false);
+ sProperty = OUString(span.begin, span.length, RTL_TEXTENCODING_UTF8);
+ }
+ else if (span == "target")
+ {
+ span = reader.getAttributeValue(false);
+ sValue = OUString(span.begin, span.length, RTL_TEXTENCODING_UTF8);
+ sal_Int32 nDelim = sValue.indexOf(':');
+ if (nDelim != -1)
+ sValue = sValue.copy(0, nDelim);
+ }
+ }
+
+ if (!sProperty.isEmpty())
+ rMap[sProperty] = sValue;
+}
+
+void VclBuilder::collectAtkRoleAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
+{
+ xmlreader::Span span;
+ int nsId;
+
+ OUString sProperty;
+
+ while (reader.nextAttribute(&nsId, &span))
+ {
+ if (span == "type")
+ {
+ span = reader.getAttributeValue(false);
+ sProperty = OUString(span.begin, span.length, RTL_TEXTENCODING_UTF8);
+ }
+ }
+
+ if (!sProperty.isEmpty())
+ rMap["role"] = sProperty;
+}
+
+void VclBuilder::handleRow(xmlreader::XmlReader &reader, const OUString &rID)
+{
+ int nLevel = 1;
+
+ ListStore::row aRow;
+
+ while(true)
+ {
+ xmlreader::Span name;
+ int nsId;
+
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ ++nLevel;
+ if (name == "col")
+ {
+ bool bTranslated = false;
+ sal_uInt32 nId = 0;
+ OString sContext;
+
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "id")
+ {
+ name = reader.getAttributeValue(false);
+ nId = OString(name.begin, name.length).toUInt32();
+ }
+ else if (nId == 0 && name == "translatable" && reader.getAttributeValue(false) == "yes")
+ {
+ bTranslated = true;
+ }
+ else if (name == "context")
+ {
+ name = reader.getAttributeValue(false);
+ sContext = OString(name.begin, name.length);
+ }
+ }
+
+ (void)reader.nextItem(
+ xmlreader::XmlReader::Text::Raw, &name, &nsId);
+
+ OString sValue(name.begin, name.length);
+ OUString sFinalValue;
+ if (bTranslated)
+ {
+ sFinalValue = Translate::get(TranslateId{sContext.getStr(), sValue.getStr()}, m_pParserState->m_aResLocale);
+ }
+ else
+ sFinalValue = OUString::fromUtf8(sValue);
+
+
+ if (aRow.size() < nId+1)
+ aRow.resize(nId+1);
+ aRow[nId] = sFinalValue;
+ }
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ {
+ --nLevel;
+ }
+
+ if (!nLevel)
+ break;
+ }
+
+ m_pParserState->m_aModels[rID].m_aEntries.push_back(aRow);
+}
+
+void VclBuilder::handleListStore(xmlreader::XmlReader &reader, const OUString &rID, std::u16string_view rClass)
+{
+ int nLevel = 1;
+
+ while(true)
+ {
+ xmlreader::Span name;
+ int nsId;
+
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ if (name == "row")
+ {
+ bool bNotTreeStore = rClass != u"GtkTreeStore";
+ if (bNotTreeStore)
+ handleRow(reader, rID);
+ assert(bNotTreeStore && "gtk, as the time of writing, doesn't support data in GtkTreeStore serialization");
+ }
+ else
+ ++nLevel;
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ {
+ --nLevel;
+ }
+
+ if (!nLevel)
+ break;
+ }
+}
+
+VclBuilder::stringmap VclBuilder::handleAtkObject(xmlreader::XmlReader &reader) const
+{
+ int nLevel = 1;
+
+ stringmap aProperties;
+
+ while (true)
+ {
+ xmlreader::Span name;
+ int nsId;
+
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ ++nLevel;
+ if (name == "property")
+ collectProperty(reader, aProperties);
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ {
+ --nLevel;
+ }
+
+ if (!nLevel)
+ break;
+ }
+
+ return aProperties;
+}
+
+void VclBuilder::applyAtkProperties(vcl::Window *pWindow, const stringmap& rProperties)
+{
+ assert(pWindow);
+ for (auto const& [ rKey, rValue ] : rProperties)
+ {
+ if (pWindow && rKey.match("AtkObject::"))
+ pWindow->set_property(rKey.copy(RTL_CONSTASCII_LENGTH("AtkObject::")), rValue);
+ else
+ SAL_WARN("vcl.builder", "unhandled atk prop: " << rKey);
+ }
+}
+
+std::vector<ComboBoxTextItem> VclBuilder::handleItems(xmlreader::XmlReader &reader) const
+{
+ int nLevel = 1;
+
+ std::vector<ComboBoxTextItem> aItems;
+
+ while(true)
+ {
+ xmlreader::Span name;
+ int nsId;
+
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ ++nLevel;
+ if (name == "item")
+ {
+ bool bTranslated = false;
+ OString sContext;
+ OUString sId;
+
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "translatable" && reader.getAttributeValue(false) == "yes")
+ {
+ bTranslated = true;
+ }
+ else if (name == "context")
+ {
+ name = reader.getAttributeValue(false);
+ sContext = OString(name.begin, name.length);
+ }
+ else if (name == "id")
+ {
+ name = reader.getAttributeValue(false);
+ sId = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ }
+ }
+
+ (void)reader.nextItem(
+ xmlreader::XmlReader::Text::Raw, &name, &nsId);
+
+ OString sValue(name.begin, name.length);
+ OUString sFinalValue;
+ if (bTranslated)
+ {
+ sFinalValue = Translate::get(TranslateId{sContext.getStr(), sValue.getStr()}, m_pParserState->m_aResLocale);
+ }
+ else
+ sFinalValue = OUString::fromUtf8(sValue);
+
+ if (m_pStringReplace)
+ sFinalValue = (*m_pStringReplace)(sFinalValue);
+
+ aItems.emplace_back(sFinalValue, sId);
+ }
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ {
+ --nLevel;
+ }
+
+ if (!nLevel)
+ break;
+ }
+
+ return aItems;
+}
+
+VclPtr<Menu> VclBuilder::handleMenu(xmlreader::XmlReader &reader, const OUString &rID, bool bMenuBar)
+{
+ VclPtr<Menu> pCurrentMenu;
+ if (bMenuBar)
+ pCurrentMenu = VclPtr<MenuBar>::Create();
+ else
+ pCurrentMenu = VclPtr<PopupMenu>::Create();
+
+ pCurrentMenu->set_id(rID);
+
+ int nLevel = 1;
+
+ stringmap aProperties;
+
+ while(true)
+ {
+ xmlreader::Span name;
+ int nsId;
+
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ if (name == "child")
+ {
+ handleMenuChild(pCurrentMenu, reader);
+ }
+ else
+ {
+ ++nLevel;
+ if (name == "property")
+ collectProperty(reader, aProperties);
+ }
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ {
+ --nLevel;
+ }
+
+ if (!nLevel)
+ break;
+ }
+
+ m_aMenus.emplace_back(rID, pCurrentMenu);
+
+ return pCurrentMenu;
+}
+
+void VclBuilder::handleMenuChild(Menu *pParent, xmlreader::XmlReader &reader)
+{
+ xmlreader::Span name;
+ int nsId;
+
+ int nLevel = 1;
+ while(true)
+ {
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ if (name == "object" || name == "placeholder")
+ {
+ handleMenuObject(pParent, reader);
+ }
+ else
+ ++nLevel;
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ --nLevel;
+
+ if (!nLevel)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+ }
+}
+
+void VclBuilder::handleMenuObject(Menu *pParent, xmlreader::XmlReader &reader)
+{
+ OUString sClass;
+ OUString sID;
+ OUString sCustomProperty;
+ PopupMenu *pSubMenu = nullptr;
+
+ xmlreader::Span name;
+ int nsId;
+
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "class")
+ {
+ name = reader.getAttributeValue(false);
+ sClass = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ }
+ else if (name == "id")
+ {
+ name = reader.getAttributeValue(false);
+ sID = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ if (m_bLegacy)
+ {
+ sal_Int32 nDelim = sID.indexOf(':');
+ if (nDelim != -1)
+ {
+ sCustomProperty = sID.subView(nDelim+1);
+ sID = sID.copy(0, nDelim);
+ }
+ }
+ }
+ }
+
+ int nLevel = 1;
+
+ stringmap aProperties;
+ stringmap aAtkProperties;
+ accelmap aAccelerators;
+
+ if (!sCustomProperty.isEmpty())
+ aProperties["customproperty"] = sCustomProperty;
+
+ while(true)
+ {
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ if (name == "child")
+ {
+ size_t nChildMenuIdx = m_aMenus.size();
+ handleChild(nullptr, &aAtkProperties, reader);
+ bool bSubMenuInserted = m_aMenus.size() > nChildMenuIdx;
+ if (bSubMenuInserted)
+ pSubMenu = dynamic_cast<PopupMenu*>(m_aMenus[nChildMenuIdx].m_pMenu.get());
+ }
+ else
+ {
+ ++nLevel;
+ if (name == "property")
+ collectProperty(reader, aProperties);
+ else if (name == "accelerator")
+ collectAccelerator(reader, aAccelerators);
+ }
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ {
+ --nLevel;
+ }
+
+ if (!nLevel)
+ break;
+ }
+
+ insertMenuObject(pParent, pSubMenu, sClass, sID, aProperties, aAtkProperties, aAccelerators);
+}
+
+void VclBuilder::handleSizeGroup(xmlreader::XmlReader &reader)
+{
+ m_pParserState->m_aSizeGroups.emplace_back();
+ SizeGroup &rSizeGroup = m_pParserState->m_aSizeGroups.back();
+
+ int nLevel = 1;
+
+ while(true)
+ {
+ xmlreader::Span name;
+ int nsId;
+
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ ++nLevel;
+ if (name == "widget")
+ {
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "name")
+ {
+ name = reader.getAttributeValue(false);
+ OUString sWidget(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ sal_Int32 nDelim = sWidget.indexOf(':');
+ if (nDelim != -1)
+ sWidget = sWidget.copy(0, nDelim);
+ rSizeGroup.m_aWidgets.push_back(sWidget);
+ }
+ }
+ }
+ else
+ {
+ if (name == "property")
+ collectProperty(reader, rSizeGroup.m_aProperties);
+ }
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ {
+ --nLevel;
+ }
+
+ if (!nLevel)
+ break;
+ }
+}
+
+namespace
+{
+ vcl::KeyCode makeKeyCode(const std::pair<OUString,OUString> &rKey)
+ {
+ bool bShift = rKey.second.indexOf("GDK_SHIFT_MASK") != -1;
+ bool bMod1 = rKey.second.indexOf("GDK_CONTROL_MASK") != -1;
+ bool bMod2 = rKey.second.indexOf("GDK_ALT_MASK") != -1;
+ bool bMod3 = rKey.second.indexOf("GDK_MOD2_MASK") != -1;
+
+ if (rKey.first == "Insert")
+ return vcl::KeyCode(KEY_INSERT, bShift, bMod1, bMod2, bMod3);
+ else if (rKey.first == "Delete")
+ return vcl::KeyCode(KEY_DELETE, bShift, bMod1, bMod2, bMod3);
+ else if (rKey.first == "Return")
+ return vcl::KeyCode(KEY_RETURN, bShift, bMod1, bMod2, bMod3);
+ else if (rKey.first == "Up")
+ return vcl::KeyCode(KEY_UP, bShift, bMod1, bMod2, bMod3);
+ else if (rKey.first == "Down")
+ return vcl::KeyCode(KEY_DOWN, bShift, bMod1, bMod2, bMod3);
+ else if (rKey.first == "Left")
+ return vcl::KeyCode(KEY_LEFT, bShift, bMod1, bMod2, bMod3);
+ else if (rKey.first == "Right")
+ return vcl::KeyCode(KEY_RIGHT, bShift, bMod1, bMod2, bMod3);
+ else if (rKey.first == "asterisk")
+ return vcl::KeyCode(KEY_MULTIPLY, bShift, bMod1, bMod2, bMod3);
+ else if (rKey.first.getLength() > 1 && rKey.first[0] == 'F')
+ {
+ sal_uInt32 nIndex = o3tl::toUInt32(rKey.first.subView(1));
+ assert(nIndex >= 1 && nIndex <= 26);
+ return vcl::KeyCode(KEY_F1 + nIndex - 1, bShift, bMod1, bMod2, bMod3);
+ }
+
+ assert (rKey.first.getLength() == 1);
+ sal_Unicode cChar = rKey.first.toChar();
+
+ if (cChar >= 'a' && cChar <= 'z')
+ return vcl::KeyCode(KEY_A + (cChar - 'a'), bShift, bMod1, bMod2, bMod3);
+ else if (cChar >= 'A' && cChar <= 'Z')
+ return vcl::KeyCode(KEY_A + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
+ else if (cChar >= '0' && cChar <= '9')
+ return vcl::KeyCode(KEY_0 + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
+
+ return vcl::KeyCode(cChar, bShift, bMod1, bMod2, bMod3);
+ }
+}
+
+void VclBuilder::insertMenuObject(Menu *pParent, PopupMenu *pSubMenu, const OUString &rClass, const OUString &rID,
+ stringmap &rProps, stringmap &rAtkProps, accelmap &rAccels)
+{
+ sal_uInt16 nOldCount = pParent->GetItemCount();
+ sal_uInt16 nNewId = ++m_pParserState->m_nLastMenuItemId;
+
+ if(rClass == "NotebookBarAddonsMenuMergePoint")
+ {
+ NotebookBarAddonsMerger::MergeNotebookBarMenuAddons(pParent, nNewId, rID, *m_pNotebookBarAddonsItem);
+ m_pParserState->m_nLastMenuItemId = pParent->GetItemCount();
+ }
+ else if (rClass == "GtkMenuItem")
+ {
+ OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
+ OUString aCommand(extractActionName(rProps));
+ pParent->InsertItem(nNewId, sLabel, MenuItemBits::NONE , rID);
+ pParent->SetItemCommand(nNewId, aCommand);
+ if (pSubMenu)
+ pParent->SetPopupMenu(nNewId, pSubMenu);
+ }
+ else if (rClass == "GtkCheckMenuItem")
+ {
+ OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
+ OUString aCommand(extractActionName(rProps));
+ pParent->InsertItem(nNewId, sLabel, MenuItemBits::CHECKABLE, rID);
+ pParent->SetItemCommand(nNewId, aCommand);
+ }
+ else if (rClass == "GtkRadioMenuItem")
+ {
+ OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
+ OUString aCommand(extractActionName(rProps));
+ pParent->InsertItem(nNewId, sLabel, MenuItemBits::AUTOCHECK | MenuItemBits::RADIOCHECK, rID);
+ pParent->SetItemCommand(nNewId, aCommand);
+ }
+ else if (rClass == "GtkSeparatorMenuItem")
+ {
+ pParent->InsertSeparator(rID);
+ }
+
+ SAL_WARN_IF(nOldCount == pParent->GetItemCount(), "vcl.builder", "probably need to implement " << rClass);
+
+ if (nOldCount != pParent->GetItemCount())
+ {
+ pParent->SetHelpId(nNewId, m_sHelpRoot + rID);
+ if (!extractVisible(rProps))
+ pParent->HideItem(nNewId);
+
+ for (auto const& [ rKey, rValue ] : rProps)
+ {
+ if (rKey == "tooltip-markup")
+ pParent->SetTipHelpText(nNewId, rValue);
+ else if (rKey == "tooltip-text")
+ pParent->SetTipHelpText(nNewId, rValue);
+ else
+ SAL_INFO("vcl.builder", "unhandled property: " << rKey);
+ }
+
+ for (auto const& [ rKey, rValue ] : rAtkProps)
+ {
+ if (rKey == "AtkObject::accessible-name")
+ pParent->SetAccessibleName(nNewId, rValue);
+ else if (rKey == "AtkObject::accessible-description")
+ pParent->SetAccessibleDescription(nNewId, rValue);
+ else
+ SAL_INFO("vcl.builder", "unhandled atk property: " << rKey);
+ }
+
+ for (auto const& [ rSignal, rValue ] : rAccels)
+ {
+ if (rSignal == "activate")
+ pParent->SetAccelKey(nNewId, makeKeyCode(rValue));
+ else
+ SAL_INFO("vcl.builder", "unhandled accelerator for: " << rSignal);
+ }
+ }
+
+ rProps.clear();
+}
+
+/// Insert items to a ComboBox or a ListBox.
+/// They have no common ancestor that would have 'InsertEntry()', so use a template.
+template<typename T> static bool insertItems(vcl::Window *pWindow, VclBuilder::stringmap &rMap,
+ std::vector<std::unique_ptr<OUString>>& rUserData,
+ const std::vector<ComboBoxTextItem> &rItems)
+{
+ T *pContainer = dynamic_cast<T*>(pWindow);
+ if (!pContainer)
+ return false;
+
+ sal_uInt16 nActiveId = extractActive(rMap);
+ for (auto const& item : rItems)
+ {
+ sal_Int32 nPos = pContainer->InsertEntry(item.m_sItem);
+ if (!item.m_sId.isEmpty())
+ {
+ rUserData.emplace_back(std::make_unique<OUString>(item.m_sId));
+ pContainer->SetEntryData(nPos, rUserData.back().get());
+ }
+ }
+ if (nActiveId < rItems.size())
+ pContainer->SelectEntryPos(nActiveId);
+
+ return true;
+}
+
+VclPtr<vcl::Window> VclBuilder::handleObject(vcl::Window *pParent, stringmap *pAtkProps, xmlreader::XmlReader &reader)
+{
+ OUString sClass;
+ OUString sID;
+ OUString sCustomProperty;
+
+ xmlreader::Span name;
+ int nsId;
+
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "class")
+ {
+ name = reader.getAttributeValue(false);
+ sClass = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ }
+ else if (name == "id")
+ {
+ name = reader.getAttributeValue(false);
+ sID = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ if (m_bLegacy)
+ {
+ sal_Int32 nDelim = sID.indexOf(':');
+ if (nDelim != -1)
+ {
+ sCustomProperty = sID.subView(nDelim+1);
+ sID = sID.copy(0, nDelim);
+ }
+ }
+ }
+ }
+
+ if (sClass == "GtkListStore" || sClass == "GtkTreeStore")
+ {
+ handleListStore(reader, sID, sClass);
+ return nullptr;
+ }
+ else if (sClass == "GtkMenu")
+ {
+ handleMenu(reader, sID, false);
+ return nullptr;
+ }
+ else if (sClass == "GtkMenuBar")
+ {
+ VclPtr<Menu> xMenu = handleMenu(reader, sID, true);
+ if (SystemWindow* pTopLevel = pParent ? pParent->GetSystemWindow() : nullptr)
+ pTopLevel->SetMenuBar(dynamic_cast<MenuBar*>(xMenu.get()));
+ return nullptr;
+ }
+ else if (sClass == "GtkSizeGroup")
+ {
+ handleSizeGroup(reader);
+ return nullptr;
+ }
+ else if (sClass == "AtkObject")
+ {
+ assert((pParent || pAtkProps) && "must have one set");
+ assert(!(pParent && pAtkProps) && "must not have both");
+ auto aAtkProperties = handleAtkObject(reader);
+ if (pParent)
+ applyAtkProperties(pParent, aAtkProperties);
+ if (pAtkProps)
+ *pAtkProps = aAtkProperties;
+ return nullptr;
+ }
+
+ int nLevel = 1;
+
+ stringmap aProperties, aPangoAttributes;
+ stringmap aAtkAttributes;
+ std::vector<ComboBoxTextItem> aItems;
+
+ if (!sCustomProperty.isEmpty())
+ aProperties["customproperty"] = sCustomProperty;
+
+ VclPtr<vcl::Window> pCurrentChild;
+ while(true)
+ {
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ if (name == "child")
+ {
+ if (!pCurrentChild)
+ {
+ pCurrentChild = insertObject(pParent, sClass, sID,
+ aProperties, aPangoAttributes, aAtkAttributes);
+ }
+ handleChild(pCurrentChild, nullptr, reader);
+ }
+ else if (name == "items")
+ aItems = handleItems(reader);
+ else if (name == "style")
+ {
+ int nPriority = 0;
+ std::vector<vcl::EnumContext::Context> aContext = handleStyle(reader, nPriority);
+ if (nPriority != 0)
+ {
+ vcl::IPrioritable* pPrioritable = dynamic_cast<vcl::IPrioritable*>(pCurrentChild.get());
+ SAL_WARN_IF(!pPrioritable, "vcl", "priority set for not supported item");
+ if (pPrioritable)
+ pPrioritable->SetPriority(nPriority);
+ }
+ if (!aContext.empty())
+ {
+ vcl::IContext* pContextControl = dynamic_cast<vcl::IContext*>(pCurrentChild.get());
+ SAL_WARN_IF(!pContextControl, "vcl", "context set for not supported item");
+ if (pContextControl)
+ pContextControl->SetContext(std::move(aContext));
+ }
+ }
+ else
+ {
+ ++nLevel;
+ if (name == "property")
+ collectProperty(reader, aProperties);
+ else if (name == "attribute")
+ collectPangoAttribute(reader, aPangoAttributes);
+ else if (name == "relation")
+ collectAtkRelationAttribute(reader, aAtkAttributes);
+ else if (name == "role")
+ collectAtkRoleAttribute(reader, aAtkAttributes);
+ else if (name == "action-widget")
+ handleActionWidget(reader);
+ }
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ {
+ --nLevel;
+ }
+
+ if (!nLevel)
+ break;
+ }
+
+ if (sClass == "GtkAdjustment")
+ {
+ m_pParserState->m_aAdjustments[sID] = aProperties;
+ return nullptr;
+ }
+ else if (sClass == "GtkTextBuffer")
+ {
+ m_pParserState->m_aTextBuffers[sID] = aProperties;
+ return nullptr;
+ }
+
+ if (!pCurrentChild)
+ {
+ pCurrentChild = insertObject(pParent, sClass, sID, aProperties,
+ aPangoAttributes, aAtkAttributes);
+ }
+
+ if (!aItems.empty())
+ {
+ // try to fill-in the items
+ if (!insertItems<ComboBox>(pCurrentChild, aProperties, m_aUserData, aItems))
+ insertItems<ListBox>(pCurrentChild, aProperties, m_aUserData, aItems);
+ }
+
+ return pCurrentChild;
+}
+
+void VclBuilder::handlePacking(vcl::Window *pCurrent, vcl::Window *pParent, xmlreader::XmlReader &reader)
+{
+ xmlreader::Span name;
+ int nsId;
+
+ int nLevel = 1;
+
+ while(true)
+ {
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ ++nLevel;
+ if (name == "property")
+ applyPackingProperty(pCurrent, pParent, reader);
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ {
+ --nLevel;
+ }
+
+ if (!nLevel)
+ break;
+ }
+}
+
+void VclBuilder::applyPackingProperty(vcl::Window *pCurrent,
+ vcl::Window *pParent,
+ xmlreader::XmlReader &reader)
+{
+ if (!pCurrent)
+ return;
+
+ //ToolBoxItems are not true widgets just elements
+ //of the ToolBox itself
+ ToolBox *pToolBoxParent = nullptr;
+ if (pCurrent == pParent)
+ pToolBoxParent = dynamic_cast<ToolBox*>(pParent);
+
+ xmlreader::Span name;
+ int nsId;
+
+ if (pCurrent->GetType() == WindowType::SCROLLWINDOW)
+ {
+ auto aFind = m_pParserState->m_aRedundantParentWidgets.find(VclPtr<vcl::Window>(pCurrent));
+ if (aFind != m_pParserState->m_aRedundantParentWidgets.end())
+ {
+ pCurrent = aFind->second;
+ assert(pCurrent);
+ }
+ }
+
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "name")
+ {
+ name = reader.getAttributeValue(false);
+ OString sKey(name.begin, name.length);
+ sKey = sKey.replace('_', '-');
+ (void)reader.nextItem(
+ xmlreader::XmlReader::Text::Raw, &name, &nsId);
+ OString sValue(name.begin, name.length);
+
+ if (sKey == "expand" || sKey == "resize")
+ {
+ bool bTrue = (!sValue.isEmpty() && (sValue[0] == 't' || sValue[0] == 'T' || sValue[0] == '1'));
+ if (pToolBoxParent)
+ pToolBoxParent->SetItemExpand(m_pParserState->m_nLastToolbarId, bTrue);
+ else
+ pCurrent->set_expand(bTrue);
+ continue;
+ }
+
+ if (pToolBoxParent)
+ continue;
+
+ if (sKey == "fill")
+ {
+ bool bTrue = (!sValue.isEmpty() && (sValue[0] == 't' || sValue[0] == 'T' || sValue[0] == '1'));
+ pCurrent->set_fill(bTrue);
+ }
+ else if (sKey == "pack-type")
+ {
+ VclPackType ePackType = (!sValue.isEmpty() && (sValue[0] == 'e' || sValue[0] == 'E')) ? VclPackType::End : VclPackType::Start;
+ pCurrent->set_pack_type(ePackType);
+ }
+ else if (sKey == "left-attach")
+ {
+ pCurrent->set_grid_left_attach(sValue.toInt32());
+ }
+ else if (sKey == "top-attach")
+ {
+ pCurrent->set_grid_top_attach(sValue.toInt32());
+ }
+ else if (sKey == "width")
+ {
+ pCurrent->set_grid_width(sValue.toInt32());
+ }
+ else if (sKey == "height")
+ {
+ pCurrent->set_grid_height(sValue.toInt32());
+ }
+ else if (sKey == "padding")
+ {
+ pCurrent->set_padding(sValue.toInt32());
+ }
+ else if (sKey == "position")
+ {
+ set_window_packing_position(pCurrent, sValue.toInt32());
+ }
+ else if (sKey == "secondary")
+ {
+ pCurrent->set_secondary(toBool(sValue));
+ }
+ else if (sKey == "non-homogeneous")
+ {
+ pCurrent->set_non_homogeneous(toBool(sValue));
+ }
+ else if (sKey == "homogeneous")
+ {
+ pCurrent->set_non_homogeneous(!toBool(sValue));
+ }
+ else
+ {
+ SAL_WARN_IF(sKey != "shrink", "vcl.builder", "unknown packing: " << sKey);
+ }
+ }
+ }
+}
+
+std::vector<vcl::EnumContext::Context> VclBuilder::handleStyle(xmlreader::XmlReader &reader, int &nPriority)
+{
+ std::vector<vcl::EnumContext::Context> aContext;
+
+ xmlreader::Span name;
+ int nsId;
+
+ int nLevel = 1;
+
+ while(true)
+ {
+ xmlreader::XmlReader::Result res = reader.nextItem(
+ xmlreader::XmlReader::Text::NONE, &name, &nsId);
+
+ if (res == xmlreader::XmlReader::Result::Done)
+ break;
+
+ if (res == xmlreader::XmlReader::Result::Begin)
+ {
+ ++nLevel;
+ if (name == "class")
+ {
+ OUString classStyle = getStyleClass(reader);
+ OUString rest;
+
+ if (classStyle.startsWith("context-", &rest))
+ {
+ aContext.push_back(vcl::EnumContext::GetContextEnum(rest));
+ }
+ else if (classStyle.startsWith("priority-", &rest))
+ {
+ nPriority = rest.toInt32();
+ }
+ else if (classStyle != "small-button" && classStyle != "destructive-action" && classStyle != "suggested-action")
+ {
+ SAL_WARN("vcl.builder", "unknown class: " << classStyle);
+ }
+ }
+ }
+
+ if (res == xmlreader::XmlReader::Result::End)
+ {
+ --nLevel;
+ }
+
+ if (!nLevel)
+ break;
+ }
+
+ return aContext;
+}
+
+OUString VclBuilder::getStyleClass(xmlreader::XmlReader &reader)
+{
+ xmlreader::Span name;
+ int nsId;
+ OUString aRet;
+
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "name")
+ {
+ name = reader.getAttributeValue(false);
+ aRet = OUString (name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ }
+ }
+
+ return aRet;
+}
+
+void VclBuilder::collectProperty(xmlreader::XmlReader &reader, stringmap &rMap) const
+{
+ xmlreader::Span name;
+ int nsId;
+
+ OUString sProperty;
+ OString sContext;
+
+ bool bTranslated = false;
+
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "name")
+ {
+ name = reader.getAttributeValue(false);
+ sProperty = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ }
+ else if (name == "context")
+ {
+ name = reader.getAttributeValue(false);
+ sContext = OString(name.begin, name.length);
+ }
+ else if (name == "translatable" && reader.getAttributeValue(false) == "yes")
+ {
+ bTranslated = true;
+ }
+ }
+
+ (void)reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
+ OString sValue(name.begin, name.length);
+ OUString sFinalValue;
+ if (bTranslated)
+ {
+ sFinalValue = Translate::get(TranslateId{sContext.getStr(), sValue.getStr()}, m_pParserState->m_aResLocale);
+ }
+ else
+ sFinalValue = OUString::fromUtf8(sValue);
+
+ if (!sProperty.isEmpty())
+ {
+ sProperty = sProperty.replace('_', '-');
+ if (m_pStringReplace)
+ sFinalValue = (*m_pStringReplace)(sFinalValue);
+ rMap[sProperty] = sFinalValue;
+ }
+}
+
+void VclBuilder::handleActionWidget(xmlreader::XmlReader &reader)
+{
+ xmlreader::Span name;
+ int nsId;
+
+ OString sResponse;
+
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "response")
+ {
+ name = reader.getAttributeValue(false);
+ sResponse = OString(name.begin, name.length);
+ }
+ }
+
+ (void)reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
+ OUString sID(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ sal_Int32 nDelim = sID.indexOf(':');
+ if (nDelim != -1)
+ sID = sID.copy(0, nDelim);
+ set_response(sID, sResponse.toInt32());
+}
+
+void VclBuilder::collectAccelerator(xmlreader::XmlReader &reader, accelmap &rMap)
+{
+ xmlreader::Span name;
+ int nsId;
+
+ OUString sProperty;
+ OUString sValue;
+ OUString sModifiers;
+
+ while (reader.nextAttribute(&nsId, &name))
+ {
+ if (name == "key")
+ {
+ name = reader.getAttributeValue(false);
+ sValue = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ }
+ else if (name == "signal")
+ {
+ name = reader.getAttributeValue(false);
+ sProperty = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ }
+ else if (name == "modifiers")
+ {
+ name = reader.getAttributeValue(false);
+ sModifiers = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
+ }
+ }
+
+ if (!sProperty.isEmpty() && !sValue.isEmpty())
+ {
+ rMap[sProperty] = std::make_pair(sValue, sModifiers);
+ }
+}
+
+vcl::Window *VclBuilder::get_widget_root()
+{
+ return m_aChildren.empty() ? nullptr : m_aChildren[0].m_pWindow.get();
+}
+
+vcl::Window *VclBuilder::get_by_name(std::u16string_view sID)
+{
+ for (auto const& child : m_aChildren)
+ {
+ if (child.m_sID == sID)
+ return child.m_pWindow;
+ }
+
+ return nullptr;
+}
+
+PopupMenu *VclBuilder::get_menu(std::u16string_view sID)
+{
+ for (auto const& menu : m_aMenus)
+ {
+ if (menu.m_sID == sID)
+ return dynamic_cast<PopupMenu*>(menu.m_pMenu.get());
+ }
+
+ return nullptr;
+}
+
+void VclBuilder::set_response(std::u16string_view sID, short nResponse)
+{
+ switch (nResponse)
+ {
+ case -5:
+ nResponse = RET_OK;
+ break;
+ case -6:
+ nResponse = RET_CANCEL;
+ break;
+ case -7:
+ nResponse = RET_CLOSE;
+ break;
+ case -8:
+ nResponse = RET_YES;
+ break;
+ case -9:
+ nResponse = RET_NO;
+ break;
+ case -11:
+ nResponse = RET_HELP;
+ break;
+ default:
+ assert(nResponse >= 100 && "keep non-canned responses in range 100+ to avoid collision with vcl RET_*");
+ break;
+ }
+
+ for (const auto & child : m_aChildren)
+ {
+ if (child.m_sID == sID)
+ {
+ PushButton* pPushButton = dynamic_cast<PushButton*>(child.m_pWindow.get());
+ assert(pPushButton);
+ Dialog* pDialog = pPushButton->GetParentDialog();
+ assert(pDialog);
+ pDialog->add_button(pPushButton, nResponse, false);
+ return;
+ }
+ }
+
+ assert(false);
+}
+
+void VclBuilder::delete_by_name(const OUString& sID)
+{
+ auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
+ [&sID](WinAndId& rItem) { return rItem.m_sID == sID; });
+ if (aI != m_aChildren.end())
+ {
+ aI->m_pWindow.disposeAndClear();
+ m_aChildren.erase(aI);
+ }
+}
+
+void VclBuilder::delete_by_window(vcl::Window *pWindow)
+{
+ drop_ownership(pWindow);
+ pWindow->disposeOnce();
+}
+
+void VclBuilder::drop_ownership(const vcl::Window *pWindow)
+{
+ auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
+ [&pWindow](WinAndId& rItem) { return rItem.m_pWindow == pWindow; });
+ if (aI != m_aChildren.end())
+ m_aChildren.erase(aI);
+}
+
+OUString VclBuilder::get_by_window(const vcl::Window *pWindow) const
+{
+ for (auto const& child : m_aChildren)
+ {
+ if (child.m_pWindow == pWindow)
+ return child.m_sID;
+ }
+
+ return {};
+}
+
+VclBuilder::PackingData VclBuilder::get_window_packing_data(const vcl::Window *pWindow) const
+{
+ //We've stored the return of new Control, some of these get
+ //border windows placed around them which are what you get
+ //from GetChild, so scoot up a level if necessary to get the
+ //window whose position value we have
+ const vcl::Window *pPropHolder = pWindow->ImplGetWindow();
+
+ for (auto const& child : m_aChildren)
+ {
+ if (child.m_pWindow == pPropHolder)
+ return child.m_aPackingData;
+ }
+
+ return PackingData();
+}
+
+void VclBuilder::set_window_packing_position(const vcl::Window *pWindow, sal_Int32 nPosition)
+{
+ for (auto & child : m_aChildren)
+ {
+ if (child.m_pWindow == pWindow)
+ child.m_aPackingData.m_nPosition = nPosition;
+ }
+}
+
+const VclBuilder::ListStore *VclBuilder::get_model_by_name(const OUString& sID) const
+{
+ const auto aI = m_pParserState->m_aModels.find(sID);
+ if (aI != m_pParserState->m_aModels.end())
+ return &(aI->second);
+ return nullptr;
+}
+
+const VclBuilder::TextBuffer *VclBuilder::get_buffer_by_name(const OUString& sID) const
+{
+ const auto aI = m_pParserState->m_aTextBuffers.find(sID);
+ if (aI != m_pParserState->m_aTextBuffers.end())
+ return &(aI->second);
+ return nullptr;
+}
+
+const VclBuilder::Adjustment *VclBuilder::get_adjustment_by_name(const OUString& sID) const
+{
+ const auto aI = m_pParserState->m_aAdjustments.find(sID);
+ if (aI != m_pParserState->m_aAdjustments.end())
+ return &(aI->second);
+ return nullptr;
+}
+
+void VclBuilder::mungeModel(ComboBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
+{
+ for (auto const& entry : rStore.m_aEntries)
+ {
+ const ListStore::row &rRow = entry;
+ sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
+ if (rRow.size() > 1)
+ {
+ if (m_bLegacy)
+ {
+ sal_Int32 nValue = rRow[1].toInt32();
+ rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
+ }
+ else
+ {
+ if (!rRow[1].isEmpty())
+ {
+ m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
+ rTarget.SetEntryData(nEntry, m_aUserData.back().get());
+ }
+ }
+ }
+ }
+ if (nActiveId < rStore.m_aEntries.size())
+ rTarget.SelectEntryPos(nActiveId);
+}
+
+void VclBuilder::mungeModel(ListBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
+{
+ for (auto const& entry : rStore.m_aEntries)
+ {
+ const ListStore::row &rRow = entry;
+ sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
+ if (rRow.size() > 1)
+ {
+ if (m_bLegacy)
+ {
+ sal_Int32 nValue = rRow[1].toInt32();
+ rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
+ }
+ else
+ {
+ if (!rRow[1].isEmpty())
+ {
+ m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
+ rTarget.SetEntryData(nEntry, m_aUserData.back().get());
+ }
+ }
+ }
+ }
+ if (nActiveId < rStore.m_aEntries.size())
+ rTarget.SelectEntryPos(nActiveId);
+}
+
+void VclBuilder::mungeModel(SvTabListBox& rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
+{
+ for (auto const& entry : rStore.m_aEntries)
+ {
+ const ListStore::row &rRow = entry;
+ auto pEntry = rTarget.InsertEntry(rRow[0]);
+ if (rRow.size() > 1)
+ {
+ if (m_bLegacy)
+ {
+ sal_Int32 nValue = rRow[1].toInt32();
+ pEntry->SetUserData(reinterpret_cast<void*>(nValue));
+ }
+ else
+ {
+ if (!rRow[1].isEmpty())
+ {
+ m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
+ pEntry->SetUserData(m_aUserData.back().get());
+ }
+ }
+ }
+ }
+ if (nActiveId < rStore.m_aEntries.size())
+ {
+ SvTreeListEntry* pEntry = rTarget.GetEntry(nullptr, nActiveId);
+ rTarget.Select(pEntry);
+ }
+}
+
+void VclBuilder::mungeAdjustment(NumericFormatter &rTarget, const Adjustment &rAdjustment)
+{
+ int nMul = rtl_math_pow10Exp(1, rTarget.GetDecimalDigits());
+
+ for (auto const& [ rKey, rValue ] : rAdjustment)
+ {
+ if (rKey == "upper")
+ {
+ sal_Int64 nUpper = rValue.toDouble() * nMul;
+ rTarget.SetMax(nUpper);
+ rTarget.SetLast(nUpper);
+ }
+ else if (rKey == "lower")
+ {
+ sal_Int64 nLower = rValue.toDouble() * nMul;
+ rTarget.SetMin(nLower);
+ rTarget.SetFirst(nLower);
+ }
+ else if (rKey == "value")
+ {
+ sal_Int64 nValue = rValue.toDouble() * nMul;
+ rTarget.SetValue(nValue);
+ }
+ else if (rKey == "step-increment")
+ {
+ sal_Int64 nSpinSize = rValue.toDouble() * nMul;
+ rTarget.SetSpinSize(nSpinSize);
+ }
+ else
+ {
+ SAL_INFO("vcl.builder", "unhandled property :" << rKey);
+ }
+ }
+}
+
+void VclBuilder::mungeAdjustment(FormattedField &rTarget, const Adjustment &rAdjustment)
+{
+ double nMaxValue = 0, nMinValue = 0, nValue = 0, nSpinSize = 0;
+
+ for (auto const& [ rKey, rValue ] : rAdjustment)
+ {
+ if (rKey == "upper")
+ nMaxValue = rValue.toDouble();
+ else if (rKey == "lower")
+ nMinValue = rValue.toDouble();
+ else if (rKey == "value")
+ nValue = rValue.toDouble();
+ else if (rKey == "step-increment")
+ nSpinSize = rValue.toDouble();
+ else
+ SAL_INFO("vcl.builder", "unhandled property :" << rKey);
+ }
+
+ Formatter& rFormatter = rTarget.GetFormatter();
+ rFormatter.SetMinValue(nMinValue);
+ rFormatter.SetMaxValue(nMaxValue);
+ rFormatter.SetValue(nValue);
+ rFormatter.SetSpinSize(nSpinSize);
+}
+
+void VclBuilder::mungeAdjustment(ScrollBar &rTarget, const Adjustment &rAdjustment)
+{
+ for (auto const& [ rKey, rValue ] : rAdjustment)
+ {
+ if (rKey == "upper")
+ rTarget.SetRangeMax(rValue.toInt32());
+ else if (rKey == "lower")
+ rTarget.SetRangeMin(rValue.toInt32());
+ else if (rKey == "value")
+ rTarget.SetThumbPos(rValue.toInt32());
+ else if (rKey == "step-increment")
+ rTarget.SetLineSize(rValue.toInt32());
+ else if (rKey == "page-increment")
+ rTarget.SetPageSize(rValue.toInt32());
+ else
+ {
+ SAL_INFO("vcl.builder", "unhandled property :" << rKey);
+ }
+ }
+}
+
+void VclBuilder::mungeAdjustment(Slider& rTarget, const Adjustment& rAdjustment)
+{
+ for (auto const& [ rKey, rValue ] : rAdjustment)
+ {
+ if (rKey == "upper")
+ rTarget.SetRangeMax(rValue.toInt32());
+ else if (rKey == "lower")
+ rTarget.SetRangeMin(rValue.toInt32());
+ else if (rKey == "value")
+ rTarget.SetThumbPos(rValue.toInt32());
+ else if (rKey == "step-increment")
+ rTarget.SetLineSize(rValue.toInt32());
+ else if (rKey == "page-increment")
+ rTarget.SetPageSize(rValue.toInt32());
+ else
+ {
+ SAL_INFO("vcl.builder", "unhandled property :" << rKey);
+ }
+ }
+}
+
+void VclBuilder::mungeTextBuffer(VclMultiLineEdit &rTarget, const TextBuffer &rTextBuffer)
+{
+ for (auto const& [ rKey, rValue ] : rTextBuffer)
+ {
+ if (rKey == "text")
+ rTarget.SetText(rValue);
+ else
+ {
+ SAL_INFO("vcl.builder", "unhandled property :" << rKey);
+ }
+ }
+}
+
+VclBuilder::ParserState::ParserState()
+ : m_nLastToolbarId(0)
+ , m_nLastMenuItemId(0)
+{}
+
+VclBuilder::MenuAndId::MenuAndId(OUString aId, Menu *pMenu)
+ : m_sID(std::move(aId))
+ , m_pMenu(pMenu)
+{}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/clipping.cxx b/vcl/source/window/clipping.cxx
new file mode 100644
index 0000000000..f55283cff8
--- /dev/null
+++ b/vcl/source/window/clipping.cxx
@@ -0,0 +1,709 @@
+/* -*- 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 <vcl/window.hxx>
+#include <vcl/virdev.hxx>
+
+#include <tools/debug.hxx>
+
+#include <salobj.hxx>
+#include <window.h>
+
+namespace vcl {
+
+vcl::Region WindowOutputDevice::GetOutputBoundsClipRegion() const
+{
+ vcl::Region aClip(GetClipRegion());
+ aClip.Intersect(tools::Rectangle(Point(), GetOutputSize()));
+
+ return aClip;
+}
+
+void WindowOutputDevice::InitClipRegion()
+{
+ DBG_TESTSOLARMUTEX();
+
+ vcl::Region aRegion;
+
+ if ( mxOwnerWindow->mpWindowImpl->mbInPaint )
+ aRegion = *(mxOwnerWindow->mpWindowImpl->mpPaintRegion);
+ else
+ {
+ aRegion = mxOwnerWindow->ImplGetWinChildClipRegion();
+ // only this region is in frame coordinates, so re-mirror it
+ // the mpWindowImpl->mpPaintRegion above is already correct (see ImplCallPaint()) !
+ if( ImplIsAntiparallel() )
+ ReMirror ( aRegion );
+ }
+ if ( mbClipRegion )
+ aRegion.Intersect( ImplPixelToDevicePixel( maRegion ) );
+ if ( aRegion.IsEmpty() )
+ mbOutputClipped = true;
+ else
+ {
+ mbOutputClipped = false;
+ SelectClipRegion( aRegion );
+ }
+ mbClipRegionSet = true;
+
+ mbInitClipRegion = false;
+}
+
+void Window::SetParentClipMode( ParentClipMode nMode )
+{
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->SetParentClipMode( nMode );
+ else
+ {
+ if ( !ImplIsOverlapWindow() )
+ {
+ mpWindowImpl->mnParentClipMode = nMode;
+ if ( nMode & ParentClipMode::Clip )
+ mpWindowImpl->mpParent->mpWindowImpl->mbClipChildren = true;
+ }
+ }
+}
+
+ParentClipMode Window::GetParentClipMode() const
+{
+ if ( mpWindowImpl->mpBorderWindow )
+ return mpWindowImpl->mpBorderWindow->GetParentClipMode();
+ else
+ return mpWindowImpl->mnParentClipMode;
+}
+
+void Window::ExpandPaintClipRegion( const vcl::Region& rRegion )
+{
+ if( !mpWindowImpl->mpPaintRegion )
+ return;
+
+ vcl::Region aPixRegion = LogicToPixel( rRegion );
+ vcl::Region aDevPixRegion = GetOutDev()->ImplPixelToDevicePixel( aPixRegion );
+
+ vcl::Region aWinChildRegion = ImplGetWinChildClipRegion();
+ // only this region is in frame coordinates, so re-mirror it
+ if( GetOutDev()->ImplIsAntiparallel() )
+ {
+ const OutputDevice *pOutDev = GetOutDev();
+ pOutDev->ReMirror( aWinChildRegion );
+ }
+
+ aDevPixRegion.Intersect( aWinChildRegion );
+ if( ! aDevPixRegion.IsEmpty() )
+ {
+ mpWindowImpl->mpPaintRegion->Union( aDevPixRegion );
+ GetOutDev()->mbInitClipRegion = true;
+ }
+}
+
+vcl::Region Window::GetWindowClipRegionPixel() const
+{
+ vcl::Region aWinClipRegion;
+
+ if ( mpWindowImpl->mbInitWinClipRegion )
+ const_cast<vcl::Window*>(this)->ImplInitWinClipRegion();
+ aWinClipRegion = mpWindowImpl->maWinClipRegion;
+
+ vcl::Region aWinRegion( GetOutputRectPixel() );
+
+ if ( aWinRegion == aWinClipRegion )
+ aWinClipRegion.SetNull();
+
+ aWinClipRegion.Move( -GetOutDev()->mnOutOffX, -GetOutDev()->mnOutOffY );
+
+ return aWinClipRegion;
+}
+
+
+vcl::Region WindowOutputDevice::GetActiveClipRegion() const
+{
+ vcl::Region aRegion(true);
+
+ if ( mxOwnerWindow->mpWindowImpl->mbInPaint )
+ {
+ aRegion = *(mxOwnerWindow->mpWindowImpl->mpPaintRegion);
+ aRegion.Move( -mnOutOffX, -mnOutOffY );
+ }
+
+ if ( mbClipRegion )
+ aRegion.Intersect( maRegion );
+
+ return PixelToLogic( aRegion );
+}
+
+void WindowOutputDevice::ClipToPaintRegion(tools::Rectangle& rDstRect)
+{
+ const vcl::Region aPaintRgn(mxOwnerWindow->GetPaintRegion());
+
+ if (!aPaintRgn.IsNull())
+ rDstRect.Intersection(LogicToPixel(aPaintRgn.GetBoundRect()));
+}
+
+void Window::EnableClipSiblings( bool bClipSiblings )
+{
+
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->EnableClipSiblings( bClipSiblings );
+
+ mpWindowImpl->mbClipSiblings = bClipSiblings;
+}
+
+void Window::ImplClipBoundaries( vcl::Region& rRegion, bool bThis, bool bOverlaps )
+{
+ if ( bThis )
+ ImplIntersectWindowClipRegion( rRegion );
+ else if ( ImplIsOverlapWindow() )
+ {
+ // clip to frame if required
+ if ( !mpWindowImpl->mbFrame )
+ rRegion.Intersect( tools::Rectangle( Point( 0, 0 ), mpWindowImpl->mpFrameWindow->GetOutputSizePixel() ) );
+
+ if ( bOverlaps && !rRegion.IsEmpty() )
+ {
+ // Clip Overlap Siblings
+ vcl::Window* pStartOverlapWindow = this;
+ while ( !pStartOverlapWindow->mpWindowImpl->mbFrame )
+ {
+ vcl::Window* pOverlapWindow = pStartOverlapWindow->mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap;
+ while ( pOverlapWindow && (pOverlapWindow != pStartOverlapWindow) )
+ {
+ pOverlapWindow->ImplExcludeOverlapWindows2( rRegion );
+ pOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
+ }
+ pStartOverlapWindow = pStartOverlapWindow->mpWindowImpl->mpOverlapWindow;
+ }
+
+ // Clip Child Overlap Windows
+ ImplExcludeOverlapWindows( rRegion );
+ }
+ }
+ else
+ ImplGetParent()->ImplIntersectWindowClipRegion( rRegion );
+}
+
+bool Window::ImplClipChildren( vcl::Region& rRegion ) const
+{
+ bool bOtherClip = false;
+ vcl::Window* pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbReallyVisible )
+ {
+ // read-out ParentClipMode-Flags
+ ParentClipMode nClipMode = pWindow->GetParentClipMode();
+ if ( !(nClipMode & ParentClipMode::NoClip) &&
+ ((nClipMode & ParentClipMode::Clip) || (GetStyle() & WB_CLIPCHILDREN)) )
+ pWindow->ImplExcludeWindowRegion( rRegion );
+ else
+ bOtherClip = true;
+ }
+
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+
+ return bOtherClip;
+}
+
+void Window::ImplClipAllChildren( vcl::Region& rRegion ) const
+{
+ vcl::Window* pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbReallyVisible )
+ pWindow->ImplExcludeWindowRegion( rRegion );
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplClipSiblings( vcl::Region& rRegion ) const
+{
+ vcl::Window* pWindow = ImplGetParent()->mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ if ( pWindow == this )
+ break;
+
+ if ( pWindow->mpWindowImpl->mbReallyVisible )
+ pWindow->ImplExcludeWindowRegion( rRegion );
+
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplInitWinClipRegion()
+{
+ // Build Window Region
+ mpWindowImpl->maWinClipRegion = GetOutputRectPixel();
+ if ( mpWindowImpl->mbWinRegion )
+ mpWindowImpl->maWinClipRegion.Intersect( GetOutDev()->ImplPixelToDevicePixel( mpWindowImpl->maWinRegion ) );
+
+ // ClipSiblings
+ if ( mpWindowImpl->mbClipSiblings && !ImplIsOverlapWindow() )
+ ImplClipSiblings( mpWindowImpl->maWinClipRegion );
+
+ // Clip Parent Boundaries
+ ImplClipBoundaries( mpWindowImpl->maWinClipRegion, false, true );
+
+ // Clip Children
+ if ( (GetStyle() & WB_CLIPCHILDREN) || mpWindowImpl->mbClipChildren )
+ mpWindowImpl->mbInitChildRegion = true;
+
+ mpWindowImpl->mbInitWinClipRegion = false;
+}
+
+void Window::ImplInitWinChildClipRegion()
+{
+ if ( !mpWindowImpl->mpFirstChild )
+ {
+ mpWindowImpl->mpChildClipRegion.reset();
+ }
+ else
+ {
+ if ( !mpWindowImpl->mpChildClipRegion )
+ mpWindowImpl->mpChildClipRegion.reset( new vcl::Region( mpWindowImpl->maWinClipRegion ) );
+ else
+ *mpWindowImpl->mpChildClipRegion = mpWindowImpl->maWinClipRegion;
+
+ ImplClipChildren( *mpWindowImpl->mpChildClipRegion );
+ }
+
+ mpWindowImpl->mbInitChildRegion = false;
+}
+
+Region& Window::ImplGetWinChildClipRegion()
+{
+ if ( mpWindowImpl->mbInitWinClipRegion )
+ ImplInitWinClipRegion();
+ if ( mpWindowImpl->mbInitChildRegion )
+ ImplInitWinChildClipRegion();
+ if ( mpWindowImpl->mpChildClipRegion )
+ return *mpWindowImpl->mpChildClipRegion;
+ return mpWindowImpl->maWinClipRegion;
+}
+
+bool Window::ImplSysObjClip( const vcl::Region* pOldRegion )
+{
+ bool bUpdate = true;
+
+ if ( mpWindowImpl->mpSysObj )
+ {
+ bool bVisibleState = mpWindowImpl->mbReallyVisible;
+
+ if ( bVisibleState )
+ {
+ vcl::Region& rWinChildClipRegion = ImplGetWinChildClipRegion();
+
+ if (!rWinChildClipRegion.IsEmpty())
+ {
+ if ( pOldRegion )
+ {
+ vcl::Region aNewRegion = rWinChildClipRegion;
+ rWinChildClipRegion.Intersect(*pOldRegion);
+ bUpdate = aNewRegion == rWinChildClipRegion;
+ }
+
+ vcl::Region aRegion = rWinChildClipRegion;
+ vcl::Region aWinRectRegion( GetOutputRectPixel() );
+
+ if ( aRegion == aWinRectRegion )
+ mpWindowImpl->mpSysObj->ResetClipRegion();
+ else
+ {
+ aRegion.Move( -GetOutDev()->mnOutOffX, -GetOutDev()->mnOutOffY );
+
+ // set/update clip region
+ RectangleVector aRectangles;
+ aRegion.GetRegionRectangles(aRectangles);
+ mpWindowImpl->mpSysObj->BeginSetClipRegion(aRectangles.size());
+
+ for (auto const& rectangle : aRectangles)
+ {
+ mpWindowImpl->mpSysObj->UnionClipRegion(
+ rectangle.Left(),
+ rectangle.Top(),
+ rectangle.GetWidth(), // orig nWidth was ((R - L) + 1), same as GetWidth does
+ rectangle.GetHeight()); // same for height
+ }
+
+ mpWindowImpl->mpSysObj->EndSetClipRegion();
+ }
+ }
+ else
+ bVisibleState = false;
+ }
+
+ // update visible status
+ mpWindowImpl->mpSysObj->Show( bVisibleState );
+ }
+
+ return bUpdate;
+}
+
+void Window::ImplUpdateSysObjChildrenClip()
+{
+ if ( mpWindowImpl->mpSysObj && mpWindowImpl->mbInitWinClipRegion )
+ ImplSysObjClip( nullptr );
+
+ vcl::Window* pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ pWindow->ImplUpdateSysObjChildrenClip();
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplUpdateSysObjOverlapsClip()
+{
+ ImplUpdateSysObjChildrenClip();
+
+ vcl::Window* pWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pWindow )
+ {
+ pWindow->ImplUpdateSysObjOverlapsClip();
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplUpdateSysObjClip()
+{
+ if ( !ImplIsOverlapWindow() )
+ {
+ ImplUpdateSysObjChildrenClip();
+
+ // siblings should recalculate their clip region
+ if ( mpWindowImpl->mbClipSiblings )
+ {
+ vcl::Window* pWindow = mpWindowImpl->mpNext;
+ while ( pWindow )
+ {
+ pWindow->ImplUpdateSysObjChildrenClip();
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+ }
+ }
+ else
+ mpWindowImpl->mpFrameWindow->ImplUpdateSysObjOverlapsClip();
+}
+
+bool Window::ImplSetClipFlagChildren( bool bSysObjOnlySmaller )
+{
+ bool bUpdate = true;
+ if ( mpWindowImpl->mpSysObj )
+ {
+ std::unique_ptr<vcl::Region> pOldRegion;
+ if ( bSysObjOnlySmaller && !mpWindowImpl->mbInitWinClipRegion )
+ pOldRegion.reset(new vcl::Region( mpWindowImpl->maWinClipRegion ));
+
+ GetOutDev()->mbInitClipRegion = true;
+ mpWindowImpl->mbInitWinClipRegion = true;
+
+ vcl::Window* pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ if ( !pWindow->ImplSetClipFlagChildren( bSysObjOnlySmaller ) )
+ bUpdate = false;
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+
+ if ( !ImplSysObjClip( pOldRegion.get() ) )
+ {
+ GetOutDev()->mbInitClipRegion = true;
+ mpWindowImpl->mbInitWinClipRegion = true;
+ bUpdate = false;
+ }
+ }
+ else
+ {
+ GetOutDev()->mbInitClipRegion = true;
+ mpWindowImpl->mbInitWinClipRegion = true;
+
+ vcl::Window* pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ if ( !pWindow->ImplSetClipFlagChildren( bSysObjOnlySmaller ) )
+ bUpdate = false;
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+ }
+ return bUpdate;
+}
+
+bool Window::ImplSetClipFlagOverlapWindows( bool bSysObjOnlySmaller )
+{
+ bool bUpdate = ImplSetClipFlagChildren( bSysObjOnlySmaller );
+
+ vcl::Window* pWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pWindow )
+ {
+ if ( !pWindow->ImplSetClipFlagOverlapWindows( bSysObjOnlySmaller ) )
+ bUpdate = false;
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+
+ return bUpdate;
+}
+
+bool Window::ImplSetClipFlag( bool bSysObjOnlySmaller )
+{
+ if ( !ImplIsOverlapWindow() )
+ {
+ bool bUpdate = ImplSetClipFlagChildren( bSysObjOnlySmaller );
+
+ vcl::Window* pParent = ImplGetParent();
+ if ( pParent &&
+ ((pParent->GetStyle() & WB_CLIPCHILDREN) || (mpWindowImpl->mnParentClipMode & ParentClipMode::Clip)) )
+ {
+ pParent->GetOutDev()->mbInitClipRegion = true;
+ pParent->mpWindowImpl->mbInitChildRegion = true;
+ }
+
+ // siblings should recalculate their clip region
+ if ( mpWindowImpl->mbClipSiblings )
+ {
+ vcl::Window* pWindow = mpWindowImpl->mpNext;
+ while ( pWindow )
+ {
+ if ( !pWindow->ImplSetClipFlagChildren( bSysObjOnlySmaller ) )
+ bUpdate = false;
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+ }
+
+ return bUpdate;
+ }
+ else
+ return mpWindowImpl->mpFrameWindow->ImplSetClipFlagOverlapWindows( bSysObjOnlySmaller );
+}
+
+void Window::ImplIntersectWindowClipRegion( vcl::Region& rRegion )
+{
+ if ( mpWindowImpl->mbInitWinClipRegion )
+ ImplInitWinClipRegion();
+
+ rRegion.Intersect( mpWindowImpl->maWinClipRegion );
+}
+
+void Window::ImplIntersectWindowRegion( vcl::Region& rRegion )
+{
+ rRegion.Intersect( GetOutputRectPixel() );
+ if ( mpWindowImpl->mbWinRegion )
+ rRegion.Intersect( GetOutDev()->ImplPixelToDevicePixel( mpWindowImpl->maWinRegion ) );
+}
+
+void Window::ImplExcludeWindowRegion( vcl::Region& rRegion )
+{
+ if ( mpWindowImpl->mbWinRegion )
+ {
+ vcl::Region aRegion( GetOutputRectPixel() );
+ aRegion.Intersect( GetOutDev()->ImplPixelToDevicePixel( mpWindowImpl->maWinRegion ) );
+ rRegion.Exclude( aRegion );
+ }
+ else
+ {
+ rRegion.Exclude( GetOutputRectPixel() );
+ }
+}
+
+void Window::ImplExcludeOverlapWindows( vcl::Region& rRegion ) const
+{
+ vcl::Window* pWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbReallyVisible )
+ {
+ pWindow->ImplExcludeWindowRegion( rRegion );
+ pWindow->ImplExcludeOverlapWindows( rRegion );
+ }
+
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplExcludeOverlapWindows2( vcl::Region& rRegion )
+{
+ if ( mpWindowImpl->mbReallyVisible )
+ ImplExcludeWindowRegion( rRegion );
+
+ ImplExcludeOverlapWindows( rRegion );
+}
+
+void Window::ImplIntersectAndUnionOverlapWindows( const vcl::Region& rInterRegion, vcl::Region& rRegion ) const
+{
+ vcl::Window* pWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbReallyVisible )
+ {
+ vcl::Region aTempRegion( rInterRegion );
+ pWindow->ImplIntersectWindowRegion( aTempRegion );
+ rRegion.Union( aTempRegion );
+ pWindow->ImplIntersectAndUnionOverlapWindows( rInterRegion, rRegion );
+ }
+
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplIntersectAndUnionOverlapWindows2( const vcl::Region& rInterRegion, vcl::Region& rRegion )
+{
+ if ( mpWindowImpl->mbReallyVisible )
+ {
+ vcl::Region aTempRegion( rInterRegion );
+ ImplIntersectWindowRegion( aTempRegion );
+ rRegion.Union( aTempRegion );
+ }
+
+ ImplIntersectAndUnionOverlapWindows( rInterRegion, rRegion );
+}
+
+void Window::ImplCalcOverlapRegionOverlaps( const vcl::Region& rInterRegion, vcl::Region& rRegion ) const
+{
+ // Clip Overlap Siblings
+ vcl::Window const * pStartOverlapWindow;
+ if ( !ImplIsOverlapWindow() )
+ pStartOverlapWindow = mpWindowImpl->mpOverlapWindow;
+ else
+ pStartOverlapWindow = this;
+ while ( !pStartOverlapWindow->mpWindowImpl->mbFrame )
+ {
+ vcl::Window* pOverlapWindow = pStartOverlapWindow->mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap;
+ while ( pOverlapWindow && (pOverlapWindow != pStartOverlapWindow) )
+ {
+ pOverlapWindow->ImplIntersectAndUnionOverlapWindows2( rInterRegion, rRegion );
+ pOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
+ }
+ pStartOverlapWindow = pStartOverlapWindow->mpWindowImpl->mpOverlapWindow;
+ }
+
+ // Clip Child Overlap Windows
+ if ( !ImplIsOverlapWindow() )
+ mpWindowImpl->mpOverlapWindow->ImplIntersectAndUnionOverlapWindows( rInterRegion, rRegion );
+ else
+ ImplIntersectAndUnionOverlapWindows( rInterRegion, rRegion );
+}
+
+void Window::ImplCalcOverlapRegion( const tools::Rectangle& rSourceRect, vcl::Region& rRegion,
+ bool bChildren, bool bSiblings )
+{
+ vcl::Region aRegion( rSourceRect );
+ if ( mpWindowImpl->mbWinRegion )
+ rRegion.Intersect( GetOutDev()->ImplPixelToDevicePixel( mpWindowImpl->maWinRegion ) );
+ vcl::Region aTempRegion;
+ vcl::Window* pWindow;
+
+ ImplCalcOverlapRegionOverlaps( aRegion, rRegion );
+
+ // Parent-Boundaries
+ pWindow = this;
+ if ( !ImplIsOverlapWindow() )
+ {
+ pWindow = ImplGetParent();
+ do
+ {
+ aTempRegion = aRegion;
+ pWindow->ImplExcludeWindowRegion( aTempRegion );
+ rRegion.Union( aTempRegion );
+ if ( pWindow->ImplIsOverlapWindow() )
+ break;
+ pWindow = pWindow->ImplGetParent();
+ }
+ while ( pWindow );
+ }
+ if ( pWindow && !pWindow->mpWindowImpl->mbFrame )
+ {
+ aTempRegion = aRegion;
+ aTempRegion.Exclude( tools::Rectangle( Point( 0, 0 ), mpWindowImpl->mpFrameWindow->GetOutputSizePixel() ) );
+ rRegion.Union( aTempRegion );
+ }
+
+ // Siblings
+ if ( bSiblings && !ImplIsOverlapWindow() )
+ {
+ pWindow = mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild;
+ do
+ {
+ if ( pWindow->mpWindowImpl->mbReallyVisible && (pWindow != this) )
+ {
+ aTempRegion = aRegion;
+ pWindow->ImplIntersectWindowRegion( aTempRegion );
+ rRegion.Union( aTempRegion );
+ }
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+ while ( pWindow );
+ }
+
+ if ( !bChildren )
+ return;
+
+ pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbReallyVisible )
+ {
+ aTempRegion = aRegion;
+ pWindow->ImplIntersectWindowRegion( aTempRegion );
+ rRegion.Union( aTempRegion );
+ }
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void WindowOutputDevice::SaveBackground(VirtualDevice& rSaveDevice, const Point& rPos, const Size& rSize, const Size&) const
+{
+ MapMode aTempMap(GetMapMode());
+ aTempMap.SetOrigin(Point());
+ rSaveDevice.SetMapMode(aTempMap);
+
+ if ( mxOwnerWindow->mpWindowImpl->mpPaintRegion )
+ {
+ vcl::Region aClip( *mxOwnerWindow->mpWindowImpl->mpPaintRegion );
+ const Point aPixPos( LogicToPixel( rPos ) );
+
+ aClip.Move( -mnOutOffX, -mnOutOffY );
+ aClip.Intersect( tools::Rectangle( aPixPos, LogicToPixel( rSize ) ) );
+
+ if ( !aClip.IsEmpty() )
+ {
+ const vcl::Region aOldClip( rSaveDevice.GetClipRegion() );
+ const Point aPixOffset( rSaveDevice.LogicToPixel( Point() ) );
+ const bool bMap = rSaveDevice.IsMapModeEnabled();
+
+ // move clip region to have the same distance to DestOffset
+ aClip.Move( aPixOffset.X() - aPixPos.X(), aPixOffset.Y() - aPixPos.Y() );
+
+ // set pixel clip region
+ rSaveDevice.EnableMapMode( false );
+ rSaveDevice.SetClipRegion( aClip );
+ rSaveDevice.EnableMapMode( bMap );
+ rSaveDevice.DrawOutDev( Point(), rSize, rPos, rSize, *this );
+ rSaveDevice.SetClipRegion( aOldClip );
+ }
+ }
+ else
+ {
+ rSaveDevice.DrawOutDev( Point(), rSize, rPos, rSize, *this );
+ }
+
+ rSaveDevice.SetMapMode(MapMode());
+}
+
+} /* namespace vcl */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/commandevent.cxx b/vcl/source/window/commandevent.cxx
new file mode 100644
index 0000000000..83c21826e7
--- /dev/null
+++ b/vcl/source/window/commandevent.cxx
@@ -0,0 +1,214 @@
+/* -*- 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 <utility>
+#include <vcl/commandevent.hxx>
+
+CommandExtTextInputData::CommandExtTextInputData( OUString aText,
+ const ExtTextInputAttr* pTextAttr, sal_Int32 nCursorPos, sal_uInt16 nCursorFlags,
+ bool bOnlyCursor)
+ : maText(std::move(aText))
+{
+ if ( pTextAttr && !maText.isEmpty() )
+ {
+ mpTextAttr.reset( new ExtTextInputAttr[maText.getLength()] );
+ memcpy( mpTextAttr.get(), pTextAttr, maText.getLength()*sizeof(ExtTextInputAttr) );
+ }
+
+ mnCursorPos = nCursorPos;
+ mnCursorFlags = nCursorFlags;
+ mbOnlyCursor = bOnlyCursor;
+}
+
+CommandExtTextInputData::CommandExtTextInputData( const CommandExtTextInputData& rData ) :
+ maText( rData.maText )
+{
+ if ( rData.mpTextAttr && !maText.isEmpty() )
+ {
+ mpTextAttr.reset( new ExtTextInputAttr[maText.getLength()] );
+ memcpy( mpTextAttr.get(), rData.mpTextAttr.get(), maText.getLength()*sizeof(ExtTextInputAttr) );
+ }
+
+ mnCursorPos = rData.mnCursorPos;
+ mnCursorFlags = rData.mnCursorFlags;
+ mbOnlyCursor = rData.mbOnlyCursor;
+}
+
+CommandExtTextInputData::~CommandExtTextInputData()
+{
+}
+
+CommandWheelData::CommandWheelData()
+{
+ mnDelta = 0;
+ mnNotchDelta = 0;
+ mnLines = 0.0;
+ mnWheelMode = CommandWheelMode::NONE;
+ mnCode = 0;
+ mbHorz = false;
+ mbDeltaIsPixel = false;
+}
+
+CommandWheelData::CommandWheelData( tools::Long nWheelDelta, tools::Long nWheelNotchDelta,
+ double nScrollLines,
+ CommandWheelMode nWheelMode, sal_uInt16 nKeyModifier,
+ bool bHorz, bool bDeltaIsPixel )
+{
+ mnDelta = nWheelDelta;
+ mnNotchDelta = nWheelNotchDelta;
+ mnLines = nScrollLines;
+ mnWheelMode = nWheelMode;
+ mnCode = nKeyModifier;
+ mbHorz = bHorz;
+ mbDeltaIsPixel = bDeltaIsPixel;
+}
+
+CommandScrollData::CommandScrollData( tools::Long nDeltaX, tools::Long nDeltaY )
+{
+ mnDeltaX = nDeltaX;
+ mnDeltaY = nDeltaY;
+}
+
+CommandModKeyData::CommandModKeyData( ModKeyFlags nCode, bool bDown )
+{
+ mbDown = bDown;
+ mnCode = nCode;
+}
+
+CommandSelectionChangeData::CommandSelectionChangeData( sal_uLong nStart, sal_uLong nEnd )
+{
+ mnStart = nStart;
+ mnEnd = nEnd;
+}
+
+CommandEvent::CommandEvent()
+{
+ mpData = nullptr;
+ mnCommand = CommandEventId::NONE;
+ mbMouseEvent = false;
+}
+
+CommandEvent::CommandEvent( const Point& rMousePos,
+ CommandEventId nCmd, bool bMEvt, const void* pCmdData ) :
+ maPos( rMousePos )
+{
+ mpData = const_cast<void*>(pCmdData);
+ mnCommand = nCmd;
+ mbMouseEvent = bMEvt;
+}
+
+const CommandExtTextInputData* CommandEvent::GetExtTextInputData() const
+{
+ if ( mnCommand == CommandEventId::ExtTextInput )
+ return static_cast<const CommandExtTextInputData*>(mpData);
+ else
+ return nullptr;
+}
+
+const CommandWheelData* CommandEvent::GetWheelData() const
+{
+ if ( mnCommand == CommandEventId::Wheel )
+ return static_cast<const CommandWheelData*>(mpData);
+ else
+ return nullptr;
+}
+
+const CommandScrollData* CommandEvent::GetAutoScrollData() const
+{
+ if ( mnCommand == CommandEventId::AutoScroll )
+ return static_cast<const CommandScrollData*>(mpData);
+ else
+ return nullptr;
+}
+
+const CommandModKeyData* CommandEvent::GetModKeyData() const
+{
+ if( mnCommand == CommandEventId::ModKeyChange )
+ return static_cast<const CommandModKeyData*>(mpData);
+ else
+ return nullptr;
+}
+
+const CommandDialogData* CommandEvent::GetDialogData() const
+{
+ if( mnCommand == CommandEventId::ShowDialog )
+ return static_cast<const CommandDialogData*>(mpData);
+ else
+ return nullptr;
+}
+
+CommandMediaData* CommandEvent::GetMediaData() const
+{
+ if( mnCommand == CommandEventId::Media )
+ return static_cast<CommandMediaData*>(mpData);
+ else
+ return nullptr;
+}
+
+const CommandSelectionChangeData* CommandEvent::GetSelectionChangeData() const
+{
+ if( mnCommand == CommandEventId::SelectionChange )
+ return static_cast<const CommandSelectionChangeData*>(mpData);
+ else
+ return nullptr;
+}
+
+const CommandGestureSwipeData* CommandEvent::GetGestureSwipeData() const
+{
+ if( mnCommand == CommandEventId::GestureSwipe )
+ return static_cast<const CommandGestureSwipeData*>(mpData);
+ else
+ return nullptr;
+}
+
+const CommandGestureLongPressData* CommandEvent::GetLongPressData() const
+{
+ if( mnCommand == CommandEventId::GestureLongPress )
+ return static_cast<const CommandGestureLongPressData*>(mpData);
+ else
+ return nullptr;
+}
+
+const CommandGesturePanData* CommandEvent::GetGesturePanData() const
+{
+ if (mnCommand == CommandEventId::GesturePan)
+ return static_cast<const CommandGesturePanData*>(mpData);
+ else
+ return nullptr;
+}
+
+const CommandGestureZoomData* CommandEvent::GetGestureZoomData() const
+{
+ if (mnCommand == CommandEventId::GestureZoom)
+ return static_cast<const CommandGestureZoomData*>(mpData);
+ else
+ return nullptr;
+}
+
+const CommandGestureRotateData* CommandEvent::GetGestureRotateData() const
+{
+ if (mnCommand == CommandEventId::GestureRotate)
+ return static_cast<const CommandGestureRotateData*>(mpData);
+ else
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/cursor.cxx b/vcl/source/window/cursor.cxx
new file mode 100644
index 0000000000..d160e8aa73
--- /dev/null
+++ b/vcl/source/window/cursor.cxx
@@ -0,0 +1,485 @@
+/* -*- 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 <comphelper/lok.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/window.hxx>
+#include <vcl/cursor.hxx>
+
+#include <window.h>
+
+#include <tools/poly.hxx>
+
+struct ImplCursorData
+{
+ AutoTimer maTimer { "vcl ImplCursorData maTimer" }; // Timer
+ Point maPixPos; // Pixel-Position
+ Point maPixRotOff; // Pixel-Offset-Position
+ Size maPixSize; // Pixel-Size
+ Degree10 mnOrientation; // Pixel-Orientation
+ CursorDirection mnDirection; // indicates writing direction
+ sal_uInt16 mnStyle; // Cursor-Style
+ bool mbCurVisible; // Is cursor currently visible
+ VclPtr<vcl::Window> mpWindow; // assigned window
+};
+
+namespace
+{
+const char* pDisableCursorIndicator(getenv("SAL_DISABLE_CURSOR_INDICATOR"));
+bool bDisableCursorIndicator(nullptr != pDisableCursorIndicator);
+}
+
+static tools::Rectangle ImplCursorInvert(vcl::RenderContext* pRenderContext, ImplCursorData const * pData)
+{
+ tools::Rectangle aPaintRect;
+
+ bool bMapMode = pRenderContext->IsMapModeEnabled();
+ pRenderContext->EnableMapMode( false );
+ InvertFlags nInvertStyle;
+ if ( pData->mnStyle & CURSOR_SHADOW )
+ nInvertStyle = InvertFlags::N50;
+ else
+ nInvertStyle = InvertFlags::NONE;
+
+ tools::Rectangle aRect( pData->maPixPos, pData->maPixSize );
+ if ( pData->mnDirection != CursorDirection::NONE || pData->mnOrientation )
+ {
+ tools::Polygon aPoly( aRect );
+ if( aPoly.GetSize() == 5 )
+ {
+ aPoly[1].AdjustX(1 ); // include the right border
+ aPoly[2].AdjustX(1 );
+
+ // apply direction flag after slant to use the correct shape
+ if (!bDisableCursorIndicator && pData->mnDirection != CursorDirection::NONE)
+ {
+ Point pAry[7];
+ // Related system settings for "delta" could be:
+ // gtk cursor-aspect-ratio and windows SPI_GETCARETWIDTH
+ int delta = (aRect.getOpenHeight() * 4 / 100) + 1;
+ if( pData->mnDirection == CursorDirection::LTR )
+ {
+ // left-to-right
+ pAry[0] = aPoly.GetPoint( 0 );
+ pAry[1] = aPoly.GetPoint( 1 );
+ pAry[2] = pAry[1];
+ pAry[2].AdjustX(delta);
+ pAry[2].AdjustY(delta);
+ pAry[3] = pAry[1];
+ pAry[3].AdjustY(delta * 2);
+ pAry[4] = aPoly.GetPoint( 2 );
+ pAry[5] = aPoly.GetPoint( 3 );
+ pAry[6] = aPoly.GetPoint( 4 );
+ }
+ else if( pData->mnDirection == CursorDirection::RTL )
+ {
+ // right-to-left
+ pAry[0] = aPoly.GetPoint( 0 );
+ pAry[1] = aPoly.GetPoint( 1 );
+ pAry[2] = aPoly.GetPoint( 2 );
+ pAry[3] = aPoly.GetPoint( 3 );
+ pAry[4] = pAry[0];
+ pAry[4].AdjustY(delta*2);
+ pAry[5] = pAry[0];
+ pAry[5].AdjustX(-delta);
+ pAry[5].AdjustY(delta);
+ pAry[6] = aPoly.GetPoint( 4 );
+ }
+ aPoly = tools::Polygon( 7, pAry);
+ }
+
+ if ( pData->mnOrientation )
+ aPoly.Rotate( pData->maPixRotOff, pData->mnOrientation );
+ pRenderContext->Invert( aPoly, nInvertStyle );
+ aPaintRect = aPoly.GetBoundRect();
+ }
+ }
+ else
+ {
+ pRenderContext->Invert( aRect, nInvertStyle );
+ aPaintRect = aRect;
+ }
+ pRenderContext->EnableMapMode( bMapMode );
+ return aPaintRect;
+}
+
+static void ImplCursorInvert(vcl::Window* pWindow, ImplCursorData const * pData)
+{
+ if (!pWindow || pWindow->isDisposed())
+ return;
+
+ vcl::PaintBufferGuardPtr pGuard;
+ const bool bDoubleBuffering = pWindow->SupportsDoubleBuffering();
+ if (bDoubleBuffering)
+ pGuard.reset(new vcl::PaintBufferGuard(pWindow->ImplGetWindowImpl()->mpFrameData, pWindow));
+
+ vcl::RenderContext* pRenderContext = bDoubleBuffering ? pGuard->GetRenderContext() : pWindow->GetOutDev();
+
+ tools::Rectangle aPaintRect = ImplCursorInvert(pRenderContext, pData);
+ if (bDoubleBuffering)
+ pGuard->SetPaintRect(pRenderContext->PixelToLogic(aPaintRect));
+}
+
+bool vcl::Cursor::ImplPrepForDraw(const OutputDevice* pDevice, ImplCursorData& rData)
+{
+ if (pDevice && !rData.mbCurVisible)
+ {
+ rData.maPixPos = pDevice->LogicToPixel( maPos );
+ rData.maPixSize = pDevice->LogicToPixel( maSize );
+ rData.mnOrientation = mnOrientation;
+ rData.mnDirection = mnDirection;
+
+ // correct the position with the offset
+ rData.maPixRotOff = rData.maPixPos;
+
+ // use width (as set in Settings) if size is 0,
+ if (!rData.maPixSize.Width())
+ rData.maPixSize.setWidth(pDevice->GetSettings().GetStyleSettings().GetCursorSize());
+ return true;
+ }
+ return false;
+}
+
+void vcl::Cursor::ImplDraw()
+{
+ if (mpData && mpData->mpWindow)
+ {
+ // calculate output area
+ if (ImplPrepForDraw(mpData->mpWindow->GetOutDev(), *mpData))
+ {
+ // display
+ ImplCursorInvert(mpData->mpWindow, mpData.get());
+ mpData->mbCurVisible = true;
+ }
+ }
+}
+
+void vcl::Cursor::DrawToDevice(OutputDevice& rRenderContext)
+{
+ ImplCursorData aData;
+ aData.mnStyle = 0;
+ aData.mbCurVisible = false;
+ // calculate output area
+ if (ImplPrepForDraw(&rRenderContext, aData))
+ {
+ // display
+ ImplCursorInvert(&rRenderContext, &aData);
+ }
+}
+
+void vcl::Cursor::ImplRestore()
+{
+ assert( mpData && mpData->mbCurVisible );
+
+ ImplCursorInvert(mpData->mpWindow, mpData.get());
+ mpData->mbCurVisible = false;
+}
+
+void vcl::Cursor::ImplDoShow( bool bDrawDirect, bool bRestore )
+{
+ if ( !mbVisible )
+ return;
+
+ vcl::Window* pWindow;
+ if ( mpWindow )
+ pWindow = mpWindow;
+ else
+ {
+ // show the cursor, if there is an active window and the cursor
+ // has been selected in this window
+ pWindow = Application::GetFocusWindow();
+ if (!pWindow || !pWindow->mpWindowImpl || (pWindow->mpWindowImpl->mpCursor != this)
+ || pWindow->mpWindowImpl->mbInPaint
+ || !pWindow->mpWindowImpl->mpFrameData->mbHasFocus)
+ pWindow = nullptr;
+ }
+
+ if ( !pWindow )
+ return;
+
+ if ( !mpData )
+ {
+ mpData.reset( new ImplCursorData );
+ mpData->mbCurVisible = false;
+ mpData->maTimer.SetInvokeHandler( LINK( this, Cursor, ImplTimerHdl ) );
+ }
+
+ mpData->mpWindow = pWindow;
+ mpData->mnStyle = mnStyle;
+ if ( bDrawDirect || bRestore )
+ ImplDraw();
+
+ if ( !mpWindow && (bDrawDirect || !mpData->maTimer.IsActive()) )
+ {
+ mpData->maTimer.SetTimeout( pWindow->GetSettings().GetStyleSettings().GetCursorBlinkTime() );
+ if ( mpData->maTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME )
+ mpData->maTimer.Start();
+ else if ( !mpData->mbCurVisible )
+ ImplDraw();
+ LOKNotify( pWindow, "cursor_invalidate" );
+ LOKNotify( pWindow, "cursor_visible" );
+ }
+}
+
+void vcl::Cursor::LOKNotify( vcl::Window* pWindow, const OUString& rAction )
+{
+ VclPtr<vcl::Window> pParent = pWindow->GetParentWithLOKNotifier();
+ if (!pParent)
+ return;
+
+ assert(pWindow && "Cannot notify without a window");
+ assert(mpData && "Require ImplCursorData");
+ assert(comphelper::LibreOfficeKit::isActive());
+
+ if (comphelper::LibreOfficeKit::isDialogPainting())
+ return;
+
+ const vcl::ILibreOfficeKitNotifier* pNotifier = pParent->GetLOKNotifier();
+ std::vector<vcl::LOKPayloadItem> aItems;
+ if (rAction == "cursor_visible")
+ aItems.emplace_back("visible", mpData->mbCurVisible ? "true" : "false");
+ else if (rAction == "cursor_invalidate")
+ {
+ const tools::Long nX = pWindow->GetOutOffXPixel() + pWindow->LogicToPixel(GetPos()).X() - pParent->GetOutOffXPixel();
+ const tools::Long nY = pWindow->GetOutOffYPixel() + pWindow->LogicToPixel(GetPos()).Y() - pParent->GetOutOffYPixel();
+ Size aSize = pWindow->LogicToPixel(GetSize());
+ if (!aSize.Width())
+ aSize.setWidth( pWindow->GetSettings().GetStyleSettings().GetCursorSize() );
+
+ Point aPos(nX, nY);
+
+ if (pWindow->IsRTLEnabled() && pWindow->GetOutDev() && pParent->GetOutDev()
+ && !pWindow->GetOutDev()->ImplIsAntiparallel())
+ pParent->GetOutDev()->ReMirror(aPos);
+
+ if (!pWindow->IsRTLEnabled() && pWindow->GetOutDev() && pParent->GetOutDev()
+ && pWindow->GetOutDev()->ImplIsAntiparallel())
+ {
+ pWindow->GetOutDev()->ReMirror(aPos);
+ pParent->GetOutDev()->ReMirror(aPos);
+ }
+
+ const tools::Rectangle aRect(aPos, aSize);
+ aItems.emplace_back("rectangle", aRect.toString());
+ }
+
+ pNotifier->notifyWindow(pParent->GetLOKWindowId(), rAction, aItems);
+}
+
+bool vcl::Cursor::ImplDoHide( bool bSuspend )
+{
+ bool bWasCurVisible = false;
+ if ( mpData && mpData->mpWindow )
+ {
+ bWasCurVisible = mpData->mbCurVisible;
+ if ( mpData->mbCurVisible )
+ ImplRestore();
+
+ if ( !bSuspend )
+ {
+ LOKNotify( mpData->mpWindow, "cursor_visible" );
+ mpData->maTimer.Stop();
+ mpData->mpWindow = nullptr;
+ }
+ }
+ return bWasCurVisible;
+}
+
+void vcl::Cursor::ImplShow()
+{
+ ImplDoShow( true/*bDrawDirect*/, false );
+}
+
+void vcl::Cursor::ImplHide()
+{
+ ImplDoHide( false );
+}
+
+void vcl::Cursor::ImplResume( bool bRestore )
+{
+ ImplDoShow( false, bRestore );
+}
+
+bool vcl::Cursor::ImplSuspend()
+{
+ return ImplDoHide( true );
+}
+
+void vcl::Cursor::ImplNew()
+{
+ if ( !(mbVisible && mpData && mpData->mpWindow) )
+ return;
+
+ if ( mpData->mbCurVisible )
+ ImplRestore();
+
+ ImplDraw();
+ if ( !mpWindow )
+ {
+ LOKNotify( mpData->mpWindow, "cursor_invalidate" );
+ if ( mpData->maTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME )
+ mpData->maTimer.Start();
+ }
+}
+
+IMPL_LINK_NOARG(vcl::Cursor, ImplTimerHdl, Timer *, void)
+{
+ if ( mpData->mbCurVisible )
+ ImplRestore();
+ else
+ ImplDraw();
+}
+
+vcl::Cursor::Cursor()
+{
+ mpData = nullptr;
+ mpWindow = nullptr;
+ mnOrientation = 0_deg10;
+ mnDirection = CursorDirection::NONE;
+ mnStyle = 0;
+ mbVisible = false;
+}
+
+vcl::Cursor::Cursor( const Cursor& rCursor ) :
+ maSize( rCursor.maSize ),
+ maPos( rCursor.maPos )
+{
+ mpData = nullptr;
+ mpWindow = nullptr;
+ mnOrientation = rCursor.mnOrientation;
+ mnDirection = rCursor.mnDirection;
+ mnStyle = 0;
+ mbVisible = rCursor.mbVisible;
+}
+
+vcl::Cursor::~Cursor()
+{
+ if (mpData && mpData->mbCurVisible)
+ ImplRestore();
+}
+
+void vcl::Cursor::SetStyle( sal_uInt16 nStyle )
+{
+ if ( mnStyle != nStyle )
+ {
+ mnStyle = nStyle;
+ ImplNew();
+ }
+}
+
+void vcl::Cursor::Show()
+{
+ if ( !mbVisible )
+ {
+ mbVisible = true;
+ ImplShow();
+ }
+}
+
+void vcl::Cursor::Hide()
+{
+ if ( mbVisible )
+ {
+ mbVisible = false;
+ ImplHide();
+ }
+}
+
+void vcl::Cursor::SetWindow( vcl::Window* pWindow )
+{
+ if ( mpWindow.get() != pWindow )
+ {
+ mpWindow = pWindow;
+ ImplNew();
+ }
+}
+
+void vcl::Cursor::SetPos( const Point& rPoint )
+{
+ if ( maPos != rPoint )
+ {
+ maPos = rPoint;
+ ImplNew();
+ }
+}
+
+void vcl::Cursor::SetSize( const Size& rSize )
+{
+ if ( maSize != rSize )
+ {
+ maSize = rSize;
+ ImplNew();
+ }
+}
+
+void vcl::Cursor::SetWidth( tools::Long nNewWidth )
+{
+ if ( maSize.Width() != nNewWidth )
+ {
+ maSize.setWidth( nNewWidth );
+ ImplNew();
+ }
+}
+
+void vcl::Cursor::SetOrientation( Degree10 nNewOrientation )
+{
+ if ( mnOrientation != nNewOrientation )
+ {
+ mnOrientation = nNewOrientation;
+ ImplNew();
+ }
+}
+
+void vcl::Cursor::SetDirection( CursorDirection nNewDirection )
+{
+ if ( mnDirection != nNewDirection )
+ {
+ mnDirection = nNewDirection;
+ ImplNew();
+ }
+}
+
+vcl::Cursor& vcl::Cursor::operator=( const vcl::Cursor& rCursor )
+{
+ maPos = rCursor.maPos;
+ maSize = rCursor.maSize;
+ mnOrientation = rCursor.mnOrientation;
+ mnDirection = rCursor.mnDirection;
+ mbVisible = rCursor.mbVisible;
+ ImplNew();
+
+ return *this;
+}
+
+bool vcl::Cursor::operator==( const vcl::Cursor& rCursor ) const
+{
+ return
+ ((maPos == rCursor.maPos) &&
+ (maSize == rCursor.maSize) &&
+ (mnOrientation == rCursor.mnOrientation) &&
+ (mnDirection == rCursor.mnDirection) &&
+ (mbVisible == rCursor.mbVisible))
+ ;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/debug.cxx b/vcl/source/window/debug.cxx
new file mode 100644
index 0000000000..3464569238
--- /dev/null
+++ b/vcl/source/window/debug.cxx
@@ -0,0 +1,48 @@
+/* -*- 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 <vcl/window.hxx>
+#include <tools/debug.hxx>
+
+#include <window.h>
+
+#ifdef DBG_UTIL
+const char* ImplDbgCheckWindow(const void* pObj)
+{
+ DBG_TESTSOLARMUTEX();
+
+ const vcl::Window* pWindow = static_cast<vcl::Window const*>(pObj);
+
+ if ((pWindow->GetType() < WindowType::FIRST) || (pWindow->GetType() > WindowType::LAST))
+ return "Window data overwrite";
+
+ // check window-chain
+ vcl::Window* pChild = pWindow->mpWindowImpl->mpFirstChild;
+ while (pChild)
+ {
+ if (pChild->mpWindowImpl->mpParent != pWindow)
+ return "Child-Window-Parent wrong";
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+
+ return nullptr;
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/debugevent.cxx b/vcl/source/window/debugevent.cxx
new file mode 100644
index 0000000000..f9f6978f0e
--- /dev/null
+++ b/vcl/source/window/debugevent.cxx
@@ -0,0 +1,271 @@
+/* -*- 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/.
+ */
+
+#include <comphelper/random.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/string.hxx>
+#include <sal/log.hxx>
+#include <vcl/keycodes.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/wrkwin.hxx>
+#include <vcl/menu.hxx>
+#include <debugevent.hxx>
+#include <window.h>
+#include <salwtype.hxx>
+
+DebugEventInjector::DebugEventInjector( sal_uInt32 nMaxEvents) :
+ Timer("debug event injector")
+ , mnEventsLeft( nMaxEvents )
+{
+ SetTimeout( 1000 /* ms */ );
+ Start();
+}
+
+static double getRandom()
+{
+ return comphelper::rng::uniform_real_distribution();
+}
+
+vcl::Window *DebugEventInjector::ChooseWindow()
+{
+ vcl::Window *pParent;
+
+ if (getRandom() < 0.80)
+ if (vcl::Window * pWindow = Application::GetFocusWindow())
+ return pWindow;
+
+ if (getRandom() > 0.50 ||
+ !(pParent = Application::GetActiveTopWindow()))
+ {
+ // select a top window at random
+ tools::Long nIdx = Application::GetTopWindowCount() * getRandom();
+ pParent = Application::GetTopWindow( nIdx );
+ }
+ assert (pParent != nullptr);
+
+ std::vector< vcl::Window *> aChildren;
+ pParent->CollectChildren( aChildren );
+
+ return aChildren[ aChildren.size() * getRandom() ];
+}
+
+
+static void CollectMenuItemIds( Menu *pMenu, std::vector< SalMenuEvent > &rIds )
+{
+ sal_uInt16 nItems = pMenu->GetItemCount();
+ for (sal_uInt16 i = 0; i < nItems; i++)
+ {
+ if (pMenu->GetItemType( i ) != MenuItemType::SEPARATOR || getRandom() < 0.01)
+ rIds.emplace_back( pMenu->GetItemId( i ), pMenu );
+ PopupMenu *pPopup = pMenu->GetPopupMenu( i );
+ if (pPopup)
+ CollectMenuItemIds( pPopup, rIds );
+ }
+}
+
+void DebugEventInjector::InjectMenuEvent()
+{
+ vcl::Window *pFocus = Application::GetFocusWindow();
+ if (!pFocus)
+ return;
+
+ SystemWindow *pSysWin = pFocus->GetSystemWindow();
+ if (!pSysWin)
+ return;
+
+ MenuBar *pMenuBar = pSysWin->GetMenuBar();
+ if (!pMenuBar)
+ return;
+
+ SalEvent nEvents[] = {
+ SalEvent::MenuCommand,
+ SalEvent::MenuCommand,
+ SalEvent::MenuActivate,
+ SalEvent::MenuDeactivate,
+ SalEvent::MenuHighlight,
+ SalEvent::MenuCommand,
+ SalEvent::MenuCommand,
+ SalEvent::MenuCommand,
+ SalEvent::MenuButtonCommand,
+ SalEvent::MenuButtonCommand,
+ };
+
+ std::vector< SalMenuEvent > aIds;
+ CollectMenuItemIds( pMenuBar, aIds );
+
+ SalEvent nEvent = nEvents[ static_cast<int>(getRandom() * SAL_N_ELEMENTS( nEvents )) ];
+ SalMenuEvent aEvent = aIds[ getRandom() * aIds.size() ];
+ bool bHandled = ImplWindowFrameProc( pSysWin, nEvent, &aEvent);
+
+ SAL_INFO( "vcl.debugevent",
+ "Injected menu event " << aEvent.mpMenu
+ << " (" << aEvent.mnId << ") '"
+ << static_cast<Menu *>(aEvent.mpMenu)->GetItemText( aEvent.mnId ) << "' -> "
+ << bHandled );
+}
+
+static void InitKeyEvent( SalKeyEvent &rKeyEvent )
+{
+ if (getRandom() < 0.01)
+ rKeyEvent.mnRepeat = getRandom() * 20;
+ else
+ rKeyEvent.mnRepeat = 0;
+}
+
+void DebugEventInjector::InjectTextEvent()
+{
+ SalKeyEvent aKeyEvent;
+ vcl::Window *pWindow = ChooseWindow();
+
+ InitKeyEvent( aKeyEvent );
+
+ if (getRandom() < 0.10) // Occasionally a truly random event
+ {
+ aKeyEvent.mnCode = getRandom() * KEY_CODE_MASK;
+ aKeyEvent.mnCharCode = getRandom() * 0xffff;
+ }
+ else
+ {
+ static struct {
+ sal_uInt16 nCodeStart, nCodeEnd;
+ char aCharStart;
+ } const nTextCodes[] = {
+ { KEY_0, KEY_9, '0' },
+ { KEY_A, KEY_Z, 'a' }
+ };
+
+ size_t i = getRandom() * SAL_N_ELEMENTS( nTextCodes );
+ int offset = int( getRandom() * ( nTextCodes[i].nCodeEnd - nTextCodes[i].nCodeStart ) );
+ aKeyEvent.mnCode = nTextCodes[i].nCodeStart + offset;
+ aKeyEvent.mnCharCode = nTextCodes[i].aCharStart + offset;
+// fprintf( stderr, "Char '%c' offset %d into record %d base '%c'\n",
+// aKeyEvent.mnCharCode, offset, (int)i, nTextCodes[i].aCharStart );
+ }
+
+ if( getRandom() < 0.05 ) // modifier
+ aKeyEvent.mnCode |= static_cast<sal_uInt16>( getRandom() * KEY_MODIFIERS_MASK ) & KEY_MODIFIERS_MASK;
+
+ bool bHandled = ImplWindowFrameProc( pWindow, SalEvent::KeyInput, &aKeyEvent);
+
+ SAL_INFO( "vcl.debugevent",
+ "Injected key 0x" << std::hex << static_cast<int>(aKeyEvent.mnCode) << std::dec
+ << " -> " << bHandled
+ << " win " << pWindow );
+
+ ImplWindowFrameProc( pWindow, SalEvent::KeyUp, &aKeyEvent );
+}
+
+/*
+ * The more heuristics we have to inform this the better,
+ * key-bindings, menu entries, allowable entry types etc.
+ */
+void DebugEventInjector::InjectEvent()
+{
+// fprintf( stderr, "%6d - ", (int)mnEventsLeft );
+
+ double nRand = getRandom();
+ if (nRand < 0.30)
+ {
+ int nEvents = getRandom() * 10;
+ for (int i = 0; i < nEvents; i++)
+ InjectTextEvent();
+ }
+ else if (nRand < 0.60)
+ InjectKeyNavEdit();
+ else if (nRand < 0.95)
+ InjectMenuEvent();
+}
+
+void DebugEventInjector::InjectKeyNavEdit()
+{
+ vcl::Window *pWindow = ChooseWindow();
+
+ static struct {
+ double mnProb;
+ sal_uInt16 mnKey;
+ } const nWeights[] = {
+ // edit / escape etc. - 50%
+ { 0.20, KEY_SPACE },
+ { 0.10, KEY_TAB },
+ { 0.07, KEY_RETURN },
+ { 0.05, KEY_DELETE },
+ { 0.05, KEY_BACKSPACE },
+
+ // navigate - 45%
+ { 0.15, KEY_LEFT },
+ { 0.10, KEY_RIGHT },
+ { 0.05, KEY_UP },
+ { 0.05, KEY_DOWN },
+ { 0.05, KEY_PAGEUP },
+ { 0.05, KEY_PAGEDOWN },
+
+ // other
+ { 0.01, KEY_INSERT },
+ { 0.02, KEY_HOME },
+ { 0.02, KEY_END },
+ };
+
+ double d = 0.0, nRand = getRandom();
+ sal_uInt16 nKey = KEY_SPACE;
+ for (auto & rWeight : nWeights)
+ {
+ d += rWeight.mnProb;
+ assert (d < 1.01);
+ if ( nRand < d )
+ {
+ nKey = rWeight.mnKey;
+ break;
+ }
+ }
+
+ SalKeyEvent aKeyEvent;
+ InitKeyEvent( aKeyEvent );
+ aKeyEvent.mnCode = nKey;
+
+ if (getRandom() < 0.15) // modifier
+ aKeyEvent.mnCode |= static_cast<sal_uInt16>(getRandom() * KEY_MODIFIERS_MASK) & KEY_MODIFIERS_MASK;
+
+ aKeyEvent.mnCharCode = 0x0; // hopefully unused.
+
+ bool bHandled = ImplWindowFrameProc( pWindow, SalEvent::KeyInput, &aKeyEvent );
+
+ SAL_INFO( "vcl.debugevent",
+ "Injected edit / move key 0x" << std::hex << static_cast<int>(aKeyEvent.mnCode) << std::dec
+ << " -> " << bHandled
+ << " win " << pWindow );
+ ImplWindowFrameProc( pWindow, SalEvent::KeyUp, &aKeyEvent );
+}
+
+void DebugEventInjector::Invoke()
+{
+ InjectEvent();
+ mnEventsLeft--;
+ if (mnEventsLeft > 0)
+ {
+ SetTimeout( 1 );
+ Start();
+ }
+ else
+ Application::Quit();
+}
+
+DebugEventInjector *DebugEventInjector::getCreate()
+{
+ sal_uInt32 nEvents;
+ const char *pEvents = getenv("VCL_EVENT_INJECTION");
+ if (!pEvents)
+ return nullptr;
+ nEvents = o3tl::toUInt32( pEvents );
+ if (nEvents > 0)
+ return new DebugEventInjector( nEvents );
+ else
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/decoview.cxx b/vcl/source/window/decoview.cxx
new file mode 100644
index 0000000000..2067a9e4ca
--- /dev/null
+++ b/vcl/source/window/decoview.cxx
@@ -0,0 +1,1020 @@
+/* -*- 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 <vcl/settings.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/window.hxx>
+#include <vcl/ctrl.hxx>
+
+namespace {
+
+tools::Long AdjustRectToSquare( tools::Rectangle &rRect )
+{
+ const tools::Long nWidth = rRect.GetWidth();
+ const tools::Long nHeight = rRect.GetHeight();
+ tools::Long nSide = std::min( nWidth, nHeight );
+
+ if ( nSide && !(nSide & 1) )
+ {
+ // we prefer an odd size
+ --nSide;
+ }
+
+ // Make the rectangle a square
+ rRect.SetSize( Size( nSide, nSide ) );
+
+ // and place it at the center of the original rectangle
+ rRect.Move( (nWidth-nSide)/2, (nHeight-nSide)/2 );
+
+ return nSide;
+}
+
+void ImplDrawSymbol( OutputDevice* pDev, tools::Rectangle nRect, const SymbolType eType )
+{
+ const tools::Long nSide = AdjustRectToSquare( nRect );
+
+ if ( !nSide ) return;
+ if ( nSide==1 )
+ {
+ pDev->DrawPixel( Point( nRect.Left(), nRect.Top() ) );
+ return;
+ }
+
+ // Precalculate some values
+ const tools::Long n2 = nSide/2;
+ const tools::Long n4 = (n2+1)/2;
+ const tools::Long n8 = (n4+1)/2;
+ const tools::Long n16 = (n8+1)/2;
+ const Point aCenter = nRect.Center();
+
+ switch ( eType )
+ {
+ case SymbolType::ARROW_UP:
+ {
+ tools::Polygon arrow(7);
+ arrow.SetPoint( Point( aCenter.X(), nRect.Top()), 0 );
+ arrow.SetPoint( Point( aCenter.X() - n2, nRect.Top() + n2 ), 1 );
+ arrow.SetPoint( Point( aCenter.X() - n8, nRect.Top() + n2 ), 2 );
+ arrow.SetPoint( Point( aCenter.X() - n8, nRect.Bottom()), 3 );
+ arrow.SetPoint( Point( aCenter.X() + n8, nRect.Bottom()), 4 );
+ arrow.SetPoint( Point( aCenter.X() + n8, nRect.Top() + n2 ), 5 );
+ arrow.SetPoint( Point( aCenter.X() + n2, nRect.Top() + n2 ), 6 );
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+ pDev->SetLineColor();
+ pDev->DrawPolygon( arrow );
+ pDev->Pop();
+ break;
+ }
+
+ case SymbolType::ARROW_DOWN:
+ {
+ tools::Polygon arrow(7);
+ arrow.SetPoint( Point( aCenter.X(), nRect.Bottom()), 0 );
+ arrow.SetPoint( Point( aCenter.X() - n2, nRect.Bottom() - n2 ), 1 );
+ arrow.SetPoint( Point( aCenter.X() - n8, nRect.Bottom() - n2 ), 2 );
+ arrow.SetPoint( Point( aCenter.X() - n8, nRect.Top()), 3 );
+ arrow.SetPoint( Point( aCenter.X() + n8, nRect.Top()), 4 );
+ arrow.SetPoint( Point( aCenter.X() + n8, nRect.Bottom() - n2 ), 5 );
+ arrow.SetPoint( Point( aCenter.X() + n2, nRect.Bottom() - n2 ), 6 );
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+ pDev->SetLineColor();
+ pDev->DrawPolygon( arrow );
+ pDev->Pop();
+ break;
+ }
+
+ case SymbolType::ARROW_LEFT:
+ {
+ tools::Polygon arrow(7);
+ arrow.SetPoint( Point( nRect.Left(), aCenter.Y()), 0 );
+ arrow.SetPoint( Point( nRect.Left() + n2, aCenter.Y() - n2 ), 1 );
+ arrow.SetPoint( Point( nRect.Left() + n2, aCenter.Y() - n8 ), 2 );
+ arrow.SetPoint( Point( nRect.Right(), aCenter.Y() - n8 ), 3 );
+ arrow.SetPoint( Point( nRect.Right(), aCenter.Y() + n8 ), 4 );
+ arrow.SetPoint( Point( nRect.Left() + n2, aCenter.Y() + n8 ), 5 );
+ arrow.SetPoint( Point( nRect.Left() + n2, aCenter.Y() + n2 ), 6 );
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+ pDev->SetLineColor();
+ pDev->DrawPolygon( arrow );
+ pDev->Pop();
+ break;
+ }
+
+ case SymbolType::ARROW_RIGHT:
+ {
+ tools::Polygon arrow(7);
+ arrow.SetPoint( Point( nRect.Right(), aCenter.Y()), 0 );
+ arrow.SetPoint( Point( nRect.Right() - n2, aCenter.Y() - n2 ), 1 );
+ arrow.SetPoint( Point( nRect.Right() - n2, aCenter.Y() - n8 ), 2 );
+ arrow.SetPoint( Point( nRect.Left(), aCenter.Y() - n8 ), 3 );
+ arrow.SetPoint( Point( nRect.Left(), aCenter.Y() + n8 ), 4 );
+ arrow.SetPoint( Point( nRect.Right() - n2, aCenter.Y() + n8 ), 5 );
+ arrow.SetPoint( Point( nRect.Right() - n2, aCenter.Y() + n2 ), 6 );
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+ pDev->SetLineColor();
+ pDev->DrawPolygon( arrow );
+ pDev->Pop();
+ break;
+ }
+
+ case SymbolType::SPIN_UP:
+ {
+ tools::Polygon triangle( 3 );
+ triangle.SetPoint( Point( aCenter.X(), nRect.Top() + n4 ), 0 );
+ triangle.SetPoint( Point( aCenter.X() - n2, nRect.Top() + n4 + n2 ), 1 );
+ triangle.SetPoint( Point( aCenter.X() + n2, nRect.Top() + n4 + n2 ), 2 );
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+ pDev->SetLineColor();
+ pDev->DrawPolygon( triangle );
+ pDev->Pop();
+ break;
+ }
+
+ case SymbolType::SPIN_DOWN:
+ {
+ tools::Polygon triangle( 3 );
+ triangle.SetPoint( Point( aCenter.X(), nRect.Bottom() - n4 ), 0 );
+ triangle.SetPoint( Point( aCenter.X() - n2, nRect.Bottom() - n4 - n2 ), 1 );
+ triangle.SetPoint( Point( aCenter.X() + n2, nRect.Bottom() - n4 - n2 ), 2 );
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+ pDev->SetLineColor();
+ pDev->DrawPolygon( triangle );
+ pDev->Pop();
+ break;
+ }
+
+ case SymbolType::SPIN_LEFT:
+ case SymbolType::FIRST:
+ case SymbolType::PREV:
+ {
+ nRect.AdjustLeft(n4 );
+ if ( eType==SymbolType::FIRST )
+ {
+ pDev->DrawLine( Point( nRect.Left(), nRect.Top() ),
+ Point( nRect.Left(), nRect.Bottom() ) );
+ nRect.AdjustLeft( 1 );
+ }
+
+ tools::Polygon aTriangle(3);
+ aTriangle.SetPoint(Point(nRect.Left() + n2, aCenter.Y() - n2), 0);
+ aTriangle.SetPoint(Point(nRect.Left(), aCenter.Y()), 1);
+ aTriangle.SetPoint(Point(nRect.Left() + n2, aCenter.Y() + n2), 2);
+
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+ pDev->SetLineColor();
+ pDev->DrawPolygon(aTriangle);
+ pDev->Pop();
+
+ break;
+ }
+
+ case SymbolType::SPIN_RIGHT:
+ case SymbolType::LAST:
+ case SymbolType::NEXT:
+ case SymbolType::PLAY:
+ {
+ nRect.AdjustRight( -n4 );
+ if ( eType==SymbolType::LAST )
+ {
+ pDev->DrawLine( Point( nRect.Right(), nRect.Top() ),
+ Point( nRect.Right(), nRect.Bottom() ) );
+ nRect.AdjustRight( -1 );
+ }
+
+ tools::Polygon aTriangle(3);
+ aTriangle.SetPoint(Point(nRect.Right() - n2, aCenter.Y() - n2), 0);
+ aTriangle.SetPoint(Point(nRect.Right(), aCenter.Y()), 1);
+ aTriangle.SetPoint(Point(nRect.Right() - n2, aCenter.Y() + n2), 2);
+
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+ pDev->SetLineColor();
+ pDev->DrawPolygon(aTriangle);
+ pDev->Pop();
+ break;
+ }
+
+ case SymbolType::PAGEUP:
+ {
+ tools::Polygon triangle( 3 );
+ triangle.SetPoint( Point( aCenter.X(), nRect.Top()), 0 );
+ triangle.SetPoint( Point( aCenter.X() - n2, nRect.Top() + n2 ), 1 );
+ triangle.SetPoint( Point( aCenter.X() + n2, nRect.Top() + n2 ), 2 );
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+ pDev->SetLineColor();
+ pDev->DrawPolygon( triangle );
+ triangle.Move( 0, n2 );
+ pDev->DrawPolygon( triangle );
+ pDev->Pop();
+ break;
+ }
+
+ case SymbolType::PAGEDOWN:
+ {
+ tools::Polygon triangle( 3 );
+ triangle.SetPoint( Point( aCenter.X(), nRect.Bottom()), 0 );
+ triangle.SetPoint( Point( aCenter.X() - n2, nRect.Bottom() - n2 ), 1 );
+ triangle.SetPoint( Point( aCenter.X() + n2, nRect.Bottom() - n2 ), 2 );
+ pDev->Push(vcl::PushFlags::LINECOLOR);
+ pDev->SetLineColor();
+ pDev->DrawPolygon( triangle );
+ triangle.Move( 0, -n2 );
+ pDev->DrawPolygon( triangle );
+ pDev->Pop();
+ break;
+ }
+
+ case SymbolType::RADIOCHECKMARK:
+ {
+ pDev->DrawEllipse(nRect);
+ break;
+ }
+
+ case SymbolType::STOP:
+ pDev->DrawRect( nRect );
+ break;
+
+ case SymbolType::CLOSE:
+ {
+ const tools::Long diff = std::max<tools::Long>( 0, n8 - 1 );
+ tools::Polygon cross( 16 );
+ cross.SetPoint( Point( nRect.Left(), nRect.Top()), 0 );
+ cross.SetPoint( Point( nRect.Left(), nRect.Top() + diff ), 1 );
+ cross.SetPoint( Point( aCenter.X() - diff, aCenter.Y()), 2 );
+ cross.SetPoint( Point( nRect.Left(), nRect.Bottom() - diff ), 3 );
+ cross.SetPoint( Point( nRect.Left(), nRect.Bottom()), 4 );
+ cross.SetPoint( Point( nRect.Left() + diff, nRect.Bottom()), 5 );
+ cross.SetPoint( Point( aCenter.X(), aCenter.Y() + diff ), 6 );
+ cross.SetPoint( Point( nRect.Right() - diff, nRect.Bottom()), 7 );
+ cross.SetPoint( Point( nRect.Right(), nRect.Bottom()), 8 );
+ cross.SetPoint( Point( nRect.Right(), nRect.Bottom() - diff ), 9 );
+ cross.SetPoint( Point( aCenter.X() + diff, aCenter.Y()), 10 );
+ cross.SetPoint( Point( nRect.Right(), nRect.Top() + diff ), 11 );
+ cross.SetPoint( Point( nRect.Right(), nRect.Top()), 12 );
+ cross.SetPoint( Point( nRect.Right() - diff, nRect.Top()), 13 );
+ cross.SetPoint( Point( aCenter.X(), aCenter.Y() - diff ), 14 );
+ cross.SetPoint( Point( nRect.Left() + diff, nRect.Top()), 15 );
+ pDev->DrawPolygon( cross );
+ break;
+ }
+
+ case SymbolType::CHECKMARK:
+ {
+ tools::Long n3 = nSide/3;
+ nRect.AdjustTop( -(n3/2) );
+ nRect.AdjustBottom( -(n3/2) );
+ tools::Polygon checkmark(6);
+ // #106953# never mirror checkmarks
+ if ( pDev->HasMirroredGraphics() && pDev->IsRTLEnabled() )
+ {
+ // Draw a mirrored checkmark so that it looks "normal" in a
+ // mirrored graphics device (double mirroring!)
+ checkmark.SetPoint( Point( nRect.Right(), nRect.Bottom()-n3 ), 0 );
+ checkmark.SetPoint( Point( nRect.Right()-n3, nRect.Bottom()), 1 );
+ checkmark.SetPoint( Point( nRect.Left(), nRect.Top()+n3 ), 2 );
+ checkmark.SetPoint( Point( nRect.Left(), nRect.Top()+n3 + 1 ), 3 );
+ checkmark.SetPoint( Point( nRect.Right()-n3, nRect.Bottom() + 1 ), 4 );
+ checkmark.SetPoint( Point( nRect.Right(), nRect.Bottom()-n3 + 1 ), 5 );
+ }
+ else
+ {
+ checkmark.SetPoint( Point( nRect.Left(), nRect.Bottom()-n3 ), 0 );
+ checkmark.SetPoint( Point( nRect.Left()+n3, nRect.Bottom()), 1 );
+ checkmark.SetPoint( Point( nRect.Right(), nRect.Top()+n3 ), 2 );
+ checkmark.SetPoint( Point( nRect.Right(), nRect.Top()+n3 + 1 ), 3 );
+ checkmark.SetPoint( Point( nRect.Left()+n3, nRect.Bottom() + 1 ), 4 );
+ checkmark.SetPoint( Point( nRect.Left(), nRect.Bottom()-n3 + 1 ), 5 );
+ }
+ pDev->DrawPolygon( checkmark );
+ }
+ break;
+
+ case SymbolType::FLOAT:
+ nRect.AdjustRight( -n4 );
+ nRect.AdjustTop(n4+1 );
+ pDev->DrawRect( tools::Rectangle( nRect.Left(), nRect.Top(),
+ nRect.Right(), nRect.Top()+n8 ) );
+ pDev->DrawLine( Point( nRect.Left(), nRect.Top()+n8 ),
+ Point( nRect.Left(), nRect.Bottom() ) );
+ pDev->DrawLine( Point( nRect.Left(), nRect.Bottom() ),
+ Point( nRect.Right(), nRect.Bottom() ) );
+ pDev->DrawLine( Point( nRect.Right(), nRect.Top()+n8 ),
+ Point( nRect.Right(), nRect.Bottom() ) );
+ nRect.AdjustRight(n4 );
+ nRect.AdjustTop( -(n4+1) );
+ nRect.AdjustLeft(n4 );
+ nRect.AdjustBottom( -(n4+1) );
+ pDev->DrawRect( tools::Rectangle( nRect.Left(), nRect.Top(),
+ nRect.Right(), nRect.Top()+n8 ) );
+ pDev->DrawLine( Point( nRect.Left(), nRect.Top()+n8 ),
+ Point( nRect.Left(), nRect.Bottom() ) );
+ pDev->DrawLine( Point( nRect.Left(), nRect.Bottom() ),
+ Point( nRect.Right(), nRect.Bottom() ) );
+ pDev->DrawLine( Point( nRect.Right(), nRect.Top()+n8 ),
+ Point( nRect.Right(), nRect.Bottom() ) );
+ break;
+
+ case SymbolType::DOCK:
+ pDev->DrawLine( Point( nRect.Left(), nRect.Top() ),
+ Point( nRect.Right(), nRect.Top() ) );
+ pDev->DrawLine( Point( nRect.Left(), nRect.Top() ),
+ Point( nRect.Left(), nRect.Bottom() ) );
+ pDev->DrawLine( Point( nRect.Left(), nRect.Bottom() ),
+ Point( nRect.Right(), nRect.Bottom() ) );
+ pDev->DrawLine( Point( nRect.Right(), nRect.Top() ),
+ Point( nRect.Right(), nRect.Bottom() ) );
+ break;
+
+ case SymbolType::HIDE:
+ pDev->DrawRect( tools::Rectangle( nRect.Left()+n8, nRect.Bottom()-n8,
+ nRect.Right()-n8, nRect.Bottom() ) );
+ break;
+
+ case SymbolType::PLUS:
+ pDev->DrawRect( tools::Rectangle( nRect.Left(), aCenter.Y()-n16,
+ nRect.Right(), aCenter.Y()+n16 ) );
+ pDev->DrawRect( tools::Rectangle( aCenter.X()-n16, nRect.Top(),
+ aCenter.X()+n16, nRect.Bottom() ) );
+ break;
+ case SymbolType::DONTKNOW:
+ case SymbolType::IMAGE:
+ case SymbolType::HELP: break;
+ }
+}
+
+void ImplDrawDPILineRect( OutputDevice *const pDev, tools::Rectangle& rRect,
+ const Color *const pColor, const bool bRound = false )
+{
+ tools::Long nLineWidth = pDev->GetDPIX()/300;
+ tools::Long nLineHeight = pDev->GetDPIY()/300;
+ if ( !nLineWidth )
+ nLineWidth = 1;
+ if ( !nLineHeight )
+ nLineHeight = 1;
+
+ if ( pColor )
+ {
+ if ( (nLineWidth == 1) && (nLineHeight == 1) )
+ {
+ pDev->SetLineColor( *pColor );
+ if( bRound )
+ {
+ pDev->DrawLine( Point( rRect.Left()+1, rRect.Top()), Point( rRect.Right()-1, rRect.Top()) );
+ pDev->DrawLine( Point( rRect.Left()+1, rRect.Bottom()), Point( rRect.Right()-1, rRect.Bottom()) );
+ pDev->DrawLine( Point( rRect.Left(), rRect.Top()+1), Point( rRect.Left(), rRect.Bottom()-1) );
+ pDev->DrawLine( Point( rRect.Right(), rRect.Top()+1), Point( rRect.Right(), rRect.Bottom()-1) );
+ }
+ else
+ {
+ pDev->SetFillColor();
+ pDev->DrawRect( rRect );
+ }
+ }
+ else
+ {
+ const tools::Long nWidth = rRect.GetWidth();
+ const tools::Long nHeight = rRect.GetHeight();
+ pDev->SetLineColor();
+ pDev->SetFillColor( *pColor );
+ pDev->DrawRect( tools::Rectangle( rRect.TopLeft(), Size( nWidth, nLineHeight ) ) );
+ pDev->DrawRect( tools::Rectangle( rRect.TopLeft(), Size( nLineWidth, nHeight ) ) );
+ pDev->DrawRect( tools::Rectangle( Point( rRect.Left(), rRect.Bottom()-nLineHeight ),
+ Size( nWidth, nLineHeight ) ) );
+ pDev->DrawRect( tools::Rectangle( Point( rRect.Right()-nLineWidth, rRect.Top() ),
+ Size( nLineWidth, nHeight ) ) );
+ }
+ }
+
+ rRect.AdjustLeft(nLineWidth );
+ rRect.AdjustTop(nLineHeight );
+ rRect.AdjustRight( -nLineWidth );
+ rRect.AdjustBottom( -nLineHeight );
+}
+
+void ImplDraw2ColorFrame( OutputDevice *const pDev, tools::Rectangle& rRect,
+ const Color& rLeftTopColor, const Color& rRightBottomColor )
+{
+ pDev->SetLineColor( rLeftTopColor );
+ pDev->DrawLine( rRect.TopLeft(), rRect.BottomLeft() );
+ pDev->DrawLine( rRect.TopLeft(), rRect.TopRight() );
+ pDev->SetLineColor( rRightBottomColor );
+ pDev->DrawLine( rRect.BottomLeft(), rRect.BottomRight() );
+ pDev->DrawLine( rRect.TopRight(), rRect.BottomRight() );
+
+ // reduce drawing area
+ rRect.AdjustLeft( 1 );
+ rRect.AdjustTop( 1 );
+ rRect.AdjustRight( -1 );
+ rRect.AdjustBottom( -1 );
+}
+
+void ImplDrawButton( OutputDevice *const pDev, tools::Rectangle aFillRect,
+ const DrawButtonFlags nStyle )
+{
+ const StyleSettings& rStyleSettings = pDev->GetSettings().GetStyleSettings();
+
+ if ( (nStyle & DrawButtonFlags::Mono) ||
+ (rStyleSettings.GetOptions() & StyleSettingsOptions::Mono) )
+ {
+ const Color aBlackColor(COL_BLACK);
+
+ if ( nStyle & DrawButtonFlags::Default )
+ {
+ // default selection shows a wider border
+ ImplDrawDPILineRect( pDev, aFillRect, &aBlackColor );
+ }
+
+ ImplDrawDPILineRect( pDev, aFillRect, &aBlackColor );
+
+ Size aBrdSize(pDev->GetButtonBorderSize());
+
+ pDev->SetLineColor();
+ pDev->SetFillColor( aBlackColor );
+ const tools::Rectangle aOrigFillRect(aFillRect);
+ if ( nStyle & (DrawButtonFlags::Pressed | DrawButtonFlags::Checked) )
+ {
+ // shrink fill rect
+ aFillRect.AdjustLeft(aBrdSize.Width() );
+ aFillRect.AdjustTop(aBrdSize.Height() );
+ // draw top and left borders (aOrigFillRect-aFillRect)
+ pDev->DrawRect( tools::Rectangle( aOrigFillRect.Left(), aOrigFillRect.Top(),
+ aOrigFillRect.Right(), aFillRect.Top()-1 ) );
+ pDev->DrawRect( tools::Rectangle( aOrigFillRect.Left(), aOrigFillRect.Top(),
+ aFillRect.Left()-1, aOrigFillRect.Bottom() ) );
+ }
+ else
+ {
+ // shrink fill rect
+ aFillRect.AdjustRight( -(aBrdSize.Width()) );
+ aFillRect.AdjustBottom( -(aBrdSize.Height()) );
+ // draw bottom and right borders (aOrigFillRect-aFillRect)
+ pDev->DrawRect( tools::Rectangle( aOrigFillRect.Left(), aFillRect.Bottom()+1,
+ aOrigFillRect.Right(), aOrigFillRect.Bottom() ) );
+ pDev->DrawRect( tools::Rectangle( aFillRect.Right()+1, aOrigFillRect.Top(),
+ aOrigFillRect.Right(), aOrigFillRect.Bottom() ) );
+ }
+
+ // Hack: in monochrome mode on printers we like to have grey buttons
+ pDev->SetFillColor(pDev->GetMonochromeButtonColor());
+ pDev->DrawRect( aFillRect );
+ }
+ else
+ {
+ const bool bFlat(nStyle & DrawButtonFlags::Flat);
+ const bool bDepressed(nStyle & (DrawButtonFlags::Pressed | DrawButtonFlags::Checked));
+
+ if ( nStyle & DrawButtonFlags::Default )
+ {
+ const Color aDefBtnColor = rStyleSettings.GetDarkShadowColor();
+ ImplDrawDPILineRect( pDev, aFillRect, &aDefBtnColor );
+ }
+
+ if ( nStyle & DrawButtonFlags::NoLeftLightBorder )
+ {
+ pDev->SetLineColor( rStyleSettings.GetLightBorderColor() );
+ pDev->DrawLine( Point( aFillRect.Left(), aFillRect.Top() ),
+ Point( aFillRect.Left(), aFillRect.Bottom() ) );
+ aFillRect.AdjustLeft( 1 );
+ }
+
+ bool bNoFace = false;
+ Color aColor1;
+ Color aColor2;
+ if (!bFlat)
+ {
+ if (bDepressed)
+ {
+ aColor1 = rStyleSettings.GetDarkShadowColor();
+ aColor2 = rStyleSettings.GetLightColor();
+ }
+ else
+ {
+ if ( nStyle & DrawButtonFlags::NoLightBorder )
+ aColor1 = rStyleSettings.GetLightBorderColor();
+ else
+ aColor1 = rStyleSettings.GetLightColor();
+ aColor2 = rStyleSettings.GetDarkShadowColor();
+ }
+
+ ImplDraw2ColorFrame( pDev, aFillRect, aColor1, aColor2 );
+
+ if (bDepressed)
+ {
+ aColor1 = rStyleSettings.GetShadowColor();
+ aColor2 = rStyleSettings.GetLightBorderColor();
+ }
+ else
+ {
+ if ( nStyle & DrawButtonFlags::NoLightBorder )
+ aColor1 = rStyleSettings.GetLightColor();
+ else
+ aColor1 = rStyleSettings.GetLightBorderColor();
+ aColor2 = rStyleSettings.GetShadowColor();
+ }
+ ImplDraw2ColorFrame( pDev, aFillRect, aColor1, aColor2 );
+ }
+ else // flat buttons
+ {
+ // draw a border if the flat button is highlighted
+ if (nStyle & DrawButtonFlags::Highlight)
+ {
+ aColor1 = rStyleSettings.GetShadowColor();
+ ImplDraw2ColorFrame(pDev, aFillRect, aColor1, aColor1);
+ }
+ // fill in the button if it is pressed in
+ bNoFace = !bDepressed;
+ }
+
+ pDev->SetLineColor();
+ if ( nStyle & (DrawButtonFlags::Checked | DrawButtonFlags::DontKnow) )
+ pDev->SetFillColor( rStyleSettings.GetCheckedColor() );
+ else if (!bNoFace)
+ pDev->SetFillColor( rStyleSettings.GetFaceColor() );
+ pDev->DrawRect( aFillRect );
+ }
+}
+
+void ImplDrawFrame( OutputDevice *const pDev, tools::Rectangle& rRect,
+ const StyleSettings& rStyleSettings, DrawFrameStyle nStyle, DrawFrameFlags nFlags )
+{
+ vcl::Window * pWin = pDev->GetOwnerWindow();
+
+ const bool bMenuStyle(nFlags & DrawFrameFlags::Menu);
+
+ // UseFlatBorders disables 3D style for all frames except menus
+ // menus may use different border colors (eg on XP)
+ // normal frames will be drawn using the shadow color
+ // whereas window frame borders will use black
+ bool bFlatBorders = !bMenuStyle && rStyleSettings.GetUseFlatBorders();
+
+ // no flat borders for standard VCL controls (ie formcontrols that keep their classic look)
+ // will not affect frame windows (like dropdowns)
+ if( bFlatBorders && pWin && pWin->GetType() == WindowType::BORDERWINDOW && (pWin != pWin->ImplGetFrameWindow()) )
+ {
+ // check for formcontrol, i.e., a control without NWF enabled
+ Control *const pControl = dynamic_cast< Control* >( pWin->GetWindow( GetWindowType::Client ) );
+ if( !pControl || !pControl->IsNativeWidgetEnabled() )
+ bFlatBorders = false;
+ }
+
+ const bool bNoDraw(nFlags & DrawFrameFlags::NoDraw);
+
+ if ( (rStyleSettings.GetOptions() & StyleSettingsOptions::Mono) ||
+ (pDev->GetOutDevType() == OUTDEV_PRINTER) ||
+ bFlatBorders )
+ nFlags |= DrawFrameFlags::Mono;
+
+ if( nStyle != DrawFrameStyle::NWF &&
+ pWin && pWin->IsNativeControlSupported(ControlType::Frame, ControlPart::Border) )
+ {
+ tools::Long nControlFlags = static_cast<tools::Long>(nStyle);
+ nControlFlags |= static_cast<tools::Long>(nFlags);
+ nControlFlags |= static_cast<tools::Long>(pWin->GetType() == WindowType::BORDERWINDOW ?
+ DrawFrameFlags::BorderWindowBorder : DrawFrameFlags::NONE);
+ ImplControlValue aControlValue( nControlFlags );
+
+ tools::Rectangle aBound, aContent;
+ tools::Rectangle aNatRgn( rRect );
+ if( pWin->GetNativeControlRegion(ControlType::Frame, ControlPart::Border,
+ aNatRgn, ControlState::NONE, aControlValue, aBound, aContent) )
+ {
+ // if bNoDraw is true then don't call the drawing routine
+ // but just update the target rectangle
+ if( bNoDraw ||
+ pWin->GetOutDev()->DrawNativeControl( ControlType::Frame, ControlPart::Border, aBound, ControlState::ENABLED,
+ aControlValue, OUString()) )
+ {
+ rRect = aContent;
+ return;
+ }
+ }
+ }
+
+ if ( nFlags & DrawFrameFlags::Mono )
+ {
+ // no round corners for window frame borders
+ const bool bRound = bFlatBorders && !(nFlags & DrawFrameFlags::WindowBorder);
+
+ if ( bNoDraw )
+ {
+ ImplDrawDPILineRect( pDev, rRect, nullptr, bRound );
+ }
+ else
+ {
+ Color aColor = bRound ? rStyleSettings.GetShadowColor()
+ : pDev->GetSettings().GetStyleSettings().GetMonoColor();
+ // when the MonoColor wasn't set, check face color
+ if (
+ (bRound && aColor.IsDark()) ||
+ (
+ (aColor == COL_BLACK) &&
+ pDev->GetSettings().GetStyleSettings().GetFaceColor().IsDark()
+ )
+ )
+ {
+ aColor = COL_WHITE;
+ }
+ ImplDrawDPILineRect( pDev, rRect, &aColor, bRound );
+ }
+ }
+ else
+ {
+ if ( bNoDraw )
+ {
+ switch ( nStyle )
+ {
+ case DrawFrameStyle::In:
+ case DrawFrameStyle::Out:
+ rRect.AdjustLeft( 1 );
+ rRect.AdjustTop( 1 );
+ rRect.AdjustRight( -1 );
+ rRect.AdjustBottom( -1 );
+ break;
+
+ case DrawFrameStyle::Group:
+ case DrawFrameStyle::DoubleIn:
+ case DrawFrameStyle::DoubleOut:
+ rRect.AdjustLeft(2 );
+ rRect.AdjustTop(2 );
+ rRect.AdjustRight( -2 );
+ rRect.AdjustBottom( -2 );
+ break;
+
+ case DrawFrameStyle::NWF:
+ // enough space for the native rendering
+ rRect.AdjustLeft(4 );
+ rRect.AdjustTop(4 );
+ rRect.AdjustRight( -4 );
+ rRect.AdjustBottom( -4 );
+ break;
+ default: break;
+ }
+ }
+ else
+ {
+ switch ( nStyle )
+ {
+ case DrawFrameStyle::Group:
+ pDev->SetFillColor();
+ pDev->SetLineColor( rStyleSettings.GetLightColor() );
+ pDev->DrawRect( tools::Rectangle( rRect.Left()+1, rRect.Top()+1,
+ rRect.Right(), rRect.Bottom() ) );
+ pDev->SetLineColor( rStyleSettings.GetShadowColor() );
+ pDev->DrawRect( tools::Rectangle( rRect.Left(), rRect.Top(),
+ rRect.Right()-1, rRect.Bottom()-1 ) );
+
+ // adjust target rectangle
+ rRect.AdjustLeft(2 );
+ rRect.AdjustTop(2 );
+ rRect.AdjustRight( -2 );
+ rRect.AdjustBottom( -2 );
+ break;
+
+ case DrawFrameStyle::In:
+ ImplDraw2ColorFrame( pDev, rRect,
+ rStyleSettings.GetShadowColor(),
+ rStyleSettings.GetLightColor() );
+ break;
+
+ case DrawFrameStyle::Out:
+ ImplDraw2ColorFrame( pDev, rRect,
+ rStyleSettings.GetLightColor(),
+ rStyleSettings.GetShadowColor() );
+ break;
+
+ case DrawFrameStyle::DoubleIn:
+ if( bFlatBorders )
+ {
+ // no 3d effect
+ ImplDraw2ColorFrame( pDev, rRect,
+ rStyleSettings.GetShadowColor(),
+ rStyleSettings.GetShadowColor() );
+ ImplDraw2ColorFrame( pDev, rRect,
+ rStyleSettings.GetFaceColor(),
+ rStyleSettings.GetFaceColor() );
+ }
+ else
+ {
+ ImplDraw2ColorFrame( pDev, rRect,
+ rStyleSettings.GetShadowColor(),
+ rStyleSettings.GetLightColor() );
+ ImplDraw2ColorFrame( pDev, rRect,
+ rStyleSettings.GetDarkShadowColor(),
+ rStyleSettings.GetLightBorderColor() );
+ }
+ break;
+
+ case DrawFrameStyle::DoubleOut:
+ if( bMenuStyle )
+ {
+ ImplDraw2ColorFrame( pDev, rRect,
+ rStyleSettings.GetMenuBorderColor(),
+ rStyleSettings.GetDarkShadowColor() );
+ if ( !rStyleSettings.GetUseFlatMenus() )
+ {
+ ImplDraw2ColorFrame( pDev, rRect,
+ rStyleSettings.GetLightColor(),
+ rStyleSettings.GetShadowColor() );
+ }
+ }
+ else
+ {
+ ImplDraw2ColorFrame( pDev, rRect,
+ bFlatBorders ? // no 3d effect
+ rStyleSettings.GetDarkShadowColor() :
+ rStyleSettings.GetLightBorderColor(),
+ rStyleSettings.GetDarkShadowColor() );
+ ImplDraw2ColorFrame( pDev, rRect,
+ rStyleSettings.GetLightColor(),
+ rStyleSettings.GetShadowColor() );
+ }
+ break;
+
+ case DrawFrameStyle::NWF:
+ // no rendering, just enough space for the native rendering
+ rRect.AdjustLeft(4 );
+ rRect.AdjustTop(4 );
+ rRect.AdjustRight( -4 );
+ rRect.AdjustBottom( -4 );
+ break;
+ default: break;
+ }
+ }
+ }
+}
+
+} // end anonymous namespace
+
+DecorationView::DecorationView(OutputDevice* pOutDev) :
+ mpOutDev(pOutDev)
+{}
+
+void DecorationView::DrawSymbol( const tools::Rectangle& rRect, SymbolType eType,
+ const Color& rColor, DrawSymbolFlags nStyle )
+{
+ const StyleSettings& rStyleSettings = mpOutDev->GetSettings().GetStyleSettings();
+ const tools::Rectangle aRect = mpOutDev->LogicToPixel( rRect );
+ const Color aOldLineColor = mpOutDev->GetLineColor();
+ const Color aOldFillColor = mpOutDev->GetFillColor();
+ const bool bOldMapMode = mpOutDev->IsMapModeEnabled();
+ Color nColor(rColor);
+ mpOutDev->EnableMapMode( false );
+
+ if ( (rStyleSettings.GetOptions() & StyleSettingsOptions::Mono) ||
+ (mpOutDev->GetOutDevType() == OUTDEV_PRINTER) )
+ nStyle |= DrawSymbolFlags::Mono;
+
+ if ( nStyle & DrawSymbolFlags::Mono )
+ {
+ // Monochrome: set color to black if enabled, to gray if disabled
+ nColor = ( nStyle & DrawSymbolFlags::Disable ) ? COL_GRAY : COL_BLACK;
+ }
+ else
+ {
+ if ( nStyle & DrawSymbolFlags::Disable )
+ {
+ // Draw shifted and brighter symbol for embossed look
+ mpOutDev->SetLineColor( rStyleSettings.GetLightColor() );
+ mpOutDev->SetFillColor( rStyleSettings.GetLightColor() );
+ ImplDrawSymbol( mpOutDev, aRect + Point(1, 1) , eType );
+ nColor = rStyleSettings.GetShadowColor();
+ }
+ }
+
+ // Set selected color and draw the symbol
+ mpOutDev->SetLineColor( nColor );
+ mpOutDev->SetFillColor( nColor );
+ ImplDrawSymbol( mpOutDev, aRect, eType );
+
+ // Restore previous settings
+ mpOutDev->SetLineColor( aOldLineColor );
+ mpOutDev->SetFillColor( aOldFillColor );
+ mpOutDev->EnableMapMode( bOldMapMode );
+}
+
+void DecorationView::DrawFrame( const tools::Rectangle& rRect,
+ const Color& rLeftTopColor,
+ const Color& rRightBottomColor )
+{
+ tools::Rectangle aRect = mpOutDev->LogicToPixel( rRect );
+ const Color aOldLineColor = mpOutDev->GetLineColor();
+ const bool bOldMapMode = mpOutDev->IsMapModeEnabled();
+ mpOutDev->EnableMapMode( false );
+ ImplDraw2ColorFrame( mpOutDev, aRect, rLeftTopColor, rRightBottomColor );
+ mpOutDev->SetLineColor( aOldLineColor );
+ mpOutDev->EnableMapMode( bOldMapMode );
+}
+
+void DecorationView::DrawHighlightFrame( const tools::Rectangle& rRect,
+ DrawHighlightFrameStyle nStyle )
+{
+ const StyleSettings& rStyleSettings = mpOutDev->GetSettings().GetStyleSettings();
+ Color aLightColor = rStyleSettings.GetLightColor();
+ Color aShadowColor = rStyleSettings.GetShadowColor();
+
+ if ( (rStyleSettings.GetOptions() & StyleSettingsOptions::Mono) ||
+ (mpOutDev->GetOutDevType() == OUTDEV_PRINTER) )
+ {
+ aLightColor = COL_BLACK;
+ aShadowColor = COL_BLACK;
+ }
+ else
+ {
+ Wallpaper aBackground = mpOutDev->GetBackground();
+ if ( aBackground.IsBitmap() || aBackground.IsGradient() )
+ {
+ aLightColor = rStyleSettings.GetFaceColor();
+ aShadowColor = COL_BLACK;
+ }
+ else
+ {
+ Color aBackColor = aBackground.GetColor();
+ if ( (aLightColor.GetColorError( aBackColor ) < 96) ||
+ (aShadowColor.GetColorError( aBackColor ) < 96) )
+ {
+ aLightColor = COL_WHITE;
+ aShadowColor = COL_BLACK;
+
+ if ( aLightColor.GetColorError( aBackColor ) < 96 )
+ aLightColor.DecreaseLuminance( 64 );
+ if ( aShadowColor.GetColorError( aBackColor ) < 96 )
+ aShadowColor.IncreaseLuminance( 64 );
+ }
+ }
+ }
+
+ if ( nStyle == DrawHighlightFrameStyle::In )
+ std::swap( aLightColor, aShadowColor );
+
+ DrawFrame( rRect, aLightColor, aShadowColor );
+}
+
+tools::Rectangle DecorationView::DrawFrame( const tools::Rectangle& rRect, DrawFrameStyle nStyle, DrawFrameFlags nFlags )
+{
+ tools::Rectangle aRect = mpOutDev->LogicToPixel( rRect );
+ bool bOldMap = mpOutDev->IsMapModeEnabled();
+ mpOutDev->EnableMapMode( false );
+
+ if ( !rRect.IsEmpty() )
+ {
+ if ( nFlags & DrawFrameFlags::NoDraw )
+ ImplDrawFrame( mpOutDev, aRect, mpOutDev->GetSettings().GetStyleSettings(), nStyle, nFlags );
+ else
+ {
+ Color aOldLineColor = mpOutDev->GetLineColor();
+ Color aOldFillColor = mpOutDev->GetFillColor();
+ ImplDrawFrame( mpOutDev, aRect, mpOutDev->GetSettings().GetStyleSettings(), nStyle, nFlags );
+ mpOutDev->SetLineColor( aOldLineColor );
+ mpOutDev->SetFillColor( aOldFillColor );
+ }
+ }
+
+ mpOutDev->EnableMapMode( bOldMap );
+ aRect = mpOutDev->PixelToLogic( aRect );
+
+ return aRect;
+}
+
+tools::Rectangle DecorationView::DrawButton( const tools::Rectangle& rRect, DrawButtonFlags nStyle )
+{
+ if ( rRect.IsEmpty() )
+ {
+ return rRect;
+ }
+
+ tools::Rectangle aRect = mpOutDev->LogicToPixel( rRect );
+ const bool bOldMap = mpOutDev->IsMapModeEnabled();
+ mpOutDev->EnableMapMode( false );
+
+ const Color aOldLineColor = mpOutDev->GetLineColor();
+ const Color aOldFillColor = mpOutDev->GetFillColor();
+ ImplDrawButton( mpOutDev, aRect, nStyle );
+ mpOutDev->SetLineColor( aOldLineColor );
+ mpOutDev->SetFillColor( aOldFillColor );
+
+ // keep border free, although it is used at default representation
+ aRect.AdjustLeft( 1 );
+ aRect.AdjustTop( 1 );
+ aRect.AdjustRight( -1 );
+ aRect.AdjustBottom( -1 );
+
+ if ( nStyle & DrawButtonFlags::NoLightBorder )
+ {
+ aRect.AdjustLeft( 1 );
+ aRect.AdjustTop( 1 );
+ }
+ else if ( nStyle & DrawButtonFlags::NoLeftLightBorder )
+ {
+ aRect.AdjustLeft( 1 );
+ }
+
+ if ( nStyle & DrawButtonFlags::Pressed )
+ {
+ if ( (aRect.GetHeight() > 10) && (aRect.GetWidth() > 10) )
+ {
+ aRect.AdjustLeft(4 );
+ aRect.AdjustTop(4 );
+ aRect.AdjustRight( -1 );
+ aRect.AdjustBottom( -1 );
+ }
+ else
+ {
+ aRect.AdjustLeft(3 );
+ aRect.AdjustTop(3 );
+ aRect.AdjustRight( -2 );
+ aRect.AdjustBottom( -2 );
+ }
+ }
+ else if ( nStyle & DrawButtonFlags::Checked )
+ {
+ aRect.AdjustLeft(3 );
+ aRect.AdjustTop(3 );
+ aRect.AdjustRight( -2 );
+ aRect.AdjustBottom( -2 );
+ }
+ else
+ {
+ aRect.AdjustLeft(2 );
+ aRect.AdjustTop(2 );
+ aRect.AdjustRight( -3 );
+ aRect.AdjustBottom( -3 );
+ }
+
+ mpOutDev->EnableMapMode( bOldMap );
+ aRect = mpOutDev->PixelToLogic( aRect );
+
+ return aRect;
+}
+
+void DecorationView::DrawSeparator( const Point& rStart, const Point& rStop, bool bVertical )
+{
+ Point aStart( rStart ), aStop( rStop );
+ const StyleSettings& rStyleSettings = mpOutDev->GetSettings().GetStyleSettings();
+ vcl::Window *const pWin = mpOutDev->GetOwnerWindow();
+ if(pWin)
+ {
+ ControlPart nPart = ( bVertical ? ControlPart::SeparatorVert : ControlPart::SeparatorHorz );
+ bool nativeSupported = pWin->IsNativeControlSupported( ControlType::Fixedline, nPart );
+ ImplControlValue aValue;
+ tools::Rectangle aRect(rStart,rStop);
+ if(nativeSupported && pWin->GetOutDev()->DrawNativeControl(ControlType::Fixedline,nPart,aRect,ControlState::NONE,aValue,OUString()))
+ return;
+ }
+
+ mpOutDev->Push( vcl::PushFlags::LINECOLOR );
+ if ( rStyleSettings.GetOptions() & StyleSettingsOptions::Mono )
+ mpOutDev->SetLineColor( COL_BLACK );
+ else
+ mpOutDev->SetLineColor( rStyleSettings.GetSeparatorColor() );
+
+ mpOutDev->DrawLine( aStart, aStop );
+
+ mpOutDev->Pop();
+}
+
+void DecorationView::DrawHandle(const tools::Rectangle& rRect)
+{
+ const StyleSettings& rStyleSettings = mpOutDev->GetSettings().GetStyleSettings();
+
+ Size aOutputSize = rRect.GetSize();
+
+ mpOutDev->SetLineColor(rStyleSettings.GetDarkShadowColor());
+ mpOutDev->SetFillColor(rStyleSettings.GetDarkShadowColor());
+
+ const sal_Int32 nNumberOfPoints = 3;
+
+ tools::Long nHalfWidth = aOutputSize.Width() / 2.0f;
+
+ float fDistance = aOutputSize.Height();
+ fDistance /= (nNumberOfPoints + 1);
+
+ tools::Long nRadius = aOutputSize.Width();
+ nRadius /= (nNumberOfPoints + 2);
+
+ for (tools::Long i = 1; i <= nNumberOfPoints; i++)
+ {
+ tools::Rectangle aLocation(nHalfWidth - nRadius,
+ round(fDistance * i) - nRadius,
+ nHalfWidth + nRadius,
+ round(fDistance * i) + nRadius);
+ mpOutDev->DrawEllipse(aLocation);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/dialog.cxx b/vcl/source/window/dialog.cxx
new file mode 100644
index 0000000000..83a8e8baba
--- /dev/null
+++ b/vcl/source/window/dialog.cxx
@@ -0,0 +1,1736 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_feature_desktop.h>
+
+#ifdef IOS
+#include <premac.h>
+#include <UIKit/UIKit.h>
+#include <postmac.h>
+#endif
+
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <comphelper/lok.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/processfactory.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <osl/diagnose.h>
+
+#include <svdata.hxx>
+#include <window.h>
+#include <accel.hxx>
+#include <brdwin.hxx>
+#include <salinst.hxx>
+
+#include <rtl/bootstrap.hxx>
+#include <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+
+#include <vcl/abstdlg.hxx>
+#include <vcl/builder.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/event.hxx>
+#include <vcl/locktoplevels.hxx>
+#include <vcl/wrkwin.hxx>
+#include <vcl/toolkit/button.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/toolkit/dialog.hxx>
+#include <vcl/dialoghelper.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/uitest/uiobject.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/IDialogRenderable.hxx>
+#include <messagedialog.hxx>
+#include <salframe.hxx>
+#include <tools/json_writer.hxx>
+
+#include <iostream>
+#include <stack>
+#include <utility>
+#include <vector>
+
+static OString ImplGetDialogText( Dialog* pDialog )
+{
+ OUString aErrorStr(pDialog->GetText());
+
+ OUString sMessage;
+ if (MessageDialog* pMessDialog = dynamic_cast<MessageDialog*>(pDialog))
+ {
+ sMessage = pMessDialog->get_primary_text();
+ }
+
+ if (!sMessage.isEmpty())
+ {
+ aErrorStr += ", " + sMessage;
+ }
+ return OUStringToOString(aErrorStr, RTL_TEXTENCODING_UTF8);
+}
+
+static bool ImplIsMnemonicCtrl( vcl::Window* pWindow )
+{
+ if( ! pWindow->GetSettings().GetStyleSettings().GetAutoMnemonic() )
+ return false;
+
+ if ( (pWindow->GetType() == WindowType::RADIOBUTTON) ||
+ (pWindow->GetType() == WindowType::CHECKBOX) ||
+ (pWindow->GetType() == WindowType::TRISTATEBOX) ||
+ (pWindow->GetType() == WindowType::PUSHBUTTON) )
+ return true;
+
+ if ( pWindow->GetType() == WindowType::FIXEDTEXT )
+ {
+ FixedText *pText = static_cast<FixedText*>(pWindow);
+ if (pText->get_mnemonic_widget())
+ return true;
+ //This is the legacy pre-layout logic which we retain
+ //until we can be sure we can remove it
+ if (pWindow->GetStyle() & WB_NOLABEL)
+ return false;
+ vcl::Window* pNextWindow = pWindow->GetWindow( GetWindowType::Next );
+ if ( !pNextWindow )
+ return false;
+ pNextWindow = pNextWindow->GetWindow( GetWindowType::Client );
+ return !(!(pNextWindow->GetStyle() & WB_TABSTOP) ||
+ (pNextWindow->GetType() == WindowType::FIXEDTEXT) ||
+ (pNextWindow->GetType() == WindowType::GROUPBOX) ||
+ (pNextWindow->GetType() == WindowType::RADIOBUTTON) ||
+ (pNextWindow->GetType() == WindowType::CHECKBOX) ||
+ (pNextWindow->GetType() == WindowType::TRISTATEBOX) ||
+ (pNextWindow->GetType() == WindowType::PUSHBUTTON));
+ }
+
+ return false;
+}
+
+// Called by native error dialog popup implementations
+void ImplHideSplash()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ if( pSVData->mpIntroWindow )
+ pSVData->mpIntroWindow->Hide();
+}
+
+vcl::Window * nextLogicalChildOfParent(const vcl::Window *pTopLevel, const vcl::Window *pChild)
+{
+ const vcl::Window *pLastChild = pChild;
+
+ if (pChild->GetType() == WindowType::SCROLLWINDOW)
+ pChild = static_cast<const VclScrolledWindow*>(pChild)->get_child();
+ else if (isContainerWindow(*pChild))
+ pChild = pChild->GetWindow(GetWindowType::FirstChild);
+ else
+ pChild = pChild->GetWindow(GetWindowType::Next);
+
+ while (!pChild)
+ {
+ vcl::Window *pParent = pLastChild->GetParent();
+ if (!pParent)
+ return nullptr;
+ if (pParent == pTopLevel)
+ return nullptr;
+ pLastChild = pParent;
+ pChild = pParent->GetWindow(GetWindowType::Next);
+ }
+
+ if (isContainerWindow(*pChild))
+ pChild = nextLogicalChildOfParent(pTopLevel, pChild);
+
+ return const_cast<vcl::Window *>(pChild);
+}
+
+vcl::Window * prevLogicalChildOfParent(const vcl::Window *pTopLevel, const vcl::Window *pChild)
+{
+ const vcl::Window *pLastChild = pChild;
+
+ if (pChild->GetType() == WindowType::SCROLLWINDOW)
+ pChild = static_cast<const VclScrolledWindow*>(pChild)->get_child();
+ else if (isContainerWindow(*pChild))
+ pChild = pChild->GetWindow(GetWindowType::LastChild);
+ else
+ pChild = pChild->GetWindow(GetWindowType::Prev);
+
+ while (!pChild)
+ {
+ vcl::Window *pParent = pLastChild->GetParent();
+ if (!pParent)
+ return nullptr;
+ if (pParent == pTopLevel)
+ return nullptr;
+ pLastChild = pParent;
+ pChild = pParent->GetWindow(GetWindowType::Prev);
+ }
+
+ if (isContainerWindow(*pChild))
+ pChild = prevLogicalChildOfParent(pTopLevel, pChild);
+
+ return const_cast<vcl::Window *>(pChild);
+}
+
+vcl::Window * firstLogicalChildOfParent(const vcl::Window *pTopLevel)
+{
+ const vcl::Window *pChild = pTopLevel->GetWindow(GetWindowType::FirstChild);
+ if (pChild && isContainerWindow(*pChild))
+ pChild = nextLogicalChildOfParent(pTopLevel, pChild);
+ return const_cast<vcl::Window *>(pChild);
+}
+
+vcl::Window * lastLogicalChildOfParent(const vcl::Window *pTopLevel)
+{
+ const vcl::Window *pChild = pTopLevel->GetWindow(GetWindowType::LastChild);
+ if (pChild && isContainerWindow(*pChild))
+ pChild = prevLogicalChildOfParent(pTopLevel, pChild);
+ return const_cast<vcl::Window *>(pChild);
+}
+
+void GenerateAutoMnemonicsOnHierarchy(const vcl::Window* pWindow)
+{
+ MnemonicGenerator aMnemonicGenerator;
+ vcl::Window* pGetChild;
+ vcl::Window* pChild;
+
+ // register the assigned mnemonics
+ pGetChild = pWindow->GetWindow( GetWindowType::FirstChild );
+ while ( pGetChild )
+ {
+ pChild = pGetChild->ImplGetWindow();
+ aMnemonicGenerator.RegisterMnemonic( pChild->GetText() );
+ pGetChild = nextLogicalChildOfParent(pWindow, pGetChild);
+ }
+
+ // take the Controls of the dialog into account for TabPages
+ if ( pWindow->GetType() == WindowType::TABPAGE )
+ {
+ vcl::Window* pParent = pWindow->GetParent();
+ if (pParent && pParent->GetType() == WindowType::TABCONTROL )
+ pParent = pParent->GetParent();
+
+ if (pParent && (pParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL )
+ {
+ pGetChild = pParent->GetWindow( GetWindowType::FirstChild );
+ while ( pGetChild )
+ {
+ pChild = pGetChild->ImplGetWindow();
+ aMnemonicGenerator.RegisterMnemonic( pChild->GetText() );
+ pGetChild = nextLogicalChildOfParent(pWindow, pGetChild);
+ }
+ }
+ }
+
+ // assign mnemonics to Controls which have none
+ pGetChild = pWindow->GetWindow( GetWindowType::FirstChild );
+ while ( pGetChild )
+ {
+ pChild = pGetChild->ImplGetWindow();
+ if ( ImplIsMnemonicCtrl( pChild ) )
+ {
+ OUString aText = pChild->GetText();
+ OUString aNewText = aMnemonicGenerator.CreateMnemonic( aText );
+ if ( aText != aNewText )
+ pChild->SetText( aNewText );
+ }
+
+ pGetChild = nextLogicalChildOfParent(pWindow, pGetChild);
+ }
+}
+
+static VclButtonBox* getActionArea(Dialog const *pDialog)
+{
+ VclButtonBox *pButtonBox = nullptr;
+ if (pDialog->isLayoutEnabled())
+ {
+ vcl::Window *pBox = pDialog->GetWindow(GetWindowType::FirstChild);
+ vcl::Window *pChild = pBox->GetWindow(GetWindowType::LastChild);
+ while (pChild)
+ {
+ pButtonBox = dynamic_cast<VclButtonBox*>(pChild);
+ if (pButtonBox)
+ break;
+ pChild = pChild->GetWindow(GetWindowType::Prev);
+ }
+ }
+ return pButtonBox;
+}
+
+static vcl::Window* getActionAreaButtonList(Dialog const *pDialog)
+{
+ VclButtonBox* pButtonBox = getActionArea(pDialog);
+ if (pButtonBox)
+ return pButtonBox->GetWindow(GetWindowType::FirstChild);
+ return pDialog->GetWindow(GetWindowType::FirstChild);
+}
+
+static PushButton* ImplGetDefaultButton( Dialog const * pDialog )
+{
+ vcl::Window* pChild = getActionAreaButtonList(pDialog);
+ while ( pChild )
+ {
+ if ( pChild->ImplIsPushButton() )
+ {
+ PushButton* pPushButton = static_cast<PushButton*>(pChild);
+ if ( pPushButton->ImplIsDefButton() )
+ return pPushButton;
+ }
+
+ pChild = pChild->GetWindow( GetWindowType::Next );
+ }
+
+ return nullptr;
+}
+
+static PushButton* ImplGetOKButton( Dialog const * pDialog )
+{
+ vcl::Window* pChild = getActionAreaButtonList(pDialog);
+ while ( pChild )
+ {
+ if ( pChild->GetType() == WindowType::OKBUTTON )
+ return static_cast<PushButton*>(pChild);
+
+ pChild = pChild->GetWindow( GetWindowType::Next );
+ }
+
+ return nullptr;
+}
+
+static PushButton* ImplGetCancelButton( Dialog const * pDialog )
+{
+ vcl::Window* pChild = getActionAreaButtonList(pDialog);
+
+ while ( pChild )
+ {
+ if ( pChild->GetType() == WindowType::CANCELBUTTON )
+ return static_cast<PushButton*>(pChild);
+
+ pChild = pChild->GetWindow( GetWindowType::Next );
+ }
+
+ return nullptr;
+}
+
+static void ImplMouseAutoPos( Dialog* pDialog )
+{
+ MouseSettingsOptions nMouseOptions = pDialog->GetSettings().GetMouseSettings().GetOptions();
+ if ( nMouseOptions & MouseSettingsOptions::AutoCenterPos )
+ {
+ Size aSize = pDialog->GetOutputSizePixel();
+ pDialog->SetPointerPosPixel( Point( aSize.Width()/2, aSize.Height()/2 ) );
+ }
+ else if ( nMouseOptions & MouseSettingsOptions::AutoDefBtnPos )
+ {
+ vcl::Window* pWindow = ImplGetDefaultButton( pDialog );
+ if ( !pWindow )
+ pWindow = ImplGetOKButton( pDialog );
+ if ( !pWindow )
+ pWindow = ImplGetCancelButton( pDialog );
+ if ( !pWindow )
+ pWindow = pDialog;
+ Size aSize = pWindow->GetOutputSizePixel();
+ pWindow->SetPointerPosPixel( Point( aSize.Width()/2, aSize.Height()/2 ) );
+ }
+}
+
+struct DialogImpl
+{
+ std::vector<VclPtr<PushButton>> maOwnedButtons;
+ std::map<VclPtr<vcl::Window>, short> maResponses;
+ tools::Long mnResult;
+ bool mbStartedModal;
+ VclAbstractDialog::AsyncContext maEndCtx;
+ Link<const CommandEvent&, bool> m_aPopupMenuHdl;
+ Link<void*, vcl::ILibreOfficeKitNotifier*> m_aInstallLOKNotifierHdl;
+ bool m_bLOKTunneling;
+
+ DialogImpl() : mnResult( -1 ), mbStartedModal( false ), m_bLOKTunneling( true ) {}
+
+#ifndef NDEBUG
+ short get_response(vcl::Window *pWindow) const
+ {
+ auto aFind = maResponses.find(pWindow);
+ if (aFind != maResponses.end())
+ return aFind->second;
+ return RET_CANCEL;
+ }
+#endif
+
+ ~DialogImpl()
+ {
+ for (VclPtr<PushButton> & pOwnedButton : maOwnedButtons)
+ pOwnedButton.disposeAndClear();
+ }
+};
+
+void Dialog::disposeOwnedButtons()
+{
+ for (VclPtr<PushButton> & pOwnedButton : mpDialogImpl->maOwnedButtons)
+ pOwnedButton.disposeAndClear();
+}
+
+void Dialog::ImplInitDialogData()
+{
+ mpWindowImpl->mbDialog = true;
+ mbInExecute = false;
+ mbInSyncExecute = false;
+ mbInClose = false;
+ mbModalMode = false;
+ mpContentArea.clear();
+ mpActionArea.clear();
+ mnMousePositioned = 0;
+ mpDialogImpl.reset(new DialogImpl);
+}
+
+void Dialog::PixelInvalidate(const tools::Rectangle* pRectangle)
+{
+ if (!mpDialogImpl->m_bLOKTunneling)
+ return;
+
+ Window::PixelInvalidate(pRectangle);
+}
+
+vcl::Window* Dialog::GetDefaultParent(WinBits nStyle)
+{
+ vcl::Window* pParent = Dialog::GetDefDialogParent();
+ if (!pParent && !(nStyle & WB_SYSTEMWINDOW))
+ pParent = ImplGetSVData()->maFrameData.mpAppWin;
+
+ // If Parent is disabled, then we search for a modal dialog
+ // in this frame
+ if (pParent && (!pParent->IsInputEnabled() || pParent->IsInModalMode()))
+ {
+ ImplSVData* pSVData = ImplGetSVData();
+ auto& rExecuteDialogs = pSVData->mpWinData->mpExecuteDialogs;
+ auto it = std::find_if(rExecuteDialogs.rbegin(), rExecuteDialogs.rend(),
+ [&pParent](VclPtr<Dialog>& rDialogPtr) {
+ return pParent->ImplGetFirstOverlapWindow() &&
+ pParent->ImplGetFirstOverlapWindow()->IsWindowOrChild(rDialogPtr, true) &&
+ rDialogPtr->IsReallyVisible() && rDialogPtr->IsEnabled() &&
+ rDialogPtr->IsInputEnabled() && !rDialogPtr->IsInModalMode(); });
+ if (it != rExecuteDialogs.rend())
+ pParent = it->get();
+ }
+
+ return pParent;
+}
+
+VclPtr<vcl::Window> Dialog::AddBorderWindow(vcl::Window* pParent, WinBits nStyle)
+{
+ VclPtrInstance<ImplBorderWindow> pBorderWin( pParent, nStyle, BorderWindowStyle::Frame );
+ ImplInit( pBorderWin, nStyle & ~WB_BORDER, nullptr );
+ pBorderWin->mpWindowImpl->mpClientWindow = this;
+ pBorderWin->GetBorder( mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder );
+ mpWindowImpl->mpBorderWindow = pBorderWin;
+ mpWindowImpl->mpRealParent = pParent;
+
+ return pBorderWin;
+}
+
+void Dialog::ImplInitDialog( vcl::Window* pParent, WinBits nStyle, InitFlag eFlag )
+{
+ SystemWindowFlags nSysWinMode = Application::GetSystemWindowMode();
+
+ if ( !(nStyle & WB_NODIALOGCONTROL) )
+ nStyle |= WB_DIALOGCONTROL;
+
+ // Now, all Dialogs are per default system windows !!!
+ nStyle |= WB_SYSTEMWINDOW;
+
+ if (InitFlag::NoParent == eFlag)
+ {
+ pParent = nullptr;
+ }
+ else if (!pParent) // parent is NULL: get the default Dialog parent
+ {
+ pParent = Dialog::GetDefaultParent(nStyle);
+ }
+
+ if ( !pParent || (nStyle & WB_SYSTEMWINDOW) ||
+ (pParent->mpWindowImpl->mpFrameData->mbNeedSysWindow && !(nSysWinMode & SystemWindowFlags::NOAUTOMODE)) ||
+ (nSysWinMode & SystemWindowFlags::DIALOG) )
+ {
+ // create window with a small border ?
+ if ((nStyle & WB_ALLOWMENUBAR) || ((nStyle & (WB_BORDER | WB_NOBORDER | WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE)) == WB_BORDER))
+ {
+ AddBorderWindow(pParent, nStyle);
+ }
+ else
+ {
+ mpWindowImpl->mbFrame = true;
+ mpWindowImpl->mbOverlapWin = true;
+ ImplInit( pParent, (nStyle & (WB_MOVEABLE | WB_SIZEABLE | WB_STANDALONE)) | WB_CLOSEABLE, nullptr );
+ // Now set all style bits
+ mpWindowImpl->mnStyle = nStyle;
+ }
+ }
+ else
+ {
+ VclPtrInstance<ImplBorderWindow> pBorderWin( pParent, nStyle, BorderWindowStyle::Overlap );
+ ImplInit( pBorderWin, nStyle & ~WB_BORDER, nullptr );
+ pBorderWin->mpWindowImpl->mpClientWindow = this;
+ pBorderWin->GetBorder( mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder );
+ mpWindowImpl->mpBorderWindow = pBorderWin;
+ mpWindowImpl->mpRealParent = pParent;
+ }
+
+ SetActivateMode( ActivateModeFlags::GrabFocus );
+
+ ImplInitSettings();
+}
+
+void Dialog::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ if (IsControlBackground())
+ {
+ // user override
+ SetBackground(GetControlBackground());
+ }
+ else if (rRenderContext.IsNativeControlSupported(ControlType::WindowBackground, ControlPart::BackgroundDialog))
+ {
+ // NWF background
+ mpWindowImpl->mnNativeBackground = ControlPart::BackgroundDialog;
+ EnableChildTransparentMode();
+ }
+ else
+ {
+ // fallback to settings color
+ rRenderContext.SetBackground(GetSettings().GetStyleSettings().GetDialogColor());
+ }
+}
+
+void Dialog::ImplInitSettings()
+{
+ // user override
+ if (IsControlBackground())
+ SetBackground(GetControlBackground());
+ // NWF background
+ else if( IsNativeControlSupported(ControlType::WindowBackground, ControlPart::BackgroundDialog))
+ {
+ mpWindowImpl->mnNativeBackground = ControlPart::BackgroundDialog;
+ EnableChildTransparentMode();
+ }
+ // fallback to settings color
+ else
+ SetBackground(GetSettings().GetStyleSettings().GetDialogColor());
+}
+
+void Dialog::ImplLOKNotifier(vcl::Window* pParent)
+{
+ if (comphelper::LibreOfficeKit::isActive() && pParent)
+ {
+ if (VclPtr<vcl::Window> pWin = pParent->GetParentWithLOKNotifier())
+ {
+ SetLOKNotifier(pWin->GetLOKNotifier());
+ }
+ }
+}
+
+Dialog::Dialog( WindowType nType )
+ : SystemWindow( nType, "vcl::Dialog maLayoutIdle" )
+ , mnInitFlag(InitFlag::Default)
+{
+ ImplInitDialogData();
+}
+
+void VclBuilderContainer::disposeBuilder()
+{
+ if (m_pUIBuilder)
+ m_pUIBuilder->disposeBuilder();
+}
+
+OUString AllSettings::GetUIRootDir()
+{
+ OUString sShareLayer("$BRAND_BASE_DIR/$BRAND_SHARE_SUBDIR/config/soffice.cfg/");
+ rtl::Bootstrap::expandMacros(sShareLayer);
+ return sShareLayer;
+}
+
+//we can't change sizeable after the fact, so need to defer until we know and then
+//do the init. Find the real parent stashed in mpDialogParent.
+void Dialog::doDeferredInit(WinBits nBits)
+{
+ VclPtr<vcl::Window> pParent = mpDialogParent;
+ mpDialogParent = nullptr;
+ ImplInitDialog(pParent, nBits | WB_BORDER, mnInitFlag);
+ mbIsDeferredInit = false;
+}
+
+Dialog::Dialog(vcl::Window* pParent, const OUString& rID, const OUString& rUIXMLDescription)
+ : SystemWindow(WindowType::DIALOG, "vcl::Dialog maLayoutIdle")
+ , mnInitFlag(InitFlag::Default)
+{
+ ImplLOKNotifier(pParent);
+ ImplInitDialogData();
+ loadUI(pParent, rID, rUIXMLDescription);
+}
+
+Dialog::Dialog(vcl::Window* pParent, WinBits nStyle, InitFlag eFlag)
+ : SystemWindow(WindowType::DIALOG, "vcl::Dialog maLayoutIdle")
+ , mnInitFlag(eFlag)
+{
+ ImplLOKNotifier(pParent);
+ ImplInitDialogData();
+ ImplInitDialog( pParent, nStyle, eFlag );
+}
+
+void Dialog::set_action_area(VclButtonBox* pBox)
+{
+ mpActionArea.set(pBox);
+ if (pBox)
+ {
+ const DialogStyle& rDialogStyle =
+ GetSettings().GetStyleSettings().GetDialogStyle();
+ pBox->set_border_width(rDialogStyle.action_area_border);
+ }
+}
+
+void Dialog::set_content_area(VclBox* pBox)
+{
+ mpContentArea.set(pBox);
+}
+
+void Dialog::settingOptimalLayoutSize(Window *pBox)
+{
+ const DialogStyle& rDialogStyle =
+ GetSettings().GetStyleSettings().GetDialogStyle();
+ VclBox * pBox2 = static_cast<VclBox*>(pBox);
+ pBox2->set_border_width(rDialogStyle.content_area_border);
+}
+
+Dialog::~Dialog()
+{
+ disposeOnce();
+}
+
+void Dialog::dispose()
+{
+ bool bTunnelingEnabled = mpDialogImpl->m_bLOKTunneling;
+
+ mpDialogImpl.reset();
+ RemoveFromDlgList();
+ mpActionArea.clear();
+ mpContentArea.clear();
+
+ css::uno::Reference< css::uno::XComponentContext > xContext(
+ comphelper::getProcessComponentContext() );
+ css::uno::Reference<css::frame::XGlobalEventBroadcaster> xEventBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext), css::uno::UNO_SET_THROW);
+ css::document::DocumentEvent aObject;
+ aObject.EventName = "DialogClosed";
+ xEventBroadcaster->documentEventOccured(aObject);
+ UITestLogger::getInstance().log(u"Close Dialog");
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if(const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
+ {
+ if (bTunnelingEnabled)
+ pNotifier->notifyWindow(GetLOKWindowId(), "close");
+ ReleaseLOKNotifier();
+ }
+ }
+
+ SystemWindow::dispose();
+}
+
+IMPL_LINK_NOARG(Dialog, ImplAsyncCloseHdl, void*, void)
+{
+ Close();
+}
+
+bool Dialog::EventNotify( NotifyEvent& rNEvt )
+{
+ // first call the base class due to Tab control
+ bool bRet = SystemWindow::EventNotify( rNEvt );
+ if ( !bRet )
+ {
+ if ( rNEvt.GetType() == NotifyEventType::KEYINPUT )
+ {
+ const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
+ vcl::KeyCode aKeyCode = pKEvt->GetKeyCode();
+ sal_uInt16 nKeyCode = aKeyCode.GetCode();
+
+ if ( (nKeyCode == KEY_ESCAPE) &&
+ ((GetStyle() & WB_CLOSEABLE) || ImplGetCancelButton( this ) || ImplGetOKButton( this )) )
+ {
+ // #i89505# for the benefit of slightly mentally challenged implementations
+ // like e.g. SfxModelessDialog which destroy themselves inside Close()
+ // post this Close asynchronous so we can leave our key handler before
+ // we get destroyed
+ PostUserEvent( LINK( this, Dialog, ImplAsyncCloseHdl ), nullptr, true);
+ return true;
+ }
+ }
+ else if ( rNEvt.GetType() == NotifyEventType::GETFOCUS )
+ {
+ // make sure the dialog is still modal
+ // changing focus between application frames may
+ // have re-enabled input for our parent
+ if( mbInExecute && mbModalMode )
+ {
+ ImplSetModalInputMode( false );
+ ImplSetModalInputMode( true );
+
+ // #93022# def-button might have changed after show
+ if( !mnMousePositioned )
+ {
+ mnMousePositioned = 1;
+ ImplMouseAutoPos( this );
+ }
+
+ }
+ }
+ }
+
+ return bRet;
+}
+
+//What we really want here is something that gives the available width and
+//height of a users screen, taking away the space taken up the OS
+//taskbar, menus, etc.
+Size bestmaxFrameSizeForScreenSize(const Size &rScreenSize)
+{
+#ifndef IOS
+ tools::Long w = rScreenSize.Width();
+ if (w <= 800)
+ w -= 15;
+ else if (w <= 1024)
+ w -= 65;
+ else
+ w -= 115;
+
+ tools::Long h = rScreenSize.Height();
+ if (h <= 768)
+ h -= 50;
+ else
+ h -= 100;
+
+ return Size(std::max<tools::Long>(w, 640 - 15),
+ std::max<tools::Long>(h, 480 - 50));
+#else
+ // Don't bother with ancient magic numbers of unclear relevance on non-desktop apps anyway. It
+ // seems that at least currently in the iOS app, this function is called just once per dialog,
+ // with a rScreenSize parameter of 1x1 (!). This would lead to always returning 625x430 which is
+ // a bit random and needlessly small on an iPad at least. We want something that closely will
+ // just fit on the display in either orientation.
+
+ // We ignore the rScreenSize as it will be the dummy 1x1 from iosinst.cxx (see "Totally wrong of course").
+ (void) rScreenSize;
+
+ const int n = std::min<CGFloat>([[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height);
+ return Size(n-10, n-10);
+#endif
+}
+
+void Dialog::SetPopupMenuHdl(const Link<const CommandEvent&, bool>& rLink)
+{
+ mpDialogImpl->m_aPopupMenuHdl = rLink;
+}
+
+void Dialog::SetInstallLOKNotifierHdl(const Link<void*, vcl::ILibreOfficeKitNotifier*>& rLink)
+{
+ mpDialogImpl->m_aInstallLOKNotifierHdl = rLink;
+}
+
+void Dialog::SetLOKTunnelingState(bool bEnabled)
+{
+ mpDialogImpl->m_bLOKTunneling = bEnabled;
+}
+
+void Dialog::StateChanged( StateChangedType nType )
+{
+ bool bTunnelingEnabled = mpDialogImpl->m_bLOKTunneling;
+
+ if (nType == StateChangedType::InitShow)
+ {
+ DoInitialLayout();
+
+ const bool bKitActive = comphelper::LibreOfficeKit::isActive();
+ if (bKitActive && bTunnelingEnabled)
+ {
+ std::vector<vcl::LOKPayloadItem> aItems;
+ aItems.emplace_back("type", "dialog");
+ aItems.emplace_back("size", GetSizePixel().toString());
+ aItems.emplace_back("unique_id", this->get_id().toUtf8());
+ if (!GetText().isEmpty())
+ aItems.emplace_back("title", GetText().toUtf8());
+
+ if (const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
+ {
+ pNotifier->notifyWindow(GetLOKWindowId(), "created", aItems);
+ pNotifier->notifyWindow(GetLOKWindowId(), "created", aItems);
+ }
+ else
+ {
+ vcl::ILibreOfficeKitNotifier* pViewShell = mpDialogImpl->m_aInstallLOKNotifierHdl.Call(nullptr);
+ if (pViewShell)
+ {
+ SetLOKNotifier(pViewShell);
+ pViewShell->notifyWindow(GetLOKWindowId(), "created", aItems);
+ }
+ }
+ }
+
+ if ( !HasChildPathFocus() || HasFocus() )
+ GrabFocusToFirstControl();
+ if ( !(GetStyle() & WB_CLOSEABLE) )
+ {
+ if ( ImplGetCancelButton( this ) || ImplGetOKButton( this ) )
+ {
+ if ( ImplGetBorderWindow() )
+ static_cast<ImplBorderWindow*>(ImplGetBorderWindow())->SetCloseButton();
+ }
+ }
+
+ ImplMouseAutoPos( this );
+ }
+ else if (nType == StateChangedType::Text)
+ {
+ const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier();
+ if (pNotifier && bTunnelingEnabled)
+ {
+ std::vector<vcl::LOKPayloadItem> aPayload;
+ aPayload.emplace_back("title", GetText().toUtf8());
+ pNotifier->notifyWindow(GetLOKWindowId(), "title_changed", aPayload);
+ }
+ }
+
+ SystemWindow::StateChanged( nType );
+
+ if (nType == StateChangedType::ControlBackground)
+ {
+ ImplInitSettings();
+ Invalidate();
+ }
+
+ if (!mbModalMode && nType == StateChangedType::Visible)
+ {
+ const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier();
+ if (pNotifier && bTunnelingEnabled)
+ {
+ std::vector<vcl::LOKPayloadItem> aPayload;
+ aPayload.emplace_back("title", GetText().toUtf8());
+ pNotifier->notifyWindow(GetLOKWindowId(), IsVisible()? OUString("show"): OUString("hide"), aPayload);
+ }
+ }
+}
+
+void Dialog::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ SystemWindow::DataChanged( rDCEvt );
+
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ ImplInitSettings();
+ Invalidate();
+ }
+}
+
+bool Dialog::Close()
+{
+ VclPtr<vcl::Window> xWindow = this;
+ CallEventListeners( VclEventId::WindowClose );
+ if ( xWindow->isDisposed() )
+ return false;
+
+ if ( mpWindowImpl->mxWindowPeer.is() && IsCreatedWithToolkit() && !IsInExecute() )
+ return false;
+
+ // If there's a cancel button with a custom handler, then always give it a chance to
+ // handle Dialog::Close
+ PushButton* pCustomCancelButton;
+ PushButton* pCancelButton = dynamic_cast<PushButton*>(get_widget_for_response(RET_CANCEL));
+ if (!mbInClose && pCancelButton && pCancelButton->GetClickHdl().IsSet())
+ pCustomCancelButton = pCancelButton;
+ else
+ pCustomCancelButton = nullptr;
+
+ mbInClose = true;
+
+ if (pCustomCancelButton)
+ {
+ pCustomCancelButton->Click();
+ if (xWindow->isDisposed())
+ return true;
+ mbInClose = false;
+ return false;
+ }
+
+ if ( !(GetStyle() & WB_CLOSEABLE) )
+ {
+ bool bRet = true;
+ PushButton* pButton = ImplGetCancelButton( this );
+ if ( pButton )
+ pButton->Click();
+ else
+ {
+ pButton = ImplGetOKButton( this );
+ if ( pButton )
+ pButton->Click();
+ else
+ bRet = false;
+ }
+ if ( xWindow->isDisposed() )
+ return true;
+ return bRet;
+ }
+
+ if (IsInExecute() || mpDialogImpl->maEndCtx.isSet())
+ {
+ EndDialog();
+ mbInClose = false;
+ return true;
+ }
+ else
+ {
+ mbInClose = false;
+ return SystemWindow::Close();
+ }
+}
+
+bool Dialog::ImplStartExecute()
+{
+ setDeferredProperties();
+
+ if (IsInExecute() || mpDialogImpl->maEndCtx.isSet())
+ {
+#ifdef DBG_UTIL
+ SAL_WARN( "vcl", "Dialog::StartExecuteModal() is called in Dialog::StartExecuteModal(): "
+ << ImplGetDialogText(this) );
+#endif
+ return false;
+ }
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ const bool bKitActive = comphelper::LibreOfficeKit::isActive();
+
+ const bool bModal = GetType() != WindowType::MODELESSDIALOG;
+
+ if (bModal)
+ {
+ if (bKitActive && !GetLOKNotifier())
+ {
+#ifdef IOS
+ // gh#5908 handle pasting disallowed clipboard contents on iOS
+ // When another app owns the current clipboard contents, pasting
+ // will display a "allow or disallow" dialog. If the disallow
+ // option is selected, the data from the UIPasteboard will be
+ // garbage and we will find ourselves here. Since calling
+ // SetLOKNotifier() with a nullptr aborts in an assert(), fix
+ // the crash by failing gracefully.
+ return false;
+#else
+ SetLOKNotifier(mpDialogImpl->m_aInstallLOKNotifierHdl.Call(nullptr));
+#endif
+ }
+
+ switch ( Application::GetDialogCancelMode() )
+ {
+ case DialogCancelMode::Off:
+ break;
+ case DialogCancelMode::Silent:
+ if (bModal && GetLOKNotifier())
+ {
+ // check if there's already some dialog being ::Execute()d
+ const bool bDialogExecuting = std::any_of(pSVData->mpWinData->mpExecuteDialogs.begin(),
+ pSVData->mpWinData->mpExecuteDialogs.end(),
+ [](const Dialog* pDialog) {
+ return pDialog->IsInSyncExecute();
+ });
+ if (!(bDialogExecuting && IsInSyncExecute()))
+ break;
+ else
+ SAL_WARN("lok.dialog", "Dialog \"" << ImplGetDialogText(this) << "\" is being synchronously executed over an existing synchronously executing dialog.");
+ }
+
+ if (SalInstance::IsRunningUnitTest())
+ { // helps starbasic unit tests show their errors
+ std::cerr << "Dialog \"" << ImplGetDialogText(this)
+ << "\"cancelled in silent mode";
+ }
+
+ SAL_INFO(
+ "vcl",
+ "Dialog \"" << ImplGetDialogText(this)
+ << "\"cancelled in silent mode");
+ return false;
+
+ case DialogCancelMode::LOKSilent:
+ return false;
+
+ default: // default cannot happen
+ case DialogCancelMode::Fatal:
+ std::abort();
+ }
+
+#ifdef DBG_UTIL
+ vcl::Window* pParent = GetParent();
+ if ( pParent )
+ {
+ pParent = pParent->ImplGetFirstOverlapWindow();
+ if (pParent)
+ {
+ SAL_WARN_IF( !pParent->IsReallyVisible(), "vcl",
+ "Dialog::StartExecuteModal() - Parent not visible" );
+ SAL_WARN_IF( !pParent->IsInputEnabled(), "vcl",
+ "Dialog::StartExecuteModal() - Parent input disabled, use another parent to ensure modality!" );
+ SAL_WARN_IF( pParent->IsInModalMode(), "vcl",
+ "Dialog::StartExecuteModal() - Parent already modally disabled, use another parent to ensure modality!" );
+ }
+ }
+#endif
+
+ // link all dialogs which are being executed
+ pSVData->mpWinData->mpExecuteDialogs.push_back(this);
+
+ // stop capturing, in order to have control over the dialog
+ if (pSVData->mpWinData->mpTrackWin)
+ pSVData->mpWinData->mpTrackWin->EndTracking(TrackingEventFlags::Cancel);
+ if (pSVData->mpWinData->mpCaptureWin)
+ pSVData->mpWinData->mpCaptureWin->ReleaseMouse();
+ EnableInput();
+ }
+
+ mbInExecute = true;
+ // no real modality in LibreOfficeKit
+ if (!bKitActive && bModal)
+ SetModalInputMode(true);
+
+ // FIXME: no layouting, workaround some clipping issues
+ ImplAdjustNWFSizes();
+
+ css::uno::Reference< css::uno::XComponentContext > xContext(
+ comphelper::getProcessComponentContext());
+ bool bForceFocusAndToFront(officecfg::Office::Common::View::NewDocumentHandling::ForceFocusAndToFront::get());
+ ShowFlags showFlags = bForceFocusAndToFront ? ShowFlags::ForegroundTask : ShowFlags::NONE;
+ Show(true, showFlags);
+
+ if (bModal)
+ pSVData->maAppData.mnModalMode++;
+
+ css::uno::Reference<css::frame::XGlobalEventBroadcaster> xEventBroadcaster(
+ css::frame::theGlobalEventBroadcaster::get(xContext), css::uno::UNO_SET_THROW);
+ css::document::DocumentEvent aObject;
+ aObject.EventName = "DialogExecute";
+ xEventBroadcaster->documentEventOccured(aObject);
+ if (bModal)
+ UITestLogger::getInstance().log(Concat2View("Open Modal " + get_id()));
+ else
+ UITestLogger::getInstance().log(Concat2View("Open Modeless " + get_id()));
+
+ bool bTunnelingEnabled = mpDialogImpl->m_bLOKTunneling;
+ if (comphelper::LibreOfficeKit::isActive() && bTunnelingEnabled)
+ {
+ if (const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
+ {
+ // Dialog boxes don't get the Resize call and they
+ // can have invalid size at 'created' message above.
+ // If there is no difference, the client should detect it and ignore us,
+ // otherwise, this should make sure that the window has the correct size.
+ std::vector<vcl::LOKPayloadItem> aItems;
+ aItems.emplace_back("size", GetSizePixel().toString());
+ aItems.emplace_back("unique_id", this->get_id().toUtf8());
+ pNotifier->notifyWindow(GetLOKWindowId(), "size_changed", aItems);
+ }
+ }
+
+ return true;
+}
+
+void Dialog::ImplEndExecuteModal()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ pSVData->maAppData.mnModalMode--;
+}
+
+short Dialog::Execute()
+{
+ VclPtr<vcl::Window> xWindow = this;
+
+ mbInSyncExecute = true;
+ comphelper::ScopeGuard aGuard([&]() {
+ mbInSyncExecute = false;
+ });
+
+ if ( !ImplStartExecute() )
+ return 0;
+
+ // Yield util EndDialog is called or dialog gets destroyed
+ // (the latter should not happen, but better safe than sorry
+ while ( !xWindow->isDisposed() && mbInExecute && !Application::IsQuit() )
+ Application::Yield();
+
+ ImplEndExecuteModal();
+#ifdef DBG_UTIL
+ assert (!mpDialogParent || !mpDialogParent->isDisposed());
+#endif
+ if ( !xWindow->isDisposed() )
+ xWindow.clear();
+ else
+ {
+ OSL_FAIL( "Dialog::Execute() - Dialog destroyed in Execute()" );
+ }
+
+ assert(mpDialogImpl);
+
+ if (mpDialogImpl)
+ {
+ tools::Long nRet = mpDialogImpl->mnResult;
+ mpDialogImpl->mnResult = -1;
+
+ return static_cast<short>(nRet);
+ }
+ else
+ {
+ SAL_WARN( "vcl", "Dialog::Execute() : missing mpDialogImpl " );
+ return 0;
+ }
+}
+
+// virtual
+bool Dialog::StartExecuteAsync( VclAbstractDialog::AsyncContext &rCtx )
+{
+ const bool bModal = GetType() != WindowType::MODELESSDIALOG;
+ if (!ImplStartExecute())
+ {
+ rCtx.mxOwner.disposeAndClear();
+ rCtx.mxOwnerDialogController.reset();
+ rCtx.mxOwnerSelf.reset();
+ return false;
+ }
+
+ mpDialogImpl->maEndCtx = rCtx;
+ mpDialogImpl->mbStartedModal = bModal;
+
+ return true;
+}
+
+void Dialog::RemoveFromDlgList()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ auto& rExecuteDialogs = pSVData->mpWinData->mpExecuteDialogs;
+
+ // remove dialog from the list of dialogs which are being executed
+ std::erase_if(rExecuteDialogs, [this](VclPtr<Dialog>& dialog){ return dialog.get() == this; });
+}
+
+void Dialog::EndDialog( tools::Long nResult )
+{
+ if (!mbInExecute || isDisposed())
+ return;
+
+ const bool bModal = GetType() != WindowType::MODELESSDIALOG;
+
+ Hide();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if(const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
+ {
+ if (mpDialogImpl->m_bLOKTunneling)
+ pNotifier->notifyWindow(GetLOKWindowId(), "close");
+ ReleaseLOKNotifier();
+ }
+ }
+
+ if (bModal)
+ {
+ SetModalInputMode(false);
+
+ RemoveFromDlgList();
+
+ // set focus to previous modal dialog if it is modal for
+ // the same frame parent (or NULL)
+ ImplSVData* pSVData = ImplGetSVData();
+ if (!pSVData->mpWinData->mpExecuteDialogs.empty())
+ {
+ VclPtr<Dialog> pPrevious = pSVData->mpWinData->mpExecuteDialogs.back();
+
+ vcl::Window* pFrameParent = ImplGetFrameWindow()->ImplGetParent();
+ vcl::Window* pPrevFrameParent = pPrevious->ImplGetFrameWindow()? pPrevious->ImplGetFrameWindow()->ImplGetParent(): nullptr;
+ if( ( !pFrameParent && !pPrevFrameParent ) ||
+ ( pFrameParent && pPrevFrameParent && pFrameParent->ImplGetFrame() == pPrevFrameParent->ImplGetFrame() )
+ )
+ {
+ pPrevious->GrabFocus();
+ }
+ }
+ }
+
+ mpDialogImpl->mnResult = nResult;
+
+ if ( mpDialogImpl->mbStartedModal )
+ ImplEndExecuteModal();
+
+ // coverity[check_after_deref] - ImplEndExecuteModal might trigger destruction of mpDialogImpl
+ if ( mpDialogImpl && mpDialogImpl->maEndCtx.isSet() )
+ {
+ auto fn = std::move(mpDialogImpl->maEndCtx.maEndDialogFn);
+ // std::move leaves maEndDialogFn in a valid state with unspecified
+ // value. For the SwSyncBtnDlg case gcc and msvc left maEndDialogFn
+ // unset, but clang left maEndDialogFn at its original value, keeping
+ // an extra reference to the DialogController in its lambda giving
+ // an inconsistent lifecycle for the dialog. Force it to be unset.
+ mpDialogImpl->maEndCtx.maEndDialogFn = nullptr;
+ fn(nResult);
+ }
+
+ if ( mpDialogImpl && mpDialogImpl->mbStartedModal )
+ {
+ mpDialogImpl->mbStartedModal = false;
+ mpDialogImpl->mnResult = -1;
+ }
+ mbInExecute = false;
+
+ if ( mpDialogImpl )
+ {
+ // Destroy ourselves (if we have a context with VclPtr owner)
+ std::shared_ptr<weld::DialogController> xOwnerDialogController = std::move(mpDialogImpl->maEndCtx.mxOwnerDialogController);
+ std::shared_ptr<weld::Dialog> xOwnerSelf = std::move(mpDialogImpl->maEndCtx.mxOwnerSelf);
+ mpDialogImpl->maEndCtx.mxOwner.disposeAndClear();
+ xOwnerDialogController.reset();
+ xOwnerSelf.reset();
+ }
+}
+
+namespace vcl
+{
+ void EndAllDialogs( vcl::Window const * pParent )
+ {
+ ImplSVData* pSVData = ImplGetSVData();
+ auto& rExecuteDialogs = pSVData->mpWinData->mpExecuteDialogs;
+
+ for (auto it = rExecuteDialogs.rbegin(); it != rExecuteDialogs.rend(); ++it)
+ {
+ if (!pParent || pParent->IsWindowOrChild(*it, true))
+ {
+ (*it)->EndDialog();
+ (*it)->PostUserEvent(Link<void*, void>());
+ }
+ }
+ }
+
+ void EnableDialogInput(vcl::Window* pWindow)
+ {
+ if (Dialog* pDialog = dynamic_cast<Dialog*>(pWindow))
+ {
+ pDialog->EnableInput();
+ }
+ }
+
+ void CloseTopLevel(vcl::Window* pWindow)
+ {
+ if (Dialog* pDialog = dynamic_cast<Dialog*>(pWindow))
+ pDialog->Close();
+ else if (FloatingWindow* pFloatWin = dynamic_cast<FloatingWindow*>(pWindow))
+ pFloatWin->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll);
+ }
+}
+
+void Dialog::SetModalInputMode( bool bModal )
+{
+ if ( bModal == mbModalMode )
+ return;
+
+ ImplGetFrame()->SetModal(bModal);
+
+ if (GetParent())
+ {
+ SalFrame* pFrame = GetParent()->ImplGetFrame();
+ pFrame->NotifyModalHierarchy(bModal);
+ }
+
+ ImplSetModalInputMode(bModal);
+}
+
+void Dialog::ImplSetModalInputMode( bool bModal )
+{
+ if ( bModal == mbModalMode )
+ return;
+
+ // previously Execute()'d dialog - the one below the top-most one
+ VclPtr<Dialog> pPrevious;
+ ImplSVData* pSVData = ImplGetSVData();
+ auto& rExecuteDialogs = pSVData->mpWinData->mpExecuteDialogs;
+ if (rExecuteDialogs.size() > 1)
+ pPrevious = rExecuteDialogs[rExecuteDialogs.size() - 2];
+
+ mbModalMode = bModal;
+ if ( bModal )
+ {
+ // Disable the prev Modal Dialog, because our dialog must close at first,
+ // before the other dialog can be closed (because the other dialog
+ // is on stack since our dialog returns)
+ if (pPrevious && !pPrevious->IsWindowOrChild(this, true))
+ pPrevious->EnableInput(false, this);
+
+ // determine next overlap dialog parent
+ vcl::Window* pParent = GetParent();
+ if ( pParent )
+ {
+ // #103716# dialogs should always be modal to the whole frame window
+ // #115933# disable the whole frame hierarchy, useful if our parent
+ // is a modeless dialog
+ mpDialogParent = pParent->mpWindowImpl->mpFrameWindow;
+ mpDialogParent->IncModalCount();
+ }
+ }
+ else
+ {
+ if ( mpDialogParent )
+ {
+ // #115933# re-enable the whole frame hierarchy again (see above)
+ // note that code in getfocus assures that we do not accidentally enable
+ // windows that were disabled before
+ mpDialogParent->DecModalCount();
+ }
+
+ // Enable the prev Modal Dialog
+ if (pPrevious && !pPrevious->IsWindowOrChild(this, true))
+ {
+ pPrevious->EnableInput(true, this);
+
+ // ensure continued modality of prev dialog
+ // do not change modality counter
+
+ // #i119994# need find the last modal dialog before reactive it
+ if (pPrevious->IsModalInputMode() || !pPrevious->IsWindowOrChild(this, true))
+ {
+ pPrevious->ImplSetModalInputMode(false);
+ pPrevious->ImplSetModalInputMode(true);
+ }
+ }
+ }
+}
+
+vcl::Window* Dialog::GetFirstControlForFocus()
+{
+ vcl::Window* pFocusControl = nullptr;
+ vcl::Window* pFirstOverlapWindow = ImplGetFirstOverlapWindow();
+
+ // find focus control, even if the dialog has focus
+ if (!HasFocus() && pFirstOverlapWindow && pFirstOverlapWindow->mpWindowImpl)
+ {
+ // prefer a child window which had focus before
+ pFocusControl = ImplGetFirstOverlapWindow()->mpWindowImpl->mpLastFocusWindow;
+ // find the control out of the dialog control
+ if ( pFocusControl )
+ pFocusControl = ImplFindDlgCtrlWindow( pFocusControl );
+ }
+ // no control had the focus before or the control is not
+ // part of the tab-control, now give focus to the
+ // first control in the tab-control
+ if ( !pFocusControl ||
+ !(pFocusControl->GetStyle() & WB_TABSTOP) ||
+ !isVisibleInLayout(pFocusControl) ||
+ !isEnabledInLayout(pFocusControl) || !pFocusControl->IsInputEnabled() )
+ {
+ pFocusControl = ImplGetDlgWindow( 0, GetDlgWindowType::First );
+ }
+
+ return pFocusControl;
+}
+
+void Dialog::GrabFocusToFirstControl()
+{
+ vcl::Window* pFocusControl = GetFirstControlForFocus();
+ if ( pFocusControl )
+ pFocusControl->ImplControlFocus( GetFocusFlags::Init );
+}
+
+void Dialog::GetDrawWindowBorder( sal_Int32& rLeftBorder, sal_Int32& rTopBorder, sal_Int32& rRightBorder, sal_Int32& rBottomBorder ) const
+{
+ ScopedVclPtrInstance<ImplBorderWindow> aImplWin( static_cast<vcl::Window*>(const_cast<Dialog *>(this)), WB_BORDER|WB_STDWORK, BorderWindowStyle::Overlap );
+ aImplWin->GetBorder( rLeftBorder, rTopBorder, rRightBorder, rBottomBorder );
+}
+
+void Dialog::Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags )
+{
+ Point aPos = pDev->LogicToPixel( rPos );
+ Size aSize = GetSizePixel();
+
+ Wallpaper aWallpaper = GetBackground();
+ if ( !aWallpaper.IsBitmap() )
+ ImplInitSettings();
+
+ pDev->Push();
+ pDev->SetMapMode();
+ pDev->SetLineColor();
+
+ if ( aWallpaper.IsBitmap() )
+ pDev->DrawBitmapEx( aPos, aSize, aWallpaper.GetBitmap() );
+ else
+ {
+ pDev->SetFillColor( aWallpaper.GetColor() );
+ pDev->DrawRect( tools::Rectangle( aPos, aSize ) );
+ }
+
+ if (!( GetStyle() & WB_NOBORDER ))
+ {
+ ScopedVclPtrInstance< ImplBorderWindow > aImplWin( this, WB_BORDER|WB_STDWORK, BorderWindowStyle::Overlap );
+ aImplWin->SetText( GetText() );
+ aImplWin->setPosSizePixel( aPos.X(), aPos.Y(), aSize.Width(), aSize.Height() );
+ aImplWin->SetDisplayActive( true );
+ aImplWin->InitView();
+
+ aImplWin->Draw( pDev, aPos );
+ }
+
+ pDev->Pop();
+}
+
+void Dialog::queue_resize(StateChangedType eReason)
+{
+ if (IsInClose())
+ return;
+ SystemWindow::queue_resize(eReason);
+}
+
+void Dialog::Resize()
+{
+ SystemWindow::Resize();
+
+ if (comphelper::LibreOfficeKit::isDialogPainting())
+ return;
+
+ bool bTunnelingEnabled = mpDialogImpl->m_bLOKTunneling;
+ const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier();
+ if (pNotifier && bTunnelingEnabled)
+ {
+ std::vector<vcl::LOKPayloadItem> aItems;
+ aItems.emplace_back("size", GetSizePixel().toString());
+ aItems.emplace_back("unique_id", this->get_id().toUtf8());
+ pNotifier->notifyWindow(GetLOKWindowId(), "size_changed", aItems);
+ }
+}
+
+bool Dialog::set_property(const OUString &rKey, const OUString &rValue)
+{
+ if (rKey == "border-width")
+ set_border_width(rValue.toInt32());
+ else
+ return SystemWindow::set_property(rKey, rValue);
+ return true;
+}
+
+FactoryFunction Dialog::GetUITestFactory() const
+{
+ return DialogUIObject::create;
+}
+
+IMPL_LINK(Dialog, ResponseHdl, Button*, pButton, void)
+{
+ auto aFind = mpDialogImpl->maResponses.find(pButton);
+ if (aFind == mpDialogImpl->maResponses.end())
+ return;
+ short nResponse = aFind->second;
+ if (nResponse == RET_HELP)
+ {
+ vcl::Window* pFocusWin = Application::GetFocusWindow();
+ if (!pFocusWin || comphelper::LibreOfficeKit::isActive())
+ pFocusWin = pButton;
+ HelpEvent aEvt(pFocusWin->GetPointerPosPixel(), HelpEventMode::CONTEXT);
+ pFocusWin->RequestHelp(aEvt);
+ return;
+ }
+ EndDialog(nResponse);
+}
+
+void Dialog::add_button(PushButton* pButton, int response, bool bTransferOwnership)
+{
+ if (bTransferOwnership)
+ mpDialogImpl->maOwnedButtons.push_back(pButton);
+ mpDialogImpl->maResponses[pButton] = response;
+ switch (pButton->GetType())
+ {
+ case WindowType::PUSHBUTTON:
+ {
+ if (!pButton->GetClickHdl().IsSet())
+ pButton->SetClickHdl(LINK(this, Dialog, ResponseHdl));
+ break;
+ }
+ //insist that the response ids match the default actions for those
+ //widgets, and leave their default handlers in place
+ case WindowType::OKBUTTON:
+ assert(mpDialogImpl->get_response(pButton) == RET_OK);
+ break;
+ case WindowType::CANCELBUTTON:
+ assert(mpDialogImpl->get_response(pButton) == RET_CANCEL || mpDialogImpl->get_response(pButton) == RET_CLOSE);
+ break;
+ case WindowType::HELPBUTTON:
+ assert(mpDialogImpl->get_response(pButton) == RET_HELP);
+ break;
+ default:
+ SAL_WARN("vcl.layout", "The type of widget " <<
+ pButton->GetHelpId() << " is currently not handled");
+ break;
+ }
+}
+
+vcl::Window* Dialog::get_widget_for_response(int response)
+{
+ //copy explicit responses
+ std::map<VclPtr<vcl::Window>, short> aResponses(mpDialogImpl->maResponses);
+
+ if (mpActionArea)
+ {
+ //add implicit responses
+ for (vcl::Window* pChild = mpActionArea->GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (aResponses.find(pChild) != aResponses.end())
+ continue;
+ switch (pChild->GetType())
+ {
+ case WindowType::OKBUTTON:
+ aResponses[pChild] = RET_OK;
+ break;
+ case WindowType::CANCELBUTTON:
+ aResponses[pChild] = RET_CANCEL;
+ break;
+ case WindowType::HELPBUTTON:
+ aResponses[pChild] = RET_HELP;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ for (const auto& a : aResponses)
+ {
+ if (a.second == response)
+ return a.first;
+ }
+
+ return nullptr;
+}
+
+int Dialog::get_default_response() const
+{
+ //copy explicit responses
+ std::map<VclPtr<vcl::Window>, short> aResponses(mpDialogImpl->maResponses);
+
+ if (mpActionArea)
+ {
+ //add implicit responses
+ for (vcl::Window* pChild = mpActionArea->GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (aResponses.find(pChild) != aResponses.end())
+ continue;
+ switch (pChild->GetType())
+ {
+ case WindowType::OKBUTTON:
+ aResponses[pChild] = RET_OK;
+ break;
+ case WindowType::CANCELBUTTON:
+ aResponses[pChild] = RET_CANCEL;
+ break;
+ case WindowType::HELPBUTTON:
+ aResponses[pChild] = RET_HELP;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ for (const auto& a : aResponses)
+ {
+ if (a.first->GetStyle() & WB_DEFBUTTON)
+ {
+ return a.second;
+ }
+ }
+ return RET_CANCEL;
+}
+
+void Dialog::set_default_response(int response)
+{
+ //copy explicit responses
+ std::map<VclPtr<vcl::Window>, short> aResponses(mpDialogImpl->maResponses);
+
+ if (mpActionArea)
+ {
+ //add implicit responses
+ for (vcl::Window* pChild = mpActionArea->GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (aResponses.find(pChild) != aResponses.end())
+ continue;
+ switch (pChild->GetType())
+ {
+ case WindowType::OKBUTTON:
+ aResponses[pChild] = RET_OK;
+ break;
+ case WindowType::CANCELBUTTON:
+ aResponses[pChild] = RET_CANCEL;
+ break;
+ case WindowType::HELPBUTTON:
+ aResponses[pChild] = RET_HELP;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ for (auto& a : aResponses)
+ {
+ if (a.second == response)
+ {
+ a.first->SetStyle(a.first->GetStyle() | WB_DEFBUTTON);
+ a.first->GrabFocus();
+ }
+ else
+ {
+ a.first->SetStyle(a.first->GetStyle() & ~WB_DEFBUTTON);
+ }
+ }
+}
+
+VclBuilderContainer::VclBuilderContainer()
+{
+}
+
+void VclBuilderContainer::setDeferredProperties()
+{
+ if (!m_pUIBuilder)
+ return;
+ m_pUIBuilder->setDeferredProperties();
+}
+
+VclBuilderContainer::~VclBuilderContainer()
+{
+}
+
+void Dialog::Activate()
+{
+ if (GetType() == WindowType::MODELESSDIALOG)
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext(
+ comphelper::getProcessComponentContext() );
+ css::uno::Reference<css::frame::XGlobalEventBroadcaster> xEventBroadcaster(css::frame::theGlobalEventBroadcaster::get(xContext), css::uno::UNO_SET_THROW);
+ css::document::DocumentEvent aObject;
+ aObject.EventName = "ModelessDialogVisible";
+ xEventBroadcaster->documentEventOccured(aObject);
+ }
+ SystemWindow::Activate();
+}
+
+void Dialog::Command(const CommandEvent& rCEvt)
+{
+ if (mpDialogImpl && mpDialogImpl->m_aPopupMenuHdl.Call(rCEvt))
+ return;
+ SystemWindow::Command(rCEvt);
+}
+
+struct TopLevelWindowLockerImpl
+{
+ std::stack<std::vector<VclPtr<vcl::Window>>> m_aBusyStack;
+};
+
+TopLevelWindowLocker::TopLevelWindowLocker()
+ : m_xImpl(std::make_unique<TopLevelWindowLockerImpl>())
+{
+}
+
+void TopLevelWindowLocker::incBusy(const weld::Widget* pIgnore)
+{
+ // lock any toplevel windows from being closed until busy is over
+ std::vector<VclPtr<vcl::Window>> aTopLevels;
+ vcl::Window *pTopWin = Application::GetFirstTopLevelWindow();
+ while (pTopWin)
+ {
+ vcl::Window* pCandidate = pTopWin;
+ if (pCandidate->GetType() == WindowType::BORDERWINDOW)
+ pCandidate = pCandidate->GetWindow(GetWindowType::FirstChild);
+ // tdf#125266 ignore HelpTextWindows
+ if (pCandidate &&
+ pCandidate->GetType() != WindowType::HELPTEXTWINDOW &&
+ pCandidate->GetType() != WindowType::FLOATINGWINDOW &&
+ pCandidate->GetFrameWeld() != pIgnore)
+ {
+ aTopLevels.push_back(pCandidate);
+ }
+ pTopWin = Application::GetNextTopLevelWindow(pTopWin);
+ }
+ for (auto& a : aTopLevels)
+ {
+ a->IncModalCount();
+ a->ImplGetFrame()->NotifyModalHierarchy(true);
+ }
+ m_xImpl->m_aBusyStack.push(aTopLevels);
+}
+
+void TopLevelWindowLocker::decBusy()
+{
+ // unlock locked toplevel windows from being closed now busy is over
+ for (auto& a : m_xImpl->m_aBusyStack.top())
+ {
+ if (a->isDisposed())
+ continue;
+ a->DecModalCount();
+ a->ImplGetFrame()->NotifyModalHierarchy(false);
+ }
+ m_xImpl->m_aBusyStack.pop();
+}
+
+bool TopLevelWindowLocker::isBusy() const
+{
+ return !m_xImpl->m_aBusyStack.empty();
+}
+
+TopLevelWindowLocker::~TopLevelWindowLocker()
+{
+}
+
+void Dialog::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ SystemWindow::DumpAsPropertyTree(rJsonWriter);
+ rJsonWriter.put("title", GetText());
+ if (vcl::Window* pActionArea = get_action_area())
+ {
+ if (!pActionArea->IsVisible())
+ rJsonWriter.put("collapsed", true);
+ }
+
+ OUString sDialogId = GetHelpId();
+ sal_Int32 nStartPos = sDialogId.lastIndexOf('/');
+ nStartPos = nStartPos >= 0 ? nStartPos + 1 : 0;
+ rJsonWriter.put("dialogid", sDialogId.copy(nStartPos));
+
+ {
+ auto aResponses = rJsonWriter.startArray("responses");
+ for (const auto& rResponse : mpDialogImpl->maResponses)
+ {
+ auto aResponse = rJsonWriter.startStruct();
+ rJsonWriter.put("id", rResponse.first->get_id());
+ rJsonWriter.put("response", rResponse.second);
+ }
+ }
+
+ vcl::Window* pFocusControl = GetFirstControlForFocus();
+ if (pFocusControl)
+ rJsonWriter.put("init_focus_id", pFocusControl->get_id());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/dlgctrl.cxx b/vcl/source/window/dlgctrl.cxx
new file mode 100644
index 0000000000..ac75333d90
--- /dev/null
+++ b/vcl/source/window/dlgctrl.cxx
@@ -0,0 +1,1168 @@
+/* -*- 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 <svdata.hxx>
+#include <window.h>
+
+#include "dlgctrl.hxx"
+#include <vcl/event.hxx>
+#include <vcl/toolkit/fixed.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/tabpage.hxx>
+#include <vcl/tabctrl.hxx>
+#include <vcl/toolkit/button.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/settings.hxx>
+#include <sal/log.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <com/sun/star/i18n/XCharacterClassification.hpp>
+
+using namespace ::com::sun::star;
+
+static bool ImplHasIndirectTabParent( vcl::Window* pWindow )
+{
+ // The window has indirect tab parent if it is included in tab hierarchy
+ // of the indirect parent window
+
+ vcl::Window* pNonLayoutParent = getNonLayoutParent(pWindow);
+ return ( pNonLayoutParent
+ && ( pNonLayoutParent->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL ) );
+}
+
+static vcl::Window* ImplGetTopParentOfTabHierarchy( vcl::Window* pParent )
+{
+ // The method allows to find the most close parent containing all the
+ // window from the current tab-hierarchy
+ // The direct parent should be provided as a parameter here
+
+ vcl::Window* pResult = pParent;
+
+ if ( pResult )
+ {
+ vcl::Window* pNonLayoutParent = getNonLayoutParent(pResult);
+ while ( pNonLayoutParent && ( pResult->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL ) )
+ {
+ pResult = pNonLayoutParent;
+ pNonLayoutParent = getNonLayoutParent(pResult);
+ }
+ }
+
+ return pResult;
+}
+
+static vcl::Window* ImplGetCurTabWindow(const vcl::Window* pWindow)
+{
+ assert(pWindow->GetType() == WindowType::TABCONTROL);
+ const TabControl* pTabControl = static_cast<const TabControl*>(pWindow);
+ // Check if the TabPage is a Child of the TabControl and still exists (by
+ // walking all child windows); because it could be that the TabPage has been
+ // destroyed already by a Dialog-Dtor, event that the TabControl still exists.
+ const TabPage* pTempTabPage = pTabControl->GetTabPage(pTabControl->GetCurPageId());
+ if (pTempTabPage)
+ {
+ vcl::Window* pTempWindow = pTabControl->GetWindow(GetWindowType::FirstChild);
+ while (pTempWindow)
+ {
+ if (pTempWindow->ImplGetWindow() == pTempTabPage)
+ {
+ return const_cast<TabPage*>(pTempTabPage);
+ }
+ pTempWindow = nextLogicalChildOfParent(pTabControl, pTempWindow);
+ }
+ }
+
+ return nullptr;
+}
+
+static vcl::Window* ImplGetSubChildWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex )
+{
+ // ignore all windows with mpClientWindow set
+ for (vcl::Window *pNewParent = pParent->ImplGetWindow();
+ pParent != pNewParent; pParent = pNewParent);
+
+ vcl::Window* pFoundWindow = nullptr;
+ vcl::Window* pWindow = firstLogicalChildOfParent(pParent);
+ vcl::Window* pNextWindow = pWindow;
+
+ // process just the current page of a tab control
+ if (pWindow && pParent->GetType() == WindowType::TABCONTROL)
+ {
+ pWindow = ImplGetCurTabWindow(pParent);
+ pNextWindow = lastLogicalChildOfParent(pParent);
+ }
+
+ while (pWindow)
+ {
+ pWindow = pWindow->ImplGetWindow();
+
+ // skip invisible and disabled windows
+ if (isVisibleInLayout(pWindow))
+ {
+ // return the TabControl itself, before handling its page
+ if (pWindow->GetType() == WindowType::TABCONTROL)
+ {
+ if (n == nIndex)
+ return pWindow;
+ ++nIndex;
+ }
+ if (pWindow->GetStyle() & (WB_DIALOGCONTROL | WB_CHILDDLGCTRL))
+ pFoundWindow = ImplGetSubChildWindow(pWindow, n, nIndex);
+ else
+ pFoundWindow = pWindow;
+
+ if (n == nIndex)
+ return pFoundWindow;
+ ++nIndex;
+ }
+
+ pWindow = nextLogicalChildOfParent(pParent, pNextWindow);
+ pNextWindow = pWindow;
+ }
+
+ --nIndex;
+ assert(!pFoundWindow || (pFoundWindow == pFoundWindow->ImplGetWindow()));
+ return pFoundWindow;
+}
+
+vcl::Window* ImplGetChildWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex, bool bTestEnable )
+{
+ pParent = ImplGetTopParentOfTabHierarchy( pParent );
+
+ nIndex = 0;
+ vcl::Window* pWindow = ImplGetSubChildWindow( pParent, n, nIndex );
+ if ( bTestEnable )
+ {
+ sal_uInt16 n2 = nIndex;
+ while ( pWindow && (!isEnabledInLayout(pWindow) || !pWindow->IsInputEnabled()) )
+ {
+ n2 = nIndex+1;
+ nIndex = 0;
+ pWindow = ImplGetSubChildWindow( pParent, n2, nIndex );
+ if ( nIndex < n2 )
+ break;
+ }
+
+ if ( (nIndex < n2) && n )
+ {
+ do
+ {
+ n--;
+ nIndex = 0;
+ pWindow = ImplGetSubChildWindow( pParent, n, nIndex );
+ }
+ while ( pWindow && n && (!isEnabledInLayout(pWindow) || !pWindow->IsInputEnabled()) );
+ }
+ }
+ return pWindow;
+}
+
+static vcl::Window* ImplGetNextWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex, bool bTestEnable )
+{
+ vcl::Window* pWindow = ImplGetChildWindow( pParent, n+1, nIndex, bTestEnable );
+ if ( n == nIndex )
+ {
+ n = 0;
+ pWindow = ImplGetChildWindow( pParent, n, nIndex, bTestEnable );
+ }
+ return pWindow;
+}
+
+namespace vcl {
+
+static bool lcl_ToolBoxTabStop( Window* pWindow )
+{
+ ToolBox* pToolBoxWindow = static_cast<ToolBox*>( pWindow );
+
+ for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < pToolBoxWindow->GetItemCount(); nPos++ )
+ {
+ ToolBoxItemId nId = pToolBoxWindow->GetItemId( nPos );
+ if ( pToolBoxWindow->IsItemVisible( nId ) && pToolBoxWindow->IsItemEnabled( nId ) )
+ return true;
+ }
+
+ return false;
+}
+
+vcl::Window* Window::ImplGetDlgWindow( sal_uInt16 nIndex, GetDlgWindowType nType,
+ sal_uInt16 nFormStart, sal_uInt16 nFormEnd,
+ sal_uInt16* pIndex )
+{
+ SAL_WARN_IF( (nIndex < nFormStart) || (nIndex > nFormEnd), "vcl",
+ "Window::ImplGetDlgWindow() - nIndex not in Form" );
+
+ vcl::Window* pWindow = nullptr;
+ sal_uInt16 i;
+ sal_uInt16 nTemp;
+ sal_uInt16 nStartIndex;
+
+ if ( nType == GetDlgWindowType::Prev )
+ {
+ i = nIndex;
+ do
+ {
+ if ( i > nFormStart )
+ i--;
+ else
+ i = nFormEnd;
+ pWindow = ImplGetChildWindow( this, i, nTemp, true );
+ if ( !pWindow )
+ break;
+ if ( (i == nTemp) && (pWindow->GetStyle() & WB_TABSTOP) )
+ {
+ if ( WindowType::TOOLBOX == pWindow->GetType() )
+ {
+ if ( lcl_ToolBoxTabStop( pWindow ) )
+ break;
+ }
+ else
+ break;
+ }
+ }
+ while ( i != nIndex );
+ }
+ else
+ {
+ i = nIndex;
+ pWindow = ImplGetChildWindow( this, i, i, (nType == GetDlgWindowType::First) );
+ if ( pWindow )
+ {
+ nStartIndex = i;
+
+ if ( nType == GetDlgWindowType::Next )
+ {
+ if ( i < nFormEnd )
+ {
+ pWindow = ImplGetNextWindow( this, i, i, true );
+ if ( (i > nFormEnd) || (i < nFormStart) )
+ pWindow = ImplGetChildWindow( this, nFormStart, i, true );
+ }
+ else
+ pWindow = ImplGetChildWindow( this, nFormStart, i, true );
+ }
+
+ if (i <= nFormEnd && pWindow)
+ {
+ // carry the 2nd index, in case all controls are disabled
+ sal_uInt16 nStartIndex2 = i;
+ sal_uInt16 nOldIndex = i+1;
+
+ do
+ {
+ if ( pWindow->GetStyle() & WB_TABSTOP )
+ {
+ if ( WindowType::TOOLBOX == pWindow->GetType() )
+ {
+ if ( lcl_ToolBoxTabStop( pWindow ) )
+ break;
+ }
+ else
+ break;
+ }
+ if( i == nOldIndex ) // only disabled controls ?
+ {
+ i = nStartIndex2;
+ break;
+ }
+ nOldIndex = i;
+ if ( (i > nFormEnd) || (i < nFormStart) )
+ pWindow = ImplGetChildWindow( this, nFormStart, i, true );
+ else
+ pWindow = ImplGetNextWindow( this, i, i, true );
+ }
+ while (i != nStartIndex && i != nStartIndex2 && pWindow);
+
+ if ( (i == nStartIndex2) && pWindow &&
+ (!(pWindow->GetStyle() & WB_TABSTOP) || !isEnabledInLayout(pWindow)) )
+ i = nStartIndex;
+ }
+ }
+
+ if ( nType == GetDlgWindowType::First )
+ {
+ if ( pWindow )
+ {
+ if ( pWindow->GetType() == WindowType::TABCONTROL )
+ {
+ vcl::Window* pNextWindow = ImplGetDlgWindow( i, GetDlgWindowType::Next );
+ if ( pNextWindow )
+ {
+ if ( pWindow->IsChild( pNextWindow ) )
+ pWindow = pNextWindow;
+ }
+ }
+
+ if ( !(pWindow->GetStyle() & WB_TABSTOP) )
+ pWindow = nullptr;
+ }
+ }
+ }
+
+ if ( pIndex )
+ *pIndex = i;
+
+ return pWindow;
+}
+
+} /* namespace vcl */
+
+vcl::Window* ImplFindDlgCtrlWindow( vcl::Window* pParent, vcl::Window* pWindow, sal_uInt16& rIndex,
+ sal_uInt16& rFormStart, sal_uInt16& rFormEnd )
+{
+ vcl::Window* pSWindow;
+ vcl::Window* pSecondWindow = nullptr;
+ vcl::Window* pTempWindow = nullptr;
+ sal_uInt16 i;
+ sal_uInt16 nSecond_i = 0;
+ sal_uInt16 nFormStart = 0;
+ sal_uInt16 nSecondFormStart = 0;
+ sal_uInt16 nFormEnd;
+
+ // find focus window in the child list
+ vcl::Window* pFirstChildWindow = pSWindow = ImplGetChildWindow( pParent, 0, i, false );
+
+ if( pWindow == nullptr )
+ pWindow = pSWindow;
+
+ while ( pSWindow )
+ {
+ // the DialogControlStart mark is only accepted for the direct children
+ if ( !ImplHasIndirectTabParent( pSWindow )
+ && pSWindow->ImplGetWindow()->IsDialogControlStart() )
+ nFormStart = i;
+
+ // SecondWindow for composite controls like ComboBoxes and arrays
+ if ( pSWindow->ImplIsWindowOrChild( pWindow ) )
+ {
+ pSecondWindow = pSWindow;
+ nSecond_i = i;
+ nSecondFormStart = nFormStart;
+ if ( pSWindow == pWindow )
+ break;
+ }
+
+ pSWindow = ImplGetNextWindow( pParent, i, i, false );
+ if ( !i )
+ pSWindow = nullptr;
+ }
+
+ if ( !pSWindow )
+ {
+ // Window not found; we cannot handle it
+ if ( !pSecondWindow )
+ return nullptr;
+ else
+ {
+ pSWindow = pSecondWindow;
+ i = nSecond_i;
+ nFormStart = nSecondFormStart;
+ }
+ }
+
+ // initialize
+ rIndex = i;
+ rFormStart = nFormStart;
+
+ // find end of template
+ sal_Int32 nIteration = 0;
+ do
+ {
+ nFormEnd = i;
+ pTempWindow = ImplGetNextWindow( pParent, i, i, false );
+
+ // the DialogControlStart mark is only accepted for the direct children
+ if ( !i
+ || ( pTempWindow && !ImplHasIndirectTabParent( pTempWindow )
+ && pTempWindow->ImplGetWindow()->IsDialogControlStart() ) )
+ break;
+
+ if ( pTempWindow && pTempWindow == pFirstChildWindow )
+ {
+ // It is possible to go through the begin of hierarchy once
+ // while looking for DialogControlStart mark.
+ // If it happens second time, it looks like an endless loop,
+ // that should be impossible, but just for the case...
+ nIteration++;
+ if ( nIteration >= 2 )
+ {
+ // this is an unexpected scenario
+ SAL_WARN( "vcl", "It seems to be an endless loop!" );
+ rFormStart = 0;
+ break;
+ }
+ }
+ }
+ while ( pTempWindow );
+ rFormEnd = nFormEnd;
+
+ return pSWindow;
+}
+
+vcl::Window* ImplFindAccelWindow( vcl::Window* pParent, sal_uInt16& rIndex, sal_Unicode cCharCode,
+ sal_uInt16 nFormStart, sal_uInt16 nFormEnd, bool bCheckEnable )
+{
+ SAL_WARN_IF( (rIndex < nFormStart) || (rIndex > nFormEnd), "vcl",
+ "Window::ImplFindAccelWindow() - rIndex not in Form" );
+
+ sal_Unicode cCompareChar;
+ sal_uInt16 nStart = rIndex;
+ sal_uInt16 i = rIndex;
+ vcl::Window* pWindow;
+
+ uno::Reference<i18n::XCharacterClassification> const& xCharClass(ImplGetCharClass());
+
+ const css::lang::Locale& rLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+ cCharCode = xCharClass->toUpper( OUString(cCharCode), 0, 1, rLocale )[0];
+
+ if ( i < nFormEnd )
+ pWindow = ImplGetNextWindow( pParent, i, i, true );
+ else
+ pWindow = ImplGetChildWindow( pParent, nFormStart, i, true );
+ while( pWindow )
+ {
+ const OUString aStr = pWindow->GetText();
+ sal_Int32 nPos = aStr.indexOf( '~' );
+ while (nPos != -1)
+ {
+ cCompareChar = aStr[nPos+1];
+ cCompareChar = xCharClass->toUpper( OUString(cCompareChar), 0, 1, rLocale )[0];
+ if ( cCompareChar == cCharCode )
+ {
+ if (pWindow->GetType() == WindowType::FIXEDTEXT)
+ {
+ FixedText *pFixedText = static_cast<FixedText*>(pWindow);
+ vcl::Window *pMnemonicWidget = pFixedText->get_mnemonic_widget();
+ SAL_WARN_IF(isContainerWindow(pFixedText->GetParent()) && !pMnemonicWidget,
+ "vcl.a11y", "label missing mnemonic_widget?");
+ if (pMnemonicWidget)
+ return pMnemonicWidget;
+ }
+
+ // skip Static-Controls
+ if ( (pWindow->GetType() == WindowType::FIXEDTEXT) ||
+ (pWindow->GetType() == WindowType::FIXEDLINE) ||
+ (pWindow->GetType() == WindowType::GROUPBOX) )
+ pWindow = pParent->ImplGetDlgWindow( i, GetDlgWindowType::Next );
+ rIndex = i;
+ return pWindow;
+ }
+ nPos = aStr.indexOf( '~', nPos+1 );
+ }
+
+ // #i93011# it would have made sense to have this really recursive
+ // right from the start. However this would cause unpredictable side effects now
+ // so instead we have a style bit for some child windows, that want their
+ // children checked for accelerators
+ if( (pWindow->GetStyle() & WB_CHILDDLGCTRL) != 0 )
+ {
+ sal_uInt16 nChildIndex;
+ sal_uInt16 nChildFormStart;
+ sal_uInt16 nChildFormEnd;
+
+ // get form start and end
+ ::ImplFindDlgCtrlWindow( pWindow, nullptr,
+ nChildIndex, nChildFormStart, nChildFormEnd );
+ vcl::Window* pAccelWin = ImplFindAccelWindow( pWindow, nChildIndex, cCharCode,
+ nChildFormStart, nChildFormEnd,
+ bCheckEnable );
+ if( pAccelWin )
+ return pAccelWin;
+ }
+
+ if ( i == nStart )
+ break;
+
+ if ( i < nFormEnd )
+ {
+ pWindow = ImplGetNextWindow( pParent, i, i, bCheckEnable );
+ if( ! pWindow )
+ pWindow = ImplGetChildWindow( pParent, nFormStart, i, bCheckEnable );
+ }
+ else
+ pWindow = ImplGetChildWindow( pParent, nFormStart, i, bCheckEnable );
+ }
+
+ return nullptr;
+}
+
+namespace vcl {
+
+void Window::SetMnemonicActivateHdl(const Link<vcl::Window&, bool>& rLink)
+{
+ if (mpWindowImpl) // may be called after dispose
+ {
+ mpWindowImpl->maMnemonicActivateHdl = rLink;
+ }
+}
+
+void Window::ImplControlFocus( GetFocusFlags nFlags )
+{
+ if ( nFlags & GetFocusFlags::Mnemonic )
+ {
+ if (mpWindowImpl->maMnemonicActivateHdl.Call(*this))
+ return;
+
+ const bool bUniqueMnemonic(nFlags & GetFocusFlags::UniqueMnemonic);
+
+ if ( GetType() == WindowType::RADIOBUTTON )
+ {
+ if (bUniqueMnemonic && !static_cast<RadioButton*>(this)->IsChecked())
+ static_cast<RadioButton*>(this)->ImplCallClick( true, nFlags );
+ else
+ ImplGrabFocus( nFlags );
+ }
+ else
+ {
+ ImplGrabFocus( nFlags );
+ if (bUniqueMnemonic)
+ {
+ if ( GetType() == WindowType::CHECKBOX )
+ static_cast<CheckBox*>(this)->ImplCheck();
+ else if ( mpWindowImpl->mbPushButton )
+ {
+ static_cast<PushButton*>(this)->SetPressed( true );
+ static_cast<PushButton*>(this)->SetPressed( false );
+ static_cast<PushButton*>(this)->Click();
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( GetType() == WindowType::RADIOBUTTON )
+ {
+ if ( !static_cast<RadioButton*>(this)->IsChecked() )
+ static_cast<RadioButton*>(this)->ImplCallClick( true, nFlags );
+ else
+ ImplGrabFocus( nFlags );
+ }
+ else
+ ImplGrabFocus( nFlags );
+ }
+}
+
+} /* namespace vcl */
+
+namespace
+{
+ bool isSuitableDestination(vcl::Window const *pWindow)
+ {
+ return (pWindow && isVisibleInLayout(pWindow) &&
+ isEnabledInLayout(pWindow) && pWindow->IsInputEnabled() &&
+ //Pure window shouldn't get window after controls such as
+ //buttons.
+ (pWindow->GetType() != WindowType::WINDOW &&
+ pWindow->GetType() != WindowType::WORKWINDOW && pWindow->GetType() != WindowType::CONTROL)
+ );
+ }
+
+ bool focusNextInGroup(const std::vector<VclPtr<RadioButton> >::iterator& aStart, std::vector<VclPtr<RadioButton> > &rGroup)
+ {
+ std::vector<VclPtr<RadioButton> >::iterator aI(aStart);
+
+ if (aStart != rGroup.end())
+ ++aI;
+
+ aI = std::find_if(aI, rGroup.end(), isSuitableDestination);
+ if (aI != rGroup.end())
+ {
+ vcl::Window *pWindow = *aI;
+ pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Forward );
+ return true;
+ }
+ aI = std::find_if(rGroup.begin(), aStart, isSuitableDestination);
+ if (aI != aStart)
+ {
+ vcl::Window *pWindow = *aI;
+ pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Forward );
+ return true;
+ }
+ return false;
+ }
+
+ bool nextInGroup(RadioButton *pSourceWindow, bool bBackward)
+ {
+ std::vector<VclPtr<RadioButton> > aGroup(pSourceWindow->GetRadioButtonGroup());
+
+ if (aGroup.size() < 2) // have to have at last 2 buttons to be a useful group
+ return false;
+
+ if (bBackward)
+ std::reverse(aGroup.begin(), aGroup.end());
+
+ auto aStart(std::find(aGroup.begin(), aGroup.end(), VclPtr<RadioButton>(pSourceWindow)));
+
+ assert(aStart != aGroup.end());
+
+ return focusNextInGroup(aStart, aGroup);
+ }
+}
+
+namespace vcl {
+
+bool Window::ImplDlgCtrl( const KeyEvent& rKEvt, bool bKeyInput )
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nKeyCode = aKeyCode.GetCode();
+ vcl::Window* pSWindow;
+ vcl::Window* pTempWindow;
+ vcl::Window* pButtonWindow;
+ sal_uInt16 i;
+ sal_uInt16 iButton;
+ sal_uInt16 iButtonStart;
+ sal_uInt16 iTemp;
+ sal_uInt16 nIndex;
+ sal_uInt16 nFormStart;
+ sal_uInt16 nFormEnd;
+ DialogControlFlags nDlgCtrlFlags;
+
+ // we cannot take over control without Focus-window
+ vcl::Window* pFocusWindow = Application::GetFocusWindow();
+ if ( !pFocusWindow || !ImplIsWindowOrChild( pFocusWindow ) )
+ return false;
+
+ // find Focus-Window in the child list
+ pSWindow = ::ImplFindDlgCtrlWindow( this, pFocusWindow,
+ nIndex, nFormStart, nFormEnd );
+ if ( !pSWindow )
+ return false;
+ i = nIndex;
+
+ nDlgCtrlFlags = DialogControlFlags::NONE;
+ pTempWindow = pSWindow;
+ do
+ {
+ nDlgCtrlFlags |= pTempWindow->GetDialogControlFlags();
+ if ( pTempWindow == this )
+ break;
+ pTempWindow = pTempWindow->ImplGetParent();
+ }
+ while ( pTempWindow );
+
+ pButtonWindow = nullptr;
+
+ if ( nKeyCode == KEY_RETURN )
+ {
+ // search first for a DefPushButton/CancelButton
+ pButtonWindow = ImplGetChildWindow( this, nFormStart, iButton, true );
+ iButtonStart = iButton;
+ while ( pButtonWindow )
+ {
+ if ( (pButtonWindow->GetStyle() & WB_DEFBUTTON) &&
+ pButtonWindow->mpWindowImpl->mbPushButton )
+ break;
+
+ pButtonWindow = ImplGetNextWindow( this, iButton, iButton, true );
+ if ( (iButton <= iButtonStart) || (iButton > nFormEnd) )
+ pButtonWindow = nullptr;
+ }
+
+ if ( bKeyInput && !pButtonWindow && (nDlgCtrlFlags & DialogControlFlags::Return) )
+ {
+ GetDlgWindowType nType;
+ GetFocusFlags nGetFocusFlags = GetFocusFlags::Tab;
+ sal_uInt16 nNewIndex;
+ sal_uInt16 iStart;
+ if ( aKeyCode.IsShift() )
+ {
+ nType = GetDlgWindowType::Prev;
+ nGetFocusFlags |= GetFocusFlags::Backward;
+ }
+ else
+ {
+ nType = GetDlgWindowType::Next;
+ nGetFocusFlags |= GetFocusFlags::Forward;
+ }
+ iStart = i;
+ pTempWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
+ while ( pTempWindow && (pTempWindow != pSWindow) )
+ {
+ if ( !pTempWindow->mpWindowImpl->mbPushButton )
+ {
+ // get Around-Flag
+ if ( nType == GetDlgWindowType::Prev )
+ {
+ if ( nNewIndex > iStart )
+ nGetFocusFlags |= GetFocusFlags::Around;
+ }
+ else
+ {
+ if ( nNewIndex < iStart )
+ nGetFocusFlags |= GetFocusFlags::Around;
+ }
+ pTempWindow->ImplControlFocus( nGetFocusFlags );
+ return true;
+ }
+ else
+ {
+ i = nNewIndex;
+ pTempWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
+ }
+ if ( (i <= iStart) || (i > nFormEnd) )
+ pTempWindow = nullptr;
+ }
+ // if this is the same window, simulate a Get/LoseFocus,
+ // in case AROUND is being processed
+ if ( pTempWindow && (pTempWindow == pSWindow) )
+ {
+ NotifyEvent aNEvt1( NotifyEventType::LOSEFOCUS, pSWindow );
+ if ( !ImplCallPreNotify( aNEvt1 ) )
+ pSWindow->CompatLoseFocus();
+ pSWindow->mpWindowImpl->mnGetFocusFlags = nGetFocusFlags | GetFocusFlags::Around;
+ NotifyEvent aNEvt2( NotifyEventType::GETFOCUS, pSWindow );
+ if ( !ImplCallPreNotify( aNEvt2 ) )
+ pSWindow->CompatGetFocus();
+ pSWindow->mpWindowImpl->mnGetFocusFlags = GetFocusFlags::NONE;
+ return true;
+ }
+ }
+ }
+ else if ( nKeyCode == KEY_ESCAPE )
+ {
+ // search first for a DefPushButton/CancelButton
+ pButtonWindow = ImplGetChildWindow( this, nFormStart, iButton, true );
+ iButtonStart = iButton;
+ while ( pButtonWindow )
+ {
+ if ( pButtonWindow->GetType() == WindowType::CANCELBUTTON )
+ break;
+
+ pButtonWindow = ImplGetNextWindow( this, iButton, iButton, true );
+ if ( (iButton <= iButtonStart) || (iButton > nFormEnd) )
+ pButtonWindow = nullptr;
+ }
+
+ if ( bKeyInput && mpWindowImpl->mpDlgCtrlDownWindow )
+ {
+ if ( mpWindowImpl->mpDlgCtrlDownWindow.get() != pButtonWindow )
+ {
+ mpWindowImpl->mpDlgCtrlDownWindow->SetPressed( false );
+ mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
+ return true;
+ }
+ }
+ }
+ else if ( bKeyInput )
+ {
+ if ( nKeyCode == KEY_TAB )
+ {
+ // do not skip Alt key, for MS Windows
+ if ( !aKeyCode.IsMod2() )
+ {
+ sal_uInt16 nNewIndex;
+ bool bForm = false;
+
+ // for Ctrl-Tab check if we want to jump to next template
+ if ( aKeyCode.IsMod1() )
+ {
+ // search group
+ vcl::Window* pFormFirstWindow = nullptr;
+ vcl::Window* pLastFormFirstWindow = nullptr;
+ pTempWindow = ImplGetChildWindow( this, 0, iTemp, false );
+ vcl::Window* pPrevFirstFormFirstWindow = nullptr;
+ vcl::Window* pFirstFormFirstWindow = pTempWindow;
+ while ( pTempWindow )
+ {
+ if ( pTempWindow->ImplGetWindow()->IsDialogControlStart() )
+ {
+ if ( iTemp != 0 )
+ bForm = true;
+ if ( aKeyCode.IsShift() )
+ {
+ if ( iTemp <= nIndex )
+ pFormFirstWindow = pPrevFirstFormFirstWindow;
+ pPrevFirstFormFirstWindow = pTempWindow;
+ }
+ else
+ {
+ if ( (iTemp > nIndex) && !pFormFirstWindow )
+ pFormFirstWindow = pTempWindow;
+ }
+ pLastFormFirstWindow = pTempWindow;
+ }
+
+ pTempWindow = ImplGetNextWindow( this, iTemp, iTemp, false );
+ if ( !iTemp )
+ pTempWindow = nullptr;
+ }
+
+ if ( bForm )
+ {
+ if ( !pFormFirstWindow )
+ {
+ if ( aKeyCode.IsShift() )
+ pFormFirstWindow = pLastFormFirstWindow;
+ else
+ pFormFirstWindow = pFirstFormFirstWindow;
+ }
+
+ sal_uInt16 nFoundFormStart = 0;
+ sal_uInt16 nFoundFormEnd = 0;
+ sal_uInt16 nTempIndex = 0;
+ if ( ::ImplFindDlgCtrlWindow( this, pFormFirstWindow, nTempIndex,
+ nFoundFormStart, nFoundFormEnd ) )
+ {
+ nTempIndex = nFoundFormStart;
+ pFormFirstWindow = ImplGetDlgWindow( nTempIndex, GetDlgWindowType::First, nFoundFormStart, nFoundFormEnd );
+ if ( pFormFirstWindow )
+ {
+ pFormFirstWindow->ImplControlFocus();
+ return true;
+ }
+ }
+ }
+ }
+
+ if ( !bForm )
+ {
+ // Only use Ctrl-TAB if it was allowed for the whole
+ // dialog or for the current control (#103667#)
+ if (!aKeyCode.IsMod1() || (pSWindow->GetStyle() & WB_NODIALOGCONTROL))
+ {
+ GetDlgWindowType nType;
+ GetFocusFlags nGetFocusFlags = GetFocusFlags::Tab;
+ if ( aKeyCode.IsShift() )
+ {
+ nType = GetDlgWindowType::Prev;
+ nGetFocusFlags |= GetFocusFlags::Backward;
+ }
+ else
+ {
+ nType = GetDlgWindowType::Next;
+ nGetFocusFlags |= GetFocusFlags::Forward;
+ }
+ vcl::Window* pWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
+ // if this is the same window, simulate a Get/LoseFocus,
+ // in case AROUND is being processed
+ if ( pWindow == pSWindow )
+ {
+ NotifyEvent aNEvt1( NotifyEventType::LOSEFOCUS, pSWindow );
+ if ( !ImplCallPreNotify( aNEvt1 ) )
+ pSWindow->CompatLoseFocus();
+ pSWindow->mpWindowImpl->mnGetFocusFlags = nGetFocusFlags | GetFocusFlags::Around;
+ NotifyEvent aNEvt2( NotifyEventType::GETFOCUS, pSWindow );
+ if ( !ImplCallPreNotify( aNEvt2 ) )
+ pSWindow->CompatGetFocus();
+ pSWindow->mpWindowImpl->mnGetFocusFlags = GetFocusFlags::NONE;
+ return true;
+ }
+ else if ( pWindow )
+ {
+ // get Around-Flag
+ if ( nType == GetDlgWindowType::Prev )
+ {
+ if ( nNewIndex > i )
+ nGetFocusFlags |= GetFocusFlags::Around;
+ }
+ else
+ {
+ if ( nNewIndex < i )
+ nGetFocusFlags |= GetFocusFlags::Around;
+ }
+ pWindow->ImplControlFocus( nGetFocusFlags );
+ return true;
+ }
+ }
+ }
+ }
+ }
+ else if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_UP) )
+ {
+ if (pSWindow->GetType() == WindowType::RADIOBUTTON)
+ return nextInGroup(static_cast<RadioButton*>(pSWindow), true);
+ else
+ {
+ WinBits nStyle = pSWindow->GetStyle();
+ if ( !(nStyle & WB_GROUP) )
+ {
+ vcl::Window* pWindow = prevLogicalChildOfParent(this, pSWindow);
+ while ( pWindow )
+ {
+ pWindow = pWindow->ImplGetWindow();
+
+ nStyle = pWindow->GetStyle();
+
+ if (isSuitableDestination(pWindow))
+ {
+ if ( pWindow != pSWindow )
+ pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Backward );
+ return true;
+ }
+
+ if ( nStyle & WB_GROUP )
+ break;
+
+ pWindow = prevLogicalChildOfParent(this, pWindow);
+ }
+ }
+ }
+ }
+ else if ( (nKeyCode == KEY_RIGHT) || (nKeyCode == KEY_DOWN) )
+ {
+ if (pSWindow->GetType() == WindowType::RADIOBUTTON)
+ return nextInGroup(static_cast<RadioButton*>(pSWindow), false);
+ else
+ {
+ vcl::Window* pWindow = nextLogicalChildOfParent(this, pSWindow);
+ while ( pWindow )
+ {
+ pWindow = pWindow->ImplGetWindow();
+
+ WinBits nStyle = pWindow->GetStyle();
+
+ if ( nStyle & WB_GROUP )
+ break;
+
+ if (isSuitableDestination(pWindow))
+ {
+ pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Backward );
+ return true;
+ }
+
+ pWindow = nextLogicalChildOfParent(this, pWindow);
+ }
+ }
+ }
+ else
+ {
+ sal_Unicode c = rKEvt.GetCharCode();
+ if ( c )
+ {
+ pSWindow = ::ImplFindAccelWindow( this, i, c, nFormStart, nFormEnd );
+ if ( pSWindow )
+ {
+ GetFocusFlags nGetFocusFlags = GetFocusFlags::Mnemonic;
+ if ( pSWindow == ::ImplFindAccelWindow( this, i, c, nFormStart, nFormEnd ) )
+ nGetFocusFlags |= GetFocusFlags::UniqueMnemonic;
+#ifdef _WIN32
+ // tdf#157649 Allow omitting the Alt key when focus is in the dialog action area:
+ bool bIsButtonBox = dynamic_cast<VclButtonBox*>(pSWindow->GetParent()) != nullptr;
+ if ((bIsButtonBox && pSWindow->GetParent()->HasChildPathFocus(true)) || aKeyCode.IsMod2())
+#else
+ if (aKeyCode.IsMod2())
+#endif
+ {
+ pSWindow->ImplControlFocus( nGetFocusFlags );
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ if (isSuitableDestination(pButtonWindow))
+ {
+ if ( bKeyInput )
+ {
+ if ( mpWindowImpl->mpDlgCtrlDownWindow && (mpWindowImpl->mpDlgCtrlDownWindow.get() != pButtonWindow) )
+ {
+ mpWindowImpl->mpDlgCtrlDownWindow->SetPressed( false );
+ mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
+ }
+
+ static_cast<PushButton*>(pButtonWindow)->SetPressed( true );
+ mpWindowImpl->mpDlgCtrlDownWindow = static_cast<PushButton*>(pButtonWindow);
+ }
+ else if ( mpWindowImpl->mpDlgCtrlDownWindow.get() == pButtonWindow )
+ {
+ mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
+ static_cast<PushButton*>(pButtonWindow)->SetPressed( false );
+ static_cast<PushButton*>(pButtonWindow)->Click();
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// checks if this window has dialog control
+bool Window::ImplHasDlgCtrl() const
+{
+ vcl::Window* pDlgCtrlParent;
+
+ // lookup window for dialog control
+ pDlgCtrlParent = ImplGetParent();
+ while ( pDlgCtrlParent &&
+ !pDlgCtrlParent->ImplIsOverlapWindow() &&
+ ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL) )
+ pDlgCtrlParent = pDlgCtrlParent->ImplGetParent();
+
+ return pDlgCtrlParent && ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL);
+}
+
+void Window::ImplDlgCtrlNextWindow()
+{
+ vcl::Window* pDlgCtrlParent;
+ vcl::Window* pDlgCtrl;
+ vcl::Window* pSWindow;
+ sal_uInt16 nIndex;
+ sal_uInt16 nFormStart;
+ sal_uInt16 nFormEnd;
+
+ // lookup window for dialog control
+ pDlgCtrl = this;
+ pDlgCtrlParent = ImplGetParent();
+ while ( pDlgCtrlParent &&
+ !pDlgCtrlParent->ImplIsOverlapWindow() &&
+ ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL) )
+ pDlgCtrlParent = pDlgCtrlParent->ImplGetParent();
+
+ if ( !pDlgCtrlParent || (GetStyle() & WB_NODIALOGCONTROL) || ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL) )
+ return;
+
+ // lookup window in child list
+ pSWindow = ::ImplFindDlgCtrlWindow( pDlgCtrlParent, pDlgCtrl,
+ nIndex, nFormStart, nFormEnd );
+ if ( !pSWindow )
+ return;
+
+ vcl::Window* pWindow = pDlgCtrlParent->ImplGetDlgWindow( nIndex, GetDlgWindowType::Next, nFormStart, nFormEnd );
+ if ( pWindow && (pWindow != pSWindow) )
+ pWindow->ImplControlFocus();
+}
+
+static void ImplDlgCtrlUpdateDefButton( vcl::Window* pParent, vcl::Window* pFocusWindow,
+ bool bGetFocus )
+{
+ PushButton* pOldDefButton = nullptr;
+ PushButton* pNewDefButton = nullptr;
+ vcl::Window* pSWindow;
+ sal_uInt16 i;
+ sal_uInt16 nFormStart;
+ sal_uInt16 nFormEnd;
+
+ // find template
+ pSWindow = ::ImplFindDlgCtrlWindow( pParent, pFocusWindow, i, nFormStart, nFormEnd );
+ if ( !pSWindow )
+ {
+ nFormStart = 0;
+ nFormEnd = 0xFFFF;
+ }
+
+ pSWindow = ImplGetChildWindow( pParent, nFormStart, i, false );
+ while ( pSWindow )
+ {
+ if ( pSWindow->ImplIsPushButton() )
+ {
+ PushButton* pPushButton = static_cast<PushButton*>(pSWindow);
+ if ( pPushButton->ImplIsDefButton() )
+ pOldDefButton = pPushButton;
+ if ( pPushButton->HasChildPathFocus() )
+ pNewDefButton = pPushButton;
+ else if ( !pNewDefButton && (pPushButton->GetStyle() & WB_DEFBUTTON) )
+ pNewDefButton = pPushButton;
+ }
+
+ pSWindow = ImplGetNextWindow( pParent, i, i, false );
+ if ( !i || (i > nFormEnd) )
+ pSWindow = nullptr;
+ }
+
+ if ( !bGetFocus )
+ {
+ sal_uInt16 nDummy;
+ vcl::Window* pNewFocusWindow = Application::GetFocusWindow();
+ if ( !pNewFocusWindow || !pParent->ImplIsWindowOrChild( pNewFocusWindow ) )
+ pNewDefButton = nullptr;
+ else if ( !::ImplFindDlgCtrlWindow( pParent, pNewFocusWindow, i, nDummy, nDummy ) ||
+ (i < nFormStart) || (i > nFormEnd) )
+ pNewDefButton = nullptr;
+ }
+
+ if ( pOldDefButton != pNewDefButton )
+ {
+ if ( pOldDefButton )
+ pOldDefButton->ImplSetDefButton( false );
+ if ( pNewDefButton )
+ pNewDefButton->ImplSetDefButton( true );
+ }
+}
+
+void Window::ImplDlgCtrlFocusChanged( vcl::Window* pWindow, bool bGetFocus )
+{
+ if ( mpWindowImpl->mpDlgCtrlDownWindow && !bGetFocus )
+ {
+ mpWindowImpl->mpDlgCtrlDownWindow->SetPressed( false );
+ mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
+ }
+
+ ImplDlgCtrlUpdateDefButton( this, pWindow, bGetFocus );
+}
+
+vcl::Window* Window::ImplFindDlgCtrlWindow( vcl::Window* pWindow )
+{
+ sal_uInt16 nIndex;
+ sal_uInt16 nFormStart;
+ sal_uInt16 nFormEnd;
+
+ // find Focus-Window in the Child-List and return
+ return ::ImplFindDlgCtrlWindow( this, pWindow, nIndex, nFormStart, nFormEnd );
+}
+
+KeyEvent Window::GetActivationKey() const
+{
+ KeyEvent aKeyEvent;
+
+ sal_Unicode nAccel = getAccel( GetText() );
+ if( ! nAccel )
+ {
+ vcl::Window* pWindow = GetAccessibleRelationLabeledBy();
+ if( pWindow )
+ nAccel = getAccel( pWindow->GetText() );
+ }
+ if( nAccel )
+ {
+ sal_uInt16 nCode = 0;
+ if( nAccel >= 'a' && nAccel <= 'z' )
+ nCode = KEY_A + (nAccel-'a');
+ else if( nAccel >= 'A' && nAccel <= 'Z' )
+ nCode = KEY_A + (nAccel-'A');
+ else if( nAccel >= '0' && nAccel <= '9' )
+ nCode = KEY_0 + (nAccel-'0');
+ else if( nAccel == '.' )
+ nCode = KEY_POINT;
+ else if( nAccel == '-' )
+ nCode = KEY_SUBTRACT;
+ vcl::KeyCode aKeyCode( nCode, false, false, true, false );
+ aKeyEvent = KeyEvent( nAccel, aKeyCode );
+ }
+ return aKeyEvent;
+}
+
+} /* namespace vcl */
+
+sal_Unicode getAccel( std::u16string_view rStr )
+{
+ sal_Unicode nChar = 0;
+ size_t nPos = 0;
+ do
+ {
+ nPos = rStr.find( '~', nPos );
+ if( nPos != std::u16string_view::npos && nPos < rStr.size() )
+ nChar = rStr[ ++nPos ];
+ else
+ nChar = 0;
+ } while( nChar == '~' );
+ return nChar;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/dlgctrl.hxx b/vcl/source/window/dlgctrl.hxx
new file mode 100644
index 0000000000..94af911c78
--- /dev/null
+++ b/vcl/source/window/dlgctrl.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <vcl/window.hxx>
+
+vcl::Window* ImplGetChildWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex, bool bTestEnable );
+
+vcl::Window* ImplFindDlgCtrlWindow( vcl::Window* pParent, vcl::Window* pWindow, sal_uInt16& rIndex,
+ sal_uInt16& rFormStart, sal_uInt16& rFormEnd );
+
+vcl::Window* ImplFindAccelWindow( vcl::Window* pParent, sal_uInt16& rIndex, sal_Unicode cCharCode,
+ sal_uInt16 nFormStart, sal_uInt16 nFormEnd, bool bCheckEnable = true );
+
+sal_Unicode getAccel( std::u16string_view rStr );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/dndeventdispatcher.cxx b/vcl/source/window/dndeventdispatcher.cxx
new file mode 100644
index 0000000000..c57841c1fd
--- /dev/null
+++ b/vcl/source/window/dndeventdispatcher.cxx
@@ -0,0 +1,412 @@
+/* -*- 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 <dndeventdispatcher.hxx>
+#include <dndlistenercontainer.hxx>
+#include <sal/log.hxx>
+
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+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::datatransfer::dnd;
+
+DNDEventDispatcher::DNDEventDispatcher( vcl::Window * pTopWindow ):
+ m_pTopWindow( pTopWindow ),
+ m_pCurrentWindow( nullptr )
+{
+}
+
+DNDEventDispatcher::~DNDEventDispatcher()
+{
+ designate_currentwindow(nullptr);
+}
+
+vcl::Window* DNDEventDispatcher::findTopLevelWindow(Point& location)
+{
+ SolarMutexGuard aSolarGuard;
+
+ // find the window that is toplevel for this coordinates
+ // because those coordinates come from outside, they must be mirrored if RTL layout is active
+ if( AllSettings::GetLayoutRTL() )
+ m_pTopWindow->ImplMirrorFramePos( location );
+ vcl::Window * pChildWindow = m_pTopWindow->ImplFindWindow( location );
+
+ if( nullptr == pChildWindow )
+ pChildWindow = m_pTopWindow;
+
+ while( pChildWindow->ImplGetClientWindow() )
+ pChildWindow = pChildWindow->ImplGetClientWindow();
+
+ if( pChildWindow->GetOutDev()->ImplIsAntiparallel() )
+ {
+ const OutputDevice *pChildWinOutDev = pChildWindow->GetOutDev();
+ pChildWinOutDev->ReMirror( location );
+ }
+
+ return pChildWindow;
+}
+
+IMPL_LINK(DNDEventDispatcher, WindowEventListener, VclWindowEvent&, rEvent, void)
+{
+ if (rEvent.GetId() == VclEventId::ObjectDying)
+ {
+ designate_currentwindow(nullptr);
+ }
+}
+
+void DNDEventDispatcher::designate_currentwindow(vcl::Window *pWindow)
+{
+ if (m_pCurrentWindow)
+ m_pCurrentWindow->RemoveEventListener(LINK(this, DNDEventDispatcher, WindowEventListener));
+ m_pCurrentWindow = pWindow;
+ if (m_pCurrentWindow)
+ m_pCurrentWindow->AddEventListener(LINK(this, DNDEventDispatcher, WindowEventListener));
+}
+
+void SAL_CALL DNDEventDispatcher::drop( const DropTargetDropEvent& dtde )
+{
+ std::scoped_lock aImplGuard( m_aMutex );
+
+ Point location( dtde.LocationX, dtde.LocationY );
+
+ vcl::Window* pChildWindow = findTopLevelWindow(location);
+
+ // handle the case that drop is in another vcl window than the last dragOver
+ if( pChildWindow != m_pCurrentWindow.get() )
+ {
+ // fire dragExit on listeners of previous window
+ fireDragExitEvent( m_pCurrentWindow );
+
+ fireDragEnterEvent( pChildWindow, static_cast < XDropTargetDragContext * > (this),
+ dtde.DropAction, location, dtde.SourceActions, m_aDataFlavorList );
+ }
+
+ // send drop event to the child window
+ sal_Int32 nListeners = fireDropEvent( pChildWindow, dtde.Context, dtde.DropAction,
+ location, dtde.SourceActions, dtde.Transferable );
+
+ // reject drop if no listeners found
+ if( nListeners == 0 ) {
+ SAL_WARN( "vcl", "rejecting drop due to missing listeners." );
+ dtde.Context->rejectDrop();
+ }
+
+ // this is a drop -> no further drag overs
+ designate_currentwindow(nullptr);
+ m_aDataFlavorList.realloc( 0 );
+}
+
+void SAL_CALL DNDEventDispatcher::dragEnter( const DropTargetDragEnterEvent& dtdee )
+{
+ std::scoped_lock aImplGuard( m_aMutex );
+ Point location( dtdee.LocationX, dtdee.LocationY );
+
+ vcl::Window * pChildWindow = findTopLevelWindow(location);
+
+ // assume pointer write operation to be atomic
+ designate_currentwindow(pChildWindow);
+ m_aDataFlavorList = dtdee.SupportedDataFlavors;
+
+ // fire dragEnter on listeners of current window
+ sal_Int32 nListeners = fireDragEnterEvent( pChildWindow, dtdee.Context, dtdee.DropAction, location,
+ dtdee.SourceActions, dtdee.SupportedDataFlavors );
+
+ // reject drag if no listener found
+ if( nListeners == 0 ) {
+ SAL_WARN( "vcl", "rejecting drag enter due to missing listeners." );
+ dtdee.Context->rejectDrag();
+ }
+
+}
+
+void SAL_CALL DNDEventDispatcher::dragExit( const DropTargetEvent& /*dte*/ )
+{
+ std::scoped_lock aImplGuard( m_aMutex );
+
+ fireDragExitEvent( m_pCurrentWindow );
+
+ // reset member values
+ designate_currentwindow(nullptr);
+ m_aDataFlavorList.realloc( 0 );
+}
+
+void SAL_CALL DNDEventDispatcher::dragOver( const DropTargetDragEvent& dtde )
+{
+ std::scoped_lock aImplGuard( m_aMutex );
+
+ Point location( dtde.LocationX, dtde.LocationY );
+ sal_Int32 nListeners;
+
+ vcl::Window * pChildWindow = findTopLevelWindow(location);
+
+ if( pChildWindow != m_pCurrentWindow.get() )
+ {
+ // fire dragExit on listeners of previous window
+ fireDragExitEvent( m_pCurrentWindow );
+
+ // remember new window
+ designate_currentwindow(pChildWindow);
+
+ // fire dragEnter on listeners of current window
+ nListeners = fireDragEnterEvent( pChildWindow, dtde.Context, dtde.DropAction, location,
+ dtde.SourceActions, m_aDataFlavorList );
+ }
+ else
+ {
+ // fire dragOver on listeners of current window
+ nListeners = fireDragOverEvent( pChildWindow, dtde.Context, dtde.DropAction, location,
+ dtde.SourceActions );
+ }
+
+ // reject drag if no listener found
+ if( nListeners == 0 )
+ {
+ SAL_WARN( "vcl", "rejecting drag over due to missing listeners." );
+ dtde.Context->rejectDrag();
+ }
+}
+
+void SAL_CALL DNDEventDispatcher::dropActionChanged( const DropTargetDragEvent& dtde )
+{
+ std::scoped_lock aImplGuard( m_aMutex );
+
+ Point location( dtde.LocationX, dtde.LocationY );
+ sal_Int32 nListeners;
+
+ vcl::Window* pChildWindow = findTopLevelWindow(location);
+
+ if( pChildWindow != m_pCurrentWindow.get() )
+ {
+ // fire dragExit on listeners of previous window
+ fireDragExitEvent( m_pCurrentWindow );
+
+ // remember new window
+ designate_currentwindow(pChildWindow);
+
+ // fire dragEnter on listeners of current window
+ nListeners = fireDragEnterEvent( pChildWindow, dtde.Context, dtde.DropAction, location,
+ dtde.SourceActions, m_aDataFlavorList );
+ }
+ else
+ {
+ // fire dropActionChanged on listeners of current window
+ nListeners = fireDropActionChangedEvent( pChildWindow, dtde.Context, dtde.DropAction, location,
+ dtde.SourceActions );
+ }
+
+ // reject drag if no listener found
+ if( nListeners == 0 )
+ {
+ SAL_WARN( "vcl", "rejecting dropActionChanged due to missing listeners." );
+ dtde.Context->rejectDrag();
+ }
+}
+
+void SAL_CALL DNDEventDispatcher::dragGestureRecognized( const DragGestureEvent& dge )
+{
+ std::scoped_lock aImplGuard( m_aMutex );
+
+ Point origin( dge.DragOriginX, dge.DragOriginY );
+
+ vcl::Window* pChildWindow = findTopLevelWindow(origin);
+
+ fireDragGestureEvent( pChildWindow, dge.DragSource, dge.Event, origin, dge.DragAction );
+}
+
+void SAL_CALL DNDEventDispatcher::disposing( const EventObject& )
+{
+}
+
+void SAL_CALL DNDEventDispatcher::acceptDrag( sal_Int8 /*dropAction*/ )
+{
+}
+
+void SAL_CALL DNDEventDispatcher::rejectDrag()
+{
+}
+
+sal_Int32 DNDEventDispatcher::fireDragEnterEvent( vcl::Window *pWindow,
+ const Reference< XDropTargetDragContext >& xContext, const sal_Int8 nDropAction,
+ const Point& rLocation, const sal_Int8 nSourceActions, const Sequence< DataFlavor >& aFlavorList
+)
+{
+ sal_Int32 n = 0;
+
+ if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
+ {
+ SolarMutexClearableGuard aSolarGuard;
+
+ // query DropTarget from window
+ Reference< XDropTarget > xDropTarget = pWindow->GetDropTarget();
+
+ if( xDropTarget.is() )
+ {
+ // retrieve relative mouse position
+ Point relLoc = pWindow->ScreenToOutputPixel( rLocation );
+ aSolarGuard.clear();
+
+ n = static_cast < DNDListenerContainer * > ( xDropTarget.get() )->fireDragEnterEvent(
+ xContext, nDropAction, relLoc.X(), relLoc.Y(), nSourceActions, aFlavorList );
+ }
+ }
+
+ return n;
+}
+
+sal_Int32 DNDEventDispatcher::fireDragOverEvent( vcl::Window *pWindow,
+ const Reference< XDropTargetDragContext >& xContext, const sal_Int8 nDropAction,
+ const Point& rLocation, const sal_Int8 nSourceActions
+)
+{
+ sal_Int32 n = 0;
+
+ if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
+ {
+ SolarMutexClearableGuard aSolarGuard;
+
+ // query DropTarget from window
+ Reference< XDropTarget > xDropTarget = pWindow->GetDropTarget();
+
+ if( xDropTarget.is() )
+ {
+ // retrieve relative mouse position
+ Point relLoc = pWindow->ScreenToOutputPixel( rLocation );
+ aSolarGuard.clear();
+
+ n = static_cast < DNDListenerContainer * > ( xDropTarget.get() )->fireDragOverEvent(
+ xContext, nDropAction, relLoc.X(), relLoc.Y(), nSourceActions );
+ }
+ }
+
+ return n;
+}
+
+sal_Int32 DNDEventDispatcher::fireDragExitEvent( vcl::Window *pWindow )
+{
+ sal_Int32 n = 0;
+
+ if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
+ {
+ SolarMutexClearableGuard aGuard;
+
+ // query DropTarget from window
+ Reference< XDropTarget > xDropTarget = pWindow->GetDropTarget();
+
+ aGuard.clear();
+
+ if( xDropTarget.is() )
+ n = static_cast < DNDListenerContainer * > ( xDropTarget.get() )->fireDragExitEvent();
+ }
+
+ return n;
+}
+
+sal_Int32 DNDEventDispatcher::fireDropActionChangedEvent( vcl::Window *pWindow,
+ const Reference< XDropTargetDragContext >& xContext, const sal_Int8 nDropAction,
+ const Point& rLocation, const sal_Int8 nSourceActions
+)
+{
+ sal_Int32 n = 0;
+
+ if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
+ {
+ SolarMutexClearableGuard aGuard;
+
+ // query DropTarget from window
+ Reference< XDropTarget > xDropTarget = pWindow->GetDropTarget();
+
+ if( xDropTarget.is() )
+ {
+ // retrieve relative mouse position
+ Point relLoc = pWindow->ScreenToOutputPixel( rLocation );
+ aGuard.clear();
+
+ n = static_cast < DNDListenerContainer * > ( xDropTarget.get() )->fireDropActionChangedEvent(
+ xContext, nDropAction, relLoc.X(), relLoc.Y(), nSourceActions );
+ }
+ }
+
+ return n;
+}
+
+sal_Int32 DNDEventDispatcher::fireDropEvent( vcl::Window *pWindow,
+ const Reference< XDropTargetDropContext >& xContext, const sal_Int8 nDropAction, const Point& rLocation,
+ const sal_Int8 nSourceActions, const Reference< XTransferable >& xTransferable
+)
+{
+ sal_Int32 n = 0;
+
+ if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
+ {
+ SolarMutexClearableGuard aGuard;
+
+ // query DropTarget from window
+ Reference< XDropTarget > xDropTarget = pWindow->GetDropTarget();
+
+ // window may be destroyed in drop event handler
+ VclPtr<vcl::Window> xPreventDelete = pWindow;
+
+ if( xDropTarget.is() )
+ {
+ // retrieve relative mouse position
+ Point relLoc = pWindow->ScreenToOutputPixel( rLocation );
+ aGuard.clear();
+
+ n = static_cast < DNDListenerContainer * > ( xDropTarget.get() )->fireDropEvent(
+ xContext, nDropAction, relLoc.X(), relLoc.Y(), nSourceActions, xTransferable );
+ }
+ }
+
+ return n;
+}
+
+sal_Int32 DNDEventDispatcher::fireDragGestureEvent( vcl::Window *pWindow,
+ const Reference< XDragSource >& xSource, const Any& event,
+ const Point& rOrigin, const sal_Int8 nDragAction
+)
+{
+ sal_Int32 n = 0;
+
+ if( pWindow && pWindow->IsInputEnabled() && ! pWindow->IsInModalMode() )
+ {
+ SolarMutexClearableGuard aGuard;
+
+ // query DropTarget from window
+ Reference< XDragGestureRecognizer > xDragGestureRecognizer = pWindow->GetDragGestureRecognizer();
+
+ if( xDragGestureRecognizer.is() )
+ {
+ // retrieve relative mouse position
+ Point relLoc = pWindow->ScreenToOutputPixel( rOrigin );
+ aGuard.clear();
+
+ n = static_cast < DNDListenerContainer * > ( xDragGestureRecognizer.get() )->fireDragGestureEvent(
+ nDragAction, relLoc.X(), relLoc.Y(), xSource, event );
+ }
+ }
+
+ return n;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/dndlistenercontainer.cxx b/vcl/source/window/dndlistenercontainer.cxx
new file mode 100644
index 0000000000..9ff128c808
--- /dev/null
+++ b/vcl/source/window/dndlistenercontainer.cxx
@@ -0,0 +1,455 @@
+/* -*- 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 <dndlistenercontainer.hxx>
+
+using namespace ::cppu;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::datatransfer::dnd;
+
+DNDListenerContainer::DNDListenerContainer( sal_Int8 nDefaultActions )
+{
+ m_bActive = true;
+ m_nDefaultActions = nDefaultActions;
+}
+
+DNDListenerContainer::~DNDListenerContainer()
+{
+}
+
+void SAL_CALL DNDListenerContainer::addDragGestureListener( const Reference< XDragGestureListener >& dgl )
+{
+ std::unique_lock g(m_aMutex);
+ maDragGestureListeners.addInterface( g, dgl );
+}
+
+void SAL_CALL DNDListenerContainer::removeDragGestureListener( const Reference< XDragGestureListener >& dgl )
+{
+ std::unique_lock g(m_aMutex);
+ maDragGestureListeners.removeInterface( g, dgl );
+}
+
+void SAL_CALL DNDListenerContainer::resetRecognizer( )
+{
+}
+
+void SAL_CALL DNDListenerContainer::addDropTargetListener( const Reference< XDropTargetListener >& dtl )
+{
+ std::unique_lock g(m_aMutex);
+ maDropTargetListeners.addInterface( g, dtl );
+}
+
+void SAL_CALL DNDListenerContainer::removeDropTargetListener( const Reference< XDropTargetListener >& dtl )
+{
+ std::unique_lock g(m_aMutex);
+ maDropTargetListeners.removeInterface( g, dtl );
+}
+
+sal_Bool SAL_CALL DNDListenerContainer::isActive( )
+{
+ return m_bActive;
+}
+
+void SAL_CALL DNDListenerContainer::setActive( sal_Bool active )
+{
+ m_bActive = active;
+}
+
+sal_Int8 SAL_CALL DNDListenerContainer::getDefaultActions( )
+{
+ return m_nDefaultActions;
+}
+
+void SAL_CALL DNDListenerContainer::setDefaultActions( sal_Int8 actions )
+{
+ m_nDefaultActions = actions;
+}
+
+sal_uInt32 DNDListenerContainer::fireDropEvent( const Reference< XDropTargetDropContext >& context,
+ sal_Int8 dropAction, sal_Int32 locationX, sal_Int32 locationY, sal_Int8 sourceActions,
+ const Reference< XTransferable >& transferable )
+{
+ std::unique_lock g(m_aMutex);
+ if (!m_bActive || maDropTargetListeners.getLength(g) == 0)
+ return 0;
+
+ sal_uInt32 nRet = 0;
+
+ comphelper::OInterfaceIteratorHelper4 aIterator( g, maDropTargetListeners );
+
+ // remember context to use in own context methods
+ m_xDropTargetDropContext = context;
+
+ // do not construct the event before you are sure at least one listener is registered
+ DropTargetDropEvent aEvent( static_cast < XDropTarget * > (this), 0,
+ static_cast < XDropTargetDropContext * > (this), dropAction,
+ locationX, locationY, sourceActions, transferable );
+
+ while (aIterator.hasMoreElements())
+ {
+ Reference< XDropTargetListener > xListener( aIterator.next() );
+ try
+ {
+ // fire drop until the first one has accepted
+ if( m_xDropTargetDropContext.is() )
+ {
+ g.unlock();
+ xListener->drop( aEvent );
+ }
+ else
+ {
+ g.unlock();
+ DropTargetEvent aDTEvent( static_cast < XDropTarget * > (this), 0 );
+ xListener->dragExit( aDTEvent );
+ }
+
+ g.lock();
+ nRet++;
+ }
+ catch (const RuntimeException&)
+ {
+ aIterator.remove( g );
+ }
+ }
+
+ // if context still valid, then reject drop
+ if( m_xDropTargetDropContext.is() )
+ {
+ m_xDropTargetDropContext.clear();
+
+ try
+ {
+ context->rejectDrop();
+ }
+ catch (const RuntimeException&)
+ {
+ }
+ }
+
+ return nRet;
+}
+
+sal_uInt32 DNDListenerContainer::fireDragExitEvent()
+{
+ std::unique_lock g(m_aMutex);
+ if (!m_bActive || maDropTargetListeners.getLength(g) == 0)
+ return 0;
+
+ sal_uInt32 nRet = 0;
+
+ // do not construct the event before you are sure at least one listener is registered
+ DropTargetEvent aEvent( static_cast < XDropTarget * > (this), 0 );
+
+ comphelper::OInterfaceIteratorHelper4 aIterator( g, maDropTargetListeners );
+ g.unlock();
+ while (aIterator.hasMoreElements())
+ {
+ Reference< XDropTargetListener > xListener( aIterator.next() );
+ try
+ {
+ xListener->dragExit( aEvent );
+ nRet++;
+ }
+ catch (const RuntimeException&)
+ {
+ g.lock();
+ aIterator.remove( g );
+ g.unlock();
+ }
+ }
+
+ return nRet;
+}
+
+sal_uInt32 DNDListenerContainer::fireDragOverEvent( const Reference< XDropTargetDragContext >& context,
+ sal_Int8 dropAction, sal_Int32 locationX, sal_Int32 locationY, sal_Int8 sourceActions )
+{
+ std::unique_lock g(m_aMutex);
+ if (!m_bActive || maDropTargetListeners.getLength(g) == 0)
+ return 0;
+
+ sal_uInt32 nRet = 0;
+
+ // fire DropTargetDropEvent on all XDropTargetListeners
+
+ comphelper::OInterfaceIteratorHelper4 aIterator( g, maDropTargetListeners );
+
+ // remember context to use in own context methods
+ m_xDropTargetDragContext = context;
+
+ // do not construct the event before you are sure at least one listener is registered
+ DropTargetDragEvent aEvent( static_cast < XDropTarget * > (this), 0,
+ static_cast < XDropTargetDragContext * > (this),
+ dropAction, locationX, locationY, sourceActions );
+
+ while (aIterator.hasMoreElements())
+ {
+ Reference< XDropTargetListener > xListener( aIterator.next() );
+ try
+ {
+ if( m_xDropTargetDragContext.is() )
+ {
+ g.unlock();
+ xListener->dragOver( aEvent );
+ g.lock();
+ }
+ nRet++;
+ }
+ catch (const RuntimeException&)
+ {
+ aIterator.remove(g);
+ }
+ }
+
+ // if context still valid, then reject drag
+ if( m_xDropTargetDragContext.is() )
+ {
+ m_xDropTargetDragContext.clear();
+
+ try
+ {
+ context->rejectDrag();
+ }
+ catch (const RuntimeException&)
+ {
+ }
+ }
+
+ return nRet;
+}
+
+sal_uInt32 DNDListenerContainer::fireDragEnterEvent( const Reference< XDropTargetDragContext >& context,
+ sal_Int8 dropAction, sal_Int32 locationX, sal_Int32 locationY, sal_Int8 sourceActions,
+ const Sequence< DataFlavor >& dataFlavors )
+{
+ std::unique_lock g(m_aMutex);
+ if (!m_bActive || maDropTargetListeners.getLength(g) == 0)
+ return 0;
+
+ sal_uInt32 nRet = 0;
+
+ comphelper::OInterfaceIteratorHelper4 aIterator( g, maDropTargetListeners );
+
+ // remember context to use in own context methods
+ m_xDropTargetDragContext = context;
+
+ // do not construct the event before you are sure at least one listener is registered
+ DropTargetDragEnterEvent aEvent( static_cast < XDropTarget * > (this), 0,
+ static_cast < XDropTargetDragContext * > (this),
+ dropAction, locationX, locationY, sourceActions, dataFlavors );
+
+ while (aIterator.hasMoreElements())
+ {
+ Reference< XDropTargetListener > xListener( aIterator.next() );
+ try
+ {
+ if( m_xDropTargetDragContext.is() )
+ {
+ g.unlock();
+ xListener->dragEnter( aEvent );
+ g.lock();
+ }
+ nRet++;
+ }
+ catch (const RuntimeException&)
+ {
+ aIterator.remove( g );
+ }
+ }
+
+ // if context still valid, then reject drag
+ if( m_xDropTargetDragContext.is() )
+ {
+ m_xDropTargetDragContext.clear();
+
+ try
+ {
+ context->rejectDrag();
+ }
+ catch (const RuntimeException&)
+ {
+ }
+ }
+
+ return nRet;
+}
+
+sal_uInt32 DNDListenerContainer::fireDropActionChangedEvent( const Reference< XDropTargetDragContext >& context,
+ sal_Int8 dropAction, sal_Int32 locationX, sal_Int32 locationY, sal_Int8 sourceActions )
+{
+ std::unique_lock g(m_aMutex);
+ if (!m_bActive || maDropTargetListeners.getLength(g) == 0)
+ return 0;
+
+ sal_uInt32 nRet = 0;
+
+ // fire DropTargetDropEvent on all XDropTargetListeners
+
+ comphelper::OInterfaceIteratorHelper4 aIterator( g, maDropTargetListeners );
+
+ // remember context to use in own context methods
+ m_xDropTargetDragContext = context;
+
+ // do not construct the event before you are sure at least one listener is registered
+ DropTargetDragEvent aEvent( static_cast < XDropTarget * > (this), 0,
+ static_cast < XDropTargetDragContext * > (this),
+ dropAction, locationX, locationY, sourceActions );
+
+ while (aIterator.hasMoreElements())
+ {
+ Reference< XDropTargetListener > xListener( aIterator.next() );
+ try
+ {
+ if( m_xDropTargetDragContext.is() )
+ {
+ g.unlock();
+ xListener->dropActionChanged( aEvent );
+ g.lock();
+ }
+ nRet++;
+ }
+ catch (const RuntimeException&)
+ {
+ aIterator.remove( g );
+ }
+ }
+
+ // if context still valid, then reject drag
+ if( m_xDropTargetDragContext.is() )
+ {
+ m_xDropTargetDragContext.clear();
+
+ try
+ {
+ context->rejectDrag();
+ }
+ catch (const RuntimeException&)
+ {
+ }
+ }
+
+ return nRet;
+}
+
+sal_uInt32 DNDListenerContainer::fireDragGestureEvent( sal_Int8 dragAction, sal_Int32 dragOriginX,
+ sal_Int32 dragOriginY, const Reference< XDragSource >& dragSource, const Any& triggerEvent )
+{
+ std::unique_lock g(m_aMutex);
+ if (maDragGestureListeners.getLength(g) == 0)
+ return 0;
+
+ sal_uInt32 nRet = 0;
+
+ // do not construct the event before you are sure at least one listener is registered
+ DragGestureEvent aEvent( static_cast < XDragGestureRecognizer * > (this), dragAction,
+ dragOriginX, dragOriginY, dragSource, triggerEvent );
+
+ comphelper::OInterfaceIteratorHelper4 aIterator( g, maDragGestureListeners );
+ g.unlock();
+ while( aIterator.hasMoreElements() )
+ {
+ Reference< XDragGestureListener > xListener( aIterator.next() );
+ try
+ {
+ xListener->dragGestureRecognized( aEvent );
+ nRet++;
+ }
+ catch (const RuntimeException&)
+ {
+ g.lock();
+ aIterator.remove( g );
+ g.unlock();
+ }
+ }
+
+ return nRet;
+}
+
+void SAL_CALL DNDListenerContainer::acceptDrag( sal_Int8 dragOperation )
+{
+ if( m_xDropTargetDragContext.is() )
+ {
+ m_xDropTargetDragContext->acceptDrag( dragOperation );
+ m_xDropTargetDragContext.clear();
+ }
+}
+
+void SAL_CALL DNDListenerContainer::rejectDrag( )
+{
+ // nothing to do here
+}
+
+void SAL_CALL DNDListenerContainer::acceptDrop( sal_Int8 dropOperation )
+{
+ if( m_xDropTargetDropContext.is() )
+ m_xDropTargetDropContext->acceptDrop( dropOperation );
+}
+
+void SAL_CALL DNDListenerContainer::rejectDrop( )
+{
+ // nothing to do here
+}
+
+void SAL_CALL DNDListenerContainer::dropComplete( sal_Bool success )
+{
+ if( m_xDropTargetDropContext.is() )
+ {
+ m_xDropTargetDropContext->dropComplete( success );
+ m_xDropTargetDropContext.clear();
+ }
+}
+
+/*
+ * GenericDropTargetDropContext
+ */
+
+GenericDropTargetDropContext::GenericDropTargetDropContext()
+{
+}
+
+void GenericDropTargetDropContext::acceptDrop( sal_Int8 /*dragOperation*/ )
+{
+}
+
+void GenericDropTargetDropContext::rejectDrop()
+{
+}
+
+void GenericDropTargetDropContext::dropComplete( sal_Bool /*success*/ )
+{
+}
+
+/*
+ * GenericDropTargetDragContext
+ */
+
+GenericDropTargetDragContext::GenericDropTargetDragContext()
+{
+}
+
+void GenericDropTargetDragContext::acceptDrag( sal_Int8 /*dragOperation*/ )
+{
+}
+
+void GenericDropTargetDragContext::rejectDrag()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/dockingarea.cxx b/vcl/source/window/dockingarea.cxx
new file mode 100644
index 0000000000..be3f6ef99e
--- /dev/null
+++ b/vcl/source/window/dockingarea.cxx
@@ -0,0 +1,257 @@
+/* -*- 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 <vcl/dockingarea.hxx>
+#include <vcl/syswin.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/event.hxx>
+#include <toolbarvalue.hxx>
+
+#include <svdata.hxx>
+
+#include <map>
+
+class DockingAreaWindow::ImplData
+{
+public:
+ ImplData();
+
+ WindowAlign meAlign;
+};
+
+DockingAreaWindow::ImplData::ImplData()
+{
+ meAlign = WindowAlign::Top;
+}
+
+DockingAreaWindow::DockingAreaWindow( vcl::Window* pParent ) :
+ Window( WindowType::DOCKINGAREA )
+{
+ ImplInit( pParent, WB_CLIPCHILDREN|WB_3DLOOK, nullptr );
+
+ mpImplData.reset(new ImplData);
+}
+
+DockingAreaWindow::~DockingAreaWindow()
+{
+ disposeOnce();
+}
+
+void DockingAreaWindow::dispose()
+{
+ mpImplData.reset();
+ Window::dispose();
+}
+
+void DockingAreaWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ Invalidate();
+ }
+}
+
+static void ImplInvalidateMenubar( DockingAreaWindow const * pThis )
+{
+ // due to a possible common gradient covering menubar and top dockingarea
+ // the menubar must be repainted if the top dockingarea changes size or visibility
+ if( ImplGetSVData()->maNWFData.mbMenuBarDockingAreaCommonBG &&
+ (pThis->GetAlign() == WindowAlign::Top)
+ && pThis->IsNativeControlSupported( ControlType::Toolbar, ControlPart::Entire )
+ && pThis->IsNativeControlSupported( ControlType::Menubar, ControlPart::Entire ) )
+ {
+ SystemWindow *pSysWin = pThis->GetSystemWindow();
+ if( pSysWin && pSysWin->GetMenuBar() )
+ {
+ vcl::Window *pMenubarWin = pSysWin->GetMenuBar()->GetWindow();
+ if( pMenubarWin )
+ pMenubarWin->Invalidate();
+ }
+ }
+}
+
+void DockingAreaWindow::StateChanged( StateChangedType nType )
+{
+ Window::StateChanged( nType );
+
+ if ( nType == StateChangedType::Visible )
+ ImplInvalidateMenubar( this );
+}
+
+bool DockingAreaWindow::IsHorizontal() const
+{
+ return ( mpImplData->meAlign == WindowAlign::Top || mpImplData->meAlign == WindowAlign::Bottom );
+}
+
+void DockingAreaWindow::SetAlign( WindowAlign eNewAlign )
+{
+ if( eNewAlign != mpImplData->meAlign )
+ {
+ mpImplData->meAlign = eNewAlign;
+ Invalidate();
+ }
+}
+
+WindowAlign DockingAreaWindow::GetAlign() const
+{
+ return mpImplData->meAlign;
+}
+
+void DockingAreaWindow::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ const StyleSettings rSetting = rRenderContext.GetSettings().GetStyleSettings();
+ const BitmapEx& rPersonaBitmap = (GetAlign() == WindowAlign::Top) ? rSetting.GetPersonaHeader() : rSetting.GetPersonaFooter();
+
+ if (!rPersonaBitmap.IsEmpty() && (GetAlign() == WindowAlign::Top || GetAlign()==WindowAlign::Bottom))
+ {
+ Wallpaper aWallpaper(rPersonaBitmap);
+ if (GetAlign() == WindowAlign::Top)
+ aWallpaper.SetStyle(WallpaperStyle::TopRight);
+ else
+ aWallpaper.SetStyle(WallpaperStyle::BottomRight);
+ aWallpaper.SetColor(rSetting.GetWorkspaceColor());
+
+ // we need to shift the bitmap vertically so that it spans over the
+ // menubar conveniently
+ SystemWindow* pSysWin = GetSystemWindow();
+ MenuBar* pMenuBar = pSysWin ? pSysWin->GetMenuBar() : nullptr;
+ int nMenubarHeight = pMenuBar ? pMenuBar->GetMenuBarHeight() : 0;
+ aWallpaper.SetRect(tools::Rectangle(Point(0, -nMenubarHeight),
+ Size(rRenderContext.GetOutputWidthPixel(),
+ rRenderContext.GetOutputHeightPixel() + nMenubarHeight)));
+
+ rRenderContext.SetBackground(aWallpaper);
+ }
+ else if (!rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Entire))
+ {
+ Wallpaper aWallpaper;
+ aWallpaper.SetStyle(WallpaperStyle::ApplicationGradient);
+ rRenderContext.SetBackground(aWallpaper);
+ }
+ else
+ rRenderContext.SetBackground(Wallpaper(rSetting.GetFaceColor()));
+
+}
+
+void DockingAreaWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ const StyleSettings rSetting = rRenderContext.GetSettings().GetStyleSettings();
+
+ EnableNativeWidget(); // only required because the toolkit currently switches this flag off
+ if (!rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Entire))
+ return;
+
+ ToolbarValue aControlValue;
+
+ if (GetAlign() == WindowAlign::Top && ImplGetSVData()->maNWFData.mbMenuBarDockingAreaCommonBG)
+ {
+ // give NWF a hint that this dockingarea is adjacent to the menubar
+ // useful for special gradient effects that should cover both windows
+ aControlValue.mbIsTopDockingArea = true;
+ }
+
+ ControlState nState = ControlState::ENABLED;
+ const bool isFooter = GetAlign() == WindowAlign::Bottom && !rSetting.GetPersonaFooter().IsEmpty();
+
+ if ((GetAlign() == WindowAlign::Top && !rSetting.GetPersonaHeader().IsEmpty() ) || isFooter)
+ Erase(rRenderContext);
+ else if (!ImplGetSVData()->maNWFData.mbDockingAreaSeparateTB)
+ {
+ // draw a single toolbar background covering the whole docking area
+ tools::Rectangle aCtrlRegion(Point(), GetOutputSizePixel());
+
+ rRenderContext.DrawNativeControl(ControlType::Toolbar, IsHorizontal() ? ControlPart::DrawBackgroundHorz : ControlPart::DrawBackgroundVert,
+ aCtrlRegion, nState, aControlValue, OUString() );
+
+ if (!ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames)
+ {
+ // each toolbar gets a thin border to better recognize its borders on the homogeneous docking area
+ sal_uInt16 nChildren = GetChildCount();
+ for (sal_uInt16 n = 0; n < nChildren; n++)
+ {
+ vcl::Window* pChild = GetChild(n);
+ if (pChild->IsVisible())
+ {
+ Point aPos = pChild->GetPosPixel();
+ Size aSize = pChild->GetSizePixel();
+ tools::Rectangle aRect(aPos, aSize);
+
+ rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetLightColor());
+ rRenderContext.DrawLine(aRect.TopLeft(), aRect.TopRight());
+ rRenderContext.DrawLine(aRect.TopLeft(), aRect.BottomLeft());
+
+ rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetSeparatorColor());
+ rRenderContext.DrawLine(aRect.BottomLeft(), aRect.BottomRight());
+ rRenderContext.DrawLine(aRect.TopRight(), aRect.BottomRight());
+ }
+ }
+ }
+ }
+ else
+ {
+ // create map to find toolbar lines
+ Size aOutSz(GetOutputSizePixel());
+ std::map<int, int> ranges;
+ sal_uInt16 nChildren = GetChildCount();
+ for (sal_uInt16 n = 0; n < nChildren; n++)
+ {
+ vcl::Window* pChild = GetChild(n);
+ Point aPos = pChild->GetPosPixel();
+ Size aSize = pChild->GetSizePixel();
+ if (IsHorizontal())
+ ranges[aPos.Y()] = aSize.Height();
+ else
+ ranges[aPos.X()] = aSize.Width();
+ }
+
+ // draw multiple toolbar backgrounds, i.e., one for each toolbar line
+ for (auto const& range : ranges)
+ {
+ tools::Rectangle aTBRect;
+ if (IsHorizontal())
+ {
+ aTBRect.SetLeft( 0 );
+ aTBRect.SetRight( aOutSz.Width() - 1 );
+ aTBRect.SetTop( range.first );
+ aTBRect.SetBottom( range.first + range.second - 1 );
+ }
+ else
+ {
+ aTBRect.SetLeft( range.first );
+ aTBRect.SetRight( range.first + range.second - 1 );
+ aTBRect.SetTop( 0 );
+ aTBRect.SetBottom( aOutSz.Height() - 1 );
+ }
+ rRenderContext.DrawNativeControl(ControlType::Toolbar, IsHorizontal() ? ControlPart::DrawBackgroundHorz : ControlPart::DrawBackgroundVert,
+ aTBRect, nState, aControlValue, OUString());
+ }
+ }
+}
+
+void DockingAreaWindow::Resize()
+{
+ ImplInvalidateMenubar( this );
+ if (IsNativeControlSupported(ControlType::Toolbar, ControlPart::Entire))
+ Invalidate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/dockmgr.cxx b/vcl/source/window/dockmgr.cxx
new file mode 100644
index 0000000000..7cde1910fe
--- /dev/null
+++ b/vcl/source/window/dockmgr.cxx
@@ -0,0 +1,1083 @@
+/* -*- 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 <tools/time.hxx>
+#include <sal/log.hxx>
+#include <o3tl/deleter.hxx>
+
+#include <brdwin.hxx>
+#include <svdata.hxx>
+#include <window.h>
+
+#include <vcl/event.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/dockwin.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/settings.hxx>
+
+#include "impldockingwrapper.hxx"
+
+#define DOCKWIN_FLOATSTYLES (WB_SIZEABLE | WB_MOVEABLE | WB_CLOSEABLE | WB_STANDALONE)
+
+namespace {
+
+class ImplDockFloatWin2 : public FloatingWindow
+{
+private:
+ ImplDockingWindowWrapper* mpDockWin;
+ sal_uInt64 mnLastTicks;
+ Timer m_aDockTimer;
+ Timer m_aEndDockTimer;
+ Point maDockPos;
+ tools::Rectangle maDockRect;
+ bool mbInMove;
+ ImplSVEvent * mnLastUserEvent;
+
+ DECL_LINK(DockingHdl, void *, void);
+ DECL_LINK(DockTimerHdl, Timer *, void);
+ DECL_LINK(EndDockTimerHdl, Timer *, void);
+public:
+ ImplDockFloatWin2( vcl::Window* pParent, WinBits nWinBits,
+ ImplDockingWindowWrapper* pDockingWin );
+ virtual ~ImplDockFloatWin2() override;
+ virtual void dispose() override;
+
+ virtual void Move() override;
+ virtual void Resize() override;
+ virtual void TitleButtonClick( TitleButton nButton ) override;
+ virtual void Resizing( Size& rSize ) override;
+ virtual bool Close() override;
+};
+
+}
+
+ImplDockFloatWin2::ImplDockFloatWin2( vcl::Window* pParent, WinBits nWinBits,
+ ImplDockingWindowWrapper* pDockingWin ) :
+ FloatingWindow( pParent, nWinBits ),
+ mpDockWin( pDockingWin ),
+ mnLastTicks( tools::Time::GetSystemTicks() ),
+ m_aDockTimer("vcl::ImplDockFloatWin2 m_aDockTimer"),
+ m_aEndDockTimer( "vcl::ImplDockFloatWin2 m_aEndDockTimer" ),
+ mbInMove( false ),
+ mnLastUserEvent( nullptr )
+{
+ // copy state of DockingWindow
+ if ( pDockingWin )
+ {
+ GetOutDev()->SetSettings( pDockingWin->GetWindow()->GetSettings() );
+ Enable( pDockingWin->GetWindow()->IsEnabled(), false );
+ EnableInput( pDockingWin->GetWindow()->IsInputEnabled(), false );
+ AlwaysEnableInput( pDockingWin->GetWindow()->IsAlwaysEnableInput(), false );
+ EnableAlwaysOnTop( pDockingWin->GetWindow()->IsAlwaysOnTopEnabled() );
+ SetActivateMode( pDockingWin->GetWindow()->GetActivateMode() );
+ }
+
+ SetBackground( GetSettings().GetStyleSettings().GetFaceColor() );
+
+ m_aDockTimer.SetInvokeHandler( LINK( this, ImplDockFloatWin2, DockTimerHdl ) );
+ m_aDockTimer.SetPriority( TaskPriority::HIGH_IDLE );
+ m_aDockTimer.SetTimeout( 50 );
+
+ m_aEndDockTimer.SetInvokeHandler( LINK( this, ImplDockFloatWin2, EndDockTimerHdl ) );
+ m_aEndDockTimer.SetPriority( TaskPriority::HIGH_IDLE );
+ m_aEndDockTimer.SetTimeout( 50 );
+}
+
+ImplDockFloatWin2::~ImplDockFloatWin2()
+{
+ disposeOnce();
+}
+
+void ImplDockFloatWin2::dispose()
+{
+ if( mnLastUserEvent )
+ Application::RemoveUserEvent( mnLastUserEvent );
+ FloatingWindow::dispose();
+}
+
+IMPL_LINK_NOARG(ImplDockFloatWin2, DockTimerHdl, Timer *, void)
+{
+ SAL_WARN_IF( !mpDockWin->IsFloatingMode(), "vcl", "docktimer called but not floating" );
+
+ PointerState aState = GetPointerState();
+
+ if( aState.mnState & KEY_MOD1 )
+ {
+ // i43499 CTRL disables docking now
+ mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
+ if( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) )
+ m_aDockTimer.Start();
+ }
+ else if( ! ( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) )
+ {
+ mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
+ mpDockWin->EndDocking( maDockRect, false );
+ }
+ else
+ {
+ mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->ShowTracking( maDockRect, ShowTrackFlags::Big | ShowTrackFlags::TrackWindow );
+ m_aDockTimer.Start();
+ }
+}
+
+IMPL_LINK_NOARG(ImplDockFloatWin2, EndDockTimerHdl, Timer *, void)
+{
+ SAL_WARN_IF( !mpDockWin->IsFloatingMode(), "vcl", "enddocktimer called but not floating" );
+
+ PointerState aState = GetPointerState();
+ if( ! ( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) )
+ {
+ mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
+ mpDockWin->EndDocking( maDockRect, true );
+ }
+ else
+ m_aEndDockTimer.Start();
+}
+
+IMPL_LINK_NOARG(ImplDockFloatWin2, DockingHdl, void*, void)
+{
+ // called during move of a floating window
+ mnLastUserEvent = nullptr;
+
+ vcl::Window *pDockingArea = mpDockWin->GetWindow()->GetParent();
+ PointerState aState = pDockingArea->GetPointerState();
+
+ bool bRealMove = true;
+ if( GetStyle() & WB_OWNERDRAWDECORATION )
+ {
+ // for windows with ownerdraw decoration
+ // we allow docking only when the window was moved
+ // by dragging its caption
+ // and ignore move request due to resizing
+ vcl::Window *pBorder = GetWindow( GetWindowType::Border );
+ if( pBorder != this )
+ {
+ tools::Rectangle aBorderRect( Point(), pBorder->GetSizePixel() );
+ sal_Int32 nLeft, nTop, nRight, nBottom;
+ GetBorder( nLeft, nTop, nRight, nBottom );
+ // limit borderrect to the caption part only and without the resizing borders
+ aBorderRect.SetBottom( aBorderRect.Top() + nTop );
+ aBorderRect.AdjustLeft(nLeft );
+ aBorderRect.AdjustRight( -nRight );
+
+ PointerState aBorderState = pBorder->GetPointerState();
+ bRealMove = aBorderRect.Contains( aBorderState.maPos );
+ }
+ }
+
+ if( mpDockWin->GetWindow()->IsVisible() &&
+ (tools::Time::GetSystemTicks() - mnLastTicks > 500) &&
+ ( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) &&
+ !(aState.mnState & KEY_MOD1) && // i43499 CTRL disables docking now
+ bRealMove )
+ {
+ maDockPos = pDockingArea->OutputToScreenPixel( pDockingArea->AbsoluteScreenToOutputPixel( OutputToAbsoluteScreenPixel( Point() ) ) );
+ maDockRect = tools::Rectangle( maDockPos, mpDockWin->GetSizePixel() );
+
+ // mouse pos in screen pixels
+ Point aMousePos = pDockingArea->OutputToScreenPixel( aState.maPos );
+
+ if( ! mpDockWin->IsDocking() )
+ mpDockWin->StartDocking( aMousePos, maDockRect );
+
+ bool bFloatMode = mpDockWin->Docking( aMousePos, maDockRect );
+
+ if( ! bFloatMode )
+ {
+ // indicates that the window could be docked at maDockRect
+ maDockRect.SetPos( mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->ScreenToOutputPixel(
+ maDockRect.TopLeft() ) );
+ mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->ShowTracking( maDockRect, ShowTrackFlags::Big | ShowTrackFlags::TrackWindow );
+ m_aEndDockTimer.Stop();
+ m_aDockTimer.Invoke();
+ }
+ else
+ {
+ mpDockWin->GetWindow()->GetParent()->ImplGetFrameWindow()->HideTracking();
+ m_aDockTimer.Stop();
+ m_aEndDockTimer.Invoke();
+ }
+ }
+ mbInMove = false;
+}
+
+void ImplDockFloatWin2::Move()
+{
+ if( mbInMove )
+ return;
+
+ mbInMove = true;
+ FloatingWindow::Move();
+ mpDockWin->GetWindow()->Move();
+
+ /*
+ * note: the window should only dock if KEY_MOD1 is pressed
+ * and the user releases all mouse buttons. The real problem here
+ * is that we don't get mouse events (at least not on X)
+ * if the mouse is on the decoration. So we have to start an
+ * awkward timer based process that polls the modifier/buttons
+ * to see whether they are in the right condition shortly after the
+ * last Move message.
+ */
+ if( ! mnLastUserEvent )
+ mnLastUserEvent = Application::PostUserEvent( LINK( this, ImplDockFloatWin2, DockingHdl ), nullptr, true );
+}
+
+void ImplDockFloatWin2::Resize()
+{
+ // forwarding of resize only required if we have no borderwindow ( GetWindow() then returns 'this' )
+ if( GetWindow( GetWindowType::Border ) == this )
+ {
+ FloatingWindow::Resize();
+ Size aSize( GetSizePixel() );
+ mpDockWin->GetWindow()->ImplPosSizeWindow( 0, 0, aSize.Width(), aSize.Height(), PosSizeFlags::PosSize ); // TODO: is this needed ???
+ }
+}
+
+void ImplDockFloatWin2::TitleButtonClick( TitleButton nButton )
+{
+ FloatingWindow::TitleButtonClick( nButton );
+ mpDockWin->TitleButtonClick( nButton );
+}
+
+void ImplDockFloatWin2::Resizing( Size& rSize )
+{
+ FloatingWindow::Resizing( rSize );
+ mpDockWin->Resizing( rSize );
+}
+
+bool ImplDockFloatWin2::Close()
+{
+ return true;
+}
+
+DockingManager::DockingManager()
+{
+}
+
+DockingManager::~DockingManager()
+{
+}
+
+ImplDockingWindowWrapper* DockingManager::GetDockingWindowWrapper( const vcl::Window *pWindow )
+{
+ for( const auto& xWrapper : mvDockingWindows )
+ {
+ if (xWrapper && xWrapper->mpDockingWindow == pWindow)
+ return xWrapper.get();
+ }
+ return nullptr;
+}
+
+bool DockingManager::IsDockable( const vcl::Window *pWindow )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+
+ /*
+ if( pWindow->HasDockingHandler() )
+ return true;
+ */
+ return (pWrapper != nullptr);
+}
+
+bool DockingManager::IsFloating( const vcl::Window *pWindow )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ if( pWrapper )
+ return pWrapper->IsFloatingMode();
+ else
+ return false;
+}
+
+bool DockingManager::IsLocked( const vcl::Window *pWindow )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ return pWrapper && pWrapper->IsLocked();
+}
+
+void DockingManager::Lock( const vcl::Window *pWindow )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ if( pWrapper )
+ pWrapper->Lock();
+}
+
+void DockingManager::Unlock( const vcl::Window *pWindow )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ if( pWrapper )
+ pWrapper->Unlock();
+}
+
+void DockingManager::SetFloatingMode( const vcl::Window *pWindow, bool bFloating )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ if( pWrapper )
+ pWrapper->SetFloatingMode( bFloating );
+}
+
+void DockingManager::StartPopupMode( const vcl::Window *pWindow, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ if( pWrapper )
+ pWrapper->StartPopupMode( rRect, nFlags );
+}
+
+void DockingManager::StartPopupMode( ToolBox *pParentToolBox, const vcl::Window *pWindow, FloatWinPopupFlags nFlags )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ if( pWrapper )
+ pWrapper->StartPopupMode( pParentToolBox, nFlags );
+}
+
+void DockingManager::StartPopupMode( ToolBox *pParentToolBox, const vcl::Window *pWindow )
+{
+ StartPopupMode( pParentToolBox, pWindow, FloatWinPopupFlags::AllowTearOff |
+ FloatWinPopupFlags::AllMouseButtonClose |
+ FloatWinPopupFlags::NoMouseUpClose );
+}
+
+bool DockingManager::IsInPopupMode( const vcl::Window *pWindow )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ return pWrapper && pWrapper->IsInPopupMode();
+}
+
+void DockingManager::EndPopupMode( const vcl::Window *pWin )
+{
+ ImplDockingWindowWrapper *pWrapper = GetDockingWindowWrapper( pWin );
+ if( pWrapper && pWrapper->GetFloatingWindow() && static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->IsInPopupMode() )
+ static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->EndPopupMode();
+}
+
+SystemWindow* DockingManager::GetFloatingWindow(const vcl::Window *pWin)
+{
+ ImplDockingWindowWrapper *pWrapper = GetDockingWindowWrapper( pWin );
+ if (pWrapper)
+ return pWrapper->GetFloatingWindow();
+ return nullptr;
+}
+
+void DockingManager::SetPopupModeEndHdl( const vcl::Window *pWindow, const Link<FloatingWindow*,void>& rLink )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ if( pWrapper )
+ pWrapper->SetPopupModeEndHdl(rLink);
+}
+
+void DockingManager::AddWindow( const vcl::Window *pWindow )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ if( pWrapper )
+ return;
+ mvDockingWindows.emplace_back( new ImplDockingWindowWrapper( pWindow ) );
+}
+
+void DockingManager::RemoveWindow( const vcl::Window *pWindow )
+{
+ for( auto it = mvDockingWindows.begin(); it != mvDockingWindows.end(); ++it )
+ {
+ const auto& xWrapper = *it;
+ if (xWrapper && xWrapper->mpDockingWindow == pWindow)
+ {
+ // deleting wrappers calls set of actions which may want to use
+ // wrapper we want to delete - avoid crash using temporary owner
+ // while erasing
+ auto pTemporaryOwner = std::move(*it);
+ mvDockingWindows.erase( it );
+ break;
+ }
+ }
+}
+
+void DockingManager::SetPosSizePixel( vcl::Window const *pWindow, tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight,
+ PosSizeFlags nFlags )
+{
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ if( pWrapper )
+ pWrapper->setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
+}
+
+tools::Rectangle DockingManager::GetPosSizePixel( const vcl::Window *pWindow )
+{
+ tools::Rectangle aRect;
+ ImplDockingWindowWrapper* pWrapper = GetDockingWindowWrapper( pWindow );
+ if( pWrapper )
+ aRect = tools::Rectangle( pWrapper->GetPosPixel(), pWrapper->GetSizePixel() );
+
+ return aRect;
+}
+
+class ImplPopupFloatWin : public FloatingWindow
+{
+private:
+ bool mbToolBox;
+
+public:
+ ImplPopupFloatWin( vcl::Window* pParent, bool bToolBox );
+ virtual ~ImplPopupFloatWin() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessible() override;
+};
+
+ImplPopupFloatWin::ImplPopupFloatWin( vcl::Window* pParent, bool bToolBox ) :
+ FloatingWindow( pParent, bToolBox ? WB_BORDER | WB_POPUP | WB_SYSTEMWINDOW | WB_NOSHADOW : WB_STDPOPUP ),
+ mbToolBox( bToolBox )
+{
+ if ( bToolBox )
+ {
+ // indicate window type, required for accessibility
+ // which should not see this window as a toplevel window
+ mpWindowImpl->mbToolbarFloatingWindow = true;
+ }
+}
+
+ImplPopupFloatWin::~ImplPopupFloatWin()
+{
+ disposeOnce();
+}
+
+css::uno::Reference< css::accessibility::XAccessible > ImplPopupFloatWin::CreateAccessible()
+{
+ if ( !mbToolBox )
+ return FloatingWindow::CreateAccessible();
+
+ // switch off direct accessibility support for this window
+
+ // this is to avoid appearance of this window as standalone window in the accessibility hierarchy
+ // as this window is only used as a helper for subtoolbars that are not teared-off, the parent toolbar
+ // has to provide accessibility support (as implemented in the toolkit)
+ // so the contained toolbar should appear as child of the corresponding toolbar item of the parent toolbar
+ return css::uno::Reference< css::accessibility::XAccessible >();
+}
+
+ImplDockingWindowWrapper::ImplDockingWindowWrapper( const vcl::Window *pWindow )
+ : mpDockingWindow(const_cast<vcl::Window*>(pWindow))
+ , mpFloatWin(nullptr)
+ , mpOldBorderWin(nullptr)
+ , mpParent(pWindow->GetParent())
+ , maMaxOutSize( SHRT_MAX, SHRT_MAX )
+ , mnTrackX(0)
+ , mnTrackY(0)
+ , mnTrackWidth(0)
+ , mnTrackHeight(0)
+ , mnDockLeft(0)
+ , mnDockTop(0)
+ , mnDockRight(0)
+ , mnDockBottom(0)
+ , mnFloatBits(WB_BORDER | WB_CLOSEABLE | WB_SIZEABLE | (pWindow->GetStyle() & DOCKWIN_FLOATSTYLES))
+ , mbDockCanceled(false)
+ , mbDocking(false)
+ , mbLastFloatMode(false)
+ , mbDockBtn(false)
+ , mbHideBtn(false)
+ // must be enabled in Window::Notify to prevent permanent docking during mouse move
+ , mbStartDockingEnabled(false)
+ , mbLocked(false)
+{
+ assert(mpDockingWindow);
+ DockingWindow *pDockWin = dynamic_cast< DockingWindow* > ( mpDockingWindow.get() );
+ if( pDockWin )
+ mnFloatBits = pDockWin->GetFloatStyle();
+}
+
+ImplDockingWindowWrapper::~ImplDockingWindowWrapper()
+{
+ if ( IsFloatingMode() )
+ {
+ GetWindow()->Show( false, ShowFlags::NoFocusChange );
+ SetFloatingMode(false);
+ }
+}
+
+void ImplDockingWindowWrapper::ImplStartDocking( const Point& rPos )
+{
+ if( !mbStartDockingEnabled )
+ return;
+
+ maMouseOff = rPos;
+ mbDocking = true;
+ mbLastFloatMode = IsFloatingMode();
+
+ // calculate FloatingBorder
+ VclPtr<FloatingWindow> pWin;
+ if ( mpFloatWin )
+ pWin = mpFloatWin;
+ else
+ pWin = VclPtr<ImplDockFloatWin2>::Create( mpParent, mnFloatBits, nullptr );
+ pWin->GetBorder( mnDockLeft, mnDockTop, mnDockRight, mnDockBottom );
+ if ( !mpFloatWin )
+ pWin.disposeAndClear();
+
+ Point aPos = GetWindow()->OutputToScreenPixel( Point() );
+ Size aSize = GetWindow()->GetOutputSizePixel();
+ mnTrackX = aPos.X();
+ mnTrackY = aPos.Y();
+ mnTrackWidth = aSize.Width();
+ mnTrackHeight = aSize.Height();
+
+ if ( mbLastFloatMode )
+ {
+ maMouseOff.AdjustX(mnDockLeft );
+ maMouseOff.AdjustY(mnDockTop );
+ mnTrackX -= mnDockLeft;
+ mnTrackY -= mnDockTop;
+ mnTrackWidth += mnDockLeft+mnDockRight;
+ mnTrackHeight += mnDockTop+mnDockBottom;
+ }
+
+ vcl::Window *pDockingArea = GetWindow()->GetParent();
+ vcl::Window::PointerState aState = pDockingArea->GetPointerState();
+
+ // mouse pos in screen pixels
+ Point aMousePos = pDockingArea->OutputToScreenPixel( aState.maPos );
+ Point aDockPos = pDockingArea->AbsoluteScreenToOutputPixel( GetWindow()->OutputToAbsoluteScreenPixel( GetWindow()->GetPosPixel() ) );
+ tools::Rectangle aDockRect( aDockPos, GetWindow()->GetSizePixel() );
+ StartDocking( aMousePos, aDockRect );
+
+ GetWindow()->ImplUpdateAll();
+ GetWindow()->ImplGetFrameWindow()->ImplUpdateAll();
+
+ GetWindow()->StartTracking( StartTrackingFlags::KeyMod );
+}
+
+void ImplDockingWindowWrapper::Tracking( const TrackingEvent& rTEvt )
+{
+ // used during docking of a currently docked window
+ if ( !mbDocking )
+ return;
+
+ if ( rTEvt.IsTrackingEnded() )
+ {
+ mbDocking = false;
+ GetWindow()->HideTracking();
+ if ( rTEvt.IsTrackingCanceled() )
+ {
+ mbDockCanceled = true;
+ EndDocking( tools::Rectangle( Point( mnTrackX, mnTrackY ), Size( mnTrackWidth, mnTrackHeight ) ), mbLastFloatMode );
+ mbDockCanceled = false;
+ }
+ else
+ EndDocking( tools::Rectangle( Point( mnTrackX, mnTrackY ), Size( mnTrackWidth, mnTrackHeight ) ), mbLastFloatMode );
+ }
+ // Docking only upon non-synthetic MouseEvents
+ else if ( !rTEvt.GetMouseEvent().IsSynthetic() || rTEvt.GetMouseEvent().IsModifierChanged() )
+ {
+ Point aMousePos = rTEvt.GetMouseEvent().GetPosPixel();
+ Point aFrameMousePos = GetWindow()->OutputToScreenPixel( aMousePos );
+ Size aFrameSize = GetWindow()->ImplGetFrameWindow()->GetOutputSizePixel();
+ if ( aFrameMousePos.X() < 0 )
+ aFrameMousePos.setX( 0 );
+ if ( aFrameMousePos.Y() < 0 )
+ aFrameMousePos.setY( 0 );
+ if ( aFrameMousePos.X() > aFrameSize.Width()-1 )
+ aFrameMousePos.setX( aFrameSize.Width()-1 );
+ if ( aFrameMousePos.Y() > aFrameSize.Height()-1 )
+ aFrameMousePos.setY( aFrameSize.Height()-1 );
+ aMousePos = GetWindow()->ScreenToOutputPixel( aFrameMousePos );
+ aMousePos.AdjustX( -(maMouseOff.X()) );
+ aMousePos.AdjustY( -(maMouseOff.Y()) );
+ Point aPos = GetWindow()->OutputToScreenPixel( aMousePos );
+ tools::Rectangle aTrackRect( aPos, Size( mnTrackWidth, mnTrackHeight ) );
+ tools::Rectangle aCompRect = aTrackRect;
+ aPos.AdjustX(maMouseOff.X() );
+ aPos.AdjustY(maMouseOff.Y() );
+
+ bool bFloatMode = Docking( aPos, aTrackRect );
+
+ if ( mbLastFloatMode != bFloatMode )
+ {
+ if ( bFloatMode )
+ {
+ aTrackRect.AdjustLeft( -mnDockLeft );
+ aTrackRect.AdjustTop( -mnDockTop );
+ aTrackRect.AdjustRight(mnDockRight );
+ aTrackRect.AdjustBottom(mnDockBottom );
+ }
+ else
+ {
+ if ( aCompRect == aTrackRect )
+ {
+ aTrackRect.AdjustLeft(mnDockLeft );
+ aTrackRect.AdjustTop(mnDockTop );
+ aTrackRect.AdjustRight( -mnDockRight );
+ aTrackRect.AdjustBottom( -mnDockBottom );
+ }
+ }
+ mbLastFloatMode = bFloatMode;
+ }
+
+ ShowTrackFlags nTrackStyle;
+ if ( bFloatMode )
+ nTrackStyle = ShowTrackFlags::Object;
+ else
+ nTrackStyle = ShowTrackFlags::Big;
+ tools::Rectangle aShowTrackRect = aTrackRect;
+ aShowTrackRect.SetPos( GetWindow()->ScreenToOutputPixel( aShowTrackRect.TopLeft() ) );
+
+ GetWindow()->ShowTracking( aShowTrackRect, nTrackStyle );
+
+ // calculate mouse offset again, as the rectangle was changed
+ maMouseOff.setX( aPos.X() - aTrackRect.Left() );
+ maMouseOff.setY( aPos.Y() - aTrackRect.Top() );
+
+ mnTrackX = aTrackRect.Left();
+ mnTrackY = aTrackRect.Top();
+ mnTrackWidth = aTrackRect.GetWidth();
+ mnTrackHeight = aTrackRect.GetHeight();
+ }
+}
+
+void ImplDockingWindowWrapper::StartDocking( const Point& rPoint, tools::Rectangle const & rRect )
+{
+ DockingData data( rPoint, rRect, IsFloatingMode() );
+
+ GetWindow()->CallEventListeners( VclEventId::WindowStartDocking, &data );
+ mbDocking = true;
+}
+
+bool ImplDockingWindowWrapper::Docking( const Point& rPoint, tools::Rectangle& rRect )
+{
+ DockingData data( rPoint, rRect, IsFloatingMode() );
+
+ GetWindow()->CallEventListeners( VclEventId::WindowDocking, &data );
+ rRect = data.maTrackRect;
+ return data.mbFloating;
+}
+
+void ImplDockingWindowWrapper::EndDocking( const tools::Rectangle& rRect, bool bFloatMode )
+{
+ tools::Rectangle aRect( rRect );
+
+ bool bOrigDockCanceled = mbDockCanceled;
+ if (bFloatMode && !StyleSettings::GetDockingFloatsSupported())
+ mbDockCanceled = true;
+
+ if ( !IsDockingCanceled() )
+ {
+ bool bShow = false;
+ if ( bFloatMode != IsFloatingMode() )
+ {
+ GetWindow()->Show( false, ShowFlags::NoFocusChange );
+ SetFloatingMode( bFloatMode );
+ bShow = true;
+ if ( bFloatMode )
+ {
+ // #i44800# always use outputsize - as in all other places
+ mpFloatWin->SetOutputSizePixel( aRect.GetSize() );
+ mpFloatWin->SetPosPixel( aRect.TopLeft() );
+ }
+ }
+ if ( !bFloatMode )
+ {
+ Point aPos = aRect.TopLeft();
+ aPos = GetWindow()->GetParent()->ScreenToOutputPixel( aPos );
+ GetWindow()->SetPosSizePixel( aPos, aRect.GetSize() );
+ }
+
+ if ( bShow )
+ GetWindow()->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+ }
+
+ EndDockingData data( aRect, IsFloatingMode(), IsDockingCanceled() );
+ GetWindow()->CallEventListeners( VclEventId::WindowEndDocking, &data );
+
+ mbDocking = false;
+
+ // must be enabled in Window::Notify to prevent permanent docking during mouse move
+ mbStartDockingEnabled = false;
+
+ mbDockCanceled = bOrigDockCanceled;
+}
+
+bool ImplDockingWindowWrapper::PrepareToggleFloatingMode()
+{
+ bool bFloating = true;
+ GetWindow()->CallEventListeners( VclEventId::WindowPrepareToggleFloating, &bFloating );
+ return bFloating;
+}
+
+void ImplDockingWindowWrapper::ToggleFloatingMode()
+{
+ // notify dockingwindow/toolbox
+ // note: this must be done *before* notifying the
+ // listeners to have the toolbox in the proper state
+ if( GetWindow()->IsDockingWindow() )
+ static_cast<DockingWindow*>(GetWindow())->ToggleFloatingMode();
+
+ // now notify listeners
+ GetWindow()->CallEventListeners( VclEventId::WindowToggleFloating );
+
+ // must be enabled in Window::Notify to prevent permanent docking during mouse move
+ mbStartDockingEnabled = false;
+}
+
+void ImplDockingWindowWrapper::TitleButtonClick( TitleButton nType )
+{
+ if( nType == TitleButton::Menu )
+ {
+ ToolBox *pToolBox = dynamic_cast< ToolBox* >( GetWindow() );
+ if( pToolBox )
+ {
+ pToolBox->ExecuteCustomMenu();
+ }
+ }
+ if( nType == TitleButton::Docking )
+ {
+ SetFloatingMode( !IsFloatingMode() );
+ }
+}
+
+void ImplDockingWindowWrapper::Resizing( Size& rSize )
+{
+ // TODO: add virtual Resizing() to class Window, so we can get rid of class DockingWindow
+ DockingWindow *pDockingWindow = dynamic_cast< DockingWindow* >( GetWindow() );
+ if( pDockingWindow )
+ pDockingWindow->Resizing( rSize );
+}
+
+void ImplDockingWindowWrapper::ShowMenuTitleButton( bool bVisible )
+{
+ if ( mpFloatWin )
+ mpFloatWin->ShowTitleButton( TitleButton::Menu, bVisible );
+}
+
+void ImplDockingWindowWrapper::ImplPreparePopupMode()
+{
+ VclPtr<vcl::Window> xWindow = GetWindow();
+ xWindow->Show( false, ShowFlags::NoFocusChange );
+
+ // prepare reparenting
+ vcl::Window* pRealParent = xWindow->GetWindow( GetWindowType::Parent );
+ mpOldBorderWin = xWindow->GetWindow( GetWindowType::Border );
+ if( mpOldBorderWin.get() == xWindow )
+ mpOldBorderWin = nullptr; // no border window found
+
+ // the new parent for popup mode
+ VclPtrInstance<ImplPopupFloatWin> pWin( mpParent, xWindow->GetType() == WindowType::TOOLBOX );
+ pWin->SetPopupModeEndHdl( LINK( this, ImplDockingWindowWrapper, PopupModeEnd ) );
+
+ // At least for DockingWindow, GetText() has a side effect of setting deferred
+ // properties. This must be done before setting the border window (see below),
+ // so that the border width will end up in mpWindowImpl->mnBorderWidth, not in
+ // the border window (See DockingWindow::setPosSizeOnContainee() and
+ // DockingWindow::GetOptimalSize()).
+ pWin->SetText( xWindow->GetText() );
+ pWin->SetOutputSizePixel( xWindow->GetSizePixel() );
+
+ xWindow->mpWindowImpl->mpBorderWindow = nullptr;
+ xWindow->mpWindowImpl->mnLeftBorder = 0;
+ xWindow->mpWindowImpl->mnTopBorder = 0;
+ xWindow->mpWindowImpl->mnRightBorder = 0;
+ xWindow->mpWindowImpl->mnBottomBorder = 0;
+
+ // reparent borderwindow and window
+ if ( mpOldBorderWin )
+ mpOldBorderWin->SetParent( pWin );
+ xWindow->SetParent( pWin );
+
+ // correct border window pointers
+ xWindow->mpWindowImpl->mpBorderWindow = pWin;
+ pWin->mpWindowImpl->mpClientWindow = xWindow;
+ xWindow->mpWindowImpl->mpRealParent = pRealParent;
+
+ // set mpFloatWin not until all window positioning is done !!!
+ // (SetPosPixel etc. check for valid mpFloatWin pointer)
+ mpFloatWin = pWin;
+}
+
+void ImplDockingWindowWrapper::StartPopupMode( ToolBox *pParentToolBox, FloatWinPopupFlags nFlags )
+{
+ // do nothing if window is floating
+ if( IsFloatingMode() )
+ return;
+
+ ImplPreparePopupMode();
+
+ // don't allow tearoff, if globally disabled
+ if( !StyleSettings::GetDockingFloatsSupported() )
+ nFlags &= ~FloatWinPopupFlags::AllowTearOff;
+
+ // if the subtoolbar was opened via keyboard make sure that key events
+ // will go into subtoolbar
+ if( pParentToolBox->IsKeyEvent() )
+ nFlags |= FloatWinPopupFlags::GrabFocus;
+
+ mpFloatWin->StartPopupMode( pParentToolBox, nFlags );
+ GetWindow()->Show();
+ // grab focus (again) after showing docking window, as e.g. a11y focus
+ // events require window to be visible first
+ if (nFlags & FloatWinPopupFlags::GrabFocus)
+ mpFloatWin->GrabFocus();
+
+ if( pParentToolBox->IsKeyEvent() )
+ {
+ // send HOME key to subtoolbar in order to select first item
+ KeyEvent aEvent( 0, vcl::KeyCode( KEY_HOME ) );
+ GetWindow()->KeyInput(aEvent);
+ }
+}
+
+void ImplDockingWindowWrapper::StartPopupMode( const tools::Rectangle& rRect, FloatWinPopupFlags nFlags )
+{
+ // do nothing if window is floating
+ if( IsFloatingMode() )
+ return;
+
+ ImplPreparePopupMode();
+ mpFloatWin->StartPopupMode( rRect, nFlags );
+ GetWindow()->Show();
+ // grab focus (again) after showing docking window, as e.g. a11y focus
+ // events require window to be visible first
+ if (nFlags & FloatWinPopupFlags::GrabFocus)
+ mpFloatWin->GrabFocus();
+}
+
+IMPL_LINK_NOARG(ImplDockingWindowWrapper, PopupModeEnd, FloatingWindow*, void)
+{
+ VclPtr<vcl::Window> xWindow = GetWindow();
+ xWindow->Show( false, ShowFlags::NoFocusChange );
+
+ // set parameter for handler before destroying floating window
+ EndPopupModeData aData( mpFloatWin->GetWindow( GetWindowType::Border )->GetPosPixel(), mpFloatWin->IsPopupModeTearOff() );
+
+ // before deleting change parent back, so we can delete the floating window alone
+ vcl::Window* pRealParent = xWindow->GetWindow( GetWindowType::Parent );
+ xWindow->mpWindowImpl->mpBorderWindow = nullptr;
+ if ( mpOldBorderWin )
+ {
+ xWindow->SetParent( mpOldBorderWin );
+ static_cast<ImplBorderWindow*>(mpOldBorderWin.get())->GetBorder(
+ xWindow->mpWindowImpl->mnLeftBorder, xWindow->mpWindowImpl->mnTopBorder,
+ xWindow->mpWindowImpl->mnRightBorder, xWindow->mpWindowImpl->mnBottomBorder );
+ mpOldBorderWin->Resize();
+ }
+ xWindow->mpWindowImpl->mpBorderWindow = mpOldBorderWin;
+ xWindow->SetParent( pRealParent );
+ xWindow->mpWindowImpl->mpRealParent = pRealParent;
+
+ // take ownership to local variable to protect against maPopupModeEndHdl destroying this object
+ auto xFloatWin = std::move(mpFloatWin);
+ maPopupModeEndHdl.Call(xFloatWin);
+ xFloatWin.disposeAndClear();
+
+ // call handler - which will destroy the window and thus the wrapper as well !
+ xWindow->CallEventListeners( VclEventId::WindowEndPopupMode, &aData );
+}
+
+bool ImplDockingWindowWrapper::IsInPopupMode() const
+{
+ if( GetFloatingWindow() )
+ return static_cast<FloatingWindow*>(GetFloatingWindow())->IsInPopupMode();
+ else
+ return false;
+}
+
+void ImplDockingWindowWrapper::SetFloatingMode( bool bFloatMode )
+{
+ // do nothing if window is docked and locked
+ if( !IsFloatingMode() && IsLocked() )
+ return;
+
+ if ( IsFloatingMode() == bFloatMode )
+ return;
+
+ if ( !PrepareToggleFloatingMode() )
+ return;
+
+ bool bVisible = GetWindow()->IsVisible();
+
+ if ( bFloatMode )
+ {
+ GetWindow()->Show( false, ShowFlags::NoFocusChange );
+
+ maDockPos = GetWindow()->GetPosPixel();
+
+ vcl::Window* pRealParent = GetWindow()->GetWindow( GetWindowType::Parent );
+ mpOldBorderWin = GetWindow()->GetWindow( GetWindowType::Border );
+ if( mpOldBorderWin == mpDockingWindow )
+ mpOldBorderWin = nullptr; // no border window found
+
+ VclPtrInstance<ImplDockFloatWin2> pWin(
+ mpParent,
+ mnFloatBits & ( WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE ) ?
+ mnFloatBits | WB_SYSTEMWINDOW
+ | WB_OWNERDRAWDECORATION
+ : mnFloatBits,
+ this );
+
+ // At least for DockingWindow, GetText() has a side effect of setting deferred
+ // properties. This must be done before setting the border window (see below),
+ // so that the border width will end up in mpWindowImpl->mnBorderWidth, not in
+ // the border window (See DockingWindow::setPosSizeOnContainee() and
+ // DockingWindow::GetOptimalSize()).
+ pWin->SetText( GetWindow()->GetText() );
+
+ GetWindow()->mpWindowImpl->mpBorderWindow = nullptr;
+ GetWindow()->mpWindowImpl->mnLeftBorder = 0;
+ GetWindow()->mpWindowImpl->mnTopBorder = 0;
+ GetWindow()->mpWindowImpl->mnRightBorder = 0;
+ GetWindow()->mpWindowImpl->mnBottomBorder = 0;
+
+ // if the parent gets destroyed, we also have to reset the parent of the BorderWindow
+ if ( mpOldBorderWin )
+ mpOldBorderWin->SetParent( pWin );
+ GetWindow()->SetParent( pWin );
+ pWin->SetPosPixel( Point() );
+
+ GetWindow()->mpWindowImpl->mpBorderWindow = pWin;
+ pWin->mpWindowImpl->mpClientWindow = mpDockingWindow;
+ GetWindow()->mpWindowImpl->mpRealParent = pRealParent;
+
+ pWin->SetOutputSizePixel( GetWindow()->GetSizePixel() );
+ pWin->SetPosPixel( maFloatPos );
+ // pass on DockingData to FloatingWindow
+ pWin->ShowTitleButton( TitleButton::Docking, mbDockBtn );
+ pWin->ShowTitleButton( TitleButton::Hide, mbHideBtn );
+ pWin->SetMinOutputSizePixel( maMinOutSize );
+ pWin->SetMaxOutputSizePixel( maMaxOutSize );
+
+ mpFloatWin = pWin;
+
+ if ( bVisible )
+ GetWindow()->Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+
+ ToggleFloatingMode();
+ }
+ else
+ {
+ GetWindow()->Show( false, ShowFlags::NoFocusChange );
+
+ // store FloatingData in FloatingWindow
+ maFloatPos = mpFloatWin->GetPosPixel();
+ mbDockBtn = mpFloatWin->IsTitleButtonVisible( TitleButton::Docking );
+ mbHideBtn = mpFloatWin->IsTitleButtonVisible( TitleButton::Hide );
+ maMinOutSize = mpFloatWin->GetMinOutputSizePixel();
+ maMaxOutSize = mpFloatWin->GetMaxOutputSizePixel();
+
+ vcl::Window* pRealParent = GetWindow()->GetWindow( GetWindowType::Parent ); //mpWindowImpl->mpRealParent;
+ GetWindow()->mpWindowImpl->mpBorderWindow = nullptr;
+ if ( mpOldBorderWin )
+ {
+ GetWindow()->SetParent( mpOldBorderWin );
+ static_cast<ImplBorderWindow*>(mpOldBorderWin.get())->GetBorder(
+ GetWindow()->mpWindowImpl->mnLeftBorder, GetWindow()->mpWindowImpl->mnTopBorder,
+ GetWindow()->mpWindowImpl->mnRightBorder, GetWindow()->mpWindowImpl->mnBottomBorder );
+ mpOldBorderWin->Resize();
+ }
+ GetWindow()->mpWindowImpl->mpBorderWindow = mpOldBorderWin;
+ GetWindow()->SetParent( pRealParent );
+ GetWindow()->mpWindowImpl->mpRealParent = pRealParent;
+
+ mpFloatWin.disposeAndClear();
+ GetWindow()->SetPosPixel( maDockPos );
+
+ if ( bVisible )
+ GetWindow()->Show();
+
+ ToggleFloatingMode();
+
+ }
+}
+
+void ImplDockingWindowWrapper::SetFloatStyle( WinBits nStyle )
+{
+ mnFloatBits = nStyle;
+}
+
+
+void ImplDockingWindowWrapper::setPosSizePixel( tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight,
+ PosSizeFlags nFlags )
+{
+ if ( mpFloatWin )
+ mpFloatWin->setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
+ else
+ GetWindow()->setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
+}
+
+Point ImplDockingWindowWrapper::GetPosPixel() const
+{
+ if ( mpFloatWin )
+ return mpFloatWin->GetPosPixel();
+ else
+ return mpDockingWindow->GetPosPixel();
+}
+
+Size ImplDockingWindowWrapper::GetSizePixel() const
+{
+ if ( mpFloatWin )
+ return mpFloatWin->GetSizePixel();
+ else
+ return mpDockingWindow->GetSizePixel();
+}
+
+// old inlines from DockingWindow
+
+void ImplDockingWindowWrapper::SetMinOutputSizePixel( const Size& rSize )
+{
+ if ( mpFloatWin )
+ mpFloatWin->SetMinOutputSizePixel( rSize );
+ maMinOutSize = rSize;
+}
+
+void ImplDockingWindowWrapper::SetMaxOutputSizePixel( const Size& rSize )
+{
+ if ( mpFloatWin )
+ mpFloatWin->SetMaxOutputSizePixel( rSize );
+ maMaxOutSize = rSize;
+}
+
+bool ImplDockingWindowWrapper::IsFloatingMode() const
+{
+ return (mpFloatWin != nullptr);
+}
+
+void ImplDockingWindowWrapper::SetDragArea( const tools::Rectangle& rRect )
+{
+ maDragArea = rRect;
+}
+
+
+void ImplDockingWindowWrapper::Lock()
+{
+ mbLocked = true;
+ // only toolbars support locking
+ ToolBox *pToolBox = dynamic_cast< ToolBox * >( GetWindow() );
+ if( pToolBox )
+ pToolBox->Lock( mbLocked );
+}
+
+void ImplDockingWindowWrapper::Unlock()
+{
+ mbLocked = false;
+ // only toolbars support locking
+ ToolBox *pToolBox = dynamic_cast< ToolBox * >( GetWindow() );
+ if( pToolBox )
+ pToolBox->Lock( mbLocked );
+}
+
+SystemWindow* ImplDockingWindowWrapper::GetFloatingWindow() const
+{
+ return mpFloatWin;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/dockwin.cxx b/vcl/source/window/dockwin.cxx
new file mode 100644
index 0000000000..3f8853877b
--- /dev/null
+++ b/vcl/source/window/dockwin.cxx
@@ -0,0 +1,1148 @@
+/* -*- 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 <tools/time.hxx>
+#include <sal/log.hxx>
+#include <vcl/event.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/dockwin.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/settings.hxx>
+#include <comphelper/lok.hxx>
+
+#include <accel.hxx>
+#include <svdata.hxx>
+#include <window.h>
+#include <brdwin.hxx>
+
+#include "impldockingwrapper.hxx"
+
+#define DOCKWIN_FLOATSTYLES (WB_SIZEABLE | WB_MOVEABLE | WB_CLOSEABLE | WB_STANDALONE)
+
+class DockingWindow::ImplData
+{
+public:
+ ImplData();
+
+ VclPtr<vcl::Window> mpParent;
+ Size maMaxOutSize;
+};
+
+DockingWindow::ImplData::ImplData()
+{
+ mpParent = nullptr;
+ maMaxOutSize = Size( SHRT_MAX, SHRT_MAX );
+}
+
+namespace {
+
+class ImplDockFloatWin : public FloatingWindow
+{
+private:
+ VclPtr<DockingWindow> mpDockWin;
+ sal_uInt64 mnLastTicks;
+ Idle maDockIdle;
+ Point maDockPos;
+ tools::Rectangle maDockRect;
+ bool mbInMove;
+ ImplSVEvent * mnLastUserEvent;
+
+ DECL_LINK(DockingHdl, void *, void);
+ DECL_LINK(DockTimerHdl, Timer *, void);
+public:
+ ImplDockFloatWin( vcl::Window* pParent, WinBits nWinBits,
+ DockingWindow* pDockingWin );
+ virtual ~ImplDockFloatWin() override;
+ virtual void dispose() override;
+
+ virtual void Move() override;
+ virtual void Resize() override;
+ virtual void Resizing( Size& rSize ) override;
+ virtual bool Close() override;
+};
+
+}
+
+ImplDockFloatWin::ImplDockFloatWin( vcl::Window* pParent, WinBits nWinBits,
+ DockingWindow* pDockingWin ) :
+ FloatingWindow( pParent, nWinBits ),
+ mpDockWin( pDockingWin ),
+ mnLastTicks( tools::Time::GetSystemTicks() ),
+ maDockIdle( "vcl::ImplDockFloatWin maDockIdle" ),
+ mbInMove( false ),
+ mnLastUserEvent( nullptr )
+{
+ // copy settings of DockingWindow
+ if ( pDockingWin )
+ {
+ GetOutDev()->SetSettings( pDockingWin->GetSettings() );
+ Enable( pDockingWin->IsEnabled(), false );
+ EnableInput( pDockingWin->IsInputEnabled(), false );
+ AlwaysEnableInput( pDockingWin->IsAlwaysEnableInput(), false );
+ EnableAlwaysOnTop( pDockingWin->IsAlwaysOnTopEnabled() );
+ SetActivateMode( pDockingWin->GetActivateMode() );
+ }
+
+ SetBackground();
+
+ maDockIdle.SetInvokeHandler( LINK( this, ImplDockFloatWin, DockTimerHdl ) );
+ maDockIdle.SetPriority( TaskPriority::HIGH_IDLE );
+}
+
+ImplDockFloatWin::~ImplDockFloatWin()
+{
+ disposeOnce();
+}
+
+void ImplDockFloatWin::dispose()
+{
+ if( mnLastUserEvent )
+ Application::RemoveUserEvent( mnLastUserEvent );
+
+ disposeBuilder();
+
+ mpDockWin.clear();
+ FloatingWindow::dispose();
+}
+
+IMPL_LINK_NOARG(ImplDockFloatWin, DockTimerHdl, Timer *, void)
+{
+ SAL_WARN_IF( !mpDockWin->IsFloatingMode(), "vcl", "docktimer called but not floating" );
+
+ maDockIdle.Stop();
+ PointerState aState = GetPointerState();
+
+ if( aState.mnState & KEY_MOD1 )
+ {
+ // i43499 CTRL disables docking now
+ mpDockWin->GetParent()->ImplGetFrameWindow()->HideTracking();
+ mpDockWin->EndDocking( maDockRect, true );
+ if( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) )
+ maDockIdle.Start();
+ }
+ else if( ! ( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) )
+ {
+ mpDockWin->GetParent()->ImplGetFrameWindow()->HideTracking();
+ mpDockWin->EndDocking( maDockRect, false );
+ }
+ else
+ {
+ mpDockWin->GetParent()->ImplGetFrameWindow()->ShowTracking( maDockRect, ShowTrackFlags::Big | ShowTrackFlags::TrackWindow );
+ maDockIdle.Start();
+ }
+}
+
+IMPL_LINK_NOARG(ImplDockFloatWin, DockingHdl, void*, void)
+{
+ PointerState aState = mpDockWin->GetParent()->GetPointerState();
+
+ mnLastUserEvent = nullptr;
+ if( mpDockWin->IsDockable() &&
+ (tools::Time::GetSystemTicks() - mnLastTicks > 500) &&
+ ( aState.mnState & ( MOUSE_LEFT | MOUSE_MIDDLE | MOUSE_RIGHT ) ) &&
+ !(aState.mnState & KEY_MOD1) ) // i43499 CTRL disables docking now
+ {
+ maDockPos = mpDockWin->GetParent()->AbsoluteScreenToOutputPixel( OutputToAbsoluteScreenPixel( Point() ) );
+ maDockPos = mpDockWin->GetParent()->OutputToScreenPixel( maDockPos ); // sfx expects screen coordinates
+
+ if( ! mpDockWin->IsDocking() )
+ mpDockWin->StartDocking();
+ maDockRect = tools::Rectangle( maDockPos, mpDockWin->GetSizePixel() );
+
+ // mouse pos also in screen pixels
+ Point aMousePos = mpDockWin->GetParent()->OutputToScreenPixel( aState.maPos );
+
+ bool bFloatMode = mpDockWin->Docking( aMousePos, maDockRect );
+ if( ! bFloatMode )
+ {
+ mpDockWin->GetParent()->ImplGetFrameWindow()->ShowTracking( maDockRect, ShowTrackFlags::Object | ShowTrackFlags::TrackWindow );
+ DockTimerHdl( nullptr );
+ }
+ else
+ {
+ mpDockWin->GetParent()->ImplGetFrameWindow()->HideTracking();
+ maDockIdle.Stop();
+ mpDockWin->EndDocking( maDockRect, true );
+ }
+ }
+ mbInMove = false;
+}
+
+void ImplDockFloatWin::Move()
+{
+ if( mbInMove )
+ return;
+
+ mbInMove = true;
+ FloatingWindow::Move();
+ mpDockWin->Move();
+
+ /*
+ * note: the window should only dock if
+ * the user releases all mouse buttons. The real problem here
+ * is that we don't get mouse events (at least not on X)
+ * if the mouse is on the decoration. So we have to start an
+ * awkward timer based process that polls the modifier/buttons
+ * to see whether they are in the right condition shortly after the
+ * last Move message.
+ */
+ if( ! mnLastUserEvent )
+ mnLastUserEvent = Application::PostUserEvent( LINK( this, ImplDockFloatWin, DockingHdl ), nullptr, true );
+}
+
+void ImplDockFloatWin::Resize()
+{
+ FloatingWindow::Resize();
+ Size aSize( GetSizePixel() );
+ mpDockWin->ImplPosSizeWindow( 0, 0, aSize.Width(), aSize.Height(), PosSizeFlags::PosSize );
+}
+
+void ImplDockFloatWin::Resizing( Size& rSize )
+{
+ FloatingWindow::Resizing( rSize );
+ mpDockWin->Resizing( rSize );
+}
+
+bool ImplDockFloatWin::Close()
+{
+ return mpDockWin->Close();
+}
+
+void DockingWindow::ImplStartDocking( const Point& rPos )
+{
+ if ( !mbDockable )
+ return;
+
+ maMouseOff = rPos;
+ mbDocking = true;
+ mbLastFloatMode = IsFloatingMode();
+ mbStartFloat = mbLastFloatMode;
+
+ // calculate FloatingBorder
+ VclPtr<FloatingWindow> pWin;
+ if ( mpFloatWin )
+ pWin = mpFloatWin;
+ else
+ pWin = VclPtr<ImplDockFloatWin>::Create( mpImplData->mpParent, mnFloatBits, nullptr );
+ pWin->GetBorder( mnDockLeft, mnDockTop, mnDockRight, mnDockBottom );
+ if ( !mpFloatWin )
+ pWin.disposeAndClear();
+
+ Point aPos = OutputToScreenPixel( Point() );
+ Size aSize = Window::GetOutputSizePixel();
+ mnTrackX = aPos.X();
+ mnTrackY = aPos.Y();
+ mnTrackWidth = aSize.Width();
+ mnTrackHeight = aSize.Height();
+
+ if ( mbLastFloatMode )
+ {
+ maMouseOff.AdjustX(mnDockLeft );
+ maMouseOff.AdjustY(mnDockTop );
+ mnTrackX -= mnDockLeft;
+ mnTrackY -= mnDockTop;
+ mnTrackWidth += mnDockLeft+mnDockRight;
+ mnTrackHeight += mnDockTop+mnDockBottom;
+ }
+
+ if ( GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Docking &&
+ !( mnFloatBits & ( WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE ) ) ) // no full drag when migrating to system window
+ mbDragFull = true;
+ else
+ {
+ StartDocking();
+ mbDragFull = false;
+ ImplUpdateAll();
+ ImplGetFrameWindow()->ImplUpdateAll();
+ }
+
+ StartTracking( StartTrackingFlags::KeyMod );
+}
+
+void DockingWindow::ImplInitDockingWindowData()
+{
+ mpWindowImpl->mbDockWin = true;
+ mpFloatWin = nullptr;
+ mpOldBorderWin = nullptr;
+ mpImplData.reset(new ImplData);
+ mnTrackX = 0;
+ mnTrackY = 0;
+ mnTrackWidth = 0;
+ mnTrackHeight = 0;
+ mnDockLeft = 0;
+ mnDockTop = 0;
+ mnDockRight = 0;
+ mnDockBottom = 0;
+ mnFloatBits = 0;
+ mbDockCanceled = false;
+ mbDockable = false;
+ mbDocking = false;
+ mbDragFull = false;
+ mbLastFloatMode = false;
+ mbStartFloat = false;
+ mbDockBtn = false;
+ mbHideBtn = false;
+ mbIsDeferredInit = false;
+ mbIsCalculatingInitialLayoutSize = false;
+ mpDialogParent = nullptr;
+
+ //To-Do, reuse maResizeTimer
+ maLayoutIdle.SetPriority(TaskPriority::RESIZE);
+ maLayoutIdle.SetInvokeHandler( LINK( this, DockingWindow, ImplHandleLayoutTimerHdl ) );
+}
+
+void DockingWindow::ImplInit( vcl::Window* pParent, WinBits nStyle )
+{
+ if ( !(nStyle & WB_NODIALOGCONTROL) )
+ nStyle |= WB_DIALOGCONTROL;
+
+ mpImplData->mpParent = pParent;
+ mbDockable = (nStyle & WB_DOCKABLE) != 0;
+ mnFloatBits = WB_BORDER | (nStyle & DOCKWIN_FLOATSTYLES);
+ nStyle &= ~(DOCKWIN_FLOATSTYLES | WB_BORDER);
+
+ Window::ImplInit( pParent, nStyle, nullptr );
+
+ ImplInitSettings();
+}
+
+void DockingWindow::ImplInitSettings()
+{
+ // Hack: to be able to build DockingWindows w/o background before switching
+ // TODO: Hack
+ if ( !IsBackground() )
+ return;
+
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+
+ Color aColor;
+ if ( IsControlBackground() )
+ aColor = GetControlBackground();
+ else if ( Window::GetStyle() & WB_3DLOOK )
+ aColor = rStyleSettings.GetFaceColor();
+ else
+ aColor = rStyleSettings.GetWindowColor();
+ SetBackground( aColor );
+}
+
+DockingWindow::DockingWindow( WindowType nType, const char* pIdleDebugName ) :
+ Window(nType),
+ maLayoutIdle( pIdleDebugName )
+{
+ ImplInitDockingWindowData();
+}
+
+DockingWindow::DockingWindow( vcl::Window* pParent, WinBits nStyle, const char* pIdleDebugName ) :
+ Window( WindowType::DOCKINGWINDOW ),
+ maLayoutIdle( pIdleDebugName )
+{
+ ImplInitDockingWindowData();
+ ImplInit( pParent, nStyle );
+}
+
+//Find the real parent stashed in mpDialogParent.
+void DockingWindow::doDeferredInit(WinBits nBits)
+{
+ vcl::Window *pParent = mpDialogParent;
+ mpDialogParent = nullptr;
+ ImplInit(pParent, nBits);
+ mbIsDeferredInit = false;
+}
+
+void DockingWindow::loadUI(vcl::Window* pParent, const OUString& rID, const OUString& rUIXMLDescription,
+ const css::uno::Reference<css::frame::XFrame> &rFrame)
+{
+ mbIsDeferredInit = true;
+ mpDialogParent = pParent; //should be unset in doDeferredInit
+ m_pUIBuilder.reset( new VclBuilder(this, AllSettings::GetUIRootDir(), rUIXMLDescription, rID, rFrame) );
+}
+
+DockingWindow::DockingWindow(vcl::Window* pParent, const OUString& rID,
+ const OUString& rUIXMLDescription, const char* pIdleDebugName,
+ const css::uno::Reference<css::frame::XFrame> &rFrame)
+ : Window(WindowType::DOCKINGWINDOW),
+ maLayoutIdle( pIdleDebugName )
+{
+ ImplInitDockingWindowData();
+
+ loadUI(pParent, rID, rUIXMLDescription, rFrame);
+}
+
+DockingWindow::~DockingWindow()
+{
+ disposeOnce();
+}
+
+void DockingWindow::dispose()
+{
+ if ( IsFloatingMode() )
+ {
+ Show( false, ShowFlags::NoFocusChange );
+ SetFloatingMode(false);
+ }
+ mpImplData.reset();
+ mpFloatWin.clear();
+ mpOldBorderWin.clear();
+ mpDialogParent.clear();
+ disposeBuilder();
+ Window::dispose();
+}
+
+void DockingWindow::Tracking( const TrackingEvent& rTEvt )
+{
+ if( GetDockingManager()->IsDockable( this ) ) // new docking interface
+ return Window::Tracking( rTEvt );
+
+ if ( !mbDocking )
+ return;
+
+ if ( rTEvt.IsTrackingEnded() )
+ {
+ mbDocking = false;
+ if ( mbDragFull )
+ {
+ // reset old state on Cancel
+ if ( rTEvt.IsTrackingCanceled() )
+ {
+ StartDocking();
+ tools::Rectangle aRect( Point( mnTrackX, mnTrackY ), Size( mnTrackWidth, mnTrackHeight ) );
+ EndDocking( aRect, mbStartFloat );
+ }
+ }
+ else
+ {
+ HideTracking();
+ if ( rTEvt.IsTrackingCanceled() )
+ {
+ mbDockCanceled = true;
+ EndDocking( tools::Rectangle( Point( mnTrackX, mnTrackY ), Size( mnTrackWidth, mnTrackHeight ) ), mbLastFloatMode );
+ mbDockCanceled = false;
+ }
+ else
+ EndDocking( tools::Rectangle( Point( mnTrackX, mnTrackY ), Size( mnTrackWidth, mnTrackHeight ) ), mbLastFloatMode );
+ }
+ }
+ // dock only for non-synthetic MouseEvents
+ else if ( !rTEvt.GetMouseEvent().IsSynthetic() || rTEvt.GetMouseEvent().IsModifierChanged() )
+ {
+ Point aMousePos = rTEvt.GetMouseEvent().GetPosPixel();
+ Point aFrameMousePos = OutputToScreenPixel( aMousePos );
+ Size aFrameSize = mpWindowImpl->mpFrameWindow->GetOutputSizePixel();
+ if ( aFrameMousePos.X() < 0 )
+ aFrameMousePos.setX( 0 );
+ if ( aFrameMousePos.Y() < 0 )
+ aFrameMousePos.setY( 0 );
+ if ( aFrameMousePos.X() > aFrameSize.Width()-1 )
+ aFrameMousePos.setX( aFrameSize.Width()-1 );
+ if ( aFrameMousePos.Y() > aFrameSize.Height()-1 )
+ aFrameMousePos.setY( aFrameSize.Height()-1 );
+ aMousePos = ScreenToOutputPixel( aFrameMousePos );
+ aMousePos.AdjustX( -(maMouseOff.X()) );
+ aMousePos.AdjustY( -(maMouseOff.Y()) );
+ Point aFramePos = OutputToScreenPixel( aMousePos );
+ tools::Rectangle aTrackRect( aFramePos, Size( mnTrackWidth, mnTrackHeight ) );
+ tools::Rectangle aCompRect = aTrackRect;
+ aFramePos.AdjustX(maMouseOff.X() );
+ aFramePos.AdjustY(maMouseOff.Y() );
+ if ( mbDragFull )
+ StartDocking();
+ bool bFloatMode = Docking( aFramePos, aTrackRect );
+ if ( mbLastFloatMode != bFloatMode )
+ {
+ if ( bFloatMode )
+ {
+ aTrackRect.AdjustLeft( -mnDockLeft );
+ aTrackRect.AdjustTop( -mnDockTop );
+ aTrackRect.AdjustRight(mnDockRight );
+ aTrackRect.AdjustBottom(mnDockBottom );
+ }
+ else
+ {
+ if ( aCompRect == aTrackRect )
+ {
+ aTrackRect.AdjustLeft(mnDockLeft );
+ aTrackRect.AdjustTop(mnDockTop );
+ aTrackRect.AdjustRight( -mnDockRight );
+ aTrackRect.AdjustBottom( -mnDockBottom );
+ }
+ }
+ mbLastFloatMode = bFloatMode;
+ }
+ if ( mbDragFull )
+ {
+ Point aOldPos = OutputToScreenPixel( Point() );
+ EndDocking( aTrackRect, mbLastFloatMode );
+ // repaint if state or position has changed
+ if ( aOldPos != OutputToScreenPixel( Point() ) )
+ {
+ ImplUpdateAll();
+ ImplGetFrameWindow()->ImplUpdateAll();
+ }
+// EndDocking( aTrackRect, mbLastFloatMode );
+ }
+ else
+ {
+ ShowTrackFlags nTrackStyle;
+ if ( bFloatMode )
+ nTrackStyle = ShowTrackFlags::Big;
+ else
+ nTrackStyle = ShowTrackFlags::Object;
+ tools::Rectangle aShowTrackRect = aTrackRect;
+ aShowTrackRect.SetPos( ScreenToOutputPixel( aShowTrackRect.TopLeft() ) );
+ ShowTracking( aShowTrackRect, nTrackStyle );
+
+ // recalculate mouse offset, as the rectangle was changed
+ maMouseOff.setX( aFramePos.X() - aTrackRect.Left() );
+ maMouseOff.setY( aFramePos.Y() - aTrackRect.Top() );
+ }
+
+ mnTrackX = aTrackRect.Left();
+ mnTrackY = aTrackRect.Top();
+ mnTrackWidth = aTrackRect.GetWidth();
+ mnTrackHeight = aTrackRect.GetHeight();
+ }
+}
+
+bool DockingWindow::EventNotify( NotifyEvent& rNEvt )
+{
+ if( GetDockingManager()->IsDockable( this ) ) // new docking interface
+ return Window::EventNotify( rNEvt );
+
+ if ( mbDockable )
+ {
+ const bool bDockingSupportCrippled = !StyleSettings::GetDockingFloatsSupported();
+
+ if ( rNEvt.GetType() == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ const MouseEvent* pMEvt = rNEvt.GetMouseEvent();
+ if ( pMEvt->IsLeft() )
+ {
+ if (!bDockingSupportCrippled && pMEvt->IsMod1() && (pMEvt->GetClicks() == 2) )
+ {
+ SetFloatingMode( !IsFloatingMode() );
+ if ( IsFloatingMode() )
+ ToTop( ToTopFlags::GrabFocusOnly );
+ return true;
+ }
+ else if ( pMEvt->GetClicks() == 1 )
+ {
+ // check if window is floating standalone (IsFloating())
+ // or only partially floating and still docked with one border
+ // ( !mpWindowImpl->mbFrame)
+ if( ! IsFloatingMode() || ! mpFloatWin->mpWindowImpl->mbFrame )
+ {
+ Point aPos = pMEvt->GetPosPixel();
+ vcl::Window* pWindow = rNEvt.GetWindow();
+ if ( pWindow != this )
+ {
+ aPos = pWindow->OutputToScreenPixel( aPos );
+ aPos = ScreenToOutputPixel( aPos );
+ }
+ ImplStartDocking( aPos );
+ }
+ return true;
+ }
+ }
+ }
+ else if( rNEvt.GetType() == NotifyEventType::KEYINPUT )
+ {
+ const vcl::KeyCode& rKey = rNEvt.GetKeyEvent()->GetKeyCode();
+ if( rKey.GetCode() == KEY_F10 && rKey.GetModifier() &&
+ rKey.IsShift() && rKey.IsMod1() && !bDockingSupportCrippled )
+ {
+ SetFloatingMode( !IsFloatingMode() );
+ if ( IsFloatingMode() )
+ ToTop( ToTopFlags::GrabFocusOnly );
+ return true;
+ }
+ }
+ }
+
+ return Window::EventNotify( rNEvt );
+}
+
+void DockingWindow::StartDocking()
+{
+ mbDocking = true;
+}
+
+bool DockingWindow::Docking( const Point&, tools::Rectangle& )
+{
+ return IsFloatingMode();
+}
+
+void DockingWindow::EndDocking( const tools::Rectangle& rRect, bool bFloatMode )
+{
+ bool bOrigDockCanceled = mbDockCanceled;
+ if (bFloatMode && !StyleSettings::GetDockingFloatsSupported())
+ mbDockCanceled = true;
+
+ if ( !IsDockingCanceled() )
+ {
+ if ( bFloatMode != IsFloatingMode() )
+ {
+ SetFloatingMode( bFloatMode );
+ if ( IsFloatingMode() )
+ ToTop( ToTopFlags::GrabFocusOnly );
+ if ( bFloatMode && mpFloatWin )
+ mpFloatWin->SetPosSizePixel( rRect.TopLeft(), rRect.GetSize() );
+ }
+ if ( !bFloatMode )
+ {
+ Point aPos = rRect.TopLeft();
+ aPos = GetParent()->ScreenToOutputPixel( aPos );
+ Window::SetPosSizePixel( aPos, rRect.GetSize() );
+ }
+ }
+ mbDocking = false;
+ mbDockCanceled = bOrigDockCanceled;
+}
+
+bool DockingWindow::PrepareToggleFloatingMode()
+{
+ return true;
+}
+
+bool DockingWindow::Close()
+{
+ VclPtr<vcl::Window> xWindow = this;
+ CallEventListeners( VclEventId::WindowClose );
+ if ( xWindow->isDisposed() )
+ return false;
+
+ if ( mpWindowImpl->mxWindowPeer.is() && IsCreatedWithToolkit() )
+ return false;
+
+ Show( false, ShowFlags::NoFocusChange );
+ return true;
+}
+
+void DockingWindow::ToggleFloatingMode()
+{
+}
+
+void DockingWindow::Resizing( Size& )
+{
+}
+
+void DockingWindow::DoInitialLayout()
+{
+ if (GetSettings().GetStyleSettings().GetAutoMnemonic())
+ GenerateAutoMnemonicsOnHierarchy(this);
+
+ if (isLayoutEnabled())
+ {
+ mbIsCalculatingInitialLayoutSize = true;
+ setDeferredProperties();
+ if (IsFloatingMode())
+ setOptimalLayoutSize();
+ mbIsCalculatingInitialLayoutSize = false;
+ }
+}
+
+void DockingWindow::StateChanged( StateChangedType nType )
+{
+ switch(nType)
+ {
+ case StateChangedType::InitShow:
+ DoInitialLayout();
+ break;
+
+ case StateChangedType::ControlBackground:
+ ImplInitSettings();
+ Invalidate();
+ break;
+
+ case StateChangedType::Style:
+ mbDockable = (GetStyle() & WB_DOCKABLE) != 0;
+ break;
+
+ default:
+ break;
+ }
+
+ Window::StateChanged( nType );
+}
+
+void DockingWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ ImplInitSettings();
+ Invalidate();
+ }
+ else
+ Window::DataChanged( rDCEvt );
+}
+
+void DockingWindow::SetFloatingMode( bool bFloatMode )
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ {
+ pWrapper->SetFloatingMode( bFloatMode );
+ return;
+ }
+ if ( IsFloatingMode() == bFloatMode )
+ return;
+
+ if ( !PrepareToggleFloatingMode() ) // changes to floating mode can be vetoed
+ return;
+
+ bool bVisible = IsVisible();
+
+ if ( bFloatMode )
+ {
+ // set deferred properties early, so border width will end up
+ // in our mpWindowImpl->mnBorderWidth, not in mpBorderWindow.
+ // (see its usage in setPosSizeOnContainee and GetOptimalSize.)
+ setDeferredProperties();
+
+ Show( false, ShowFlags::NoFocusChange );
+
+ maDockPos = Window::GetPosPixel();
+
+ vcl::Window* pRealParent = mpWindowImpl->mpRealParent;
+ mpOldBorderWin = mpWindowImpl->mpBorderWindow;
+
+ VclPtrInstance<ImplDockFloatWin> pWin(
+ mpImplData->mpParent,
+ mnFloatBits & ( WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE ) ? mnFloatBits | WB_SYSTEMWINDOW : mnFloatBits,
+ this );
+ mpFloatWin = pWin;
+ mpWindowImpl->mpBorderWindow = nullptr;
+ mpWindowImpl->mnLeftBorder = 0;
+ mpWindowImpl->mnTopBorder = 0;
+ mpWindowImpl->mnRightBorder = 0;
+ mpWindowImpl->mnBottomBorder = 0;
+ // if the parent gets destroyed, we also have to reset the parent of the BorderWindow
+ if ( mpOldBorderWin )
+ mpOldBorderWin->SetParent( pWin );
+
+ // #i123765# reset the buffered DropTargets when undocking, else it may not
+ // be correctly initialized
+ mpWindowImpl->mxDNDListenerContainer.clear();
+
+ SetParent( pWin );
+ SetPosPixel( Point() );
+ mpWindowImpl->mpBorderWindow = pWin;
+ pWin->mpWindowImpl->mpClientWindow = this;
+ mpWindowImpl->mpRealParent = pRealParent;
+ pWin->SetText( Window::GetText() );
+ Size aSize(Window::GetSizePixel());
+ pWin->SetOutputSizePixel(aSize);
+ pWin->SetPosPixel( maFloatPos );
+ // pass on DockingData to FloatingWindow
+ pWin->ShowTitleButton( TitleButton::Docking, mbDockBtn );
+ pWin->ShowTitleButton( TitleButton::Hide, mbHideBtn );
+ pWin->SetMinOutputSizePixel( maMinOutSize );
+
+ pWin->SetMaxOutputSizePixel( mpImplData->maMaxOutSize );
+
+ ToggleFloatingMode();
+
+ if ( bVisible )
+ Show();
+ }
+ else
+ {
+ Show( false, ShowFlags::NoFocusChange );
+
+ // store FloatingData in FloatingWindow
+ maFloatPos = mpFloatWin->GetPosPixel();
+ mbDockBtn = mpFloatWin->IsTitleButtonVisible( TitleButton::Docking );
+ mbHideBtn = mpFloatWin->IsTitleButtonVisible( TitleButton::Hide );
+ maMinOutSize = mpFloatWin->GetMinOutputSizePixel();
+ mpImplData->maMaxOutSize = mpFloatWin->GetMaxOutputSizePixel();
+
+ vcl::Window* pRealParent = mpWindowImpl->mpRealParent;
+ mpWindowImpl->mpBorderWindow = nullptr;
+ if ( mpOldBorderWin )
+ {
+ SetParent( mpOldBorderWin );
+ static_cast<ImplBorderWindow*>(mpOldBorderWin.get())->GetBorder( mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder );
+ mpOldBorderWin->Resize();
+ }
+ mpWindowImpl->mpBorderWindow = mpOldBorderWin;
+ SetParent( pRealParent );
+ mpWindowImpl->mpRealParent = pRealParent;
+ mpFloatWin.disposeAndClear();
+ SetPosPixel( maDockPos );
+
+ ToggleFloatingMode();
+
+ if ( bVisible )
+ Show();
+ }
+}
+
+void DockingWindow::SetFloatStyle( WinBits nStyle )
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ {
+ pWrapper->SetFloatStyle( nStyle );
+ return;
+ }
+
+ mnFloatBits = nStyle;
+}
+
+WinBits DockingWindow::GetFloatStyle() const
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ {
+ return pWrapper->GetFloatStyle();
+ }
+
+ return mnFloatBits;
+}
+
+void DockingWindow::setPosSizePixel( tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight,
+ PosSizeFlags nFlags )
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if (pWrapper)
+ {
+ if (!pWrapper->mpFloatWin)
+ Window::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
+ }
+ else
+ {
+ if (!mpFloatWin)
+ Window::setPosSizePixel( nX, nY, nWidth, nHeight, nFlags );
+ else if (comphelper::LibreOfficeKit::isActive())
+ {
+ if ((nFlags & PosSizeFlags::Size) == PosSizeFlags::Size)
+ mpFloatWin->SetOutputSizePixel({ nWidth, nHeight });
+ if ((nFlags & PosSizeFlags::Pos) == PosSizeFlags::Pos)
+ mpFloatWin->SetPosPixel({ nX, nY });
+ }
+ }
+
+ if (::isLayoutEnabled(this))
+ setPosSizeOnContainee();
+}
+
+Point DockingWindow::GetPosPixel() const
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ {
+ if ( pWrapper->mpFloatWin )
+ return pWrapper->mpFloatWin->GetPosPixel();
+ else
+ return Window::GetPosPixel();
+ }
+
+ if ( mpFloatWin )
+ return mpFloatWin->GetPosPixel();
+ else
+ return Window::GetPosPixel();
+}
+
+Size DockingWindow::GetSizePixel() const
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ {
+ if ( pWrapper->mpFloatWin )
+ return pWrapper->mpFloatWin->GetSizePixel();
+ else
+ return Window::GetSizePixel();
+ }
+
+ if ( mpFloatWin )
+ return mpFloatWin->GetSizePixel();
+ else
+ return Window::GetSizePixel();
+}
+
+void DockingWindow::SetOutputSizePixel( const Size& rNewSize )
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ {
+ if ( pWrapper->mpFloatWin )
+ pWrapper->mpFloatWin->SetOutputSizePixel( rNewSize );
+ else
+ Window::SetOutputSizePixel( rNewSize );
+ return;
+ }
+
+ if ( mpFloatWin )
+ mpFloatWin->SetOutputSizePixel( rNewSize );
+ else
+ Window::SetOutputSizePixel( rNewSize );
+}
+
+Size DockingWindow::GetOutputSizePixel() const
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ {
+ if ( pWrapper->mpFloatWin )
+ return pWrapper->mpFloatWin->GetOutputSizePixel();
+ else
+ return Window::GetOutputSizePixel();
+ }
+
+ if ( mpFloatWin )
+ return mpFloatWin->GetOutputSizePixel();
+ else
+ return Window::GetOutputSizePixel();
+}
+
+Point DockingWindow::GetFloatingPos() const
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ {
+ if ( pWrapper->mpFloatWin )
+ {
+ vcl::WindowData aData;
+ aData.setMask(vcl::WindowDataMask::Pos);
+ pWrapper->mpFloatWin->GetWindowState( aData );
+ AbsoluteScreenPixelPoint aPos(aData.x(), aData.y());
+ // LOK needs logic coordinates not absolute screen position for autofilter menu
+ if (!comphelper::LibreOfficeKit::isActive() || get_id() != "check_list_menu")
+ return pWrapper->mpFloatWin->GetParent()->ImplGetFrameWindow()->AbsoluteScreenToOutputPixel( aPos );
+ return Point(aPos);
+ }
+ else
+ return maFloatPos;
+ }
+
+ if ( mpFloatWin )
+ {
+ vcl::WindowData aData;
+ aData.setMask(vcl::WindowDataMask::Pos);
+ mpFloatWin->GetWindowState( aData );
+ AbsoluteScreenPixelPoint aPos(aData.x(), aData.y());
+ return mpFloatWin->GetParent()->ImplGetFrameWindow()->AbsoluteScreenToOutputPixel( aPos );
+ }
+ else
+ return maFloatPos;
+}
+
+bool DockingWindow::IsFloatingMode() const
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ return pWrapper->IsFloatingMode();
+ else
+ return (mpFloatWin != nullptr);
+}
+
+void DockingWindow::SetMaxOutputSizePixel( const Size& rSize )
+{
+ if ( mpFloatWin )
+ mpFloatWin->SetMaxOutputSizePixel( rSize );
+ mpImplData->maMaxOutSize = rSize;
+}
+
+void DockingWindow::SetText(const OUString& rStr)
+{
+ setDeferredProperties();
+ Window::SetText(rStr);
+}
+
+OUString DockingWindow::GetText() const
+{
+ const_cast<DockingWindow*>(this)->setDeferredProperties();
+ return Window::GetText();
+}
+
+bool DockingWindow::isLayoutEnabled() const
+{
+ //pre dtor called, and single child is a container => we're layout enabled
+ return mpImplData && ::isLayoutEnabled(this);
+}
+
+void DockingWindow::setOptimalLayoutSize()
+{
+ maLayoutIdle.Stop();
+
+ //resize DockingWindow to fit requisition on initial show
+ Size aSize = get_preferred_size();
+
+ Size aMax(bestmaxFrameSizeForScreenSize(Size(GetDesktopRectPixel().GetSize())));
+
+ aSize.setWidth( std::min(aMax.Width(), aSize.Width()) );
+ aSize.setHeight( std::min(aMax.Height(), aSize.Height()) );
+
+ SetMinOutputSizePixel(aSize);
+ setPosSizeOnContainee();
+}
+
+void DockingWindow::setPosSizeOnContainee()
+{
+ Size aSize = GetOutputSizePixel();
+
+ // Don't make the border width accessible via get_border_width(),
+ // otherwise the floating window will handle the border as well.
+ sal_Int32 nBorderWidth = mpWindowImpl->mnBorderWidth;
+
+ aSize.AdjustWidth( -(2 * nBorderWidth) );
+ aSize.AdjustHeight( -(2 * nBorderWidth) );
+
+ Window* pBox = GetWindow(GetWindowType::FirstChild);
+ assert(pBox);
+ VclContainer::setLayoutAllocation(*pBox, Point(nBorderWidth, nBorderWidth), aSize);
+}
+
+Size DockingWindow::GetOptimalSize() const
+{
+ if (!isLayoutEnabled())
+ return Window::GetOptimalSize();
+
+ Size aSize = VclContainer::getLayoutRequisition(*GetWindow(GetWindowType::FirstChild));
+
+ // Don't make the border width accessible via get_border_width(),
+ // otherwise the floating window will handle the border as well.
+ sal_Int32 nBorderWidth = mpWindowImpl->mnBorderWidth;
+
+ aSize.AdjustHeight(2 * nBorderWidth );
+ aSize.AdjustWidth(2 * nBorderWidth );
+
+ return aSize;
+}
+
+void DockingWindow::queue_resize(StateChangedType eReason)
+{
+ bool bTriggerLayout = true;
+ if (maLayoutIdle.IsActive() || mbIsCalculatingInitialLayoutSize)
+ {
+ bTriggerLayout = false;
+ }
+ if (!isLayoutEnabled())
+ {
+ bTriggerLayout = false;
+ }
+ if (bTriggerLayout)
+ {
+ InvalidateSizeCache();
+ maLayoutIdle.Start();
+ }
+ vcl::Window::queue_resize(eReason);
+}
+
+IMPL_LINK_NOARG(DockingWindow, ImplHandleLayoutTimerHdl, Timer*, void)
+{
+ if (!isLayoutEnabled())
+ {
+ SAL_WARN_IF(GetWindow(GetWindowType::FirstChild), "vcl.layout", "DockingWindow has become non-layout because extra children have been added directly to it.");
+ return;
+ }
+ setPosSizeOnContainee();
+}
+
+void DockingWindow::SetMinOutputSizePixel( const Size& rSize )
+{
+ if ( mpFloatWin )
+ mpFloatWin->SetMinOutputSizePixel( rSize );
+ maMinOutSize = rSize;
+}
+
+const Size& DockingWindow::GetMinOutputSizePixel() const
+{
+ if ( mpFloatWin )
+ return mpFloatWin->GetMinOutputSizePixel();
+ return maMinOutSize;
+}
+
+void DockingWindow::SetFloatingPos( const Point& rNewPos )
+{
+ if ( mpFloatWin )
+ mpFloatWin->SetPosPixel( rNewPos );
+ else
+ maFloatPos = rNewPos;
+}
+
+SystemWindow* DockingWindow::GetFloatingWindow() const
+{
+ return mpFloatWin;
+}
+
+DropdownDockingWindow::DropdownDockingWindow(vcl::Window* pParent, const css::uno::Reference<css::frame::XFrame>& rFrame, bool bTearable)
+ : DockingWindow(pParent,
+ !bTearable ? OUString("InterimDockParent") : OUString("InterimTearableParent"),
+ !bTearable ? OUString("vcl/ui/interimdockparent.ui") : OUString("vcl/ui/interimtearableparent.ui"),
+ "vcl::DropdownDockingWindow maLayoutIdle",
+ rFrame)
+ , m_xBox(m_pUIBuilder->get("box"))
+{
+}
+
+DropdownDockingWindow::~DropdownDockingWindow()
+{
+ disposeOnce();
+}
+
+void DropdownDockingWindow::dispose()
+{
+ m_xBox.clear();
+ DockingWindow::dispose();
+}
+
+ResizableDockingWindow::ResizableDockingWindow(vcl::Window* pParent, const css::uno::Reference<css::frame::XFrame>& rFrame)
+ : DockingWindow(pParent, "DockingWindow", "vcl/ui/dockingwindow.ui", "vcl::ResizableDockingWindow maLayoutIdle", rFrame)
+ , m_xBox(m_pUIBuilder->get("box"))
+{
+}
+
+ResizableDockingWindow::ResizableDockingWindow(vcl::Window* pParent, WinBits nStyle)
+ : DockingWindow(pParent, nStyle, "vcl::ResizableDockingWindow maLayoutIdle")
+{
+}
+
+// needed to blow away the cached size of the boundary between vcl and hosted child widget
+void ResizableDockingWindow::InvalidateChildSizeCache()
+{
+ // find the bottom vcl::Window of the hierarchy and queue_resize on that
+ // one will invalidate all the size caches upwards
+ vcl::Window* pChild = GetWindow(GetWindowType::FirstChild);
+ while (true)
+ {
+ vcl::Window* pSubChild = pChild->GetWindow(GetWindowType::FirstChild);
+ if (!pSubChild)
+ break;
+ pChild = pSubChild;
+ }
+ pChild->queue_resize();
+}
+
+ResizableDockingWindow::~ResizableDockingWindow()
+{
+ disposeOnce();
+}
+
+void ResizableDockingWindow::dispose()
+{
+ m_xBox.clear();
+ DockingWindow::dispose();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/errinf.cxx b/vcl/source/window/errinf.cxx
new file mode 100644
index 0000000000..409f54eb1f
--- /dev/null
+++ b/vcl/source/window/errinf.cxx
@@ -0,0 +1,222 @@
+/* -*- 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 <sal/log.hxx>
+
+#include <tools/debug.hxx>
+#include <utility>
+#include <vcl/errinf.hxx>
+
+#include <algorithm>
+#include <vector>
+
+class ErrorHandler;
+
+namespace {
+
+ ErrorRegistry& GetErrorRegistry()
+ {
+ static ErrorRegistry gErrorRegistry;
+ return gErrorRegistry;
+ }
+
+}
+
+bool ErrorStringFactory::CreateString(const ErrCodeMsg& nInfo, OUString& rStr)
+{
+ for(const ErrorHandler *pHdlr : GetErrorRegistry().errorHandlers)
+ {
+ if(pHdlr->CreateString(nInfo, rStr))
+ return true;
+ }
+ return false;
+}
+
+ErrorRegistry::ErrorRegistry()
+ : pDsp(nullptr)
+ , bIsWindowDsp(false)
+ , m_bLock(false)
+{
+}
+
+void ErrorRegistry::RegisterDisplay(BasicDisplayErrorFunc *aDsp)
+{
+ ErrorRegistry &rData = GetErrorRegistry();
+ rData.bIsWindowDsp = false;
+ rData.pDsp = reinterpret_cast< DisplayFnPtr >(aDsp);
+}
+
+void ErrorRegistry::RegisterDisplay(WindowDisplayErrorFunc *aDsp)
+{
+ ErrorRegistry &rData = GetErrorRegistry();
+ rData.bIsWindowDsp = true;
+ rData.pDsp = reinterpret_cast< DisplayFnPtr >(aDsp);
+}
+
+void ErrorRegistry::SetLock(bool bLock)
+{
+ ErrorRegistry& rData = GetErrorRegistry();
+ rData.m_bLock = bLock;
+}
+
+bool ErrorRegistry::GetLock()
+{
+ ErrorRegistry& rData = GetErrorRegistry();
+ return rData.m_bLock;
+}
+
+void ErrorRegistry::Reset()
+{
+ ErrorRegistry &rData = GetErrorRegistry();
+ rData = ErrorRegistry();
+}
+
+static void aDspFunc(const OUString &rErr, const OUString &rAction)
+{
+ SAL_WARN("vcl", "Action: " << rAction << " Error: " << rErr);
+}
+
+ErrorHandler::ErrorHandler()
+{
+ ErrorRegistry &rData = GetErrorRegistry();
+ rData.errorHandlers.insert(rData.errorHandlers.begin(), this);
+
+ if(!rData.pDsp)
+ ErrorRegistry::RegisterDisplay(&aDspFunc);
+}
+
+ErrorHandler::~ErrorHandler()
+{
+ auto &rErrorHandlers = GetErrorRegistry().errorHandlers;
+ std::erase(rErrorHandlers, this);
+}
+
+bool ErrorHandler::GetErrorString(const ErrCodeMsg& nErrCode, OUString& rErrStr)
+{
+ OUString aErr;
+
+ if(!nErrCode || nErrCode == ERRCODE_ABORT)
+ return false;
+
+ if (ErrorStringFactory::CreateString(nErrCode, aErr))
+ {
+ rErrStr = aErr;
+ return true;
+ }
+
+ return false;
+}
+
+DialogMask ErrorHandler::HandleError(const ErrCodeMsg& nErr, weld::Window *pParent, DialogMask nFlags)
+{
+ if (nErr == ERRCODE_NONE || nErr == ERRCODE_ABORT)
+ return DialogMask::NONE;
+
+ ErrorRegistry &rData = GetErrorRegistry();
+ OUString aAction;
+
+ if (!rData.contexts.empty())
+ {
+ rData.contexts.front()->GetString(nErr, aAction);
+
+ for(ErrorContext *pCtx : rData.contexts)
+ {
+ if(pCtx->GetParent())
+ {
+ pParent = pCtx->GetParent();
+ break;
+ }
+ }
+ }
+
+ bool bWarning = nErr.IsWarning();
+ DialogMask nErrFlags = DialogMask::ButtonDefaultsOk | DialogMask::ButtonsOk;
+ if (bWarning)
+ nErrFlags |= DialogMask::MessageWarning;
+ else
+ nErrFlags |= DialogMask::MessageError;
+
+ if( nErr.GetDialogMask() != DialogMask::NONE )
+ nErrFlags = nErr.GetDialogMask();
+
+ OUString aErr;
+ if (ErrorStringFactory::CreateString(nErr, aErr))
+ {
+ if (!rData.pDsp || rData.m_bLock)
+ {
+ SAL_WARN( "vcl", "Action: " << aAction << "Error: " << aErr);
+ }
+ else
+ {
+ if(!rData.bIsWindowDsp)
+ {
+ (*reinterpret_cast<BasicDisplayErrorFunc*>(rData.pDsp))(aErr,aAction);
+ return DialogMask::NONE;
+ }
+ else
+ {
+ if (nFlags != DialogMask::MAX)
+ nErrFlags = nFlags;
+
+ return (*reinterpret_cast<WindowDisplayErrorFunc*>(rData.pDsp))(
+ pParent, nErrFlags, aErr, aAction);
+ }
+ }
+ }
+
+ SAL_WARN( "vcl", "Error not handled " << nErr);
+ // Error 1 (ERRCODE_ABORT) is classified as a General Error in sfx
+ if (nErr.GetCode() != ERRCODE_ABORT)
+ HandleError(ERRCODE_ABORT);
+ else
+ OSL_FAIL("ERRCODE_ABORT not handled");
+
+ return DialogMask::NONE;
+}
+
+struct ImplErrorContext
+{
+ weld::Window *pWin;
+};
+
+ErrorContext::ErrorContext(weld::Window *pWinP)
+ : pImpl( new ImplErrorContext )
+{
+ pImpl->pWin = pWinP;
+ GetErrorRegistry().contexts.insert(GetErrorRegistry().contexts.begin(), this);
+}
+
+ErrorContext::~ErrorContext()
+{
+ auto &rContexts = GetErrorRegistry().contexts;
+ std::erase(rContexts, this);
+}
+
+ErrorContext *ErrorContext::GetContext()
+{
+ return GetErrorRegistry().contexts.empty() ? nullptr : GetErrorRegistry().contexts.front();
+}
+
+weld::Window* ErrorContext::GetParent()
+{
+ return pImpl ? pImpl->pWin : nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/event.cxx b/vcl/source/window/event.cxx
new file mode 100644
index 0000000000..23d910112a
--- /dev/null
+++ b/vcl/source/window/event.cxx
@@ -0,0 +1,673 @@
+/* -*- 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 <vcl/event.hxx>
+#include <vcl/window.hxx>
+#include <vcl/dockwin.hxx>
+#include <vcl/layout.hxx>
+#include <sal/log.hxx>
+
+#include <window.h>
+#include <svdata.hxx>
+#include <salframe.hxx>
+#include <config_features.h>
+#include <comphelper/scopeguard.hxx>
+
+#include "impldockingwrapper.hxx"
+
+namespace vcl {
+
+void Window::DataChanged( const DataChangedEvent& )
+{
+}
+
+void Window::NotifyAllChildren( DataChangedEvent& rDCEvt )
+{
+ CompatDataChanged( rDCEvt );
+
+ vcl::Window* pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->NotifyAllChildren( rDCEvt );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+}
+
+bool Window::PreNotify( NotifyEvent& rNEvt )
+{
+ bool bDone = false;
+ if ( mpWindowImpl->mpParent && !ImplIsOverlapWindow() )
+ bDone = mpWindowImpl->mpParent->CompatPreNotify( rNEvt );
+
+ if ( !bDone )
+ {
+ if( rNEvt.GetType() == NotifyEventType::GETFOCUS )
+ {
+ bool bCompoundFocusChanged = false;
+ if ( mpWindowImpl->mbCompoundControl && !mpWindowImpl->mbCompoundControlHasFocus && HasChildPathFocus() )
+ {
+ mpWindowImpl->mbCompoundControlHasFocus = true;
+ bCompoundFocusChanged = true;
+ }
+
+ if ( bCompoundFocusChanged || ( rNEvt.GetWindow() == this ) )
+ CallEventListeners( VclEventId::WindowGetFocus );
+ }
+ else if( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
+ {
+ bool bCompoundFocusChanged = false;
+ if ( mpWindowImpl->mbCompoundControl && mpWindowImpl->mbCompoundControlHasFocus && !HasChildPathFocus() )
+ {
+ mpWindowImpl->mbCompoundControlHasFocus = false ;
+ bCompoundFocusChanged = true;
+ }
+
+ if ( bCompoundFocusChanged || ( rNEvt.GetWindow() == this ) )
+ CallEventListeners( VclEventId::WindowLoseFocus );
+ }
+
+ // #82968# mouse and key events will be notified after processing ( in ImplNotifyKeyMouseCommandEventListeners() )!
+ // see also ImplHandleMouseEvent(), ImplHandleKey()
+
+ }
+
+ return bDone;
+}
+
+namespace
+{
+ bool parentNotDialogControl(Window* pWindow)
+ {
+ vcl::Window* pParent = getNonLayoutParent(pWindow);
+ if (!pParent)
+ return true;
+ return ((pParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL);
+ }
+}
+
+bool Window::EventNotify( NotifyEvent& rNEvt )
+{
+ bool bRet = false;
+
+ if (isDisposed())
+ return false;
+
+ // check for docking window
+ // but do nothing if window is docked and locked
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if ((GetStyle() & WB_DOCKABLE) &&
+ pWrapper && ( pWrapper->IsFloatingMode() || !pWrapper->IsLocked() ))
+ {
+ const bool bDockingSupportCrippled = !StyleSettings::GetDockingFloatsSupported();
+
+ if ( rNEvt.GetType() == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ const MouseEvent* pMEvt = rNEvt.GetMouseEvent();
+ bool bHit = pWrapper->GetDragArea().Contains( pMEvt->GetPosPixel() );
+ if ( pMEvt->IsLeft() )
+ {
+ if (!bDockingSupportCrippled && pMEvt->IsMod1() && (pMEvt->GetClicks() == 2))
+ {
+ // ctrl double click toggles floating mode
+ pWrapper->SetFloatingMode( !pWrapper->IsFloatingMode() );
+ return true;
+ }
+ else if ( pMEvt->GetClicks() == 1 && bHit)
+ {
+ // allow start docking during mouse move
+ pWrapper->ImplEnableStartDocking();
+ return true;
+ }
+ }
+ }
+ else if ( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
+ {
+ const MouseEvent* pMEvt = rNEvt.GetMouseEvent();
+ bool bHit = pWrapper->GetDragArea().Contains( pMEvt->GetPosPixel() );
+ if ( pMEvt->IsLeft() )
+ {
+ // check if a single click initiated this sequence ( ImplStartDockingEnabled() )
+ // check if window is docked and
+ if( pWrapper->ImplStartDockingEnabled() && !pWrapper->IsFloatingMode() &&
+ !pWrapper->IsDocking() && bHit )
+ {
+ Point aPos = pMEvt->GetPosPixel();
+ vcl::Window* pWindow = rNEvt.GetWindow();
+ if ( pWindow != this )
+ {
+ aPos = pWindow->OutputToScreenPixel( aPos );
+ aPos = ScreenToOutputPixel( aPos );
+ }
+ pWrapper->ImplStartDocking( aPos );
+ }
+ return true;
+ }
+ }
+ else if( rNEvt.GetType() == NotifyEventType::KEYINPUT )
+ {
+ const vcl::KeyCode& rKey = rNEvt.GetKeyEvent()->GetKeyCode();
+ if (rKey.GetCode() == KEY_F10 && rKey.GetModifier() &&
+ rKey.IsShift() && rKey.IsMod1() && !bDockingSupportCrippled)
+ {
+ pWrapper->SetFloatingMode( !pWrapper->IsFloatingMode() );
+ /* At this point the floating toolbar frame does not have the
+ * input focus since these frames don't get the focus per default
+ * To enable keyboard handling of this toolbar set the input focus
+ * to the frame. This needs to be done with ToTop since GrabFocus
+ * would not notice any change since "this" already has the focus.
+ */
+ if( pWrapper->IsFloatingMode() )
+ ToTop( ToTopFlags::GrabFocusOnly );
+ return true;
+ }
+ }
+ }
+
+ // manage the dialogs
+ if ( (GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL )
+ {
+ // if the parent also has dialog control activated, the parent takes over control
+ if ( (rNEvt.GetType() == NotifyEventType::KEYINPUT) || (rNEvt.GetType() == NotifyEventType::KEYUP) )
+ {
+ // ScGridWindow has WB_DIALOGCONTROL set, so pressing tab in ScCheckListMenuControl won't
+ // get processed here by the toplevel DockingWindow of ScCheckListMenuControl by
+ // just checking if parentNotDialogControl is true
+ bool bTopLevelFloatingWindow = (pWrapper && pWrapper->IsFloatingMode());
+ if (ImplIsOverlapWindow() || parentNotDialogControl(this) || bTopLevelFloatingWindow)
+ {
+ bRet = ImplDlgCtrl( *rNEvt.GetKeyEvent(), rNEvt.GetType() == NotifyEventType::KEYINPUT );
+ }
+ }
+ else if ( (rNEvt.GetType() == NotifyEventType::GETFOCUS) || (rNEvt.GetType() == NotifyEventType::LOSEFOCUS) )
+ {
+ ImplDlgCtrlFocusChanged( rNEvt.GetWindow(), rNEvt.GetType() == NotifyEventType::GETFOCUS );
+ if ( (rNEvt.GetWindow() == this) && (rNEvt.GetType() == NotifyEventType::GETFOCUS) &&
+ !(GetStyle() & WB_TABSTOP) && !(mpWindowImpl->mnDlgCtrlFlags & DialogControlFlags::WantFocus) )
+ {
+ vcl::Window* pFirstChild = ImplGetDlgWindow( 0, GetDlgWindowType::First );
+ if ( pFirstChild )
+ pFirstChild->ImplControlFocus();
+ }
+ }
+ }
+
+ if ( !bRet )
+ {
+ if ( mpWindowImpl->mpParent && !ImplIsOverlapWindow() )
+ bRet = mpWindowImpl->mpParent->CompatNotify( rNEvt );
+ }
+
+ return bRet;
+}
+
+void Window::CallEventListeners( VclEventId nEvent, void* pData )
+{
+ VclWindowEvent aEvent( this, nEvent, pData );
+
+ VclPtr<vcl::Window> xWindow = this;
+
+ Application::ImplCallEventListeners( aEvent );
+
+ // if we have ObjectDying, then the bIsDisposed flag has already been set,
+ // but we still need to let listeners know.
+ const bool bIgnoreDisposed = nEvent == VclEventId::ObjectDying;
+
+ if ( !bIgnoreDisposed && xWindow->isDisposed() )
+ return;
+
+ // If maEventListeners is empty, the XVCLWindow has not yet been initialized.
+ // Calling GetComponentInterface will do that.
+ if (mpWindowImpl->maEventListeners.empty() && pData)
+ xWindow->GetComponentInterface();
+
+ if (!mpWindowImpl->maEventListeners.empty())
+ {
+ // Copy the list, because this can be destroyed when calling a Link...
+ std::vector<Link<VclWindowEvent&,void>> aCopy( mpWindowImpl->maEventListeners );
+ // we use an iterating counter/flag and a set of deleted Link's to avoid O(n^2) behaviour
+ mpWindowImpl->mnEventListenersIteratingCount++;
+ auto& rWindowImpl = *mpWindowImpl;
+ comphelper::ScopeGuard aGuard(
+ [&rWindowImpl, &xWindow, &bIgnoreDisposed]()
+ {
+ if (bIgnoreDisposed || !xWindow->isDisposed())
+ {
+ rWindowImpl.mnEventListenersIteratingCount--;
+ if (rWindowImpl.mnEventListenersIteratingCount == 0)
+ rWindowImpl.maEventListenersDeleted.clear();
+ }
+ }
+ );
+ for ( const Link<VclWindowEvent&,void>& rLink : aCopy )
+ {
+ if (!bIgnoreDisposed && xWindow->isDisposed()) break;
+ // check this hasn't been removed in some re-enterancy scenario fdo#47368
+ if( rWindowImpl.maEventListenersDeleted.find(rLink) == rWindowImpl.maEventListenersDeleted.end() )
+ rLink.Call( aEvent );
+ }
+ }
+
+ while ( xWindow )
+ {
+
+ if ( !bIgnoreDisposed && xWindow->isDisposed() )
+ return;
+
+ auto& rWindowImpl = *xWindow->mpWindowImpl;
+ if (!rWindowImpl.maChildEventListeners.empty())
+ {
+ // Copy the list, because this can be destroyed when calling a Link...
+ std::vector<Link<VclWindowEvent&,void>> aCopy( rWindowImpl.maChildEventListeners );
+ // we use an iterating counter/flag and a set of deleted Link's to avoid O(n^2) behaviour
+ rWindowImpl.mnChildEventListenersIteratingCount++;
+ comphelper::ScopeGuard aGuard(
+ [&rWindowImpl, &xWindow, &bIgnoreDisposed]()
+ {
+ if (bIgnoreDisposed || !xWindow->isDisposed())
+ {
+ rWindowImpl.mnChildEventListenersIteratingCount--;
+ if (rWindowImpl.mnChildEventListenersIteratingCount == 0)
+ rWindowImpl.maChildEventListenersDeleted.clear();
+ }
+ }
+ );
+ for ( const Link<VclWindowEvent&,void>& rLink : aCopy )
+ {
+ if (!bIgnoreDisposed && xWindow->isDisposed())
+ return;
+ // Check this hasn't been removed in some re-enterancy scenario fdo#47368.
+ if( rWindowImpl.maChildEventListenersDeleted.find(rLink) == rWindowImpl.maChildEventListenersDeleted.end() )
+ rLink.Call( aEvent );
+ }
+ }
+
+ if ( !bIgnoreDisposed && xWindow->isDisposed() )
+ return;
+
+ xWindow = xWindow->GetParent();
+ }
+}
+
+void Window::AddEventListener( const Link<VclWindowEvent&,void>& rEventListener )
+{
+ mpWindowImpl->maEventListeners.push_back( rEventListener );
+}
+
+void Window::RemoveEventListener( const Link<VclWindowEvent&,void>& rEventListener )
+{
+ if (mpWindowImpl)
+ {
+ auto& rListeners = mpWindowImpl->maEventListeners;
+ std::erase(rListeners, rEventListener);
+ if (mpWindowImpl->mnEventListenersIteratingCount)
+ mpWindowImpl->maEventListenersDeleted.insert(rEventListener);
+ }
+}
+
+void Window::AddChildEventListener( const Link<VclWindowEvent&,void>& rEventListener )
+{
+ mpWindowImpl->maChildEventListeners.push_back( rEventListener );
+}
+
+void Window::RemoveChildEventListener( const Link<VclWindowEvent&,void>& rEventListener )
+{
+ if (mpWindowImpl)
+ {
+ auto& rListeners = mpWindowImpl->maChildEventListeners;
+ std::erase(rListeners, rEventListener);
+ if (mpWindowImpl->mnChildEventListenersIteratingCount)
+ mpWindowImpl->maChildEventListenersDeleted.insert(rEventListener);
+ }
+}
+
+ImplSVEvent * Window::PostUserEvent( const Link<void*,void>& rLink, void* pCaller, bool bReferenceLink )
+{
+ std::unique_ptr<ImplSVEvent> pSVEvent(new ImplSVEvent);
+ pSVEvent->mpData = pCaller;
+ pSVEvent->maLink = rLink;
+ pSVEvent->mpWindow = this;
+ pSVEvent->mbCall = true;
+ if (bReferenceLink)
+ {
+ pSVEvent->mpInstanceRef = static_cast<vcl::Window *>(rLink.GetInstance());
+ }
+
+ auto pTmpEvent = pSVEvent.get();
+ if (!mpWindowImpl->mpFrame->PostEvent( std::move(pSVEvent) ))
+ return nullptr;
+ return pTmpEvent;
+}
+
+void Window::RemoveUserEvent( ImplSVEvent * nUserEvent )
+{
+ SAL_WARN_IF( nUserEvent->mpWindow.get() != this, "vcl",
+ "Window::RemoveUserEvent(): Event doesn't send to this window or is already removed" );
+ SAL_WARN_IF( !nUserEvent->mbCall, "vcl",
+ "Window::RemoveUserEvent(): Event is already removed" );
+
+ if ( nUserEvent->mpWindow )
+ {
+ nUserEvent->mpWindow = nullptr;
+ }
+
+ nUserEvent->mbCall = false;
+}
+
+
+static MouseEvent ImplTranslateMouseEvent( const MouseEvent& rE, vcl::Window const * pSource, vcl::Window const * pDest )
+{
+ // the mouse event occurred in a different window, we need to translate the coordinates of
+ // the mouse cursor within that (source) window to the coordinates the mouse cursor would
+ // be in the destination window
+ Point aPos = pSource->OutputToScreenPixel( rE.GetPosPixel() );
+ return MouseEvent( pDest->ScreenToOutputPixel( aPos ), rE.GetClicks(), rE.GetMode(), rE.GetButtons(), rE.GetModifier() );
+}
+
+void Window::ImplNotifyKeyMouseCommandEventListeners( NotifyEvent& rNEvt )
+{
+ if( rNEvt.GetType() == NotifyEventType::COMMAND )
+ {
+ const CommandEvent* pCEvt = rNEvt.GetCommandEvent();
+ if ( pCEvt->GetCommand() != CommandEventId::ContextMenu )
+ // non context menu events are not to be notified up the chain
+ // so we return immediately
+ return;
+
+ if ( mpWindowImpl->mbCompoundControl || ( rNEvt.GetWindow() == this ) )
+ {
+ // not interested: The event listeners are already called in ::Command,
+ // and calling them here a second time doesn't make sense
+ if ( rNEvt.GetWindow() != this )
+ {
+ CommandEvent aCommandEvent;
+
+ if ( !pCEvt->IsMouseEvent() )
+ {
+ aCommandEvent = *pCEvt;
+ }
+ else
+ {
+ // the mouse event occurred in a different window, we need to translate the coordinates of
+ // the mouse cursor within that window to the coordinates the mouse cursor would be in the
+ // current window
+ vcl::Window* pSource = rNEvt.GetWindow();
+ Point aPos = pSource->OutputToScreenPixel( pCEvt->GetMousePosPixel() );
+ aCommandEvent = CommandEvent( ScreenToOutputPixel( aPos ), pCEvt->GetCommand(), pCEvt->IsMouseEvent(), pCEvt->GetEventData() );
+ }
+
+ CallEventListeners( VclEventId::WindowCommand, &aCommandEvent );
+ }
+ }
+ }
+
+ // #82968# notify event listeners for mouse and key events separately and
+ // not in PreNotify ( as for focus listeners )
+ // this allows for processing those events internally first and pass it to
+ // the toolkit later
+
+ VclPtr<vcl::Window> xWindow = this;
+
+ if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
+ {
+ if ( mpWindowImpl->mbCompoundControl || ( rNEvt.GetWindow() == this ) )
+ {
+ if ( rNEvt.GetWindow() == this )
+ CallEventListeners( VclEventId::WindowMouseMove, const_cast<MouseEvent *>(rNEvt.GetMouseEvent()) );
+ else
+ {
+ MouseEvent aMouseEvent = ImplTranslateMouseEvent( *rNEvt.GetMouseEvent(), rNEvt.GetWindow(), this );
+ CallEventListeners( VclEventId::WindowMouseMove, &aMouseEvent );
+ }
+ }
+ }
+ else if( rNEvt.GetType() == NotifyEventType::MOUSEBUTTONUP )
+ {
+ if ( mpWindowImpl->mbCompoundControl || ( rNEvt.GetWindow() == this ) )
+ {
+ if ( rNEvt.GetWindow() == this )
+ CallEventListeners( VclEventId::WindowMouseButtonUp, const_cast<MouseEvent *>(rNEvt.GetMouseEvent()) );
+ else
+ {
+ MouseEvent aMouseEvent = ImplTranslateMouseEvent( *rNEvt.GetMouseEvent(), rNEvt.GetWindow(), this );
+ CallEventListeners( VclEventId::WindowMouseButtonUp, &aMouseEvent );
+ }
+ }
+ }
+ else if( rNEvt.GetType() == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ if ( mpWindowImpl->mbCompoundControl || ( rNEvt.GetWindow() == this ) )
+ {
+ if ( rNEvt.GetWindow() == this )
+ CallEventListeners( VclEventId::WindowMouseButtonDown, const_cast<MouseEvent *>(rNEvt.GetMouseEvent()) );
+ else
+ {
+ MouseEvent aMouseEvent = ImplTranslateMouseEvent( *rNEvt.GetMouseEvent(), rNEvt.GetWindow(), this );
+ CallEventListeners( VclEventId::WindowMouseButtonDown, &aMouseEvent );
+ }
+ }
+ }
+ else if( rNEvt.GetType() == NotifyEventType::KEYINPUT )
+ {
+ if ( mpWindowImpl->mbCompoundControl || ( rNEvt.GetWindow() == this ) )
+ CallEventListeners( VclEventId::WindowKeyInput, const_cast<KeyEvent *>(rNEvt.GetKeyEvent()) );
+ }
+ else if( rNEvt.GetType() == NotifyEventType::KEYUP )
+ {
+ if ( mpWindowImpl->mbCompoundControl || ( rNEvt.GetWindow() == this ) )
+ CallEventListeners( VclEventId::WindowKeyUp, const_cast<KeyEvent *>(rNEvt.GetKeyEvent()) );
+ }
+
+ if ( xWindow->isDisposed() )
+ return;
+
+ // #106721# check if we're part of a compound control and notify
+ vcl::Window *pParent = ImplGetParent();
+ while( pParent )
+ {
+ if( pParent->IsCompoundControl() )
+ {
+ pParent->ImplNotifyKeyMouseCommandEventListeners( rNEvt );
+ break;
+ }
+ pParent = pParent->ImplGetParent();
+ }
+}
+
+void Window::ImplCallInitShow()
+{
+ mpWindowImpl->mbReallyShown = true;
+ mpWindowImpl->mbInInitShow = true;
+ CompatStateChanged( StateChangedType::InitShow );
+ mpWindowImpl->mbInInitShow = false;
+
+ vcl::Window* pWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbVisible )
+ pWindow->ImplCallInitShow();
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+
+ pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbVisible )
+ pWindow->ImplCallInitShow();
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+
+void Window::ImplCallResize()
+{
+ mpWindowImpl->mbCallResize = false;
+
+ // Normally we avoid blanking on re-size unless people might notice:
+ if( GetBackground().IsGradient() )
+ Invalidate();
+
+ Resize();
+
+ // #88419# Most classes don't call the base class in Resize() and Move(),
+ // => Call ImpleResize/Move instead of Resize/Move directly...
+ CallEventListeners( VclEventId::WindowResize );
+}
+
+void Window::ImplCallMove()
+{
+ mpWindowImpl->mbCallMove = false;
+
+ if( mpWindowImpl->mbFrame )
+ {
+ // update frame position
+ SalFrame *pParentFrame = nullptr;
+ vcl::Window *pParent = ImplGetParent();
+ while( pParent )
+ {
+ if( pParent->mpWindowImpl &&
+ pParent->mpWindowImpl->mpFrame != mpWindowImpl->mpFrame )
+ {
+ pParentFrame = pParent->mpWindowImpl->mpFrame;
+ break;
+ }
+ pParent = pParent->GetParent();
+ }
+
+ SalFrameGeometry g = mpWindowImpl->mpFrame->GetGeometry();
+ mpWindowImpl->maPos = Point(g.x(), g.y());
+ if( pParentFrame )
+ {
+ g = pParentFrame->GetGeometry();
+ mpWindowImpl->maPos -= Point(g.x(), g.y());
+ }
+ // the client window and all its subclients have the same position as the borderframe
+ // this is important for floating toolbars where the borderwindow is a floating window
+ // which has another borderwindow (ie the system floating window)
+ vcl::Window *pClientWin = mpWindowImpl->mpClientWindow;
+ while( pClientWin )
+ {
+ pClientWin->mpWindowImpl->maPos = mpWindowImpl->maPos;
+ pClientWin = pClientWin->mpWindowImpl->mpClientWindow;
+ }
+ }
+
+ Move();
+
+ CallEventListeners( VclEventId::WindowMove );
+}
+
+void Window::ImplCallFocusChangeActivate( vcl::Window* pNewOverlapWindow,
+ vcl::Window* pOldOverlapWindow )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::Window* pNewRealWindow;
+ vcl::Window* pOldRealWindow;
+ bool bCallActivate = true;
+ bool bCallDeactivate = true;
+
+ if (!pOldOverlapWindow)
+ {
+ return;
+ }
+
+ pOldRealWindow = pOldOverlapWindow->ImplGetWindow();
+ if (!pNewOverlapWindow)
+ {
+ return;
+ }
+
+ pNewRealWindow = pNewOverlapWindow->ImplGetWindow();
+ if ( (pOldRealWindow->GetType() != WindowType::FLOATINGWINDOW) ||
+ pOldRealWindow->GetActivateMode() != ActivateModeFlags::NONE )
+ {
+ if ( (pNewRealWindow->GetType() == WindowType::FLOATINGWINDOW) &&
+ pNewRealWindow->GetActivateMode() == ActivateModeFlags::NONE)
+ {
+ pSVData->mpWinData->mpLastDeacWin = pOldOverlapWindow;
+ bCallDeactivate = false;
+ }
+ }
+ else if ( (pNewRealWindow->GetType() != WindowType::FLOATINGWINDOW) ||
+ pNewRealWindow->GetActivateMode() != ActivateModeFlags::NONE )
+ {
+ if (pSVData->mpWinData->mpLastDeacWin)
+ {
+ if (pSVData->mpWinData->mpLastDeacWin.get() == pNewOverlapWindow)
+ bCallActivate = false;
+ else
+ {
+ vcl::Window* pLastRealWindow = pSVData->mpWinData->mpLastDeacWin->ImplGetWindow();
+ pSVData->mpWinData->mpLastDeacWin->mpWindowImpl->mbActive = false;
+ pSVData->mpWinData->mpLastDeacWin->Deactivate();
+ if (pLastRealWindow != pSVData->mpWinData->mpLastDeacWin.get())
+ {
+ pLastRealWindow->mpWindowImpl->mbActive = true;
+ pLastRealWindow->Activate();
+ }
+ }
+ pSVData->mpWinData->mpLastDeacWin = nullptr;
+ }
+ }
+
+ if ( bCallDeactivate )
+ {
+ if( pOldOverlapWindow->mpWindowImpl->mbActive )
+ {
+ pOldOverlapWindow->mpWindowImpl->mbActive = false;
+ pOldOverlapWindow->Deactivate();
+ }
+ if ( pOldRealWindow != pOldOverlapWindow )
+ {
+ if( pOldRealWindow->mpWindowImpl->mbActive )
+ {
+ pOldRealWindow->mpWindowImpl->mbActive = false;
+ pOldRealWindow->Deactivate();
+ }
+ }
+ }
+ if ( !bCallActivate || pNewOverlapWindow->mpWindowImpl->mbActive )
+ return;
+
+ pNewOverlapWindow->mpWindowImpl->mbActive = true;
+ pNewOverlapWindow->Activate();
+
+ if ( pNewRealWindow != pNewOverlapWindow )
+ {
+ if( ! pNewRealWindow->mpWindowImpl->mbActive )
+ {
+ pNewRealWindow->mpWindowImpl->mbActive = true;
+ pNewRealWindow->Activate();
+ }
+ }
+}
+
+} /* namespace vcl */
+
+
+NotifyEvent::NotifyEvent( NotifyEventType nEventType, vcl::Window* pWindow,
+ const void* pEvent )
+{
+ mpWindow = pWindow;
+ mpData = const_cast<void*>(pEvent);
+ mnEventType = nEventType;
+}
+
+NotifyEvent::~NotifyEvent() = default;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/floatwin.cxx b/vcl/source/window/floatwin.cxx
new file mode 100644
index 0000000000..b2faacadb5
--- /dev/null
+++ b/vcl/source/window/floatwin.cxx
@@ -0,0 +1,974 @@
+/* -*- 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 <svdata.hxx>
+#include <brdwin.hxx>
+#include <window.h>
+#include <salframe.hxx>
+#include <helpwin.hxx>
+
+#include <comphelper/lok.hxx>
+#include <sal/log.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/wrkwin.hxx>
+#include <vcl/event.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/IDialogRenderable.hxx>
+
+class FloatingWindow::ImplData
+{
+public:
+ ImplData();
+
+ VclPtr<ToolBox> mpBox;
+ AbsoluteScreenPixelRectangle maItemEdgeClipRect; // used to clip the common edge between a toolbar item and the border of this window
+ Point maPos; // position of the floating window wrt. parent
+ Point maLOKTwipsPos; ///< absolute position of the floating window in the document - in twips (for toplevel floating windows).
+};
+
+FloatingWindow::ImplData::ImplData()
+{
+ mpBox = nullptr;
+}
+
+AbsoluteScreenPixelRectangle FloatingWindow::ImplGetItemEdgeClipRect()
+{
+ return mpImplData->maItemEdgeClipRect;
+}
+
+void FloatingWindow::ImplInitFloating( vcl::Window* pParent, WinBits nStyle )
+{
+ mpImplData.reset(new ImplData);
+
+ mpWindowImpl->mbFloatWin = true;
+ mbInCleanUp = false;
+ mbGrabFocus = false;
+
+ SAL_WARN_IF(!pParent, "vcl", "FloatWindow::FloatingWindow(): - pParent == NULL!");
+
+ if (!pParent)
+ pParent = ImplGetSVData()->maFrameData.mpAppWin;
+
+ SAL_WARN_IF(!pParent, "vcl", "FloatWindow::FloatingWindow(): - pParent == NULL and no AppWindow exists");
+
+ // no Border, then we don't need a border window
+ if (!nStyle)
+ {
+ mpWindowImpl->mbOverlapWin = true;
+ nStyle |= WB_DIALOGCONTROL;
+ ImplInit(pParent, nStyle, nullptr);
+ }
+ else
+ {
+ if (!(nStyle & WB_NODIALOGCONTROL))
+ nStyle |= WB_DIALOGCONTROL;
+
+ if (nStyle & (WB_MOVEABLE | WB_SIZEABLE | WB_CLOSEABLE | WB_STANDALONE)
+ && !(nStyle & WB_OWNERDRAWDECORATION))
+ {
+ WinBits nFloatWinStyle = nStyle;
+ // #99154# floaters are not closeable by default anymore, eg fullscreen floater
+ // nFloatWinStyle |= WB_CLOSEABLE;
+ mpWindowImpl->mbFrame = true;
+ mpWindowImpl->mbOverlapWin = true;
+ ImplInit(pParent, nFloatWinStyle & ~WB_BORDER, nullptr);
+ }
+ else
+ {
+ VclPtr<ImplBorderWindow> pBorderWin;
+ BorderWindowStyle nBorderStyle = BorderWindowStyle::Float;
+
+ if (nStyle & WB_OWNERDRAWDECORATION)
+ nBorderStyle |= BorderWindowStyle::Frame;
+ else
+ nBorderStyle |= BorderWindowStyle::Overlap;
+
+ if ((nStyle & WB_SYSTEMWINDOW) && !(nStyle & (WB_MOVEABLE | WB_SIZEABLE)))
+ {
+ nBorderStyle |= BorderWindowStyle::Frame;
+ nStyle |= WB_CLOSEABLE; // make undecorated floaters closeable
+ }
+ pBorderWin = VclPtr<ImplBorderWindow>::Create(pParent, nStyle, nBorderStyle);
+ ImplInit(pBorderWin, nStyle & ~WB_BORDER, nullptr);
+ pBorderWin->mpWindowImpl->mpClientWindow = this;
+ pBorderWin->GetBorder(mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder,
+ mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder);
+ pBorderWin->SetDisplayActive(true);
+ mpWindowImpl->mpBorderWindow = pBorderWin;
+ mpWindowImpl->mpRealParent = pParent;
+ }
+ }
+ SetActivateMode( ActivateModeFlags::NONE );
+
+ mpNextFloat = nullptr;
+ mpFirstPopupModeWin = nullptr;
+ mnPostId = nullptr;
+ mnTitle = (nStyle & (WB_MOVEABLE | WB_POPUP)) ? FloatWinTitleType::Normal : FloatWinTitleType::NONE;
+ mnOldTitle = mnTitle;
+ mnPopupModeFlags = FloatWinPopupFlags::NONE;
+ mbInPopupMode = false;
+ mbPopupMode = false;
+ mbPopupModeCanceled = false;
+ mbPopupModeTearOff = false;
+ mbMouseDown = false;
+
+ ImplInitSettings();
+}
+
+void FloatingWindow::ImplInitSettings()
+{
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+
+ Color aColor;
+ if (IsControlBackground())
+ aColor = GetControlBackground();
+ else if (Window::GetStyle() & WB_3DLOOK)
+ aColor = rStyleSettings.GetFaceColor();
+ else
+ aColor = rStyleSettings.GetWindowColor();
+ SetBackground(aColor);
+}
+
+FloatingWindow::FloatingWindow(vcl::Window* pParent, WinBits nStyle) :
+ SystemWindow(WindowType::FLOATINGWINDOW, "vcl::FloatingWindow maLayoutIdle")
+{
+ ImplInitFloating(pParent, nStyle);
+}
+
+FloatingWindow::FloatingWindow(vcl::Window* pParent, const OUString& rID, const OUString& rUIXMLDescription, const css::uno::Reference<css::frame::XFrame> &rFrame)
+ : SystemWindow(WindowType::FLOATINGWINDOW, "vcl::FloatingWindow maLayoutIdle")
+ , mpNextFloat(nullptr)
+ , mpFirstPopupModeWin(nullptr)
+ , mnPostId(nullptr)
+ , mnPopupModeFlags(FloatWinPopupFlags::NONE)
+ , mnTitle(FloatWinTitleType::Unknown)
+ , mnOldTitle(FloatWinTitleType::Unknown)
+ , mbInPopupMode(false)
+ , mbPopupMode(false)
+ , mbPopupModeCanceled(false)
+ , mbPopupModeTearOff(false)
+ , mbMouseDown(false)
+ , mbGrabFocus(false)
+ , mbInCleanUp(false)
+{
+ loadUI(pParent, rID, rUIXMLDescription, rFrame);
+}
+
+//Find the real parent stashed in mpDialogParent.
+void FloatingWindow::doDeferredInit(WinBits nBits)
+{
+ vcl::Window *pParent = mpDialogParent;
+ mpDialogParent = nullptr;
+ ImplInitFloating(pParent, nBits);
+ mbIsDeferredInit = false;
+}
+
+void FloatingWindow::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ Color aColor;
+ if (Window::GetStyle() & WB_3DLOOK)
+ aColor = rStyleSettings.GetFaceColor();
+ else
+ aColor = rStyleSettings.GetWindowColor();
+
+ ApplyControlBackground(rRenderContext, aColor);
+}
+
+FloatingWindow::~FloatingWindow()
+{
+ disposeOnce();
+ assert (!mnPostId);
+}
+
+void FloatingWindow::dispose()
+{
+ ReleaseLOKNotifier();
+
+ if (mpImplData)
+ {
+ if( mbPopupModeCanceled )
+ // indicates that ESC key was pressed
+ // will be handled in Window::ImplGrabFocus()
+ SetDialogControlFlags( GetDialogControlFlags() | DialogControlFlags::FloatWinPopupModeEndCancel );
+
+ if ( IsInPopupMode() )
+ EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll | FloatWinPopupEndFlags::DontCallHdl );
+
+ if ( mnPostId )
+ Application::RemoveUserEvent( mnPostId );
+ mnPostId = nullptr;
+ }
+
+ mpImplData.reset();
+
+ mpNextFloat.clear();
+ mpFirstPopupModeWin.clear();
+ mxPrevFocusWin.clear();
+ SystemWindow::dispose();
+}
+
+Point FloatingWindow::ImplCalcPos(vcl::Window* pWindow,
+ const tools::Rectangle& rRect, FloatWinPopupFlags nFlags,
+ sal_uInt16& rArrangeIndex, Point* pLOKTwipsPos)
+{
+ // get window position
+ AbsoluteScreenPixelPoint aPos;
+ Size aSize = ::isLayoutEnabled(pWindow) ? pWindow->get_preferred_size() : pWindow->GetSizePixel();
+ AbsoluteScreenPixelRectangle aScreenRect = pWindow->ImplGetFrameWindow()->GetDesktopRectPixel();
+ FloatingWindow *pFloatingWindow = dynamic_cast<FloatingWindow*>( pWindow );
+
+ // convert...
+ vcl::Window* pW = pWindow;
+ if ( pW->mpWindowImpl->mpRealParent )
+ pW = pW->mpWindowImpl->mpRealParent;
+
+ tools::Rectangle normRect( rRect ); // rRect is already relative to top-level window
+ normRect.SetPos( pW->ScreenToOutputPixel( normRect.TopLeft() ) );
+
+ bool bRTL = AllSettings::GetLayoutRTL();
+
+ AbsoluteScreenPixelRectangle devRect( pW->OutputToAbsoluteScreenPixel( normRect.TopLeft() ),
+ pW->OutputToAbsoluteScreenPixel( normRect.BottomRight() ) );
+
+ AbsoluteScreenPixelRectangle devRectRTL( devRect );
+ if( bRTL )
+ // create a rect that can be compared to desktop coordinates
+ devRectRTL = pW->ImplOutputToUnmirroredAbsoluteScreenPixel( normRect );
+ if( Application::GetScreenCount() > 1 )
+ aScreenRect = Application::GetScreenPosSizePixel(
+ Application::GetBestScreen( bRTL ? devRectRTL : devRect ) );
+
+ FloatWinPopupFlags nArrangeAry[5];
+ sal_uInt16 nArrangeAttempts = 5;
+ AbsoluteScreenPixelPoint e1,e2; // the common edge between the item rect and the floating window
+
+ if ( nFlags & FloatWinPopupFlags::Left )
+ {
+ nArrangeAry[0] = FloatWinPopupFlags::Left;
+ nArrangeAry[1] = FloatWinPopupFlags::Right;
+ nArrangeAry[2] = FloatWinPopupFlags::Up;
+ nArrangeAry[3] = FloatWinPopupFlags::Down;
+ nArrangeAry[4] = FloatWinPopupFlags::Left;
+ }
+ else if ( nFlags & FloatWinPopupFlags::Right )
+ {
+ nArrangeAry[0] = FloatWinPopupFlags::Right;
+ nArrangeAry[1] = FloatWinPopupFlags::Left;
+ nArrangeAry[2] = FloatWinPopupFlags::Up;
+ nArrangeAry[3] = FloatWinPopupFlags::Down;
+ nArrangeAry[4] = FloatWinPopupFlags::Right;
+ }
+ else if ( nFlags & FloatWinPopupFlags::Up )
+ {
+ nArrangeAry[0] = FloatWinPopupFlags::Up;
+ nArrangeAry[1] = FloatWinPopupFlags::Down;
+ if (nFlags & FloatWinPopupFlags::NoHorzPlacement)
+ {
+ nArrangeAry[2] = FloatWinPopupFlags::Up;
+ nArrangeAttempts = 3;
+ }
+ else
+ {
+ nArrangeAry[2] = FloatWinPopupFlags::Right;
+ nArrangeAry[3] = FloatWinPopupFlags::Left;
+ nArrangeAry[4] = FloatWinPopupFlags::Up;
+ }
+ }
+ else
+ {
+ nArrangeAry[0] = FloatWinPopupFlags::Down;
+ nArrangeAry[1] = FloatWinPopupFlags::Up;
+ if (nFlags & FloatWinPopupFlags::NoHorzPlacement)
+ {
+ nArrangeAry[2] = FloatWinPopupFlags::Down;
+ nArrangeAttempts = 3;
+ }
+ else
+ {
+ nArrangeAry[2] = FloatWinPopupFlags::Right;
+ nArrangeAry[3] = FloatWinPopupFlags::Left;
+ nArrangeAry[4] = FloatWinPopupFlags::Down;
+ }
+ }
+
+ sal_uInt16 nArrangeIndex = 0;
+ const bool bLOKActive = comphelper::LibreOfficeKit::isActive();
+
+ for ( ; nArrangeIndex < nArrangeAttempts; nArrangeIndex++ )
+ {
+ bool bBreak = true;
+ switch ( nArrangeAry[nArrangeIndex] )
+ {
+
+ case FloatWinPopupFlags::Left:
+ aPos.setX( devRect.Left()-aSize.Width()+1 );
+ aPos.setY( devRect.Top() );
+ aPos.AdjustY( -(pWindow->mpWindowImpl->mnTopBorder) );
+ if( bRTL )
+ {
+ if( (devRectRTL.Right()+aSize.Width()) > aScreenRect.Right() )
+ bBreak = false;
+ }
+ else
+ {
+ if ( aPos.X() < aScreenRect.Left() )
+ bBreak = false;
+ }
+ if (bBreak || bLOKActive)
+ {
+ e1 = devRect.TopLeft();
+ e2 = devRect.BottomLeft();
+ // set non-zero width
+ e2.AdjustX( 1 );
+ // don't clip corners
+ e1.AdjustY( 1 );
+ e2.AdjustY( -1 );
+ }
+ break;
+ case FloatWinPopupFlags::Right:
+ aPos = devRect.TopRight();
+ aPos.AdjustY( -(pWindow->mpWindowImpl->mnTopBorder) );
+ if( bRTL )
+ {
+ if( (devRectRTL.Left() - aSize.Width()) < aScreenRect.Left() )
+ bBreak = false;
+ }
+ else
+ {
+ if ( aPos.X()+aSize.Width() > aScreenRect.Right() )
+ bBreak = false;
+ }
+ if (bBreak || bLOKActive)
+ {
+ e1 = devRect.TopRight();
+ e2 = devRect.BottomRight();
+ // set non-zero width
+ e2.AdjustX( 1 );
+ // don't clip corners
+ e1.AdjustY( 1 );
+ e2.AdjustY( -1 );
+ }
+ break;
+ case FloatWinPopupFlags::Up:
+ aPos.setX( devRect.Left() );
+ aPos.setY( devRect.Top()-aSize.Height()+1 );
+ if ( aPos.Y() < aScreenRect.Top() )
+ bBreak = false;
+ if (bBreak || bLOKActive)
+ {
+ e1 = devRect.TopLeft();
+ e2 = devRect.TopRight();
+ // set non-zero height
+ e2.AdjustY( 1 );
+ // don't clip corners
+ e1.AdjustX( 1 );
+ e2.AdjustX( -1 );
+ }
+ break;
+ case FloatWinPopupFlags::Down:
+ aPos = devRect.BottomLeft();
+ if ( aPos.Y()+aSize.Height() > aScreenRect.Bottom() )
+ bBreak = false;
+ if (bBreak || bLOKActive)
+ {
+ e1 = devRect.BottomLeft();
+ e2 = devRect.BottomRight();
+ // set non-zero height
+ e2.AdjustY( 1 );
+ // don't clip corners
+ e1.AdjustX( 1 );
+ e2.AdjustX( -1 );
+ }
+ break;
+ default: break;
+ }
+
+ // no further adjustment for LibreOfficeKit
+ if (bLOKActive)
+ break;
+
+ // adjust if necessary
+ if (bBreak)
+ {
+ if ( (nArrangeAry[nArrangeIndex] == FloatWinPopupFlags::Left) ||
+ (nArrangeAry[nArrangeIndex] == FloatWinPopupFlags::Right) )
+ {
+ if ( aPos.Y()+aSize.Height() > aScreenRect.Bottom() )
+ {
+ aPos.setY( devRect.Bottom()-aSize.Height()+1 );
+ if ( aPos.Y() < aScreenRect.Top() )
+ aPos.setY( aScreenRect.Top() );
+ }
+ }
+ else
+ {
+ if( bRTL )
+ {
+ if( devRectRTL.Right()-aSize.Width()+1 < aScreenRect.Left() )
+ aPos.AdjustX( -(aScreenRect.Left() - devRectRTL.Right() + aSize.Width() - 1) );
+ }
+ else if ( aPos.X()+aSize.Width() > aScreenRect.Right() )
+ {
+ aPos.setX( devRect.Right()-aSize.Width()+1 );
+ if ( aPos.X() < aScreenRect.Left() )
+ aPos.setX( aScreenRect.Left() );
+ }
+ }
+ }
+
+ if ( bBreak )
+ break;
+ }
+ if (nArrangeIndex >= nArrangeAttempts)
+ nArrangeIndex = nArrangeAttempts - 1;
+
+ rArrangeIndex = nArrangeIndex;
+
+ Point aPosOut = pW->AbsoluteScreenToOutputPixel( aPos );
+
+ // store a cliprect that can be used to clip the common edge of the itemrect and the floating window
+ if( pFloatingWindow && pFloatingWindow->mpImplData->mpBox )
+ {
+ pFloatingWindow->mpImplData->maItemEdgeClipRect =
+ AbsoluteScreenPixelRectangle( e1, e2 );
+ }
+
+ if (bLOKActive && pLOKTwipsPos)
+ {
+ if (pW->IsMapModeEnabled() || pW->GetMapMode().GetMapUnit() == MapUnit::MapPixel)
+ {
+ // if we use pW->LogicToLogic(aPos, pW->GetMapMode(), MapMode(MapUnit::MapTwip)),
+ // for pixel conversions when map mode is not enabled, we get
+ // a 20 twips per pixel conversion since LogicToLogic uses
+ // a fixed 72 dpi value, instead of a correctly computed output
+ // device dpi or at least the most commonly used 96 dpi value;
+ // and anyway the following is what we already do in
+ // ScGridWindow::LogicInvalidate when map mode is not enabled.
+
+ *pLOKTwipsPos = pW->PixelToLogic(aPosOut, MapMode(MapUnit::MapTwip));
+ }
+ else
+ {
+ *pLOKTwipsPos = OutputDevice::LogicToLogic(aPosOut, pW->GetMapMode(), MapMode(MapUnit::MapTwip));
+ }
+ }
+
+ // caller expects coordinates relative to top-level win
+ return pW->OutputToScreenPixel( aPosOut );
+}
+
+AbsoluteScreenPixelPoint FloatingWindow::ImplConvertToAbsPos(vcl::Window* pReference, const Point& rPos)
+{
+ const OutputDevice *pWindowOutDev = pReference->GetOutDev();
+
+ // compare coordinates in absolute screen coordinates
+ if ( pWindowOutDev->HasMirroredGraphics() && !comphelper::LibreOfficeKit::isActive() )
+ {
+ Point aTmp(rPos);
+ if(!pReference->IsRTLEnabled() )
+ pWindowOutDev->ReMirror( aTmp );
+
+ tools::Rectangle aRect( pReference->ScreenToOutputPixel(aTmp), Size(1,1) ) ;
+ aRect = tools::Rectangle(pReference->ImplOutputToUnmirroredAbsoluteScreenPixel( aRect ));
+ return AbsoluteScreenPixelPoint(aRect.TopLeft());
+ }
+ else
+ return pReference->OutputToAbsoluteScreenPixel(
+ pReference->ScreenToOutputPixel(rPos) );
+}
+
+AbsoluteScreenPixelRectangle FloatingWindow::ImplConvertToAbsPos(vcl::Window* pReference, const tools::Rectangle& rRect)
+{
+ AbsoluteScreenPixelRectangle aFloatRect;
+
+ const OutputDevice *pParentWinOutDev = pReference->GetOutDev();
+
+ // compare coordinates in absolute screen coordinates
+ // Keep in sync with FloatingWindow::ImplFloatHitTest, e.g. fdo#33509
+ if( pParentWinOutDev->HasMirroredGraphics() && !comphelper::LibreOfficeKit::isActive() )
+ {
+ tools::Rectangle aScreenRect(rRect);
+ if(!pReference->IsRTLEnabled() )
+ pParentWinOutDev->ReMirror(aScreenRect);
+
+ tools::Rectangle aOutRect(pReference->ScreenToOutputPixel(aScreenRect.TopLeft()), aScreenRect.GetSize());
+ aFloatRect = pReference->ImplOutputToUnmirroredAbsoluteScreenPixel(aOutRect);
+ }
+ else
+ aFloatRect = AbsoluteScreenPixelRectangle(
+ pReference->OutputToAbsoluteScreenPixel(pReference->ScreenToOutputPixel(rRect.TopLeft())),
+ rRect.GetSize());
+
+ return aFloatRect;
+}
+
+tools::Rectangle FloatingWindow::ImplConvertToRelPos(vcl::Window* pReference, const AbsoluteScreenPixelRectangle& rRect)
+{
+ tools::Rectangle aFloatRect;
+
+ const OutputDevice *pParentWinOutDev = pReference->GetOutDev();
+
+ // compare coordinates in absolute screen coordinates
+ // Keep in sync with FloatingWindow::ImplFloatHitTest, e.g. fdo#33509
+ if( pParentWinOutDev->HasMirroredGraphics() )
+ {
+ aFloatRect = pReference->ImplUnmirroredAbsoluteScreenToOutputPixel(rRect);
+ aFloatRect.SetPos(pReference->OutputToScreenPixel(aFloatRect.TopLeft()));
+
+ if(!pReference->IsRTLEnabled() )
+ pParentWinOutDev->ReMirror(aFloatRect);
+ }
+ else
+ aFloatRect = tools::Rectangle(pReference->OutputToScreenPixel(pReference->AbsoluteScreenToOutputPixel(rRect.TopLeft())),
+ rRect.GetSize());
+
+ return aFloatRect;
+}
+
+FloatingWindow* FloatingWindow::ImplFloatHitTest( vcl::Window* pReference, const Point& rPos, bool& rbHitTestInsideRect )
+{
+ FloatingWindow* pWin = this;
+ rbHitTestInsideRect = false;
+
+ AbsoluteScreenPixelPoint aAbsolute(FloatingWindow::ImplConvertToAbsPos(pReference, rPos));
+
+ do
+ {
+ // compute the floating window's size in absolute screen coordinates
+
+ // use the border window to have the exact position
+ vcl::Window *pBorderWin = pWin->GetWindow( GetWindowType::Border );
+ if (!pBorderWin)
+ break;
+
+ // the top-left corner in output coordinates ie (0,0)
+ AbsoluteScreenPixelRectangle devRect( pBorderWin->ImplOutputToUnmirroredAbsoluteScreenPixel( tools::Rectangle( Point(), pBorderWin->GetSizePixel()) ) ) ;
+ if ( devRect.Contains( aAbsolute ) )
+ {
+ // inside the window
+ return pWin;
+ }
+
+ // test, if mouse is in rectangle, (this is typically the rect of the active
+ // toolbox item or similar)
+ // note: maFloatRect is set in FloatingWindow::StartPopupMode() and
+ // is already in absolute device coordinates
+ if ( pWin->maFloatRect.Contains( aAbsolute ) )
+ {
+ rbHitTestInsideRect = true;
+ return pWin;
+ }
+
+ pWin = pWin->mpNextFloat;
+ }
+ while ( pWin );
+
+ return nullptr;
+}
+
+FloatingWindow* FloatingWindow::ImplFindLastLevelFloat()
+{
+ FloatingWindow* pWin = this;
+ FloatingWindow* pLastFoundWin = pWin;
+
+ do
+ {
+ if ( pWin->GetPopupModeFlags() & FloatWinPopupFlags::NewLevel )
+ pLastFoundWin = pWin;
+
+ pWin = pWin->mpNextFloat;
+ }
+ while ( pWin );
+
+ return pLastFoundWin;
+}
+
+bool FloatingWindow::ImplIsFloatPopupModeWindow( const vcl::Window* pWindow )
+{
+ FloatingWindow* pWin = this;
+
+ do
+ {
+ if ( pWin->mpFirstPopupModeWin == pWindow )
+ return true;
+
+ pWin = pWin->mpNextFloat;
+ }
+ while ( pWin );
+
+ return false;
+}
+
+IMPL_LINK_NOARG(FloatingWindow, ImplEndPopupModeHdl, void*, void)
+{
+ VclPtr<FloatingWindow> pThis(this);
+ mnPostId = nullptr;
+ mnPopupModeFlags = FloatWinPopupFlags::NONE;
+ mbPopupMode = false;
+ PopupModeEnd();
+}
+
+bool FloatingWindow::EventNotify( NotifyEvent& rNEvt )
+{
+ // call Base Class first for tab control
+ bool bRet = SystemWindow::EventNotify( rNEvt );
+ if ( !bRet )
+ {
+ if ( rNEvt.GetType() == NotifyEventType::KEYINPUT )
+ {
+ const KeyEvent* pKEvt = rNEvt.GetKeyEvent();
+ vcl::KeyCode aKeyCode = pKEvt->GetKeyCode();
+ sal_uInt16 nKeyCode = aKeyCode.GetCode();
+
+ if ( (nKeyCode == KEY_ESCAPE) && (GetStyle() & WB_CLOSEABLE) )
+ {
+ Close();
+ return true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void FloatingWindow::PixelInvalidate(const tools::Rectangle* /*pRectangle*/)
+{
+ if (VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier())
+ {
+ const tools::Rectangle aRect(Point(0,0), Size(GetSizePixel().Width()+1, GetSizePixel().Height()+1));
+ std::vector<vcl::LOKPayloadItem> aPayload
+ {
+ std::make_pair("rectangle"_ostr, aRect.toString())
+ };
+ const vcl::ILibreOfficeKitNotifier* pNotifier = pParent->GetLOKNotifier();
+ pNotifier->notifyWindow(GetLOKWindowId(), "invalidate", aPayload);
+ }
+}
+
+void FloatingWindow::StateChanged( StateChangedType nType )
+{
+ if (nType == StateChangedType::InitShow)
+ {
+ DoInitialLayout();
+ }
+
+ SystemWindow::StateChanged( nType );
+
+ VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier();
+ if (pParent)
+ {
+ if (nType == StateChangedType::InitShow)
+ {
+ std::vector<vcl::LOKPayloadItem> aItems;
+ if (pParent == this)
+ {
+ // we are a toplevel window, let's so far pretend to be a
+ // dialog - but maybe we'll need a separate type for this
+ // later
+ if (mbInPopupMode)
+ aItems.emplace_back("type", "dropdown");
+ else
+ aItems.emplace_back("type", "dialog");
+ aItems.emplace_back("position", mpImplData->maLOKTwipsPos.toString()); // twips
+ }
+ else
+ {
+ SetLOKNotifier(pParent->GetLOKNotifier());
+ if (dynamic_cast<HelpTextWindow*>(this))
+ aItems.emplace_back("type", "tooltip");
+ else
+ aItems.emplace_back("type", "child");
+
+ aItems.emplace_back("parentId", OString::number(pParent->GetLOKWindowId()));
+ if (mbInPopupMode)
+ aItems.emplace_back("position", mpImplData->maPos.toString()); // pixels
+ else // mpImplData->maPos is not set
+ aItems.emplace_back("position", GetPosPixel().toString());
+
+ }
+ aItems.emplace_back("size", GetSizePixel().toString());
+ GetLOKNotifier()->notifyWindow(GetLOKWindowId(), "created", aItems);
+ }
+ else if (!IsVisible() && nType == StateChangedType::Visible)
+ {
+ if (const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
+ {
+ pNotifier->notifyWindow(GetLOKWindowId(), "close");
+ ReleaseLOKNotifier();
+ }
+ }
+ }
+
+ if ( nType == StateChangedType::ControlBackground )
+ {
+ ImplInitSettings();
+ Invalidate();
+ }
+}
+
+void FloatingWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ SystemWindow::DataChanged( rDCEvt );
+
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ ImplInitSettings();
+ Invalidate();
+ }
+}
+
+void FloatingWindow::ImplCallPopupModeEnd()
+{
+ // PopupMode is finished
+ mbInPopupMode = false;
+
+ // call Handler asynchronously.
+ if ( mpImplData && !mnPostId )
+ mnPostId = Application::PostUserEvent(LINK(this, FloatingWindow, ImplEndPopupModeHdl));
+}
+
+void FloatingWindow::PopupModeEnd()
+{
+ maPopupModeEndHdl.Call( this );
+}
+
+void FloatingWindow::SetTitleType( FloatWinTitleType nTitle )
+{
+ if ( (mnTitle == nTitle) || !mpWindowImpl->mpBorderWindow )
+ return;
+
+ mnTitle = nTitle;
+ Size aOutSize = GetOutputSizePixel();
+ BorderWindowTitleType nTitleStyle;
+ if ( nTitle == FloatWinTitleType::Normal )
+ nTitleStyle = BorderWindowTitleType::Small;
+ else if ( nTitle == FloatWinTitleType::TearOff )
+ nTitleStyle = BorderWindowTitleType::Tearoff;
+ else if ( nTitle == FloatWinTitleType::Popup )
+ nTitleStyle = BorderWindowTitleType::Popup;
+ else // nTitle == FloatWinTitleType::NONE
+ nTitleStyle = BorderWindowTitleType::NONE;
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetTitleType( nTitleStyle, aOutSize );
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->GetBorder( mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder );
+}
+
+void FloatingWindow::StartPopupMode( const tools::Rectangle& rRect, FloatWinPopupFlags nFlags )
+{
+ // remove title
+ mnOldTitle = mnTitle;
+ if ( ( mpWindowImpl->mnStyle & WB_POPUP ) && !GetText().isEmpty() )
+ SetTitleType( FloatWinTitleType::Popup );
+ else if ( nFlags & FloatWinPopupFlags::AllowTearOff )
+ SetTitleType( FloatWinTitleType::TearOff );
+ else
+ SetTitleType( FloatWinTitleType::NONE );
+
+ // avoid close on focus change for decorated floating windows only
+ if( mpWindowImpl->mbFrame && (GetStyle() & WB_MOVEABLE) )
+ nFlags |= FloatWinPopupFlags::NoAppFocusClose;
+
+ // compute window position according to flags and arrangement
+ sal_uInt16 nArrangeIndex;
+ DoInitialLayout();
+ mpImplData->maPos = ImplCalcPos(this, rRect, nFlags, nArrangeIndex, &mpImplData->maLOKTwipsPos);
+ SetPosPixel( mpImplData->maPos );
+ ImplGetFrame()->PositionByToolkit(rRect, nFlags);
+
+ // set data and display window
+ // convert maFloatRect to absolute device coordinates
+ // so they can be compared across different frames
+ // !!! rRect is expected to be in screen coordinates of the parent frame window !!!
+ maFloatRect = FloatingWindow::ImplConvertToAbsPos(GetParent(), rRect);
+
+ maFloatRect.AdjustLeft( -2 );
+ maFloatRect.AdjustTop( -2 );
+ maFloatRect.AdjustRight(2 );
+ maFloatRect.AdjustBottom(2 );
+ mnPopupModeFlags = nFlags;
+ mbInPopupMode = true;
+ mbPopupMode = true;
+ mbPopupModeCanceled = false;
+ mbPopupModeTearOff = false;
+ mbMouseDown = false;
+
+ // add FloatingWindow to list of windows that are in popup mode
+ ImplSVData* pSVData = ImplGetSVData();
+ mpNextFloat = pSVData->mpWinData->mpFirstFloat;
+ pSVData->mpWinData->mpFirstFloat = this;
+ bool bGrabFocus(nFlags & FloatWinPopupFlags::GrabFocus);
+ if (bGrabFocus)
+ {
+ // force key input even without focus (useful for menus)
+ mbGrabFocus = true;
+ mxPrevFocusWin = Window::SaveFocus();
+ mpWindowImpl->mpFrameData->mbHasFocus = true;
+ }
+ Show( true, ShowFlags::NoActivate );
+ if (bGrabFocus)
+ GrabFocus();
+}
+
+void FloatingWindow::StartPopupMode( ToolBox* pBox, FloatWinPopupFlags nFlags )
+{
+ mpImplData->mpBox = pBox;
+
+ // get selected button
+ ToolBoxItemId nItemId = pBox->GetDownItemId();
+
+ if ( nItemId )
+ pBox->ImplFloatControl( true, this );
+
+ // retrieve some data from the ToolBox
+ tools::Rectangle aRect = nItemId ? pBox->GetItemRect( nItemId ) : pBox->GetOverflowRect();
+
+ // convert to parent's screen coordinates
+ mpImplData->maPos = GetParent()->OutputToScreenPixel( GetParent()->AbsoluteScreenToOutputPixel( pBox->OutputToAbsoluteScreenPixel( aRect.TopLeft() ) ) );
+ aRect.SetPos( mpImplData->maPos );
+
+ nFlags |=
+ FloatWinPopupFlags::AllMouseButtonClose |
+ FloatWinPopupFlags::NoMouseUpClose;
+
+ // set Flags for positioning
+ if ( !(nFlags & (FloatWinPopupFlags::Down | FloatWinPopupFlags::Up |
+ FloatWinPopupFlags::Left | FloatWinPopupFlags::Right)) )
+ {
+ if ( pBox->IsHorizontal() )
+ nFlags |= FloatWinPopupFlags::Down;
+ else
+ nFlags |= FloatWinPopupFlags::Right;
+ }
+
+ // start FloatingMode
+ StartPopupMode( aRect, nFlags );
+}
+
+void FloatingWindow::ImplEndPopupMode( FloatWinPopupEndFlags nFlags, const VclPtr<vcl::Window>& xFocusId )
+{
+ if ( !mbInPopupMode )
+ return;
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ mbInCleanUp = true; // prevent killing this window due to focus change while working with it
+
+ if (!(nFlags & FloatWinPopupEndFlags::NoCloseChildren))
+ {
+ // stop the PopupMode also for all PopupMode windows created after us
+ std::vector<VclPtr<FloatingWindow>> aCancelFloats;
+ // stop the PopupMode also for all following PopupMode windows
+ for (auto pFloat = pSVData->mpWinData->mpFirstFloat;
+ pFloat != nullptr && pFloat != this;
+ pFloat = pFloat->mpNextFloat)
+ aCancelFloats.push_back(pFloat);
+ for (auto & it : aCancelFloats)
+ it->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::NoCloseChildren);
+ }
+
+ // delete window from the list
+ pSVData->mpWinData->mpFirstFloat = mpNextFloat;
+ mpNextFloat = nullptr;
+
+ FloatWinPopupFlags nPopupModeFlags = mnPopupModeFlags;
+ mbPopupModeTearOff = nFlags & FloatWinPopupEndFlags::TearOff &&
+ nPopupModeFlags & FloatWinPopupFlags::AllowTearOff;
+
+ // hide window again if it was not deleted
+ if (!mbPopupModeTearOff)
+ Show( false, ShowFlags::NoFocusChange );
+
+ if (HasChildPathFocus() && xFocusId != nullptr)
+ {
+ // restore focus to previous focus window if we still have the focus
+ Window::EndSaveFocus(xFocusId);
+ }
+ else if ( pSVData->mpWinData->mpFocusWin && pSVData->mpWinData->mpFirstFloat &&
+ ImplIsWindowOrChild( pSVData->mpWinData->mpFocusWin ) )
+ {
+ // maybe pass focus on to a suitable FloatingWindow
+ pSVData->mpWinData->mpFirstFloat->GrabFocus();
+ }
+
+ mbPopupModeCanceled = bool(nFlags & FloatWinPopupEndFlags::Cancel);
+
+ // redo title
+ SetTitleType( mnOldTitle );
+
+ // set ToolBox again to normal
+ if (mpImplData && mpImplData->mpBox)
+ {
+ mpImplData->mpBox->ImplFloatControl( false, this );
+ // if the parent ToolBox is in popup mode, it should be closed too.
+ if ( GetDockingManager()->IsInPopupMode( mpImplData->mpBox ) )
+ nFlags |= FloatWinPopupEndFlags::CloseAll;
+
+ mpImplData->mpBox = nullptr;
+ }
+
+ // call PopupModeEnd-Handler depending on parameter
+ if ( !(nFlags & FloatWinPopupEndFlags::DontCallHdl) )
+ ImplCallPopupModeEnd();
+
+ // close all other windows depending on parameter
+ if ( nFlags & FloatWinPopupEndFlags::CloseAll )
+ {
+ if ( !(nPopupModeFlags & FloatWinPopupFlags::NewLevel) )
+ {
+ if (pSVData->mpWinData->mpFirstFloat)
+ {
+ FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat();
+ pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
+ }
+ }
+ }
+
+ mbInCleanUp = false;
+}
+
+void FloatingWindow::EndPopupMode( FloatWinPopupEndFlags nFlags )
+{
+ ImplEndPopupMode(nFlags, mxPrevFocusWin);
+}
+
+void FloatingWindow::AddPopupModeWindow(vcl::Window* pWindow)
+{
+ // !!! up-to-now only 1 window and not yet a list
+ mpFirstPopupModeWin = pWindow;
+}
+
+bool SystemWindow::UpdatePositionData()
+{
+ auto pWin = ImplGetParent();
+ if (pWin)
+ {
+ // Simulate Move, so the relative position of the floating window will be recalculated
+ pWin->ImplCallMove();
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/globalization.cxx b/vcl/source/window/globalization.cxx
new file mode 100644
index 0000000000..9521829785
--- /dev/null
+++ b/vcl/source/window/globalization.cxx
@@ -0,0 +1,44 @@
+/* -*- 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 <vcl/window.hxx>
+#include <vcl/outdev.hxx>
+#include <windowdev.hxx>
+#include <window.h>
+
+namespace vcl {
+
+void WindowOutputDevice::EnableRTL ( bool bEnable )
+{
+ if (mbEnableRTL != bEnable)
+ mxOwnerWindow->ImplEnableRTL(bEnable);
+}
+
+void Window::ImplEnableRTL( bool bEnable )
+{
+ if (mpWindowImpl->mxOutDev->mbEnableRTL != bEnable)
+ {
+ CompatStateChanged( StateChangedType::Mirroring );
+ mpWindowImpl->mxOutDev->OutputDevice::EnableRTL(bEnable);
+ }
+}
+
+} /* namespace vcl */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/impldockingwrapper.hxx b/vcl/source/window/impldockingwrapper.hxx
new file mode 100644
index 0000000000..671510e3c5
--- /dev/null
+++ b/vcl/source/window/impldockingwrapper.hxx
@@ -0,0 +1,131 @@
+/* -*- 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 <vcl/dockwin.hxx>
+#include <memory>
+#include <vector>
+
+/** ImplDockingWindowWrapper
+ *
+ * ImplDockingWindowWrapper obsoletes the DockingWindow class.
+ * It is better because it can make a "normal window" dockable.
+ * All DockingWindows should be converted the new class.
+ */
+
+class ImplDockingWindowWrapper final
+{
+ friend class ::vcl::Window;
+ friend class DockingManager;
+ friend class DockingWindow;
+
+private:
+
+ // the original 'Docking'window
+ VclPtr<vcl::Window> mpDockingWindow;
+
+ // the original DockingWindow members
+ VclPtr<FloatingWindow> mpFloatWin;
+ VclPtr<vcl::Window> mpOldBorderWin;
+ VclPtr<vcl::Window> mpParent;
+ Link<FloatingWindow*,void> maPopupModeEndHdl;
+ Point maFloatPos;
+ Point maDockPos;
+ Point maMouseOff;
+ Size maMinOutSize;
+ Size maMaxOutSize;
+ tools::Rectangle maDragArea;
+ tools::Long mnTrackX;
+ tools::Long mnTrackY;
+ tools::Long mnTrackWidth;
+ tools::Long mnTrackHeight;
+ sal_Int32 mnDockLeft;
+ sal_Int32 mnDockTop;
+ sal_Int32 mnDockRight;
+ sal_Int32 mnDockBottom;
+ WinBits mnFloatBits;
+ bool mbDockCanceled:1,
+ mbDocking:1,
+ mbLastFloatMode:1,
+ mbDockBtn:1,
+ mbHideBtn:1,
+ mbStartDockingEnabled:1,
+ mbLocked:1;
+
+ DECL_LINK( PopupModeEnd, FloatingWindow*, void );
+ void ImplEnableStartDocking() { mbStartDockingEnabled = true; }
+ bool ImplStartDockingEnabled() const { return mbStartDockingEnabled; }
+ void ImplPreparePopupMode();
+
+public:
+ ImplDockingWindowWrapper( const vcl::Window *pWindow );
+ ~ImplDockingWindowWrapper();
+
+ vcl::Window* GetWindow() { return mpDockingWindow; }
+ void ImplStartDocking( const Point& rPos );
+
+ // those methods actually call the corresponding handlers
+ void StartDocking( const Point& rPos, tools::Rectangle const & rRect );
+ bool Docking( const Point& rPos, tools::Rectangle& rRect );
+ void EndDocking( const tools::Rectangle& rRect, bool bFloatMode );
+ bool PrepareToggleFloatingMode();
+ void ToggleFloatingMode();
+
+ void SetDragArea( const tools::Rectangle& rRect );
+ const tools::Rectangle& GetDragArea() const { return maDragArea;}
+
+ void Lock();
+ void Unlock();
+ bool IsLocked() const { return mbLocked;}
+
+ void StartPopupMode( const tools::Rectangle& rRect, FloatWinPopupFlags nPopupModeFlags );
+ void StartPopupMode( ToolBox* pParentToolBox, FloatWinPopupFlags nPopupModeFlags );
+ bool IsInPopupMode() const;
+
+ void SetPopupModeEndHdl( const Link<FloatingWindow*,void>& rLink ) { maPopupModeEndHdl = rLink; }
+
+ void TitleButtonClick( TitleButton nButton );
+ void Resizing( Size& rSize );
+ void Tracking( const TrackingEvent& rTEvt );
+
+ void ShowMenuTitleButton( bool bVisible );
+
+ void SetMinOutputSizePixel( const Size& rSize );
+
+ void SetMaxOutputSizePixel( const Size& rSize );
+
+ bool IsDocking() const { return mbDocking; }
+ bool IsDockingCanceled() const { return mbDockCanceled; }
+
+ void SetFloatingMode( bool bFloatMode );
+ bool IsFloatingMode() const;
+ SystemWindow* GetFloatingWindow() const;
+
+ void SetFloatStyle( WinBits nWinStyle );
+ WinBits GetFloatStyle() const { return mnFloatBits;}
+
+ void setPosSizePixel( tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight,
+ PosSizeFlags nFlags );
+ Point GetPosPixel() const;
+ Size GetSizePixel() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/introwin.cxx b/vcl/source/window/introwin.cxx
new file mode 100644
index 0000000000..3ac75eab0d
--- /dev/null
+++ b/vcl/source/window/introwin.cxx
@@ -0,0 +1,49 @@
+/* -*- 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 <vcl/wrkwin.hxx>
+#include <vcl/introwin.hxx>
+
+#include <svdata.hxx>
+
+void IntroWindow::ImplInitIntroWindowData()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ pSVData->mpIntroWindow = this;
+}
+
+IntroWindow::IntroWindow()
+ : WorkWindow(WindowType::INTROWINDOW)
+{
+ ImplInitIntroWindowData();
+ WorkWindow::ImplInit(nullptr, WB_INTROWIN);
+}
+
+IntroWindow::~IntroWindow() { disposeOnce(); }
+
+void IntroWindow::dispose()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ if (pSVData->mpIntroWindow.get() == this)
+ pSVData->mpIntroWindow = nullptr;
+
+ WorkWindow::dispose();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/keycod.cxx b/vcl/source/window/keycod.cxx
new file mode 100644
index 0000000000..cc3fca5802
--- /dev/null
+++ b/vcl/source/window/keycod.cxx
@@ -0,0 +1,95 @@
+/* -*- 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 <accel.hxx>
+#include <salframe.hxx>
+#include <svdata.hxx>
+
+#include <vcl/window.hxx>
+#include <vcl/keycod.hxx>
+
+const sal_uInt16 aImplKeyFuncTab[(static_cast<int>(KeyFuncType::DELETE)+1)*4] =
+{
+ 0, 0, 0, 0, // KeyFuncType::DONTKNOW
+ KEY_X | KEY_MOD1, KEY_DELETE | KEY_SHIFT, KEY_CUT, 0, // KeyFuncType::CUT
+ KEY_C | KEY_MOD1, KEY_INSERT | KEY_MOD1, KEY_COPY, 0, // KeyFuncType::COPY
+ KEY_V | KEY_MOD1, KEY_INSERT | KEY_SHIFT, KEY_PASTE, 0, // KeyFuncType::PASTE
+ KEY_Z | KEY_MOD1, KEY_BACKSPACE | KEY_MOD2, KEY_UNDO, 0, // KeyFuncType::UNDO
+ KEY_Y | KEY_MOD1, KEY_UNDO | KEY_SHIFT, 0, 0, // KeyFuncType::REDO
+ KEY_DELETE, 0, 0, 0 // KeyFuncType::DELETE
+};
+
+bool ImplGetKeyCode( KeyFuncType eFunc, sal_uInt16& rCode1, sal_uInt16& rCode2, sal_uInt16& rCode3, sal_uInt16& rCode4 )
+{
+ size_t nIndex = static_cast<size_t>(eFunc);
+ nIndex *= 4;
+
+ assert(nIndex + 3 < SAL_N_ELEMENTS(aImplKeyFuncTab) && "bad key code index");
+ if (nIndex + 3 >= SAL_N_ELEMENTS(aImplKeyFuncTab))
+ {
+ rCode1 = rCode2 = rCode3 = rCode4 = 0;
+ return false;
+ }
+
+ rCode1 = aImplKeyFuncTab[nIndex];
+ rCode2 = aImplKeyFuncTab[nIndex+1];
+ rCode3 = aImplKeyFuncTab[nIndex+2];
+ rCode4 = aImplKeyFuncTab[nIndex+3];
+ return true;
+}
+
+vcl::KeyCode::KeyCode( KeyFuncType eFunction )
+{
+ sal_uInt16 nDummy;
+ ImplGetKeyCode( eFunction, nKeyCodeAndModifiers, nDummy, nDummy, nDummy );
+ eFunc = eFunction;
+}
+
+OUString vcl::KeyCode::GetName() const
+{
+ vcl::Window* pWindow = ImplGetDefaultWindow();
+ return pWindow ? pWindow->ImplGetFrame()->GetKeyName( GetFullCode() ) : "";
+}
+
+KeyFuncType vcl::KeyCode::GetFunction() const
+{
+ if ( eFunc != KeyFuncType::DONTKNOW )
+ return eFunc;
+
+ sal_uInt16 nCompCode = GetModifier() | GetCode();
+ if ( nCompCode )
+ {
+ for ( sal_uInt16 i = sal_uInt16(KeyFuncType::CUT); i <= sal_uInt16(KeyFuncType::DELETE); i++ )
+ {
+ sal_uInt16 nKeyCode1;
+ sal_uInt16 nKeyCode2;
+ sal_uInt16 nKeyCode3;
+ sal_uInt16 nKeyCode4;
+ ImplGetKeyCode( static_cast<KeyFuncType>(i), nKeyCode1, nKeyCode2, nKeyCode3, nKeyCode4 );
+ if ( (nCompCode == nKeyCode1) || (nCompCode == nKeyCode2) || (nCompCode == nKeyCode3) || (nCompCode == nKeyCode4) )
+ return static_cast<KeyFuncType>(i);
+ }
+ }
+
+ return KeyFuncType::DONTKNOW;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/keyevent.cxx b/vcl/source/window/keyevent.cxx
new file mode 100644
index 0000000000..eca00d4114
--- /dev/null
+++ b/vcl/source/window/keyevent.cxx
@@ -0,0 +1,67 @@
+/* -*- 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 <vcl/event.hxx>
+
+KeyEvent KeyEvent::LogicalTextDirectionality (TextDirectionality eMode) const
+{
+ KeyEvent aClone(*this);
+
+ sal_uInt16 nCode = maKeyCode.GetCode();
+ sal_uInt16 nMod = maKeyCode.GetModifier();
+
+ switch (eMode)
+ {
+ case TextDirectionality::RightToLeft_TopToBottom:
+ switch (nCode)
+ {
+ case KEY_LEFT: aClone.maKeyCode = vcl::KeyCode(KEY_RIGHT, nMod); break;
+ case KEY_RIGHT: aClone.maKeyCode = vcl::KeyCode(KEY_LEFT, nMod); break;
+ }
+ break;
+
+ case TextDirectionality::TopToBottom_RightToLeft:
+ switch (nCode)
+ {
+ case KEY_DOWN: aClone.maKeyCode = vcl::KeyCode(KEY_RIGHT, nMod); break;
+ case KEY_UP: aClone.maKeyCode = vcl::KeyCode(KEY_LEFT, nMod); break;
+ case KEY_LEFT: aClone.maKeyCode = vcl::KeyCode(KEY_DOWN, nMod); break;
+ case KEY_RIGHT: aClone.maKeyCode = vcl::KeyCode(KEY_UP, nMod); break;
+ }
+ break;
+
+ case TextDirectionality::BottomToTop_LeftToRight:
+ switch (nCode)
+ {
+ case KEY_DOWN: aClone.maKeyCode = vcl::KeyCode(KEY_LEFT, nMod); break;
+ case KEY_UP: aClone.maKeyCode = vcl::KeyCode(KEY_RIGHT, nMod); break;
+ case KEY_LEFT: aClone.maKeyCode = vcl::KeyCode(KEY_UP, nMod); break;
+ case KEY_RIGHT: aClone.maKeyCode = vcl::KeyCode(KEY_DOWN, nMod); break;
+ }
+ break;
+
+ case TextDirectionality::LeftToRight_TopToBottom:
+ /* do nothing */
+ break;
+ }
+
+ return aClone;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/layout.cxx b/vcl/source/window/layout.cxx
new file mode 100644
index 0000000000..5639d8e62d
--- /dev/null
+++ b/vcl/source/window/layout.cxx
@@ -0,0 +1,3128 @@
+/* -*- 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/.
+ */
+
+#include <sal/config.h>
+
+// Needed since LLVM 15 libc++ (hence the ignored -Wunused-macros for older libc++) when
+// #include <boost/multi_array.hpp> below includes Boost 1.79.0
+// workdir/UnpackedTarball/boost/boost/functional.hpp using std::unary_function, but must
+// come very early here in case <functional> is already (indirectly) included earlier:
+#include <config_libcxx.h>
+#if HAVE_LIBCPP
+#if defined __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-macros"
+#endif
+// [-loplugin:reservedid]:
+#define _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION
+#if defined __clang__
+#pragma clang diagnostic pop
+#endif
+#endif
+
+#include <string_view>
+
+#include <config_features.h>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <comphelper/base64.hxx>
+#include <comphelper/lok.hxx>
+#include <o3tl/enumarray.hxx>
+#include <o3tl/enumrange.hxx>
+#include <o3tl/string_view.hxx>
+#include <tools/stream.hxx>
+#include <utility>
+#include <vcl/builder.hxx>
+#include <vcl/toolkit/button.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/help.hxx>
+#include <vcl/toolkit/dialog.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/toolkit/scrbar.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/split.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <bitmaps.hlst>
+#include <messagedialog.hxx>
+#include <svdata.hxx>
+#include <window.h>
+#include <boost/multi_array.hpp>
+#include <vcl/toolkit/vclmedit.hxx>
+#include <vcl/uitest/uiobject.hxx>
+#include <sal/log.hxx>
+#include <tools/json_writer.hxx>
+
+VclContainer::VclContainer(vcl::Window *pParent, WinBits nStyle)
+ : Window(WindowType::CONTAINER)
+ , m_bLayoutDirty(true)
+{
+ ImplInit(pParent, nStyle, nullptr);
+ EnableChildTransparentMode();
+ SetPaintTransparent(true);
+ SetBackground();
+}
+
+sal_uInt16 VclContainer::getDefaultAccessibleRole() const
+{
+ return css::accessibility::AccessibleRole::PANEL;
+}
+
+Size VclContainer::GetOptimalSize() const
+{
+ return calculateRequisition();
+}
+
+void VclContainer::setLayoutPosSize(vcl::Window &rWindow, const Point &rPos, const Size &rSize)
+{
+ sal_Int32 nBorderWidth = rWindow.get_border_width();
+ sal_Int32 nLeft = rWindow.get_margin_start() + nBorderWidth;
+ sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth;
+ sal_Int32 nRight = rWindow.get_margin_end() + nBorderWidth;
+ sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth;
+ Point aPos(rPos.X() + nLeft, rPos.Y() + nTop);
+ Size aSize(rSize.Width() - nLeft - nRight, rSize.Height() - nTop - nBottom);
+ rWindow.SetPosSizePixel(aPos, aSize);
+}
+
+void VclContainer::setLayoutAllocation(vcl::Window &rChild, const Point &rAllocPos, const Size &rChildAlloc)
+{
+ VclAlign eHalign = rChild.get_halign();
+ VclAlign eValign = rChild.get_valign();
+
+ //typical case
+ if (eHalign == VclAlign::Fill && eValign == VclAlign::Fill)
+ {
+ setLayoutPosSize(rChild, rAllocPos, rChildAlloc);
+ return;
+ }
+
+ Point aChildPos(rAllocPos);
+ Size aChildSize(rChildAlloc);
+ Size aChildPreferredSize(getLayoutRequisition(rChild));
+
+ switch (eHalign)
+ {
+ case VclAlign::Fill:
+ break;
+ case VclAlign::Start:
+ if (aChildPreferredSize.Width() < rChildAlloc.Width())
+ aChildSize.setWidth( aChildPreferredSize.Width() );
+ break;
+ case VclAlign::End:
+ if (aChildPreferredSize.Width() < rChildAlloc.Width())
+ aChildSize.setWidth( aChildPreferredSize.Width() );
+ aChildPos.AdjustX(rChildAlloc.Width() );
+ aChildPos.AdjustX( -(aChildSize.Width()) );
+ break;
+ case VclAlign::Center:
+ if (aChildPreferredSize.Width() < aChildSize.Width())
+ aChildSize.setWidth( aChildPreferredSize.Width() );
+ aChildPos.AdjustX((rChildAlloc.Width() - aChildSize.Width()) / 2 );
+ break;
+ }
+
+ switch (eValign)
+ {
+ case VclAlign::Fill:
+ break;
+ case VclAlign::Start:
+ if (aChildPreferredSize.Height() < rChildAlloc.Height())
+ aChildSize.setHeight( aChildPreferredSize.Height() );
+ break;
+ case VclAlign::End:
+ if (aChildPreferredSize.Height() < rChildAlloc.Height())
+ aChildSize.setHeight( aChildPreferredSize.Height() );
+ aChildPos.AdjustY(rChildAlloc.Height() );
+ aChildPos.AdjustY( -(aChildSize.Height()) );
+ break;
+ case VclAlign::Center:
+ if (aChildPreferredSize.Height() < aChildSize.Height())
+ aChildSize.setHeight( aChildPreferredSize.Height() );
+ aChildPos.AdjustY((rChildAlloc.Height() - aChildSize.Height()) / 2 );
+ break;
+ }
+
+ setLayoutPosSize(rChild, aChildPos, aChildSize);
+}
+
+namespace
+{
+ Size subtractBorder(const vcl::Window &rWindow, const Size& rSize)
+ {
+ sal_Int32 nBorderWidth = rWindow.get_border_width();
+ sal_Int32 nLeft = rWindow.get_margin_start() + nBorderWidth;
+ sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth;
+ sal_Int32 nRight = rWindow.get_margin_end() + nBorderWidth;
+ sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth;
+ Size aSize(rSize);
+ return Size(aSize.Width() + nLeft + nRight, aSize.Height() + nTop + nBottom);
+ }
+}
+
+Size VclContainer::getLayoutRequisition(const vcl::Window &rWindow)
+{
+ return subtractBorder(rWindow, rWindow.get_preferred_size());
+}
+
+void VclContainer::SetPosSizePixel(const Point& rAllocPos, const Size& rAllocation)
+{
+ bool bSizeChanged = rAllocation != GetOutputSizePixel();
+ Window::SetPosSizePixel(rAllocPos, rAllocation);
+ if (m_bLayoutDirty || bSizeChanged)
+ {
+ m_bLayoutDirty = false;
+ setAllocation(rAllocation);
+ }
+}
+
+void VclContainer::SetPosPixel(const Point& rAllocPos)
+{
+ Point aAllocPos = rAllocPos;
+ sal_Int32 nBorderWidth = get_border_width();
+ aAllocPos.AdjustX(nBorderWidth + get_margin_start() );
+ aAllocPos.AdjustY(nBorderWidth + get_margin_top() );
+
+ if (aAllocPos != GetPosPixel())
+ Window::SetPosPixel(aAllocPos);
+}
+
+void VclContainer::SetSizePixel(const Size& rAllocation)
+{
+ Size aAllocation = rAllocation;
+ sal_Int32 nBorderWidth = get_border_width();
+ aAllocation.AdjustWidth( -(nBorderWidth*2 + get_margin_start() + get_margin_end()) );
+ aAllocation.AdjustHeight( -(nBorderWidth*2 + get_margin_top() + get_margin_bottom()) );
+ bool bSizeChanged = aAllocation != GetSizePixel();
+ if (bSizeChanged)
+ Window::SetSizePixel(aAllocation);
+ if (m_bLayoutDirty || bSizeChanged)
+ {
+ m_bLayoutDirty = false;
+ setAllocation(aAllocation);
+ }
+}
+
+void VclContainer::queue_resize(StateChangedType eReason)
+{
+ m_bLayoutDirty = true;
+ Window::queue_resize(eReason);
+}
+
+// support for screenshot context menu
+void VclContainer::Command(const CommandEvent& rCEvt)
+{
+ if (CommandEventId::ContextMenu == rCEvt.GetCommand())
+ {
+ auto pParent = GetParent();
+ if (pParent)
+ {
+ CommandEvent aCEvt(rCEvt.GetMousePosPixel() + GetPosPixel(), rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData());
+ pParent->Command(aCEvt);
+ return;
+ }
+ }
+
+ // call parent (do not consume)
+ Window::Command(rCEvt);
+}
+
+void VclBox::accumulateMaxes(const Size &rChildSize, Size &rSize) const
+{
+ tools::Long nSecondaryChildDimension = getSecondaryDimension(rChildSize);
+ tools::Long nSecondaryBoxDimension = getSecondaryDimension(rSize);
+ setSecondaryDimension(rSize, std::max(nSecondaryChildDimension, nSecondaryBoxDimension));
+
+ tools::Long nPrimaryChildDimension = getPrimaryDimension(rChildSize);
+ tools::Long nPrimaryBoxDimension = getPrimaryDimension(rSize);
+ if (m_bHomogeneous)
+ setPrimaryDimension(rSize, std::max(nPrimaryBoxDimension, nPrimaryChildDimension));
+ else
+ setPrimaryDimension(rSize, nPrimaryBoxDimension + nPrimaryChildDimension);
+}
+
+Size VclBox::calculateRequisition() const
+{
+ sal_uInt16 nVisibleChildren = 0;
+
+ Size aSize;
+ for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ ++nVisibleChildren;
+ Size aChildSize = getLayoutRequisition(*pChild);
+
+ tools::Long nPrimaryDimension = getPrimaryDimension(aChildSize);
+ nPrimaryDimension += pChild->get_padding() * 2;
+ setPrimaryDimension(aChildSize, nPrimaryDimension);
+
+ accumulateMaxes(aChildSize, aSize);
+ }
+
+ return finalizeMaxes(aSize, nVisibleChildren);
+}
+
+void VclBox::setAllocation(const Size &rAllocation)
+{
+ sal_uInt16 nVisibleChildren = 0, nExpandChildren = 0;
+ for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ ++nVisibleChildren;
+ bool bExpand = getPrimaryDimensionChildExpand(*pChild);
+ if (bExpand)
+ ++nExpandChildren;
+ }
+
+ if (!nVisibleChildren)
+ return;
+
+ tools::Long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);
+
+ tools::Long nHomogeneousDimension = 0, nExtraSpace = 0;
+ if (m_bHomogeneous)
+ {
+ nHomogeneousDimension = (nAllocPrimaryDimension -
+ (nVisibleChildren - 1) * m_nSpacing) / nVisibleChildren;
+ }
+ else if (nExpandChildren)
+ {
+ Size aRequisition = calculateRequisition();
+ tools::Long nPrimaryDimension = getPrimaryDimension(rAllocation);
+ nExtraSpace = (nPrimaryDimension - getPrimaryDimension(aRequisition)) / nExpandChildren;
+ }
+
+ //Split into those we pack from the start onwards, and those we pack from the end backwards
+ o3tl::enumarray<VclPackType,std::vector<vcl::Window*>> aWindows;
+ for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+
+ VclPackType ePacking = pChild->get_pack_type();
+ aWindows[ePacking].push_back(pChild);
+ }
+
+ //See VclBuilder::sortIntoBestTabTraversalOrder for why they are in visual
+ //order under the parent which requires us to reverse them here to
+ //pack from the end back
+ std::reverse(aWindows[VclPackType::End].begin(),aWindows[VclPackType::End].end());
+
+ for (VclPackType ePackType : o3tl::enumrange<VclPackType>())
+ {
+ Point aPos(0, 0);
+ if (ePackType == VclPackType::End)
+ {
+ tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aPos);
+ setPrimaryCoordinate(aPos, nPrimaryCoordinate + nAllocPrimaryDimension);
+ }
+
+ for (auto const& window : aWindows[ePackType])
+ {
+ vcl::Window *pChild = window;
+
+ tools::Long nPadding = pChild->get_padding();
+
+ Size aBoxSize;
+ if (m_bHomogeneous)
+ setPrimaryDimension(aBoxSize, nHomogeneousDimension);
+ else
+ {
+ aBoxSize = getLayoutRequisition(*pChild);
+ tools::Long nPrimaryDimension = getPrimaryDimension(aBoxSize);
+ nPrimaryDimension += nPadding * 2;
+ if (getPrimaryDimensionChildExpand(*pChild))
+ nPrimaryDimension += nExtraSpace;
+ setPrimaryDimension(aBoxSize, nPrimaryDimension);
+ }
+ setSecondaryDimension(aBoxSize, getSecondaryDimension(rAllocation));
+
+ Point aChildPos(aPos);
+ Size aChildSize(aBoxSize);
+ tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aPos);
+
+ bool bFill = pChild->get_fill();
+ if (bFill)
+ {
+ setPrimaryDimension(aChildSize, std::max(static_cast<tools::Long>(1),
+ std::min(getPrimaryDimension(rAllocation), getPrimaryDimension(aBoxSize) - nPadding * 2)));
+
+ setPrimaryCoordinate(aChildPos, nPrimaryCoordinate + nPadding);
+ }
+ else
+ {
+ setPrimaryDimension(aChildSize,
+ getPrimaryDimension(getLayoutRequisition(*pChild)));
+
+ setPrimaryCoordinate(aChildPos, nPrimaryCoordinate +
+ (getPrimaryDimension(aBoxSize) - getPrimaryDimension(aChildSize)) / 2);
+ }
+
+ tools::Long nDiff = getPrimaryDimension(aBoxSize) + m_nSpacing;
+ if (ePackType == VclPackType::Start)
+ setPrimaryCoordinate(aPos, nPrimaryCoordinate + nDiff);
+ else
+ {
+ setPrimaryCoordinate(aPos, nPrimaryCoordinate - nDiff);
+ setPrimaryCoordinate(aChildPos, getPrimaryCoordinate(aChildPos) -
+ getPrimaryDimension(aBoxSize));
+ }
+
+ setLayoutAllocation(*pChild, aChildPos, aChildSize);
+ }
+ }
+}
+
+bool VclBox::set_property(const OUString &rKey, const OUString &rValue)
+{
+ if (rKey == "spacing")
+ set_spacing(rValue.toInt32());
+ else if (rKey == "homogeneous")
+ set_homogeneous(toBool(rValue));
+ else
+ return VclContainer::set_property(rKey, rValue);
+ return true;
+}
+
+void VclBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ VclContainer::DumpAsPropertyTree(rJsonWriter);
+ rJsonWriter.put("vertical", m_bVerticalContainer);
+}
+
+sal_uInt16 VclBox::getDefaultAccessibleRole() const
+{
+ // fdo#74284 call Boxes Panels, keep them as "Filler" under
+ // at least Linux seeing as that's what Gtk3 did for GtkBoxes.
+ // Though now with Gtk4 that uses GTK_ACCESSIBLE_ROLE_GROUP
+ // which maps to ATSPI_ROLE_PANEL
+#if defined(_WIN32)
+ return css::accessibility::AccessibleRole::PANEL;
+#else
+ static sal_uInt16 eRole = Application::GetToolkitName() == "gtk4" ?
+ css::accessibility::AccessibleRole::PANEL :
+ css::accessibility::AccessibleRole::FILLER;
+ return eRole;
+#endif
+}
+
+#define DEFAULT_CHILD_MIN_WIDTH 85
+#define DEFAULT_CHILD_MIN_HEIGHT 27
+
+Size VclBox::finalizeMaxes(const Size &rSize, sal_uInt16 nVisibleChildren) const
+{
+ Size aRet;
+
+ if (nVisibleChildren)
+ {
+ tools::Long nPrimaryDimension = getPrimaryDimension(rSize);
+ if (m_bHomogeneous)
+ nPrimaryDimension *= nVisibleChildren;
+ setPrimaryDimension(aRet, nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
+ setSecondaryDimension(aRet, getSecondaryDimension(rSize));
+ }
+
+ return aRet;
+}
+
+Size VclButtonBox::addReqGroups(const VclButtonBox::Requisition &rReq) const
+{
+ Size aRet;
+
+ tools::Long nMainGroupDimension = getPrimaryDimension(rReq.m_aMainGroupSize);
+ tools::Long nSubGroupDimension = getPrimaryDimension(rReq.m_aSubGroupSize);
+
+ setPrimaryDimension(aRet, nMainGroupDimension + nSubGroupDimension);
+
+ setSecondaryDimension(aRet,
+ std::max(getSecondaryDimension(rReq.m_aMainGroupSize),
+ getSecondaryDimension(rReq.m_aSubGroupSize)));
+
+ return aRet;
+}
+
+static tools::Long getMaxNonOutlier(const std::vector<tools::Long> &rG, tools::Long nAvgDimension)
+{
+ tools::Long nMaxDimensionNonOutlier = 0;
+ for (auto const& nPrimaryChildDimension : rG)
+ {
+ if (nPrimaryChildDimension < nAvgDimension * 1.5)
+ {
+ nMaxDimensionNonOutlier = std::max(nPrimaryChildDimension,
+ nMaxDimensionNonOutlier);
+ }
+ }
+ return nMaxDimensionNonOutlier;
+}
+
+static std::vector<tools::Long> setButtonSizes(const std::vector<tools::Long> &rG,
+ const std::vector<bool> &rNonHomogeneous,
+ tools::Long nAvgDimension, tools::Long nMaxNonOutlier, tools::Long nMinWidth)
+{
+ std::vector<tools::Long> aVec;
+ //set everything < 1.5 times the average to the same width, leave the
+ //outliers un-touched
+ std::vector<bool>::const_iterator aJ = rNonHomogeneous.begin();
+ auto nNonOutlierWidth = std::max(nMaxNonOutlier, nMinWidth);
+ for (auto const& nPrimaryChildDimension : rG)
+ {
+ bool bNonHomogeneous = *aJ;
+ if (!bNonHomogeneous && nPrimaryChildDimension < nAvgDimension * 1.5)
+ {
+ aVec.push_back(nNonOutlierWidth);
+ }
+ else
+ {
+ aVec.push_back(std::max(nPrimaryChildDimension, nMinWidth));
+ }
+ ++aJ;
+ }
+ return aVec;
+}
+
+VclButtonBox::Requisition VclButtonBox::calculatePrimarySecondaryRequisitions() const
+{
+ Requisition aReq;
+
+ Size aMainGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
+ Size aSubGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
+
+ tools::Long nMinMainGroupPrimary = getPrimaryDimension(aMainGroupSize);
+ tools::Long nMinSubGroupPrimary = getPrimaryDimension(aSubGroupSize);
+ tools::Long nMainGroupSecondary = getSecondaryDimension(aMainGroupSize);
+ tools::Long nSubGroupSecondary = getSecondaryDimension(aSubGroupSize);
+
+ bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center);
+
+ std::vector<tools::Long> aMainGroupSizes;
+ std::vector<bool> aMainGroupNonHomogeneous;
+ std::vector<tools::Long> aSubGroupSizes;
+ std::vector<bool> aSubGroupNonHomogeneous;
+
+ for (const vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ Size aChildSize = getLayoutRequisition(*pChild);
+ if (bIgnoreSecondaryPacking || !pChild->get_secondary())
+ {
+ //set the max secondary dimension
+ nMainGroupSecondary = std::max(nMainGroupSecondary, getSecondaryDimension(aChildSize));
+ //collect the primary dimensions
+ aMainGroupSizes.push_back(getPrimaryDimension(aChildSize));
+ aMainGroupNonHomogeneous.push_back(pChild->get_non_homogeneous());
+ }
+ else
+ {
+ nSubGroupSecondary = std::max(nSubGroupSecondary, getSecondaryDimension(aChildSize));
+ aSubGroupSizes.push_back(getPrimaryDimension(aChildSize));
+ aSubGroupNonHomogeneous.push_back(pChild->get_non_homogeneous());
+ }
+ }
+
+ if (m_bHomogeneous)
+ {
+ tools::Long nMaxMainDimension = aMainGroupSizes.empty() ? 0 :
+ *std::max_element(aMainGroupSizes.begin(), aMainGroupSizes.end());
+ nMaxMainDimension = std::max(nMaxMainDimension, nMinMainGroupPrimary);
+ tools::Long nMaxSubDimension = aSubGroupSizes.empty() ? 0 :
+ *std::max_element(aSubGroupSizes.begin(), aSubGroupSizes.end());
+ nMaxSubDimension = std::max(nMaxSubDimension, nMinSubGroupPrimary);
+ tools::Long nMaxDimension = std::max(nMaxMainDimension, nMaxSubDimension);
+ aReq.m_aMainGroupDimensions.resize(aMainGroupSizes.size(), nMaxDimension);
+ aReq.m_aSubGroupDimensions.resize(aSubGroupSizes.size(), nMaxDimension);
+ }
+ else
+ {
+ //Ideally set everything to the same size, but find outlier widgets
+ //that are way wider than the average and leave them
+ //at their natural size and set the remainder to share the
+ //max size of the remaining members of the buttonbox
+ tools::Long nAccDimension = std::accumulate(aMainGroupSizes.begin(),
+ aMainGroupSizes.end(), 0);
+ nAccDimension = std::accumulate(aSubGroupSizes.begin(),
+ aSubGroupSizes.end(), nAccDimension);
+
+ size_t nTotalSize = aMainGroupSizes.size() + aSubGroupSizes.size();
+
+ tools::Long nAvgDimension = nTotalSize ? nAccDimension / nTotalSize : 0;
+
+ tools::Long nMaxMainNonOutlier = getMaxNonOutlier(aMainGroupSizes,
+ nAvgDimension);
+ tools::Long nMaxSubNonOutlier = getMaxNonOutlier(aSubGroupSizes,
+ nAvgDimension);
+ tools::Long nMaxNonOutlier = std::max(nMaxMainNonOutlier, nMaxSubNonOutlier);
+
+ aReq.m_aMainGroupDimensions = setButtonSizes(aMainGroupSizes,
+ aMainGroupNonHomogeneous,
+ nAvgDimension, nMaxNonOutlier, nMinMainGroupPrimary);
+ aReq.m_aSubGroupDimensions = setButtonSizes(aSubGroupSizes,
+ aSubGroupNonHomogeneous,
+ nAvgDimension, nMaxNonOutlier, nMinSubGroupPrimary);
+ }
+
+ if (!aReq.m_aMainGroupDimensions.empty())
+ {
+ setSecondaryDimension(aReq.m_aMainGroupSize, nMainGroupSecondary);
+ setPrimaryDimension(aReq.m_aMainGroupSize,
+ std::accumulate(aReq.m_aMainGroupDimensions.begin(),
+ aReq.m_aMainGroupDimensions.end(), 0));
+ }
+ if (!aReq.m_aSubGroupDimensions.empty())
+ {
+ setSecondaryDimension(aReq.m_aSubGroupSize, nSubGroupSecondary);
+ setPrimaryDimension(aReq.m_aSubGroupSize,
+ std::accumulate(aReq.m_aSubGroupDimensions.begin(),
+ aReq.m_aSubGroupDimensions.end(), 0));
+ }
+
+ return aReq;
+}
+
+Size VclButtonBox::addSpacing(const Size &rSize, sal_uInt16 nVisibleChildren) const
+{
+ Size aRet;
+
+ if (nVisibleChildren)
+ {
+ tools::Long nPrimaryDimension = getPrimaryDimension(rSize);
+ setPrimaryDimension(aRet,
+ nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
+ setSecondaryDimension(aRet, getSecondaryDimension(rSize));
+ }
+
+ return aRet;
+}
+
+Size VclButtonBox::calculateRequisition() const
+{
+ Requisition aReq(calculatePrimarySecondaryRequisitions());
+ sal_uInt16 nVisibleChildren = aReq.m_aMainGroupDimensions.size() +
+ aReq.m_aSubGroupDimensions.size();
+ return addSpacing(addReqGroups(aReq), nVisibleChildren);
+}
+
+bool VclButtonBox::set_property(const OUString &rKey, const OUString &rValue)
+{
+ if (rKey == "layout-style")
+ {
+ VclButtonBoxStyle eStyle = VclButtonBoxStyle::Default;
+ if (rValue == "spread")
+ eStyle = VclButtonBoxStyle::Spread;
+ else if (rValue == "edge")
+ eStyle = VclButtonBoxStyle::Edge;
+ else if (rValue == "start")
+ eStyle = VclButtonBoxStyle::Start;
+ else if (rValue == "end")
+ eStyle = VclButtonBoxStyle::End;
+ else if (rValue == "center")
+ eStyle = VclButtonBoxStyle::Center;
+ else
+ {
+ SAL_WARN("vcl.layout", "unknown layout style " << rValue);
+ }
+ m_eLayoutStyle = eStyle;
+ }
+ else
+ return VclBox::set_property(rKey, rValue);
+ return true;
+}
+
+void VclButtonBox::setAllocation(const Size &rAllocation)
+{
+ Requisition aReq(calculatePrimarySecondaryRequisitions());
+
+ if (aReq.m_aMainGroupDimensions.empty() && aReq.m_aSubGroupDimensions.empty())
+ return;
+
+ tools::Long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);
+
+ Point aMainGroupPos, aOtherGroupPos;
+ int nSpacing = m_nSpacing;
+
+ //To-Do, other layout styles
+ switch (m_eLayoutStyle)
+ {
+ case VclButtonBoxStyle::Start:
+ if (!aReq.m_aSubGroupDimensions.empty())
+ {
+ tools::Long nOtherPrimaryDimension = getPrimaryDimension(
+ addSpacing(aReq.m_aSubGroupSize, aReq.m_aSubGroupDimensions.size()));
+ setPrimaryCoordinate(aOtherGroupPos,
+ nAllocPrimaryDimension - nOtherPrimaryDimension);
+ }
+ break;
+ case VclButtonBoxStyle::Spread:
+ if (!aReq.m_aMainGroupDimensions.empty())
+ {
+ tools::Long nMainPrimaryDimension = getPrimaryDimension(
+ addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
+ tools::Long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
+ nExtraSpace += (aReq.m_aMainGroupDimensions.size()-1) * nSpacing;
+ nSpacing = nExtraSpace/(aReq.m_aMainGroupDimensions.size()+1);
+ setPrimaryCoordinate(aMainGroupPos, nSpacing);
+ }
+ break;
+ case VclButtonBoxStyle::Center:
+ if (!aReq.m_aMainGroupDimensions.empty())
+ {
+ tools::Long nMainPrimaryDimension = getPrimaryDimension(
+ addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
+ tools::Long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
+ setPrimaryCoordinate(aMainGroupPos, nExtraSpace/2);
+ }
+ break;
+ default:
+ SAL_WARN("vcl.layout", "todo unimplemented layout style");
+ [[fallthrough]];
+ case VclButtonBoxStyle::Default:
+ case VclButtonBoxStyle::End:
+ if (!aReq.m_aMainGroupDimensions.empty())
+ {
+ tools::Long nMainPrimaryDimension = getPrimaryDimension(
+ addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
+ setPrimaryCoordinate(aMainGroupPos,
+ nAllocPrimaryDimension - nMainPrimaryDimension);
+ }
+ break;
+ }
+
+ Size aChildSize;
+ setSecondaryDimension(aChildSize, getSecondaryDimension(rAllocation));
+
+ std::vector<tools::Long>::const_iterator aPrimaryI = aReq.m_aMainGroupDimensions.begin();
+ std::vector<tools::Long>::const_iterator aSecondaryI = aReq.m_aSubGroupDimensions.begin();
+ bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center);
+ for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+
+ if (bIgnoreSecondaryPacking || !pChild->get_secondary())
+ {
+ tools::Long nMainGroupPrimaryDimension = *aPrimaryI++;
+ setPrimaryDimension(aChildSize, nMainGroupPrimaryDimension);
+ setLayoutAllocation(*pChild, aMainGroupPos, aChildSize);
+ tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aMainGroupPos);
+ setPrimaryCoordinate(aMainGroupPos, nPrimaryCoordinate + nMainGroupPrimaryDimension + nSpacing);
+ }
+ else
+ {
+ tools::Long nSubGroupPrimaryDimension = *aSecondaryI++;
+ setPrimaryDimension(aChildSize, nSubGroupPrimaryDimension);
+ setLayoutAllocation(*pChild, aOtherGroupPos, aChildSize);
+ tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aOtherGroupPos);
+ setPrimaryCoordinate(aOtherGroupPos, nPrimaryCoordinate + nSubGroupPrimaryDimension + nSpacing);
+ }
+ }
+}
+
+void VclButtonBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ VclBox::DumpAsPropertyTree(rJsonWriter);
+ rJsonWriter.put("type", "buttonbox");
+
+ switch(m_eLayoutStyle)
+ {
+ case VclButtonBoxStyle::Default:
+ rJsonWriter.put("layoutstyle", "default");
+ break;
+
+ case VclButtonBoxStyle::Spread:
+ rJsonWriter.put("layoutstyle", "spread");
+ break;
+
+ case VclButtonBoxStyle::Edge:
+ rJsonWriter.put("layoutstyle", "edge");
+ break;
+
+ case VclButtonBoxStyle::Center:
+ rJsonWriter.put("layoutstyle", "center");
+ break;
+
+ case VclButtonBoxStyle::Start:
+ rJsonWriter.put("layoutstyle", "start");
+ break;
+
+ case VclButtonBoxStyle::End:
+ rJsonWriter.put("layoutstyle", "end");
+ break;
+ }
+}
+
+namespace {
+
+struct ButtonOrder
+{
+ std::u16string_view m_aType;
+ int m_nPriority;
+};
+
+}
+
+static int getButtonPriority(std::u16string_view rType)
+{
+ static const size_t N_TYPES = 6;
+ static const ButtonOrder aDiscardCancelSave[N_TYPES] =
+ {
+ { u"discard", 0 },
+ { u"cancel", 1 },
+ { u"no", 2 },
+ { u"save", 3 },
+ { u"yes", 3 },
+ { u"ok", 3 }
+ };
+
+ static const ButtonOrder aSaveDiscardCancel[N_TYPES] =
+ {
+ { u"save", 0 },
+ { u"yes", 0 },
+ { u"ok", 0 },
+ { u"discard", 1 },
+ { u"no", 1 },
+ { u"cancel", 2 }
+ };
+
+ const ButtonOrder* pOrder = &aDiscardCancelSave[0];
+
+ const OUString &rEnv = Application::GetDesktopEnvironment();
+
+ if (rEnv.equalsIgnoreAsciiCase("windows") ||
+ rEnv.equalsIgnoreAsciiCase("lxqt") ||
+ rEnv.startsWithIgnoreAsciiCase("plasma"))
+ {
+ pOrder = &aSaveDiscardCancel[0];
+ }
+
+ for (size_t i = 0; i < N_TYPES; ++i, ++pOrder)
+ {
+ if (rType == pOrder->m_aType)
+ return pOrder->m_nPriority;
+ }
+
+ return -1;
+}
+
+namespace {
+
+class sortButtons
+{
+ bool m_bVerticalContainer;
+public:
+ explicit sortButtons(bool bVerticalContainer)
+ : m_bVerticalContainer(bVerticalContainer)
+ {
+ }
+ bool operator()(const vcl::Window *pA, const vcl::Window *pB) const;
+};
+
+}
+
+bool sortButtons::operator()(const vcl::Window *pA, const vcl::Window *pB) const
+{
+ //sort into two groups of pack start and pack end
+ VclPackType ePackA = pA->get_pack_type();
+ VclPackType ePackB = pB->get_pack_type();
+ if (ePackA < ePackB)
+ return true;
+ if (ePackA > ePackB)
+ return false;
+ bool bPackA = pA->get_secondary();
+ bool bPackB = pB->get_secondary();
+ if (!m_bVerticalContainer)
+ {
+ //for horizontal boxes group secondaries before primaries
+ if (bPackA > bPackB)
+ return true;
+ if (bPackA < bPackB)
+ return false;
+ }
+ else
+ {
+ //for vertical boxes group secondaries after primaries
+ if (bPackA < bPackB)
+ return true;
+ if (bPackA > bPackB)
+ return false;
+ }
+
+ //now order within groups according to platform rules
+ return getButtonPriority(pA->get_id()) < getButtonPriority(pB->get_id());
+}
+
+void sort_native_button_order(const VclBox& rContainer)
+{
+ std::vector<vcl::Window*> aChilds;
+ for (vcl::Window* pChild = rContainer.GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ aChilds.push_back(pChild);
+ }
+
+ //sort child order within parent so that we match the platform
+ //button order
+ std::stable_sort(aChilds.begin(), aChilds.end(), sortButtons(rContainer.get_orientation()));
+ BuilderUtils::reorderWithinParent(aChilds, true);
+}
+
+namespace {
+
+struct GridEntry
+{
+ VclPtr<vcl::Window> pChild;
+ sal_Int32 nSpanWidth;
+ sal_Int32 nSpanHeight;
+ int x;
+ int y;
+ GridEntry()
+ : pChild(nullptr)
+ , nSpanWidth(0)
+ , nSpanHeight(0)
+ , x(-1)
+ , y(-1)
+ {
+ }
+};
+
+}
+
+typedef boost::multi_array<GridEntry, 2> array_type;
+
+static array_type assembleGrid(const VclGrid &rGrid);
+static bool isNullGrid(const array_type& A);
+static void calcMaxs(const array_type &A, std::vector<VclGrid::Value> &rWidths, std::vector<VclGrid::Value> &rHeights);
+
+array_type assembleGrid(const VclGrid &rGrid)
+{
+ array_type A;
+
+ for (vcl::Window* pChild = rGrid.GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ sal_Int32 nLeftAttach = std::max<sal_Int32>(pChild->get_grid_left_attach(), 0);
+ sal_Int32 nWidth = pChild->get_grid_width();
+ sal_Int32 nMaxXPos = nLeftAttach+nWidth-1;
+
+ sal_Int32 nTopAttach = std::max<sal_Int32>(pChild->get_grid_top_attach(), 0);
+ sal_Int32 nHeight = pChild->get_grid_height();
+ sal_Int32 nMaxYPos = nTopAttach+nHeight-1;
+
+ sal_Int32 nCurrentMaxXPos = A.shape()[0]-1;
+ sal_Int32 nCurrentMaxYPos = A.shape()[1]-1;
+ if (nMaxXPos > nCurrentMaxXPos || nMaxYPos > nCurrentMaxYPos)
+ {
+ nCurrentMaxXPos = std::max(nMaxXPos, nCurrentMaxXPos);
+ nCurrentMaxYPos = std::max(nMaxYPos, nCurrentMaxYPos);
+ A.resize(boost::extents[nCurrentMaxXPos+1][nCurrentMaxYPos+1]);
+ }
+
+ GridEntry &rEntry = A[nLeftAttach][nTopAttach];
+ rEntry.pChild = pChild;
+ rEntry.nSpanWidth = nWidth;
+ rEntry.nSpanHeight = nHeight;
+ rEntry.x = nLeftAttach;
+ rEntry.y = nTopAttach;
+
+ for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
+ {
+ for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
+ {
+ GridEntry &rSpan = A[nLeftAttach+nSpanX][nTopAttach+nSpanY];
+ rSpan.x = nLeftAttach;
+ rSpan.y = nTopAttach;
+ }
+ }
+ }
+
+ //see if we have any empty rows/cols
+ sal_Int32 nMaxX = A.shape()[0];
+ sal_Int32 nMaxY = A.shape()[1];
+
+ std::vector<bool> aNonEmptyCols(nMaxX);
+ std::vector<bool> aNonEmptyRows(nMaxY);
+
+ for (sal_Int32 x = 0; x < nMaxX; ++x)
+ {
+ for (sal_Int32 y = 0; y < nMaxY; ++y)
+ {
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-reference"
+#endif
+ const GridEntry &rEntry = A[x][y];
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
+#pragma GCC diagnostic pop
+#endif
+ const vcl::Window *pChild = rEntry.pChild;
+ if (pChild && pChild->IsVisible())
+ {
+ aNonEmptyCols[x] = true;
+ if (rGrid.get_column_homogeneous())
+ {
+ for (sal_Int32 nSpanX = 1; nSpanX < rEntry.nSpanWidth; ++nSpanX)
+ aNonEmptyCols[x+nSpanX] = true;
+ }
+ aNonEmptyRows[y] = true;
+ if (rGrid.get_row_homogeneous())
+ {
+ for (sal_Int32 nSpanY = 1; nSpanY < rEntry.nSpanHeight; ++nSpanY)
+ aNonEmptyRows[y+nSpanY] = true;
+ }
+ }
+ }
+ }
+
+ if (!rGrid.get_column_homogeneous())
+ {
+ //reduce the spans of elements that span empty columns
+ for (sal_Int32 x = 0; x < nMaxX; ++x)
+ {
+ std::set<GridEntry*> candidates;
+ for (sal_Int32 y = 0; y < nMaxY; ++y)
+ {
+ if (aNonEmptyCols[x])
+ continue;
+ GridEntry &rSpan = A[x][y];
+ //cell x/y is spanned by the widget at cell rSpan.x/rSpan.y,
+ //just points back to itself if there's no cell spanning
+ if ((rSpan.x == -1) || (rSpan.y == -1))
+ {
+ //there is no entry for this cell, i.e. this is a cell
+ //with no widget in it, or spanned by any other widget
+ continue;
+ }
+ GridEntry &rEntry = A[rSpan.x][rSpan.y];
+ candidates.insert(&rEntry);
+ }
+ for (auto const& candidate : candidates)
+ {
+ GridEntry *pEntry = candidate;
+ --pEntry->nSpanWidth;
+ }
+ }
+ }
+
+ if (!rGrid.get_row_homogeneous())
+ {
+ //reduce the spans of elements that span empty rows
+ for (sal_Int32 y = 0; y < nMaxY; ++y)
+ {
+ std::set<GridEntry*> candidates;
+ for (sal_Int32 x = 0; x < nMaxX; ++x)
+ {
+ if (aNonEmptyRows[y])
+ continue;
+ GridEntry &rSpan = A[x][y];
+ //cell x/y is spanned by the widget at cell rSpan.x/rSpan.y,
+ //just points back to itself if there's no cell spanning
+ if ((rSpan.x == -1) || (rSpan.y == -1))
+ {
+ //there is no entry for this cell, i.e. this is a cell
+ //with no widget in it, or spanned by any other widget
+ continue;
+ }
+ GridEntry &rEntry = A[rSpan.x][rSpan.y];
+ candidates.insert(&rEntry);
+ }
+ for (auto const& candidate : candidates)
+ {
+ GridEntry *pEntry = candidate;
+ --pEntry->nSpanHeight;
+ }
+ }
+ }
+
+ sal_Int32 nNonEmptyCols = std::count(aNonEmptyCols.begin(), aNonEmptyCols.end(), true);
+ sal_Int32 nNonEmptyRows = std::count(aNonEmptyRows.begin(), aNonEmptyRows.end(), true);
+
+ //make new grid without empty rows and columns
+ array_type B(boost::extents[nNonEmptyCols][nNonEmptyRows]);
+ for (sal_Int32 x = 0, x2 = 0; x < nMaxX; ++x)
+ {
+ if (!aNonEmptyCols[x])
+ continue;
+ for (sal_Int32 y = 0, y2 = 0; y < nMaxY; ++y)
+ {
+ if (!aNonEmptyRows[y])
+ continue;
+ GridEntry &rEntry = A[x][y];
+ B[x2][y2++] = rEntry;
+ }
+ ++x2;
+ }
+
+ return B;
+}
+
+static bool isNullGrid(const array_type &A)
+{
+ sal_Int32 nMaxX = A.shape()[0];
+ sal_Int32 nMaxY = A.shape()[1];
+
+ return !nMaxX || !nMaxY;
+}
+
+static void calcMaxs(const array_type &A, std::vector<VclGrid::Value> &rWidths, std::vector<VclGrid::Value> &rHeights)
+{
+ sal_Int32 nMaxX = A.shape()[0];
+ sal_Int32 nMaxY = A.shape()[1];
+
+ rWidths.resize(nMaxX);
+ rHeights.resize(nMaxY);
+
+ //first use the non spanning entries to set default width/heights
+ for (sal_Int32 x = 0; x < nMaxX; ++x)
+ {
+ for (sal_Int32 y = 0; y < nMaxY; ++y)
+ {
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-reference"
+#endif
+ const GridEntry &rEntry = A[x][y];
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
+#pragma GCC diagnostic pop
+#endif
+ const vcl::Window *pChild = rEntry.pChild;
+ if (!pChild || !pChild->IsVisible())
+ continue;
+
+ sal_Int32 nWidth = rEntry.nSpanWidth;
+ sal_Int32 nHeight = rEntry.nSpanHeight;
+
+ for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
+ rWidths[x+nSpanX].m_bExpand |= pChild->get_hexpand();
+
+ for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
+ rHeights[y+nSpanY].m_bExpand |= pChild->get_vexpand();
+
+ if (nWidth == 1 || nHeight == 1)
+ {
+ Size aChildSize = VclContainer::getLayoutRequisition(*pChild);
+ if (nWidth == 1)
+ rWidths[x].m_nValue = std::max(rWidths[x].m_nValue, aChildSize.Width());
+ if (nHeight == 1)
+ rHeights[y].m_nValue = std::max(rHeights[y].m_nValue, aChildSize.Height());
+ }
+ }
+ }
+
+ //now use the spanning entries and split any extra sizes across expanding rows/cols
+ //where possible
+ for (sal_Int32 x = 0; x < nMaxX; ++x)
+ {
+ for (sal_Int32 y = 0; y < nMaxY; ++y)
+ {
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-reference"
+#endif
+ const GridEntry &rEntry = A[x][y];
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
+#pragma GCC diagnostic pop
+#endif
+ const vcl::Window *pChild = rEntry.pChild;
+ if (!pChild || !pChild->IsVisible())
+ continue;
+
+ sal_Int32 nWidth = rEntry.nSpanWidth;
+ sal_Int32 nHeight = rEntry.nSpanHeight;
+
+ if (nWidth == 1 && nHeight == 1)
+ continue;
+
+ Size aChildSize = VclContainer::getLayoutRequisition(*pChild);
+
+ if (nWidth > 1)
+ {
+ sal_Int32 nExistingWidth = 0;
+ for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
+ nExistingWidth += rWidths[x+nSpanX].m_nValue;
+
+ sal_Int32 nExtraWidth = aChildSize.Width() - nExistingWidth;
+
+ if (nExtraWidth > 0)
+ {
+ bool bForceExpandAll = false;
+ sal_Int32 nExpandables = 0;
+ for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
+ if (rWidths[x+nSpanX].m_bExpand)
+ ++nExpandables;
+ if (nExpandables == 0)
+ {
+ nExpandables = nWidth;
+ bForceExpandAll = true;
+ }
+
+ for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
+ {
+ if (rWidths[x+nSpanX].m_bExpand || bForceExpandAll)
+ rWidths[x+nSpanX].m_nValue += nExtraWidth/nExpandables;
+ }
+ }
+ }
+
+ if (nHeight > 1)
+ {
+ sal_Int32 nExistingHeight = 0;
+ for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
+ nExistingHeight += rHeights[y+nSpanY].m_nValue;
+
+ sal_Int32 nExtraHeight = aChildSize.Height() - nExistingHeight;
+
+ if (nExtraHeight > 0)
+ {
+ bool bForceExpandAll = false;
+ sal_Int32 nExpandables = 0;
+ for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
+ if (rHeights[y+nSpanY].m_bExpand)
+ ++nExpandables;
+ if (nExpandables == 0)
+ {
+ nExpandables = nHeight;
+ bForceExpandAll = true;
+ }
+
+ for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
+ {
+ if (rHeights[y+nSpanY].m_bExpand || bForceExpandAll)
+ rHeights[y+nSpanY].m_nValue += nExtraHeight/nExpandables;
+ }
+ }
+ }
+ }
+ }
+}
+
+static bool compareValues(const VclGrid::Value &i, const VclGrid::Value &j)
+{
+ return i.m_nValue < j.m_nValue;
+}
+
+static VclGrid::Value accumulateValues(const VclGrid::Value &i, const VclGrid::Value &j)
+{
+ VclGrid::Value aRet;
+ aRet.m_nValue = i.m_nValue + j.m_nValue;
+ aRet.m_bExpand = i.m_bExpand || j.m_bExpand;
+ return aRet;
+}
+
+Size VclGrid::calculateRequisition() const
+{
+ return calculateRequisitionForSpacings(get_row_spacing(), get_column_spacing());
+}
+
+Size VclGrid::calculateRequisitionForSpacings(sal_Int32 nRowSpacing, sal_Int32 nColSpacing) const
+{
+ array_type A = assembleGrid(*this);
+
+ if (isNullGrid(A))
+ return Size();
+
+ std::vector<Value> aWidths;
+ std::vector<Value> aHeights;
+ calcMaxs(A, aWidths, aHeights);
+
+ tools::Long nTotalWidth = 0;
+ if (get_column_homogeneous())
+ {
+ nTotalWidth = std::max_element(aWidths.begin(), aWidths.end(), compareValues)->m_nValue;
+ nTotalWidth *= aWidths.size();
+ }
+ else
+ {
+ nTotalWidth = std::accumulate(aWidths.begin(), aWidths.end(), Value(), accumulateValues).m_nValue;
+ }
+
+ nTotalWidth += nColSpacing * (aWidths.size()-1);
+
+ tools::Long nTotalHeight = 0;
+ if (get_row_homogeneous())
+ {
+ nTotalHeight = std::max_element(aHeights.begin(), aHeights.end(), compareValues)->m_nValue;
+ nTotalHeight *= aHeights.size();
+ }
+ else
+ {
+ nTotalHeight = std::accumulate(aHeights.begin(), aHeights.end(), Value(), accumulateValues).m_nValue;
+ }
+
+ nTotalHeight += nRowSpacing * (aHeights.size()-1);
+
+ return Size(nTotalWidth, nTotalHeight);
+}
+
+void VclGrid::setAllocation(const Size& rAllocation)
+{
+ array_type A = assembleGrid(*this);
+
+ if (isNullGrid(A))
+ return;
+
+ sal_Int32 nMaxX = A.shape()[0];
+ sal_Int32 nMaxY = A.shape()[1];
+
+ Size aRequisition;
+ std::vector<Value> aWidths(nMaxX);
+ std::vector<Value> aHeights(nMaxY);
+ if (!get_column_homogeneous() || !get_row_homogeneous())
+ {
+ aRequisition = calculateRequisition();
+ calcMaxs(A, aWidths, aHeights);
+ }
+
+ sal_Int32 nColSpacing(get_column_spacing());
+ sal_Int32 nRowSpacing(get_row_spacing());
+
+ tools::Long nAvailableWidth = rAllocation.Width();
+ if (nMaxX)
+ nAvailableWidth -= nColSpacing * (nMaxX - 1);
+ if (get_column_homogeneous())
+ {
+ for (sal_Int32 x = 0; x < nMaxX; ++x)
+ aWidths[x].m_nValue = nAvailableWidth/nMaxX;
+ }
+ else if (rAllocation.Width() != aRequisition.Width())
+ {
+ sal_Int32 nExpandables = 0;
+ for (sal_Int32 x = 0; x < nMaxX; ++x)
+ if (aWidths[x].m_bExpand)
+ ++nExpandables;
+ tools::Long nExtraWidthForExpanders = nExpandables ? (rAllocation.Width() - aRequisition.Width()) / nExpandables : 0;
+
+ //We don't fit and there is no volunteer to be shrunk
+ if (!nExpandables && rAllocation.Width() < aRequisition.Width())
+ {
+ //first reduce spacing
+ while (nColSpacing)
+ {
+ nColSpacing /= 2;
+ aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing);
+ if (aRequisition.Width() <= rAllocation.Width())
+ break;
+ }
+
+ //share out the remaining pain to everyone
+ tools::Long nExtraWidth = (rAllocation.Width() - aRequisition.Width()) / nMaxX;
+
+ for (sal_Int32 x = 0; x < nMaxX; ++x)
+ aWidths[x].m_nValue += nExtraWidth;
+ }
+
+ if (nExtraWidthForExpanders)
+ {
+ for (sal_Int32 x = 0; x < nMaxX; ++x)
+ if (aWidths[x].m_bExpand)
+ aWidths[x].m_nValue += nExtraWidthForExpanders;
+ }
+ }
+
+ tools::Long nAvailableHeight = rAllocation.Height();
+ if (nMaxY)
+ nAvailableHeight -= nRowSpacing * (nMaxY - 1);
+ if (get_row_homogeneous())
+ {
+ for (sal_Int32 y = 0; y < nMaxY; ++y)
+ aHeights[y].m_nValue = nAvailableHeight/nMaxY;
+ }
+ else if (rAllocation.Height() != aRequisition.Height())
+ {
+ sal_Int32 nExpandables = 0;
+ for (sal_Int32 y = 0; y < nMaxY; ++y)
+ if (aHeights[y].m_bExpand)
+ ++nExpandables;
+ tools::Long nExtraHeightForExpanders = nExpandables ? (rAllocation.Height() - aRequisition.Height()) / nExpandables : 0;
+
+ //We don't fit and there is no volunteer to be shrunk
+ if (!nExpandables && rAllocation.Height() < aRequisition.Height())
+ {
+ //first reduce spacing
+ while (nRowSpacing)
+ {
+ nRowSpacing /= 2;
+ aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing);
+ if (aRequisition.Height() <= rAllocation.Height())
+ break;
+ }
+
+ //share out the remaining pain to everyone
+ tools::Long nExtraHeight = (rAllocation.Height() - aRequisition.Height()) / nMaxY;
+
+ for (sal_Int32 y = 0; y < nMaxY; ++y)
+ aHeights[y].m_nValue += nExtraHeight;
+ }
+
+ if (nExtraHeightForExpanders)
+ {
+ for (sal_Int32 y = 0; y < nMaxY; ++y)
+ if (aHeights[y].m_bExpand)
+ aHeights[y].m_nValue += nExtraHeightForExpanders;
+ }
+ }
+
+ Point aAllocPos(0, 0);
+ for (sal_Int32 x = 0; x < nMaxX; ++x)
+ {
+ for (sal_Int32 y = 0; y < nMaxY; ++y)
+ {
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdangling-reference"
+#endif
+ GridEntry &rEntry = A[x][y];
+#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
+#pragma GCC diagnostic pop
+#endif
+ vcl::Window *pChild = rEntry.pChild;
+ if (pChild)
+ {
+ Size aChildAlloc(0, 0);
+
+ sal_Int32 nWidth = rEntry.nSpanWidth;
+ for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
+ aChildAlloc.AdjustWidth(aWidths[x+nSpanX].m_nValue );
+ aChildAlloc.AdjustWidth(nColSpacing*(nWidth-1) );
+
+ sal_Int32 nHeight = rEntry.nSpanHeight;
+ for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
+ aChildAlloc.AdjustHeight(aHeights[y+nSpanY].m_nValue );
+ aChildAlloc.AdjustHeight(nRowSpacing*(nHeight-1) );
+
+ setLayoutAllocation(*pChild, aAllocPos, aChildAlloc);
+ }
+ aAllocPos.AdjustY(aHeights[y].m_nValue + nRowSpacing );
+ }
+ aAllocPos.AdjustX(aWidths[x].m_nValue + nColSpacing );
+ aAllocPos.setY( 0 );
+ }
+}
+
+void VclGrid::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ VclContainer::DumpAsPropertyTree(rJsonWriter);
+ rJsonWriter.put("type", "grid");
+}
+
+bool toBool(std::u16string_view rValue)
+{
+ return (!rValue.empty() && (rValue[0] == 't' || rValue[0] == 'T' || rValue[0] == '1'));
+}
+
+bool VclGrid::set_property(const OUString &rKey, const OUString &rValue)
+{
+ if (rKey == "row-spacing")
+ set_row_spacing(rValue.toInt32());
+ else if (rKey == "column-spacing")
+ set_column_spacing(rValue.toInt32());
+ else if (rKey == "row-homogeneous")
+ m_bRowHomogeneous = toBool(rValue);
+ else if (rKey == "column-homogeneous")
+ m_bColumnHomogeneous = toBool(rValue);
+ else if (rKey == "n-rows")
+ /*nothing to do*/;
+ else
+ return VclContainer::set_property(rKey, rValue);
+ return true;
+}
+
+const vcl::Window *VclBin::get_child() const
+{
+ const WindowImpl* pWindowImpl = ImplGetWindowImpl();
+
+ return pWindowImpl->mpFirstChild;
+}
+
+vcl::Window *VclBin::get_child()
+{
+ return const_cast<vcl::Window*>(const_cast<const VclBin*>(this)->get_child());
+}
+
+Size VclBin::calculateRequisition() const
+{
+ const vcl::Window *pChild = get_child();
+ if (pChild && pChild->IsVisible())
+ return getLayoutRequisition(*pChild);
+ return Size(0, 0);
+}
+
+void VclBin::setAllocation(const Size &rAllocation)
+{
+ vcl::Window *pChild = get_child();
+ if (pChild && pChild->IsVisible())
+ setLayoutAllocation(*pChild, Point(0, 0), rAllocation);
+}
+
+VclFrame::~VclFrame()
+{
+ disposeOnce();
+}
+
+void VclFrame::dispose()
+{
+ m_pLabel.clear();
+ VclBin::dispose();
+}
+
+//To-Do, hook a DecorationView into VclFrame ?
+
+Size VclFrame::calculateRequisition() const
+{
+ Size aRet(0, 0);
+
+ const vcl::Window *pChild = get_child();
+ const vcl::Window *pLabel = get_label_widget();
+
+ if (pChild && pChild->IsVisible())
+ aRet = getLayoutRequisition(*pChild);
+
+ if (pLabel && pLabel->IsVisible())
+ {
+ Size aLabelSize = getLayoutRequisition(*pLabel);
+ aRet.AdjustHeight(aLabelSize.Height() );
+ aRet.setWidth( std::max(aLabelSize.Width(), aRet.Width()) );
+ }
+
+ return aRet;
+}
+
+void VclFrame::setAllocation(const Size &rAllocation)
+{
+ //SetBackground( Color(0xFF, 0x00, 0xFF) );
+
+ Size aAllocation(rAllocation);
+ Point aChildPos;
+
+ vcl::Window *pChild = get_child();
+ vcl::Window *pLabel = get_label_widget();
+
+ if (pLabel && pLabel->IsVisible())
+ {
+ Size aLabelSize = getLayoutRequisition(*pLabel);
+ aLabelSize.setHeight( std::min(aLabelSize.Height(), aAllocation.Height()) );
+ aLabelSize.setWidth( std::min(aLabelSize.Width(), aAllocation.Width()) );
+ setLayoutAllocation(*pLabel, aChildPos, aLabelSize);
+ aAllocation.AdjustHeight( -(aLabelSize.Height()) );
+ aChildPos.AdjustY(aLabelSize.Height() );
+ }
+
+ if (pChild && pChild->IsVisible())
+ setLayoutAllocation(*pChild, aChildPos, aAllocation);
+}
+
+IMPL_LINK(VclFrame, WindowEventListener, VclWindowEvent&, rEvent, void)
+{
+ if (rEvent.GetId() == VclEventId::ObjectDying)
+ designate_label(nullptr);
+}
+
+void VclFrame::designate_label(vcl::Window *pWindow)
+{
+ assert(!pWindow || pWindow->GetParent() == this);
+ if (m_pLabel)
+ m_pLabel->RemoveEventListener(LINK(this, VclFrame, WindowEventListener));
+ m_pLabel = pWindow;
+ if (m_pLabel)
+ m_pLabel->AddEventListener(LINK(this, VclFrame, WindowEventListener));
+}
+
+const vcl::Window *VclFrame::get_label_widget() const
+{
+ if (m_pLabel)
+ return m_pLabel;
+ assert(GetChildCount() <= 2);
+ //The label widget is normally the first (of two) children
+ const WindowImpl* pWindowImpl = ImplGetWindowImpl();
+ if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //no label exists
+ return nullptr;
+ return pWindowImpl->mpFirstChild;
+}
+
+vcl::Window *VclFrame::get_label_widget()
+{
+ return const_cast<vcl::Window*>(const_cast<const VclFrame*>(this)->get_label_widget());
+}
+
+const vcl::Window *VclFrame::get_child() const
+{
+ //The child widget is the normally the last (of two) children
+ const WindowImpl* pWindowImpl = ImplGetWindowImpl();
+ assert(GetChildCount() == 2 || pWindowImpl->mbInDispose);
+ if (!m_pLabel)
+ return pWindowImpl->mpLastChild;
+ if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //only label exists
+ return nullptr;
+ return pWindowImpl->mpLastChild;
+}
+
+vcl::Window *VclFrame::get_child()
+{
+ return const_cast<vcl::Window*>(const_cast<const VclFrame*>(this)->get_child());
+}
+
+void VclFrame::set_label(const OUString &rLabel)
+{
+ vcl::Window *pLabel = get_label_widget();
+ assert(pLabel);
+ pLabel->SetText(rLabel);
+}
+
+OUString VclFrame::get_label() const
+{
+ const vcl::Window *pLabel = get_label_widget();
+ assert(pLabel);
+ return pLabel->GetText();
+}
+
+OUString VclFrame::getDefaultAccessibleName() const
+{
+ const vcl::Window *pLabel = get_label_widget();
+ if (pLabel)
+ return pLabel->GetAccessibleName();
+ return VclBin::getDefaultAccessibleName();
+}
+
+void VclFrame::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ VclBin::DumpAsPropertyTree(rJsonWriter);
+ rJsonWriter.put("type", "frame");
+}
+
+class DisclosureButton final : public CheckBox
+{
+ virtual void ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext) override
+ {
+ /* HACK: DisclosureButton is currently assuming, that the disclosure sign
+ will fit into the rectangle occupied by a normal checkbox on all themes.
+ If this does not hold true for some theme, ImplGetCheckImageSize
+ would have to be overridden for DisclosureButton; also GetNativeControlRegion
+ for ControlType::ListNode would have to be implemented and taken into account
+ */
+
+ tools::Rectangle aStateRect(GetStateRect());
+
+ ImplControlValue aControlValue(GetState() == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off);
+ tools::Rectangle aCtrlRegion(aStateRect);
+ ControlState nState = ControlState::NONE;
+
+ if (HasFocus())
+ nState |= ControlState::FOCUSED;
+ if (GetButtonState() & DrawButtonFlags::Default)
+ nState |= ControlState::DEFAULT;
+ if (Window::IsEnabled())
+ nState |= ControlState::ENABLED;
+ if (IsMouseOver() && GetMouseRect().Contains(GetPointerPosPixel()))
+ nState |= ControlState::ROLLOVER;
+
+ if (rRenderContext.DrawNativeControl(ControlType::ListNode, ControlPart::Entire, aCtrlRegion,
+ nState, aControlValue, OUString()))
+ return;
+
+ ImplSVCtrlData& rCtrlData(ImplGetSVData()->maCtrlData);
+ if (!rCtrlData.moDisclosurePlus)
+ rCtrlData.moDisclosurePlus.emplace(StockImage::Yes, SV_DISCLOSURE_PLUS);
+ if (!rCtrlData.moDisclosureMinus)
+ rCtrlData.moDisclosureMinus.emplace(StockImage::Yes, SV_DISCLOSURE_MINUS);
+
+ Image* pImg
+ = IsChecked() ? &*rCtrlData.moDisclosureMinus : &*rCtrlData.moDisclosurePlus;
+
+ DrawImageFlags nStyle = DrawImageFlags::NONE;
+ if (!IsEnabled())
+ nStyle |= DrawImageFlags::Disable;
+
+ Size aSize(aStateRect.GetSize());
+ Size aImgSize(pImg->GetSizePixel());
+ Point aOff((aSize.Width() - aImgSize.Width()) / 2,
+ (aSize.Height() - aImgSize.Height()) / 2);
+ aOff += aStateRect.TopLeft();
+ rRenderContext.DrawImage(aOff, *pImg, nStyle);
+ }
+
+public:
+ explicit DisclosureButton(vcl::Window* pParent)
+ : CheckBox(pParent, 0)
+ {
+ }
+
+ virtual void KeyInput( const KeyEvent& rKEvt ) override
+ {
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+
+ if( !aKeyCode.GetModifier() &&
+ ( ( aKeyCode.GetCode() == KEY_ADD ) ||
+ ( aKeyCode.GetCode() == KEY_SUBTRACT ) )
+ )
+ {
+ Check( aKeyCode.GetCode() == KEY_ADD );
+ }
+ else
+ CheckBox::KeyInput( rKEvt );
+ }
+};
+
+VclExpander::VclExpander(vcl::Window *pParent)
+ : VclBin(pParent)
+ , m_bResizeTopLevel(false)
+ , m_pDisclosureButton(VclPtr<DisclosureButton>::Create(this))
+{
+ m_pDisclosureButton->SetToggleHdl(LINK(this, VclExpander, ClickHdl));
+ m_pDisclosureButton->Show();
+}
+
+VclExpander::~VclExpander()
+{
+ disposeOnce();
+}
+
+bool VclExpander::get_expanded() const
+{
+ return m_pDisclosureButton->IsChecked();
+}
+
+void VclExpander::set_expanded(bool bExpanded)
+{
+ m_pDisclosureButton->Check(bExpanded);
+}
+
+void VclExpander::set_label(const OUString& rLabel)
+{
+ m_pDisclosureButton->SetText(rLabel);
+}
+
+OUString VclExpander::get_label() const
+{
+ return m_pDisclosureButton->GetText();
+}
+
+void VclExpander::dispose()
+{
+ m_pDisclosureButton.disposeAndClear();
+ VclBin::dispose();
+}
+
+const vcl::Window *VclExpander::get_child() const
+{
+ const WindowImpl* pWindowImpl = ImplGetWindowImpl();
+
+ assert(pWindowImpl->mpFirstChild == m_pDisclosureButton);
+
+ return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next);
+}
+
+vcl::Window *VclExpander::get_child()
+{
+ return const_cast<vcl::Window*>(const_cast<const VclExpander*>(this)->get_child());
+}
+
+Size VclExpander::calculateRequisition() const
+{
+ Size aRet(0, 0);
+
+ WindowImpl* pWindowImpl = ImplGetWindowImpl();
+
+ const vcl::Window *pChild = get_child();
+ const vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild ? pWindowImpl->mpLastChild.get() : nullptr;
+
+ if (pChild && pChild->IsVisible() && m_pDisclosureButton->IsChecked())
+ aRet = getLayoutRequisition(*pChild);
+
+ Size aExpanderSize = getLayoutRequisition(*m_pDisclosureButton);
+
+ if (pLabel && pLabel->IsVisible())
+ {
+ Size aLabelSize = getLayoutRequisition(*pLabel);
+ aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) );
+ aExpanderSize.AdjustWidth(aLabelSize.Width() );
+ }
+
+ aRet.AdjustHeight(aExpanderSize.Height() );
+ aRet.setWidth( std::max(aExpanderSize.Width(), aRet.Width()) );
+
+ return aRet;
+}
+
+void VclExpander::setAllocation(const Size &rAllocation)
+{
+ Size aAllocation(rAllocation);
+ Point aChildPos;
+
+ WindowImpl* pWindowImpl = ImplGetWindowImpl();
+
+ //The label widget is the last (of two) children
+ vcl::Window *pChild = get_child();
+ vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild.get() ? pWindowImpl->mpLastChild.get() : nullptr;
+
+ Size aButtonSize = getLayoutRequisition(*m_pDisclosureButton);
+ Size aLabelSize;
+ Size aExpanderSize = aButtonSize;
+ if (pLabel && pLabel->IsVisible())
+ {
+ aLabelSize = getLayoutRequisition(*pLabel);
+ aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) );
+ aExpanderSize.AdjustWidth(aLabelSize.Width() );
+ }
+
+ aExpanderSize.setHeight( std::min(aExpanderSize.Height(), aAllocation.Height()) );
+ aExpanderSize.setWidth( std::min(aExpanderSize.Width(), aAllocation.Width()) );
+
+ aButtonSize.setHeight( std::min(aButtonSize.Height(), aExpanderSize.Height()) );
+ aButtonSize.setWidth( std::min(aButtonSize.Width(), aExpanderSize.Width()) );
+
+ tools::Long nExtraExpanderHeight = aExpanderSize.Height() - aButtonSize.Height();
+ Point aButtonPos(aChildPos.X(), aChildPos.Y() + nExtraExpanderHeight/2);
+ setLayoutAllocation(*m_pDisclosureButton, aButtonPos, aButtonSize);
+
+ if (pLabel && pLabel->IsVisible())
+ {
+ aLabelSize.setHeight( std::min(aLabelSize.Height(), aExpanderSize.Height()) );
+ aLabelSize.setWidth( std::min(aLabelSize.Width(),
+ aExpanderSize.Width() - aButtonSize.Width()) );
+
+ tools::Long nExtraLabelHeight = aExpanderSize.Height() - aLabelSize.Height();
+ Point aLabelPos(aChildPos.X() + aButtonSize.Width(), aChildPos.Y() + nExtraLabelHeight/2);
+ setLayoutAllocation(*pLabel, aLabelPos, aLabelSize);
+ }
+
+ aAllocation.AdjustHeight( -(aExpanderSize.Height()) );
+ aChildPos.AdjustY(aExpanderSize.Height() );
+
+ if (pChild && pChild->IsVisible())
+ {
+ if (!m_pDisclosureButton->IsChecked())
+ aAllocation = Size();
+ setLayoutAllocation(*pChild, aChildPos, aAllocation);
+ }
+}
+
+bool VclExpander::set_property(const OUString &rKey, const OUString &rValue)
+{
+ if (rKey == "expanded")
+ set_expanded(toBool(rValue));
+ else if (rKey == "resize-toplevel")
+ m_bResizeTopLevel = toBool(rValue);
+ else
+ return VclBin::set_property(rKey, rValue);
+ return true;
+}
+
+void VclExpander::StateChanged(StateChangedType nType)
+{
+ VclBin::StateChanged( nType );
+
+ if (nType == StateChangedType::InitShow)
+ {
+ vcl::Window *pChild = get_child();
+ if (pChild)
+ pChild->Show(m_pDisclosureButton->IsChecked());
+ }
+}
+
+const vcl::Window *VclExpander::get_label_widget() const
+{
+ return m_pDisclosureButton;
+}
+
+vcl::Window *VclExpander::get_label_widget()
+{
+ return const_cast<vcl::Window*>(const_cast<const VclExpander*>(this)->get_label_widget());
+}
+
+void VclExpander::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ VclContainer::DumpAsPropertyTree(rJsonWriter);
+ rJsonWriter.put("type", "expander");
+}
+
+FactoryFunction VclExpander::GetUITestFactory() const
+{
+ return ExpanderUIObject::create;
+}
+
+IMPL_LINK( VclExpander, ClickHdl, CheckBox&, rBtn, void )
+{
+ vcl::Window *pChild = get_child();
+ if (pChild)
+ {
+ pChild->Show(rBtn.IsChecked());
+ queue_resize();
+ Dialog* pResizeDialog = m_bResizeTopLevel ? GetParentDialog() : nullptr;
+ if (pResizeDialog)
+ pResizeDialog->setOptimalLayoutSize(true);
+ }
+ maExpandedHdl.Call(*this);
+}
+
+VclScrolledWindow::VclScrolledWindow(vcl::Window *pParent)
+ : VclBin(pParent, WB_HIDE | WB_CLIPCHILDREN | WB_AUTOHSCROLL | WB_AUTOVSCROLL | WB_TABSTOP)
+ , m_bUserManagedScrolling(false)
+ , m_eDrawFrameStyle(DrawFrameStyle::NONE)
+ , m_eDrawFrameFlags(DrawFrameFlags::WindowBorder)
+ , m_pVScroll(VclPtr<ScrollBar>::Create(this, WB_HIDE | WB_VERT))
+ , m_pHScroll(VclPtr<ScrollBar>::Create(this, WB_HIDE | WB_HORZ))
+ , m_aScrollBarBox(VclPtr<ScrollBarBox>::Create(this, WB_HIDE))
+{
+ SetType(WindowType::SCROLLWINDOW);
+
+ AllSettings aAllSettings = GetSettings();
+ StyleSettings aStyle = aAllSettings.GetStyleSettings();
+ aStyle.SetMonoColor(aStyle.GetShadowColor());
+ aAllSettings.SetStyleSettings(aStyle);
+ GetOutDev()->SetSettings(aAllSettings);
+
+ Link<ScrollBar*,void> aLink( LINK( this, VclScrolledWindow, ScrollBarHdl ) );
+ m_pVScroll->SetScrollHdl(aLink);
+ m_pHScroll->SetScrollHdl(aLink);
+
+ m_nBorderWidth = CalcBorderWidth();
+}
+
+int VclScrolledWindow::CalcBorderWidth() const
+{
+ if (m_eDrawFrameStyle == DrawFrameStyle::NONE)
+ return 0;
+ const tools::Rectangle aRect(tools::Rectangle(Point(0, 0), Size(100, 100)));
+ DecorationView aDecoView(const_cast<OutputDevice*>(GetOutDev()));
+ // don't actually draw anything, just measure what size it would be and the diff is the desired border size to reserve
+ const tools::Rectangle aContentRect = aDecoView.DrawFrame(aRect, m_eDrawFrameStyle, m_eDrawFrameFlags | DrawFrameFlags::NoDraw);
+ const auto nBorderWidth = (aRect.GetWidth() - aContentRect.GetWidth()) / 2;
+ return std::max<int>(nBorderWidth, 1);
+}
+
+void VclScrolledWindow::dispose()
+{
+ m_pVScroll.disposeAndClear();
+ m_pHScroll.disposeAndClear();
+ m_aScrollBarBox.disposeAndClear();
+ VclBin::dispose();
+}
+
+IMPL_LINK_NOARG(VclScrolledWindow, ScrollBarHdl, ScrollBar*, void)
+{
+ vcl::Window *pChild = get_child();
+ if (!pChild)
+ return;
+
+ assert(dynamic_cast<VclViewport*>(pChild) && "scrolledwindow child should be a Viewport");
+
+ pChild = pChild->GetWindow(GetWindowType::FirstChild);
+
+ if (!pChild)
+ return;
+
+ Point aWinPos(-m_pHScroll->GetThumbPos(), -m_pVScroll->GetThumbPos());
+ pChild->SetPosPixel(aWinPos);
+}
+
+const vcl::Window *VclScrolledWindow::get_child() const
+{
+ const WindowImpl* pWindowImpl = ImplGetWindowImpl();
+ assert(GetChildCount() == 4 || pWindowImpl->mbInDispose);
+ return pWindowImpl->mpLastChild;
+}
+
+vcl::Window *VclScrolledWindow::get_child()
+{
+ return const_cast<vcl::Window*>(const_cast<const VclScrolledWindow*>(this)->get_child());
+}
+
+Size VclScrolledWindow::calculateRequisition() const
+{
+ Size aRet(0, 0);
+
+ const vcl::Window *pChild = get_child();
+ if (pChild && pChild->IsVisible())
+ aRet = getLayoutRequisition(*pChild);
+
+ if (GetStyle() & WB_VSCROLL)
+ aRet.AdjustWidth(getLayoutRequisition(*m_pVScroll).Width() );
+
+ if (GetStyle() & WB_HSCROLL)
+ aRet.AdjustHeight(getLayoutRequisition(*m_pHScroll).Height() );
+
+ aRet.AdjustHeight(2 * m_nBorderWidth);
+ aRet.AdjustWidth(2 * m_nBorderWidth);
+
+ return aRet;
+}
+
+void VclScrolledWindow::InitScrollBars(const Size &rRequest)
+{
+ const vcl::Window *pChild = get_child();
+ if (!pChild || !pChild->IsVisible())
+ return;
+
+ Size aOutSize(getVisibleChildSize());
+
+ m_pVScroll->SetRangeMax(rRequest.Height());
+ m_pVScroll->SetVisibleSize(aOutSize.Height());
+ m_pVScroll->SetPageSize(16);
+
+ m_pHScroll->SetRangeMax(rRequest.Width());
+ m_pHScroll->SetVisibleSize(aOutSize.Width());
+ m_pHScroll->SetPageSize(16);
+
+ m_pVScroll->Scroll();
+ m_pHScroll->Scroll();
+}
+
+void VclScrolledWindow::doSetAllocation(const Size &rAllocation, bool bRetryOnFailure)
+{
+ Size aChildReq;
+
+ vcl::Window *pChild = get_child();
+ if (pChild && pChild->IsVisible())
+ aChildReq = getLayoutRequisition(*pChild);
+
+ tools::Long nAvailHeight = rAllocation.Height() - 2 * m_nBorderWidth;
+ tools::Long nAvailWidth = rAllocation.Width() - 2 * m_nBorderWidth;
+
+ // vert. ScrollBar
+ bool bShowVScroll;
+ if (GetStyle() & WB_AUTOVSCROLL)
+ bShowVScroll = nAvailHeight < aChildReq.Height();
+ else
+ bShowVScroll = (GetStyle() & WB_VSCROLL) != 0;
+
+ if (bShowVScroll)
+ nAvailWidth -= getLayoutRequisition(*m_pVScroll).Width();
+
+ // horz. ScrollBar
+ bool bShowHScroll;
+ if (GetStyle() & WB_AUTOHSCROLL)
+ {
+ bShowHScroll = nAvailWidth < aChildReq.Width();
+
+ if (bShowHScroll)
+ nAvailHeight -= getLayoutRequisition(*m_pHScroll).Height();
+
+ if (GetStyle() & WB_AUTOVSCROLL)
+ bShowVScroll = nAvailHeight < aChildReq.Height();
+ }
+ else
+ bShowHScroll = (GetStyle() & WB_HSCROLL) != 0;
+
+ if (m_pHScroll->IsVisible() != bShowHScroll)
+ m_pHScroll->Show(bShowHScroll);
+ if (m_pVScroll->IsVisible() != bShowVScroll)
+ m_pVScroll->Show(bShowVScroll);
+
+ Size aInnerSize(rAllocation);
+ aInnerSize.AdjustWidth(-2 * m_nBorderWidth);
+ aInnerSize.AdjustHeight(-2 * m_nBorderWidth);
+
+ bool bBothVisible = m_pVScroll->IsVisible() && m_pHScroll->IsVisible();
+ auto nScrollBarWidth = getLayoutRequisition(*m_pVScroll).Width();
+ auto nScrollBarHeight = getLayoutRequisition(*m_pHScroll).Height();
+
+ if (m_pVScroll->IsVisible())
+ {
+ Point aScrollPos(rAllocation.Width() - nScrollBarWidth - m_nBorderWidth, m_nBorderWidth);
+ Size aScrollSize(nScrollBarWidth, rAllocation.Height() - 2 * m_nBorderWidth);
+ if (bBothVisible)
+ aScrollSize.AdjustHeight(-nScrollBarHeight);
+ setLayoutAllocation(*m_pVScroll, aScrollPos, aScrollSize);
+ aInnerSize.AdjustWidth( -nScrollBarWidth );
+ }
+
+ if (m_pHScroll->IsVisible())
+ {
+ Point aScrollPos(m_nBorderWidth, rAllocation.Height() - nScrollBarHeight);
+ Size aScrollSize(rAllocation.Width() - 2 * m_nBorderWidth, nScrollBarHeight);
+ if (bBothVisible)
+ aScrollSize.AdjustWidth(-nScrollBarWidth);
+ setLayoutAllocation(*m_pHScroll, aScrollPos, aScrollSize);
+ aInnerSize.AdjustHeight( -nScrollBarHeight );
+ }
+
+ if (bBothVisible)
+ {
+ Point aBoxPos(aInnerSize.Width() + m_nBorderWidth, aInnerSize.Height() + m_nBorderWidth);
+ m_aScrollBarBox->SetPosSizePixel(aBoxPos, Size(nScrollBarWidth, nScrollBarHeight));
+ m_aScrollBarBox->Show();
+ }
+ else
+ {
+ m_aScrollBarBox->Hide();
+ }
+
+ if (pChild && pChild->IsVisible())
+ {
+ assert(dynamic_cast<VclViewport*>(pChild) && "scrolledwindow child should be a Viewport");
+
+ WinBits nOldBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL));
+
+ setLayoutAllocation(*pChild, Point(m_nBorderWidth, m_nBorderWidth), aInnerSize);
+
+ // tdf#128758 if the layout allocation triggered some callback that
+ // immediately invalidates the layout by adding scrollbars then
+ // normally this would simply retrigger layout and another toplevel
+ // attempt is made later. But the initial layout attempt blocks
+ // relayouts, so just make another single effort here.
+ WinBits nNewBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL));
+ if (nOldBits != nNewBits && bRetryOnFailure)
+ {
+ doSetAllocation(rAllocation, false);
+ return;
+ }
+ }
+
+ if (!m_bUserManagedScrolling)
+ InitScrollBars(aChildReq);
+}
+
+void VclScrolledWindow::setAllocation(const Size &rAllocation)
+{
+ doSetAllocation(rAllocation, true);
+}
+
+Size VclScrolledWindow::getVisibleChildSize() const
+{
+ Size aRet(GetSizePixel());
+ if (m_pVScroll->IsVisible())
+ aRet.AdjustWidth( -(m_pVScroll->GetSizePixel().Width()) );
+ if (m_pHScroll->IsVisible())
+ aRet.AdjustHeight( -(m_pHScroll->GetSizePixel().Height()) );
+ aRet.AdjustHeight(-2 * m_nBorderWidth);
+ aRet.AdjustWidth(-2 * m_nBorderWidth);
+ return aRet;
+}
+
+bool VclScrolledWindow::set_property(const OUString &rKey, const OUString &rValue)
+{
+ if (rKey == "shadow-type" || rKey == "name")
+ {
+ if (rKey == "shadow-type")
+ {
+ // despite the style names, this looks like the best mapping
+ if (rValue == "in")
+ m_eDrawFrameStyle = DrawFrameStyle::Out;
+ else if (rValue == "out")
+ m_eDrawFrameStyle = DrawFrameStyle::In;
+ else if (rValue == "etched-in")
+ m_eDrawFrameStyle = DrawFrameStyle::DoubleOut;
+ else if (rValue == "etched-out")
+ m_eDrawFrameStyle = DrawFrameStyle::DoubleIn;
+ else if (rValue == "none")
+ m_eDrawFrameStyle = DrawFrameStyle::NONE;
+ }
+ else if (rKey == "name")
+ {
+ m_eDrawFrameFlags = DrawFrameFlags::WindowBorder;
+ if (rValue == "monoborder")
+ m_eDrawFrameFlags |= DrawFrameFlags::Mono;
+ }
+
+ auto nBorderWidth = CalcBorderWidth();
+ if (m_nBorderWidth != nBorderWidth)
+ {
+ m_nBorderWidth = nBorderWidth;
+ queue_resize();
+ }
+
+ return true;
+ }
+
+ bool bRet = VclBin::set_property(rKey, rValue);
+ m_pVScroll->Show((GetStyle() & WB_VSCROLL) != 0);
+ m_pHScroll->Show((GetStyle() & WB_HSCROLL) != 0);
+ return bRet;
+}
+
+bool VclScrolledWindow::EventNotify(NotifyEvent& rNEvt)
+{
+ bool bDone = false;
+ if ( rNEvt.GetType() == NotifyEventType::COMMAND )
+ {
+ const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
+ if ( rCEvt.GetCommand() == CommandEventId::Wheel )
+ {
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
+ {
+ // tdf#140537 only handle scroll commands in the valid shown scrollbars
+ bDone = HandleScrollCommand(rCEvt,
+ m_pHScroll->IsVisible() ? m_pHScroll : nullptr,
+ m_pVScroll->IsVisible() ? m_pVScroll : nullptr);
+ }
+ }
+ }
+
+ return bDone || VclBin::EventNotify( rNEvt );
+}
+
+void VclScrolledWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ VclBin::Paint(rRenderContext, rRect);
+ if (m_eDrawFrameStyle == DrawFrameStyle::NONE)
+ return;
+ const tools::Rectangle aRect(tools::Rectangle(Point(0,0), GetSizePixel()));
+ DecorationView aDecoView(&rRenderContext);
+ const tools::Rectangle aContentRect = aDecoView.DrawFrame(aRect, m_eDrawFrameStyle, m_eDrawFrameFlags);
+ const auto nBorderWidth = (aRect.GetWidth() - aContentRect.GetWidth()) / 2;
+ SAL_WARN_IF(nBorderWidth > m_nBorderWidth, "vcl.layout", "desired border at paint " <<
+ nBorderWidth << " is larger than expected " << m_nBorderWidth);
+}
+
+namespace {
+void lcl_dumpScrollbar(::tools::JsonWriter& rJsonWriter, ScrollBar& rScrollBar)
+{
+ rJsonWriter.put("lower", rScrollBar.GetRangeMin());
+ rJsonWriter.put("upper", rScrollBar.GetRangeMax());
+ rJsonWriter.put("step_increment", rScrollBar.GetLineSize());
+ rJsonWriter.put("page_increment", rScrollBar.GetPageSize());
+ rJsonWriter.put("value", rScrollBar.GetThumbPos());
+ rJsonWriter.put("page_size", rScrollBar.GetVisibleSize());
+}
+};
+
+void VclScrolledWindow::DumpAsPropertyTree(::tools::JsonWriter& rJsonWriter)
+{
+ VclBin::DumpAsPropertyTree(rJsonWriter);
+
+ rJsonWriter.put("user_managed_scrolling", m_bUserManagedScrolling);
+
+ {
+ auto aVertical = rJsonWriter.startNode("vertical");
+
+ ScrollBar& rScrollBar = getVertScrollBar();
+ lcl_dumpScrollbar(rJsonWriter, rScrollBar);
+
+ WinBits nWinBits = GetStyle();
+ if (nWinBits & WB_VSCROLL)
+ rJsonWriter.put("policy", "always");
+ else if (nWinBits & WB_AUTOVSCROLL)
+ rJsonWriter.put("policy", "auto");
+ else
+ rJsonWriter.put("policy", "never");
+ }
+
+ {
+ auto aHorizontal = rJsonWriter.startNode("horizontal");
+
+ ScrollBar& rScrollBar = getHorzScrollBar();
+ lcl_dumpScrollbar(rJsonWriter, rScrollBar);
+
+ WinBits nWinBits = GetStyle();
+ if (nWinBits & WB_HSCROLL)
+ rJsonWriter.put("policy", "always");
+ else if (nWinBits & WB_AUTOHSCROLL)
+ rJsonWriter.put("policy", "auto");
+ else
+ rJsonWriter.put("policy", "never");
+ }
+}
+
+void VclViewport::setAllocation(const Size &rAllocation)
+{
+ vcl::Window *pChild = get_child();
+ if (!(pChild && pChild->IsVisible()))
+ return;
+
+ Size aReq(getLayoutRequisition(*pChild));
+ aReq.setWidth( std::max(aReq.Width(), rAllocation.Width()) );
+ aReq.setHeight( std::max(aReq.Height(), rAllocation.Height()) );
+ Point aKeepPos(pChild->GetPosPixel());
+ if (m_bInitialAllocation)
+ {
+ aKeepPos = Point(0, 0);
+ m_bInitialAllocation = false;
+ }
+ setLayoutAllocation(*pChild, aKeepPos, aReq);
+}
+
+const vcl::Window *VclEventBox::get_child() const
+{
+ const WindowImpl* pWindowImpl = ImplGetWindowImpl();
+
+ assert(pWindowImpl->mpFirstChild.get() == m_aEventBoxHelper.get());
+
+ return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next);
+}
+
+vcl::Window *VclEventBox::get_child()
+{
+ return const_cast<vcl::Window*>(const_cast<const VclEventBox*>(this)->get_child());
+}
+
+void VclEventBox::setAllocation(const Size& rAllocation)
+{
+ Point aChildPos(0, 0);
+ for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ setLayoutAllocation(*pChild, aChildPos, rAllocation);
+ }
+}
+
+Size VclEventBox::calculateRequisition() const
+{
+ Size aRet(0, 0);
+
+ for (const vcl::Window* pChild = get_child(); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ Size aChildSize = getLayoutRequisition(*pChild);
+ aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) );
+ aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) );
+ }
+
+ return aRet;
+}
+
+void VclEventBox::Command(const CommandEvent&)
+{
+ //discard events by default to block them reaching children
+}
+
+VclEventBox::~VclEventBox()
+{
+ disposeOnce();
+}
+
+void VclEventBox::dispose()
+{
+ m_aEventBoxHelper.disposeAndClear();
+ VclBin::dispose();
+}
+
+void VclSizeGroup::trigger_queue_resize()
+{
+ //sufficient to trigger one widget to trigger all of them
+ if (!m_aWindows.empty())
+ {
+ (*m_aWindows.begin())->queue_resize();
+ }
+}
+
+void VclSizeGroup::set_ignore_hidden(bool bIgnoreHidden)
+{
+ if (bIgnoreHidden != m_bIgnoreHidden)
+ {
+ m_bIgnoreHidden = bIgnoreHidden;
+ trigger_queue_resize();
+ }
+}
+
+void VclSizeGroup::set_mode(VclSizeGroupMode eMode)
+{
+ if (eMode != m_eMode)
+ {
+ m_eMode = eMode;
+ trigger_queue_resize();
+ }
+
+}
+
+void VclSizeGroup::set_property(const OUString &rKey, const OUString &rValue)
+{
+ if (rKey == "ignore-hidden")
+ set_ignore_hidden(toBool(rValue));
+ else if (rKey == "mode")
+ {
+ VclSizeGroupMode eMode = VclSizeGroupMode::Horizontal;
+ if (rValue == "none")
+ eMode = VclSizeGroupMode::NONE;
+ else if (rValue == "horizontal")
+ eMode = VclSizeGroupMode::Horizontal;
+ else if (rValue == "vertical")
+ eMode = VclSizeGroupMode::Vertical;
+ else if (rValue == "both")
+ eMode = VclSizeGroupMode::Both;
+ else
+ {
+ SAL_WARN("vcl.layout", "unknown size group mode" << rValue);
+ }
+ set_mode(eMode);
+ }
+ else
+ {
+ SAL_INFO("vcl.layout", "unhandled property: " << rKey);
+ }
+}
+
+void MessageDialog::create_message_area()
+{
+ setDeferredProperties();
+
+ if (m_pGrid)
+ return;
+
+ VclContainer *pContainer = get_content_area();
+ assert(pContainer);
+
+ m_pGrid.set( VclPtr<VclGrid>::Create(pContainer) );
+ m_pGrid->reorderWithinParent(0);
+ m_pGrid->set_column_spacing(12);
+ m_pMessageBox.set(VclPtr<VclVBox>::Create(m_pGrid));
+ m_pMessageBox->set_grid_left_attach(1);
+ m_pMessageBox->set_grid_top_attach(0);
+ m_pMessageBox->set_spacing(GetTextHeight());
+
+ m_pImage = VclPtr<FixedImage>::Create(m_pGrid, WB_CENTER | WB_VCENTER | WB_3DLOOK);
+ switch (m_eMessageType)
+ {
+ case VclMessageType::Info:
+ m_pImage->SetImage(GetStandardInfoBoxImage());
+ break;
+ case VclMessageType::Warning:
+ m_pImage->SetImage(GetStandardWarningBoxImage());
+ break;
+ case VclMessageType::Question:
+ m_pImage->SetImage(GetStandardQueryBoxImage());
+ break;
+ case VclMessageType::Error:
+ m_pImage->SetImage(GetStandardErrorBoxImage());
+ break;
+ case VclMessageType::Other:
+ break;
+ }
+ m_pImage->set_grid_left_attach(0);
+ m_pImage->set_grid_top_attach(0);
+ m_pImage->set_valign(VclAlign::Start);
+ m_pImage->Show(m_eMessageType != VclMessageType::Other);
+
+ WinBits nWinStyle = WB_CLIPCHILDREN | WB_LEFT | WB_VCENTER | WB_NOLABEL | WB_NOTABSTOP;
+
+ bool bHasSecondaryText = !m_sSecondaryString.isEmpty();
+
+ m_pPrimaryMessage = VclPtr<VclMultiLineEdit>::Create(m_pMessageBox, nWinStyle);
+ m_pPrimaryMessage->SetPaintTransparent(true);
+ m_pPrimaryMessage->EnableCursor(false);
+
+ m_pPrimaryMessage->set_hexpand(true);
+ m_pPrimaryMessage->SetText(m_sPrimaryString);
+ m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty());
+
+ m_pSecondaryMessage = VclPtr<VclMultiLineEdit>::Create(m_pMessageBox, nWinStyle);
+ m_pSecondaryMessage->SetPaintTransparent(true);
+ m_pSecondaryMessage->EnableCursor(false);
+ m_pSecondaryMessage->set_hexpand(true);
+ m_pSecondaryMessage->SetText(m_sSecondaryString);
+ m_pSecondaryMessage->Show(bHasSecondaryText);
+
+ MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, bHasSecondaryText ? m_pSecondaryMessage.get() : nullptr);
+
+ VclButtonBox *pButtonBox = get_action_area();
+ assert(pButtonBox);
+
+ VclPtr<PushButton> pBtn;
+ short nDefaultResponse = get_default_response();
+ switch (m_eButtonsType)
+ {
+ case VclButtonsType::NONE:
+ break;
+ case VclButtonsType::Ok:
+ pBtn.set( VclPtr<OKButton>::Create(pButtonBox) );
+ pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
+ pBtn->Show();
+ pBtn->set_id("ok");
+ add_button(pBtn, RET_OK, true);
+ nDefaultResponse = RET_OK;
+ break;
+ case VclButtonsType::Close:
+ pBtn.set( VclPtr<CloseButton>::Create(pButtonBox) );
+ pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
+ pBtn->Show();
+ pBtn->set_id("close");
+ add_button(pBtn, RET_CLOSE, true);
+ nDefaultResponse = RET_CLOSE;
+ break;
+ case VclButtonsType::Cancel:
+ pBtn.set( VclPtr<CancelButton>::Create(pButtonBox) );
+ pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
+ pBtn->Show();
+ pBtn->set_id("cancel");
+ add_button(pBtn, RET_CANCEL, true);
+ nDefaultResponse = RET_CANCEL;
+ break;
+ case VclButtonsType::YesNo:
+ pBtn = VclPtr<PushButton>::Create(pButtonBox);
+ pBtn->SetText(GetStandardText(StandardButtonType::Yes));
+ pBtn->Show();
+ pBtn->set_id("yes");
+ add_button(pBtn, RET_YES, true);
+
+ pBtn.set( VclPtr<PushButton>::Create(pButtonBox) );
+ pBtn->SetText(GetStandardText(StandardButtonType::No));
+ pBtn->Show();
+ pBtn->set_id("no");
+ add_button(pBtn, RET_NO, true);
+ nDefaultResponse = RET_NO;
+ break;
+ case VclButtonsType::OkCancel:
+ pBtn.set( VclPtr<OKButton>::Create(pButtonBox) );
+ pBtn->Show();
+ pBtn->set_id("ok");
+ add_button(pBtn, RET_OK, true);
+
+ pBtn.set( VclPtr<CancelButton>::Create(pButtonBox) );
+ pBtn->Show();
+ pBtn->set_id("cancel");
+ add_button(pBtn, RET_CANCEL, true);
+ nDefaultResponse = RET_CANCEL;
+ break;
+ }
+ set_default_response(nDefaultResponse);
+ sort_native_button_order(*pButtonBox);
+ m_pMessageBox->Show();
+ m_pGrid->Show();
+}
+
+void MessageDialog::create_owned_areas()
+{
+#if defined _WIN32
+ set_border_width(3);
+#else
+ set_border_width(12);
+#endif
+ m_pOwnedContentArea.set(VclPtr<VclVBox>::Create(this, false, 24));
+ set_content_area(m_pOwnedContentArea);
+ m_pOwnedContentArea->Show();
+ m_pOwnedActionArea.set( VclPtr<VclHButtonBox>::Create(m_pOwnedContentArea) );
+ set_action_area(m_pOwnedActionArea);
+ m_pOwnedActionArea->Show();
+}
+
+MessageDialog::MessageDialog(vcl::Window* pParent, WinBits nStyle)
+ : Dialog(pParent, nStyle)
+ , m_eButtonsType(VclButtonsType::NONE)
+ , m_eMessageType(VclMessageType::Info)
+ , m_pOwnedContentArea(nullptr)
+ , m_pOwnedActionArea(nullptr)
+ , m_pGrid(nullptr)
+ , m_pMessageBox(nullptr)
+ , m_pImage(nullptr)
+ , m_pPrimaryMessage(nullptr)
+ , m_pSecondaryMessage(nullptr)
+{
+ SetType(WindowType::MESSBOX);
+}
+
+MessageDialog::MessageDialog(vcl::Window* pParent,
+ OUString aMessage,
+ VclMessageType eMessageType,
+ VclButtonsType eButtonsType)
+ : Dialog(pParent, WB_MOVEABLE | WB_3DLOOK | WB_CLOSEABLE)
+ , m_eButtonsType(eButtonsType)
+ , m_eMessageType(eMessageType)
+ , m_pGrid(nullptr)
+ , m_pMessageBox(nullptr)
+ , m_pImage(nullptr)
+ , m_pPrimaryMessage(nullptr)
+ , m_pSecondaryMessage(nullptr)
+ , m_sPrimaryString(std::move(aMessage))
+{
+ SetType(WindowType::MESSBOX);
+ create_owned_areas();
+ create_message_area();
+
+ switch (m_eMessageType)
+ {
+ case VclMessageType::Info:
+ SetText(GetStandardInfoBoxText());
+ break;
+ case VclMessageType::Warning:
+ SetText(GetStandardWarningBoxText());
+ break;
+ case VclMessageType::Question:
+ SetText(GetStandardQueryBoxText());
+ break;
+ case VclMessageType::Error:
+ SetText(GetStandardErrorBoxText());
+ break;
+ case VclMessageType::Other:
+ SetText(Application::GetDisplayName());
+ break;
+ }
+}
+
+void MessageDialog::dispose()
+{
+ disposeOwnedButtons();
+ m_pPrimaryMessage.disposeAndClear();
+ m_pSecondaryMessage.disposeAndClear();
+ m_pImage.disposeAndClear();
+ m_pMessageBox.disposeAndClear();
+ m_pGrid.disposeAndClear();
+ m_pOwnedActionArea.disposeAndClear();
+ m_pOwnedContentArea.disposeAndClear();
+ Dialog::dispose();
+}
+
+MessageDialog::~MessageDialog()
+{
+ disposeOnce();
+}
+
+void MessageDialog::SetMessagesWidths(vcl::Window const *pParent,
+ VclMultiLineEdit *pPrimaryMessage, VclMultiLineEdit *pSecondaryMessage)
+{
+ if (pSecondaryMessage)
+ {
+ assert(pPrimaryMessage);
+ vcl::Font aFont = pParent->GetSettings().GetStyleSettings().GetLabelFont();
+ aFont.SetFontSize(Size(0, aFont.GetFontSize().Height() * 1.2));
+ aFont.SetWeight(WEIGHT_BOLD);
+ pPrimaryMessage->SetControlFont(aFont);
+ pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 44);
+ pSecondaryMessage->SetMaxTextWidth(pSecondaryMessage->approximate_char_width() * 60);
+ }
+ else
+ pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 60);
+}
+
+OUString const & MessageDialog::get_primary_text() const
+{
+ const_cast<MessageDialog*>(this)->setDeferredProperties();
+
+ return m_sPrimaryString;
+}
+
+OUString const & MessageDialog::get_secondary_text() const
+{
+ const_cast<MessageDialog*>(this)->setDeferredProperties();
+
+ return m_sSecondaryString;
+}
+
+bool MessageDialog::set_property(const OUString &rKey, const OUString &rValue)
+{
+ if (rKey == "text")
+ set_primary_text(rValue);
+ else if (rKey == "secondary-text")
+ set_secondary_text(rValue);
+ else if (rKey == "message-type")
+ {
+ VclMessageType eMode = VclMessageType::Info;
+ if (rValue == "info")
+ eMode = VclMessageType::Info;
+ else if (rValue == "warning")
+ eMode = VclMessageType::Warning;
+ else if (rValue == "question")
+ eMode = VclMessageType::Question;
+ else if (rValue == "error")
+ eMode = VclMessageType::Error;
+ else if (rValue == "other")
+ eMode = VclMessageType::Other;
+ else
+ {
+ SAL_WARN("vcl.layout", "unknown message type mode" << rValue);
+ }
+ m_eMessageType = eMode;
+ }
+ else if (rKey == "buttons")
+ {
+ VclButtonsType eMode = VclButtonsType::NONE;
+ if (rValue == "none")
+ eMode = VclButtonsType::NONE;
+ else if (rValue == "ok")
+ eMode = VclButtonsType::Ok;
+ else if (rValue == "cancel")
+ eMode = VclButtonsType::Cancel;
+ else if (rValue == "close")
+ eMode = VclButtonsType::Close;
+ else if (rValue == "yes-no")
+ eMode = VclButtonsType::YesNo;
+ else if (rValue == "ok-cancel")
+ eMode = VclButtonsType::OkCancel;
+ else
+ {
+ SAL_WARN("vcl.layout", "unknown buttons type mode" << rValue);
+ }
+ m_eButtonsType = eMode;
+ }
+ else
+ return Dialog::set_property(rKey, rValue);
+ return true;
+}
+
+void MessageDialog::set_primary_text(const OUString &rPrimaryString)
+{
+ m_sPrimaryString = rPrimaryString;
+ if (m_pPrimaryMessage)
+ {
+ m_pPrimaryMessage->SetText(m_sPrimaryString);
+ m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty());
+ MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr);
+ }
+}
+
+void MessageDialog::set_secondary_text(const OUString &rSecondaryString)
+{
+ m_sSecondaryString = rSecondaryString;
+ if (m_pSecondaryMessage)
+ {
+ m_pSecondaryMessage->SetText("\n" + m_sSecondaryString);
+ m_pSecondaryMessage->Show(!m_sSecondaryString.isEmpty());
+ MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr);
+ }
+}
+
+void MessageDialog::StateChanged(StateChangedType nType)
+{
+ Dialog::StateChanged(nType);
+ if (nType == StateChangedType::InitShow)
+ {
+ // MessageBox should be at least as wide as to see the title
+ auto nTitleWidth = CalcTitleWidth();
+ // Extra-Width for Close button
+ nTitleWidth += mpWindowImpl->mnTopBorder;
+ if (get_preferred_size().Width() < nTitleWidth)
+ {
+ set_width_request(nTitleWidth);
+ DoInitialLayout();
+ }
+ }
+}
+
+VclPaned::VclPaned(vcl::Window *pParent, bool bVertical)
+ : VclContainer(pParent, WB_HIDE | WB_CLIPCHILDREN)
+ , m_pSplitter(VclPtr<Splitter>::Create(this, bVertical ? WB_VSCROLL : WB_HSCROLL))
+ , m_nPosition(-1)
+{
+ m_pSplitter->SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetFaceColor()));
+ m_pSplitter->Show();
+}
+
+void VclPaned::dispose()
+{
+ m_pSplitter.disposeAndClear();
+ VclContainer::dispose();
+}
+
+VclVPaned::VclVPaned(vcl::Window *pParent)
+ : VclPaned(pParent, true)
+{
+ m_pSplitter->SetSplitHdl(LINK(this, VclVPaned, SplitHdl));
+}
+
+IMPL_LINK(VclVPaned, SplitHdl, Splitter*, pSplitter, void)
+{
+ tools::Long nSize = pSplitter->GetSplitPosPixel();
+ Size aSplitterSize(m_pSplitter->GetSizePixel());
+ Size aAllocation(GetSizePixel());
+ arrange(aAllocation, nSize, aAllocation.Height() - nSize - aSplitterSize.Height());
+}
+
+void VclVPaned::arrange(const Size& rAllocation, tools::Long nFirstHeight, tools::Long nSecondHeight)
+{
+ Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height());
+ Size aFirstChildSize(rAllocation.Width(), nFirstHeight);
+ Size aSecondChildSize(rAllocation.Width(), nSecondHeight);
+ int nElement = 0;
+ for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ if (nElement == 0)
+ {
+ Point aSplitterPos(0, aFirstChildSize.Height());
+ setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize);
+ m_nPosition = aSplitterPos.Y() + aSplitterSize.Height() / 2;
+ }
+ else if (nElement == 1)
+ {
+ Point aChildPos(0, 0);
+ setLayoutAllocation(*pChild, aChildPos, aFirstChildSize);
+ }
+ else if (nElement == 2)
+ {
+ Point aChildPos(0, aFirstChildSize.Height() + aSplitterSize.Height());
+ setLayoutAllocation(*pChild, aChildPos, aSecondChildSize);
+ }
+ ++nElement;
+ }
+}
+
+void VclVPaned::set_position(tools::Long nPosition)
+{
+ VclPaned::set_position(nPosition);
+
+ Size aAllocation(GetSizePixel());
+ Size aSplitterSize(m_pSplitter->GetSizePixel());
+
+ nPosition -= aSplitterSize.Height() / 2;
+
+ arrange(aAllocation, nPosition, aAllocation.Height() - nPosition - aSplitterSize.Height());
+}
+
+void VclVPaned::setAllocation(const Size& rAllocation)
+{
+ //supporting "shrink" could be done by adjusting the allowed drag rectangle
+ m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation));
+ Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height());
+ const tools::Long nHeight = rAllocation.Height() - aSplitterSize.Height();
+
+ tools::Long nFirstHeight = 0;
+ tools::Long nSecondHeight = 0;
+ bool bFirstCanResize = true;
+ bool bSecondCanResize = true;
+ const bool bInitialAllocation = get_position() < 0;
+ int nElement = 0;
+ for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ if (nElement == 1)
+ {
+ if (bInitialAllocation)
+ nFirstHeight = getLayoutRequisition(*pChild).Height();
+ else
+ nFirstHeight = pChild->GetSizePixel().Height() + pChild->get_margin_top() + pChild->get_margin_bottom();
+ bFirstCanResize = pChild->get_expand();
+ }
+ else if (nElement == 2)
+ {
+ if (bInitialAllocation)
+ nSecondHeight = getLayoutRequisition(*pChild).Height();
+ else
+ nSecondHeight = pChild->GetSizePixel().Height() + pChild->get_margin_top() + pChild->get_margin_bottom();
+ bSecondCanResize = pChild->get_expand();
+ }
+ ++nElement;
+ }
+ tools::Long nHeightRequest = nFirstHeight + nSecondHeight;
+ tools::Long nHeightDiff = nHeight - nHeightRequest;
+ if (bFirstCanResize == bSecondCanResize)
+ nFirstHeight += nHeightDiff/2;
+ else if (bFirstCanResize)
+ nFirstHeight += nHeightDiff;
+ arrange(rAllocation, nFirstHeight, rAllocation.Height() - nFirstHeight - aSplitterSize.Height());
+}
+
+Size VclVPaned::calculateRequisition() const
+{
+ Size aRet(0, 0);
+
+ for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ Size aChildSize = getLayoutRequisition(*pChild);
+ aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) );
+ aRet.AdjustHeight(aChildSize.Height() );
+ }
+
+ return aRet;
+}
+
+VclHPaned::VclHPaned(vcl::Window *pParent)
+ : VclPaned(pParent, false)
+{
+ m_pSplitter->SetSplitHdl(LINK(this, VclHPaned, SplitHdl));
+}
+
+IMPL_LINK(VclHPaned, SplitHdl, Splitter*, pSplitter, void)
+{
+ tools::Long nSize = pSplitter->GetSplitPosPixel();
+ Size aSplitterSize(m_pSplitter->GetSizePixel());
+ Size aAllocation(GetSizePixel());
+ arrange(aAllocation, nSize, aAllocation.Width() - nSize - aSplitterSize.Width());
+}
+
+void VclHPaned::arrange(const Size& rAllocation, tools::Long nFirstWidth, tools::Long nSecondWidth)
+{
+ Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height());
+ Size aFirstChildSize(nFirstWidth, rAllocation.Height());
+ Size aSecondChildSize(nSecondWidth, rAllocation.Height());
+ int nElement = 0;
+ for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ if (nElement == 0)
+ {
+ Point aSplitterPos(aFirstChildSize.Width(), 0);
+ setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize);
+ m_nPosition = aSplitterPos.X() + aSplitterSize.Width() / 2;
+ }
+ else if (nElement == 1)
+ {
+ Point aChildPos(0, 0);
+ setLayoutAllocation(*pChild, aChildPos, aFirstChildSize);
+ }
+ else if (nElement == 2)
+ {
+ Point aChildPos(aFirstChildSize.Width() + aSplitterSize.Width(), 0);
+ setLayoutAllocation(*pChild, aChildPos, aSecondChildSize);
+ }
+ ++nElement;
+ }
+}
+
+void VclHPaned::set_position(tools::Long nPosition)
+{
+ VclPaned::set_position(nPosition);
+
+ Size aAllocation(GetSizePixel());
+ Size aSplitterSize(m_pSplitter->GetSizePixel());
+
+ nPosition -= aSplitterSize.Width() / 2;
+
+ arrange(aAllocation, nPosition, aAllocation.Width() - nPosition - aSplitterSize.Width());
+}
+
+void VclHPaned::setAllocation(const Size& rAllocation)
+{
+ //supporting "shrink" could be done by adjusting the allowed drag rectangle
+ m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation));
+ Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height());
+ const tools::Long nWidth = rAllocation.Width() - aSplitterSize.Width();
+
+ tools::Long nFirstWidth = 0;
+ tools::Long nSecondWidth = 0;
+ bool bFirstCanResize = true;
+ bool bSecondCanResize = true;
+ const bool bInitialAllocation = get_position() < 0;
+ int nElement = 0;
+ for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ if (nElement == 1)
+ {
+ if (bInitialAllocation)
+ nFirstWidth = getLayoutRequisition(*pChild).Width();
+ else
+ nFirstWidth = pChild->GetSizePixel().Width() + pChild->get_margin_start() + pChild->get_margin_end();
+ bFirstCanResize = pChild->get_expand();
+ }
+ else if (nElement == 2)
+ {
+ if (bInitialAllocation)
+ nSecondWidth = getLayoutRequisition(*pChild).Width();
+ else
+ nSecondWidth = pChild->GetSizePixel().Width() + pChild->get_margin_start() + pChild->get_margin_end();
+ bSecondCanResize = pChild->get_expand();
+ }
+ ++nElement;
+ }
+ tools::Long nWidthRequest = nFirstWidth + nSecondWidth;
+ tools::Long nWidthDiff = nWidth - nWidthRequest;
+ if (bFirstCanResize == bSecondCanResize)
+ nFirstWidth += nWidthDiff/2;
+ else if (bFirstCanResize)
+ nFirstWidth += nWidthDiff;
+ arrange(rAllocation, nFirstWidth, rAllocation.Width() - nFirstWidth - aSplitterSize.Width());
+}
+
+Size VclHPaned::calculateRequisition() const
+{
+ Size aRet(0, 0);
+
+ for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+ Size aChildSize = getLayoutRequisition(*pChild);
+ aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) );
+ aRet.AdjustWidth(aChildSize.Width() );
+ }
+
+ return aRet;
+}
+
+Size getLegacyBestSizeForChildren(const vcl::Window &rWindow)
+{
+ tools::Rectangle aBounds;
+
+ for (const vcl::Window* pChild = rWindow.GetWindow(GetWindowType::FirstChild); pChild;
+ pChild = pChild->GetWindow(GetWindowType::Next))
+ {
+ if (!pChild->IsVisible())
+ continue;
+
+ tools::Rectangle aChildBounds(pChild->GetPosPixel(), pChild->GetSizePixel());
+ aBounds.Union(aChildBounds);
+ }
+
+ if (aBounds.IsEmpty())
+ return rWindow.GetSizePixel();
+
+ Size aRet(aBounds.GetSize());
+ Point aTopLeft(aBounds.TopLeft());
+ aRet.AdjustWidth(aTopLeft.X()*2 );
+ aRet.AdjustHeight(aTopLeft.Y()*2 );
+
+ return aRet;
+}
+
+vcl::Window* getNonLayoutParent(vcl::Window *pWindow)
+{
+ while (pWindow)
+ {
+ pWindow = pWindow->GetParent();
+ if (!pWindow || !isContainerWindow(*pWindow))
+ break;
+ }
+ return pWindow;
+}
+
+bool isVisibleInLayout(const vcl::Window *pWindow)
+{
+ bool bVisible = true;
+ while (bVisible)
+ {
+ bVisible = pWindow->IsVisible();
+ pWindow = pWindow->GetParent();
+ if (!pWindow || !isContainerWindow(*pWindow))
+ break;
+ }
+ return bVisible;
+}
+
+bool isEnabledInLayout(const vcl::Window *pWindow)
+{
+ bool bEnabled = true;
+ while (bEnabled)
+ {
+ bEnabled = pWindow->IsEnabled();
+ pWindow = pWindow->GetParent();
+ if (!pWindow || !isContainerWindow(*pWindow))
+ break;
+ }
+ return bEnabled;
+}
+
+bool isLayoutEnabled(const vcl::Window *pWindow)
+{
+ //Child is a container => we're layout enabled
+ const vcl::Window *pChild = pWindow ? pWindow->GetWindow(GetWindowType::FirstChild) : nullptr;
+ return pChild && isContainerWindow(*pChild) && !pChild->GetWindow(GetWindowType::Next);
+}
+
+void VclDrawingArea::RequestHelp(const HelpEvent& rHelpEvent)
+{
+ if (!(rHelpEvent.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON)))
+ return;
+
+ Point aPos(ScreenToOutputPixel(rHelpEvent.GetMousePosPixel()));
+ tools::Rectangle aHelpArea(aPos.X(), aPos.Y());
+ OUString sHelpTip = m_aQueryTooltipHdl.Call(aHelpArea);
+ if (sHelpTip.isEmpty())
+ return;
+ Point aPt = OutputToScreenPixel(aHelpArea.TopLeft());
+ aHelpArea.SetLeft(aPt.X());
+ aHelpArea.SetTop(aPt.Y());
+ aPt = OutputToScreenPixel(aHelpArea.BottomRight());
+ aHelpArea.SetRight(aPt.X());
+ aHelpArea.SetBottom(aPt.Y());
+ // tdf#125369 recover newline support of tdf#101779
+ QuickHelpFlags eHelpWinStyle = sHelpTip.indexOf('\n') != -1 ? QuickHelpFlags::TipStyleBalloon : QuickHelpFlags::NONE;
+ Help::ShowQuickHelp(this, aHelpArea, sHelpTip, eHelpWinStyle);
+}
+
+void VclDrawingArea::StartDrag(sal_Int8, const Point&)
+{
+ if (m_aStartDragHdl.Call(this))
+ return;
+
+ rtl::Reference<TransferDataContainer> xContainer = m_xTransferHelper;
+ if (!m_xTransferHelper.is())
+ return;
+
+ xContainer->StartDrag(this, m_nDragAction);
+}
+
+OUString VclDrawingArea::GetSurroundingText() const
+{
+ if (!m_aGetSurroundingHdl.IsSet())
+ return Control::GetSurroundingText();
+ OUString sSurroundingText;
+ m_aGetSurroundingHdl.Call(sSurroundingText);
+ return sSurroundingText;
+}
+
+Selection VclDrawingArea::GetSurroundingTextSelection() const
+{
+ if (!m_aGetSurroundingHdl.IsSet())
+ return Control::GetSurroundingTextSelection();
+ OUString sSurroundingText;
+ int nCursor = m_aGetSurroundingHdl.Call(sSurroundingText);
+ return Selection(nCursor, nCursor);
+}
+
+bool VclDrawingArea::DeleteSurroundingText(const Selection& rSelection)
+{
+ if (!m_aDeleteSurroundingHdl.IsSet())
+ return Control::DeleteSurroundingText(rSelection);
+ return m_aDeleteSurroundingHdl.Call(rSelection);
+}
+
+VclHPaned::~VclHPaned()
+{
+}
+
+VclVPaned::~VclVPaned()
+{
+}
+
+VclPaned::~VclPaned()
+{
+ disposeOnce();
+}
+
+VclScrolledWindow::~VclScrolledWindow()
+{
+ disposeOnce();
+}
+
+void VclDrawingArea::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ Control::DumpAsPropertyTree(rJsonWriter);
+ rJsonWriter.put("type", "drawingarea");
+
+ ScopedVclPtrInstance<VirtualDevice> pDevice;
+ OutputDevice* pRefDevice = GetOutDev();
+ Size aRenderSize(pRefDevice->PixelToLogic(GetOutputSizePixel()));
+ Size aOutputSize = GetSizePixel();
+ pDevice->SetOutputSize(aRenderSize);
+ tools::Rectangle aRect(Point(0,0), aRenderSize);
+
+ // Dark mode support
+ pDevice->DrawWallpaper(aRect, pRefDevice->GetBackground());
+
+ Paint(*pDevice, aRect);
+
+ BitmapEx aImage = pDevice->GetBitmapEx(Point(0,0), aRenderSize);
+ aImage.Scale(aOutputSize);
+ rJsonWriter.put("imagewidth", aRenderSize.Width());
+ rJsonWriter.put("imageheight", aRenderSize.Height());
+
+ SvMemoryStream aOStm(65535, 65535);
+ if(GraphicConverter::Export(aOStm, aImage, ConvertDataFormat::PNG) == ERRCODE_NONE)
+ {
+ css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
+ OStringBuffer aBuffer("data:image/png;base64,");
+ ::comphelper::Base64::encode(aBuffer, aSeq);
+ rJsonWriter.put("image", aBuffer);
+ }
+ rJsonWriter.put("text", GetQuickHelpText());
+}
+
+FactoryFunction VclDrawingArea::GetUITestFactory() const
+{
+ if (m_pFactoryFunction)
+ return m_pFactoryFunction;
+ return DrawingAreaUIObject::create;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/legacyaccessibility.cxx b/vcl/source/window/legacyaccessibility.cxx
new file mode 100644
index 0000000000..346e1fdc8f
--- /dev/null
+++ b/vcl/source/window/legacyaccessibility.cxx
@@ -0,0 +1,241 @@
+/* -*- 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 <window.h>
+
+#include "dlgctrl.hxx"
+
+using namespace ::com::sun::star;
+
+
+static vcl::Window* ImplGetLabelFor( vcl::Window* pFrameWindow, WindowType nMyType, vcl::Window* pLabel, sal_Unicode nAccel )
+{
+ vcl::Window* pWindow = nullptr;
+
+ if( nMyType == WindowType::FIXEDTEXT ||
+ nMyType == WindowType::FIXEDLINE ||
+ nMyType == WindowType::GROUPBOX )
+ {
+ // #i100833# MT 2010/02: Group box and fixed lines can also label a fixed text.
+ // See tools/options/print for example.
+ bool bThisIsAGroupControl = (nMyType == WindowType::GROUPBOX) || (nMyType == WindowType::FIXEDLINE);
+ // get index, form start and form end
+ sal_uInt16 nIndex=0, nFormStart=0, nFormEnd=0;
+ ::ImplFindDlgCtrlWindow( pFrameWindow,
+ pLabel,
+ nIndex,
+ nFormStart,
+ nFormEnd );
+ if( nAccel )
+ {
+ // find the accelerated window
+ pWindow = ::ImplFindAccelWindow( pFrameWindow,
+ nIndex,
+ nAccel,
+ nFormStart,
+ nFormEnd,
+ false );
+ }
+ else
+ {
+ // find the next control; if that is a fixed text
+ // fixed line or group box, then return NULL
+ while( nIndex < nFormEnd )
+ {
+ nIndex++;
+ vcl::Window* pSWindow = ::ImplGetChildWindow( pFrameWindow,
+ nIndex,
+ nIndex,
+ false );
+ if( pSWindow && isVisibleInLayout(pSWindow) && ! (pSWindow->GetStyle() & WB_NOLABEL) )
+ {
+ WindowType nType = pSWindow->GetType();
+ if( nType != WindowType::FIXEDTEXT &&
+ nType != WindowType::FIXEDLINE &&
+ nType != WindowType::GROUPBOX )
+ {
+ pWindow = pSWindow;
+ }
+ else if( bThisIsAGroupControl && ( nType == WindowType::FIXEDTEXT ) )
+ {
+ pWindow = pSWindow;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return pWindow;
+}
+
+namespace vcl {
+
+Window* Window::getLegacyNonLayoutAccessibleRelationLabelFor() const
+{
+ Window* pFrameWindow = ImplGetFrameWindow();
+
+ WinBits nFrameStyle = pFrameWindow->GetStyle();
+ if( ! ( nFrameStyle & WB_DIALOGCONTROL )
+ || ( nFrameStyle & WB_NODIALOGCONTROL )
+ )
+ return nullptr;
+
+ sal_Unicode nAccel = getAccel( GetText() );
+
+ Window* pWindow = ImplGetLabelFor( pFrameWindow, GetType(), const_cast<Window*>(this), nAccel );
+ if( ! pWindow && mpWindowImpl->mpRealParent )
+ pWindow = ImplGetLabelFor( mpWindowImpl->mpRealParent, GetType(), const_cast<Window*>(this), nAccel );
+ return pWindow;
+}
+
+static Window* ImplGetLabeledBy( Window* pFrameWindow, WindowType nMyType, Window* pLabeled )
+{
+ Window* pWindow = nullptr;
+ if ( (nMyType != WindowType::GROUPBOX) && (nMyType != WindowType::FIXEDLINE) )
+ {
+ // search for a control that labels this window
+ // a label is considered the last fixed text, fixed line or group box
+ // that comes before this control; with the exception of push buttons
+ // which are labeled only if the fixed text, fixed line or group box
+ // is directly before the control
+
+ // get form start and form end and index of this control
+ sal_uInt16 nIndex, nFormStart, nFormEnd;
+ Window* pSWindow = ::ImplFindDlgCtrlWindow( pFrameWindow,
+ pLabeled,
+ nIndex,
+ nFormStart,
+ nFormEnd );
+ if( pSWindow && nIndex != nFormStart )
+ {
+ if( nMyType == WindowType::PUSHBUTTON ||
+ nMyType == WindowType::HELPBUTTON ||
+ nMyType == WindowType::OKBUTTON ||
+ nMyType == WindowType::CANCELBUTTON )
+ {
+ nFormStart = nIndex-1;
+ }
+ for( sal_uInt16 nSearchIndex = nIndex-1; nSearchIndex >= nFormStart; nSearchIndex-- )
+ {
+ sal_uInt16 nFoundIndex = 0;
+ pSWindow = ::ImplGetChildWindow( pFrameWindow,
+ nSearchIndex,
+ nFoundIndex,
+ false );
+ if( pSWindow && isVisibleInLayout(pSWindow) && !(pSWindow->GetStyle() & WB_NOLABEL) )
+ {
+ WindowType nType = pSWindow->GetType();
+ if ( nType == WindowType::FIXEDTEXT ||
+ nType == WindowType::FIXEDLINE ||
+ nType == WindowType::GROUPBOX )
+ {
+ // a fixed text can't be labelled by a fixed text.
+ if ( ( nMyType != WindowType::FIXEDTEXT ) || ( nType != WindowType::FIXEDTEXT ) )
+ pWindow = pSWindow;
+ break;
+ }
+ }
+ if( nFoundIndex > nSearchIndex || nSearchIndex == 0 )
+ break;
+ }
+ }
+ }
+ return pWindow;
+}
+
+Window* Window::getLegacyNonLayoutAccessibleRelationLabeledBy() const
+{
+ Window* pFrameWindow = ImplGetFrameWindow();
+
+ // #i62723#, #104191# checkboxes and radiobuttons are not supposed to have labels
+ if( GetType() == WindowType::CHECKBOX || GetType() == WindowType::RADIOBUTTON )
+ return nullptr;
+
+// if( ! ( GetType() == WindowType::FIXEDTEXT ||
+// GetType() == WindowType::FIXEDLINE ||
+// GetType() == WindowType::GROUPBOX ) )
+ // #i100833# MT 2010/02: Group box and fixed lines can also label a fixed text.
+ // See tools/options/print for example.
+
+ Window* pWindow = ImplGetLabeledBy( pFrameWindow, GetType(), const_cast<Window*>(this) );
+ if( ! pWindow && mpWindowImpl->mpRealParent )
+ pWindow = ImplGetLabeledBy( mpWindowImpl->mpRealParent, GetType(), const_cast<Window*>(this) );
+
+ return pWindow;
+}
+
+Window* Window::getLegacyNonLayoutAccessibleRelationMemberOf() const
+{
+ Window* pWindow = nullptr;
+ Window* pFrameWindow = GetParent();
+ if ( !pFrameWindow )
+ {
+ pFrameWindow = ImplGetFrameWindow();
+ }
+ // if( ! ( GetType() == WindowType::FIXEDTEXT ||
+ if( GetType() != WindowType::FIXEDLINE && GetType() != WindowType::GROUPBOX )
+ {
+ // search for a control that makes member of this window
+ // it is considered the last fixed line or group box
+ // that comes before this control; with the exception of push buttons
+ // which are labeled only if the fixed line or group box
+ // is directly before the control
+ // get form start and form end and index of this control
+ sal_uInt16 nIndex, nFormStart, nFormEnd;
+ Window* pSWindow = ::ImplFindDlgCtrlWindow( pFrameWindow,
+ const_cast<Window*>(this),
+ nIndex,
+ nFormStart,
+ nFormEnd );
+ if( pSWindow && nIndex != nFormStart )
+ {
+ if( GetType() == WindowType::PUSHBUTTON ||
+ GetType() == WindowType::HELPBUTTON ||
+ GetType() == WindowType::OKBUTTON ||
+ GetType() == WindowType::CANCELBUTTON )
+ {
+ nFormStart = nIndex-1;
+ }
+ for( sal_uInt16 nSearchIndex = nIndex-1; nSearchIndex >= nFormStart; nSearchIndex-- )
+ {
+ sal_uInt16 nFoundIndex = 0;
+ pSWindow = ::ImplGetChildWindow( pFrameWindow,
+ nSearchIndex,
+ nFoundIndex,
+ false );
+ if( pSWindow && pSWindow->IsVisible() &&
+ ( pSWindow->GetType() == WindowType::FIXEDLINE ||
+ pSWindow->GetType() == WindowType::GROUPBOX ) )
+ {
+ pWindow = pSWindow;
+ break;
+ }
+ if( nFoundIndex > nSearchIndex || nSearchIndex == 0 )
+ break;
+ }
+ }
+ }
+ return pWindow;
+}
+
+} /* namespace vcl */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx
new file mode 100644
index 0000000000..82d630742a
--- /dev/null
+++ b/vcl/source/window/menu.cxx
@@ -0,0 +1,3143 @@
+/* -*- 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 <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+
+#include <comphelper/lok.hxx>
+#include <vcl/dialoghelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/image.hxx>
+#include <vcl/event.hxx>
+#include <vcl/help.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/taskpanelist.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandinfoprovider.hxx>
+
+#include <salinst.hxx>
+#include <svdata.hxx>
+#include <strings.hrc>
+#include <window.h>
+#include <salmenu.hxx>
+#include <salframe.hxx>
+
+#include "menubarwindow.hxx"
+#include "menufloatingwindow.hxx"
+#include "menuitemlist.hxx"
+
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <vcl/toolkit/unowrap.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <configsettings.hxx>
+
+#include <map>
+#include <string_view>
+#include <vector>
+
+#include <officecfg/Office/Common.hxx>
+
+namespace vcl
+{
+
+struct MenuLayoutData : public ControlLayoutData
+{
+ std::vector< sal_uInt16 > m_aLineItemIds;
+ std::map< sal_uInt16, tools::Rectangle > m_aVisibleItemBoundRects;
+};
+
+}
+
+using namespace vcl;
+
+#define EXTRAITEMHEIGHT 4
+#define SPACE_AROUND_TITLE 4
+
+static bool ImplAccelDisabled()
+{
+ // display of accelerator strings may be suppressed via configuration
+ static int nAccelDisabled = -1;
+
+ if( nAccelDisabled == -1 )
+ {
+ OUString aStr =
+ vcl::SettingsConfigItem::get()->
+ getValue( "Menu", "SuppressAccelerators" );
+ nAccelDisabled = aStr.equalsIgnoreAsciiCase("true") ? 1 : 0;
+ }
+ return nAccelDisabled == 1;
+}
+
+static void ImplSetMenuItemData( MenuItemData* pData )
+{
+ // convert data
+ if ( !pData->aImage )
+ pData->eType = MenuItemType::STRING;
+ else if ( pData->aText.isEmpty() )
+ pData->eType = MenuItemType::IMAGE;
+ else
+ pData->eType = MenuItemType::STRINGIMAGE;
+}
+
+namespace {
+
+void ImplClosePopupToolBox( const VclPtr<vcl::Window>& pWin )
+{
+ if ( pWin->GetType() == WindowType::TOOLBOX && ImplGetDockingManager()->IsInPopupMode( pWin ) )
+ {
+ SystemWindow* pFloatingWindow = ImplGetDockingManager()->GetFloatingWindow(pWin);
+ if (pFloatingWindow)
+ static_cast<FloatingWindow*>(pFloatingWindow)->EndPopupMode( FloatWinPopupEndFlags::CloseAll );
+ }
+}
+
+// TODO: Move to common code with the same function in toolbox
+// Draw the ">>" - more indicator at the coordinates
+void lclDrawMoreIndicator(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ rRenderContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR);
+ rRenderContext.SetLineColor();
+
+ if (rRenderContext.GetSettings().GetStyleSettings().GetFaceColor().IsDark())
+ rRenderContext.SetFillColor(COL_WHITE);
+ else
+ rRenderContext.SetFillColor(COL_BLACK);
+ float fScaleFactor = rRenderContext.GetDPIScaleFactor();
+
+ int linewidth = 1 * fScaleFactor;
+ int space = 4 * fScaleFactor;
+
+ tools::Long width = 8 * fScaleFactor;
+ tools::Long height = 5 * fScaleFactor;
+
+ //Keep odd b/c drawing code works better
+ if ( height % 2 == 0 )
+ height--;
+
+ tools::Long heightOrig = height;
+
+ tools::Long x = rRect.Left() + (rRect.getOpenWidth() - width)/2 + 1;
+ tools::Long y = rRect.Top() + (rRect.getOpenHeight() - height)/2 + 1;
+ while( height >= 1)
+ {
+ rRenderContext.DrawRect( tools::Rectangle( x, y, x + linewidth, y ) );
+ x += space;
+ rRenderContext.DrawRect( tools::Rectangle( x, y, x + linewidth, y ) );
+ x -= space;
+ y++;
+ if( height <= heightOrig / 2 + 1) x--;
+ else x++;
+ height--;
+ }
+ rRenderContext.Pop();
+}
+
+} // end anonymous namespace
+
+
+Menu::Menu()
+ : mpFirstDel(nullptr),
+ pItemList(new MenuItemList),
+ pStartedFrom(nullptr),
+ pWindow(nullptr),
+ nTitleHeight(0),
+ nEventId(nullptr),
+ mnHighlightedItemPos(ITEMPOS_INVALID),
+ nMenuFlags(MenuFlags::NONE),
+ nSelectedId(0),
+ nImgOrChkPos(0),
+ nTextPos(0),
+ bCanceled(false),
+ bInCallback(false),
+ bKilled(false)
+{
+}
+
+Menu::~Menu()
+{
+ disposeOnce();
+}
+
+void Menu::dispose()
+{
+ ImplCallEventListeners( VclEventId::ObjectDying, ITEMPOS_INVALID );
+
+ // at the window free the reference to the accessible component
+ // and make sure the MenuFloatingWindow knows about our destruction
+ if ( pWindow )
+ {
+ MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(pWindow.get());
+ if( pFloat->pMenu.get() == this )
+ pFloat->pMenu.clear();
+ pWindow->SetAccessible( css::uno::Reference< css::accessibility::XAccessible >() );
+ }
+
+ // dispose accessible components
+ if ( mxAccessible.is() )
+ {
+ css::uno::Reference< css::lang::XComponent> xComponent( mxAccessible, css::uno::UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+ }
+
+ if ( nEventId )
+ Application::RemoveUserEvent( nEventId );
+
+ // Notify deletion of this menu
+ ImplMenuDelData* pDelData = mpFirstDel;
+ while ( pDelData )
+ {
+ pDelData->mpMenu = nullptr;
+ pDelData = pDelData->mpNext;
+ }
+
+ bKilled = true;
+
+ // tdf#140225 when clearing pItemList, keep SalMenu in sync with
+ // their removal during menu teardown
+ for (size_t n = pItemList->size(); n;)
+ {
+ --n;
+ if (mpSalMenu)
+ mpSalMenu->RemoveItem(n);
+ pItemList->Remove(n);
+ }
+
+ assert(!pItemList->size());
+
+ mpLayoutData.reset();
+
+ // Native-support: destroy SalMenu
+ mpSalMenu.reset();
+
+ pStartedFrom.clear();
+ pWindow.clear();
+ VclReferenceBase::dispose();
+}
+
+void Menu::CreateAutoMnemonics()
+{
+ MnemonicGenerator aMnemonicGenerator;
+ size_t n;
+ for ( n = 0; n < pItemList->size(); n++ )
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos( n );
+ if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
+ aMnemonicGenerator.RegisterMnemonic( pData->aText );
+ }
+ for ( n = 0; n < pItemList->size(); n++ )
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos( n );
+ if ( ! (pData->nBits & MenuItemBits::NOSELECT ) )
+ pData->aText = aMnemonicGenerator.CreateMnemonic( pData->aText );
+ }
+}
+
+void Menu::Activate()
+{
+ bInCallback = true;
+
+ ImplMenuDelData aDelData( this );
+
+ ImplCallEventListeners( VclEventId::MenuActivate, ITEMPOS_INVALID );
+
+ if( !aDelData.isDeleted() )
+ {
+ if ( !aActivateHdl.Call( this ) )
+ {
+ if( !aDelData.isDeleted() )
+ {
+ Menu* pStartMenu = ImplGetStartMenu();
+ if ( pStartMenu && ( pStartMenu != this ) )
+ {
+ pStartMenu->bInCallback = true;
+ // MT 11/01: Call EventListener here? I don't know...
+ pStartMenu->aActivateHdl.Call( this );
+ pStartMenu->bInCallback = false;
+ }
+ }
+ }
+ bInCallback = false;
+ }
+
+ if (!aDelData.isDeleted() && !(nMenuFlags & MenuFlags::NoAutoMnemonics))
+ CreateAutoMnemonics();
+}
+
+void Menu::Deactivate()
+{
+ for ( size_t n = pItemList->size(); n; )
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos( --n );
+ if ( pData->bIsTemporary )
+ {
+ if ( ImplGetSalMenu() )
+ ImplGetSalMenu()->RemoveItem( n );
+
+ pItemList->Remove( n );
+ }
+ }
+
+ bInCallback = true;
+
+ ImplMenuDelData aDelData( this );
+
+ Menu* pStartMenu = ImplGetStartMenu();
+ ImplCallEventListeners( VclEventId::MenuDeactivate, ITEMPOS_INVALID );
+
+ if( !aDelData.isDeleted() )
+ {
+ if ( !aDeactivateHdl.Call( this ) )
+ {
+ if( !aDelData.isDeleted() )
+ {
+ if ( pStartMenu && ( pStartMenu != this ) )
+ {
+ pStartMenu->bInCallback = true;
+ pStartMenu->aDeactivateHdl.Call( this );
+ pStartMenu->bInCallback = false;
+ }
+ }
+ }
+ }
+
+ if( !aDelData.isDeleted() )
+ {
+ bInCallback = false;
+ }
+}
+
+void Menu::ImplSelect()
+{
+ MenuItemData* pData = GetItemList()->GetData( nSelectedId );
+ if ( pData && (pData->nBits & MenuItemBits::AUTOCHECK) )
+ {
+ bool bChecked = IsItemChecked( nSelectedId );
+ if ( pData->nBits & MenuItemBits::RADIOCHECK )
+ {
+ if ( !bChecked )
+ CheckItem( nSelectedId );
+ }
+ else
+ CheckItem( nSelectedId, !bChecked );
+ }
+
+ // call select
+ ImplSVData* pSVData = ImplGetSVData();
+ pSVData->maAppData.mpActivePopupMenu = nullptr; // if new execute in select()
+ nEventId = Application::PostUserEvent( LINK( this, Menu, ImplCallSelect ) );
+}
+
+void Menu::Select()
+{
+ ImplMenuDelData aDelData( this );
+
+ ImplCallEventListeners( VclEventId::MenuSelect, GetItemPos( GetCurItemId() ) );
+ if (aDelData.isDeleted())
+ return;
+ if (aSelectHdl.Call(this))
+ return;
+ if (aDelData.isDeleted())
+ return;
+ Menu* pStartMenu = ImplGetStartMenu();
+ if (!pStartMenu || (pStartMenu == this))
+ return;
+ pStartMenu->nSelectedId = nSelectedId;
+ pStartMenu->sSelectedIdent = sSelectedIdent;
+ pStartMenu->aSelectHdl.Call( this );
+}
+
+#if defined(MACOSX)
+void Menu::ImplSelectWithStart( Menu* pSMenu )
+{
+ auto pOldStartedFrom = pStartedFrom;
+ pStartedFrom = pSMenu;
+ auto pOldStartedStarted = pOldStartedFrom ? pOldStartedFrom->pStartedFrom : VclPtr<Menu>();
+ Select();
+ if( pOldStartedFrom )
+ pOldStartedFrom->pStartedFrom = pOldStartedStarted;
+ pStartedFrom = pOldStartedFrom;
+}
+#endif
+
+void Menu::ImplCallEventListeners( VclEventId nEvent, sal_uInt16 nPos )
+{
+ ImplMenuDelData aDelData( this );
+
+ VclMenuEvent aEvent( this, nEvent, nPos );
+
+ // This is needed by atk accessibility bridge
+ if ( nEvent == VclEventId::MenuHighlight )
+ {
+ Application::ImplCallEventListeners( aEvent );
+ }
+
+ if ( !aDelData.isDeleted() )
+ {
+ // Copy the list, because this can be destroyed when calling a Link...
+ std::list<Link<VclMenuEvent&,void>> aCopy( maEventListeners );
+ for ( const auto& rLink : aCopy )
+ {
+ if( std::find(maEventListeners.begin(), maEventListeners.end(), rLink) != maEventListeners.end() )
+ rLink.Call( aEvent );
+ }
+ }
+}
+
+void Menu::AddEventListener( const Link<VclMenuEvent&,void>& rEventListener )
+{
+ maEventListeners.push_back( rEventListener );
+}
+
+void Menu::RemoveEventListener( const Link<VclMenuEvent&,void>& rEventListener )
+{
+ maEventListeners.remove( rEventListener );
+}
+
+MenuItemData* Menu::NbcInsertItem(sal_uInt16 nId, MenuItemBits nBits,
+ const OUString& rStr, Menu* pMenu,
+ size_t nPos, const OUString &rIdent)
+{
+ // put Item in MenuItemList
+ MenuItemData* pData = pItemList->Insert(nId, MenuItemType::STRING,
+ nBits, rStr, pMenu, nPos, rIdent);
+
+ // update native menu
+ if (ImplGetSalMenu() && pData->pSalMenuItem)
+ ImplGetSalMenu()->InsertItem(pData->pSalMenuItem.get(), nPos);
+
+ return pData;
+}
+
+void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr, MenuItemBits nItemBits,
+ const OUString &rIdent, sal_uInt16 nPos)
+{
+ SAL_WARN_IF( !nItemId, "vcl", "Menu::InsertItem(): ItemId == 0" );
+ SAL_WARN_IF( GetItemPos( nItemId ) != MENU_ITEM_NOTFOUND, "vcl",
+ "Menu::InsertItem(): ItemId already exists" );
+
+ // if Position > ItemCount, append
+ if ( nPos >= pItemList->size() )
+ nPos = MENU_APPEND;
+
+ // put Item in MenuItemList
+ NbcInsertItem(nItemId, nItemBits, rStr, this, nPos, rIdent);
+
+ vcl::Window* pWin = ImplGetWindow();
+ mpLayoutData.reset();
+ if ( pWin )
+ {
+ ImplCalcSize( pWin );
+ if ( pWin->IsVisible() )
+ pWin->Invalidate();
+ }
+ ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
+}
+
+void Menu::InsertItem(sal_uInt16 nItemId, const Image& rImage,
+ MenuItemBits nItemBits, const OUString &rIdent, sal_uInt16 nPos)
+{
+ InsertItem(nItemId, OUString(), nItemBits, rIdent, nPos);
+ SetItemImage( nItemId, rImage );
+}
+
+void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr,
+ const Image& rImage, MenuItemBits nItemBits,
+ const OUString &rIdent, sal_uInt16 nPos)
+{
+ InsertItem(nItemId, rStr, nItemBits, rIdent, nPos);
+ SetItemImage( nItemId, rImage );
+}
+
+void Menu::InsertSeparator(const OUString &rIdent, sal_uInt16 nPos)
+{
+ // do nothing if it's a menu bar
+ if (IsMenuBar())
+ return;
+
+ // if position > ItemCount, append
+ if ( nPos >= pItemList->size() )
+ nPos = MENU_APPEND;
+
+ // put separator in item list
+ pItemList->InsertSeparator(rIdent, nPos);
+
+ // update native menu
+ size_t itemPos = ( nPos != MENU_APPEND ) ? nPos : pItemList->size() - 1;
+ MenuItemData *pData = pItemList->GetDataFromPos( itemPos );
+ if( ImplGetSalMenu() && pData && pData->pSalMenuItem )
+ ImplGetSalMenu()->InsertItem( pData->pSalMenuItem.get(), nPos );
+
+ mpLayoutData.reset();
+
+ ImplCallEventListeners( VclEventId::MenuInsertItem, nPos );
+}
+
+void Menu::RemoveItem( sal_uInt16 nPos )
+{
+ bool bRemove = false;
+
+ if ( nPos < GetItemCount() )
+ {
+ // update native menu
+ if( ImplGetSalMenu() )
+ ImplGetSalMenu()->RemoveItem( nPos );
+
+ pItemList->Remove( nPos );
+ bRemove = true;
+ }
+
+ vcl::Window* pWin = ImplGetWindow();
+ if ( pWin )
+ {
+ ImplCalcSize( pWin );
+ if ( pWin->IsVisible() )
+ pWin->Invalidate();
+ }
+ mpLayoutData.reset();
+
+ if ( bRemove )
+ ImplCallEventListeners( VclEventId::MenuRemoveItem, nPos );
+}
+
+static void ImplCopyItem( Menu* pThis, const Menu& rMenu, sal_uInt16 nPos, sal_uInt16 nNewPos )
+{
+ MenuItemType eType = rMenu.GetItemType( nPos );
+
+ if ( eType == MenuItemType::DONTKNOW )
+ return;
+
+ if ( eType == MenuItemType::SEPARATOR )
+ pThis->InsertSeparator( {}, nNewPos );
+ else
+ {
+ sal_uInt16 nId = rMenu.GetItemId( nPos );
+
+ SAL_WARN_IF( pThis->GetItemPos( nId ) != MENU_ITEM_NOTFOUND, "vcl",
+ "Menu::CopyItem(): ItemId already exists" );
+
+ MenuItemData* pData = rMenu.GetItemList()->GetData( nId );
+
+ if (!pData)
+ return;
+
+ if ( eType == MenuItemType::STRINGIMAGE )
+ pThis->InsertItem( nId, pData->aText, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
+ else if ( eType == MenuItemType::STRING )
+ pThis->InsertItem( nId, pData->aText, pData->nBits, pData->sIdent, nNewPos );
+ else
+ pThis->InsertItem( nId, pData->aImage, pData->nBits, pData->sIdent, nNewPos );
+
+ if ( rMenu.IsItemChecked( nId ) )
+ pThis->CheckItem( nId );
+ if ( !rMenu.IsItemEnabled( nId ) )
+ pThis->EnableItem( nId, false );
+ pThis->SetHelpId( nId, pData->aHelpId );
+ pThis->SetHelpText( nId, pData->aHelpText );
+ pThis->SetAccelKey( nId, pData->aAccelKey );
+ pThis->SetItemCommand( nId, pData->aCommandStr );
+ pThis->SetHelpCommand( nId, pData->aHelpCommandStr );
+
+ PopupMenu* pSubMenu = rMenu.GetPopupMenu( nId );
+ if ( pSubMenu )
+ {
+ // create auto-copy
+ VclPtr<PopupMenu> pNewMenu = VclPtr<PopupMenu>::Create( *pSubMenu );
+ pThis->SetPopupMenu( nId, pNewMenu );
+ }
+ }
+}
+
+void Menu::Clear()
+{
+ for ( sal_uInt16 i = GetItemCount(); i; i-- )
+ RemoveItem( 0 );
+}
+
+sal_uInt16 Menu::GetItemCount() const
+{
+ return static_cast<sal_uInt16>(pItemList->size());
+}
+
+bool Menu::HasValidEntries(bool bCheckPopups) const
+{
+ bool bValidEntries = false;
+ sal_uInt16 nCount = GetItemCount();
+ for (sal_uInt16 n = 0; !bValidEntries && (n < nCount); n++)
+ {
+ MenuItemData* pItem = pItemList->GetDataFromPos(n);
+ if (pItem->bEnabled && (pItem->eType != MenuItemType::SEPARATOR))
+ {
+ if (bCheckPopups && pItem->pSubMenu)
+ bValidEntries = pItem->pSubMenu->HasValidEntries(true);
+ else
+ bValidEntries = true;
+ }
+ }
+ return bValidEntries;
+}
+
+sal_uInt16 Menu::ImplGetVisibleItemCount() const
+{
+ sal_uInt16 nItems = 0;
+ for ( size_t n = pItemList->size(); n; )
+ {
+ if ( ImplIsVisible( --n ) )
+ nItems++;
+ }
+ return nItems;
+}
+
+sal_uInt16 Menu::ImplGetFirstVisible() const
+{
+ for ( size_t n = 0; n < pItemList->size(); n++ )
+ {
+ if ( ImplIsVisible( n ) )
+ return n;
+ }
+ return ITEMPOS_INVALID;
+}
+
+sal_uInt16 Menu::ImplGetPrevVisible( sal_uInt16 nPos ) const
+{
+ for ( size_t n = nPos; n; )
+ {
+ if (ImplIsVisible(--n))
+ return n;
+ }
+ return ITEMPOS_INVALID;
+}
+
+sal_uInt16 Menu::ImplGetNextVisible( sal_uInt16 nPos ) const
+{
+ for ( size_t n = nPos+1; n < pItemList->size(); n++ )
+ {
+ if ( ImplIsVisible( n ) )
+ return n;
+ }
+ return ITEMPOS_INVALID;
+}
+
+sal_uInt16 Menu::GetItemId(sal_uInt16 nPos) const
+{
+ MenuItemData* pData = pItemList->GetDataFromPos( nPos );
+
+ if ( pData )
+ return pData->nId;
+ else
+ return 0;
+}
+
+sal_uInt16 Menu::GetItemId(std::u16string_view rIdent) const
+{
+ for (size_t n = 0; n < pItemList->size(); ++n)
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos(n);
+ if (pData && pData->sIdent == rIdent)
+ return pData->nId;
+ }
+ return MENU_ITEM_NOTFOUND;
+}
+
+sal_uInt16 Menu::GetItemPos( sal_uInt16 nItemId ) const
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ if ( pData )
+ return static_cast<sal_uInt16>(nPos);
+ else
+ return MENU_ITEM_NOTFOUND;
+}
+
+MenuItemType Menu::GetItemType( sal_uInt16 nPos ) const
+{
+ MenuItemData* pData = pItemList->GetDataFromPos( nPos );
+
+ if ( pData )
+ return pData->eType;
+ else
+ return MenuItemType::DONTKNOW;
+}
+
+OUString Menu::GetItemIdent(sal_uInt16 nId) const
+{
+ const MenuItemData* pData = pItemList->GetData(nId);
+ return pData ? pData->sIdent : OUString();
+}
+
+void Menu::SetItemBits( sal_uInt16 nItemId, MenuItemBits nBits )
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData(nItemId, nPos);
+
+ if (pData && (pData->nBits != nBits))
+ {
+ // these two menu item bits are relevant for (accessible) role
+ const MenuItemBits nRoleMask = MenuItemBits::CHECKABLE | MenuItemBits::RADIOCHECK;
+ const bool bRoleBitsChanged = (pData->nBits & nRoleMask) != (nBits & nRoleMask);
+
+ pData->nBits = nBits;
+
+ // update native menu
+ if (ImplGetSalMenu())
+ ImplGetSalMenu()->SetItemBits(nPos, nBits);
+
+ if (bRoleBitsChanged)
+ ImplCallEventListeners(VclEventId::MenuItemRoleChanged, nPos);
+ }
+}
+
+MenuItemBits Menu::GetItemBits( sal_uInt16 nItemId ) const
+{
+ MenuItemBits nBits = MenuItemBits::NONE;
+ MenuItemData* pData = pItemList->GetData( nItemId );
+ if ( pData )
+ nBits = pData->nBits;
+ return nBits;
+}
+
+void Menu::SetUserValue(sal_uInt16 nItemId, void* nUserValue, MenuUserDataReleaseFunction aFunc)
+{
+ MenuItemData* pData = pItemList->GetData(nItemId);
+ if (pData)
+ {
+ if (pData->aUserValueReleaseFunc)
+ pData->aUserValueReleaseFunc(pData->nUserValue);
+ pData->aUserValueReleaseFunc = aFunc;
+ pData->nUserValue = nUserValue;
+ }
+}
+
+void* Menu::GetUserValue( sal_uInt16 nItemId ) const
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+ return pData ? pData->nUserValue : nullptr;
+}
+
+void Menu::SetPopupMenu( sal_uInt16 nItemId, PopupMenu* pMenu )
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ // Item does not exist -> return NULL
+ if ( !pData )
+ return;
+
+ // same menu, nothing to do
+ if ( pData->pSubMenu.get() == pMenu )
+ return;
+
+ // remove old menu
+ auto oldSubMenu = pData->pSubMenu;
+
+ // data exchange
+ pData->pSubMenu = pMenu;
+
+ // #112023# Make sure pStartedFrom does not point to invalid (old) data
+ if ( pData->pSubMenu )
+ pData->pSubMenu->pStartedFrom = nullptr;
+
+ // set native submenu
+ if( ImplGetSalMenu() && pData->pSalMenuItem )
+ {
+ if( pMenu )
+ ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), pMenu->ImplGetSalMenu(), nPos );
+ else
+ ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), nullptr, nPos );
+ }
+
+ oldSubMenu.disposeAndClear();
+
+ ImplCallEventListeners( VclEventId::MenuSubmenuChanged, nPos );
+}
+
+PopupMenu* Menu::GetPopupMenu( sal_uInt16 nItemId ) const
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ return pData->pSubMenu.get();
+ else
+ return nullptr;
+}
+
+void Menu::SetAccelKey( sal_uInt16 nItemId, const KeyCode& rKeyCode )
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ if ( !pData )
+ return;
+
+ if ( pData->aAccelKey == rKeyCode )
+ return;
+
+ pData->aAccelKey = rKeyCode;
+
+ // update native menu
+ if( ImplGetSalMenu() && pData->pSalMenuItem )
+ ImplGetSalMenu()->SetAccelerator( nPos, pData->pSalMenuItem.get(), rKeyCode, rKeyCode.GetName() );
+}
+
+KeyCode Menu::GetAccelKey( sal_uInt16 nItemId ) const
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ return pData->aAccelKey;
+ else
+ return KeyCode();
+}
+
+KeyEvent Menu::GetActivationKey( sal_uInt16 nItemId ) const
+{
+ KeyEvent aRet;
+ MenuItemData* pData = pItemList->GetData( nItemId );
+ if( pData )
+ {
+ sal_Int32 nPos = pData->aText.indexOf( '~' );
+ if( nPos != -1 && nPos < pData->aText.getLength()-1 )
+ {
+ sal_uInt16 nCode = 0;
+ sal_Unicode cAccel = pData->aText[nPos+1];
+ if( cAccel >= 'a' && cAccel <= 'z' )
+ nCode = KEY_A + (cAccel-'a');
+ else if( cAccel >= 'A' && cAccel <= 'Z' )
+ nCode = KEY_A + (cAccel-'A');
+ else if( cAccel >= '0' && cAccel <= '9' )
+ nCode = KEY_0 + (cAccel-'0');
+
+ aRet = KeyEvent( cAccel, KeyCode( nCode, KEY_MOD2 ) );
+ }
+
+ }
+ return aRet;
+}
+
+void Menu::CheckItem( sal_uInt16 nItemId, bool bCheck )
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ if ( !pData || pData->bChecked == bCheck )
+ return;
+
+ // if radio-check, then uncheck previous
+ if ( bCheck && (pData->nBits & MenuItemBits::AUTOCHECK) &&
+ (pData->nBits & MenuItemBits::RADIOCHECK) )
+ {
+ MenuItemData* pGroupData;
+ sal_uInt16 nGroupPos;
+ sal_uInt16 nItemCount = GetItemCount();
+ bool bFound = false;
+
+ nGroupPos = nPos;
+ while ( nGroupPos )
+ {
+ pGroupData = pItemList->GetDataFromPos( nGroupPos-1 );
+ if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
+ {
+ if ( IsItemChecked( pGroupData->nId ) )
+ {
+ CheckItem( pGroupData->nId, false );
+ bFound = true;
+ break;
+ }
+ }
+ else
+ break;
+ nGroupPos--;
+ }
+
+ if ( !bFound )
+ {
+ nGroupPos = nPos+1;
+ while ( nGroupPos < nItemCount )
+ {
+ pGroupData = pItemList->GetDataFromPos( nGroupPos );
+ if ( pGroupData->nBits & MenuItemBits::RADIOCHECK )
+ {
+ if ( IsItemChecked( pGroupData->nId ) )
+ {
+ CheckItem( pGroupData->nId, false );
+ break;
+ }
+ }
+ else
+ break;
+ nGroupPos++;
+ }
+ }
+ }
+
+ pData->bChecked = bCheck;
+
+ // update native menu
+ if( ImplGetSalMenu() )
+ ImplGetSalMenu()->CheckItem( nPos, bCheck );
+
+ ImplCallEventListeners( bCheck ? VclEventId::MenuItemChecked : VclEventId::MenuItemUnchecked, nPos );
+}
+
+void Menu::CheckItem( std::u16string_view rIdent , bool bCheck )
+{
+ CheckItem( GetItemId( rIdent ), bCheck );
+}
+
+bool Menu::IsItemCheckable(sal_uInt16 nItemId) const
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData(nItemId, nPos);
+
+ if (!pData)
+ return false;
+
+ return pData->HasCheck();
+}
+
+bool Menu::IsItemChecked( sal_uInt16 nItemId ) const
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ if ( !pData )
+ return false;
+
+ return pData->bChecked;
+}
+
+void Menu::EnableItem( sal_uInt16 nItemId, bool bEnable )
+{
+ size_t nPos;
+ MenuItemData* pItemData = pItemList->GetData( nItemId, nPos );
+
+ if ( !(pItemData && ( pItemData->bEnabled != bEnable )) )
+ return;
+
+ pItemData->bEnabled = bEnable;
+
+ vcl::Window* pWin = ImplGetWindow();
+ if ( pWin && pWin->IsVisible() )
+ {
+ SAL_WARN_IF(!IsMenuBar(), "vcl", "Menu::EnableItem - Popup visible!" );
+ tools::Long nX = 0;
+ size_t nCount = pItemList->size();
+ for ( size_t n = 0; n < nCount; n++ )
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos( n );
+ if ( n == nPos )
+ {
+ pWin->Invalidate( tools::Rectangle( Point( nX, 0 ), Size( pData->aSz.Width(), pData->aSz.Height() ) ) );
+ break;
+ }
+ nX += pData->aSz.Width();
+ }
+ }
+ // update native menu
+ if( ImplGetSalMenu() )
+ ImplGetSalMenu()->EnableItem( nPos, bEnable );
+
+ ImplCallEventListeners( bEnable ? VclEventId::MenuEnable : VclEventId::MenuDisable, nPos );
+}
+
+bool Menu::IsItemEnabled( sal_uInt16 nItemId ) const
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ if ( !pData )
+ return false;
+
+ return pData->bEnabled;
+}
+
+void Menu::ShowItem( sal_uInt16 nItemId, bool bVisible )
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ SAL_WARN_IF(IsMenuBar() && !bVisible , "vcl", "Menu::ShowItem - ignored for menu bar entries!");
+ if (IsMenuBar() || !pData || (pData->bVisible == bVisible))
+ return;
+
+ vcl::Window* pWin = ImplGetWindow();
+ if ( pWin && pWin->IsVisible() )
+ {
+ SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" );
+ return;
+ }
+ pData->bVisible = bVisible;
+
+ // update native menu
+ if( ImplGetSalMenu() )
+ ImplGetSalMenu()->ShowItem( nPos, bVisible );
+}
+
+void Menu::SetItemText( sal_uInt16 nItemId, const OUString& rStr )
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ if ( !pData )
+ return;
+
+ if ( rStr == pData->aText )
+ return;
+
+ pData->aText = rStr;
+ // Clear layout for aText.
+ pData->aTextGlyphs.Invalidate();
+ ImplSetMenuItemData( pData );
+ // update native menu
+ if( ImplGetSalMenu() && pData->pSalMenuItem )
+ ImplGetSalMenu()->SetItemText( nPos, pData->pSalMenuItem.get(), rStr );
+
+ vcl::Window* pWin = ImplGetWindow();
+ mpLayoutData.reset();
+ if (pWin && IsMenuBar())
+ {
+ ImplCalcSize( pWin );
+ if ( pWin->IsVisible() )
+ pWin->Invalidate();
+ }
+
+ ImplCallEventListeners( VclEventId::MenuItemTextChanged, nPos );
+}
+
+OUString Menu::GetItemText( sal_uInt16 nItemId ) const
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ if ( pData )
+ return pData->aText;
+
+ return OUString();
+}
+
+void Menu::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ if ( !pData )
+ return;
+
+ pData->aImage = rImage;
+ ImplSetMenuItemData( pData );
+
+ // update native menu
+ if( ImplGetSalMenu() && pData->pSalMenuItem )
+ ImplGetSalMenu()->SetItemImage( nPos, pData->pSalMenuItem.get(), rImage );
+}
+
+Image Menu::GetItemImage( sal_uInt16 nItemId ) const
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ return pData->aImage;
+ else
+ return Image();
+}
+
+void Menu::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ if ( pData )
+ pData->aCommandStr = rCommand;
+}
+
+OUString Menu::GetItemCommand( sal_uInt16 nItemId ) const
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if (pData)
+ return pData->aCommandStr;
+
+ return OUString();
+}
+
+void Menu::SetHelpCommand( sal_uInt16 nItemId, const OUString& rStr )
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ pData->aHelpCommandStr = rStr;
+}
+
+OUString Menu::GetHelpCommand( sal_uInt16 nItemId ) const
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ return pData->aHelpCommandStr;
+
+ return OUString();
+}
+
+void Menu::SetHelpText( sal_uInt16 nItemId, const OUString& rStr )
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ pData->aHelpText = rStr;
+}
+
+OUString Menu::ImplGetHelpText( sal_uInt16 nItemId ) const
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if (!pData)
+ return OUString();
+
+ if ( pData->aHelpText.isEmpty() &&
+ (( !pData->aHelpId.isEmpty() ) || ( !pData->aCommandStr.isEmpty() )))
+ {
+ Help* pHelp = Application::GetHelp();
+ if ( pHelp )
+ {
+ if (!pData->aCommandStr.isEmpty())
+ pData->aHelpText = pHelp->GetHelpText( pData->aCommandStr, static_cast<weld::Widget*>(nullptr) );
+ if (pData->aHelpText.isEmpty() && !pData->aHelpId.isEmpty())
+ pData->aHelpText = pHelp->GetHelpText( pData->aHelpId, static_cast<weld::Widget*>(nullptr) );
+ }
+ }
+
+ //Fallback to Menu::GetAccessibleDescription without reentry to GetHelpText()
+ if (pData->aHelpText.isEmpty())
+ return pData->aAccessibleDescription;
+ return pData->aHelpText;
+}
+
+OUString Menu::GetHelpText( sal_uInt16 nItemId ) const
+{
+ return ImplGetHelpText( nItemId );
+}
+
+void Menu::SetTipHelpText( sal_uInt16 nItemId, const OUString& rStr )
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ pData->aTipHelpText = rStr;
+}
+
+OUString Menu::GetTipHelpText( sal_uInt16 nItemId ) const
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ return pData->aTipHelpText;
+
+ return OUString();
+}
+
+void Menu::SetHelpId( sal_uInt16 nItemId, const OUString& rHelpId )
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ pData->aHelpId = rHelpId;
+}
+
+OUString Menu::GetHelpId( sal_uInt16 nItemId ) const
+{
+ OUString aRet;
+
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ {
+ if ( !pData->aHelpId.isEmpty() )
+ aRet = pData->aHelpId;
+ else
+ aRet = pData->aCommandStr;
+ }
+
+ return aRet;
+}
+
+Menu& Menu::operator=( const Menu& rMenu )
+{
+ if(this == &rMenu)
+ return *this;
+
+ // clean up
+ Clear();
+
+ // copy items
+ sal_uInt16 nCount = rMenu.GetItemCount();
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ ImplCopyItem( this, rMenu, i, MENU_APPEND );
+
+ aActivateHdl = rMenu.aActivateHdl;
+ aDeactivateHdl = rMenu.aDeactivateHdl;
+ aSelectHdl = rMenu.aSelectHdl;
+ aTitleText = rMenu.aTitleText;
+ nTitleHeight = rMenu.nTitleHeight;
+
+ return *this;
+}
+
+// Returns true if the item is completely hidden on the GUI and shouldn't
+// be possible to interact with
+bool Menu::ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos) const
+{
+ MenuItemData* pData = pItemList->GetDataFromPos(nPos);
+ if (pData)
+ {
+ MenuItemData* pPreviousData = pItemList->GetDataFromPos( nPos - 1 );
+ if (pPreviousData && pPreviousData->bHiddenOnGUI)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Menu::ImplIsVisible( sal_uInt16 nPos ) const
+{
+ bool bVisible = true;
+
+ MenuItemData* pData = pItemList->GetDataFromPos( nPos );
+ // check general visibility first
+ if( pData && !pData->bVisible )
+ bVisible = false;
+
+ if ( bVisible && pData && pData->eType == MenuItemType::SEPARATOR )
+ {
+ if( nPos == 0 ) // no separator should be shown at the very beginning
+ bVisible = false;
+ else
+ {
+ // always avoid adjacent separators
+ size_t nCount = pItemList->size();
+ size_t n;
+ MenuItemData* pNextData = nullptr;
+ // search next visible item
+ for( n = nPos + 1; n < nCount; n++ )
+ {
+ pNextData = pItemList->GetDataFromPos( n );
+ if( pNextData && pNextData->bVisible )
+ {
+ if( pNextData->eType == MenuItemType::SEPARATOR || ImplIsVisible(n) )
+ break;
+ }
+ }
+ if( n == nCount ) // no next visible item
+ bVisible = false;
+ // check for separator
+ if( pNextData && pNextData->bVisible && pNextData->eType == MenuItemType::SEPARATOR )
+ bVisible = false;
+
+ if( bVisible )
+ {
+ for( n = nPos; n > 0; n-- )
+ {
+ pNextData = pItemList->GetDataFromPos( n-1 );
+ if( pNextData && pNextData->bVisible )
+ {
+ if( pNextData->eType != MenuItemType::SEPARATOR && ImplIsVisible(n-1) )
+ break;
+ }
+ }
+ if( n == 0 ) // no previous visible item
+ bVisible = false;
+ }
+ }
+ }
+
+ // not allowed for menubar, as I do not know
+ // whether a menu-entry will disappear or will appear
+ if (bVisible && !IsMenuBar() && (nMenuFlags & MenuFlags::HideDisabledEntries) &&
+ !(nMenuFlags & MenuFlags::AlwaysShowDisabledEntries))
+ {
+ if( !pData ) // e.g. nPos == ITEMPOS_INVALID
+ bVisible = false;
+ else if ( pData->eType != MenuItemType::SEPARATOR ) // separators handled above
+ {
+ // tdf#86850 Always display clipboard functions
+ if ( pData->aCommandStr == ".uno:Cut" || pData->aCommandStr == ".uno:Copy" || pData->aCommandStr == ".uno:Paste" ||
+ pData->sIdent == ".uno:Cut" || pData->sIdent == ".uno:Copy" || pData->sIdent == ".uno:Paste" )
+ bVisible = true;
+ else
+ // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) );
+ bVisible = pData->bEnabled; // do not check submenus as they might be filled at Activate().
+ }
+ }
+
+ return bVisible;
+}
+
+bool Menu::IsItemPosVisible( sal_uInt16 nItemPos ) const
+{
+ return IsMenuVisible() && ImplIsVisible( nItemPos );
+}
+
+bool Menu::IsMenuVisible() const
+{
+ return pWindow && pWindow->IsReallyVisible();
+}
+
+bool Menu::ImplIsSelectable( sal_uInt16 nPos ) const
+{
+ bool bSelectable = true;
+
+ MenuItemData* pData = pItemList->GetDataFromPos( nPos );
+ // check general visibility first
+ if ( pData && ( pData->nBits & MenuItemBits::NOSELECT ) )
+ bSelectable = false;
+
+ return bSelectable;
+}
+
+css::uno::Reference<css::accessibility::XAccessible> Menu::GetAccessible()
+{
+ // Since PopupMenu are sometimes shared by different instances of MenuBar, the mxAccessible member gets
+ // overwritten and may contain a disposed object when the initial menubar gets set again. So use the
+ // mxAccessible member only for sub menus.
+ if (pStartedFrom && pStartedFrom != this)
+ {
+ for ( sal_uInt16 i = 0, nCount = pStartedFrom->GetItemCount(); i < nCount; ++i )
+ {
+ sal_uInt16 nItemId = pStartedFrom->GetItemId( i );
+ if ( static_cast< Menu* >( pStartedFrom->GetPopupMenu( nItemId ) ) == this )
+ {
+ css::uno::Reference<css::accessibility::XAccessible> xParent = pStartedFrom->GetAccessible();
+ if ( xParent.is() )
+ {
+ css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext( xParent->getAccessibleContext() );
+ if (xParentContext.is())
+ return xParentContext->getAccessibleChild( i );
+ }
+ }
+ }
+ }
+ else if ( !mxAccessible.is() )
+ {
+ UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
+ if ( pWrapper )
+ mxAccessible = pWrapper->CreateAccessible(this, IsMenuBar());
+ }
+
+ return mxAccessible;
+}
+
+void Menu::SetAccessible(const css::uno::Reference<css::accessibility::XAccessible>& rxAccessible )
+{
+ mxAccessible = rxAccessible;
+}
+
+Size Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext const & rRenderContext, tools::Long& rCheckHeight, tools::Long& rRadioHeight ) const
+{
+ tools::Long nCheckWidth = 0, nRadioWidth = 0;
+ rCheckHeight = rRadioHeight = 0;
+
+ if (!IsMenuBar())
+ {
+ ImplControlValue aVal;
+ tools::Rectangle aNativeBounds;
+ tools::Rectangle aNativeContent;
+
+ tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
+ if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemCheckMark))
+ {
+ if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemCheckMark,
+ aCtrlRegion, ControlState::ENABLED, aVal,
+ aNativeBounds, aNativeContent))
+ {
+ rCheckHeight = aNativeBounds.GetHeight() - 1;
+ nCheckWidth = aNativeContent.GetWidth() - 1;
+ }
+ }
+ if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemRadioMark))
+ {
+ if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemRadioMark,
+ aCtrlRegion, ControlState::ENABLED, aVal,
+ aNativeBounds, aNativeContent))
+ {
+ rRadioHeight = aNativeBounds.GetHeight() - 1;
+ nRadioWidth = aNativeContent.GetWidth() - 1;
+ }
+ }
+ }
+ return Size(std::max(nCheckWidth, nRadioWidth), std::max(rCheckHeight, rRadioHeight));
+}
+
+bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext const & rRenderContext, Size& rArrowSize, tools::Long& rArrowSpacing)
+{
+ ImplControlValue aVal;
+ tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15)));
+ if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
+ {
+ tools::Rectangle aNativeContent;
+ tools::Rectangle aNativeBounds;
+ if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::SubmenuArrow,
+ aCtrlRegion, ControlState::ENABLED,
+ aVal, aNativeBounds, aNativeContent))
+ {
+ Size aSize(aNativeContent.GetWidth(), aNativeContent.GetHeight());
+ rArrowSize = aSize;
+ rArrowSpacing = aNativeBounds.GetWidth() - aNativeContent.GetWidth();
+ return true;
+ }
+ }
+ return false;
+}
+
+void Menu::ImplAddDel( ImplMenuDelData& rDel )
+{
+ SAL_WARN_IF( rDel.mpMenu, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
+ if( !rDel.mpMenu )
+ {
+ rDel.mpMenu = this;
+ rDel.mpNext = mpFirstDel;
+ mpFirstDel = &rDel;
+ }
+}
+
+void Menu::ImplRemoveDel( ImplMenuDelData& rDel )
+{
+ rDel.mpMenu = nullptr;
+ if ( mpFirstDel == &rDel )
+ {
+ mpFirstDel = rDel.mpNext;
+ }
+ else
+ {
+ ImplMenuDelData* pData = mpFirstDel;
+ while ( pData && (pData->mpNext != &rDel) )
+ pData = pData->mpNext;
+
+ SAL_WARN_IF( !pData, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
+ if( pData )
+ pData->mpNext = rDel.mpNext;
+ }
+}
+
+Size Menu::ImplCalcSize( vcl::Window* pWin )
+{
+ // | Check/Radio/Image| Text| Accel/Popup|
+
+ // for symbols: nFontHeight x nFontHeight
+ tools::Long nFontHeight = pWin->GetTextHeight();
+ tools::Long nExtra = nFontHeight/4;
+
+ tools::Long nMinMenuItemHeight = nFontHeight;
+ tools::Long nCheckHeight = 0, nRadioHeight = 0;
+ Size aMarkSize = ImplGetNativeCheckAndRadioSize(*pWin->GetOutDev(), nCheckHeight, nRadioHeight);
+ if( aMarkSize.Height() > nMinMenuItemHeight )
+ nMinMenuItemHeight = aMarkSize.Height();
+
+ tools::Long aMaxImgWidth = 0;
+
+ const StyleSettings& rSettings = pWin->GetSettings().GetStyleSettings();
+ if ( rSettings.GetUseImagesInMenus() )
+ {
+ if ( 16 > nMinMenuItemHeight )
+ nMinMenuItemHeight = 16;
+ for ( size_t i = pItemList->size(); i; )
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos( --i );
+ if ( ImplIsVisible( i )
+ && ( ( pData->eType == MenuItemType::IMAGE )
+ || ( pData->eType == MenuItemType::STRINGIMAGE )
+ )
+ )
+ {
+ Size aImgSz = pData->aImage.GetSizePixel();
+ if ( aImgSz.Width() > aMaxImgWidth )
+ aMaxImgWidth = aImgSz.Width();
+ if ( aImgSz.Height() > nMinMenuItemHeight )
+ nMinMenuItemHeight = aImgSz.Height();
+ break;
+ }
+ }
+ }
+
+ Size aSz;
+ tools::Long nMaxWidth = 0;
+
+ for ( size_t n = pItemList->size(); n; )
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos( --n );
+
+ pData->aSz.setHeight( 0 );
+ pData->aSz.setWidth( 0 );
+
+ if ( ImplIsVisible( n ) )
+ {
+ tools::Long nWidth = 0;
+
+ // Separator
+ if (!IsMenuBar()&& (pData->eType == MenuItemType::SEPARATOR))
+ {
+ pData->aSz.setHeight( 4 );
+ }
+
+ // Image:
+ if (!IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
+ {
+ tools::Long aImgHeight = pData->aImage.GetSizePixel().Height();
+
+ aImgHeight += 4; // add a border for native marks
+ if (aImgHeight > pData->aSz.Height())
+ pData->aSz.setHeight(aImgHeight);
+ }
+
+ // Check Buttons:
+ if (!IsMenuBar() && pData->HasCheck())
+ {
+ // checks / images take the same place
+ if( ( pData->eType != MenuItemType::IMAGE ) && ( pData->eType != MenuItemType::STRINGIMAGE ) )
+ {
+ nWidth += aMarkSize.Width() + nExtra * 2;
+ if (aMarkSize.Height() > pData->aSz.Height())
+ pData->aSz.setHeight(aMarkSize.Height());
+ }
+ }
+
+ // Text:
+ if ( (pData->eType == MenuItemType::STRING) || (pData->eType == MenuItemType::STRINGIMAGE) )
+ {
+ const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(pWin->GetOutDev());
+ tools::Long nTextWidth = pWin->GetOutDev()->GetCtrlTextWidth(pData->aText, pGlyphs);
+ tools::Long nTextHeight = pWin->GetTextHeight() + EXTRAITEMHEIGHT;
+
+ if (IsMenuBar())
+ {
+ if ( nTextHeight > pData->aSz.Height() )
+ pData->aSz.setHeight( nTextHeight );
+
+ pData->aSz.setWidth( nTextWidth + 4*nExtra );
+ aSz.AdjustWidth(pData->aSz.Width() );
+ }
+ else
+ pData->aSz.setHeight( std::max( std::max( nTextHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
+
+ nWidth += nTextWidth;
+ }
+
+ // Accel
+ if (!IsMenuBar()&& pData->aAccelKey.GetCode() && !ImplAccelDisabled())
+ {
+ OUString aName = pData->aAccelKey.GetName();
+ tools::Long nAccWidth = pWin->GetTextWidth( aName );
+ nAccWidth += nExtra;
+ nWidth += nAccWidth;
+ }
+
+ // SubMenu?
+ if (!IsMenuBar() && pData->pSubMenu)
+ {
+ if ( nFontHeight > nWidth )
+ nWidth += nFontHeight;
+
+ pData->aSz.setHeight( std::max( std::max( nFontHeight, pData->aSz.Height() ), nMinMenuItemHeight ) );
+ }
+
+ if (!IsMenuBar())
+ aSz.AdjustHeight(pData->aSz.Height() );
+
+ if ( nWidth > nMaxWidth )
+ nMaxWidth = nWidth;
+
+ }
+ }
+
+ // Additional space for title
+ nTitleHeight = 0;
+ if (!IsMenuBar() && aTitleText.getLength() > 0) {
+ // Set expected font
+ pWin->GetOutDev()->Push(PushFlags::FONT);
+ vcl::Font aFont = pWin->GetFont();
+ aFont.SetWeight(WEIGHT_BOLD);
+ pWin->SetFont(aFont);
+
+ // Compute text bounding box
+ tools::Rectangle aTextBoundRect;
+ pWin->GetOutDev()->GetTextBoundRect(aTextBoundRect, aTitleText);
+
+ // Vertically, one height of char + extra space for decoration
+ nTitleHeight = aTextBoundRect.GetSize().Height() + 4 * SPACE_AROUND_TITLE ;
+ aSz.AdjustHeight(nTitleHeight );
+
+ tools::Long nWidth = aTextBoundRect.GetSize().Width() + 4 * SPACE_AROUND_TITLE;
+ pWin->GetOutDev()->Pop();
+ if ( nWidth > nMaxWidth )
+ nMaxWidth = nWidth;
+ }
+
+ if (!IsMenuBar())
+ {
+ // popup menus should not be wider than half the screen
+ // except on rather small screens
+ // TODO: move GetScreenNumber from SystemWindow to Window ?
+ // currently we rely on internal privileges
+ unsigned int nDisplayScreen = pWin->ImplGetWindowImpl()->mpFrame->maGeometry.screen();
+ tools::Rectangle aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen ) );
+ tools::Long nScreenWidth = aDispRect.GetWidth() >= 800 ? aDispRect.GetWidth() : 800;
+ if( nMaxWidth > nScreenWidth/2 )
+ nMaxWidth = nScreenWidth/2;
+
+ sal_uInt16 gfxExtra = static_cast<sal_uInt16>(std::max( nExtra, tools::Long(7) )); // #107710# increase space between checkmarks/images/text
+ nImgOrChkPos = static_cast<sal_uInt16>(nExtra);
+ tools::Long nImgOrChkWidth = 0;
+ if( aMarkSize.Height() > 0 ) // NWF case
+ nImgOrChkWidth = aMarkSize.Height() + nExtra;
+ else // non NWF case
+ nImgOrChkWidth = nFontHeight/2 + gfxExtra;
+ nImgOrChkWidth = std::max( nImgOrChkWidth, aMaxImgWidth + gfxExtra );
+ nTextPos = static_cast<sal_uInt16>(nImgOrChkPos + nImgOrChkWidth);
+ nTextPos = nTextPos + gfxExtra;
+
+ aSz.setWidth( nTextPos + nMaxWidth + nExtra );
+ aSz.AdjustWidth(4*nExtra ); // a _little_ more ...
+
+ aSz.AdjustWidth(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderX );
+ aSz.AdjustHeight(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
+ }
+ else
+ {
+ nTextPos = static_cast<sal_uInt16>(2*nExtra);
+ aSz.setHeight( nFontHeight+6 );
+
+ // get menubar height from native methods if supported
+ if( pWindow->IsNativeControlSupported( ControlType::Menubar, ControlPart::Entire ) )
+ {
+ ImplControlValue aVal;
+ tools::Rectangle aNativeBounds;
+ tools::Rectangle aNativeContent;
+ Point tmp( 0, 0 );
+ tools::Rectangle aCtrlRegion( tmp, Size( 100, 15 ) );
+ if( pWindow->GetNativeControlRegion( ControlType::Menubar,
+ ControlPart::Entire,
+ aCtrlRegion,
+ ControlState::ENABLED,
+ aVal,
+ aNativeBounds,
+ aNativeContent )
+ )
+ {
+ int nNativeHeight = aNativeBounds.GetHeight();
+ if( nNativeHeight > aSz.Height() )
+ aSz.setHeight( nNativeHeight );
+ }
+ }
+
+ // account for the size of the close button, which actually is a toolbox
+ // due to NWF this is variable
+ tools::Long nCloseButtonHeight = static_cast<MenuBarWindow*>(pWindow.get())->MinCloseButtonSize().Height();
+ if (aSz.Height() < nCloseButtonHeight)
+ aSz.setHeight( nCloseButtonHeight );
+ }
+
+ return aSz;
+}
+
+static void ImplPaintCheckBackground(vcl::RenderContext & rRenderContext, vcl::Window const & rWindow, const tools::Rectangle& i_rRect, bool i_bHighlight)
+{
+ bool bNativeOk = false;
+ if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Button))
+ {
+ ImplControlValue aControlValue;
+ aControlValue.setTristateVal(ButtonValue::On);
+ tools::Rectangle r = i_rRect;
+ r.AdjustBottom(1);
+
+ bNativeOk = rRenderContext.DrawNativeControl(ControlType::Toolbar, ControlPart::Button,
+ r,
+ ControlState::PRESSED | ControlState::ENABLED,
+ aControlValue,
+ OUString());
+ }
+
+ if (!bNativeOk)
+ {
+ const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
+ Color aColor( i_bHighlight ? rSettings.GetMenuHighlightTextColor() : rSettings.GetHighlightColor() );
+ RenderTools::DrawSelectionBackground(rRenderContext, rWindow, i_rRect, 0, i_bHighlight, true, false, nullptr, 2, &aColor);
+ }
+}
+
+static OUString getShortenedString( const OUString& i_rLong, vcl::RenderContext const & rRenderContext, tools::Long i_nMaxWidth )
+{
+ sal_Int32 nPos = -1;
+ OUString aNonMnem(removeMnemonicFromString(i_rLong, nPos));
+ aNonMnem = rRenderContext.GetEllipsisString( aNonMnem, i_nMaxWidth, DrawTextFlags::CenterEllipsis);
+ // re-insert mnemonic
+ if (nPos != -1)
+ {
+ if (nPos < aNonMnem.getLength() && i_rLong[nPos+1] == aNonMnem[nPos])
+ {
+ OUString aTmp = OUString::Concat(aNonMnem.subView(0, nPos)) + "~" + aNonMnem.subView(nPos);
+ aNonMnem = aTmp;
+ }
+ }
+ return aNonMnem;
+}
+
+void Menu::ImplPaintMenuTitle(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) const
+{
+ // Save previous graphical settings, set new one
+ rRenderContext.Push(PushFlags::FONT | PushFlags::FILLCOLOR);
+ Wallpaper aOldBackground = rRenderContext.GetBackground();
+
+ Color aBackgroundColor = rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor();
+ rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
+ rRenderContext.SetFillColor(aBackgroundColor);
+ vcl::Font aFont = rRenderContext.GetFont();
+ aFont.SetWeight(WEIGHT_BOLD);
+ rRenderContext.SetFont(aFont);
+
+ // Draw background rectangle
+ tools::Rectangle aBgRect(rRect);
+ int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
+ aBgRect.Move(SPACE_AROUND_TITLE, SPACE_AROUND_TITLE);
+ aBgRect.setWidth(aBgRect.getOpenWidth() - 2 * SPACE_AROUND_TITLE - 2 * nOuterSpaceX);
+ aBgRect.setHeight(nTitleHeight - 2 * SPACE_AROUND_TITLE);
+ rRenderContext.DrawRect(aBgRect);
+
+ // Draw the text centered
+ Point aTextTopLeft(aBgRect.TopLeft());
+ tools::Rectangle aTextBoundRect;
+ rRenderContext.GetTextBoundRect( aTextBoundRect, aTitleText );
+ aTextTopLeft.AdjustX((aBgRect.getOpenWidth() - aTextBoundRect.GetSize().Width()) / 2 );
+ aTextTopLeft.AdjustY((aBgRect.GetHeight() - aTextBoundRect.GetSize().Height()) / 2
+ - aTextBoundRect.Top() );
+ rRenderContext.DrawText(aTextTopLeft, aTitleText, 0, aTitleText.getLength());
+
+ // Restore
+ rRenderContext.Pop();
+ rRenderContext.SetBackground(aOldBackground);
+}
+
+void Menu::ImplPaint(vcl::RenderContext& rRenderContext, Size const & rSize,
+ sal_uInt16 nBorder, tools::Long nStartY, MenuItemData const * pThisItemOnly,
+ bool bHighlighted, bool bLayout, bool bRollover) const
+{
+ // for symbols: nFontHeight x nFontHeight
+ tools::Long nFontHeight = rRenderContext.GetTextHeight();
+ tools::Long nExtra = nFontHeight / 4;
+
+ tools::Long nCheckHeight = 0, nRadioHeight = 0;
+ ImplGetNativeCheckAndRadioSize(rRenderContext, nCheckHeight, nRadioHeight);
+
+ DecorationView aDecoView(&rRenderContext);
+ const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ Point aTopLeft, aTmpPos;
+
+ int nOuterSpaceX = 0;
+ if (!IsMenuBar())
+ {
+ nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
+ aTopLeft.AdjustX(nOuterSpaceX );
+ aTopLeft.AdjustY(ImplGetSVData()->maNWFData.mnMenuFormatBorderY );
+ }
+
+ // for the computations, use size of the underlying window, not of RenderContext
+ Size aOutSz(rSize);
+
+ size_t nCount = pItemList->size();
+ if (bLayout)
+ mpLayoutData->m_aVisibleItemBoundRects.clear();
+
+ // Paint title
+ if (!pThisItemOnly && !IsMenuBar() && nTitleHeight > 0)
+ ImplPaintMenuTitle(rRenderContext, tools::Rectangle(aTopLeft, aOutSz));
+
+ bool bHiddenItems = false; // are any items on the GUI hidden
+
+ for (size_t n = 0; n < nCount; n++)
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos( n );
+ if (ImplIsVisible(n) && (!pThisItemOnly || (pData == pThisItemOnly)))
+ {
+ if (pThisItemOnly)
+ {
+ if (IsMenuBar())
+ {
+ if (!ImplGetSVData()->maNWFData.mbRolloverMenubar)
+ {
+ if (bRollover)
+ rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
+ else if (bHighlighted)
+ rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
+ }
+ else
+ {
+ if (bHighlighted)
+ rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor());
+ else if (bRollover)
+ rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor());
+ }
+ if (!bRollover && !bHighlighted)
+ rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
+ }
+ else if (bHighlighted)
+ rRenderContext.SetTextColor(rSettings.GetMenuHighlightTextColor());
+ }
+
+ Point aPos(aTopLeft);
+ aPos.AdjustY(nBorder );
+ aPos.AdjustY(nStartY );
+
+ if (aPos.Y() >= 0)
+ {
+ tools::Long nTextOffsetY = (pData->aSz.Height() - nFontHeight) / 2;
+ if (IsMenuBar())
+ nTextOffsetY += (aOutSz.Height()-pData->aSz.Height()) / 2;
+ DrawTextFlags nTextStyle = DrawTextFlags::NONE;
+ DrawSymbolFlags nSymbolStyle = DrawSymbolFlags::NONE;
+ DrawImageFlags nImageStyle = DrawImageFlags::NONE;
+
+ // submenus without items are not disabled when no items are
+ // contained. The application itself should check for this!
+ // Otherwise it could happen entries are disabled due to
+ // asynchronous loading
+ if (!pData->bEnabled || !pWindow->IsEnabled())
+ {
+ nTextStyle |= DrawTextFlags::Disable;
+ nSymbolStyle |= DrawSymbolFlags::Disable;
+ nImageStyle |= DrawImageFlags::Disable;
+ }
+
+ // Separator
+ if (!bLayout && !IsMenuBar() && (pData->eType == MenuItemType::SEPARATOR))
+ {
+ bool bNativeOk = false;
+ if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Separator))
+ {
+ ControlState nState = ControlState::NONE;
+ if (pData->bEnabled && pWindow->IsEnabled())
+ nState |= ControlState::ENABLED;
+ if (bHighlighted)
+ nState |= ControlState::SELECTED;
+ Size aSz(pData->aSz);
+ aSz.setWidth( aOutSz.Width() - 2*nOuterSpaceX );
+ tools::Rectangle aItemRect(aPos, aSz);
+ MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
+ bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Separator,
+ aItemRect, nState, aVal, OUString());
+ }
+ if (!bNativeOk)
+ {
+ aTmpPos.setY( aPos.Y() + ((pData->aSz.Height() - 2) / 2) );
+ aTmpPos.setX( aPos.X() + 2 + nOuterSpaceX );
+ rRenderContext.SetLineColor(rSettings.GetShadowColor());
+ rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
+ aTmpPos.AdjustY( 1 );
+ rRenderContext.SetLineColor(rSettings.GetLightColor());
+ rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y()));
+ rRenderContext.SetLineColor();
+ }
+ }
+
+ tools::Rectangle aOuterCheckRect(Point(aPos.X()+nImgOrChkPos, aPos.Y()),
+ Size(pData->aSz.Height(), pData->aSz.Height()));
+
+ // CheckMark
+ if (!bLayout && !IsMenuBar() && pData->HasCheck())
+ {
+ // draw selection transparent marker if checked
+ // onto that either a checkmark or the item image
+ // will be painted
+ // however do not do this if native checks will be painted since
+ // the selection color too often does not fit the theme's check and/or radio
+
+ if( (pData->eType != MenuItemType::IMAGE) && (pData->eType != MenuItemType::STRINGIMAGE))
+ {
+ if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup,
+ (pData->nBits & MenuItemBits::RADIOCHECK)
+ ? ControlPart::MenuItemCheckMark
+ : ControlPart::MenuItemRadioMark))
+ {
+ ControlPart nPart = ((pData->nBits & MenuItemBits::RADIOCHECK)
+ ? ControlPart::MenuItemRadioMark
+ : ControlPart::MenuItemCheckMark);
+
+ ControlState nState = ControlState::NONE;
+
+ if (pData->bChecked)
+ nState |= ControlState::PRESSED;
+
+ if (pData->bEnabled && pWindow->IsEnabled())
+ nState |= ControlState::ENABLED;
+
+ if (bHighlighted)
+ nState |= ControlState::SELECTED;
+
+ tools::Long nCtrlHeight = (pData->nBits & MenuItemBits::RADIOCHECK) ? nCheckHeight : nRadioHeight;
+ aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - nCtrlHeight) / 2 );
+ aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - nCtrlHeight) / 2 );
+
+ tools::Rectangle aCheckRect(aTmpPos, Size(nCtrlHeight, nCtrlHeight));
+ Size aSz(pData->aSz);
+ aSz.setWidth( aOutSz.Width() - 2 * nOuterSpaceX );
+ tools::Rectangle aItemRect(aPos, aSz);
+ MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
+ rRenderContext.DrawNativeControl(ControlType::MenuPopup, nPart, aCheckRect,
+ nState, aVal, OUString());
+ }
+ else if (pData->bChecked) // by default do nothing for unchecked items
+ {
+ ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
+
+ SymbolType eSymbol;
+ Size aSymbolSize;
+ if (pData->nBits & MenuItemBits::RADIOCHECK)
+ {
+ eSymbol = SymbolType::RADIOCHECKMARK;
+ aSymbolSize = Size(nFontHeight / 2, nFontHeight / 2);
+ }
+ else
+ {
+ eSymbol = SymbolType::CHECKMARK;
+ aSymbolSize = Size((nFontHeight * 25) / 40, nFontHeight / 2);
+ }
+ aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - aSymbolSize.Width()) / 2 );
+ aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - aSymbolSize.Height()) / 2 );
+ tools::Rectangle aRect(aTmpPos, aSymbolSize);
+ aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetTextColor(), nSymbolStyle);
+ }
+ }
+ }
+
+ // Image:
+ if (!bLayout && !IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE)))
+ {
+ // Don't render an image for a check thing
+ if (pData->bChecked)
+ ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted);
+
+ Image aImage = pData->aImage;
+
+ aTmpPos = aOuterCheckRect.TopLeft();
+ aTmpPos.AdjustX((aOuterCheckRect.GetWidth() - aImage.GetSizePixel().Width()) / 2 );
+ aTmpPos.AdjustY((aOuterCheckRect.GetHeight() - aImage.GetSizePixel().Height()) / 2 );
+ rRenderContext.DrawImage(aTmpPos, aImage, nImageStyle);
+ }
+
+ // Text:
+ if ((pData->eType == MenuItemType::STRING ) || (pData->eType == MenuItemType::STRINGIMAGE))
+ {
+ aTmpPos.setX( aPos.X() + nTextPos );
+ aTmpPos.setY( aPos.Y() );
+ aTmpPos.AdjustY(nTextOffsetY );
+ DrawTextFlags nStyle = nTextStyle | DrawTextFlags::Mnemonic;
+
+ if (pData->bIsTemporary)
+ nStyle |= DrawTextFlags::Disable;
+ std::vector< tools::Rectangle >* pVector = bLayout ? &mpLayoutData->m_aUnicodeBoundRects : nullptr;
+ OUString* pDisplayText = bLayout ? &mpLayoutData->m_aDisplayText : nullptr;
+ if (bLayout)
+ {
+ mpLayoutData->m_aLineIndices.push_back(mpLayoutData->m_aDisplayText.getLength());
+ mpLayoutData->m_aLineItemIds.push_back(pData->nId);
+ }
+ // #i47946# with NWF painted menus the background is transparent
+ // since DrawCtrlText can depend on the background (e.g. for
+ // DrawTextFlags::Disable), temporarily set a background which
+ // hopefully matches the NWF background since it is read
+ // from the system style settings
+ bool bSetTmpBackground = !rRenderContext.IsBackground()
+ && rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire);
+ if (bSetTmpBackground)
+ {
+ Color aBg = IsMenuBar() ? rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor()
+ : rRenderContext.GetSettings().GetStyleSettings().GetMenuColor();
+ rRenderContext.SetBackground(Wallpaper(aBg));
+ }
+ // how much space is there for the text?
+ tools::Long nMaxItemTextWidth = aOutSz.Width() - aTmpPos.X() - nExtra - nOuterSpaceX;
+ if (!IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled())
+ {
+ OUString aAccText = pData->aAccelKey.GetName();
+ nMaxItemTextWidth -= rRenderContext.GetTextWidth(aAccText) + 3 * nExtra;
+ }
+ if (!IsMenuBar() && pData->pSubMenu)
+ {
+ nMaxItemTextWidth -= nFontHeight - nExtra;
+ }
+
+ OUString aItemText(pData->aText);
+ pData->bHiddenOnGUI = false;
+
+ if (IsMenuBar()) // In case of menubar if we are out of bounds we shouldn't paint the item
+ {
+ if (nMaxItemTextWidth < rRenderContext.GetTextWidth(aItemText))
+ {
+ aItemText = "";
+ pData->bHiddenOnGUI = true;
+ bHiddenItems = true;
+ }
+ }
+ else
+ {
+ aItemText = getShortenedString(aItemText, rRenderContext, nMaxItemTextWidth);
+ pData->bHiddenOnGUI = false;
+ }
+
+ const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(&rRenderContext);
+ if (aItemText != pData->aText)
+ // Can't use pre-computed glyphs, item text was
+ // changed.
+ pGlyphs = nullptr;
+ rRenderContext.DrawCtrlText(aTmpPos, aItemText, 0, aItemText.getLength(),
+ nStyle, pVector, pDisplayText, pGlyphs);
+ if (bSetTmpBackground)
+ rRenderContext.SetBackground();
+ }
+
+ // Accel
+ if (!bLayout && !IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled())
+ {
+ OUString aAccText = pData->aAccelKey.GetName();
+ aTmpPos.setX( aOutSz.Width() - rRenderContext.GetTextWidth(aAccText) );
+ aTmpPos.AdjustX( -(4 * nExtra) );
+
+ aTmpPos.AdjustX( -nOuterSpaceX );
+ aTmpPos.setY( aPos.Y() );
+ aTmpPos.AdjustY(nTextOffsetY );
+ rRenderContext.DrawCtrlText(aTmpPos, aAccText, 0, aAccText.getLength(), nTextStyle);
+ }
+
+ // SubMenu?
+ if (!bLayout && !IsMenuBar() && pData->pSubMenu)
+ {
+ bool bNativeOk = false;
+ if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow))
+ {
+ ControlState nState = ControlState::NONE;
+ Size aTmpSz(0, 0);
+ tools::Long aSpacing = 0;
+
+ if (!ImplGetNativeSubmenuArrowSize(rRenderContext, aTmpSz, aSpacing))
+ {
+ aTmpSz = Size(nFontHeight, nFontHeight);
+ aSpacing = nOuterSpaceX;
+ }
+
+ if (pData->bEnabled && pWindow->IsEnabled())
+ nState |= ControlState::ENABLED;
+ if (bHighlighted)
+ nState |= ControlState::SELECTED;
+
+ aTmpPos.setX( aOutSz.Width() - aTmpSz.Width() - aSpacing - nOuterSpaceX );
+ aTmpPos.setY( aPos.Y() + ( pData->aSz.Height() - aTmpSz.Height() ) / 2 );
+ aTmpPos.AdjustY(nExtra / 2 );
+
+ tools::Rectangle aItemRect(aTmpPos, aTmpSz);
+ MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect);
+ bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::SubmenuArrow,
+ aItemRect, nState, aVal, OUString());
+ }
+ if (!bNativeOk)
+ {
+ aTmpPos.setX( aOutSz.Width() - nFontHeight + nExtra - nOuterSpaceX );
+ aTmpPos.setY( aPos.Y() );
+ aTmpPos.AdjustY(nExtra/2 );
+ aTmpPos.AdjustY((pData->aSz.Height() / 2) - (nFontHeight / 4) );
+ if (pData->nBits & MenuItemBits::POPUPSELECT)
+ {
+ rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
+ Point aTmpPos2(aPos);
+ aTmpPos2.setX( aOutSz.Width() - nFontHeight - nFontHeight/4 );
+ aDecoView.DrawFrame(tools::Rectangle(aTmpPos2, Size(nFontHeight + nFontHeight / 4,
+ pData->aSz.Height())),
+ DrawFrameStyle::Group);
+ }
+ aDecoView.DrawSymbol(tools::Rectangle(aTmpPos, Size(nFontHeight / 2, nFontHeight / 2)),
+ SymbolType::SPIN_RIGHT, rRenderContext.GetTextColor(), nSymbolStyle);
+ }
+ }
+
+ if (pThisItemOnly && bHighlighted)
+ {
+ // This restores the normal menu or menu bar text
+ // color for when it is no longer highlighted.
+ if (IsMenuBar())
+ rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor());
+ else
+ rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
+ }
+ }
+ if( bLayout )
+ {
+ if (!IsMenuBar())
+ mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, Size(aOutSz.Width(), pData->aSz.Height()));
+ else
+ mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, pData->aSz);
+ }
+ }
+
+ if (!IsMenuBar())
+ aTopLeft.AdjustY(pData->aSz.Height() );
+ else
+ aTopLeft.AdjustX(pData->aSz.Width() );
+ }
+
+ // draw "more" (">>") indicator if some items have been hidden as they go out of visible area
+ if (bHiddenItems)
+ {
+ sal_Int32 nSize = nFontHeight;
+ tools::Rectangle aRectangle(Point(aOutSz.Width() - nSize, (aOutSz.Height() / 2) - (nSize / 2)), Size(nSize, nSize));
+ lclDrawMoreIndicator(rRenderContext, aRectangle);
+ }
+}
+
+Menu* Menu::ImplGetStartMenu()
+{
+ Menu* pStart = this;
+ while ( pStart && pStart->pStartedFrom && ( pStart->pStartedFrom != pStart ) )
+ pStart = pStart->pStartedFrom;
+ return pStart;
+}
+
+void Menu::ImplCallHighlight(sal_uInt16 nItem)
+{
+ ImplMenuDelData aDelData( this );
+
+ nSelectedId = 0;
+ sSelectedIdent.clear();
+ MenuItemData* pData = pItemList->GetDataFromPos(nItem);
+ if (pData)
+ {
+ nSelectedId = pData->nId;
+ sSelectedIdent = pData->sIdent;
+ }
+ ImplCallEventListeners( VclEventId::MenuHighlight, GetItemPos( GetCurItemId() ) );
+
+ if( !aDelData.isDeleted() )
+ {
+ nSelectedId = 0;
+ sSelectedIdent.clear();
+ }
+}
+
+IMPL_LINK_NOARG(Menu, ImplCallSelect, void*, void)
+{
+ nEventId = nullptr;
+ Select();
+}
+
+Menu* Menu::ImplFindSelectMenu()
+{
+ Menu* pSelMenu = nEventId ? this : nullptr;
+
+ for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
+ {
+ MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
+
+ if ( pData->pSubMenu )
+ pSelMenu = pData->pSubMenu->ImplFindSelectMenu();
+ }
+
+ return pSelMenu;
+}
+
+Menu* Menu::ImplFindMenu( sal_uInt16 nItemId )
+{
+ Menu* pSelMenu = nullptr;
+
+ for ( size_t n = GetItemList()->size(); n && !pSelMenu; )
+ {
+ MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
+
+ if( pData->nId == nItemId )
+ pSelMenu = this;
+ else if ( pData->pSubMenu )
+ pSelMenu = pData->pSubMenu->ImplFindMenu( nItemId );
+ }
+
+ return pSelMenu;
+}
+
+void Menu::RemoveDisabledEntries( bool bRemoveEmptyPopups )
+{
+ for ( sal_uInt16 n = 0; n < GetItemCount(); n++ )
+ {
+ bool bRemove = false;
+ MenuItemData* pItem = pItemList->GetDataFromPos( n );
+ if ( pItem->eType == MenuItemType::SEPARATOR )
+ {
+ if ( !n || ( GetItemType( n-1 ) == MenuItemType::SEPARATOR ) )
+ bRemove = true;
+ }
+ else
+ bRemove = !pItem->bEnabled;
+
+ if ( pItem->pSubMenu )
+ {
+ pItem->pSubMenu->RemoveDisabledEntries();
+ if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() )
+ bRemove = true;
+ }
+
+ if ( bRemove )
+ RemoveItem( n-- );
+ }
+
+ if ( GetItemCount() )
+ {
+ sal_uInt16 nLast = GetItemCount() - 1;
+ MenuItemData* pItem = pItemList->GetDataFromPos( nLast );
+ if ( pItem->eType == MenuItemType::SEPARATOR )
+ RemoveItem( nLast );
+ }
+ mpLayoutData.reset();
+}
+
+void Menu::UpdateNativeMenu()
+{
+ if ( ImplGetSalMenu() )
+ ImplGetSalMenu()->Update();
+}
+
+void Menu::MenuBarKeyInput(const KeyEvent&)
+{
+}
+
+void Menu::ImplKillLayoutData() const
+{
+ mpLayoutData.reset();
+}
+
+void Menu::ImplFillLayoutData() const
+{
+ if (!(pWindow && pWindow->IsReallyVisible()))
+ return;
+
+ mpLayoutData.reset(new MenuLayoutData);
+ if (IsMenuBar())
+ {
+ ImplPaint(*pWindow->GetOutDev(), pWindow->GetOutputSizePixel(), 0, 0, nullptr, false, true); // FIXME
+ }
+ else
+ {
+ MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(pWindow.get());
+ ImplPaint(*pWindow->GetOutDev(), pWindow->GetOutputSizePixel(), pFloat->nScrollerHeight, pFloat->ImplGetStartY(),
+ nullptr, false, true); //FIXME
+ }
+}
+
+tools::Rectangle Menu::GetCharacterBounds( sal_uInt16 nItemID, tools::Long nIndex ) const
+{
+ tools::Long nItemIndex = -1;
+ if( ! mpLayoutData )
+ ImplFillLayoutData();
+ if( mpLayoutData )
+ {
+ for( size_t i = 0; i < mpLayoutData->m_aLineItemIds.size(); i++ )
+ {
+ if( mpLayoutData->m_aLineItemIds[i] == nItemID )
+ {
+ nItemIndex = mpLayoutData->m_aLineIndices[i];
+ break;
+ }
+ }
+ }
+ return (mpLayoutData && nItemIndex != -1) ? mpLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : tools::Rectangle();
+}
+
+tools::Long Menu::GetIndexForPoint( const Point& rPoint, sal_uInt16& rItemID ) const
+{
+ tools::Long nIndex = -1;
+ rItemID = 0;
+ if( ! mpLayoutData )
+ ImplFillLayoutData();
+ if( mpLayoutData )
+ {
+ nIndex = mpLayoutData->GetIndexForPoint( rPoint );
+ for( size_t i = 0; i < mpLayoutData->m_aLineIndices.size(); i++ )
+ {
+ if( mpLayoutData->m_aLineIndices[i] <= nIndex &&
+ (i == mpLayoutData->m_aLineIndices.size()-1 || mpLayoutData->m_aLineIndices[i+1] > nIndex) )
+ {
+ // make index relative to item
+ nIndex -= mpLayoutData->m_aLineIndices[i];
+ rItemID = mpLayoutData->m_aLineItemIds[i];
+ break;
+ }
+ }
+ }
+ return nIndex;
+}
+
+tools::Rectangle Menu::GetBoundingRectangle( sal_uInt16 nPos ) const
+{
+ tools::Rectangle aRet;
+
+ if (!mpLayoutData )
+ ImplFillLayoutData();
+ if (mpLayoutData)
+ {
+ std::map< sal_uInt16, tools::Rectangle >::const_iterator it = mpLayoutData->m_aVisibleItemBoundRects.find( nPos );
+ if( it != mpLayoutData->m_aVisibleItemBoundRects.end() )
+ aRet = it->second;
+ }
+ return aRet;
+}
+
+void Menu::SetAccessibleName( sal_uInt16 nItemId, const OUString& rStr )
+{
+ size_t nPos;
+ MenuItemData* pData = pItemList->GetData( nItemId, nPos );
+
+ if (pData && !rStr.equals(pData->aAccessibleName))
+ {
+ pData->aAccessibleName = rStr;
+ ImplCallEventListeners(VclEventId::MenuAccessibleNameChanged, nPos);
+ }
+}
+
+OUString Menu::GetAccessibleName( sal_uInt16 nItemId ) const
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ return pData->aAccessibleName;
+
+ return OUString();
+}
+
+void Menu::SetAccessibleDescription( sal_uInt16 nItemId, const OUString& rStr )
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if ( pData )
+ pData->aAccessibleDescription = rStr;
+}
+
+OUString Menu::GetAccessibleDescription( sal_uInt16 nItemId ) const
+{
+ MenuItemData* pData = pItemList->GetData( nItemId );
+
+ if (pData && !pData->aAccessibleDescription.isEmpty())
+ return pData->aAccessibleDescription;
+
+ return GetHelpText(nItemId);
+}
+
+void Menu::GetSystemMenuData( SystemMenuData* pData ) const
+{
+ Menu* pMenu = const_cast<Menu*>(this);
+ if( pData && pMenu->ImplGetSalMenu() )
+ {
+ pMenu->ImplGetSalMenu()->GetSystemMenuData( pData );
+ }
+}
+
+bool Menu::IsHighlighted( sal_uInt16 nItemPos ) const
+{
+ bool bRet = false;
+
+ if( pWindow )
+ {
+ if (IsMenuBar())
+ bRet = ( nItemPos == static_cast< MenuBarWindow * > (pWindow.get())->GetHighlightedItem() );
+ else
+ bRet = ( nItemPos == static_cast< MenuFloatingWindow * > (pWindow.get())->GetHighlightedItem() );
+ }
+
+ return bRet;
+}
+
+void Menu::HighlightItem( sal_uInt16 nItemPos )
+{
+ if ( !pWindow )
+ return;
+
+ if (IsMenuBar())
+ {
+ MenuBarWindow* pMenuWin = static_cast< MenuBarWindow* >( pWindow.get() );
+ pMenuWin->SetAutoPopup( false );
+ pMenuWin->ChangeHighlightItem( nItemPos, false );
+ }
+ else
+ {
+ static_cast< MenuFloatingWindow* >( pWindow.get() )->ChangeHighlightItem( nItemPos, false );
+ }
+}
+
+MenuBarWindow* MenuBar::getMenuBarWindow()
+{
+ // so far just a dynamic_cast, hopefully to be turned into something saner
+ // at some stage
+ MenuBarWindow *pWin = dynamic_cast<MenuBarWindow*>(pWindow.get());
+ //either there is no window (fdo#87663) or it is a MenuBarWindow
+ assert(!pWindow || pWin);
+ return pWin;
+}
+
+MenuBar::MenuBar()
+ : mbCloseBtnVisible(false),
+ mbFloatBtnVisible(false),
+ mbHideBtnVisible(false),
+ mbDisplayable(true)
+{
+ mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
+}
+
+MenuBar::MenuBar( const MenuBar& rMenu )
+ : mbCloseBtnVisible(false),
+ mbFloatBtnVisible(false),
+ mbHideBtnVisible(false),
+ mbDisplayable(true)
+{
+ mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this);
+ *this = rMenu;
+}
+
+MenuBar::~MenuBar()
+{
+ disposeOnce();
+}
+
+void MenuBar::dispose()
+{
+ ImplDestroy( this, true );
+ Menu::dispose();
+}
+
+void MenuBar::ClosePopup(Menu *pMenu)
+{
+ MenuBarWindow* pMenuWin = getMenuBarWindow();
+ if (!pMenuWin)
+ return;
+ pMenuWin->PopupClosed(pMenu);
+}
+
+void MenuBar::MenuBarKeyInput(const KeyEvent& rEvent)
+{
+ pWindow->KeyInput(rEvent);
+}
+
+void MenuBar::ShowCloseButton(bool bShow)
+{
+ ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible );
+}
+
+void MenuBar::ShowButtons( bool bClose, bool bFloat, bool bHide )
+{
+ if ((bClose != mbCloseBtnVisible) ||
+ (bFloat != mbFloatBtnVisible) ||
+ (bHide != mbHideBtnVisible))
+ {
+ mbCloseBtnVisible = bClose;
+ mbFloatBtnVisible = bFloat;
+ mbHideBtnVisible = bHide;
+ MenuBarWindow* pMenuWin = getMenuBarWindow();
+ if (pMenuWin)
+ pMenuWin->ShowButtons(bClose, bFloat, bHide);
+ }
+}
+
+void MenuBar::LayoutChanged()
+{
+ MenuBarWindow* pMenuWin = getMenuBarWindow();
+ if (pMenuWin)
+ pMenuWin->LayoutChanged();
+}
+
+void MenuBar::SetDisplayable( bool bDisplayable )
+{
+ if( bDisplayable != mbDisplayable )
+ {
+ if ( ImplGetSalMenu() )
+ ImplGetSalMenu()->ShowMenuBar( bDisplayable );
+
+ mbDisplayable = bDisplayable;
+ LayoutChanged();
+ }
+}
+
+VclPtr<vcl::Window> MenuBar::ImplCreate(vcl::Window* pParent, vcl::Window* pWindow, MenuBar* pMenu)
+{
+ VclPtr<MenuBarWindow> pMenuBarWindow = dynamic_cast<MenuBarWindow*>(pWindow);
+ if (!pMenuBarWindow)
+ {
+ pWindow = pMenuBarWindow = VclPtr<MenuBarWindow>::Create( pParent );
+ }
+
+ pMenu->pStartedFrom = nullptr;
+ pMenu->pWindow = pWindow;
+ pMenuBarWindow->SetMenu(pMenu);
+ tools::Long nHeight = pWindow ? pMenu->ImplCalcSize(pWindow).Height() : 0;
+
+ // depending on the native implementation or the displayable flag
+ // the menubar windows is suppressed (ie, height=0)
+ if (!pMenu->IsDisplayable() || (pMenu->ImplGetSalMenu() && pMenu->ImplGetSalMenu()->VisibleMenuBar()))
+ {
+ nHeight = 0;
+ }
+
+ pMenuBarWindow->SetHeight(nHeight);
+ return pWindow;
+}
+
+void MenuBar::ImplDestroy( MenuBar* pMenu, bool bDelete )
+{
+ vcl::Window *pWindow = pMenu->ImplGetWindow();
+ if (pWindow && bDelete)
+ {
+ MenuBarWindow* pMenuWin = pMenu->getMenuBarWindow();
+ if (pMenuWin)
+ pMenuWin->KillActivePopup();
+ pWindow->disposeOnce();
+ }
+ pMenu->pWindow = nullptr;
+ if (pMenu->mpSalMenu) {
+ pMenu->mpSalMenu->ShowMenuBar(false);
+ }
+}
+
+bool MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent )
+{
+ // No keyboard processing when our menubar is invisible
+ if (!IsDisplayable())
+ return false;
+
+ // No keyboard processing when system handles the menu.
+ SalMenu *pNativeMenu = ImplGetSalMenu();
+ if (pNativeMenu && pNativeMenu->VisibleMenuBar())
+ {
+ // Except when the event is the F6 cycle pane event and we can put our
+ // focus into it (i.e. the gtk3 menubar case but not the mac/unity case
+ // where it's not part of the application window)
+ if (!TaskPaneList::IsCycleKey(rKEvent.GetKeyCode()))
+ return false;
+ if (!pNativeMenu->CanGetFocus())
+ return false;
+ }
+
+ bool bDone = false;
+ // check for enabled, if this method is called from another window...
+ vcl::Window* pWin = ImplGetWindow();
+ if (pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && !pWin->IsInModalMode())
+ {
+ MenuBarWindow* pMenuWin = getMenuBarWindow();
+ bDone = pMenuWin && pMenuWin->HandleKeyEvent(rKEvent, false/*bFromMenu*/);
+ }
+ return bDone;
+}
+
+void MenuBar::SelectItem(sal_uInt16 nId)
+{
+ if (!pWindow)
+ return;
+
+ pWindow->GrabFocus();
+ nId = GetItemPos( nId );
+
+ MenuBarWindow* pMenuWin = getMenuBarWindow();
+ if (pMenuWin)
+ {
+ // #99705# popup the selected menu
+ pMenuWin->SetAutoPopup( true );
+ if (ITEMPOS_INVALID != pMenuWin->GetHighlightedItem())
+ {
+ pMenuWin->KillActivePopup();
+ pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, false );
+ }
+ if (nId != ITEMPOS_INVALID)
+ pMenuWin->ChangeHighlightItem( nId, false );
+ }
+}
+
+// handler for native menu selection and command events
+bool Menu::HandleMenuActivateEvent( Menu *pMenu ) const
+{
+ if( pMenu )
+ {
+ ImplMenuDelData aDelData( this );
+
+ pMenu->pStartedFrom = const_cast<Menu*>(this);
+ pMenu->bInCallback = true;
+ pMenu->Activate();
+
+ if( !aDelData.isDeleted() )
+ pMenu->bInCallback = false;
+ }
+ return true;
+}
+
+bool Menu::HandleMenuDeActivateEvent( Menu *pMenu ) const
+{
+ if( pMenu )
+ {
+ ImplMenuDelData aDelData( this );
+
+ pMenu->pStartedFrom = const_cast<Menu*>(this);
+ pMenu->bInCallback = true;
+ pMenu->Deactivate();
+ if( !aDelData.isDeleted() )
+ pMenu->bInCallback = false;
+ }
+ return true;
+}
+
+bool MenuBar::HandleMenuHighlightEvent( Menu *pMenu, sal_uInt16 nHighlightEventId ) const
+{
+ if( !pMenu )
+ pMenu = const_cast<MenuBar*>(this)->ImplFindMenu(nHighlightEventId);
+ if( pMenu )
+ {
+ ImplMenuDelData aDelData( pMenu );
+
+ if( mnHighlightedItemPos != ITEMPOS_INVALID )
+ pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, mnHighlightedItemPos );
+
+ if( !aDelData.isDeleted() )
+ {
+ pMenu->mnHighlightedItemPos = pMenu->GetItemPos( nHighlightEventId );
+ pMenu->nSelectedId = nHighlightEventId;
+ pMenu->sSelectedIdent = pMenu->GetItemIdent( nHighlightEventId );
+ pMenu->pStartedFrom = const_cast<MenuBar*>(this);
+ pMenu->ImplCallHighlight( pMenu->mnHighlightedItemPos );
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+bool Menu::HandleMenuCommandEvent( Menu *pMenu, sal_uInt16 nCommandEventId ) const
+{
+ if( !pMenu )
+ pMenu = const_cast<Menu*>(this)->ImplFindMenu(nCommandEventId);
+ if( pMenu )
+ {
+ pMenu->nSelectedId = nCommandEventId;
+ pMenu->sSelectedIdent = pMenu->GetItemIdent(nCommandEventId);
+ pMenu->pStartedFrom = const_cast<Menu*>(this);
+ pMenu->ImplSelect();
+ return true;
+ }
+ else
+ return false;
+}
+
+sal_uInt16 MenuBar::AddMenuBarButton( const Image& i_rImage, const Link<MenuBarButtonCallbackArg&,bool>& i_rLink, const OUString& i_rToolTip )
+{
+ MenuBarWindow* pMenuWin = getMenuBarWindow();
+ return pMenuWin ? pMenuWin->AddMenuBarButton(i_rImage, i_rLink, i_rToolTip) : 0;
+}
+
+void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link<MenuBarButtonCallbackArg&,bool>& rLink )
+{
+ MenuBarWindow* pMenuWin = getMenuBarWindow();
+ if (!pMenuWin)
+ return;
+ pMenuWin->SetMenuBarButtonHighlightHdl(nId, rLink);
+}
+
+void MenuBar::RemoveMenuBarButton( sal_uInt16 nId )
+{
+ MenuBarWindow* pMenuWin = getMenuBarWindow();
+ if (!pMenuWin)
+ return;
+ pMenuWin->RemoveMenuBarButton(nId);
+}
+
+tools::Rectangle MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId )
+{
+ MenuBarWindow* pMenuWin = getMenuBarWindow();
+ return pMenuWin ? pMenuWin->GetMenuBarButtonRectPixel(nId) : tools::Rectangle();
+}
+
+bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId )
+{
+ MenuBarWindow* pMenuWin = getMenuBarWindow();
+ return pMenuWin && pMenuWin->HandleMenuButtonEvent(i_nButtonId);
+}
+
+int MenuBar::GetMenuBarHeight() const
+{
+ MenuBar* pMenuBar = const_cast<MenuBar*>(this);
+ const SalMenu *pNativeMenu = pMenuBar->ImplGetSalMenu();
+ int nMenubarHeight;
+ if (pNativeMenu)
+ nMenubarHeight = pNativeMenu->GetMenuBarHeight();
+ else
+ {
+ vcl::Window* pMenubarWin = GetWindow();
+ nMenubarHeight = pMenubarWin ? pMenubarWin->GetOutputSizePixel().Height() : 0;
+ }
+ return nMenubarHeight;
+}
+
+// bool PopupMenu::bAnyPopupInExecute = false;
+
+MenuFloatingWindow * PopupMenu::ImplGetFloatingWindow() const {
+ return static_cast<MenuFloatingWindow *>(Menu::ImplGetWindow());
+}
+
+PopupMenu::PopupMenu()
+{
+ mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
+}
+
+PopupMenu::PopupMenu( const PopupMenu& rMenu )
+{
+ mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this);
+ *this = rMenu;
+}
+
+PopupMenu::~PopupMenu()
+{
+ disposeOnce();
+}
+
+void PopupMenu::ClosePopup(Menu* pMenu)
+{
+ MenuFloatingWindow* p = dynamic_cast<MenuFloatingWindow*>(ImplGetWindow());
+ PopupMenu *pPopup = dynamic_cast<PopupMenu*>(pMenu);
+ if (p && pPopup)
+ p->KillActivePopup(pPopup);
+}
+
+namespace vcl
+{
+ bool IsInPopupMenuExecute()
+ {
+ return PopupMenu::GetActivePopupMenu() != nullptr;
+ }
+}
+
+PopupMenu* PopupMenu::GetActivePopupMenu()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ return pSVData->maAppData.mpActivePopupMenu;
+}
+
+void PopupMenu::EndExecute()
+{
+ if ( ImplGetWindow() )
+ ImplGetFloatingWindow()->EndExecute( 0 );
+}
+
+void PopupMenu::SelectItem(sal_uInt16 nId)
+{
+ if ( !ImplGetWindow() )
+ return;
+
+ if( nId != ITEMPOS_INVALID )
+ {
+ size_t nPos = 0;
+ MenuItemData* pData = GetItemList()->GetData( nId, nPos );
+ if (pData && pData->pSubMenu)
+ ImplGetFloatingWindow()->ChangeHighlightItem( nPos, true );
+ else
+ ImplGetFloatingWindow()->EndExecute( nId );
+ }
+ else
+ {
+ MenuFloatingWindow* pFloat = ImplGetFloatingWindow();
+ pFloat->GrabFocus();
+
+ for( size_t nPos = 0; nPos < GetItemList()->size(); nPos++ )
+ {
+ MenuItemData* pData = GetItemList()->GetDataFromPos( nPos );
+ if( pData->pSubMenu )
+ {
+ pFloat->KillActivePopup();
+ }
+ }
+ pFloat->ChangeHighlightItem( ITEMPOS_INVALID, false );
+ }
+}
+
+void PopupMenu::SetSelectedEntry( sal_uInt16 nId )
+{
+ nSelectedId = nId;
+ sSelectedIdent = GetItemIdent(nId);
+}
+
+sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const Point& rPopupPos )
+{
+ return Execute( pExecWindow, tools::Rectangle( rPopupPos, rPopupPos ), PopupMenuFlags::ExecuteDown );
+}
+
+static FloatWinPopupFlags lcl_TranslateFlags(PopupMenuFlags nFlags)
+{
+ FloatWinPopupFlags nPopupModeFlags = FloatWinPopupFlags::NONE;
+ if ( nFlags & PopupMenuFlags::ExecuteDown )
+ nPopupModeFlags = FloatWinPopupFlags::Down;
+ else if ( nFlags & PopupMenuFlags::ExecuteUp )
+ nPopupModeFlags = FloatWinPopupFlags::Up;
+ else if ( nFlags & PopupMenuFlags::ExecuteRight )
+ nPopupModeFlags = FloatWinPopupFlags::Right;
+ else
+ nPopupModeFlags = FloatWinPopupFlags::Down;
+
+ if (nFlags & PopupMenuFlags::NoMouseUpClose ) // allow popup menus to stay open on mouse button up
+ nPopupModeFlags |= FloatWinPopupFlags::NoMouseUpClose; // useful if the menu was opened on mousebutton down (eg toolbox configuration)
+
+ return nPopupModeFlags;
+}
+
+sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const tools::Rectangle& rRect, PopupMenuFlags nFlags )
+{
+ ENSURE_OR_RETURN( pExecWindow, "PopupMenu::Execute: need a non-NULL window!", 0 );
+ return ImplExecute( pExecWindow, rRect, lcl_TranslateFlags(nFlags), nullptr, false );
+}
+
+void PopupMenu::ImplFlushPendingSelect()
+{
+ // is there still Select?
+ Menu* pSelect = ImplFindSelectMenu();
+ if (pSelect)
+ {
+ // Select should be called prior to leaving execute in a popup menu!
+ Application::RemoveUserEvent( pSelect->nEventId );
+ pSelect->nEventId = nullptr;
+ pSelect->Select();
+ }
+}
+
+bool PopupMenu::PrepareRun(const VclPtr<vcl::Window>& pParentWin, tools::Rectangle& rRect,
+ FloatWinPopupFlags& nPopupModeFlags, Menu* pSFrom,
+ bool& bRealExecute, VclPtr<MenuFloatingWindow>& pWin)
+{
+ bRealExecute = false;
+ const sal_uInt16 nItemCount = GetItemCount();
+ if (!pSFrom && (vcl::IsInPopupMenuExecute() || !nItemCount))
+ return false;
+
+ mpLayoutData.reset();
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ pStartedFrom = pSFrom;
+ nSelectedId = 0;
+ sSelectedIdent.clear();
+ bCanceled = false;
+
+ VclPtr<vcl::Window> xFocusId;
+ if ( !pStartedFrom )
+ {
+ pSVData->mpWinData->mbNoDeactivate = true;
+ xFocusId = Window::SaveFocus();
+ bRealExecute = true;
+ }
+ else
+ {
+ // assure that only one menu is open at a time
+ if (pStartedFrom->IsMenuBar() && pSVData->mpWinData->mpFirstFloat)
+ pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel
+ | FloatWinPopupEndFlags::CloseAll);
+ }
+
+ SAL_WARN_IF( ImplGetWindow(), "vcl", "Win?!" );
+ rRect.SetPos(pParentWin->OutputToScreenPixel(rRect.TopLeft()));
+
+ nPopupModeFlags |= FloatWinPopupFlags::NoKeyClose | FloatWinPopupFlags::AllMouseButtonClose | FloatWinPopupFlags::GrabFocus;
+ if (bRealExecute)
+ nPopupModeFlags |= FloatWinPopupFlags::NewLevel;
+
+ bInCallback = true; // set it here, if Activate overridden
+ Activate();
+ bInCallback = false;
+
+ if (pParentWin->isDisposed())
+ return false;
+
+ if ( bCanceled || bKilled )
+ return false;
+
+ if (!nItemCount)
+ return false;
+
+ // The flag MenuFlags::HideDisabledEntries is inherited.
+ if ( pSFrom )
+ {
+ if ( pSFrom->nMenuFlags & MenuFlags::HideDisabledEntries )
+ nMenuFlags |= MenuFlags::HideDisabledEntries;
+ else
+ nMenuFlags &= ~MenuFlags::HideDisabledEntries;
+ }
+ else
+ {
+ if (officecfg::Office::Common::View::Menu::DontHideDisabledEntry::get())
+ nMenuFlags &= ~MenuFlags::HideDisabledEntries;
+ else
+ nMenuFlags |= MenuFlags::HideDisabledEntries;
+ }
+
+ sal_uInt16 nVisibleEntries = ImplGetVisibleItemCount();
+ if ( !nVisibleEntries )
+ {
+ OUString aTmpEntryText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE));
+
+ MenuItemData* pData = NbcInsertItem(0xFFFF, MenuItemBits::NONE, aTmpEntryText, nullptr, 0xFFFF, {});
+ size_t nPos = 0;
+ pData = pItemList->GetData( pData->nId, nPos );
+ assert(pData);
+ if (pData)
+ {
+ pData->bIsTemporary = true;
+ }
+ ImplCallEventListeners(VclEventId::MenuSubmenuChanged, nPos);
+ }
+
+ pWin = VclPtrInstance<MenuFloatingWindow>(this, pParentWin, WB_BORDER | WB_SYSTEMWINDOW);
+ if (comphelper::LibreOfficeKit::isActive() && get_id() == "editviewspellmenu")
+ {
+ VclPtr<vcl::Window> xNotifierParent = pParentWin->GetParentWithLOKNotifier();
+ assert(xNotifierParent && xNotifierParent->GetLOKNotifier() && "editview menu without LOKNotifier");
+ pWin->SetLOKNotifier(xNotifierParent->GetLOKNotifier());
+ }
+
+ if( pSVData->maNWFData.mbFlatMenu )
+ pWin->SetBorderStyle( WindowBorderStyle::NOBORDER );
+ else
+ pWin->SetBorderStyle( pWin->GetBorderStyle() | WindowBorderStyle::MENU );
+ pWindow = pWin;
+
+ Size aSz = ImplCalcSize( pWin );
+
+ AbsoluteScreenPixelRectangle aDesktopRect(pWin->GetDesktopRectPixel());
+ if( Application::GetScreenCount() > 1 )
+ {
+ vcl::Window* pDeskW = pWindow->GetWindow( GetWindowType::RealParent );
+ if( ! pDeskW )
+ pDeskW = pWindow;
+ AbsoluteScreenPixelPoint aDesktopTL(pDeskW->OutputToAbsoluteScreenPixel(rRect.TopLeft()));
+ aDesktopRect = Application::GetScreenPosSizePixel(
+ Application::GetBestScreen(AbsoluteScreenPixelRectangle(aDesktopTL, rRect.GetSize())));
+ }
+
+ tools::Long nMaxHeight = aDesktopRect.GetHeight();
+
+ //rhbz#1021915. If a menu won't fit in the desired location the default
+ //mode is to place it somewhere it will fit. e.g. above, left, right. For
+ //some cases, e.g. menubars, it's desirable to limit the options to
+ //above/below and force the menu to scroll if it won't fit
+ if (nPopupModeFlags & FloatWinPopupFlags::NoHorzPlacement)
+ {
+ vcl::Window* pRef = pWin;
+ if ( pRef->GetParent() )
+ pRef = pRef->GetParent();
+
+ AbsoluteScreenPixelRectangle devRect(pRef->OutputToAbsoluteScreenPixel(rRect.TopLeft()),
+ pRef->OutputToAbsoluteScreenPixel(rRect.BottomRight()));
+
+ tools::Long nHeightAbove = devRect.Top() - aDesktopRect.Top();
+ tools::Long nHeightBelow = aDesktopRect.Bottom() - devRect.Bottom();
+ nMaxHeight = std::min(nMaxHeight, std::max(nHeightAbove, nHeightBelow));
+ }
+
+ // In certain cases this might be misdetected with a height of 0, leading to menus not being displayed.
+ // So assume that the available screen size matches at least the system requirements
+ SAL_WARN_IF(nMaxHeight < 768, "vcl",
+ "Available height misdetected as " << nMaxHeight
+ << "px. Setting to 768px instead.");
+ nMaxHeight = std::max(nMaxHeight, tools::Long(768));
+
+ if (pStartedFrom && pStartedFrom->IsMenuBar())
+ nMaxHeight -= pParentWin->GetSizePixel().Height();
+ sal_Int32 nLeft, nTop, nRight, nBottom;
+ pWindow->GetBorder( nLeft, nTop, nRight, nBottom );
+ nMaxHeight -= nTop+nBottom;
+ if ( aSz.Height() > nMaxHeight )
+ {
+ pWin->EnableScrollMenu( true );
+ sal_uInt16 nStart = ImplGetFirstVisible();
+ sal_uInt16 nEntries = ImplCalcVisEntries( nMaxHeight, nStart );
+ aSz.setHeight( ImplCalcHeight( nEntries ) );
+ }
+
+ pWin->SetFocusId( xFocusId );
+ pWin->SetOutputSizePixel( aSz );
+ return true;
+}
+
+bool PopupMenu::Run(const VclPtr<MenuFloatingWindow>& pWin, const bool bRealExecute, const bool bPreSelectFirst,
+ const FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, const tools::Rectangle& rRect)
+{
+ SalMenu* pMenu = ImplGetSalMenu();
+ if (pMenu && bRealExecute && pMenu->ShowNativePopupMenu(pWin, rRect, nPopupModeFlags))
+ return true;
+
+ pWin->StartPopupMode(rRect, nPopupModeFlags);
+ if (pSFrom)
+ {
+ sal_uInt16 aPos;
+ if (pSFrom->IsMenuBar())
+ aPos = static_cast<MenuBarWindow *>(pSFrom->pWindow.get())->GetHighlightedItem();
+ else
+ aPos = static_cast<MenuFloatingWindow *>(pSFrom->pWindow.get())->GetHighlightedItem();
+
+ pWin->SetPosInParent(aPos); // store position to be sent in SUBMENUDEACTIVATE
+ pSFrom->ImplCallEventListeners(VclEventId::MenuSubmenuActivate, aPos);
+ }
+
+ if ( bPreSelectFirst )
+ {
+ for (size_t n = 0; n < static_cast<size_t>(GetItemCount()); n++)
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos( n );
+ if ( ( pData->bEnabled
+ || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus()
+ )
+ && ( pData->eType != MenuItemType::SEPARATOR )
+ && ImplIsVisible( n )
+ && ImplIsSelectable( n )
+ )
+ {
+ pWin->ChangeHighlightItem(n, false);
+ break;
+ }
+ }
+ }
+
+ if (bRealExecute)
+ pWin->Execute();
+
+ return false;
+}
+
+void PopupMenu::FinishRun(const VclPtr<MenuFloatingWindow>& pWin, const VclPtr<vcl::Window>& pParentWin, const bool bRealExecute, const bool bIsNativeMenu)
+{
+ if (!bRealExecute || pWin->isDisposed())
+ return;
+
+ if (!bIsNativeMenu)
+ {
+ VclPtr<vcl::Window> xFocusId = pWin->GetFocusId();
+ assert(xFocusId == nullptr && "Focus should already be restored by MenuFloatingWindow::End");
+ pWin->ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xFocusId);
+
+ if (nSelectedId) // then clean up .. ( otherwise done by TH )
+ {
+ PopupMenu* pSub = pWin->GetActivePopup();
+ while ( pSub )
+ {
+ pSub->ImplGetFloatingWindow()->EndPopupMode();
+ pSub = pSub->ImplGetFloatingWindow()->GetActivePopup();
+ }
+ }
+ }
+ else
+ pWin->StopExecute();
+
+ pWin->doShutdown();
+ pWindow.disposeAndClear();
+ ImplClosePopupToolBox(pParentWin);
+ ImplFlushPendingSelect();
+}
+
+sal_uInt16 PopupMenu::ImplExecute(const VclPtr<vcl::Window>& pParentWin, const tools::Rectangle& rRect,
+ FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst)
+{
+ // tdf#126054 hold this until after function completes
+ VclPtr<PopupMenu> xThis(this);
+ bool bRealExecute = false;
+ tools::Rectangle aRect(rRect);
+ VclPtr<MenuFloatingWindow> pWin;
+ if (!PrepareRun(pParentWin, aRect, nPopupModeFlags, pSFrom, bRealExecute, pWin))
+ return 0;
+ const bool bNative = Run(pWin, bRealExecute, bPreSelectFirst, nPopupModeFlags, pSFrom, aRect);
+ FinishRun(pWin, pParentWin, bRealExecute, bNative);
+ return nSelectedId;
+}
+
+sal_uInt16 PopupMenu::ImplCalcVisEntries( tools::Long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible ) const
+{
+ nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
+
+ tools::Long nHeight = 0;
+ size_t nEntries = pItemList->size();
+ sal_uInt16 nVisEntries = 0;
+
+ if ( pLastVisible )
+ *pLastVisible = 0;
+
+ for ( size_t n = nStartEntry; n < nEntries; n++ )
+ {
+ if ( ImplIsVisible( n ) )
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos( n );
+ nHeight += pData->aSz.Height();
+ if ( nHeight > nMaxHeight )
+ break;
+
+ if ( pLastVisible )
+ *pLastVisible = n;
+ nVisEntries++;
+ }
+ }
+ return nVisEntries;
+}
+
+tools::Long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries ) const
+{
+ tools::Long nHeight = 0;
+
+ sal_uInt16 nFound = 0;
+ for ( size_t n = 0; ( nFound < nEntries ) && ( n < pItemList->size() ); n++ )
+ {
+ if ( ImplIsVisible( static_cast<sal_uInt16>(n) ) )
+ {
+ MenuItemData* pData = pItemList->GetDataFromPos( n );
+ nHeight += pData->aSz.Height();
+ nFound++;
+ }
+ }
+
+ nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight();
+
+ return nHeight;
+}
+
+css::uno::Reference<css::awt::XPopupMenu> PopupMenu::CreateMenuInterface()
+{
+ UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
+ if ( pWrapper )
+ return pWrapper->CreateMenuInterface(this);
+ return nullptr;
+}
+
+ImplMenuDelData::ImplMenuDelData( const Menu* pMenu )
+: mpNext( nullptr )
+, mpMenu( nullptr )
+{
+ if( pMenu )
+ const_cast< Menu* >( pMenu )->ImplAddDel( *this );
+}
+
+ImplMenuDelData::~ImplMenuDelData()
+{
+ if( mpMenu )
+ const_cast< Menu* >( mpMenu.get() )->ImplRemoveDel( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/menubarwindow.cxx b/vcl/source/window/menubarwindow.cxx
new file mode 100644
index 0000000000..8e2bc8d7eb
--- /dev/null
+++ b/vcl/source/window/menubarwindow.cxx
@@ -0,0 +1,1220 @@
+/* -*- 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 "menubarwindow.hxx"
+#include "menuitemlist.hxx"
+#include "menufloatingwindow.hxx"
+
+#include <vcl/dockingarea.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/taskpanelist.hxx>
+#include <sal/log.hxx>
+
+#include <salframe.hxx>
+#include <salmenu.hxx>
+#include <svdata.hxx>
+#include <strings.hrc>
+#include <bitmaps.hlst>
+#include <window.h>
+#include "bufferdevice.hxx"
+#include <menubarvalue.hxx>
+
+// document closing button
+#define IID_DOCUMENTCLOSE 1
+
+DecoToolBox::DecoToolBox( vcl::Window* pParent ) :
+ ToolBox( pParent, 0 ),
+ lastSize(-1)
+{
+ calcMinSize();
+}
+
+void DecoToolBox::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ if ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE )
+ {
+ calcMinSize();
+ SetBackground();
+ SetImages( 0, true);
+ }
+}
+
+void DecoToolBox::calcMinSize()
+{
+ ScopedVclPtrInstance<ToolBox> aTbx( GetParent() );
+ if( GetItemCount() == 0 )
+ {
+ aTbx->InsertItem(ToolBoxItemId(IID_DOCUMENTCLOSE), Image(StockImage::Yes, SV_RESID_BITMAP_CLOSEDOC));
+ }
+ else
+ {
+ ImplToolItems::size_type nItems = GetItemCount();
+ for( ImplToolItems::size_type i = 0; i < nItems; i++ )
+ {
+ ToolBoxItemId nId = GetItemId( i );
+ aTbx->InsertItem( nId, GetItemImage( nId ) );
+ }
+ }
+ maMinSize = aTbx->CalcWindowSizePixel();
+
+ aTbx.disposeAndClear();
+}
+
+void DecoToolBox::SetImages( tools::Long nMaxHeight, bool bForce )
+{
+ tools::Long border = getMinSize().Height() - maImage.GetSizePixel().Height();
+
+ if( !nMaxHeight && lastSize != -1 )
+ nMaxHeight = lastSize + border; // don't change anything if called with 0
+
+ if( nMaxHeight < getMinSize().Height() )
+ nMaxHeight = getMinSize().Height();
+
+ if( (lastSize == nMaxHeight - border) && !bForce )
+ return;
+
+ lastSize = nMaxHeight - border;
+
+ Color aEraseColor( ColorTransparency, 255, 255, 255, 255 );
+ BitmapEx aBmpExDst( maImage.GetBitmapEx() );
+ BitmapEx aBmpExSrc( aBmpExDst );
+
+ aEraseColor.SetAlpha( 0 );
+ aBmpExDst.Erase( aEraseColor );
+ aBmpExDst.Scale( Size( lastSize, lastSize ) );
+
+ tools::Rectangle aSrcRect( Point(0,0), maImage.GetSizePixel() );
+ tools::Rectangle aDestRect( Point((lastSize - maImage.GetSizePixel().Width())/2,
+ (lastSize - maImage.GetSizePixel().Height())/2 ),
+ maImage.GetSizePixel() );
+
+ aBmpExDst.CopyPixel( aDestRect, aSrcRect, aBmpExSrc );
+ SetItemImage( ToolBoxItemId(IID_DOCUMENTCLOSE), Image( aBmpExDst ) );
+
+}
+
+MenuBarWindow::MenuBarWindow( vcl::Window* pParent ) :
+ Window( pParent, 0 ),
+ m_aCloseBtn(VclPtr<DecoToolBox>::Create(this)),
+ m_aFloatBtn(VclPtr<PushButton>::Create(this, WB_NOPOINTERFOCUS | WB_SMALLSTYLE | WB_RECTSTYLE)),
+ m_aHideBtn(VclPtr<PushButton>::Create(this, WB_NOPOINTERFOCUS | WB_SMALLSTYLE | WB_RECTSTYLE))
+{
+ SetType(WindowType::MENUBARWINDOW);
+ m_pMenu = nullptr;
+ m_pActivePopup = nullptr;
+ m_nHighlightedItem = ITEMPOS_INVALID;
+ m_nRolloveredItem = ITEMPOS_INVALID;
+ mbAutoPopup = true;
+ m_bIgnoreFirstMove = true;
+
+ m_aCloseBtn->maImage = Image(StockImage::Yes, SV_RESID_BITMAP_CLOSEDOC);
+
+ m_aCloseBtn->SetBackground();
+ m_aCloseBtn->SetPaintTransparent(true);
+ m_aCloseBtn->SetParentClipMode(ParentClipMode::NoClip);
+
+ m_aCloseBtn->InsertItem(ToolBoxItemId(IID_DOCUMENTCLOSE), m_aCloseBtn->maImage);
+ m_aCloseBtn->SetSelectHdl(LINK(this, MenuBarWindow, CloseHdl));
+ m_aCloseBtn->AddEventListener(LINK(this, MenuBarWindow, ToolboxEventHdl));
+ m_aCloseBtn->SetQuickHelpText(ToolBoxItemId(IID_DOCUMENTCLOSE), VclResId(SV_HELPTEXT_CLOSEDOCUMENT));
+
+ m_aFloatBtn->SetSymbol( SymbolType::FLOAT );
+ m_aFloatBtn->SetQuickHelpText(VclResId(SV_HELPTEXT_RESTORE));
+
+ m_aHideBtn->SetSymbol( SymbolType::HIDE );
+ m_aHideBtn->SetQuickHelpText(VclResId(SV_HELPTEXT_MINIMIZE));
+
+ ImplInitStyleSettings();
+
+ AddEventListener(LINK(this, MenuBarWindow, ShowHideListener));
+}
+
+MenuBarWindow::~MenuBarWindow()
+{
+ disposeOnce();
+}
+
+void MenuBarWindow::dispose()
+{
+ m_aCloseBtn->RemoveEventListener(LINK(this, MenuBarWindow, ToolboxEventHdl));
+ RemoveEventListener(LINK(this, MenuBarWindow, ShowHideListener));
+
+ mpParentPopup.disposeAndClear();
+ m_aHideBtn.disposeAndClear();
+ m_aFloatBtn.disposeAndClear();
+ m_aCloseBtn.disposeAndClear();
+ m_pMenu.clear();
+ m_pActivePopup.clear();
+ m_xSaveFocusId.clear();
+
+ Window::dispose();
+}
+
+void MenuBarWindow::SetMenu( MenuBar* pMen )
+{
+ m_pMenu = pMen;
+ KillActivePopup();
+ m_nHighlightedItem = ITEMPOS_INVALID;
+ if (pMen)
+ {
+ m_aCloseBtn->ShowItem(ToolBoxItemId(IID_DOCUMENTCLOSE), pMen->HasCloseButton());
+ m_aCloseBtn->Show(pMen->HasCloseButton() || !m_aAddButtons.empty());
+ m_aFloatBtn->Show(pMen->HasFloatButton());
+ m_aHideBtn->Show(pMen->HasHideButton());
+ }
+ Invalidate();
+
+ // show and connect native menubar
+ if( m_pMenu && m_pMenu->ImplGetSalMenu() )
+ {
+ if( m_pMenu->ImplGetSalMenu()->VisibleMenuBar() )
+ ImplGetFrame()->SetMenu( m_pMenu->ImplGetSalMenu() );
+
+ m_pMenu->ImplGetSalMenu()->SetFrame( ImplGetFrame() );
+ m_pMenu->ImplGetSalMenu()->ShowMenuBar(true);
+ }
+}
+
+void MenuBarWindow::SetHeight(tools::Long nHeight)
+{
+ setPosSizePixel(0, 0, 0, nHeight, PosSizeFlags::Height);
+}
+
+void MenuBarWindow::ShowButtons( bool bClose, bool bFloat, bool bHide )
+{
+ m_aCloseBtn->ShowItem(ToolBoxItemId(IID_DOCUMENTCLOSE), bClose);
+ m_aCloseBtn->Show(bClose || !m_aAddButtons.empty());
+ if (m_pMenu->mpSalMenu)
+ m_pMenu->mpSalMenu->ShowCloseButton(bClose);
+ m_aFloatBtn->Show( bFloat );
+ m_aHideBtn->Show( bHide );
+ Resize();
+}
+
+Size const & MenuBarWindow::MinCloseButtonSize() const
+{
+ return m_aCloseBtn->getMinSize();
+}
+
+IMPL_LINK_NOARG(MenuBarWindow, CloseHdl, ToolBox *, void)
+{
+ if( ! m_pMenu )
+ return;
+
+ if( m_aCloseBtn->GetCurItemId() == ToolBoxItemId(IID_DOCUMENTCLOSE) )
+ {
+ // #i106052# call close hdl asynchronously to ease handler implementation
+ // this avoids still being in the handler while the DecoToolBox already
+ // gets destroyed
+ Application::PostUserEvent(m_pMenu->GetCloseButtonClickHdl());
+ }
+ else
+ {
+ std::map<sal_uInt16,AddButtonEntry>::iterator it = m_aAddButtons.find(sal_uInt16(m_aCloseBtn->GetCurItemId()));
+ if( it != m_aAddButtons.end() )
+ {
+ MenuBarButtonCallbackArg aArg;
+ aArg.nId = it->first;
+ aArg.bHighlight = (sal_uInt16(m_aCloseBtn->GetHighlightItemId()) == it->first);
+ it->second.m_aSelectLink.Call( aArg );
+ }
+ }
+}
+
+IMPL_LINK( MenuBarWindow, ToolboxEventHdl, VclWindowEvent&, rEvent, void )
+{
+ if( ! m_pMenu )
+ return;
+
+ MenuBarButtonCallbackArg aArg;
+ aArg.nId = 0xffff;
+ aArg.bHighlight = (rEvent.GetId() == VclEventId::ToolboxHighlight);
+ if( rEvent.GetId() == VclEventId::ToolboxHighlight )
+ aArg.nId =sal_uInt16(m_aCloseBtn->GetHighlightItemId());
+ else if( rEvent.GetId() == VclEventId::ToolboxHighlightOff )
+ {
+ auto nPos = static_cast<ToolBox::ImplToolItems::size_type>(reinterpret_cast<sal_IntPtr>(rEvent.GetData()));
+ aArg.nId = sal_uInt16(m_aCloseBtn->GetItemId(nPos));
+ }
+ std::map< sal_uInt16, AddButtonEntry >::iterator it = m_aAddButtons.find( aArg.nId );
+ if( it != m_aAddButtons.end() )
+ {
+ it->second.m_aHighlightLink.Call( aArg );
+ }
+}
+
+IMPL_LINK( MenuBarWindow, ShowHideListener, VclWindowEvent&, rEvent, void )
+{
+ if( ! m_pMenu )
+ return;
+
+ if( rEvent.GetId() == VclEventId::WindowShow )
+ m_pMenu->ImplCallEventListeners( VclEventId::MenuShow, ITEMPOS_INVALID );
+ else if( rEvent.GetId() == VclEventId::WindowHide )
+ m_pMenu->ImplCallEventListeners( VclEventId::MenuHide, ITEMPOS_INVALID );
+}
+
+void MenuBarWindow::ImplCreatePopup( bool bPreSelectFirst )
+{
+ MenuItemData* pItemData = m_pMenu ? m_pMenu->GetItemList()->GetDataFromPos( m_nHighlightedItem ) : nullptr;
+ if ( !pItemData )
+ return;
+
+ m_bIgnoreFirstMove = true;
+ if ( m_pActivePopup && ( m_pActivePopup != pItemData->pSubMenu ) )
+ {
+ KillActivePopup();
+ }
+ if ( !(pItemData->bEnabled && pItemData->pSubMenu && ( m_nHighlightedItem != ITEMPOS_INVALID ) &&
+ ( pItemData->pSubMenu != m_pActivePopup )) )
+ return;
+
+ m_pActivePopup = pItemData->pSubMenu.get();
+ tools::Long nX = 0;
+ MenuItemData* pData = nullptr;
+ for ( sal_uLong n = 0; n < m_nHighlightedItem; n++ )
+ {
+ pData = m_pMenu->GetItemList()->GetDataFromPos( n );
+ nX += pData->aSz.Width();
+ }
+ pData = m_pMenu->pItemList->GetDataFromPos( m_nHighlightedItem );
+ Point aItemTopLeft( nX, 0 );
+ Point aItemBottomRight( aItemTopLeft );
+ aItemBottomRight.AdjustX(pData->aSz.Width() );
+
+ if (pData->bHiddenOnGUI)
+ {
+ mpParentPopup.disposeAndClear();
+ mpParentPopup = VclPtr<PopupMenu>::Create();
+ m_pActivePopup = mpParentPopup.get();
+
+ for (sal_uInt16 i = m_nHighlightedItem; i < m_pMenu->GetItemCount(); ++i)
+ {
+ sal_uInt16 nId = m_pMenu->GetItemId(i);
+
+ MenuItemData* pParentItemData = m_pMenu->GetItemList()->GetData(nId);
+ assert(pParentItemData);
+ mpParentPopup->InsertItem(nId, pParentItemData->aText, pParentItemData->nBits, pParentItemData->sIdent);
+ mpParentPopup->SetHelpId(nId, pParentItemData->aHelpId);
+ mpParentPopup->SetHelpText(nId, pParentItemData->aHelpText);
+ mpParentPopup->SetAccelKey(nId, pParentItemData->aAccelKey);
+ mpParentPopup->SetItemCommand(nId, pParentItemData->aCommandStr);
+ mpParentPopup->SetHelpCommand(nId, pParentItemData->aHelpCommandStr);
+
+ PopupMenu* pPopup = m_pMenu->GetPopupMenu(nId);
+ mpParentPopup->SetPopupMenu(nId, pPopup);
+ }
+ }
+ // the menu bar could have height 0 in fullscreen mode:
+ // so do not use always WindowHeight, as ItemHeight < WindowHeight.
+ if ( GetSizePixel().Height() )
+ {
+ // #107747# give menuitems the height of the menubar
+ aItemBottomRight.AdjustY(GetOutputSizePixel().Height()-1 );
+ }
+
+ // ImplExecute is not modal...
+ // #99071# do not grab the focus, otherwise it will be restored to the menubar
+ // when the frame is reactivated later
+ //GrabFocus();
+ m_pActivePopup->ImplExecute( this, tools::Rectangle( aItemTopLeft, aItemBottomRight ), FloatWinPopupFlags::Down | FloatWinPopupFlags::NoHorzPlacement, m_pMenu, bPreSelectFirst );
+ // does not have a window, if aborted before or if there are no entries
+ if ( m_pActivePopup->ImplGetFloatingWindow() )
+ m_pActivePopup->ImplGetFloatingWindow()->AddPopupModeWindow( this );
+ else
+ m_pActivePopup = nullptr;
+}
+
+void MenuBarWindow::KillActivePopup()
+{
+ if ( !m_pActivePopup )
+ return;
+
+ if( m_pActivePopup->pWindow )
+ if( static_cast<FloatingWindow *>(m_pActivePopup->pWindow.get())->IsInCleanUp() )
+ return; // kill it later
+
+ if ( m_pActivePopup->bInCallback )
+ m_pActivePopup->bCanceled = true;
+
+ m_pActivePopup->bInCallback = true;
+ m_pActivePopup->Deactivate();
+ m_pActivePopup->bInCallback = false;
+ // check for pActivePopup, if stopped by deactivate...
+ if ( m_pActivePopup->ImplGetWindow() )
+ {
+ if (mpParentPopup)
+ {
+ for (sal_uInt16 i = 0; i < mpParentPopup->GetItemCount(); ++i)
+ {
+ sal_uInt16 nId = mpParentPopup->GetItemId(i);
+ MenuItemData* pParentItemData = mpParentPopup->GetItemList()->GetData(nId);
+ assert(pParentItemData);
+ pParentItemData->pSubMenu = nullptr;
+ }
+ }
+ m_pActivePopup->ImplGetFloatingWindow()->StopExecute();
+ m_pActivePopup->ImplGetFloatingWindow()->doShutdown();
+ m_pActivePopup->pWindow.disposeAndClear();
+ }
+ m_pActivePopup = nullptr;
+}
+
+void MenuBarWindow::PopupClosed( Menu const * pPopup )
+{
+ if ( pPopup == m_pActivePopup )
+ {
+ KillActivePopup();
+ ChangeHighlightItem( ITEMPOS_INVALID, false, ImplGetFrameWindow()->ImplGetFrameData()->mbHasFocus, false );
+ }
+}
+
+void MenuBarWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ mbAutoPopup = true;
+ sal_uInt16 nEntry = ImplFindEntry( rMEvt.GetPosPixel() );
+ if ( ( nEntry != ITEMPOS_INVALID ) && !m_pActivePopup )
+ {
+ ChangeHighlightItem( nEntry, false );
+ }
+ else
+ {
+ KillActivePopup();
+ ChangeHighlightItem( ITEMPOS_INVALID, false );
+ }
+}
+
+void MenuBarWindow::MouseButtonUp( const MouseEvent& )
+{
+}
+
+void MenuBarWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ if ( rMEvt.IsSynthetic() || rMEvt.IsEnterWindow() )
+ return;
+
+ if ( rMEvt.IsLeaveWindow() )
+ {
+ if ( m_nRolloveredItem != ITEMPOS_INVALID && m_nRolloveredItem != m_nHighlightedItem )
+ Invalidate(); //HighlightItem( nRolloveredItem, false );
+
+ m_nRolloveredItem = ITEMPOS_INVALID;
+ return;
+ }
+
+ sal_uInt16 nEntry = ImplFindEntry( rMEvt.GetPosPixel() );
+ if ( m_nHighlightedItem == ITEMPOS_INVALID )
+ {
+ if ( m_nRolloveredItem != nEntry )
+ {
+ if ( m_nRolloveredItem != ITEMPOS_INVALID )
+ Invalidate(); //HighlightItem( nRolloveredItem, false );
+
+ m_nRolloveredItem = nEntry;
+ Invalidate(); //HighlightItem( nRolloveredItem, true );
+ }
+ return;
+ }
+ m_nRolloveredItem = nEntry;
+
+ if( m_bIgnoreFirstMove )
+ {
+ m_bIgnoreFirstMove = false;
+ return;
+ }
+
+ if ( ( nEntry != ITEMPOS_INVALID )
+ && ( nEntry != m_nHighlightedItem ) )
+ ChangeHighlightItem( nEntry, false );
+}
+
+void MenuBarWindow::ChangeHighlightItem( sal_uInt16 n, bool bSelectEntry, bool bAllowRestoreFocus, bool bDefaultToDocument)
+{
+ if( ! m_pMenu )
+ return;
+
+ // #57934# close active popup if applicable, as TH's background storage works.
+ MenuItemData* pNextData = m_pMenu->pItemList->GetDataFromPos( n );
+ if ( m_pActivePopup && m_pActivePopup->ImplGetWindow() && ( !pNextData || ( m_pActivePopup != pNextData->pSubMenu ) ) )
+ KillActivePopup(); // pActivePopup when applicable without pWin, if Rescheduled in Activate()
+
+ // activate menubar only ones per cycle...
+ bool bJustActivated = false;
+ if ( ( m_nHighlightedItem == ITEMPOS_INVALID ) && ( n != ITEMPOS_INVALID ) )
+ {
+ ImplGetSVData()->mpWinData->mbNoDeactivate = true;
+ // #105406# avoid saving the focus when we already have the focus
+ bool bNoSaveFocus = (this == ImplGetSVData()->mpWinData->mpFocusWin.get());
+
+ if( m_xSaveFocusId != nullptr )
+ {
+ if (!ImplGetSVData()->mpWinData->mbNoSaveFocus)
+ {
+ m_xSaveFocusId = nullptr;
+ if( !bNoSaveFocus )
+ m_xSaveFocusId = Window::SaveFocus(); // only save focus when initially activated
+ }
+ else {
+ ; // do nothing: we 're activated again from taskpanelist, focus was already saved
+ }
+ }
+ else
+ {
+ if( !bNoSaveFocus )
+ m_xSaveFocusId = Window::SaveFocus(); // only save focus when initially activated
+ }
+ m_pMenu->bInCallback = true; // set here if Activate overridden
+ m_pMenu->Activate();
+ m_pMenu->bInCallback = false;
+ bJustActivated = true;
+ }
+ else if ( ( m_nHighlightedItem != ITEMPOS_INVALID ) && ( n == ITEMPOS_INVALID ) )
+ {
+ m_pMenu->bInCallback = true;
+ m_pMenu->Deactivate();
+ m_pMenu->bInCallback = false;
+ ImplGetSVData()->mpWinData->mbNoDeactivate = false;
+ if (!ImplGetSVData()->mpWinData->mbNoSaveFocus)
+ {
+ VclPtr<vcl::Window> xTempFocusId;
+ if (m_xSaveFocusId && !m_xSaveFocusId->isDisposed())
+ xTempFocusId = m_xSaveFocusId;
+ m_xSaveFocusId = nullptr;
+
+ if (bAllowRestoreFocus)
+ {
+ // tdf#115227 the popup is already killed, so temporarily set us as the
+ // focus window, so we could avoid sending superfluous activate events
+ // to top window listeners.
+ if (xTempFocusId || bDefaultToDocument)
+ ImplGetSVData()->mpWinData->mpFocusWin = this;
+
+ // #105406# restore focus to document if we could not save focus before
+ if (!xTempFocusId && bDefaultToDocument)
+ GrabFocusToDocument();
+ else
+ Window::EndSaveFocus(xTempFocusId);
+ }
+ }
+ }
+
+ if ( m_nHighlightedItem != ITEMPOS_INVALID )
+ {
+ if ( m_nHighlightedItem != m_nRolloveredItem )
+ Invalidate(); //HighlightItem( nHighlightedItem, false );
+
+ m_pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, m_nHighlightedItem );
+ }
+
+ m_nHighlightedItem = n;
+ SAL_WARN_IF( ( m_nHighlightedItem != ITEMPOS_INVALID ) && !m_pMenu->ImplIsVisible( m_nHighlightedItem ), "vcl", "ChangeHighlightItem: Not visible!" );
+ if ( m_nHighlightedItem != ITEMPOS_INVALID )
+ Invalidate(); //HighlightItem( nHighlightedItem, true );
+ else if ( m_nRolloveredItem != ITEMPOS_INVALID )
+ Invalidate(); //HighlightItem( nRolloveredItem, true );
+ m_pMenu->ImplCallHighlight(m_nHighlightedItem);
+
+ if( mbAutoPopup )
+ ImplCreatePopup( bSelectEntry );
+
+ // #58935# #73659# Focus, if no popup underneath...
+ if ( bJustActivated && !m_pActivePopup )
+ GrabFocus();
+}
+
+static int ImplGetTopDockingAreaHeight( vcl::Window const *pWindow )
+{
+ // find docking area that is top aligned and return its height
+ // note: dockingareas are direct children of the SystemWindow
+ if( pWindow->ImplGetFrameWindow() )
+ {
+ vcl::Window *pWin = pWindow->ImplGetFrameWindow()->GetWindow( GetWindowType::FirstChild ); //mpWindowImpl->mpFirstChild;
+ while( pWin )
+ {
+ if( pWin->IsSystemWindow() )
+ {
+ vcl::Window *pChildWin = pWin->GetWindow( GetWindowType::FirstChild ); //mpWindowImpl->mpFirstChild;
+ while( pChildWin )
+ {
+ DockingAreaWindow *pDockingArea = nullptr;
+ if ( pChildWin->GetType() == WindowType::DOCKINGAREA )
+ pDockingArea = static_cast< DockingAreaWindow* >( pChildWin );
+
+ if( pDockingArea && pDockingArea->GetAlign() == WindowAlign::Top &&
+ pDockingArea->IsVisible() && pDockingArea->GetOutputSizePixel().Height() != 0 )
+ {
+ return pDockingArea->GetOutputSizePixel().Height();
+ }
+
+ pChildWin = pChildWin->GetWindow( GetWindowType::Next ); //mpWindowImpl->mpNext;
+ }
+
+ }
+
+ pWin = pWin->GetWindow( GetWindowType::Next ); //mpWindowImpl->mpNext;
+ }
+ }
+ return 0;
+}
+
+static void ImplAddNWFSeparator(vcl::RenderContext& rRenderContext, const Size& rSize, const MenubarValue& rMenubarValue)
+{
+ // add a separator if
+ // - we have an adjacent docking area
+ // - and if toolbars would draw them as well (mbDockingAreaSeparateTB must not be set, see dockingarea.cxx)
+ if (rMenubarValue.maTopDockingAreaHeight
+ && !ImplGetSVData()->maNWFData.mbDockingAreaSeparateTB
+ && !ImplGetSVData()->maNWFData.mbDockingAreaAvoidTBFrames)
+ {
+ // note: the menubar only provides the upper (dark) half of it, the rest (bright part) is drawn by the docking area
+
+ rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetSeparatorColor());
+ tools::Rectangle aRect(Point(), rSize);
+ rRenderContext.DrawLine(aRect.BottomLeft(), aRect.BottomRight());
+ }
+}
+
+void MenuBarWindow::HighlightItem(vcl::RenderContext& rRenderContext, sal_uInt16 nPos)
+{
+ if (!m_pMenu)
+ return;
+
+ tools::Long nX = 0;
+ size_t nCount = m_pMenu->pItemList->size();
+
+ Size aOutputSize = GetOutputSizePixel();
+ aOutputSize.AdjustWidth( -(m_aCloseBtn->GetSizePixel().Width()) );
+
+ for (size_t n = 0; n < nCount; n++)
+ {
+ MenuItemData* pData = m_pMenu->pItemList->GetDataFromPos( n );
+ if (n == nPos)
+ {
+ if (pData->eType != MenuItemType::SEPARATOR)
+ {
+ // #107747# give menuitems the height of the menubar
+ tools::Rectangle aRect(Point(nX, 1), Size(pData->aSz.Width(), aOutputSize.Height() - 2));
+ rRenderContext.Push(vcl::PushFlags::CLIPREGION);
+ rRenderContext.IntersectClipRegion(aRect);
+ bool bRollover, bHighlight;
+ if (!ImplGetSVData()->maNWFData.mbRolloverMenubar)
+ {
+ bHighlight = true;
+ bRollover = nPos != m_nHighlightedItem;
+ }
+ else
+ {
+ bRollover = nPos == m_nRolloveredItem;
+ bHighlight = nPos == m_nHighlightedItem;
+ }
+ if (rRenderContext.IsNativeControlSupported(ControlType::Menubar, ControlPart::MenuItem) &&
+ rRenderContext.IsNativeControlSupported(ControlType::Menubar, ControlPart::Entire))
+ {
+ // draw background (transparency)
+ MenubarValue aControlValue;
+ aControlValue.maTopDockingAreaHeight = ImplGetTopDockingAreaHeight( this );
+
+ if (!Application::GetSettings().GetStyleSettings().GetPersonaHeader().IsEmpty() )
+ Erase(rRenderContext);
+ else
+ {
+ tools::Rectangle aBgRegion(Point(), aOutputSize);
+ rRenderContext.DrawNativeControl(ControlType::Menubar, ControlPart::Entire, aBgRegion,
+ ControlState::ENABLED, aControlValue, OUString());
+ }
+
+ ImplAddNWFSeparator(rRenderContext, aOutputSize, aControlValue);
+
+ // draw selected item
+ ControlState nState = ControlState::ENABLED;
+ if (bRollover)
+ nState |= ControlState::ROLLOVER;
+ else
+ nState |= ControlState::SELECTED;
+ rRenderContext.DrawNativeControl(ControlType::Menubar, ControlPart::MenuItem,
+ aRect, nState, aControlValue, OUString() );
+ }
+ else
+ {
+ if (bRollover)
+ rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuBarRolloverColor());
+ else
+ rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
+ rRenderContext.SetLineColor();
+ rRenderContext.DrawRect(aRect);
+ }
+ rRenderContext.Pop();
+
+ m_pMenu->ImplPaint(rRenderContext, aOutputSize, 0, 0, pData, bHighlight, false, bRollover);
+ }
+ return;
+ }
+
+ nX += pData->aSz.Width();
+ }
+}
+
+tools::Rectangle MenuBarWindow::ImplGetItemRect( sal_uInt16 nPos ) const
+{
+ tools::Rectangle aRect;
+ if( m_pMenu )
+ {
+ tools::Long nX = 0;
+ size_t nCount = m_pMenu->pItemList->size();
+ for ( size_t n = 0; n < nCount; n++ )
+ {
+ MenuItemData* pData = m_pMenu->pItemList->GetDataFromPos( n );
+ if ( n == nPos )
+ {
+ if ( pData->eType != MenuItemType::SEPARATOR )
+ // #107747# give menuitems the height of the menubar
+ aRect = tools::Rectangle( Point( nX, 1 ), Size( pData->aSz.Width(), GetOutputSizePixel().Height()-2 ) );
+ break;
+ }
+
+ nX += pData->aSz.Width();
+ }
+ }
+ return aRect;
+}
+
+void MenuBarWindow::KeyInput( const KeyEvent& rKEvent )
+{
+ if ( !HandleKeyEvent( rKEvent ) )
+ Window::KeyInput( rKEvent );
+}
+
+bool MenuBarWindow::HandleKeyEvent( const KeyEvent& rKEvent, bool bFromMenu )
+{
+ if (!m_pMenu)
+ return false;
+
+ if (m_pMenu->bInCallback)
+ return true; // swallow
+
+ bool bDone = false;
+ sal_uInt16 nCode = rKEvent.GetKeyCode().GetCode();
+
+ if( GetParent() )
+ {
+ if( GetParent()->GetWindow( GetWindowType::Client )->IsSystemWindow() )
+ {
+ SystemWindow *pSysWin = static_cast<SystemWindow*>(GetParent()->GetWindow( GetWindowType::Client ));
+ if( pSysWin->GetTaskPaneList() )
+ if( pSysWin->GetTaskPaneList()->HandleKeyEvent( rKEvent ) )
+ return true;
+ }
+ }
+
+ // no key events if native menus
+ if (m_pMenu->ImplGetSalMenu() && m_pMenu->ImplGetSalMenu()->VisibleMenuBar())
+ {
+ return false;
+ }
+
+ if ( nCode == KEY_MENU && !rKEvent.GetKeyCode().IsShift() ) // only F10, not Shift-F10
+ {
+ mbAutoPopup = false;
+ if ( m_nHighlightedItem == ITEMPOS_INVALID )
+ {
+ ChangeHighlightItem( 0, false );
+ GrabFocus();
+ }
+ else
+ {
+ ChangeHighlightItem( ITEMPOS_INVALID, false );
+ m_xSaveFocusId = nullptr;
+ }
+ bDone = true;
+ }
+ else if ( bFromMenu )
+ {
+ if ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ||
+ ( nCode == KEY_HOME ) || ( nCode == KEY_END ) )
+ {
+ sal_uInt16 n = m_nHighlightedItem;
+ if ( n == ITEMPOS_INVALID )
+ {
+ if ( nCode == KEY_LEFT)
+ n = 0;
+ else
+ n = m_pMenu->GetItemCount()-1;
+ }
+
+ sal_uInt16 nLoop = n;
+
+ if( nCode == KEY_HOME )
+ { n = sal_uInt16(-1); nLoop = n+1; }
+ if( nCode == KEY_END )
+ { n = m_pMenu->GetItemCount(); nLoop = n-1; }
+
+ do
+ {
+ if ( nCode == KEY_LEFT || nCode == KEY_END )
+ {
+ if ( n )
+ n--;
+ else
+ n = m_pMenu->GetItemCount()-1;
+ }
+ if ( nCode == KEY_RIGHT || nCode == KEY_HOME )
+ {
+ n++;
+ if ( n >= m_pMenu->GetItemCount() )
+ n = 0;
+ }
+
+ MenuItemData* pData = m_pMenu->GetItemList()->GetDataFromPos( n );
+ if (pData->eType != MenuItemType::SEPARATOR &&
+ m_pMenu->ImplIsVisible(n) &&
+ !m_pMenu->ImplCurrentlyHiddenOnGUI(n))
+ {
+ ChangeHighlightItem( n, true );
+ break;
+ }
+ } while ( n != nLoop );
+ bDone = true;
+ }
+ else if ( nCode == KEY_RETURN )
+ {
+ if( m_pActivePopup ) KillActivePopup();
+ else
+ if ( !mbAutoPopup )
+ {
+ ImplCreatePopup( true );
+ mbAutoPopup = true;
+ }
+ bDone = true;
+ }
+ else if ( ( nCode == KEY_UP ) || ( nCode == KEY_DOWN ) )
+ {
+ if ( !mbAutoPopup )
+ {
+ ImplCreatePopup( true );
+ mbAutoPopup = true;
+ }
+ bDone = true;
+ }
+ else if ( nCode == KEY_ESCAPE || ( nCode == KEY_F6 && rKEvent.GetKeyCode().IsMod1() ) )
+ {
+ if( m_pActivePopup )
+ {
+ // hide the menu and remove the focus...
+ mbAutoPopup = false;
+ KillActivePopup();
+ }
+
+ ChangeHighlightItem( ITEMPOS_INVALID, false );
+
+ if( nCode == KEY_F6 && rKEvent.GetKeyCode().IsMod1() )
+ {
+ // put focus into document
+ GrabFocusToDocument();
+ }
+
+ bDone = true;
+ }
+ }
+
+ if ( !bDone && ( bFromMenu || rKEvent.GetKeyCode().IsMod2() ) )
+ {
+ sal_Unicode nCharCode = rKEvent.GetCharCode();
+ if ( nCharCode )
+ {
+ size_t nEntry, nDuplicates;
+ MenuItemData* pData = m_pMenu->GetItemList()->SearchItem( nCharCode, rKEvent.GetKeyCode(), nEntry, nDuplicates, m_nHighlightedItem );
+ if ( pData && (nEntry != ITEMPOS_INVALID) )
+ {
+ mbAutoPopup = true;
+ ChangeHighlightItem( nEntry, true );
+ bDone = true;
+ }
+ }
+ }
+
+ return bDone;
+}
+
+void MenuBarWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ if (!m_pMenu)
+ return;
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ Size aOutputSize = GetOutputSizePixel();
+
+ // no VCL paint if native menus
+ if (m_pMenu->ImplGetSalMenu() && m_pMenu->ImplGetSalMenu()->VisibleMenuBar())
+ return;
+
+ // Make sure that all actual rendering happens in one go to avoid flicker.
+ vcl::BufferDevice pBuffer(this, rRenderContext);
+
+ if (rRenderContext.IsNativeControlSupported(ControlType::Menubar, ControlPart::Entire))
+ {
+ MenubarValue aMenubarValue;
+ aMenubarValue.maTopDockingAreaHeight = ImplGetTopDockingAreaHeight(this);
+
+ if (!rStyleSettings.GetPersonaHeader().IsEmpty())
+ Erase(*pBuffer);
+ else
+ {
+ tools::Rectangle aCtrlRegion( Point(), aOutputSize );
+
+ pBuffer->DrawNativeControl(ControlType::Menubar, ControlPart::Entire, aCtrlRegion,
+ ControlState::ENABLED, aMenubarValue, OUString());
+ }
+
+ ImplAddNWFSeparator(*pBuffer, aOutputSize, aMenubarValue);
+ }
+
+ // shrink the area of the buttons
+ aOutputSize.AdjustWidth( -(m_aCloseBtn->GetSizePixel().Width()) );
+
+ pBuffer->SetFillColor(rStyleSettings.GetMenuColor());
+ m_pMenu->ImplPaint(*pBuffer, aOutputSize, 0);
+
+ if (m_nHighlightedItem != ITEMPOS_INVALID && m_pMenu && !m_pMenu->GetItemList()->GetDataFromPos(m_nHighlightedItem)->bHiddenOnGUI)
+ HighlightItem(*pBuffer, m_nHighlightedItem);
+ else if (m_nRolloveredItem != ITEMPOS_INVALID)
+ HighlightItem(*pBuffer, m_nRolloveredItem);
+
+ // in high contrast mode draw a separating line on the lower edge
+ if (!rRenderContext.IsNativeControlSupported( ControlType::Menubar, ControlPart::Entire) &&
+ rStyleSettings.GetHighContrastMode())
+ {
+ pBuffer->Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::MAPMODE);
+ pBuffer->SetLineColor(COL_WHITE);
+ pBuffer->SetMapMode(MapMode(MapUnit::MapPixel));
+ Size aSize = GetSizePixel();
+ pBuffer->DrawLine(Point(0, aSize.Height() - 1),
+ Point(aSize.Width() - 1, aSize.Height() - 1));
+ pBuffer->Pop();
+ }
+}
+
+void MenuBarWindow::Resize()
+{
+ Size aOutSz = GetOutputSizePixel();
+ tools::Long n = aOutSz.Height()-4;
+ tools::Long nX = aOutSz.Width()-3;
+ tools::Long nY = 2;
+
+ if ( m_aCloseBtn->IsVisible() )
+ {
+ m_aCloseBtn->Hide();
+ m_aCloseBtn->SetImages(n);
+ Size aTbxSize( m_aCloseBtn->CalcWindowSizePixel() );
+ nX -= aTbxSize.Width();
+ tools::Long nTbxY = (aOutSz.Height() - aTbxSize.Height())/2;
+ m_aCloseBtn->setPosSizePixel(nX, nTbxY, aTbxSize.Width(), aTbxSize.Height());
+ nX -= 3;
+ m_aCloseBtn->Show();
+ }
+ if ( m_aFloatBtn->IsVisible() )
+ {
+ nX -= n;
+ m_aFloatBtn->setPosSizePixel( nX, nY, n, n );
+ }
+ if ( m_aHideBtn->IsVisible() )
+ {
+ nX -= n;
+ m_aHideBtn->setPosSizePixel( nX, nY, n, n );
+ }
+
+ m_aFloatBtn->SetSymbol( SymbolType::FLOAT );
+ m_aHideBtn->SetSymbol( SymbolType::HIDE );
+
+ Invalidate();
+}
+
+sal_uInt16 MenuBarWindow::ImplFindEntry( const Point& rMousePos ) const
+{
+ if( m_pMenu )
+ {
+ tools::Long nX = 0;
+ size_t nCount = m_pMenu->pItemList->size();
+ for ( size_t n = 0; n < nCount; n++ )
+ {
+ MenuItemData* pData = m_pMenu->pItemList->GetDataFromPos( n );
+ if ( m_pMenu->ImplIsVisible( n ) )
+ {
+ nX += pData->aSz.Width();
+ if ( nX > rMousePos.X() )
+ return static_cast<sal_uInt16>(n);
+ }
+ }
+ }
+ return ITEMPOS_INVALID;
+}
+
+void MenuBarWindow::RequestHelp( const HelpEvent& rHEvt )
+{
+ sal_uInt16 nId = m_nHighlightedItem;
+ if ( rHEvt.GetMode() & HelpEventMode::CONTEXT )
+ ChangeHighlightItem( ITEMPOS_INVALID, true );
+
+ tools::Rectangle aHighlightRect( ImplGetItemRect( m_nHighlightedItem ) );
+ if( !ImplHandleHelpEvent( this, m_pMenu, nId, rHEvt, aHighlightRect ) )
+ Window::RequestHelp( rHEvt );
+}
+
+void MenuBarWindow::StateChanged( StateChangedType nType )
+{
+ Window::StateChanged( nType );
+
+ if (nType == StateChangedType::ControlForeground ||
+ nType == StateChangedType::ControlBackground)
+ {
+ ApplySettings(*GetOutDev());
+ Invalidate();
+ }
+ else if (nType == StateChangedType::Enable)
+ {
+ Invalidate();
+ }
+ else if(m_pMenu)
+ {
+ m_pMenu->ImplKillLayoutData();
+ }
+}
+
+void MenuBarWindow::LayoutChanged()
+{
+ if (!m_pMenu)
+ return;
+
+ ApplySettings(*GetOutDev());
+
+ // if the font was changed.
+ tools::Long nHeight = m_pMenu->ImplCalcSize(this).Height();
+
+ // depending on the native implementation or the displayable flag
+ // the menubar windows is suppressed (ie, height=0)
+ if (!m_pMenu->IsDisplayable() ||
+ (m_pMenu->ImplGetSalMenu() && m_pMenu->ImplGetSalMenu()->VisibleMenuBar()))
+ {
+ nHeight = 0;
+ }
+ setPosSizePixel(0, 0, 0, nHeight, PosSizeFlags::Height);
+ GetParent()->Resize();
+ Invalidate();
+ Resize();
+
+ m_pMenu->ImplKillLayoutData();
+}
+
+void MenuBarWindow::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ Window::ApplySettings(rRenderContext);
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ SetPointFont(rRenderContext, rStyleSettings.GetMenuFont());
+
+ const BitmapEx& rPersonaBitmap = Application::GetSettings().GetStyleSettings().GetPersonaHeader();
+ SalMenu *pNativeMenu = m_pMenu ? m_pMenu->ImplGetSalMenu() : nullptr;
+ if (pNativeMenu)
+ pNativeMenu->ApplyPersona();
+ if (!rPersonaBitmap.IsEmpty())
+ {
+ Wallpaper aWallpaper(rPersonaBitmap);
+ aWallpaper.SetStyle(WallpaperStyle::TopRight);
+ aWallpaper.SetColor(Application::GetSettings().GetStyleSettings().GetWorkspaceColor());
+
+ rRenderContext.SetBackground(aWallpaper);
+ SetPaintTransparent(false);
+ SetParentClipMode();
+ }
+ else if (rRenderContext.IsNativeControlSupported(ControlType::Menubar, ControlPart::Entire))
+ {
+ rRenderContext.SetBackground(); // background will be drawn by NWF
+ }
+ else
+ {
+ Wallpaper aWallpaper;
+ aWallpaper.SetStyle(WallpaperStyle::ApplicationGradient);
+ rRenderContext.SetBackground(aWallpaper);
+ SetPaintTransparent(false);
+ SetParentClipMode();
+ }
+
+ rRenderContext.SetTextColor(rStyleSettings.GetMenuBarTextColor());
+ rRenderContext.SetTextFillColor();
+ rRenderContext.SetLineColor();
+}
+
+void MenuBarWindow::ImplInitStyleSettings()
+{
+ if (!(IsNativeControlSupported(ControlType::Menubar, ControlPart::MenuItem) &&
+ IsNativeControlSupported(ControlType::Menubar, ControlPart::Entire)))
+ return;
+
+ AllSettings aSettings(GetSettings());
+ ImplGetFrame()->UpdateSettings(aSettings); // to update persona
+ StyleSettings aStyle(aSettings.GetStyleSettings());
+ Color aHighlightTextColor = ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor;
+ if (aHighlightTextColor != COL_TRANSPARENT)
+ {
+ aStyle.SetMenuHighlightTextColor(aHighlightTextColor);
+ }
+ aSettings.SetStyleSettings(aStyle);
+ GetOutDev()->SetSettings(aSettings);
+}
+
+void MenuBarWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
+ ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
+ {
+ ApplySettings(*GetOutDev());
+ ImplInitStyleSettings();
+ LayoutChanged();
+ }
+}
+
+void MenuBarWindow::LoseFocus()
+{
+ if ( !HasChildPathFocus( true ) )
+ ChangeHighlightItem( ITEMPOS_INVALID, false, false );
+}
+
+void MenuBarWindow::GetFocus()
+{
+ SalMenu *pNativeMenu = m_pMenu ? m_pMenu->ImplGetSalMenu() : nullptr;
+ if (pNativeMenu && pNativeMenu->TakeFocus())
+ return;
+
+ if ( m_nHighlightedItem == ITEMPOS_INVALID )
+ {
+ mbAutoPopup = false; // do not open menu when activated by focus handling like taskpane cycling
+ ChangeHighlightItem( 0, false );
+ }
+}
+
+css::uno::Reference<css::accessibility::XAccessible> MenuBarWindow::CreateAccessible()
+{
+ css::uno::Reference<css::accessibility::XAccessible> xAcc;
+
+ if (m_pMenu)
+ xAcc = m_pMenu->GetAccessible();
+
+ return xAcc;
+}
+
+sal_uInt16 MenuBarWindow::AddMenuBarButton( const Image& i_rImage, const Link<MenuBarButtonCallbackArg&,bool>& i_rLink, const OUString& i_rToolTip )
+{
+ // find first free button id
+ sal_uInt16 nId = IID_DOCUMENTCLOSE;
+ std::map< sal_uInt16, AddButtonEntry >::const_iterator it;
+ do
+ {
+ nId++;
+ it = m_aAddButtons.find( nId );
+ } while( it != m_aAddButtons.end() && nId < 128 );
+ SAL_WARN_IF( nId >= 128, "vcl", "too many addbuttons in menubar" );
+ AddButtonEntry& rNewEntry = m_aAddButtons[nId];
+ rNewEntry.m_aSelectLink = i_rLink;
+ m_aCloseBtn->InsertItem(ToolBoxItemId(nId), i_rImage, ToolBoxItemBits::NONE, 0);
+ m_aCloseBtn->calcMinSize();
+ ShowButtons(m_aCloseBtn->IsItemVisible(ToolBoxItemId(IID_DOCUMENTCLOSE)), m_aFloatBtn->IsVisible(), m_aHideBtn->IsVisible());
+ LayoutChanged();
+
+ if( m_pMenu->mpSalMenu )
+ m_pMenu->mpSalMenu->AddMenuBarButton( SalMenuButtonItem( nId, i_rImage, i_rToolTip ) );
+
+ return nId;
+}
+
+void MenuBarWindow::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link<MenuBarButtonCallbackArg&,bool>& rLink )
+{
+ std::map< sal_uInt16, AddButtonEntry >::iterator it = m_aAddButtons.find( nId );
+ if( it != m_aAddButtons.end() )
+ it->second.m_aHighlightLink = rLink;
+}
+
+tools::Rectangle MenuBarWindow::GetMenuBarButtonRectPixel( sal_uInt16 nId )
+{
+ tools::Rectangle aRect;
+ if( m_aAddButtons.find( nId ) != m_aAddButtons.end() )
+ {
+ if( m_pMenu->mpSalMenu )
+ {
+ aRect = m_pMenu->mpSalMenu->GetMenuBarButtonRectPixel( nId, ImplGetWindowImpl()->mpFrame );
+ if( aRect == tools::Rectangle( Point( -1, -1 ), Size( 1, 1 ) ) )
+ {
+ // system menu button is somewhere but location cannot be determined
+ return tools::Rectangle();
+ }
+ }
+
+ if( aRect.IsEmpty() )
+ {
+ aRect = m_aCloseBtn->GetItemRect(ToolBoxItemId(nId));
+ Point aOffset = m_aCloseBtn->OutputToScreenPixel(Point());
+ aRect.Move( aOffset.X(), aOffset.Y() );
+ }
+ }
+ return aRect;
+}
+
+void MenuBarWindow::RemoveMenuBarButton( sal_uInt16 nId )
+{
+ ToolBox::ImplToolItems::size_type nPos = m_aCloseBtn->GetItemPos(ToolBoxItemId(nId));
+ m_aCloseBtn->RemoveItem(nPos);
+ m_aAddButtons.erase( nId );
+ m_aCloseBtn->calcMinSize();
+ LayoutChanged();
+
+ if( m_pMenu->mpSalMenu )
+ m_pMenu->mpSalMenu->RemoveMenuBarButton( nId );
+}
+
+bool MenuBarWindow::HandleMenuButtonEvent( sal_uInt16 i_nButtonId )
+{
+ std::map< sal_uInt16, AddButtonEntry >::iterator it = m_aAddButtons.find( i_nButtonId );
+ if( it != m_aAddButtons.end() )
+ {
+ MenuBarButtonCallbackArg aArg;
+ aArg.nId = it->first;
+ aArg.bHighlight = true;
+ return it->second.m_aSelectLink.Call( aArg );
+ }
+ return false;
+}
+
+bool MenuBarWindow::CanGetFocus() const
+{
+ /* #i83908# do not use the menubar if it is native or invisible
+ this relies on MenuBar::ImplCreate setting the height of the menubar
+ to 0 in this case
+ */
+ SalMenu *pNativeMenu = m_pMenu ? m_pMenu->ImplGetSalMenu() : nullptr;
+ if (pNativeMenu && pNativeMenu->VisibleMenuBar())
+ return pNativeMenu->CanGetFocus();
+ return GetSizePixel().Height() > 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/menubarwindow.hxx b/vcl/source/window/menubarwindow.hxx
new file mode 100644
index 0000000000..b5f026254d
--- /dev/null
+++ b/vcl/source/window/menubarwindow.hxx
@@ -0,0 +1,142 @@
+/* -*- 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 "menuwindow.hxx"
+
+#include <vcl/toolkit/button.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/window.hxx>
+
+#include <map>
+
+class Button;
+
+/** Toolbox that holds the close button (right hand side of the menubar).
+
+This is also used by the online update check; when an update is available, it
+inserts here the button that leads to the download of the update.
+*/
+class DecoToolBox : public ToolBox
+{
+ tools::Long lastSize;
+ Size maMinSize;
+
+public:
+ explicit DecoToolBox(vcl::Window* pParent);
+
+ void DataChanged( const DataChangedEvent& rDCEvt ) override;
+
+ void SetImages( tools::Long nMaxHeight, bool bForce = false );
+
+ void calcMinSize();
+ const Size& getMinSize() const { return maMinSize;}
+
+ Image maImage;
+};
+
+
+/** Class that implements the actual window of the menu bar.
+*/
+class MenuBarWindow : public vcl::Window, public MenuWindow
+{
+ friend class MenuBar;
+ friend class Menu;
+
+private:
+ struct AddButtonEntry
+ {
+ Link<MenuBarButtonCallbackArg&,bool> m_aSelectLink;
+ Link<MenuBarButtonCallbackArg&,bool> m_aHighlightLink;
+ };
+
+ VclPtr<MenuBar> m_pMenu;
+ VclPtr<PopupMenu> m_pActivePopup;
+ VclPtr<PopupMenu> mpParentPopup;
+ sal_uInt16 m_nHighlightedItem;
+ sal_uInt16 m_nRolloveredItem;
+ VclPtr<vcl::Window> m_xSaveFocusId;
+ bool mbAutoPopup;
+ bool m_bIgnoreFirstMove;
+
+ VclPtr<DecoToolBox> m_aCloseBtn;
+ VclPtr<PushButton> m_aFloatBtn;
+ VclPtr<PushButton> m_aHideBtn;
+
+ std::map< sal_uInt16, AddButtonEntry > m_aAddButtons;
+
+ void HighlightItem(vcl::RenderContext& rRenderContext, sal_uInt16 nPos);
+ void ChangeHighlightItem(sal_uInt16 n, bool bSelectPopupEntry, bool bAllowRestoreFocus = true, bool bDefaultToDocument = true);
+
+ sal_uInt16 ImplFindEntry( const Point& rMousePos ) const;
+ void ImplCreatePopup( bool bPreSelectFirst );
+ bool HandleKeyEvent(const KeyEvent& rKEvent, bool bFromMenu = true);
+ tools::Rectangle ImplGetItemRect( sal_uInt16 nPos ) const;
+
+ void ImplInitStyleSettings();
+
+ virtual void ApplySettings(vcl::RenderContext& rRenderContext) override;
+
+ DECL_LINK( CloseHdl, ToolBox*, void );
+ DECL_LINK( ToolboxEventHdl, VclWindowEvent&, void );
+ DECL_LINK( ShowHideListener, VclWindowEvent&, void );
+
+ void StateChanged( StateChangedType nType ) override;
+ void DataChanged( const DataChangedEvent& rDCEvt ) override;
+ void LoseFocus() override;
+ void GetFocus() override;
+
+public:
+ explicit MenuBarWindow( vcl::Window* pParent );
+ virtual ~MenuBarWindow() override;
+ virtual void dispose() override;
+
+ void ShowButtons(bool bClose, bool bFloat, bool bHide);
+
+ virtual void MouseMove( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& rMEvt ) override;
+ virtual void KeyInput( const KeyEvent& rKEvent ) override;
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
+ virtual void Resize() override;
+ virtual void RequestHelp( const HelpEvent& rHEvt ) override;
+
+ void SetMenu(MenuBar* pMenu);
+ void SetHeight(tools::Long nHeight);
+ void KillActivePopup();
+ void PopupClosed(Menu const * pMenu);
+ sal_uInt16 GetHighlightedItem() const { return m_nHighlightedItem; }
+ virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override;
+
+ void SetAutoPopup(bool bAuto) { mbAutoPopup = bAuto; }
+ void LayoutChanged();
+ Size const & MinCloseButtonSize() const;
+
+ /// Add an arbitrary button to the menubar that will appear next to the close button.
+ sal_uInt16 AddMenuBarButton(const Image&, const Link<MenuBarButtonCallbackArg&,bool>&, const OUString&);
+ void SetMenuBarButtonHighlightHdl(sal_uInt16 nId, const Link<MenuBarButtonCallbackArg&,bool>&);
+ tools::Rectangle GetMenuBarButtonRectPixel(sal_uInt16 nId);
+ void RemoveMenuBarButton(sal_uInt16 nId);
+ bool HandleMenuButtonEvent(sal_uInt16 i_nButtonId);
+ bool CanGetFocus() const;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/menufloatingwindow.cxx b/vcl/source/window/menufloatingwindow.cxx
new file mode 100644
index 0000000000..95a0d3f4d0
--- /dev/null
+++ b/vcl/source/window/menufloatingwindow.cxx
@@ -0,0 +1,1318 @@
+/* -*- 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 "menufloatingwindow.hxx"
+#include "menuitemlist.hxx"
+#include "bufferdevice.hxx"
+
+#include <sal/log.hxx>
+#include <salframe.hxx>
+#include <svdata.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/settings.hxx>
+#include <window.h>
+
+MenuFloatingWindow::MenuFloatingWindow( Menu* pMen, vcl::Window* pParent, WinBits nStyle ) :
+ FloatingWindow( pParent, nStyle ),
+ pMenu(pMen),
+ aHighlightChangedTimer("vcl::MenuFloatingWindow aHighlightChangedTimer"),
+ aSubmenuCloseTimer( "vcl::MenuFloatingWindow aSubmenuCloseTimer" ),
+ aScrollTimer( "vcl::MenuFloatingWindow aScrollTimer" ),
+ nHighlightedItem(ITEMPOS_INVALID),
+ nMBDownPos(ITEMPOS_INVALID),
+ nScrollerHeight(0),
+ nFirstEntry(0),
+ nPosInParent(ITEMPOS_INVALID),
+ bInExecute(false),
+ bScrollMenu(false),
+ bScrollUp(false),
+ bScrollDown(false),
+ bIgnoreFirstMove(true),
+ bKeyInput(false)
+{
+ mpWindowImpl->mbMenuFloatingWindow= true;
+
+ ApplySettings(*GetOutDev());
+
+ SetPopupModeEndHdl( LINK( this, MenuFloatingWindow, PopupEnd ) );
+
+ aHighlightChangedTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, HighlightChanged ) );
+ aHighlightChangedTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
+
+ aSubmenuCloseTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
+ aSubmenuCloseTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, SubmenuClose ) );
+
+ aScrollTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, AutoScroll ) );
+
+ AddEventListener( LINK( this, MenuFloatingWindow, ShowHideListener ) );
+}
+
+void MenuFloatingWindow::doShutdown()
+{
+ if( !pMenu )
+ return;
+
+ // #105373# notify toolkit that highlight was removed
+ // otherwise the entry will not be read when the menu is opened again
+ if( nHighlightedItem != ITEMPOS_INVALID )
+ pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, nHighlightedItem );
+ if (!bKeyInput && pMenu && pMenu->pStartedFrom && !pMenu->pStartedFrom->IsMenuBar())
+ {
+ // #102461# remove highlight in parent
+ size_t i, nCount = pMenu->pStartedFrom->pItemList->size();
+ for(i = 0; i < nCount; i++)
+ {
+ MenuItemData* pData = pMenu->pStartedFrom->pItemList->GetDataFromPos( i );
+ if( pData && ( pData->pSubMenu == pMenu ) )
+ break;
+ }
+ if( i < nCount )
+ {
+ MenuFloatingWindow* pPWin = static_cast<MenuFloatingWindow*>(pMenu->pStartedFrom->ImplGetWindow());
+ if (pPWin)
+ pPWin->InvalidateItem(i);
+ }
+ }
+
+ // free the reference to the accessible component
+ SetAccessible( css::uno::Reference< css::accessibility::XAccessible >() );
+
+ aHighlightChangedTimer.Stop();
+
+ // #95056# invalidate screen area covered by system window
+ // so this can be taken into account if the commandhandler performs a scroll operation
+ if( GetParent() )
+ {
+ tools::Rectangle aInvRect( GetWindowExtentsRelative( *GetParent() ) );
+ GetParent()->Invalidate( aInvRect );
+ }
+ pMenu = nullptr;
+ RemoveEventListener( LINK( this, MenuFloatingWindow, ShowHideListener ) );
+
+ aScrollTimer.Stop();
+ aSubmenuCloseTimer.Stop();
+ aSubmenuCloseTimer.Stop();
+ aHighlightChangedTimer.Stop();
+ aHighlightChangedTimer.Stop();
+
+}
+
+MenuFloatingWindow::~MenuFloatingWindow()
+{
+ disposeOnce();
+}
+
+void MenuFloatingWindow::dispose()
+{
+ doShutdown();
+ pMenu.clear();
+ pActivePopup.clear();
+ xSaveFocusId.clear();
+ FloatingWindow::dispose();
+}
+
+void MenuFloatingWindow::Resize()
+{
+ InitMenuClipRegion(*GetOutDev()); // FIXME
+}
+
+void MenuFloatingWindow::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ FloatingWindow::ApplySettings(rRenderContext);
+
+ if (IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItem) &&
+ IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
+ {
+ AllSettings aSettings(GetSettings());
+ ImplGetFrame()->UpdateSettings(aSettings); // Update theme colors.
+ StyleSettings aStyle(aSettings.GetStyleSettings());
+ Color aHighlightTextColor = ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor;
+ if (aHighlightTextColor != COL_TRANSPARENT)
+ {
+ aStyle.SetMenuHighlightTextColor(aHighlightTextColor);
+ }
+ aSettings.SetStyleSettings(aStyle);
+ GetOutDev()->SetSettings(aSettings);
+ }
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ SetPointFont(rRenderContext, rStyleSettings.GetMenuFont());
+
+ if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
+ {
+ rRenderContext.SetBackground(); // background will be drawn by NWF
+ }
+ else
+ rRenderContext.SetBackground(Wallpaper(rStyleSettings.GetMenuColor()));
+
+ rRenderContext.SetTextColor(rStyleSettings.GetMenuTextColor());
+ rRenderContext.SetTextFillColor();
+ rRenderContext.SetLineColor();
+}
+
+/// Get a negative pixel offset for an offset menu
+tools::Long MenuFloatingWindow::ImplGetStartY() const
+{
+ tools::Long nY = 0;
+ if( pMenu )
+ {
+ // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
+ if ( nFirstEntry > 0 && !pMenu->GetItemList()->GetDataFromPos(nFirstEntry - 1) )
+ {
+ return 0;
+ }
+
+ for ( sal_uInt16 n = 0; n < nFirstEntry; n++ )
+ nY += pMenu->GetItemList()->GetDataFromPos( n )->aSz.Height();
+ nY -= pMenu->GetTitleHeight();
+ }
+ return -nY;
+}
+
+vcl::Region MenuFloatingWindow::ImplCalcClipRegion() const
+{
+ Size aOutSz = GetOutputSizePixel();
+ tools::Rectangle aRect( Point(), aOutSz );
+ aRect.AdjustTop(nScrollerHeight );
+ aRect.AdjustBottom( -nScrollerHeight );
+
+ vcl::Region aRegion(aRect);
+
+ return aRegion;
+}
+
+void MenuFloatingWindow::InitMenuClipRegion(vcl::RenderContext& rRenderContext)
+{
+ if (IsScrollMenu())
+ {
+ rRenderContext.SetClipRegion(ImplCalcClipRegion());
+ }
+ else
+ {
+ rRenderContext.SetClipRegion();
+ }
+}
+
+void MenuFloatingWindow::ImplHighlightItem( const MouseEvent& rMEvt, bool bMBDown )
+{
+ if( ! pMenu )
+ return;
+
+ tools::Long nY = GetInitialItemY();
+ tools::Long nMouseY = rMEvt.GetPosPixel().Y();
+ Size aOutSz = GetOutputSizePixel();
+ if ( ( nMouseY >= nY ) && ( nMouseY < aOutSz.Height() ) )
+ {
+ bool bHighlighted = false;
+ size_t nCount = pMenu->pItemList->size();
+ for ( size_t n = 0; !bHighlighted && ( n < nCount ); n++ )
+ {
+ if ( pMenu->ImplIsVisible( n ) )
+ {
+ MenuItemData* pItemData = pMenu->pItemList->GetDataFromPos( n );
+ tools::Long nOldY = nY;
+ nY += pItemData->aSz.Height();
+ if ( ( nOldY <= nMouseY ) && ( nY > nMouseY ) && pMenu->ImplIsSelectable( n ) )
+ {
+ bool bPopupArea = true;
+ if ( pItemData->nBits & MenuItemBits::POPUPSELECT )
+ {
+ // only when clicked over the arrow...
+ Size aSz = GetOutputSizePixel();
+ tools::Long nFontHeight = GetTextHeight();
+ bPopupArea = ( rMEvt.GetPosPixel().X() >= ( aSz.Width() - nFontHeight - nFontHeight/4 ) );
+ }
+
+ if ( bMBDown )
+ {
+ if ( n != nHighlightedItem )
+ {
+ ChangeHighlightItem( static_cast<sal_uInt16>(n), false );
+ }
+
+ bool bAllowNewPopup = true;
+ if ( pActivePopup )
+ {
+ MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
+ bAllowNewPopup = pData && ( pData->pSubMenu != pActivePopup );
+ if ( bAllowNewPopup )
+ KillActivePopup();
+ }
+
+ if ( bPopupArea && bAllowNewPopup )
+ {
+ HighlightChanged( nullptr );
+ }
+ }
+ else
+ {
+ if ( n != nHighlightedItem )
+ {
+ ChangeHighlightItem( static_cast<sal_uInt16>(n), true );
+ }
+ else if ( pItemData->nBits & MenuItemBits::POPUPSELECT )
+ {
+ if ( bPopupArea && ( pActivePopup != pItemData->pSubMenu ) )
+ HighlightChanged( nullptr );
+ }
+ }
+ bHighlighted = true;
+ }
+ }
+ }
+ if ( !bHighlighted )
+ ChangeHighlightItem( ITEMPOS_INVALID, true );
+ }
+ else
+ {
+ ImplScroll( rMEvt.GetPosPixel() );
+ ChangeHighlightItem( ITEMPOS_INVALID, true );
+ }
+}
+
+IMPL_LINK_NOARG(MenuFloatingWindow, PopupEnd, FloatingWindow*, void)
+{
+ // "this" will be deleted before the end of this method!
+ Menu* pM = pMenu;
+ if ( bInExecute )
+ {
+ End();
+ if ( pActivePopup )
+ {
+ KillActivePopup(); // should be ok to just remove it
+ //pActivePopup->bCanceled = true;
+ }
+ pMenu->bInCallback = true;
+ pMenu->Deactivate();
+ pMenu->bInCallback = false;
+ }
+ else
+ {
+ if (pMenu && pMenu->pStartedFrom)
+ pMenu->pStartedFrom->ClosePopup(pMenu);
+ }
+
+ if ( pM )
+ pM->pStartedFrom = nullptr;
+}
+
+IMPL_LINK_NOARG(MenuFloatingWindow, AutoScroll, Timer *, void)
+{
+ ImplScroll( GetPointerPosPixel() );
+}
+
+IMPL_LINK( MenuFloatingWindow, HighlightChanged, Timer*, pTimer, void )
+{
+ if( ! pMenu )
+ return;
+
+ MenuItemData* pItemData = pMenu->pItemList->GetDataFromPos( nHighlightedItem );
+ if ( !pItemData )
+ return;
+
+ if ( pActivePopup && ( pActivePopup != pItemData->pSubMenu ) )
+ {
+ FloatWinPopupFlags nOldFlags = GetPopupModeFlags();
+ SetPopupModeFlags( GetPopupModeFlags() | FloatWinPopupFlags::NoAppFocusClose );
+ KillActivePopup();
+ SetPopupModeFlags( nOldFlags );
+ }
+ if ( !(pItemData->bEnabled && pItemData->pSubMenu && pItemData->pSubMenu->GetItemCount() && ( pItemData->pSubMenu != pActivePopup )) )
+ return;
+
+ pActivePopup = pItemData->pSubMenu.get();
+ tools::Long nY = nScrollerHeight+ImplGetStartY();
+ MenuItemData* pData = nullptr;
+ for ( sal_uLong n = 0; n < nHighlightedItem; n++ )
+ {
+ pData = pMenu->pItemList->GetDataFromPos( n );
+ nY += pData->aSz.Height();
+ }
+ pData = pMenu->pItemList->GetDataFromPos( nHighlightedItem );
+ Size MySize = GetOutputSizePixel();
+ Point aItemTopLeft( 0, nY );
+ Point aItemBottomRight( aItemTopLeft );
+ aItemBottomRight.AdjustX(MySize.Width() );
+ aItemBottomRight.AdjustY(pData->aSz.Height() );
+
+ // shift the popups a little
+ aItemTopLeft.AdjustX(2 );
+ aItemBottomRight.AdjustX( -2 );
+ if ( nHighlightedItem )
+ aItemTopLeft.AdjustY( -2 );
+ else
+ {
+ sal_Int32 nL, nT, nR, nB;
+ GetBorder( nL, nT, nR, nB );
+ aItemTopLeft.AdjustY( -nT );
+ }
+
+ // pTest: crash due to Reschedule() in call of Activate()
+ // Also it is prevented that submenus are displayed which
+ // were for long in Activate Rescheduled and which should not be
+ // displayed now.
+ Menu* pTest = pActivePopup;
+ FloatWinPopupFlags nOldFlags = GetPopupModeFlags();
+ SetPopupModeFlags( GetPopupModeFlags() | FloatWinPopupFlags::NoAppFocusClose );
+ sal_uInt16 nRet = pActivePopup->ImplExecute( this, tools::Rectangle( aItemTopLeft, aItemBottomRight ), FloatWinPopupFlags::Right, pMenu, pTimer == nullptr );
+ SetPopupModeFlags( nOldFlags );
+
+ // nRet != 0, if it was stopped during Activate()...
+ if ( !nRet && ( pActivePopup == pTest ) && pActivePopup->ImplGetWindow() )
+ pActivePopup->ImplGetFloatingWindow()->AddPopupModeWindow( this );
+}
+
+IMPL_LINK_NOARG(MenuFloatingWindow, SubmenuClose, Timer *, void)
+{
+ if( pMenu && pMenu->pStartedFrom )
+ {
+ MenuFloatingWindow* pWin = static_cast<MenuFloatingWindow*>(pMenu->pStartedFrom->GetWindow());
+ if( pWin )
+ pWin->KillActivePopup();
+ }
+}
+
+IMPL_LINK( MenuFloatingWindow, ShowHideListener, VclWindowEvent&, rEvent, void )
+{
+ if( ! pMenu )
+ return;
+
+ if( rEvent.GetId() == VclEventId::WindowShow )
+ pMenu->ImplCallEventListeners( VclEventId::MenuShow, ITEMPOS_INVALID );
+ else if( rEvent.GetId() == VclEventId::WindowHide )
+ pMenu->ImplCallEventListeners( VclEventId::MenuHide, ITEMPOS_INVALID );
+}
+
+void MenuFloatingWindow::EnableScrollMenu( bool b )
+{
+ bScrollMenu = b;
+ nScrollerHeight = b ? static_cast<sal_uInt16>(GetSettings().GetStyleSettings().GetScrollBarSize()) /2 : 0;
+ bScrollDown = true;
+ InitMenuClipRegion(*GetOutDev());
+}
+
+void MenuFloatingWindow::Start()
+{
+ if (bInExecute)
+ return;
+ bInExecute = true;
+ if (GetParent())
+ GetParent()->IncModalCount();
+}
+
+bool MenuFloatingWindow::MenuInHierarchyHasFocus() const
+{
+ if (HasChildPathFocus())
+ return true;
+ PopupMenu* pSub = GetActivePopup();
+ if (!pSub)
+ return false;
+ return pSub->ImplGetFloatingWindow()->HasChildPathFocus();
+}
+
+void MenuFloatingWindow::End()
+{
+ if (!bInExecute)
+ return;
+
+ if (GetParent() && !GetParent()->isDisposed())
+ GetParent()->DecModalCount();
+
+ // restore focus to previous window if we still have the focus
+ VclPtr<vcl::Window> xFocusId(xSaveFocusId);
+ xSaveFocusId = nullptr;
+ if (xFocusId != nullptr && MenuInHierarchyHasFocus())
+ {
+ ImplGetSVData()->mpWinData->mbNoDeactivate = false;
+ Window::EndSaveFocus(xFocusId);
+ }
+
+ bInExecute = false;
+}
+
+void MenuFloatingWindow::Execute()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ pSVData->maAppData.mpActivePopupMenu = static_cast<PopupMenu*>(pMenu.get());
+
+ Start();
+
+ while (bInExecute && !Application::IsQuit())
+ Application::Yield();
+
+ pSVData->maAppData.mpActivePopupMenu = nullptr;
+}
+
+void MenuFloatingWindow::StopExecute()
+{
+ End();
+
+ ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xSaveFocusId);
+
+ aHighlightChangedTimer.Stop();
+ if (pActivePopup)
+ {
+ KillActivePopup();
+ }
+ // notify parent, needed for accessibility
+ if( pMenu && pMenu->pStartedFrom )
+ pMenu->pStartedFrom->ImplCallEventListeners( VclEventId::MenuSubmenuDeactivate, nPosInParent );
+}
+
+void MenuFloatingWindow::KillActivePopup( PopupMenu* pThisOnly )
+{
+ if ( !pActivePopup || ( pThisOnly && ( pThisOnly != pActivePopup ) ) )
+ return;
+
+ if( pActivePopup->pWindow )
+ if( static_cast<FloatingWindow *>(pActivePopup->pWindow.get())->IsInCleanUp() )
+ return; // kill it later
+ if ( pActivePopup->bInCallback )
+ pActivePopup->bCanceled = true;
+
+ // For all actions pActivePopup = 0, if e.g.
+ // PopupModeEndHdl the popups to destroy were called synchronous
+ PopupMenu* pPopup = pActivePopup;
+ pActivePopup = nullptr;
+ pPopup->bInCallback = true;
+ pPopup->Deactivate();
+ pPopup->bInCallback = false;
+ if ( pPopup->ImplGetWindow() )
+ {
+ pPopup->ImplGetFloatingWindow()->StopExecute();
+ pPopup->ImplGetFloatingWindow()->doShutdown();
+ pPopup->pWindow.disposeAndClear();
+
+ PaintImmediately();
+ }
+}
+
+void MenuFloatingWindow::EndExecute()
+{
+ Menu* pStart = pMenu ? pMenu->ImplGetStartMenu() : nullptr;
+
+ // if started elsewhere, cleanup there as well
+ MenuFloatingWindow* pCleanUpFrom = this;
+ MenuFloatingWindow* pWin = this;
+ while (pWin && !pWin->bInExecute &&
+ pWin->pMenu->pStartedFrom && !pWin->pMenu->pStartedFrom->IsMenuBar())
+ {
+ pWin = static_cast<PopupMenu*>(pWin->pMenu->pStartedFrom.get())->ImplGetFloatingWindow();
+ }
+ if ( pWin )
+ pCleanUpFrom = pWin;
+
+ // this window will be destroyed => store date locally...
+ Menu* pM = pMenu;
+ sal_uInt16 nItem = nHighlightedItem;
+
+ pCleanUpFrom->StopExecute();
+
+ if ( !(nItem != ITEMPOS_INVALID && pM) )
+ return;
+
+ MenuItemData* pItemData = pM->GetItemList()->GetDataFromPos( nItem );
+ if ( pItemData && !pItemData->bIsTemporary )
+ {
+ pM->nSelectedId = pItemData->nId;
+ pM->sSelectedIdent = pItemData->sIdent;
+ if (pStart)
+ {
+ pStart->nSelectedId = pItemData->nId;
+ pStart->sSelectedIdent = pItemData->sIdent;
+ }
+
+ pM->ImplSelect();
+ }
+}
+
+void MenuFloatingWindow::EndExecute( sal_uInt16 nId )
+{
+ size_t nPos;
+ if ( pMenu && pMenu->GetItemList()->GetData( nId, nPos ) )
+ nHighlightedItem = nPos;
+ else
+ nHighlightedItem = ITEMPOS_INVALID;
+
+ EndExecute();
+}
+
+void MenuFloatingWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ // TH creates a ToTop on this window, but the active popup
+ // should stay on top...
+ // due to focus change this would close all menus -> don't do it (#94123)
+ //if ( pActivePopup && pActivePopup->ImplGetWindow() && !pActivePopup->ImplGetFloatingWindow()->pActivePopup )
+ // pActivePopup->ImplGetFloatingWindow()->ToTop( ToTopFlags::NoGrabFocus );
+
+ ImplHighlightItem( rMEvt, true );
+
+ nMBDownPos = nHighlightedItem;
+}
+
+void MenuFloatingWindow::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ MenuItemData* pData = pMenu ? pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ) : nullptr;
+ // nMBDownPos store in local variable and reset immediately,
+ // as it will be too late after EndExecute
+ sal_uInt16 _nMBDownPos = nMBDownPos;
+ nMBDownPos = ITEMPOS_INVALID;
+ if ( !(pData && pData->bEnabled && ( pData->eType != MenuItemType::SEPARATOR )) )
+ return;
+
+ if ( !pData->pSubMenu )
+ {
+ EndExecute();
+ }
+ else if ( ( pData->nBits & MenuItemBits::POPUPSELECT ) && ( nHighlightedItem == _nMBDownPos ) && ( rMEvt.GetClicks() == 2 ) )
+ {
+ // not when clicked over the arrow...
+ Size aSz = GetOutputSizePixel();
+ tools::Long nFontHeight = GetTextHeight();
+ if ( rMEvt.GetPosPixel().X() < ( aSz.Width() - nFontHeight - nFontHeight/4 ) )
+ EndExecute();
+ }
+
+}
+
+void MenuFloatingWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ if ( !IsVisible() || rMEvt.IsSynthetic() || rMEvt.IsEnterWindow() )
+ return;
+
+ if ( rMEvt.IsLeaveWindow() )
+ {
+ // #102461# do not remove highlight if a popup menu is open at this position
+ MenuItemData* pData = pMenu ? pMenu->pItemList->GetDataFromPos( nHighlightedItem ) : nullptr;
+ // close popup with some delayed if we leave somewhere else
+ if( pActivePopup && pData && pData->pSubMenu != pActivePopup )
+ pActivePopup->ImplGetFloatingWindow()->aSubmenuCloseTimer.Start();
+
+ if( !pActivePopup || (pData && pData->pSubMenu != pActivePopup ) )
+ ChangeHighlightItem( ITEMPOS_INVALID, false );
+
+ if ( IsScrollMenu() )
+ ImplScroll( rMEvt.GetPosPixel() );
+ }
+ else
+ {
+ aSubmenuCloseTimer.Stop();
+ if( bIgnoreFirstMove )
+ bIgnoreFirstMove = false;
+ else
+ ImplHighlightItem( rMEvt, false );
+ }
+}
+
+void MenuFloatingWindow::ImplScroll( bool bUp )
+{
+ KillActivePopup();
+ PaintImmediately();
+
+ if (!pMenu)
+ return;
+
+ Invalidate();
+
+ pMenu->ImplKillLayoutData();
+
+ if ( bScrollUp && bUp )
+ {
+ nFirstEntry = pMenu->ImplGetPrevVisible( nFirstEntry );
+ SAL_WARN_IF( nFirstEntry == ITEMPOS_INVALID, "vcl", "Scroll?!" );
+
+ // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
+ const auto pItemData = pMenu->GetItemList()->GetDataFromPos( nFirstEntry );
+ if ( pItemData )
+ {
+ tools::Long nScrollEntryHeight = pItemData->aSz.Height();
+
+ if ( !bScrollDown )
+ {
+ bScrollDown = true;
+ Invalidate();
+ }
+
+ if ( pMenu->ImplGetPrevVisible( nFirstEntry ) == ITEMPOS_INVALID )
+ {
+ bScrollUp = false;
+ Invalidate();
+ }
+
+ Scroll( 0, nScrollEntryHeight, ImplCalcClipRegion().GetBoundRect(), ScrollFlags::Clip );
+ }
+ }
+ else if ( bScrollDown && !bUp )
+ {
+ // avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
+ const auto pItemData = pMenu->GetItemList()->GetDataFromPos( nFirstEntry );
+ if ( pItemData )
+ {
+ tools::Long nScrollEntryHeight = pItemData->aSz.Height();
+
+ nFirstEntry = pMenu->ImplGetNextVisible( nFirstEntry );
+ SAL_WARN_IF( nFirstEntry == ITEMPOS_INVALID, "vcl", "Scroll?!" );
+
+ if ( !bScrollUp )
+ {
+ bScrollUp = true;
+ Invalidate();
+ }
+
+ tools::Long nHeight = GetOutputSizePixel().Height();
+ sal_uInt16 nLastVisible;
+ static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( nHeight, nFirstEntry, &nLastVisible );
+ if ( pMenu->ImplGetNextVisible( nLastVisible ) == ITEMPOS_INVALID )
+ {
+ bScrollDown = false;
+ Invalidate();
+ }
+
+ Scroll( 0, -nScrollEntryHeight, ImplCalcClipRegion().GetBoundRect(), ScrollFlags::Clip );
+ }
+ }
+
+ Invalidate();
+}
+
+void MenuFloatingWindow::ImplScroll( const Point& rMousePos )
+{
+ Size aOutSz = GetOutputSizePixel();
+
+ tools::Long nY = nScrollerHeight;
+ tools::Long nMouseY = rMousePos.Y();
+ tools::Long nDelta = 0;
+
+ if ( bScrollUp && ( nMouseY < nY ) )
+ {
+ ImplScroll( true );
+ nDelta = nY - nMouseY;
+ }
+ else if ( bScrollDown && ( nMouseY > ( aOutSz.Height() - nY ) ) )
+ {
+ ImplScroll( false );
+ nDelta = nMouseY - ( aOutSz.Height() - nY );
+ }
+
+ if ( !nDelta )
+ return;
+
+ aScrollTimer.Stop(); // if scrolled through MouseMove.
+ tools::Long nTimeout;
+ if ( nDelta < 3 )
+ nTimeout = 200;
+ else if ( nDelta < 5 )
+ nTimeout = 100;
+ else if ( nDelta < 8 )
+ nTimeout = 70;
+ else if ( nDelta < 12 )
+ nTimeout = 40;
+ else
+ nTimeout = 20;
+ aScrollTimer.SetTimeout( nTimeout );
+ aScrollTimer.Start();
+}
+void MenuFloatingWindow::ChangeHighlightItem( sal_uInt16 n, bool bStartPopupTimer )
+{
+ // #57934# if necessary, immediately close the active, as TH's backgroundstorage works.
+ // #65750# we prefer to refrain from the background storage of small lines.
+ // otherwise the menus are difficult to operate.
+ // MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n );
+ // if ( pActivePopup && pNextData && ( pActivePopup != pNextData->pSubMenu ) )
+ // KillActivePopup();
+
+ aSubmenuCloseTimer.Stop();
+ if( ! pMenu )
+ return;
+
+ if ( nHighlightedItem != ITEMPOS_INVALID )
+ {
+ InvalidateItem(nHighlightedItem);
+ pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, nHighlightedItem );
+ }
+
+ nHighlightedItem = n;
+ SAL_WARN_IF( !pMenu->ImplIsVisible( nHighlightedItem ) && nHighlightedItem != ITEMPOS_INVALID, "vcl", "ChangeHighlightItem: Not visible!" );
+ if( nHighlightedItem != ITEMPOS_INVALID )
+ {
+ if (pMenu->pStartedFrom && !pMenu->pStartedFrom->IsMenuBar())
+ {
+ // #102461# make sure parent entry is highlighted as well
+ size_t i, nCount = pMenu->pStartedFrom->pItemList->size();
+ for(i = 0; i < nCount; i++)
+ {
+ MenuItemData* pData = pMenu->pStartedFrom->pItemList->GetDataFromPos( i );
+ if( pData && ( pData->pSubMenu == pMenu ) )
+ break;
+ }
+ if( i < nCount )
+ {
+ MenuFloatingWindow* pPWin = static_cast<MenuFloatingWindow*>(pMenu->pStartedFrom->ImplGetWindow());
+ if( pPWin && pPWin->nHighlightedItem != i )
+ {
+ pPWin->InvalidateItem(i);
+ pPWin->nHighlightedItem = i;
+ }
+ }
+ }
+ InvalidateItem(nHighlightedItem);
+ pMenu->ImplCallHighlight( nHighlightedItem );
+ }
+ else
+ {
+ pMenu->nSelectedId = 0;
+ pMenu->sSelectedIdent.clear();
+ }
+
+ if ( bStartPopupTimer )
+ {
+ // #102438# Menu items are not selectable
+ // If a menu item is selected by an AT-tool via the XAccessibleAction, XAccessibleValue
+ // or XAccessibleSelection interface, and the parent popup menus are not executed yet,
+ // the parent popup menus must be executed SYNCHRONOUSLY, before the menu item is selected.
+ if ( GetSettings().GetMouseSettings().GetMenuDelay() )
+ aHighlightChangedTimer.Start();
+ else
+ HighlightChanged( &aHighlightChangedTimer );
+ }
+}
+
+/// Calculate the initial vertical pixel offset of the first item.
+/// May be negative for scrolled windows.
+tools::Long MenuFloatingWindow::GetInitialItemY(tools::Long *pStartY) const
+{
+ tools::Long nStartY = ImplGetStartY();
+ if (pStartY)
+ *pStartY = nStartY;
+ return nScrollerHeight + nStartY +
+ ImplGetSVData()->maNWFData.mnMenuFormatBorderY;
+}
+
+/// Emit an Invalidate just for this item's area
+void MenuFloatingWindow::InvalidateItem(sal_uInt16 nPos)
+{
+ if (!pMenu)
+ return;
+
+ tools::Long nY = GetInitialItemY();
+ size_t nCount = pMenu->pItemList->size();
+ for (size_t n = 0; n < nCount; n++)
+ {
+ MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
+ tools::Long nHeight = pData->aSz.Height();
+ if (n == nPos)
+ {
+ Size aWidth( GetSizePixel() );
+ tools::Rectangle aRect(Point(0, nY), Size(aWidth.Width(), nHeight));
+ Invalidate( aRect );
+ }
+ nY += nHeight;
+ }
+}
+
+void MenuFloatingWindow::RenderHighlightItem(vcl::RenderContext& rRenderContext, sal_uInt16 nPos)
+{
+ if (!pMenu)
+ return;
+
+ Size aSz(GetOutputSizePixel());
+
+ tools::Long nX = 0;
+ tools::Long nStartY;
+ tools::Long nY = GetInitialItemY(&nStartY);
+
+ int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
+
+ size_t nCount = pMenu->pItemList->size();
+ for (size_t n = 0; n < nCount; n++)
+ {
+ MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
+ if (n == nPos)
+ {
+ SAL_WARN_IF(!pMenu->ImplIsVisible(n), "vcl", "Highlight: Item not visible!");
+ if (pData->eType != MenuItemType::SEPARATOR)
+ {
+ bool bRestoreLineColor = false;
+ Color oldLineColor;
+ bool bDrawItemRect = true;
+
+ tools::Rectangle aItemRect(Point(nX + nOuterSpaceX, nY), Size(aSz.Width() - 2 * nOuterSpaceX, pData->aSz.Height()));
+ if (pData->nBits & MenuItemBits::POPUPSELECT)
+ {
+ tools::Long nFontHeight = GetTextHeight();
+ aItemRect.AdjustRight( -(nFontHeight + nFontHeight / 4) );
+ }
+
+ if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
+ {
+ Size aPxSize(GetOutputSizePixel());
+ rRenderContext.Push(vcl::PushFlags::CLIPREGION);
+ rRenderContext.IntersectClipRegion(tools::Rectangle(Point(nX, nY), Size(aSz.Width(), pData->aSz.Height())));
+ tools::Rectangle aCtrlRect(Point(nX, 0), Size(aPxSize.Width()-nX, aPxSize.Height()));
+ MenupopupValue aVal(pMenu->nTextPos-GUTTERBORDER, aItemRect);
+ rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Entire,
+ aCtrlRect, ControlState::ENABLED, aVal, OUString());
+ if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItem))
+ {
+ bDrawItemRect = false;
+ if (!rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::MenuItem, aItemRect,
+ ControlState::SELECTED | (pData->bEnabled
+ ? ControlState::ENABLED
+ : ControlState::NONE),
+ aVal, OUString()))
+ {
+ bDrawItemRect = true;
+ }
+ }
+ else
+ bDrawItemRect = true;
+ rRenderContext.Pop();
+ }
+ if (bDrawItemRect)
+ {
+ if (pData->bEnabled)
+ rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
+ else
+ {
+ rRenderContext.SetFillColor();
+ oldLineColor = rRenderContext.GetLineColor();
+ rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
+ bRestoreLineColor = true;
+ }
+
+ rRenderContext.DrawRect(aItemRect);
+ }
+ pMenu->ImplPaint(rRenderContext, GetOutputSizePixel(), nScrollerHeight, nStartY, pData, true/*bHighlight*/);
+ if (bRestoreLineColor)
+ rRenderContext.SetLineColor(oldLineColor);
+ }
+ return;
+ }
+
+ nY += pData->aSz.Height();
+ }
+}
+
+tools::Rectangle MenuFloatingWindow::ImplGetItemRect( sal_uInt16 nPos ) const
+{
+ if( ! pMenu )
+ return tools::Rectangle();
+
+ tools::Rectangle aRect;
+ Size aSz = GetOutputSizePixel();
+ tools::Long nStartY = ImplGetStartY();
+ tools::Long nY = nScrollerHeight+nStartY;
+
+ size_t nCount = pMenu->pItemList->size();
+ for ( size_t n = 0; n < nCount; n++ )
+ {
+ MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
+ if ( n == nPos )
+ {
+ SAL_WARN_IF( !pMenu->ImplIsVisible( n ), "vcl", "ImplGetItemRect: Item not visible!" );
+ if ( pData->eType != MenuItemType::SEPARATOR )
+ {
+ aRect = tools::Rectangle( Point( 0, nY ), Size( aSz.Width(), pData->aSz.Height() ) );
+ if ( pData->nBits & MenuItemBits::POPUPSELECT )
+ {
+ tools::Long nFontHeight = GetTextHeight();
+ aRect.AdjustRight( -(nFontHeight + nFontHeight/4) );
+ }
+ }
+ break;
+ }
+ nY += pData->aSz.Height();
+ }
+ return aRect;
+}
+
+void MenuFloatingWindow::ImplCursorUpDown( bool bUp, bool bHomeEnd )
+{
+ if( ! pMenu )
+ return;
+
+ const StyleSettings& rSettings = GetSettings().GetStyleSettings();
+
+ sal_uInt16 n = nHighlightedItem;
+ if ( n == ITEMPOS_INVALID )
+ {
+ if ( bUp )
+ n = 0;
+ else
+ n = pMenu->GetItemCount()-1;
+ }
+
+ sal_uInt16 nLoop = n;
+
+ if( bHomeEnd )
+ {
+ // absolute positioning
+ if( bUp )
+ {
+ n = pMenu->GetItemCount();
+ nLoop = n-1;
+ }
+ else
+ {
+ n = sal_uInt16(-1);
+ nLoop = n+1;
+ }
+ }
+
+ do
+ {
+ if ( bUp )
+ {
+ if ( n )
+ n--;
+ else
+ if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) )
+ n = pMenu->GetItemCount()-1;
+ else
+ break;
+ }
+ else
+ {
+ n++;
+ if ( n >= pMenu->GetItemCount() )
+ {
+ if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) )
+ n = 0;
+ else
+ break;
+ }
+ }
+
+ MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( n );
+ if ( ( pData->bEnabled || !rSettings.GetSkipDisabledInMenus() )
+ && ( pData->eType != MenuItemType::SEPARATOR ) && pMenu->ImplIsVisible( n ) && pMenu->ImplIsSelectable( n ) )
+ {
+ // Is selection in visible area?
+ if ( IsScrollMenu() )
+ {
+ ChangeHighlightItem( ITEMPOS_INVALID, false );
+
+ while ( n < nFirstEntry )
+ ImplScroll( true );
+
+ Size aOutSz = GetOutputSizePixel();
+ sal_uInt16 nLastVisible;
+ static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible );
+ while ( n > nLastVisible )
+ {
+ ImplScroll( false );
+ static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible );
+ }
+ }
+ ChangeHighlightItem( n, false );
+ break;
+ }
+ } while ( n != nLoop );
+}
+
+void MenuFloatingWindow::KeyInput( const KeyEvent& rKEvent )
+{
+ VclPtr<vcl::Window> xWindow = this;
+
+ sal_uInt16 nCode = rKEvent.GetKeyCode().GetCode();
+ bKeyInput = true;
+ switch ( nCode )
+ {
+ case KEY_UP:
+ case KEY_DOWN:
+ {
+ ImplCursorUpDown( nCode == KEY_UP );
+ }
+ break;
+ case KEY_END:
+ case KEY_HOME:
+ {
+ ImplCursorUpDown( nCode == KEY_END, true );
+ }
+ break;
+ case KEY_F6:
+ case KEY_ESCAPE:
+ {
+ // Ctrl-F6 acts like ESC here, the menu bar however will then put the focus in the document
+ if( nCode == KEY_F6 && !rKEvent.GetKeyCode().IsMod1() )
+ break;
+ if( pMenu )
+ {
+ if ( !pMenu->pStartedFrom )
+ {
+ StopExecute();
+ KillActivePopup();
+ }
+ else if (pMenu->pStartedFrom->IsMenuBar())
+ {
+ pMenu->pStartedFrom->MenuBarKeyInput(rKEvent);
+ }
+ else
+ {
+ StopExecute();
+ PopupMenu* pPopupMenu = static_cast<PopupMenu*>(pMenu->pStartedFrom.get());
+ MenuFloatingWindow* pFloat = pPopupMenu->ImplGetFloatingWindow();
+ pFloat->GrabFocus();
+ pFloat->KillActivePopup();
+ pPopupMenu->ImplCallHighlight(pFloat->nHighlightedItem);
+ }
+ }
+ }
+ break;
+ case KEY_LEFT:
+ {
+ if ( pMenu && pMenu->pStartedFrom )
+ {
+ StopExecute();
+ if (pMenu->pStartedFrom->IsMenuBar())
+ {
+ pMenu->pStartedFrom->MenuBarKeyInput(rKEvent);
+ }
+ else
+ {
+ MenuFloatingWindow* pFloat = static_cast<PopupMenu*>(pMenu->pStartedFrom.get())->ImplGetFloatingWindow();
+ pFloat->GrabFocus();
+ pFloat->KillActivePopup();
+ sal_uInt16 highlightItem = pFloat->GetHighlightedItem();
+ pFloat->ChangeHighlightItem(highlightItem, false);
+ }
+ }
+ }
+ break;
+ case KEY_RIGHT:
+ {
+ if( pMenu )
+ {
+ bool bDone = false;
+ if ( nHighlightedItem != ITEMPOS_INVALID )
+ {
+ MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
+ if ( pData && pData->pSubMenu )
+ {
+ HighlightChanged( nullptr );
+ bDone = true;
+ }
+ }
+ if ( !bDone )
+ {
+ Menu* pStart = pMenu->ImplGetStartMenu();
+ if (pStart && pStart->IsMenuBar())
+ {
+ // Forward...
+ pStart->ImplGetWindow()->KeyInput( rKEvent );
+ }
+ }
+ }
+ }
+ break;
+ case KEY_RETURN:
+ {
+ if( pMenu )
+ {
+ MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
+ if ( pData && pData->bEnabled )
+ {
+ if ( pData->pSubMenu )
+ HighlightChanged( nullptr );
+ else
+ EndExecute();
+ }
+ else
+ StopExecute();
+ }
+ }
+ break;
+ case KEY_MENU:
+ {
+ if( pMenu )
+ {
+ Menu* pStart = pMenu->ImplGetStartMenu();
+ if (pStart && pStart->IsMenuBar())
+ {
+ // Forward...
+ pStart->ImplGetWindow()->KeyInput( rKEvent );
+ }
+ }
+ }
+ break;
+ default:
+ {
+ sal_Unicode nCharCode = rKEvent.GetCharCode();
+ size_t nPos = 0;
+ size_t nDuplicates = 0;
+ MenuItemData* pData = (nCharCode && pMenu) ?
+ pMenu->GetItemList()->SearchItem(nCharCode, rKEvent.GetKeyCode(), nPos, nDuplicates, nHighlightedItem) : nullptr;
+ if (pData)
+ {
+ if ( pData->pSubMenu || nDuplicates > 1 )
+ {
+ ChangeHighlightItem( nPos, false );
+ HighlightChanged( nullptr );
+ }
+ else
+ {
+ nHighlightedItem = nPos;
+ EndExecute();
+ }
+ }
+ else
+ FloatingWindow::KeyInput( rKEvent );
+ }
+ }
+
+ // #105474# check if menu window was not destroyed
+ if ( !xWindow->isDisposed() )
+ {
+ bKeyInput = false;
+ }
+}
+
+void MenuFloatingWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rPaintRect)
+{
+ if (!pMenu)
+ return;
+
+ // Set the clip before the buffering starts: rPaintRect may be larger than the current clip,
+ // this way the buffer -> render context copy happens with this clip.
+ rRenderContext.Push(vcl::PushFlags::CLIPREGION);
+ rRenderContext.SetClipRegion(vcl::Region(rPaintRect));
+
+ // Make sure that all actual rendering happens in one go to avoid flicker.
+ vcl::BufferDevice pBuffer(this, rRenderContext);
+
+ if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
+ {
+ pBuffer->SetClipRegion();
+ tools::Long nX = 0;
+ Size aPxSize(GetOutputSizePixel());
+ aPxSize.AdjustWidth( -nX );
+ ImplControlValue aVal(pMenu->nTextPos - GUTTERBORDER);
+ pBuffer->DrawNativeControl(ControlType::MenuPopup, ControlPart::Entire,
+ tools::Rectangle(Point(nX, 0), aPxSize), ControlState::ENABLED,
+ aVal, OUString());
+ InitMenuClipRegion(*pBuffer);
+ }
+ if (IsScrollMenu())
+ {
+ ImplDrawScroller(*pBuffer, true);
+ ImplDrawScroller(*pBuffer, false);
+ }
+ pBuffer->SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuColor());
+ pMenu->ImplPaint(*pBuffer, GetOutputSizePixel(), nScrollerHeight, ImplGetStartY());
+ if (nHighlightedItem != ITEMPOS_INVALID)
+ RenderHighlightItem(*pBuffer, nHighlightedItem);
+
+ pBuffer.Dispose();
+ rRenderContext.Pop();
+}
+
+void MenuFloatingWindow::ImplDrawScroller(vcl::RenderContext& rRenderContext, bool bUp)
+{
+ if (!pMenu)
+ return;
+
+ rRenderContext.SetClipRegion();
+
+ Size aOutSz(GetOutputSizePixel());
+ tools::Long nY = bUp ? 0 : (aOutSz.Height() - nScrollerHeight);
+ tools::Long nX = 0;
+ tools::Rectangle aRect(Point(nX, nY), Size(aOutSz.Width() - nX, nScrollerHeight));
+
+ DecorationView aDecoView(&rRenderContext);
+ SymbolType eSymbol = bUp ? SymbolType::SPIN_UP : SymbolType::SPIN_DOWN;
+
+ DrawSymbolFlags nStyle = DrawSymbolFlags::NONE;
+ if ((bUp && !bScrollUp) || (!bUp && !bScrollDown))
+ nStyle |= DrawSymbolFlags::Disable;
+
+ aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle);
+
+ InitMenuClipRegion(rRenderContext);
+}
+
+void MenuFloatingWindow::RequestHelp( const HelpEvent& rHEvt )
+{
+ sal_uInt16 nId = nHighlightedItem;
+ Menu* pM = pMenu;
+ vcl::Window* pW = this;
+
+ // #102618# Get item rect before destroying the window in EndExecute() call
+ tools::Rectangle aHighlightRect( ImplGetItemRect( nHighlightedItem ) );
+
+ if ( rHEvt.GetMode() & HelpEventMode::CONTEXT )
+ {
+ nHighlightedItem = ITEMPOS_INVALID;
+ EndExecute();
+ pW = nullptr;
+ }
+
+ if( !ImplHandleHelpEvent( pW, pM, nId, rHEvt, aHighlightRect ) )
+ Window::RequestHelp( rHEvt );
+}
+
+void MenuFloatingWindow::StateChanged( StateChangedType nType )
+{
+ FloatingWindow::StateChanged( nType );
+
+ if ( ( nType == StateChangedType::ControlForeground ) || ( nType == StateChangedType::ControlBackground ) )
+ {
+ ApplySettings(*GetOutDev());
+ Invalidate();
+ }
+}
+
+void MenuFloatingWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ FloatingWindow::DataChanged( rDCEvt );
+
+ if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
+ ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
+ {
+ ApplySettings(*GetOutDev());
+ Invalidate();
+ }
+}
+
+void MenuFloatingWindow::Command( const CommandEvent& rCEvt )
+{
+ if ( rCEvt.GetCommand() == CommandEventId::Wheel )
+ {
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
+ {
+ ImplScroll( pData->GetDelta() > 0 );
+ MouseMove( MouseEvent( GetPointerPosPixel(), 0 ) );
+ }
+ }
+}
+
+css::uno::Reference<css::accessibility::XAccessible> MenuFloatingWindow::CreateAccessible()
+{
+ css::uno::Reference<css::accessibility::XAccessible> xAcc;
+
+ if (pMenu && !pMenu->pStartedFrom)
+ xAcc = pMenu->GetAccessible();
+
+ return xAcc;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/menufloatingwindow.hxx b/vcl/source/window/menufloatingwindow.hxx
new file mode 100644
index 0000000000..f26fb50373
--- /dev/null
+++ b/vcl/source/window/menufloatingwindow.hxx
@@ -0,0 +1,128 @@
+/* -*- 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 "menuwindow.hxx"
+
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/menu.hxx>
+
+#define EXTRASPACEY 2
+#define GUTTERBORDER 8
+
+/** Class that implements the actual window of the floating menu.
+*/
+class MenuFloatingWindow : public FloatingWindow, public MenuWindow
+{
+ friend void Menu::ImplFillLayoutData() const;
+ friend void Menu::dispose();
+
+private:
+ VclPtr<Menu> pMenu;
+ VclPtr<PopupMenu> pActivePopup;
+ Timer aHighlightChangedTimer;
+ Timer aSubmenuCloseTimer;
+ Timer aScrollTimer;
+ VclPtr<vcl::Window> xSaveFocusId;
+ sal_uInt16 nHighlightedItem; // highlighted/selected Item
+ sal_uInt16 nMBDownPos;
+ sal_uInt16 nScrollerHeight;
+ sal_uInt16 nFirstEntry;
+ sal_uInt16 nPosInParent;
+
+ bool bInExecute : 1;
+ bool bScrollMenu : 1;
+ bool bScrollUp : 1;
+ bool bScrollDown : 1;
+ bool bIgnoreFirstMove : 1;
+ bool bKeyInput : 1;
+
+ DECL_LINK( PopupEnd, FloatingWindow*, void );
+ DECL_LINK( HighlightChanged, Timer*, void );
+ DECL_LINK( SubmenuClose, Timer *, void );
+ DECL_LINK( AutoScroll, Timer *, void );
+ DECL_LINK( ShowHideListener, VclWindowEvent&, void );
+
+ virtual void StateChanged( StateChangedType nType ) override;
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+
+ void InitMenuClipRegion(vcl::RenderContext& rRenderContext);
+
+ void Start();
+ void End();
+
+protected:
+ vcl::Region ImplCalcClipRegion() const;
+ void ImplDrawScroller(vcl::RenderContext& rRenderContext, bool bUp);
+ using Window::ImplScroll;
+ void ImplScroll( const Point& rMousePos );
+ void ImplScroll( bool bUp );
+ void ImplCursorUpDown( bool bUp, bool bHomeEnd = false );
+ void ImplHighlightItem( const MouseEvent& rMEvt, bool bMBDown );
+ tools::Long ImplGetStartY() const;
+ tools::Rectangle ImplGetItemRect( sal_uInt16 nPos ) const;
+ void RenderHighlightItem( vcl::RenderContext& rRenderContext, sal_uInt16 nPos );
+ tools::Long GetInitialItemY( tools::Long *pOptStartY = nullptr ) const;
+ void InvalidateItem( sal_uInt16 nPos );
+
+public:
+ MenuFloatingWindow(Menu* pMenu, vcl::Window* pParent, WinBits nStyle);
+ virtual ~MenuFloatingWindow() override;
+
+ virtual void dispose() override;
+ void doShutdown();
+
+ virtual void MouseMove(const MouseEvent& rMEvt) override;
+ virtual void MouseButtonDown(const MouseEvent& rMEvt) override;
+ virtual void MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual void KeyInput(const KeyEvent& rKEvent) override;
+ virtual void Command(const CommandEvent& rCEvt) override;
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+ virtual void RequestHelp( const HelpEvent& rHEvt ) override;
+ virtual void Resize() override;
+
+ virtual void ApplySettings(vcl::RenderContext& rRenderContext) override;
+
+ void SetFocusId( const VclPtr<vcl::Window>& xId ) { xSaveFocusId = xId; }
+ const VclPtr<vcl::Window>& GetFocusId() const { return xSaveFocusId; }
+
+ void EnableScrollMenu( bool b );
+ bool IsScrollMenu() const { return bScrollMenu; }
+ sal_uInt16 GetScrollerHeight() const { return nScrollerHeight; }
+
+ void Execute();
+ void StopExecute();
+ void EndExecute();
+ void EndExecute( sal_uInt16 nSelectId );
+
+ PopupMenu* GetActivePopup() const { return pActivePopup; }
+ void KillActivePopup( PopupMenu* pThisOnly = nullptr );
+
+ void ChangeHighlightItem(sal_uInt16 n, bool bStartPopupTimer);
+ sal_uInt16 GetHighlightedItem() const { return nHighlightedItem; }
+
+ void SetPosInParent( sal_uInt16 nPos ) { nPosInParent = nPos; }
+
+ bool MenuInHierarchyHasFocus() const;
+
+ virtual css::uno::Reference<css::accessibility::XAccessible> CreateAccessible() override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/menuitemlist.cxx b/vcl/source/window/menuitemlist.cxx
new file mode 100644
index 0000000000..d6849e3e71
--- /dev/null
+++ b/vcl/source/window/menuitemlist.cxx
@@ -0,0 +1,316 @@
+/* -*- 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 "menuitemlist.hxx"
+
+#include <salframe.hxx>
+#include <salinst.hxx>
+#include <salmenu.hxx>
+#include <svdata.hxx>
+
+#include <vcl/i18nhelp.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/vcllayout.hxx>
+#include <vcl/window.hxx>
+
+using namespace css;
+using namespace vcl;
+
+MenuItemData::~MenuItemData()
+{
+ if (aUserValueReleaseFunc)
+ aUserValueReleaseFunc(nUserValue);
+ pSalMenuItem.reset();
+ pSubMenu.disposeAndClear();
+}
+
+SalLayoutGlyphs* MenuItemData::GetTextGlyphs(const OutputDevice* pOutputDevice)
+{
+ if (aTextGlyphs.IsValid())
+ // Use pre-calculated result.
+ return &aTextGlyphs;
+
+ OUString aNonMnemonicString = removeMnemonicFromString(aText);
+ std::unique_ptr<SalLayout> pLayout
+ = pOutputDevice->ImplLayout(aNonMnemonicString, 0, aNonMnemonicString.getLength(),
+ Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly);
+ if (!pLayout)
+ return nullptr;
+
+ // Remember the calculation result.
+ aTextGlyphs = pLayout->GetGlyphs();
+
+ return &aTextGlyphs;
+}
+
+MenuItemList::~MenuItemList()
+{
+}
+
+MenuItemData* MenuItemList::Insert(
+ sal_uInt16 nId,
+ MenuItemType eType,
+ MenuItemBits nBits,
+ const OUString& rStr,
+ Menu* pMenu,
+ size_t nPos,
+ const OUString &rIdent
+)
+{
+ MenuItemData* pData = new MenuItemData( rStr );
+ pData->nId = nId;
+ pData->sIdent = rIdent;
+ pData->eType = eType;
+ pData->nBits = nBits;
+ pData->pSubMenu = nullptr;
+ pData->nUserValue = nullptr;
+ pData->bChecked = false;
+ pData->bEnabled = true;
+ pData->bVisible = true;
+ pData->bIsTemporary = false;
+
+ SalItemParams aSalMIData;
+ aSalMIData.nId = nId;
+ aSalMIData.eType = eType;
+ aSalMIData.nBits = nBits;
+ aSalMIData.pMenu = pMenu;
+ aSalMIData.aText = rStr;
+
+ // Native-support: returns NULL if not supported
+ pData->pSalMenuItem = ImplGetSVData()->mpDefInst->CreateMenuItem( aSalMIData );
+
+ if( nPos < maItemList.size() ) {
+ maItemList.insert( maItemList.begin() + nPos, std::unique_ptr<MenuItemData>(pData) );
+ } else {
+ maItemList.emplace_back( pData );
+ }
+ return pData;
+}
+
+void MenuItemList::InsertSeparator(const OUString &rIdent, size_t nPos)
+{
+ MenuItemData* pData = new MenuItemData;
+ pData->nId = 0;
+ pData->sIdent = rIdent;
+ pData->eType = MenuItemType::SEPARATOR;
+ pData->nBits = MenuItemBits::NONE;
+ pData->pSubMenu = nullptr;
+ pData->nUserValue = nullptr;
+ pData->bChecked = false;
+ pData->bEnabled = true;
+ pData->bVisible = true;
+ pData->bIsTemporary = false;
+
+ SalItemParams aSalMIData;
+ aSalMIData.nId = 0;
+ aSalMIData.eType = MenuItemType::SEPARATOR;
+ aSalMIData.nBits = MenuItemBits::NONE;
+ aSalMIData.pMenu = nullptr;
+ aSalMIData.aText.clear();
+ aSalMIData.aImage = Image();
+
+ // Native-support: returns NULL if not supported
+ pData->pSalMenuItem = ImplGetSVData()->mpDefInst->CreateMenuItem( aSalMIData );
+
+ if( nPos < maItemList.size() ) {
+ maItemList.insert( maItemList.begin() + nPos, std::unique_ptr<MenuItemData>(pData) );
+ } else {
+ maItemList.emplace_back( pData );
+ }
+}
+
+void MenuItemList::Remove( size_t nPos )
+{
+ if( nPos < maItemList.size() )
+ {
+ maItemList.erase( maItemList.begin() + nPos );
+ }
+}
+
+void MenuItemList::Clear()
+{
+ maItemList.clear();
+}
+
+MenuItemData* MenuItemList::GetData( sal_uInt16 nSVId, size_t& rPos ) const
+{
+ for( size_t i = 0, n = maItemList.size(); i < n; ++i )
+ {
+ if ( maItemList[ i ]->nId == nSVId )
+ {
+ rPos = i;
+ return maItemList[ i ].get();
+ }
+ }
+ return nullptr;
+}
+
+MenuItemData* MenuItemList::SearchItem(
+ sal_Unicode cSelectChar,
+ KeyCode aKeyCode,
+ size_t& rPos,
+ size_t& nDuplicates,
+ size_t nCurrentPos
+) const
+{
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
+
+ size_t nListCount = maItemList.size();
+
+ // try character code first
+ nDuplicates = GetItemCount( cSelectChar ); // return number of duplicates
+ if( nDuplicates )
+ {
+ MenuItemData* pFirstMatch = nullptr;
+ size_t nFirstPos(0);
+ for ( rPos = 0; rPos < nListCount; rPos++)
+ {
+ MenuItemData* pData = maItemList[ rPos ].get();
+ if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) )
+ {
+ if (nDuplicates == 1)
+ return pData;
+ if (rPos > nCurrentPos)
+ return pData; // select next entry with the same mnemonic
+ if (!pFirstMatch) // stash the first match for use if nothing follows nCurrentPos
+ {
+ pFirstMatch = pData;
+ nFirstPos = rPos;
+ }
+ }
+ }
+ if (pFirstMatch)
+ {
+ rPos = nFirstPos;
+ return pFirstMatch;
+ }
+ }
+
+ // nothing found, try keycode instead
+ nDuplicates = GetItemCount( aKeyCode ); // return number of duplicates
+
+ if( nDuplicates )
+ {
+ char ascii = 0;
+ if( aKeyCode.GetCode() >= KEY_A && aKeyCode.GetCode() <= KEY_Z )
+ ascii = sal::static_int_cast<char>('A' + (aKeyCode.GetCode() - KEY_A));
+
+ MenuItemData* pFirstMatch = nullptr;
+ size_t nFirstPos(0);
+ for ( rPos = 0; rPos < nListCount; rPos++)
+ {
+ MenuItemData* pData = maItemList[ rPos ].get();
+ if ( pData->bEnabled )
+ {
+ sal_Int32 n = pData->aText.indexOf('~');
+ if ( n != -1 )
+ {
+ KeyCode nKeyCode;
+ sal_Unicode nUnicode = pData->aText[n+1];
+ vcl::Window* pDefWindow = ImplGetDefaultWindow();
+ if( ( pDefWindow
+ && pDefWindow->ImplGetFrame()->MapUnicodeToKeyCode( nUnicode,
+ Application::GetSettings().GetUILanguageTag().getLanguageType(), nKeyCode )
+ && aKeyCode.GetCode() == nKeyCode.GetCode()
+ )
+ || ( ascii
+ && rI18nHelper.MatchMnemonic( pData->aText, ascii )
+ )
+ )
+ {
+ if (nDuplicates == 1)
+ return pData;
+ if (rPos > nCurrentPos)
+ return pData; // select next entry with the same mnemonic
+ if (!pFirstMatch) // stash the first match for use if nothing follows nCurrentPos
+ {
+ pFirstMatch = pData;
+ nFirstPos = rPos;
+ }
+ }
+ }
+ }
+ }
+ if (pFirstMatch)
+ {
+ rPos = nFirstPos;
+ return pFirstMatch;
+ }
+ }
+
+ return nullptr;
+}
+
+size_t MenuItemList::GetItemCount( sal_Unicode cSelectChar ) const
+{
+ // returns number of entries with same mnemonic
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
+
+ size_t nItems = 0;
+ for ( size_t nPos = maItemList.size(); nPos; )
+ {
+ MenuItemData* pData = maItemList[ --nPos ].get();
+ if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) )
+ nItems++;
+ }
+
+ return nItems;
+}
+
+size_t MenuItemList::GetItemCount( KeyCode aKeyCode ) const
+{
+ // returns number of entries with same mnemonic
+ // uses key codes instead of character codes
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
+ char ascii = 0;
+ if( aKeyCode.GetCode() >= KEY_A && aKeyCode.GetCode() <= KEY_Z )
+ ascii = sal::static_int_cast<char>('A' + (aKeyCode.GetCode() - KEY_A));
+
+ size_t nItems = 0;
+ for ( size_t nPos = maItemList.size(); nPos; )
+ {
+ MenuItemData* pData = maItemList[ --nPos ].get();
+ if ( pData->bEnabled )
+ {
+ sal_Int32 n = pData->aText.indexOf('~');
+ if (n != -1)
+ {
+ KeyCode nKeyCode;
+ // if MapUnicodeToKeyCode fails or is unsupported we try the pure ascii mapping of the keycodes
+ // so we have working shortcuts when ascii mnemonics are used
+ vcl::Window* pDefWindow = ImplGetDefaultWindow();
+ if( ( pDefWindow
+ && pDefWindow->ImplGetFrame()->MapUnicodeToKeyCode( pData->aText[n+1],
+ Application::GetSettings().GetUILanguageTag().getLanguageType(), nKeyCode )
+ && aKeyCode.GetCode() == nKeyCode.GetCode()
+ )
+ || ( ascii
+ && rI18nHelper.MatchMnemonic( pData->aText, ascii )
+ )
+ )
+ nItems++;
+ }
+ }
+ }
+
+ return nItems;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/menuitemlist.hxx b/vcl/source/window/menuitemlist.hxx
new file mode 100644
index 0000000000..fc25a40292
--- /dev/null
+++ b/vcl/source/window/menuitemlist.hxx
@@ -0,0 +1,151 @@
+/* -*- 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 <utility>
+#include <vcl/vclenum.hxx>
+#include <vcl/glyphitem.hxx>
+#include <vcl/image.hxx>
+#include <vcl/keycod.hxx>
+#include <vcl/menu.hxx>
+#include <salmenu.hxx>
+
+#include <memory>
+#include <vector>
+
+class SalMenuItem;
+
+struct MenuItemData
+{
+ sal_uInt16 nId; // SV Id
+ MenuItemType eType; // MenuItem-Type
+ MenuItemBits nBits; // MenuItem-Bits
+ VclPtr<PopupMenu> pSubMenu; // Pointer to SubMenu
+ OUString aText; // Menu-Text
+ SalLayoutGlyphs aTextGlyphs; ///< Text layout of aText.
+ OUString aHelpText; // Help-String
+ OUString aTipHelpText; // TipHelp-String (eg, expanded filenames)
+ OUString aCommandStr; // CommandString
+ OUString aHelpCommandStr; // Help command string (to reference external help)
+ OUString sIdent;
+ OUString aHelpId; // Help-Id
+ void* nUserValue; // User value
+ MenuUserDataReleaseFunction aUserValueReleaseFunc; // called when MenuItemData is destroyed
+ Image aImage; // Image
+ vcl::KeyCode aAccelKey; // Accelerator-Key
+ bool bChecked; // Checked
+ bool bEnabled; // Enabled
+ bool bVisible; // Visible (note: this flag will not override MenuFlags::HideDisabledEntries when true)
+ bool bIsTemporary; // Temporary inserted ('No selection possible')
+ bool bHiddenOnGUI;
+ Size aSz; // only temporarily valid
+ OUString aAccessibleName; // accessible name
+ OUString aAccessibleDescription; // accessible description
+
+ std::unique_ptr<SalMenuItem> pSalMenuItem; // access to native menu
+
+ MenuItemData()
+ : nId(0)
+ , eType(MenuItemType::DONTKNOW)
+ , nBits(MenuItemBits::NONE)
+ , pSubMenu(nullptr)
+ , nUserValue(nullptr)
+ , aUserValueReleaseFunc(nullptr)
+ , bChecked(false)
+ , bEnabled(false)
+ , bVisible(false)
+ , bIsTemporary(false)
+ , bHiddenOnGUI(false)
+ {
+ }
+ MenuItemData( OUString aStr )
+ : nId(0)
+ , eType(MenuItemType::DONTKNOW)
+ , nBits(MenuItemBits::NONE)
+ , pSubMenu(nullptr)
+ , aText(std::move(aStr))
+ , nUserValue(nullptr)
+ , aUserValueReleaseFunc(nullptr)
+ , aImage()
+ , bChecked(false)
+ , bEnabled(false)
+ , bVisible(false)
+ , bIsTemporary(false)
+ , bHiddenOnGUI(false)
+ {
+ }
+ ~MenuItemData();
+
+ /// Computes aText's text layout (glyphs), cached in aTextGlyphs.
+ SalLayoutGlyphs* GetTextGlyphs(const OutputDevice* pOutputDevice);
+
+ bool HasCheck() const
+ {
+ return bChecked || ( nBits & ( MenuItemBits::RADIOCHECK | MenuItemBits::CHECKABLE | MenuItemBits::AUTOCHECK ) );
+ }
+};
+
+class MenuItemList
+{
+private:
+ ::std::vector< std::unique_ptr<MenuItemData> > maItemList;
+
+public:
+ MenuItemList() {}
+ ~MenuItemList();
+
+ MenuItemData* Insert(
+ sal_uInt16 nId,
+ MenuItemType eType,
+ MenuItemBits nBits,
+ const OUString& rStr,
+ Menu* pMenu,
+ size_t nPos,
+ const OUString &rIdent
+ );
+ void InsertSeparator(const OUString &rIdent, size_t nPos);
+ void Remove( size_t nPos );
+ void Clear();
+
+ MenuItemData* GetData( sal_uInt16 nSVId, size_t& rPos ) const;
+ MenuItemData* GetData( sal_uInt16 nSVId ) const
+ {
+ size_t nTemp;
+ return GetData( nSVId, nTemp );
+ }
+ MenuItemData* GetDataFromPos( size_t nPos ) const
+ {
+ return ( nPos < maItemList.size() ) ? maItemList[ nPos ].get() : nullptr;
+ }
+
+ MenuItemData* SearchItem(
+ sal_Unicode cSelectChar,
+ vcl::KeyCode aKeyCode,
+ size_t& rPos,
+ size_t& nDuplicates,
+ size_t nCurrentPos
+ ) const;
+ size_t GetItemCount( sal_Unicode cSelectChar ) const;
+ size_t GetItemCount( vcl::KeyCode aKeyCode ) const;
+ size_t size() const
+ {
+ return maItemList.size();
+ }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/menuwindow.cxx b/vcl/source/window/menuwindow.cxx
new file mode 100644
index 0000000000..802c62e285
--- /dev/null
+++ b/vcl/source/window/menuwindow.cxx
@@ -0,0 +1,118 @@
+/* -*- 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 "menuwindow.hxx"
+#include "menuitemlist.hxx"
+
+#include <vcl/help.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+
+static sal_uLong ImplChangeTipTimeout( sal_uLong nTimeout, vcl::Window *pWindow )
+{
+ AllSettings aAllSettings( pWindow->GetSettings() );
+ HelpSettings aHelpSettings( aAllSettings.GetHelpSettings() );
+ sal_uLong nRet = aHelpSettings.GetTipTimeout();
+ aHelpSettings.SetTipTimeout( nTimeout );
+ aAllSettings.SetHelpSettings( aHelpSettings );
+ pWindow->GetOutDev()->SetSettings( aAllSettings );
+ return nRet;
+}
+
+bool MenuWindow::ImplHandleHelpEvent(vcl::Window* pMenuWindow, Menu const * pMenu, sal_uInt16 nHighlightedItem,
+ const HelpEvent& rHEvt, const tools::Rectangle &rHighlightRect)
+{
+ if( ! pMenu )
+ return false;
+
+ bool bDone = false;
+ sal_uInt16 nId = 0;
+
+ if ( nHighlightedItem != ITEMPOS_INVALID )
+ {
+ MenuItemData* pItemData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
+ if ( pItemData )
+ nId = pItemData->nId;
+ }
+
+ if ( ( rHEvt.GetMode() & HelpEventMode::BALLOON ) && pMenuWindow )
+ {
+ Point aPos;
+ if( rHEvt.KeyboardActivated() )
+ aPos = rHighlightRect.Center();
+ else
+ aPos = rHEvt.GetMousePosPixel();
+
+ tools::Rectangle aRect( aPos, Size() );
+ if (!pMenu->GetHelpText(nId).isEmpty())
+ Help::ShowBalloon( pMenuWindow, aPos, aRect, pMenu->GetHelpText( nId ) );
+ else
+ {
+ // give user a chance to read the full filename
+ sal_uLong oldTimeout=ImplChangeTipTimeout( 60000, pMenuWindow );
+ // call always, even when strlen==0 to correctly remove tip
+ Help::ShowQuickHelp( pMenuWindow, aRect, pMenu->GetTipHelpText( nId ) );
+ ImplChangeTipTimeout( oldTimeout, pMenuWindow );
+ }
+ bDone = true;
+ }
+ else if ( ( rHEvt.GetMode() &HelpEventMode::QUICK ) && pMenuWindow )
+ {
+ Point aPos = rHEvt.GetMousePosPixel();
+ tools::Rectangle aRect( aPos, Size() );
+ // give user a chance to read the full filename
+ sal_uLong oldTimeout=ImplChangeTipTimeout( 60000, pMenuWindow );
+ // call always, even when strlen==0 to correctly remove tip
+ Help::ShowQuickHelp( pMenuWindow, aRect, pMenu->GetTipHelpText( nId ) );
+ ImplChangeTipTimeout( oldTimeout, pMenuWindow );
+ bDone = true;
+ }
+ else if ( rHEvt.GetMode() & HelpEventMode::CONTEXT )
+ {
+ // is help in the application selected
+ Help* pHelp = Application::GetHelp();
+ if ( pHelp )
+ {
+ // Check if there is a Help ID available, or else use
+ // the command URL
+ OUString aCommand = pMenu->GetItemCommand( nId );
+ OUString aHelpId;
+
+ // If no entry is selected, use the general menu Help ID
+ if (nId <= 0)
+ aHelpId = pMenu->GetHelpId();
+ else
+ aHelpId = pMenu->GetHelpId(nId);
+
+ if( aHelpId.isEmpty() )
+ aHelpId = OOO_HELP_INDEX;
+
+ if ( !aHelpId.isEmpty() )
+ pHelp->Start(aHelpId);
+ else
+ pHelp->Start(aCommand);
+ }
+ bDone = true;
+ }
+ return bDone;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/menuwindow.hxx b/vcl/source/window/menuwindow.hxx
new file mode 100644
index 0000000000..df0606b888
--- /dev/null
+++ b/vcl/source/window/menuwindow.hxx
@@ -0,0 +1,56 @@
+/* -*- 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 <vcl/event.hxx>
+
+class HelpEvent;
+class Image;
+class Menu;
+class MenuBar;
+namespace tools { class Rectangle; }
+namespace vcl { class Window; }
+
+/** Common ancestor for MenuFloatingWindow and MenuBarWindow.
+
+The menu can be a floating window, or a menu bar. Even though this has
+'Window' in the name, it is not derived from the VCL's Window class, as the
+MenuFloatingWindow's or MenuBarWindow's already are VCL Windows.
+
+TODO: move here stuff that was a mentioned previously when there was no
+common class for MenuFloatingWindow and MenuBarWindow:
+
+// a basic class for both (due to pActivePopup, Timer,...) would be nice,
+// but a container class should have been created then, as they
+// would be derived from different windows
+// In most functions we would have to create exceptions for
+// menubar, popupmenu, hence we made two classes
+
+*/
+class MenuWindow
+{
+protected:
+ /// Show the appropriate help tooltip.
+ static bool ImplHandleHelpEvent(vcl::Window* pMenuWindow, Menu const * pMenu, sal_uInt16 nHighlightedItem,
+ const HelpEvent& rHEvt, const tools::Rectangle &rHighlightRect);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/mnemonic.cxx b/vcl/source/window/mnemonic.cxx
new file mode 100644
index 0000000000..e4f4cf8cf7
--- /dev/null
+++ b/vcl/source/window/mnemonic.cxx
@@ -0,0 +1,351 @@
+/* -*- 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 <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/mnemonic.hxx>
+
+#include <vcl/unohelp.hxx>
+#include <com/sun/star/i18n/XCharacterClassification.hpp>
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <rtl/character.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+
+MnemonicGenerator::MnemonicGenerator(sal_Unicode cMnemonic)
+ : m_cMnemonic(cMnemonic)
+{
+ memset( maMnemonics, 1, sizeof( maMnemonics ) );
+}
+
+MnemonicGenerator& MnemonicGenerator::operator=(MnemonicGenerator const &) = default; //MSVC2022 workaround
+MnemonicGenerator::MnemonicGenerator(MnemonicGenerator const&) = default; //MSVC2022 workaround
+
+sal_uInt16 MnemonicGenerator::ImplGetMnemonicIndex( sal_Unicode c )
+{
+ static sal_uInt16 const aImplMnemonicRangeTab[MNEMONIC_RANGES*2] =
+ {
+ MNEMONIC_RANGE_1_START, MNEMONIC_RANGE_1_END,
+ MNEMONIC_RANGE_2_START, MNEMONIC_RANGE_2_END,
+ MNEMONIC_RANGE_3_START, MNEMONIC_RANGE_3_END,
+ MNEMONIC_RANGE_4_START, MNEMONIC_RANGE_4_END
+ };
+
+ sal_uInt16 nMnemonicIndex = 0;
+ for ( sal_uInt16 i = 0; i < MNEMONIC_RANGES; i++ )
+ {
+ if ( (c >= aImplMnemonicRangeTab[i*2]) &&
+ (c <= aImplMnemonicRangeTab[i*2+1]) )
+ return nMnemonicIndex+c-aImplMnemonicRangeTab[i*2];
+
+ nMnemonicIndex += aImplMnemonicRangeTab[i*2+1]-aImplMnemonicRangeTab[i*2];
+ }
+
+ return MNEMONIC_INDEX_NOTFOUND;
+}
+
+sal_Unicode MnemonicGenerator::ImplFindMnemonic( const OUString& rKey )
+{
+ sal_Int32 nIndex = 0;
+ while ( (nIndex = rKey.indexOf( m_cMnemonic, nIndex )) != -1 )
+ {
+ if (nIndex == rKey.getLength() - 1) {
+ SAL_WARN("vcl", "key \"" << rKey << "\" ends in lone mnemonic prefix");
+ break;
+ }
+ sal_Unicode cMnemonic = rKey[ nIndex+1 ];
+ if ( cMnemonic != m_cMnemonic )
+ return cMnemonic;
+ nIndex += 2;
+ }
+
+ return 0;
+}
+
+void MnemonicGenerator::RegisterMnemonic( const OUString& rKey )
+{
+ uno::Reference < i18n::XCharacterClassification > xCharClass = GetCharClass();
+
+ // Don't crash even when we don't have access to i18n service
+ if ( !xCharClass.is() )
+ return;
+
+ OUString aKey = xCharClass->toLower(rKey, 0, rKey.getLength(), css::lang::Locale());
+
+ // If we find a Mnemonic, set the flag. In other case count the
+ // characters, because we need this to set most as possible
+ // Mnemonics
+ sal_Unicode cMnemonic = ImplFindMnemonic( aKey );
+ if ( cMnemonic )
+ {
+ sal_uInt16 nMnemonicIndex = ImplGetMnemonicIndex( cMnemonic );
+ if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
+ maMnemonics[nMnemonicIndex] = 0;
+ }
+ else
+ {
+ sal_Int32 nIndex = 0;
+ sal_Int32 nLen = aKey.getLength();
+ while ( nIndex < nLen )
+ {
+ sal_Unicode c = aKey[ nIndex ];
+
+ sal_uInt16 nMnemonicIndex = ImplGetMnemonicIndex( c );
+ if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
+ {
+ if ( maMnemonics[nMnemonicIndex] && (maMnemonics[nMnemonicIndex] < 0xFF) )
+ maMnemonics[nMnemonicIndex]++;
+ }
+
+ nIndex++;
+ }
+ }
+}
+
+OUString MnemonicGenerator::CreateMnemonic( const OUString& _rKey )
+{
+ if ( _rKey.isEmpty() || ImplFindMnemonic( _rKey ) )
+ return _rKey;
+
+ uno::Reference < i18n::XCharacterClassification > xCharClass = GetCharClass();
+
+ // Don't crash even when we don't have access to i18n service
+ if ( !xCharClass.is() )
+ return _rKey;
+
+ OUString aKey = xCharClass->toLower(_rKey, 0, _rKey.getLength(), css::lang::Locale());
+
+ bool bChanged = false;
+ sal_Int32 nLen = aKey.getLength();
+
+ bool bCJK = MsLangId::isCJK(Application::GetSettings().GetUILanguageTag().getLanguageType());
+
+ // #107889# in CJK versions ALL strings (even those that contain latin characters)
+ // will get mnemonics in the form: xyz (M)
+ // thus steps 1) and 2) are skipped for CJK locales
+
+ // #110720#, avoid CJK-style mnemonics for latin-only strings that do not contain useful mnemonic chars
+ if( bCJK )
+ {
+ bool bLatinOnly = true;
+ bool bMnemonicIndexFound = false;
+ sal_Unicode c;
+ sal_Int32 nIndex;
+
+ for( nIndex=0; nIndex < nLen; nIndex++ )
+ {
+ c = aKey[ nIndex ];
+ if ( ((c >= 0x3000) && (c <= 0xD7FF)) || // cjk
+ ((c >= 0xFF61) && (c <= 0xFFDC)) ) // halfwidth forms
+ {
+ bLatinOnly = false;
+ break;
+ }
+ if( ImplGetMnemonicIndex( c ) != MNEMONIC_INDEX_NOTFOUND )
+ bMnemonicIndexFound = true;
+ }
+ if( bLatinOnly && !bMnemonicIndexFound )
+ return _rKey;
+ }
+
+ OUString rKey(_rKey);
+ int nCJK = 0;
+ sal_uInt16 nMnemonicIndex;
+ sal_Unicode c;
+ sal_Int32 nIndex = 0;
+ if( !bCJK )
+ {
+ // 1) first try the first character of a word
+ do
+ {
+ c = aKey[ nIndex ];
+
+ if ( nCJK != 2 )
+ {
+ if ( ((c >= 0x3000) && (c <= 0xD7FF)) || // cjk
+ ((c >= 0xFF61) && (c <= 0xFFDC)) ) // halfwidth forms
+ nCJK = 1;
+ else if ( ((c >= 0x0030) && (c <= 0x0039)) || // digits
+ ((c >= 0x0041) && (c <= 0x005A)) || // latin capitals
+ ((c >= 0x0061) && (c <= 0x007A)) || // latin small
+ ((c >= 0x0370) && (c <= 0x037F)) || // greek numeral signs
+ ((c >= 0x0400) && (c <= 0x04FF)) ) // cyrillic
+ nCJK = 2;
+ }
+
+ nMnemonicIndex = ImplGetMnemonicIndex( c );
+ if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
+ {
+ if ( maMnemonics[nMnemonicIndex] )
+ {
+ maMnemonics[nMnemonicIndex] = 0;
+ rKey = rKey.replaceAt( nIndex, 0, rtl::OUStringChar(m_cMnemonic) );
+ bChanged = true;
+ break;
+ }
+ }
+
+ // Search for next word
+ nIndex++;
+ while ( nIndex < nLen )
+ {
+ c = aKey[ nIndex ];
+ if ( c == ' ' )
+ break;
+ nIndex++;
+ }
+ nIndex++;
+ }
+ while ( nIndex < nLen );
+
+ // 2) search for a unique/uncommon character
+ if ( !bChanged )
+ {
+ sal_uInt16 nBestCount = 0xFFFF;
+ sal_uInt16 nBestMnemonicIndex = 0;
+ sal_Int32 nBestIndex = 0;
+ nIndex = 0;
+ do
+ {
+ c = aKey[ nIndex ];
+ nMnemonicIndex = ImplGetMnemonicIndex( c );
+ if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
+ {
+ if ( maMnemonics[nMnemonicIndex] )
+ {
+ if ( maMnemonics[nMnemonicIndex] < nBestCount )
+ {
+ nBestCount = maMnemonics[nMnemonicIndex];
+ nBestIndex = nIndex;
+ nBestMnemonicIndex = nMnemonicIndex;
+ if ( nBestCount == 2 )
+ break;
+ }
+ }
+ }
+
+ nIndex++;
+ }
+ while ( nIndex < nLen );
+
+ if ( nBestCount != 0xFFFF )
+ {
+ maMnemonics[nBestMnemonicIndex] = 0;
+ rKey = rKey.replaceAt( nBestIndex, 0, rtl::OUStringChar(m_cMnemonic) );
+ bChanged = true;
+ }
+ }
+ }
+ else
+ nCJK = 1;
+
+ // 3) Add English Mnemonic for CJK Text
+ if ( !bChanged && (nCJK == 1) && !rKey.isEmpty() )
+ {
+ // Append Ascii Mnemonic
+ for ( c = MNEMONIC_RANGE_2_START; c <= MNEMONIC_RANGE_2_END; c++ )
+ {
+ nMnemonicIndex = ImplGetMnemonicIndex(c);
+ if ( nMnemonicIndex != MNEMONIC_INDEX_NOTFOUND )
+ {
+ if ( maMnemonics[nMnemonicIndex] )
+ {
+ maMnemonics[nMnemonicIndex] = 0;
+ OUString aStr = OUString::Concat("(") + OUStringChar(m_cMnemonic) +
+ OUStringChar(sal_Unicode(rtl::toAsciiUpperCase(c))) +
+ ")";
+ nIndex = rKey.getLength();
+ if( nIndex >= 2 )
+ {
+ if ( ( rKey[nIndex-2] == '>' && rKey[nIndex-1] == '>' ) ||
+ ( rKey[nIndex-2] == 0xFF1E && rKey[nIndex-1] == 0xFF1E ) )
+ nIndex -= 2;
+ }
+ if( nIndex >= 3 )
+ {
+ if ( ( rKey[nIndex-3] == '.' && rKey[nIndex-2] == '.' && rKey[nIndex-1] == '.' ) ||
+ ( rKey[nIndex-3] == 0xFF0E && rKey[nIndex-2] == 0xFF0E && rKey[nIndex-1] == 0xFF0E ) )
+ nIndex -= 3;
+ }
+ if( nIndex >= 1)
+ {
+ sal_Unicode cLastChar = rKey[ nIndex-1 ];
+ if ( (cLastChar == ':') || (cLastChar == 0xFF1A) ||
+ (cLastChar == '.') || (cLastChar == 0xFF0E) ||
+ (cLastChar == '?') || (cLastChar == 0xFF1F) ||
+ (cLastChar == ' ') )
+ nIndex--;
+ }
+ rKey = rKey.replaceAt( nIndex, 0, aStr );
+ break;
+ }
+ }
+ }
+ }
+
+ return rKey;
+}
+
+uno::Reference< i18n::XCharacterClassification > const & MnemonicGenerator::GetCharClass()
+{
+ if ( !mxCharClass.is() )
+ mxCharClass = vcl::unohelper::CreateCharacterClassification();
+ return mxCharClass;
+}
+
+OUString MnemonicGenerator::EraseAllMnemonicChars( const OUString& rStr )
+{
+ OUString aStr = rStr;
+ sal_Int32 nLen = aStr.getLength();
+ sal_Int32 i = 0;
+
+ while ( i < nLen )
+ {
+ if ( aStr[ i ] == '~' )
+ {
+ // check for CJK-style mnemonic
+ if( i > 0 && (i+2) < nLen )
+ {
+ sal_Unicode c = sal_Unicode(rtl::toAsciiLowerCase(aStr[i+1]));
+ if( aStr[ i-1 ] == '(' &&
+ aStr[ i+2 ] == ')' &&
+ c >= MNEMONIC_RANGE_2_START && c <= MNEMONIC_RANGE_2_END )
+ {
+ aStr = aStr.replaceAt( i-1, 4, u"" );
+ nLen -= 4;
+ i--;
+ continue;
+ }
+ }
+
+ // remove standard mnemonics
+ aStr = aStr.replaceAt( i, 1, u"" );
+ nLen--;
+ }
+ else
+ i++;
+ }
+
+ return aStr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/mouse.cxx b/vcl/source/window/mouse.cxx
new file mode 100644
index 0000000000..e5c8e130f7
--- /dev/null
+++ b/vcl/source/window/mouse.cxx
@@ -0,0 +1,744 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <config_feature_desktop.h>
+#include <config_vclplug.h>
+
+#include <tools/time.hxx>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+
+#include <vcl/ITiledRenderable.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/event.hxx>
+
+#include <sal/types.h>
+
+#include <window.h>
+#include <svdata.hxx>
+#include <salobj.hxx>
+#include <salgdi.hxx>
+#include <salframe.hxx>
+#include <salinst.hxx>
+
+#include <dndlistenercontainer.hxx>
+#include <dndeventdispatcher.hxx>
+
+#include <com/sun/star/datatransfer/dnd/XDragSource.hpp>
+#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <comphelper/processfactory.hxx>
+
+using namespace ::com::sun::star::uno;
+
+namespace vcl {
+
+WindowHitTest Window::ImplHitTest( const Point& rFramePos )
+{
+ Point aFramePos( rFramePos );
+ if( GetOutDev()->ImplIsAntiparallel() )
+ {
+ const OutputDevice *pOutDev = GetOutDev();
+ pOutDev->ReMirror( aFramePos );
+ }
+ if ( !GetOutputRectPixel().Contains( aFramePos ) )
+ return WindowHitTest::NONE;
+ if ( mpWindowImpl->mbWinRegion )
+ {
+ Point aTempPos = aFramePos;
+ aTempPos.AdjustX( -GetOutDev()->mnOutOffX );
+ aTempPos.AdjustY( -GetOutDev()->mnOutOffY );
+ if ( !mpWindowImpl->maWinRegion.Contains( aTempPos ) )
+ return WindowHitTest::NONE;
+ }
+
+ WindowHitTest nHitTest = WindowHitTest::Inside;
+ if ( mpWindowImpl->mbMouseTransparent )
+ nHitTest |= WindowHitTest::Transparent;
+ return nHitTest;
+}
+
+bool Window::ImplTestMousePointerSet()
+{
+ // as soon as mouse is captured, switch mouse-pointer
+ if ( IsMouseCaptured() )
+ return true;
+
+ // if the mouse is over the window, switch it
+ tools::Rectangle aClientRect( Point( 0, 0 ), GetOutputSizePixel() );
+ return aClientRect.Contains( GetPointerPosPixel() );
+}
+
+PointerStyle Window::ImplGetMousePointer() const
+{
+ PointerStyle ePointerStyle;
+ bool bWait = false;
+
+ if ( IsEnabled() && IsInputEnabled() && ! IsInModalMode() )
+ ePointerStyle = GetPointer();
+ else
+ ePointerStyle = PointerStyle::Arrow;
+
+ const vcl::Window* pWindow = this;
+ do
+ {
+ // when the pointer is not visible stop the search, as
+ // this status should not be overwritten
+ if ( pWindow->mpWindowImpl->mbNoPtrVisible )
+ return PointerStyle::Null;
+
+ if ( !bWait )
+ {
+ if ( pWindow->mpWindowImpl->mnWaitCount )
+ {
+ ePointerStyle = PointerStyle::Wait;
+ bWait = true;
+ }
+ else
+ {
+ if ( pWindow->mpWindowImpl->mbChildPtrOverwrite )
+ ePointerStyle = pWindow->GetPointer();
+ }
+ }
+
+ if ( pWindow->ImplIsOverlapWindow() )
+ break;
+
+ pWindow = pWindow->ImplGetParent();
+ }
+ while ( pWindow );
+
+ return ePointerStyle;
+}
+
+void Window::ImplCallMouseMove( sal_uInt16 nMouseCode, bool bModChanged )
+{
+ if ( !(mpWindowImpl->mpFrameData->mbMouseIn && mpWindowImpl->mpFrameWindow->mpWindowImpl->mbReallyVisible) )
+ return;
+
+ sal_uInt64 nTime = tools::Time::GetSystemTicks();
+ tools::Long nX = mpWindowImpl->mpFrameData->mnLastMouseX;
+ tools::Long nY = mpWindowImpl->mpFrameData->mnLastMouseY;
+ sal_uInt16 nCode = nMouseCode;
+ MouseEventModifiers nMode = mpWindowImpl->mpFrameData->mnMouseMode;
+ bool bLeave;
+ // check for MouseLeave
+ bLeave = ((nX < 0) || (nY < 0) ||
+ (nX >= mpWindowImpl->mpFrameWindow->GetOutDev()->mnOutWidth) ||
+ (nY >= mpWindowImpl->mpFrameWindow->GetOutDev()->mnOutHeight)) &&
+ !ImplGetSVData()->mpWinData->mpCaptureWin;
+ nMode |= MouseEventModifiers::SYNTHETIC;
+ if ( bModChanged )
+ nMode |= MouseEventModifiers::MODIFIERCHANGED;
+ ImplHandleMouseEvent( mpWindowImpl->mpFrameWindow, NotifyEventType::MOUSEMOVE, bLeave, nX, nY, nTime, nCode, nMode );
+}
+
+void Window::ImplGenerateMouseMove()
+{
+ if ( mpWindowImpl && mpWindowImpl->mpFrameData &&
+ !mpWindowImpl->mpFrameData->mnMouseMoveId )
+ mpWindowImpl->mpFrameData->mnMouseMoveId = Application::PostUserEvent( LINK( mpWindowImpl->mpFrameWindow, Window, ImplGenerateMouseMoveHdl ), nullptr, true );
+}
+
+IMPL_LINK_NOARG(Window, ImplGenerateMouseMoveHdl, void*, void)
+{
+ mpWindowImpl->mpFrameData->mnMouseMoveId = nullptr;
+ vcl::Window* pCaptureWin = ImplGetSVData()->mpWinData->mpCaptureWin;
+ if( ! pCaptureWin ||
+ (pCaptureWin->mpWindowImpl && pCaptureWin->mpWindowImpl->mpFrame == mpWindowImpl->mpFrame)
+ )
+ {
+ ImplCallMouseMove( mpWindowImpl->mpFrameData->mnMouseCode );
+ }
+}
+
+void Window::ImplInvertFocus( const tools::Rectangle& rRect )
+{
+ InvertTracking( rRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow );
+}
+
+static bool IsWindowFocused(const WindowImpl& rWinImpl)
+{
+ if (rWinImpl.mpSysObj)
+ return true;
+
+ if (rWinImpl.mpFrameData->mbHasFocus)
+ return true;
+
+ if (rWinImpl.mbFakeFocusSet)
+ return true;
+
+ return false;
+}
+
+void Window::ImplGrabFocus( GetFocusFlags nFlags )
+{
+ // #143570# no focus for destructing windows
+ if( !mpWindowImpl || mpWindowImpl->mbInDispose )
+ return;
+
+ // some event listeners do really bad stuff
+ // => prepare for the worst
+ VclPtr<vcl::Window> xWindow( this );
+
+ // Currently the client window should always get the focus
+ // Should the border window at some point be focusable
+ // we need to change all GrabFocus() instances in VCL,
+ // e.g. in ToTop()
+
+ if ( mpWindowImpl->mpClientWindow )
+ {
+ // For a lack of design we need a little hack here to
+ // ensure that dialogs on close pass the focus back to
+ // the correct window
+ if ( mpWindowImpl->mpLastFocusWindow && (mpWindowImpl->mpLastFocusWindow.get() != this) &&
+ !(mpWindowImpl->mnDlgCtrlFlags & DialogControlFlags::WantFocus) &&
+ mpWindowImpl->mpLastFocusWindow->IsEnabled() &&
+ mpWindowImpl->mpLastFocusWindow->IsInputEnabled() &&
+ ! mpWindowImpl->mpLastFocusWindow->IsInModalMode()
+ )
+ mpWindowImpl->mpLastFocusWindow->GrabFocus();
+ else
+ mpWindowImpl->mpClientWindow->GrabFocus();
+ return;
+ }
+ else if ( mpWindowImpl->mbFrame )
+ {
+ // For a lack of design we need a little hack here to
+ // ensure that dialogs on close pass the focus back to
+ // the correct window
+ if ( mpWindowImpl->mpLastFocusWindow && (mpWindowImpl->mpLastFocusWindow.get() != this) &&
+ !(mpWindowImpl->mnDlgCtrlFlags & DialogControlFlags::WantFocus) &&
+ mpWindowImpl->mpLastFocusWindow->IsEnabled() &&
+ mpWindowImpl->mpLastFocusWindow->IsInputEnabled() &&
+ ! mpWindowImpl->mpLastFocusWindow->IsInModalMode()
+ )
+ {
+ mpWindowImpl->mpLastFocusWindow->GrabFocus();
+ return;
+ }
+ }
+
+ // If the Window is disabled, then we don't change the focus
+ if ( !IsEnabled() || !IsInputEnabled() || IsInModalMode() )
+ return;
+
+ // we only need to set the focus if it is not already set
+ // note: if some other frame is waiting for an asynchronous focus event
+ // we also have to post an asynchronous focus event for this frame
+ // which is done using ToTop
+ ImplSVData* pSVData = ImplGetSVData();
+
+ bool bAsyncFocusWaiting = false;
+ vcl::Window *pFrame = pSVData->maFrameData.mpFirstFrame;
+ while( pFrame && pFrame->mpWindowImpl && pFrame->mpWindowImpl->mpFrameData )
+ {
+ if( pFrame != mpWindowImpl->mpFrameWindow.get() && pFrame->mpWindowImpl->mpFrameData->mnFocusId )
+ {
+ bAsyncFocusWaiting = true;
+ break;
+ }
+ pFrame = pFrame->mpWindowImpl->mpFrameData->mpNextFrame;
+ }
+
+ bool bHasFocus = IsWindowFocused(*mpWindowImpl);
+
+ bool bMustNotGrabFocus = false;
+ // #100242#, check parent hierarchy if some floater prohibits grab focus
+
+ vcl::Window *pParent = this;
+ while( pParent )
+ {
+ if ((pParent->GetStyle() & WB_SYSTEMFLOATWIN) && !(pParent->GetStyle() & WB_MOVEABLE))
+ {
+ bMustNotGrabFocus = true;
+ break;
+ }
+ if (!pParent->mpWindowImpl)
+ break;
+ pParent = pParent->mpWindowImpl->mpParent;
+ }
+
+ if ( !(( pSVData->mpWinData->mpFocusWin.get() != this &&
+ !mpWindowImpl->mbInDispose ) ||
+ ( bAsyncFocusWaiting && !bHasFocus && !bMustNotGrabFocus )) )
+ return;
+
+ // EndExtTextInput if it is not the same window
+ if (pSVData->mpWinData->mpExtTextInputWin
+ && (pSVData->mpWinData->mpExtTextInputWin.get() != this))
+ pSVData->mpWinData->mpExtTextInputWin->EndExtTextInput();
+
+ // mark this windows as the last FocusWindow
+ vcl::Window* pOverlapWindow = ImplGetFirstOverlapWindow();
+ if (pOverlapWindow->mpWindowImpl)
+ pOverlapWindow->mpWindowImpl->mpLastFocusWindow = this;
+ mpWindowImpl->mpFrameData->mpFocusWin = this;
+
+ if( !bHasFocus )
+ {
+ // menu windows never get the system focus
+ // the application will keep the focus
+ if( bMustNotGrabFocus )
+ return;
+ else
+ {
+ // here we already switch focus as ToTop()
+ // should not give focus to another window
+ mpWindowImpl->mpFrame->ToTop( SalFrameToTop::GrabFocus | SalFrameToTop::GrabFocusOnly );
+ return;
+ }
+ }
+
+ VclPtr<vcl::Window> pOldFocusWindow = pSVData->mpWinData->mpFocusWin;
+
+ pSVData->mpWinData->mpFocusWin = this;
+
+ if ( pOldFocusWindow && pOldFocusWindow->mpWindowImpl )
+ {
+ // Cursor hidden
+ if ( pOldFocusWindow->mpWindowImpl->mpCursor )
+ pOldFocusWindow->mpWindowImpl->mpCursor->ImplHide();
+ }
+
+ // !!!!! due to old SV-Office Activate/Deactivate handling
+ // !!!!! first as before
+ if ( pOldFocusWindow )
+ {
+ // remember Focus
+ vcl::Window* pOldOverlapWindow = pOldFocusWindow->ImplGetFirstOverlapWindow();
+ vcl::Window* pNewOverlapWindow = ImplGetFirstOverlapWindow();
+ if ( pOldOverlapWindow != pNewOverlapWindow )
+ ImplCallFocusChangeActivate( pNewOverlapWindow, pOldOverlapWindow );
+ }
+ else
+ {
+ vcl::Window* pNewOverlapWindow = ImplGetFirstOverlapWindow();
+ if ( pNewOverlapWindow && pNewOverlapWindow->mpWindowImpl )
+ {
+ vcl::Window* pNewRealWindow = pNewOverlapWindow->ImplGetWindow();
+ pNewOverlapWindow->mpWindowImpl->mbActive = true;
+ pNewOverlapWindow->Activate();
+ if ( pNewRealWindow != pNewOverlapWindow && pNewRealWindow && pNewRealWindow->mpWindowImpl )
+ {
+ pNewRealWindow->mpWindowImpl->mbActive = true;
+ pNewRealWindow->Activate();
+ }
+ }
+ }
+
+ // call Get- and LoseFocus
+ if ( pOldFocusWindow && ! pOldFocusWindow->isDisposed() )
+ {
+ NotifyEvent aNEvt( NotifyEventType::LOSEFOCUS, pOldFocusWindow );
+ if ( !ImplCallPreNotify( aNEvt ) )
+ pOldFocusWindow->CompatLoseFocus();
+ pOldFocusWindow->ImplCallDeactivateListeners( this );
+ }
+
+ if (pSVData->mpWinData->mpFocusWin.get() == this)
+ {
+ if ( mpWindowImpl->mpSysObj )
+ {
+ mpWindowImpl->mpFrameData->mpFocusWin = this;
+ if ( !mpWindowImpl->mpFrameData->mbInSysObjFocusHdl )
+ mpWindowImpl->mpSysObj->GrabFocus();
+ }
+
+ if (pSVData->mpWinData->mpFocusWin.get() == this)
+ {
+ if ( mpWindowImpl->mpCursor )
+ mpWindowImpl->mpCursor->ImplShow();
+ mpWindowImpl->mbInFocusHdl = true;
+ mpWindowImpl->mnGetFocusFlags = nFlags;
+ // if we're changing focus due to closing a popup floating window
+ // notify the new focus window so it can restore the inner focus
+ // eg, toolboxes can select their recent active item
+ if( pOldFocusWindow &&
+ ! pOldFocusWindow->isDisposed() &&
+ ( pOldFocusWindow->GetDialogControlFlags() & DialogControlFlags::FloatWinPopupModeEndCancel ) )
+ mpWindowImpl->mnGetFocusFlags |= GetFocusFlags::FloatWinPopupModeEndCancel;
+ NotifyEvent aNEvt( NotifyEventType::GETFOCUS, this );
+ if ( !ImplCallPreNotify( aNEvt ) && !xWindow->isDisposed() )
+ CompatGetFocus();
+ if( !xWindow->isDisposed() )
+ ImplCallActivateListeners( (pOldFocusWindow && ! pOldFocusWindow->isDisposed()) ? pOldFocusWindow : nullptr );
+ if( !xWindow->isDisposed() )
+ {
+ mpWindowImpl->mnGetFocusFlags = GetFocusFlags::NONE;
+ mpWindowImpl->mbInFocusHdl = false;
+ }
+ }
+ }
+
+ ImplNewInputContext();
+
+}
+
+void Window::ImplGrabFocusToDocument( GetFocusFlags nFlags )
+{
+ vcl::Window *pWin = this;
+ while( pWin )
+ {
+ if( !pWin->GetParent() )
+ {
+ pWin->mpWindowImpl->mpFrame->GrabFocus();
+ pWin->ImplGetFrameWindow()->GetWindow( GetWindowType::Client )->ImplGrabFocus(nFlags);
+ return;
+ }
+ pWin = pWin->GetParent();
+ }
+}
+
+void Window::MouseMove( const MouseEvent& rMEvt )
+{
+ NotifyEvent aNEvt( NotifyEventType::MOUSEMOVE, this, &rMEvt );
+ EventNotify(aNEvt);
+}
+
+void Window::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ NotifyEvent aNEvt( NotifyEventType::MOUSEBUTTONDOWN, this, &rMEvt );
+ if (!EventNotify(aNEvt) && mpWindowImpl)
+ mpWindowImpl->mbMouseButtonDown = true;
+}
+
+void Window::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ NotifyEvent aNEvt( NotifyEventType::MOUSEBUTTONUP, this, &rMEvt );
+ if (!EventNotify(aNEvt) && mpWindowImpl)
+ mpWindowImpl->mbMouseButtonUp = true;
+}
+
+void Window::SetMouseTransparent( bool bTransparent )
+{
+
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->SetMouseTransparent( bTransparent );
+
+ if( mpWindowImpl->mpSysObj )
+ mpWindowImpl->mpSysObj->SetMouseTransparent( bTransparent );
+
+ mpWindowImpl->mbMouseTransparent = bTransparent;
+}
+
+void Window::LocalStartDrag()
+{
+ ImplGetFrameData()->mbDragging = true;
+}
+
+void Window::CaptureMouse()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // possibly stop tracking
+ if (pSVData->mpWinData->mpTrackWin.get() != this)
+ {
+ if (pSVData->mpWinData->mpTrackWin)
+ pSVData->mpWinData->mpTrackWin->EndTracking(TrackingEventFlags::Cancel);
+ }
+
+ if (pSVData->mpWinData->mpCaptureWin.get() != this)
+ {
+ pSVData->mpWinData->mpCaptureWin = this;
+ mpWindowImpl->mpFrame->CaptureMouse( true );
+ }
+}
+
+void Window::ReleaseMouse()
+{
+ if (IsMouseCaptured())
+ {
+ ImplSVData* pSVData = ImplGetSVData();
+ pSVData->mpWinData->mpCaptureWin = nullptr;
+ if (mpWindowImpl && mpWindowImpl->mpFrame)
+ mpWindowImpl->mpFrame->CaptureMouse( false );
+ ImplGenerateMouseMove();
+ }
+}
+
+bool Window::IsMouseCaptured() const
+{
+ return (this == ImplGetSVData()->mpWinData->mpCaptureWin);
+}
+
+void Window::SetPointer( PointerStyle nPointer )
+{
+ if ( mpWindowImpl->maPointer == nPointer )
+ return;
+
+ mpWindowImpl->maPointer = nPointer;
+
+ // possibly immediately move pointer
+ if ( !mpWindowImpl->mpFrameData->mbInMouseMove && ImplTestMousePointerSet() )
+ mpWindowImpl->mpFrame->SetPointer( ImplGetMousePointer() );
+}
+
+void Window::EnableChildPointerOverwrite( bool bOverwrite )
+{
+
+ if ( mpWindowImpl->mbChildPtrOverwrite == bOverwrite )
+ return;
+
+ mpWindowImpl->mbChildPtrOverwrite = bOverwrite;
+
+ // possibly immediately move pointer
+ if ( !mpWindowImpl->mpFrameData->mbInMouseMove && ImplTestMousePointerSet() )
+ mpWindowImpl->mpFrame->SetPointer( ImplGetMousePointer() );
+}
+
+void Window::SetPointerPosPixel( const Point& rPos )
+{
+ Point aPos = OutputToScreenPixel( rPos );
+ const OutputDevice *pOutDev = GetOutDev();
+ if( pOutDev->HasMirroredGraphics() )
+ {
+ if( !IsRTLEnabled() )
+ {
+ pOutDev->ReMirror( aPos );
+ }
+ // mirroring is required here, SetPointerPos bypasses SalGraphics
+ aPos.setX( GetOutDev()->mpGraphics->mirror2( aPos.X(), *GetOutDev() ) );
+ }
+ else if( GetOutDev()->ImplIsAntiparallel() )
+ {
+ pOutDev->ReMirror( aPos );
+ }
+ mpWindowImpl->mpFrame->SetPointerPos( aPos.X(), aPos.Y() );
+}
+
+void Window::SetLastMousePos(const Point& rPos)
+{
+ // Do this conversion, so when GetPointerPosPixel() calls
+ // ScreenToOutputPixel(), we get back the original position.
+ Point aPos = OutputToScreenPixel(rPos);
+ mpWindowImpl->mpFrameData->mnLastMouseX = aPos.X();
+ mpWindowImpl->mpFrameData->mnLastMouseY = aPos.Y();
+}
+
+Point Window::GetPointerPosPixel()
+{
+
+ Point aPos( mpWindowImpl->mpFrameData->mnLastMouseX, mpWindowImpl->mpFrameData->mnLastMouseY );
+ if( GetOutDev()->ImplIsAntiparallel() )
+ {
+ const OutputDevice *pOutDev = GetOutDev();
+ pOutDev->ReMirror( aPos );
+ }
+ return ScreenToOutputPixel( aPos );
+}
+
+Point Window::GetLastPointerPosPixel()
+{
+
+ Point aPos( mpWindowImpl->mpFrameData->mnBeforeLastMouseX, mpWindowImpl->mpFrameData->mnBeforeLastMouseY );
+ if( GetOutDev()->ImplIsAntiparallel() )
+ {
+ const OutputDevice *pOutDev = GetOutDev();
+ pOutDev->ReMirror( aPos );
+ }
+ return ScreenToOutputPixel( aPos );
+}
+
+void Window::ShowPointer( bool bVisible )
+{
+
+ if ( mpWindowImpl->mbNoPtrVisible != !bVisible )
+ {
+ mpWindowImpl->mbNoPtrVisible = !bVisible;
+
+ // possibly immediately move pointer
+ if ( !mpWindowImpl->mpFrameData->mbInMouseMove && ImplTestMousePointerSet() )
+ mpWindowImpl->mpFrame->SetPointer( ImplGetMousePointer() );
+ }
+}
+
+Window::PointerState Window::GetPointerState()
+{
+ PointerState aState;
+ aState.mnState = 0;
+
+ if (mpWindowImpl->mpFrame)
+ {
+ SalFrame::SalPointerState aSalPointerState = mpWindowImpl->mpFrame->GetPointerState();
+ if( GetOutDev()->ImplIsAntiparallel() )
+ {
+ const OutputDevice *pOutDev = GetOutDev();
+ pOutDev->ReMirror( aSalPointerState.maPos );
+ }
+ aState.maPos = ScreenToOutputPixel( aSalPointerState.maPos );
+ aState.mnState = aSalPointerState.mnState;
+ }
+ return aState;
+}
+
+bool Window::IsMouseOver() const
+{
+ return ImplGetWinData()->mbMouseOver;
+}
+
+void Window::EnterWait()
+{
+
+ mpWindowImpl->mnWaitCount++;
+
+ if ( mpWindowImpl->mnWaitCount == 1 )
+ {
+ // possibly immediately move pointer
+ if ( !mpWindowImpl->mpFrameData->mbInMouseMove && ImplTestMousePointerSet() )
+ mpWindowImpl->mpFrame->SetPointer( ImplGetMousePointer() );
+ }
+}
+
+void Window::LeaveWait()
+{
+ if( !mpWindowImpl )
+ return;
+
+ if ( mpWindowImpl->mnWaitCount )
+ {
+ mpWindowImpl->mnWaitCount--;
+
+ if ( !mpWindowImpl->mnWaitCount )
+ {
+ // possibly immediately move pointer
+ if ( !mpWindowImpl->mpFrameData->mbInMouseMove && ImplTestMousePointerSet() )
+ mpWindowImpl->mpFrame->SetPointer( ImplGetMousePointer() );
+ }
+ }
+}
+
+bool Window::ImplStopDnd()
+{
+ bool bRet = false;
+ if( mpWindowImpl->mpFrameData && mpWindowImpl->mpFrameData->mxDropTargetListener.is() )
+ {
+ bRet = true;
+ mpWindowImpl->mpFrameData->mxDropTarget.clear();
+ mpWindowImpl->mpFrameData->mxDragSource.clear();
+ mpWindowImpl->mpFrameData->mxDropTargetListener.clear();
+ }
+
+ return bRet;
+}
+
+void Window::ImplStartDnd()
+{
+ GetDropTarget();
+}
+
+Reference< css::datatransfer::dnd::XDropTarget > Window::GetDropTarget()
+{
+ if( !mpWindowImpl )
+ return Reference< css::datatransfer::dnd::XDropTarget >();
+
+ if( ! mpWindowImpl->mxDNDListenerContainer.is() )
+ {
+ sal_Int8 nDefaultActions = 0;
+
+ if( mpWindowImpl->mpFrameData )
+ {
+ if( ! mpWindowImpl->mpFrameData->mxDropTarget.is() )
+ {
+ // initialization is done in GetDragSource
+ GetDragSource();
+ }
+
+ if( mpWindowImpl->mpFrameData->mxDropTarget.is() )
+ {
+ nDefaultActions = mpWindowImpl->mpFrameData->mxDropTarget->getDefaultActions();
+
+ if( ! mpWindowImpl->mpFrameData->mxDropTargetListener.is() )
+ {
+ mpWindowImpl->mpFrameData->mxDropTargetListener = new DNDEventDispatcher( mpWindowImpl->mpFrameWindow );
+
+ try
+ {
+ mpWindowImpl->mpFrameData->mxDropTarget->addDropTargetListener( mpWindowImpl->mpFrameData->mxDropTargetListener );
+
+ // register also as drag gesture listener if directly supported by drag source
+ Reference< css::datatransfer::dnd::XDragGestureRecognizer > xDragGestureRecognizer(
+ mpWindowImpl->mpFrameData->mxDragSource, UNO_QUERY);
+
+ if( xDragGestureRecognizer.is() )
+ {
+ xDragGestureRecognizer->addDragGestureListener(
+ Reference< css::datatransfer::dnd::XDragGestureListener > (mpWindowImpl->mpFrameData->mxDropTargetListener, UNO_QUERY));
+ }
+ else
+ mpWindowImpl->mpFrameData->mbInternalDragGestureRecognizer = true;
+
+ }
+ catch (const RuntimeException&)
+ {
+ // release all instances
+ mpWindowImpl->mpFrameData->mxDropTarget.clear();
+ mpWindowImpl->mpFrameData->mxDragSource.clear();
+ }
+ }
+ }
+
+ }
+
+ mpWindowImpl->mxDNDListenerContainer = static_cast < css::datatransfer::dnd::XDropTarget * > ( new DNDListenerContainer( nDefaultActions ) );
+ }
+
+ // this object is located in the same process, so there will be no runtime exception
+ return Reference< css::datatransfer::dnd::XDropTarget > ( mpWindowImpl->mxDNDListenerContainer, UNO_QUERY );
+}
+
+Reference< css::datatransfer::dnd::XDragSource > Window::GetDragSource()
+{
+#if HAVE_FEATURE_DESKTOP
+ const SystemEnvData* pEnvData = GetSystemData();
+ if (!mpWindowImpl->mpFrameData || !pEnvData)
+ return Reference<css::datatransfer::dnd::XDragSource>();
+ if (mpWindowImpl->mpFrameData->mxDragSource.is())
+ return mpWindowImpl->mpFrameData->mxDragSource;
+
+ try
+ {
+ SalInstance* pInst = ImplGetSVData()->mpDefInst;
+ mpWindowImpl->mpFrameData->mxDragSource.set(pInst->CreateDragSource(pEnvData), UNO_QUERY);
+ mpWindowImpl->mpFrameData->mxDropTarget.set(pInst->CreateDropTarget(pEnvData), UNO_QUERY);
+ }
+ catch (const Exception&)
+ {
+ mpWindowImpl->mpFrameData->mxDropTarget.clear();
+ mpWindowImpl->mpFrameData->mxDragSource.clear();
+ }
+ return mpWindowImpl->mpFrameData->mxDragSource;
+#else
+ return Reference< css::datatransfer::dnd::XDragSource > ();
+#endif
+}
+
+Reference< css::datatransfer::dnd::XDragGestureRecognizer > Window::GetDragGestureRecognizer()
+{
+ return Reference< css::datatransfer::dnd::XDragGestureRecognizer > ( GetDropTarget(), UNO_QUERY );
+}
+
+} /* namespace vcl */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/paint.cxx b/vcl/source/window/paint.cxx
new file mode 100644
index 0000000000..a98703ca25
--- /dev/null
+++ b/vcl/source/window/paint.cxx
@@ -0,0 +1,1808 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <vcl/gdimtf.hxx>
+#include <vcl/window.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/syswin.hxx>
+
+#include <sal/types.h>
+#include <sal/log.hxx>
+
+#include <window.h>
+#include <salgdi.hxx>
+#include <salframe.hxx>
+#include <svdata.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/profilezone.hxx>
+#if HAVE_FEATURE_OPENGL
+#include <vcl/opengl/OpenGLHelper.hxx>
+#endif
+
+// PaintBufferGuard
+
+namespace vcl
+{
+PaintBufferGuard::PaintBufferGuard(ImplFrameData* pFrameData, vcl::Window* pWindow)
+ : mpFrameData(pFrameData),
+ m_pWindow(pWindow),
+ mbBackground(false),
+ mnOutOffX(0),
+ mnOutOffY(0)
+{
+ if (!pFrameData->mpBuffer)
+ return;
+
+ // transfer various settings
+ // FIXME: this must disappear as we move to RenderContext only,
+ // the painting must become state-less, so that no actual
+ // vcl::Window setting affects this
+ mbBackground = pFrameData->mpBuffer->IsBackground();
+ if (pWindow->IsBackground())
+ {
+ maBackground = pFrameData->mpBuffer->GetBackground();
+ pFrameData->mpBuffer->SetBackground(pWindow->GetBackground());
+ }
+ //else
+ //SAL_WARN("vcl.window", "the root of the double-buffering hierarchy should not have a transparent background");
+
+ vcl::PushFlags nFlags = vcl::PushFlags::NONE;
+ nFlags |= vcl::PushFlags::CLIPREGION;
+ nFlags |= vcl::PushFlags::FILLCOLOR;
+ nFlags |= vcl::PushFlags::FONT;
+ nFlags |= vcl::PushFlags::LINECOLOR;
+ nFlags |= vcl::PushFlags::MAPMODE;
+ maSettings = pFrameData->mpBuffer->GetSettings();
+ nFlags |= vcl::PushFlags::REFPOINT;
+ nFlags |= vcl::PushFlags::TEXTCOLOR;
+ nFlags |= vcl::PushFlags::TEXTLINECOLOR;
+ nFlags |= vcl::PushFlags::OVERLINECOLOR;
+ nFlags |= vcl::PushFlags::TEXTFILLCOLOR;
+ nFlags |= vcl::PushFlags::TEXTALIGN;
+ nFlags |= vcl::PushFlags::RASTEROP;
+ nFlags |= vcl::PushFlags::TEXTLAYOUTMODE;
+ nFlags |= vcl::PushFlags::TEXTLANGUAGE;
+ pFrameData->mpBuffer->Push(nFlags);
+ auto& rDev = *pWindow->GetOutDev();
+ pFrameData->mpBuffer->SetClipRegion(rDev.GetClipRegion());
+ pFrameData->mpBuffer->SetFillColor(rDev.GetFillColor());
+ pFrameData->mpBuffer->SetFont(pWindow->GetFont());
+ pFrameData->mpBuffer->SetLineColor(rDev.GetLineColor());
+ pFrameData->mpBuffer->SetMapMode(pWindow->GetMapMode());
+ pFrameData->mpBuffer->SetRefPoint(rDev.GetRefPoint());
+ pFrameData->mpBuffer->SetSettings(pWindow->GetSettings());
+ pFrameData->mpBuffer->SetTextColor(pWindow->GetTextColor());
+ pFrameData->mpBuffer->SetTextLineColor(pWindow->GetTextLineColor());
+ pFrameData->mpBuffer->SetOverlineColor(pWindow->GetOverlineColor());
+ pFrameData->mpBuffer->SetTextFillColor(pWindow->GetTextFillColor());
+ pFrameData->mpBuffer->SetTextAlign(pWindow->GetTextAlign());
+ pFrameData->mpBuffer->SetRasterOp(rDev.GetRasterOp());
+ pFrameData->mpBuffer->SetLayoutMode(rDev.GetLayoutMode());
+ pFrameData->mpBuffer->SetDigitLanguage(rDev.GetDigitLanguage());
+
+ mnOutOffX = pFrameData->mpBuffer->GetOutOffXPixel();
+ mnOutOffY = pFrameData->mpBuffer->GetOutOffYPixel();
+ pFrameData->mpBuffer->SetOutOffXPixel(pWindow->GetOutOffXPixel());
+ pFrameData->mpBuffer->SetOutOffYPixel(pWindow->GetOutOffYPixel());
+ pFrameData->mpBuffer->EnableRTL(pWindow->IsRTLEnabled());
+}
+
+PaintBufferGuard::~PaintBufferGuard() COVERITY_NOEXCEPT_FALSE
+{
+ if (!mpFrameData->mpBuffer)
+ return;
+
+ if (!m_aPaintRect.IsEmpty())
+ {
+ // copy the buffer content to the actual window
+ // export VCL_DOUBLEBUFFERING_AVOID_PAINT=1 to see where we are
+ // painting directly instead of using Invalidate()
+ // [ie. everything you can see was painted directly to the
+ // window either above or in eg. an event handler]
+ if (!getenv("VCL_DOUBLEBUFFERING_AVOID_PAINT"))
+ {
+ // Make sure that the +1 value GetSize() adds to the size is in pixels.
+ Size aPaintRectSize;
+ if (m_pWindow->GetMapMode().GetMapUnit() == MapUnit::MapPixel)
+ {
+ aPaintRectSize = m_aPaintRect.GetSize();
+ }
+ else
+ {
+ tools::Rectangle aRectanglePixel = m_pWindow->LogicToPixel(m_aPaintRect);
+ aPaintRectSize = m_pWindow->PixelToLogic(aRectanglePixel.GetSize());
+ }
+
+ m_pWindow->GetOutDev()->DrawOutDev(m_aPaintRect.TopLeft(), aPaintRectSize, m_aPaintRect.TopLeft(), aPaintRectSize, *mpFrameData->mpBuffer);
+ }
+ }
+
+ // Restore buffer state.
+ mpFrameData->mpBuffer->SetOutOffXPixel(mnOutOffX);
+ mpFrameData->mpBuffer->SetOutOffYPixel(mnOutOffY);
+
+ mpFrameData->mpBuffer->Pop();
+ mpFrameData->mpBuffer->SetSettings(maSettings);
+ if (mbBackground)
+ mpFrameData->mpBuffer->SetBackground(maBackground);
+ else
+ mpFrameData->mpBuffer->SetBackground();
+}
+
+void PaintBufferGuard::SetPaintRect(const tools::Rectangle& rRectangle)
+{
+ m_aPaintRect = rRectangle;
+}
+
+vcl::RenderContext* PaintBufferGuard::GetRenderContext()
+{
+ if (mpFrameData->mpBuffer)
+ return mpFrameData->mpBuffer;
+ else
+ return m_pWindow->GetOutDev();
+}
+}
+
+class PaintHelper
+{
+private:
+ VclPtr<vcl::Window> m_pWindow;
+ std::unique_ptr<vcl::Region> m_pChildRegion;
+ tools::Rectangle m_aSelectionRect;
+ tools::Rectangle m_aPaintRect;
+ vcl::Region m_aPaintRegion;
+ ImplPaintFlags m_nPaintFlags;
+ bool m_bPop : 1;
+ bool m_bRestoreCursor : 1;
+ bool m_bStartedBufferedPaint : 1; ///< This PaintHelper started a buffered paint, and should paint it on the screen when being destructed.
+public:
+ PaintHelper(vcl::Window* pWindow, ImplPaintFlags nPaintFlags);
+ void SetPop()
+ {
+ m_bPop = true;
+ }
+ void SetPaintRect(const tools::Rectangle& rRect)
+ {
+ m_aPaintRect = rRect;
+ }
+ void SetSelectionRect(const tools::Rectangle& rRect)
+ {
+ m_aSelectionRect = rRect;
+ }
+ void SetRestoreCursor(bool bRestoreCursor)
+ {
+ m_bRestoreCursor = bRestoreCursor;
+ }
+ bool GetRestoreCursor() const
+ {
+ return m_bRestoreCursor;
+ }
+ ImplPaintFlags GetPaintFlags() const
+ {
+ return m_nPaintFlags;
+ }
+ vcl::Region& GetPaintRegion()
+ {
+ return m_aPaintRegion;
+ }
+ void DoPaint(const vcl::Region* pRegion);
+
+ /// Start buffered paint: set it up to have the same settings as m_pWindow.
+ void StartBufferedPaint();
+
+ /// Paint the content of the buffer to the current m_pWindow.
+ void PaintBuffer();
+
+ ~PaintHelper();
+};
+
+PaintHelper::PaintHelper(vcl::Window *pWindow, ImplPaintFlags nPaintFlags)
+ : m_pWindow(pWindow)
+ , m_nPaintFlags(nPaintFlags)
+ , m_bPop(false)
+ , m_bRestoreCursor(false)
+ , m_bStartedBufferedPaint(false)
+{
+}
+
+void PaintHelper::StartBufferedPaint()
+{
+ ImplFrameData* pFrameData = m_pWindow->mpWindowImpl->mpFrameData;
+ assert(!pFrameData->mbInBufferedPaint);
+
+ pFrameData->mbInBufferedPaint = true;
+ pFrameData->maBufferedRect = tools::Rectangle();
+ m_bStartedBufferedPaint = true;
+}
+
+void PaintHelper::PaintBuffer()
+{
+ ImplFrameData* pFrameData = m_pWindow->mpWindowImpl->mpFrameData;
+ assert(pFrameData->mbInBufferedPaint);
+ assert(m_bStartedBufferedPaint);
+
+ vcl::PaintBufferGuard aGuard(pFrameData, m_pWindow);
+ aGuard.SetPaintRect(pFrameData->maBufferedRect);
+}
+
+void PaintHelper::DoPaint(const vcl::Region* pRegion)
+{
+ WindowImpl* pWindowImpl = m_pWindow->ImplGetWindowImpl();
+
+ vcl::Region& rWinChildClipRegion = m_pWindow->ImplGetWinChildClipRegion();
+ ImplFrameData* pFrameData = m_pWindow->mpWindowImpl->mpFrameData;
+ if (pWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll || pFrameData->mbInBufferedPaint)
+ {
+ pWindowImpl->maInvalidateRegion = rWinChildClipRegion;
+ }
+ else
+ {
+ if (pRegion)
+ pWindowImpl->maInvalidateRegion.Union( *pRegion );
+
+ if (pWindowImpl->mpWinData && pWindowImpl->mbTrackVisible)
+ /* #98602# need to repaint all children within the
+ * tracking rectangle, so the following invert
+ * operation takes places without traces of the previous
+ * one.
+ */
+ pWindowImpl->maInvalidateRegion.Union(*pWindowImpl->mpWinData->mpTrackRect);
+
+ if (pWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAllChildren)
+ m_pChildRegion.reset( new vcl::Region(pWindowImpl->maInvalidateRegion) );
+ pWindowImpl->maInvalidateRegion.Intersect(rWinChildClipRegion);
+ }
+ pWindowImpl->mnPaintFlags = ImplPaintFlags::NONE;
+ if (pWindowImpl->maInvalidateRegion.IsEmpty())
+ return;
+
+#if HAVE_FEATURE_OPENGL
+ VCL_GL_INFO("PaintHelper::DoPaint on " <<
+ typeid( *m_pWindow ).name() << " '" << m_pWindow->GetText() << "' begin");
+#endif
+ // double-buffering: setup the buffer if it does not exist
+ if (!pFrameData->mbInBufferedPaint && m_pWindow->SupportsDoubleBuffering())
+ StartBufferedPaint();
+
+ // double-buffering: if this window does not support double-buffering,
+ // but we are in the middle of double-buffered paint, we might be
+ // losing information
+ if (pFrameData->mbInBufferedPaint && !m_pWindow->SupportsDoubleBuffering())
+ SAL_WARN("vcl.window", "non-double buffered window in the double-buffered hierarchy, painting directly: " << typeid(*m_pWindow.get()).name());
+
+ if (pFrameData->mbInBufferedPaint && m_pWindow->SupportsDoubleBuffering())
+ {
+ // double-buffering
+ vcl::PaintBufferGuard g(pFrameData, m_pWindow);
+ m_pWindow->ApplySettings(*pFrameData->mpBuffer);
+
+ m_pWindow->PushPaintHelper(this, *pFrameData->mpBuffer);
+ m_pWindow->Paint(*pFrameData->mpBuffer, m_aPaintRect);
+ pFrameData->maBufferedRect.Union(m_aPaintRect);
+ }
+ else
+ {
+ // direct painting
+ Wallpaper aBackground = m_pWindow->GetBackground();
+ m_pWindow->ApplySettings(*m_pWindow->GetOutDev());
+ // Restore bitmap background if it was lost.
+ if (aBackground.IsBitmap() && !m_pWindow->GetBackground().IsBitmap())
+ {
+ m_pWindow->SetBackground(aBackground);
+ }
+ m_pWindow->PushPaintHelper(this, *m_pWindow->GetOutDev());
+ m_pWindow->Paint(*m_pWindow->GetOutDev(), m_aPaintRect);
+ }
+#if HAVE_FEATURE_OPENGL
+ VCL_GL_INFO("PaintHelper::DoPaint end on " <<
+ typeid( *m_pWindow ).name() << " '" << m_pWindow->GetText() << "'");
+#endif
+}
+
+namespace vcl
+{
+
+void RenderTools::DrawSelectionBackground(vcl::RenderContext& rRenderContext, vcl::Window const & rWindow,
+ const tools::Rectangle& rRect, sal_uInt16 nHighlight,
+ bool bChecked, bool bDrawBorder, bool bDrawExtBorderOnly,
+ Color* pSelectionTextColor, tools::Long nCornerRadius, Color const * pPaintColor)
+{
+ if (rRect.IsEmpty())
+ return;
+
+ bool bRoundEdges = nCornerRadius > 0;
+
+ const StyleSettings& rStyles = rRenderContext.GetSettings().GetStyleSettings();
+
+ // colors used for item highlighting
+ Color aSelectionBorderColor(pPaintColor ? *pPaintColor : rStyles.GetHighlightColor());
+ Color aSelectionFillColor(aSelectionBorderColor);
+
+ bool bDark = rStyles.GetFaceColor().IsDark();
+ bool bBright = ( rStyles.GetFaceColor() == COL_WHITE );
+
+ int c1 = aSelectionBorderColor.GetLuminance();
+ int c2 = rWindow.GetBackgroundColor().GetLuminance();
+
+ if (!bDark && !bBright && std::abs(c2 - c1) < (pPaintColor ? 40 : 75))
+ {
+ // contrast too low
+ sal_uInt16 h, s, b;
+ aSelectionFillColor.RGBtoHSB( h, s, b );
+ if( b > 50 ) b -= 40;
+ else b += 40;
+ aSelectionFillColor = Color::HSBtoRGB( h, s, b );
+ aSelectionBorderColor = aSelectionFillColor;
+ }
+
+ if (bRoundEdges)
+ {
+ if (aSelectionBorderColor.IsDark())
+ aSelectionBorderColor.IncreaseLuminance(128);
+ else
+ aSelectionBorderColor.DecreaseLuminance(128);
+ }
+
+ tools::Rectangle aRect(rRect);
+ if (bDrawExtBorderOnly)
+ {
+ aRect.AdjustLeft( -1 );
+ aRect.AdjustTop( -1 );
+ aRect.AdjustRight(1 );
+ aRect.AdjustBottom(1 );
+ }
+ rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
+
+ if (bDrawBorder)
+ rRenderContext.SetLineColor(bDark ? COL_WHITE : (bBright ? COL_BLACK : aSelectionBorderColor));
+ else
+ rRenderContext.SetLineColor();
+
+ sal_uInt16 nPercent = 0;
+ if (!nHighlight)
+ {
+ if (bDark)
+ aSelectionFillColor = COL_BLACK;
+ else
+ nPercent = 80; // just checked (light)
+ }
+ else
+ {
+ if (bChecked && nHighlight == 2)
+ {
+ if (bDark)
+ aSelectionFillColor = COL_LIGHTGRAY;
+ else if (bBright)
+ {
+ aSelectionFillColor = COL_BLACK;
+ rRenderContext.SetLineColor(COL_BLACK);
+ nPercent = 0;
+ }
+ else
+ nPercent = bRoundEdges ? 40 : 20; // selected, pressed or checked ( very dark )
+ }
+ else if (bChecked || nHighlight == 1)
+ {
+ if (bDark)
+ aSelectionFillColor = COL_GRAY;
+ else if (bBright)
+ {
+ aSelectionFillColor = COL_BLACK;
+ rRenderContext.SetLineColor(COL_BLACK);
+ nPercent = 0;
+ }
+ else
+ nPercent = bRoundEdges ? 60 : 35; // selected, pressed or checked ( very dark )
+ }
+ else
+ {
+ if (bDark)
+ aSelectionFillColor = COL_LIGHTGRAY;
+ else if (bBright)
+ {
+ aSelectionFillColor = COL_BLACK;
+ rRenderContext.SetLineColor(COL_BLACK);
+ if (nHighlight == 3)
+ nPercent = 80;
+ else
+ nPercent = 0;
+ }
+ else
+ nPercent = 70; // selected ( dark )
+ }
+ }
+
+ if (bDark && bDrawExtBorderOnly)
+ {
+ rRenderContext.SetFillColor();
+ if (pSelectionTextColor)
+ *pSelectionTextColor = rStyles.GetHighlightTextColor();
+ }
+ else
+ {
+ rRenderContext.SetFillColor(aSelectionFillColor);
+ if (pSelectionTextColor)
+ {
+ Color aTextColor = rWindow.IsControlBackground() ? rWindow.GetControlForeground() : rStyles.GetButtonTextColor();
+ Color aHLTextColor = rStyles.GetHighlightTextColor();
+ int nTextDiff = std::abs(aSelectionFillColor.GetLuminance() - aTextColor.GetLuminance());
+ int nHLDiff = std::abs(aSelectionFillColor.GetLuminance() - aHLTextColor.GetLuminance());
+ *pSelectionTextColor = (nHLDiff >= nTextDiff) ? aHLTextColor : aTextColor;
+ }
+ }
+
+ if (bDark)
+ {
+ rRenderContext.DrawRect(aRect);
+ }
+ else
+ {
+ if (bRoundEdges)
+ {
+ tools::Polygon aPoly(aRect, nCornerRadius, nCornerRadius);
+ tools::PolyPolygon aPolyPoly(aPoly);
+ rRenderContext.DrawTransparent(aPolyPoly, nPercent);
+ }
+ else
+ {
+ tools::Polygon aPoly(aRect);
+ tools::PolyPolygon aPolyPoly(aPoly);
+ rRenderContext.DrawTransparent(aPolyPoly, nPercent);
+ }
+ }
+
+ rRenderContext.Pop(); // LINECOLOR | FILLCOLOR
+}
+
+void Window::PushPaintHelper(PaintHelper *pHelper, vcl::RenderContext& rRenderContext)
+{
+ pHelper->SetPop();
+
+ if ( mpWindowImpl->mpCursor )
+ pHelper->SetRestoreCursor(mpWindowImpl->mpCursor->ImplSuspend());
+
+ GetOutDev()->mbInitClipRegion = true;
+ mpWindowImpl->mbInPaint = true;
+
+ // restore Paint-Region
+ vcl::Region &rPaintRegion = pHelper->GetPaintRegion();
+ rPaintRegion = mpWindowImpl->maInvalidateRegion;
+ tools::Rectangle aPaintRect = rPaintRegion.GetBoundRect();
+
+ // RTL: re-mirror paint rect and region at this window
+ if (GetOutDev()->ImplIsAntiparallel())
+ {
+ rRenderContext.ReMirror(aPaintRect);
+ rRenderContext.ReMirror(rPaintRegion);
+ }
+ aPaintRect = GetOutDev()->ImplDevicePixelToLogic(aPaintRect);
+ mpWindowImpl->mpPaintRegion = &rPaintRegion;
+ mpWindowImpl->maInvalidateRegion.SetEmpty();
+
+ if ((pHelper->GetPaintFlags() & ImplPaintFlags::Erase) && rRenderContext.IsBackground())
+ {
+ if (rRenderContext.IsClipRegion())
+ {
+ vcl::Region aOldRegion = rRenderContext.GetClipRegion();
+ rRenderContext.SetClipRegion();
+ Erase(rRenderContext);
+ rRenderContext.SetClipRegion(aOldRegion);
+ }
+ else
+ Erase(rRenderContext);
+ }
+
+ // #98943# trigger drawing of toolbox selection after all children are painted
+ if (mpWindowImpl->mbDrawSelectionBackground)
+ pHelper->SetSelectionRect(aPaintRect);
+ pHelper->SetPaintRect(aPaintRect);
+}
+
+void Window::PopPaintHelper(PaintHelper const *pHelper)
+{
+ if (mpWindowImpl->mpWinData)
+ {
+ if (mpWindowImpl->mbFocusVisible)
+ ImplInvertFocus(*mpWindowImpl->mpWinData->mpFocusRect);
+ }
+ mpWindowImpl->mbInPaint = false;
+ GetOutDev()->mbInitClipRegion = true;
+ mpWindowImpl->mpPaintRegion = nullptr;
+ if (mpWindowImpl->mpCursor)
+ mpWindowImpl->mpCursor->ImplResume(pHelper->GetRestoreCursor());
+}
+
+} /* namespace vcl */
+
+PaintHelper::~PaintHelper()
+{
+ WindowImpl* pWindowImpl = m_pWindow->ImplGetWindowImpl();
+ if (m_bPop)
+ {
+ m_pWindow->PopPaintHelper(this);
+ }
+
+ ImplFrameData* pFrameData = m_pWindow->mpWindowImpl->mpFrameData;
+ if ( m_nPaintFlags & (ImplPaintFlags::PaintAllChildren | ImplPaintFlags::PaintChildren) )
+ {
+ // Paint from the bottom child window and frontward.
+ vcl::Window* pTempWindow = pWindowImpl->mpLastChild;
+ while (pTempWindow)
+ {
+ if (pTempWindow->mpWindowImpl->mbVisible)
+ pTempWindow->ImplCallPaint(m_pChildRegion.get(), m_nPaintFlags);
+ pTempWindow = pTempWindow->mpWindowImpl->mpPrev;
+ }
+ }
+
+ if ( pWindowImpl->mpWinData && pWindowImpl->mbTrackVisible && (pWindowImpl->mpWinData->mnTrackFlags & ShowTrackFlags::TrackWindow) )
+ /* #98602# need to invert the tracking rect AFTER
+ * the children have painted
+ */
+ m_pWindow->InvertTracking( *pWindowImpl->mpWinData->mpTrackRect, pWindowImpl->mpWinData->mnTrackFlags );
+
+ // double-buffering: paint in case we created the buffer, the children are
+ // already painted inside
+ if (m_bStartedBufferedPaint && pFrameData->mbInBufferedPaint)
+ {
+ PaintBuffer();
+ pFrameData->mbInBufferedPaint = false;
+ pFrameData->maBufferedRect = tools::Rectangle();
+ }
+
+ // #98943# draw toolbox selection
+ if( !m_aSelectionRect.IsEmpty() )
+ m_pWindow->DrawSelectionBackground( m_aSelectionRect, 3, false, true );
+}
+
+namespace vcl {
+
+void Window::ImplCallPaint(const vcl::Region* pRegion, ImplPaintFlags nPaintFlags)
+{
+ // call PrePaint. PrePaint may add to the invalidate region as well as
+ // other parameters used below.
+ PrePaint(*GetOutDev());
+
+ mpWindowImpl->mbPaintFrame = false;
+
+ if (nPaintFlags & ImplPaintFlags::PaintAllChildren)
+ mpWindowImpl->mnPaintFlags |= ImplPaintFlags::Paint | ImplPaintFlags::PaintAllChildren | (nPaintFlags & ImplPaintFlags::PaintAll);
+ if (nPaintFlags & ImplPaintFlags::PaintChildren)
+ mpWindowImpl->mnPaintFlags |= ImplPaintFlags::PaintChildren;
+ if (nPaintFlags & ImplPaintFlags::Erase)
+ mpWindowImpl->mnPaintFlags |= ImplPaintFlags::Erase;
+ if (nPaintFlags & ImplPaintFlags::CheckRtl)
+ mpWindowImpl->mnPaintFlags |= ImplPaintFlags::CheckRtl;
+ if (!mpWindowImpl->mpFirstChild)
+ mpWindowImpl->mnPaintFlags &= ~ImplPaintFlags::PaintAllChildren;
+
+ // If tiled rendering is used, windows are only invalidated, never painted to.
+ if (mpWindowImpl->mbPaintDisabled || comphelper::LibreOfficeKit::isActive())
+ {
+ if (mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll)
+ Invalidate(InvalidateFlags::NoChildren | InvalidateFlags::NoErase | InvalidateFlags::NoTransparent | InvalidateFlags::NoClipChildren);
+ else if ( pRegion )
+ Invalidate(*pRegion, InvalidateFlags::NoChildren | InvalidateFlags::NoErase | InvalidateFlags::NoTransparent | InvalidateFlags::NoClipChildren);
+
+ // call PostPaint before returning
+ PostPaint(*GetOutDev());
+
+ return;
+ }
+
+ nPaintFlags = mpWindowImpl->mnPaintFlags & ~ImplPaintFlags::Paint;
+
+ PaintHelper aHelper(this, nPaintFlags);
+
+ if (mpWindowImpl->mnPaintFlags & ImplPaintFlags::Paint)
+ aHelper.DoPaint(pRegion);
+ else
+ mpWindowImpl->mnPaintFlags = ImplPaintFlags::NONE;
+
+ // call PostPaint
+ PostPaint(*GetOutDev());
+}
+
+void Window::ImplCallOverlapPaint()
+{
+ if (!mpWindowImpl)
+ return;
+
+ // emit overlapping windows first
+ vcl::Window* pTempWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pTempWindow )
+ {
+ if ( pTempWindow->mpWindowImpl->mbReallyVisible )
+ pTempWindow->ImplCallOverlapPaint();
+ pTempWindow = pTempWindow->mpWindowImpl->mpNext;
+ }
+
+ // only then ourself
+ if ( mpWindowImpl->mnPaintFlags & (ImplPaintFlags::Paint | ImplPaintFlags::PaintChildren) )
+ {
+ // RTL: notify ImplCallPaint to check for re-mirroring
+ // because we were called from the Sal layer
+ ImplCallPaint(nullptr, mpWindowImpl->mnPaintFlags /*| ImplPaintFlags::CheckRtl */);
+ }
+}
+
+IMPL_LINK_NOARG(Window, ImplHandlePaintHdl, Timer *, void)
+{
+ comphelper::ProfileZone aZone("VCL idle re-paint");
+
+ // save paint events until layout is done
+ if (IsSystemWindow() && static_cast<const SystemWindow*>(this)->hasPendingLayout())
+ {
+ mpWindowImpl->mpFrameData->maPaintIdle.Start();
+ return;
+ }
+
+ // save paint events until resizing or initial sizing done
+ if (mpWindowImpl->mbFrame &&
+ mpWindowImpl->mpFrameData->maResizeIdle.IsActive())
+ {
+ mpWindowImpl->mpFrameData->maPaintIdle.Start();
+ }
+ else if ( mpWindowImpl->mbReallyVisible )
+ {
+ ImplCallOverlapPaint();
+ if (comphelper::LibreOfficeKit::isActive() &&
+ mpWindowImpl->mpFrameData->maPaintIdle.IsActive())
+ mpWindowImpl->mpFrameData->maPaintIdle.Stop();
+ }
+}
+
+IMPL_LINK_NOARG(Window, ImplHandleResizeTimerHdl, Timer *, void)
+{
+ comphelper::ProfileZone aZone("VCL idle resize");
+
+ if( mpWindowImpl->mbReallyVisible )
+ {
+ ImplCallResize();
+ if( mpWindowImpl->mpFrameData->maPaintIdle.IsActive() )
+ {
+ mpWindowImpl->mpFrameData->maPaintIdle.Stop();
+ mpWindowImpl->mpFrameData->maPaintIdle.Invoke( nullptr );
+ }
+ }
+}
+
+void Window::ImplInvalidateFrameRegion( const vcl::Region* pRegion, InvalidateFlags nFlags )
+{
+ // set PAINTCHILDREN for all parent windows till the first OverlapWindow
+ if ( !ImplIsOverlapWindow() )
+ {
+ vcl::Window* pTempWindow = this;
+ ImplPaintFlags nTranspPaint = IsPaintTransparent() ? ImplPaintFlags::Paint : ImplPaintFlags::NONE;
+ do
+ {
+ pTempWindow = pTempWindow->ImplGetParent();
+ if ( pTempWindow->mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintChildren )
+ break;
+ pTempWindow->mpWindowImpl->mnPaintFlags |= ImplPaintFlags::PaintChildren | nTranspPaint;
+ if( ! pTempWindow->IsPaintTransparent() )
+ nTranspPaint = ImplPaintFlags::NONE;
+ }
+ while ( !pTempWindow->ImplIsOverlapWindow() );
+ }
+
+ // set Paint-Flags
+ mpWindowImpl->mnPaintFlags |= ImplPaintFlags::Paint;
+ if ( nFlags & InvalidateFlags::Children )
+ mpWindowImpl->mnPaintFlags |= ImplPaintFlags::PaintAllChildren;
+ if ( !(nFlags & InvalidateFlags::NoErase) )
+ mpWindowImpl->mnPaintFlags |= ImplPaintFlags::Erase;
+
+ if ( !pRegion )
+ mpWindowImpl->mnPaintFlags |= ImplPaintFlags::PaintAll;
+ else if ( !(mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll) )
+ {
+ // if not everything has to be redrawn, add the region to it
+ mpWindowImpl->maInvalidateRegion.Union( *pRegion );
+ }
+
+ // Handle transparent windows correctly: invalidate must be done on the first opaque parent
+ if( ((IsPaintTransparent() && !(nFlags & InvalidateFlags::NoTransparent)) || (nFlags & InvalidateFlags::Transparent) )
+ && ImplGetParent() )
+ {
+ vcl::Window *pParent = ImplGetParent();
+ while( pParent && pParent->IsPaintTransparent() )
+ pParent = pParent->ImplGetParent();
+ if( pParent )
+ {
+ vcl::Region *pChildRegion;
+ if ( mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll )
+ // invalidate the whole child window region in the parent
+ pChildRegion = &ImplGetWinChildClipRegion();
+ else
+ // invalidate the same region in the parent that has to be repainted in the child
+ pChildRegion = &mpWindowImpl->maInvalidateRegion;
+
+ nFlags |= InvalidateFlags::Children; // paint should also be done on all children
+ nFlags &= ~InvalidateFlags::NoErase; // parent should paint and erase to create proper background
+ pParent->ImplInvalidateFrameRegion( pChildRegion, nFlags );
+ }
+ }
+
+ if ( !mpWindowImpl->mpFrameData->maPaintIdle.IsActive() )
+ mpWindowImpl->mpFrameData->maPaintIdle.Start();
+}
+
+void Window::ImplInvalidateOverlapFrameRegion( const vcl::Region& rRegion )
+{
+ vcl::Region aRegion = rRegion;
+
+ ImplClipBoundaries( aRegion, true, true );
+ if ( !aRegion.IsEmpty() )
+ ImplInvalidateFrameRegion( &aRegion, InvalidateFlags::Children );
+
+ // now we invalidate the overlapping windows
+ vcl::Window* pTempWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pTempWindow )
+ {
+ if ( pTempWindow->IsVisible() )
+ pTempWindow->ImplInvalidateOverlapFrameRegion( rRegion );
+
+ pTempWindow = pTempWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplInvalidateParentFrameRegion( const vcl::Region& rRegion )
+{
+ if ( mpWindowImpl->mbOverlapWin )
+ mpWindowImpl->mpFrameWindow->ImplInvalidateOverlapFrameRegion( rRegion );
+ else
+ {
+ if( ImplGetParent() )
+ ImplGetParent()->ImplInvalidateFrameRegion( &rRegion, InvalidateFlags::Children );
+ }
+}
+
+void Window::ImplInvalidate( const vcl::Region* pRegion, InvalidateFlags nFlags )
+{
+ // check what has to be redrawn
+ bool bInvalidateAll = !pRegion;
+
+ // take Transparent-Invalidate into account
+ vcl::Window* pOpaqueWindow = this;
+ if ( (mpWindowImpl->mbPaintTransparent && !(nFlags & InvalidateFlags::NoTransparent)) || (nFlags & InvalidateFlags::Transparent) )
+ {
+ vcl::Window* pTempWindow = pOpaqueWindow->ImplGetParent();
+ while ( pTempWindow )
+ {
+ if ( !pTempWindow->IsPaintTransparent() )
+ {
+ pOpaqueWindow = pTempWindow;
+ nFlags |= InvalidateFlags::Children;
+ bInvalidateAll = false;
+ break;
+ }
+
+ if ( pTempWindow->ImplIsOverlapWindow() )
+ break;
+
+ pTempWindow = pTempWindow->ImplGetParent();
+ }
+ }
+
+ // assemble region
+ InvalidateFlags nOrgFlags = nFlags;
+ if ( !(nFlags & (InvalidateFlags::Children | InvalidateFlags::NoChildren)) )
+ {
+ if ( GetStyle() & WB_CLIPCHILDREN )
+ nFlags |= InvalidateFlags::NoChildren;
+ else
+ nFlags |= InvalidateFlags::Children;
+ }
+ if ( (nFlags & InvalidateFlags::NoChildren) && mpWindowImpl->mpFirstChild )
+ bInvalidateAll = false;
+ if ( bInvalidateAll )
+ ImplInvalidateFrameRegion( nullptr, nFlags );
+ else
+ {
+ vcl::Region aRegion( GetOutputRectPixel() );
+ if ( pRegion )
+ {
+ // RTL: remirror region before intersecting it
+ if ( GetOutDev()->ImplIsAntiparallel() )
+ {
+ const OutputDevice *pOutDev = GetOutDev();
+
+ vcl::Region aRgn( *pRegion );
+ pOutDev->ReMirror( aRgn );
+ aRegion.Intersect( aRgn );
+ }
+ else
+ aRegion.Intersect( *pRegion );
+ }
+ ImplClipBoundaries( aRegion, true, true );
+ if ( nFlags & InvalidateFlags::NoChildren )
+ {
+ nFlags &= ~InvalidateFlags::Children;
+ if ( !(nFlags & InvalidateFlags::NoClipChildren) )
+ {
+ if ( nOrgFlags & InvalidateFlags::NoChildren )
+ ImplClipAllChildren( aRegion );
+ else
+ {
+ if ( ImplClipChildren( aRegion ) )
+ nFlags |= InvalidateFlags::Children;
+ }
+ }
+ }
+ if ( !aRegion.IsEmpty() )
+ ImplInvalidateFrameRegion( &aRegion, nFlags ); // transparency is handled here, pOpaqueWindow not required
+ }
+
+ if ( nFlags & InvalidateFlags::Update )
+ pOpaqueWindow->PaintImmediately(); // start painting at the opaque parent
+}
+
+void Window::ImplMoveInvalidateRegion( const tools::Rectangle& rRect,
+ tools::Long nHorzScroll, tools::Long nVertScroll,
+ bool bChildren )
+{
+ if ( (mpWindowImpl->mnPaintFlags & (ImplPaintFlags::Paint | ImplPaintFlags::PaintAll)) == ImplPaintFlags::Paint )
+ {
+ vcl::Region aTempRegion = mpWindowImpl->maInvalidateRegion;
+ aTempRegion.Intersect( rRect );
+ aTempRegion.Move( nHorzScroll, nVertScroll );
+ mpWindowImpl->maInvalidateRegion.Union( aTempRegion );
+ }
+
+ if ( bChildren && (mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintChildren) )
+ {
+ vcl::Window* pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ pWindow->ImplMoveInvalidateRegion( rRect, nHorzScroll, nVertScroll, true );
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+ }
+}
+
+void Window::ImplMoveAllInvalidateRegions( const tools::Rectangle& rRect,
+ tools::Long nHorzScroll, tools::Long nVertScroll,
+ bool bChildren )
+{
+ // also shift Paint-Region when paints need processing
+ ImplMoveInvalidateRegion( rRect, nHorzScroll, nVertScroll, bChildren );
+ // Paint-Region should be shifted, as drawn by the parents
+ if ( ImplIsOverlapWindow() )
+ return;
+
+ vcl::Region aPaintAllRegion;
+ vcl::Window* pPaintAllWindow = this;
+ do
+ {
+ pPaintAllWindow = pPaintAllWindow->ImplGetParent();
+ if ( pPaintAllWindow->mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAllChildren )
+ {
+ if ( pPaintAllWindow->mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll )
+ {
+ aPaintAllRegion.SetEmpty();
+ break;
+ }
+ else
+ aPaintAllRegion.Union( pPaintAllWindow->mpWindowImpl->maInvalidateRegion );
+ }
+ }
+ while ( !pPaintAllWindow->ImplIsOverlapWindow() );
+ if ( !aPaintAllRegion.IsEmpty() )
+ {
+ aPaintAllRegion.Move( nHorzScroll, nVertScroll );
+ InvalidateFlags nPaintFlags = InvalidateFlags::NONE;
+ if ( bChildren )
+ nPaintFlags |= InvalidateFlags::Children;
+ ImplInvalidateFrameRegion( &aPaintAllRegion, nPaintFlags );
+ }
+}
+
+void Window::ImplValidateFrameRegion( const vcl::Region* pRegion, ValidateFlags nFlags )
+{
+ if ( !pRegion )
+ mpWindowImpl->maInvalidateRegion.SetEmpty();
+ else
+ {
+ // when all child windows have to be drawn we need to invalidate them before doing so
+ if ( (mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAllChildren) && mpWindowImpl->mpFirstChild )
+ {
+ vcl::Region aChildRegion = mpWindowImpl->maInvalidateRegion;
+ if ( mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll )
+ {
+ aChildRegion = GetOutputRectPixel();
+ }
+ vcl::Window* pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->Invalidate( aChildRegion, InvalidateFlags::Children | InvalidateFlags::NoTransparent );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+ }
+ if ( mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAll )
+ {
+ mpWindowImpl->maInvalidateRegion = GetOutputRectPixel();
+ }
+ mpWindowImpl->maInvalidateRegion.Exclude( *pRegion );
+ }
+ mpWindowImpl->mnPaintFlags &= ~ImplPaintFlags::PaintAll;
+
+ if ( nFlags & ValidateFlags::Children )
+ {
+ vcl::Window* pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->ImplValidateFrameRegion( pRegion, nFlags );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+ }
+}
+
+void Window::ImplValidate()
+{
+ // assemble region
+ bool bValidateAll = true;
+ ValidateFlags nFlags = ValidateFlags::NONE;
+ if ( GetStyle() & WB_CLIPCHILDREN )
+ nFlags |= ValidateFlags::NoChildren;
+ else
+ nFlags |= ValidateFlags::Children;
+ if ( (nFlags & ValidateFlags::NoChildren) && mpWindowImpl->mpFirstChild )
+ bValidateAll = false;
+ if ( bValidateAll )
+ ImplValidateFrameRegion( nullptr, nFlags );
+ else
+ {
+ vcl::Region aRegion( GetOutputRectPixel() );
+ ImplClipBoundaries( aRegion, true, true );
+ if ( nFlags & ValidateFlags::NoChildren )
+ {
+ nFlags &= ~ValidateFlags::Children;
+ if ( ImplClipChildren( aRegion ) )
+ nFlags |= ValidateFlags::Children;
+ }
+ if ( !aRegion.IsEmpty() )
+ ImplValidateFrameRegion( &aRegion, nFlags );
+ }
+}
+
+void Window::ImplUpdateAll()
+{
+ if ( !mpWindowImpl || !mpWindowImpl->mbReallyVisible )
+ return;
+
+ bool bFlush = false;
+ if ( mpWindowImpl->mpFrameWindow->mpWindowImpl->mbPaintFrame )
+ {
+ Point aPoint( 0, 0 );
+ vcl::Region aRegion( tools::Rectangle( aPoint, GetOutputSizePixel() ) );
+ ImplInvalidateOverlapFrameRegion( aRegion );
+ if ( mpWindowImpl->mbFrame || (mpWindowImpl->mpBorderWindow && mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame) )
+ bFlush = true;
+ }
+
+ // an update changes the OverlapWindow, such that for later paints
+ // not too much has to be drawn, if ALLCHILDREN etc. is set
+ vcl::Window* pWindow = ImplGetFirstOverlapWindow();
+ pWindow->ImplCallOverlapPaint();
+
+ if ( bFlush )
+ GetOutDev()->Flush();
+}
+
+void Window::PrePaint(vcl::RenderContext& /*rRenderContext*/)
+{
+}
+
+void Window::PostPaint(vcl::RenderContext& /*rRenderContext*/)
+{
+}
+
+void Window::Paint(vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& rRect)
+{
+ CallEventListeners(VclEventId::WindowPaint, const_cast<tools::Rectangle *>(&rRect));
+}
+
+void Window::SetPaintTransparent( bool bTransparent )
+{
+ // transparency is not useful for frames as the background would have to be provided by a different frame
+ if( bTransparent && mpWindowImpl->mbFrame )
+ return;
+
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->SetPaintTransparent( bTransparent );
+
+ mpWindowImpl->mbPaintTransparent = bTransparent;
+}
+
+void Window::SetWindowRegionPixel()
+{
+
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->SetWindowRegionPixel();
+ else if( mpWindowImpl->mbFrame )
+ {
+ mpWindowImpl->maWinRegion = vcl::Region(true);
+ mpWindowImpl->mbWinRegion = false;
+ mpWindowImpl->mpFrame->ResetClipRegion();
+ }
+ else
+ {
+ if ( mpWindowImpl->mbWinRegion )
+ {
+ mpWindowImpl->maWinRegion = vcl::Region(true);
+ mpWindowImpl->mbWinRegion = false;
+ ImplSetClipFlag();
+
+ if ( IsReallyVisible() )
+ {
+ vcl::Region aRegion( GetOutputRectPixel() );
+ ImplInvalidateParentFrameRegion( aRegion );
+ }
+ }
+ }
+}
+
+void Window::SetWindowRegionPixel( const vcl::Region& rRegion )
+{
+
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->SetWindowRegionPixel( rRegion );
+ else if( mpWindowImpl->mbFrame )
+ {
+ if( !rRegion.IsNull() )
+ {
+ mpWindowImpl->maWinRegion = rRegion;
+ mpWindowImpl->mbWinRegion = ! rRegion.IsEmpty();
+
+ if( mpWindowImpl->mbWinRegion )
+ {
+ // set/update ClipRegion
+ RectangleVector aRectangles;
+ mpWindowImpl->maWinRegion.GetRegionRectangles(aRectangles);
+ mpWindowImpl->mpFrame->BeginSetClipRegion(aRectangles.size());
+
+ for (auto const& rectangle : aRectangles)
+ {
+ mpWindowImpl->mpFrame->UnionClipRegion(
+ rectangle.Left(),
+ rectangle.Top(),
+ rectangle.GetWidth(), // orig nWidth was ((R - L) + 1), same as GetWidth does
+ rectangle.GetHeight()); // same for height
+ }
+
+ mpWindowImpl->mpFrame->EndSetClipRegion();
+ }
+ else
+ SetWindowRegionPixel();
+ }
+ else
+ SetWindowRegionPixel();
+ }
+ else
+ {
+ if ( rRegion.IsNull() )
+ {
+ if ( mpWindowImpl->mbWinRegion )
+ {
+ mpWindowImpl->maWinRegion = vcl::Region(true);
+ mpWindowImpl->mbWinRegion = false;
+ ImplSetClipFlag();
+ }
+ }
+ else
+ {
+ mpWindowImpl->maWinRegion = rRegion;
+ mpWindowImpl->mbWinRegion = true;
+ ImplSetClipFlag();
+ }
+
+ if ( IsReallyVisible() )
+ {
+ vcl::Region aRegion( GetOutputRectPixel() );
+ ImplInvalidateParentFrameRegion( aRegion );
+ }
+ }
+}
+
+vcl::Region Window::GetPaintRegion() const
+{
+
+ if ( mpWindowImpl->mpPaintRegion )
+ {
+ vcl::Region aRegion = *mpWindowImpl->mpPaintRegion;
+ aRegion.Move( -GetOutDev()->mnOutOffX, -GetOutDev()->mnOutOffY );
+ return PixelToLogic( aRegion );
+ }
+ else
+ {
+ vcl::Region aPaintRegion(true);
+ return aPaintRegion;
+ }
+}
+
+void Window::Invalidate( InvalidateFlags nFlags )
+{
+ if ( !comphelper::LibreOfficeKit::isActive() && (!GetOutDev()->IsDeviceOutputNecessary() || !GetOutDev()->mnOutWidth || !GetOutDev()->mnOutHeight) )
+ return;
+
+ ImplInvalidate( nullptr, nFlags );
+ LogicInvalidate(nullptr);
+}
+
+void Window::Invalidate( const tools::Rectangle& rRect, InvalidateFlags nFlags )
+{
+ if ( !comphelper::LibreOfficeKit::isActive() && (!GetOutDev()->IsDeviceOutputNecessary() || !GetOutDev()->mnOutWidth || !GetOutDev()->mnOutHeight) )
+ return;
+
+ OutputDevice *pOutDev = GetOutDev();
+ tools::Rectangle aRect = pOutDev->ImplLogicToDevicePixel( rRect );
+ if ( !aRect.IsEmpty() )
+ {
+ vcl::Region aRegion( aRect );
+ ImplInvalidate( &aRegion, nFlags );
+ tools::Rectangle aLogicRectangle(rRect);
+ LogicInvalidate(&aLogicRectangle);
+ }
+}
+
+void Window::Invalidate( const vcl::Region& rRegion, InvalidateFlags nFlags )
+{
+ if ( !comphelper::LibreOfficeKit::isActive() && (!GetOutDev()->IsDeviceOutputNecessary() || !GetOutDev()->mnOutWidth || !GetOutDev()->mnOutHeight) )
+ return;
+
+ if ( rRegion.IsNull() )
+ {
+ ImplInvalidate( nullptr, nFlags );
+ LogicInvalidate(nullptr);
+ }
+ else
+ {
+ vcl::Region aRegion = GetOutDev()->ImplPixelToDevicePixel( LogicToPixel( rRegion ) );
+ if ( !aRegion.IsEmpty() )
+ {
+ ImplInvalidate( &aRegion, nFlags );
+ tools::Rectangle aLogicRectangle = rRegion.GetBoundRect();
+ LogicInvalidate(&aLogicRectangle);
+ }
+ }
+}
+
+void Window::LogicInvalidate(const tools::Rectangle* pRectangle)
+{
+ if(pRectangle)
+ {
+ tools::Rectangle aRect = GetOutDev()->ImplLogicToDevicePixel( *pRectangle );
+ PixelInvalidate(&aRect);
+ }
+ else
+ PixelInvalidate(nullptr);
+}
+
+void Window::PixelInvalidate(const tools::Rectangle* pRectangle)
+{
+ if (comphelper::LibreOfficeKit::isDialogPainting() || !comphelper::LibreOfficeKit::isActive())
+ return;
+
+ Size aSize = GetSizePixel();
+ if (aSize.IsEmpty())
+ return;
+
+ if (const vcl::ILibreOfficeKitNotifier* pNotifier = GetLOKNotifier())
+ {
+ // In case we are routing the window, notify the client
+ std::vector<vcl::LOKPayloadItem> aPayload;
+ tools::Rectangle aRect(Point(0, 0), aSize);
+ if (pRectangle)
+ aRect = *pRectangle;
+
+ if (IsRTLEnabled() && GetOutDev() && !GetOutDev()->ImplIsAntiparallel())
+ GetOutDev()->ReMirror(aRect);
+
+ aPayload.emplace_back("rectangle", aRect.toString());
+
+ pNotifier->notifyWindow(GetLOKWindowId(), "invalidate", aPayload);
+ }
+ // Added for dialog items. Pass invalidation to the parent window.
+ else if (VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier())
+ {
+ const tools::Rectangle aRect(Point(GetOutOffXPixel(), GetOutOffYPixel()), GetSizePixel());
+ pParent->PixelInvalidate(&aRect);
+ }
+}
+
+void Window::Validate()
+{
+ if ( !comphelper::LibreOfficeKit::isActive() && (!GetOutDev()->IsDeviceOutputNecessary() || !GetOutDev()->mnOutWidth || !GetOutDev()->mnOutHeight) )
+ return;
+
+ ImplValidate();
+}
+
+bool Window::HasPaintEvent() const
+{
+
+ if ( !mpWindowImpl->mbReallyVisible )
+ return false;
+
+ if ( mpWindowImpl->mpFrameWindow->mpWindowImpl->mbPaintFrame )
+ return true;
+
+ if ( mpWindowImpl->mnPaintFlags & ImplPaintFlags::Paint )
+ return true;
+
+ if ( !ImplIsOverlapWindow() )
+ {
+ const vcl::Window* pTempWindow = this;
+ do
+ {
+ pTempWindow = pTempWindow->ImplGetParent();
+ if ( pTempWindow->mpWindowImpl->mnPaintFlags & (ImplPaintFlags::PaintChildren | ImplPaintFlags::PaintAllChildren) )
+ return true;
+ }
+ while ( !pTempWindow->ImplIsOverlapWindow() );
+ }
+
+ return false;
+}
+
+void Window::PaintImmediately()
+{
+ if (!mpWindowImpl)
+ return;
+
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ mpWindowImpl->mpBorderWindow->PaintImmediately();
+ return;
+ }
+
+ if ( !mpWindowImpl->mbReallyVisible )
+ return;
+
+ bool bFlush = false;
+ if ( mpWindowImpl->mpFrameWindow->mpWindowImpl->mbPaintFrame )
+ {
+ Point aPoint( 0, 0 );
+ vcl::Region aRegion( tools::Rectangle( aPoint, GetOutputSizePixel() ) );
+ ImplInvalidateOverlapFrameRegion( aRegion );
+ if ( mpWindowImpl->mbFrame || (mpWindowImpl->mpBorderWindow && mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame) )
+ bFlush = true;
+ }
+
+ // First we should skip all windows which are Paint-Transparent
+ vcl::Window* pUpdateWindow = this;
+ vcl::Window* pWindow = pUpdateWindow;
+ while ( !pWindow->ImplIsOverlapWindow() )
+ {
+ if ( !pWindow->mpWindowImpl->mbPaintTransparent )
+ {
+ pUpdateWindow = pWindow;
+ break;
+ }
+ pWindow = pWindow->ImplGetParent();
+ }
+ // In order to limit drawing, an update only draws the window which
+ // has PAINTALLCHILDREN set
+ pWindow = pUpdateWindow;
+ do
+ {
+ if ( pWindow->mpWindowImpl->mnPaintFlags & ImplPaintFlags::PaintAllChildren )
+ pUpdateWindow = pWindow;
+ if ( pWindow->ImplIsOverlapWindow() )
+ break;
+ pWindow = pWindow->ImplGetParent();
+ }
+ while ( pWindow );
+
+ // if there is something to paint, trigger a Paint
+ if ( pUpdateWindow->mpWindowImpl->mnPaintFlags & (ImplPaintFlags::Paint | ImplPaintFlags::PaintChildren) )
+ {
+ VclPtr<vcl::Window> xWindow(this);
+
+ // trigger an update also for system windows on top of us,
+ // otherwise holes would remain
+ vcl::Window* pUpdateOverlapWindow = ImplGetFirstOverlapWindow();
+ if (pUpdateOverlapWindow->mpWindowImpl)
+ pUpdateOverlapWindow = pUpdateOverlapWindow->mpWindowImpl->mpFirstOverlap;
+ else
+ pUpdateOverlapWindow = nullptr;
+ while ( pUpdateOverlapWindow )
+ {
+ pUpdateOverlapWindow->PaintImmediately();
+ pUpdateOverlapWindow = pUpdateOverlapWindow->mpWindowImpl->mpNext;
+ }
+
+ pUpdateWindow->ImplCallPaint(nullptr, pUpdateWindow->mpWindowImpl->mnPaintFlags);
+
+ if (comphelper::LibreOfficeKit::isActive() && pUpdateWindow->GetParentDialog())
+ pUpdateWindow->LogicInvalidate(nullptr);
+
+ if (xWindow->isDisposed())
+ return;
+
+ bFlush = true;
+ }
+
+ if ( bFlush )
+ GetOutDev()->Flush();
+}
+
+void Window::ImplPaintToDevice( OutputDevice* i_pTargetOutDev, const Point& i_rPos )
+{
+ // Special drawing when called through LOKit
+ // TODO: Move to its own method
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ VclPtrInstance<VirtualDevice> pDevice(*i_pTargetOutDev);
+ pDevice->EnableRTL(IsRTLEnabled());
+
+ Size aSize(GetOutputSizePixel());
+ pDevice->SetOutputSizePixel(aSize);
+
+ vcl::Font aCopyFont = GetFont();
+ pDevice->SetFont(aCopyFont);
+
+ pDevice->SetTextColor(GetTextColor());
+ if (GetOutDev()->IsLineColor())
+ pDevice->SetLineColor(GetOutDev()->GetLineColor());
+ else
+ pDevice->SetLineColor();
+
+ if (GetOutDev()->IsFillColor())
+ pDevice->SetFillColor(GetOutDev()->GetFillColor());
+ else
+ pDevice->SetFillColor();
+
+ if (IsTextLineColor())
+ pDevice->SetTextLineColor(GetTextLineColor());
+ else
+ pDevice->SetTextLineColor();
+
+ if (IsOverlineColor())
+ pDevice->SetOverlineColor(GetOverlineColor());
+ else
+ pDevice->SetOverlineColor();
+
+ if (IsTextFillColor())
+ pDevice->SetTextFillColor(GetTextFillColor());
+ else
+ pDevice->SetTextFillColor();
+
+ pDevice->SetTextAlign(GetTextAlign());
+ pDevice->SetRasterOp(GetOutDev()->GetRasterOp());
+
+ tools::Rectangle aPaintRect(Point(), GetOutputSizePixel());
+
+ vcl::Region aClipRegion(GetOutDev()->GetClipRegion());
+ pDevice->SetClipRegion();
+ aClipRegion.Intersect(aPaintRect);
+ pDevice->SetClipRegion(aClipRegion);
+
+ if (!IsPaintTransparent() && IsBackground() && ! (GetParentClipMode() & ParentClipMode::NoClip))
+ Erase(*pDevice);
+
+ pDevice->SetMapMode(GetMapMode());
+
+ Paint(*pDevice, tools::Rectangle(Point(), GetOutputSizePixel()));
+
+ i_pTargetOutDev->DrawOutDev(i_rPos, aSize, Point(), pDevice->PixelToLogic(aSize), *pDevice);
+
+ bool bHasMirroredGraphics = pDevice->HasMirroredGraphics();
+
+ // get rid of virtual device now so they don't pile up during recursive calls
+ pDevice.disposeAndClear();
+
+
+ for( vcl::Window* pChild = mpWindowImpl->mpFirstChild; pChild; pChild = pChild->mpWindowImpl->mpNext )
+ {
+ if( pChild->mpWindowImpl->mpFrame == mpWindowImpl->mpFrame && pChild->IsVisible() )
+ {
+ tools::Long nDeltaX = pChild->GetOutDev()->mnOutOffX - GetOutDev()->mnOutOffX;
+ if( bHasMirroredGraphics )
+ nDeltaX = GetOutDev()->mnOutWidth - nDeltaX - pChild->GetOutDev()->mnOutWidth;
+
+ tools::Long nDeltaY = pChild->GetOutOffYPixel() - GetOutOffYPixel();
+
+ Point aPos( i_rPos );
+ aPos += Point(nDeltaX, nDeltaY);
+
+ pChild->ImplPaintToDevice( i_pTargetOutDev, aPos );
+ }
+ }
+ return;
+ }
+
+
+ bool bRVisible = mpWindowImpl->mbReallyVisible;
+ mpWindowImpl->mbReallyVisible = mpWindowImpl->mbVisible;
+ bool bDevOutput = GetOutDev()->mbDevOutput;
+ GetOutDev()->mbDevOutput = true;
+
+ const OutputDevice *pOutDev = GetOutDev();
+ tools::Long nOldDPIX = pOutDev->GetDPIX();
+ tools::Long nOldDPIY = pOutDev->GetDPIY();
+ GetOutDev()->mnDPIX = i_pTargetOutDev->GetDPIX();
+ GetOutDev()->mnDPIY = i_pTargetOutDev->GetDPIY();
+ bool bOutput = GetOutDev()->IsOutputEnabled();
+ GetOutDev()->EnableOutput();
+
+ SAL_WARN_IF( GetMapMode().GetMapUnit() != MapUnit::MapPixel, "vcl.window", "MapMode must be PIXEL based" );
+ if ( GetMapMode().GetMapUnit() != MapUnit::MapPixel )
+ return;
+
+ // preserve graphicsstate
+ GetOutDev()->Push();
+ vcl::Region aClipRegion( GetOutDev()->GetClipRegion() );
+ GetOutDev()->SetClipRegion();
+
+ GDIMetaFile* pOldMtf = GetOutDev()->GetConnectMetaFile();
+ GDIMetaFile aMtf;
+ GetOutDev()->SetConnectMetaFile( &aMtf );
+
+ // put a push action to metafile
+ GetOutDev()->Push();
+ // copy graphics state to metafile
+ vcl::Font aCopyFont = GetFont();
+ if( nOldDPIX != GetOutDev()->mnDPIX || nOldDPIY != GetOutDev()->mnDPIY )
+ {
+ aCopyFont.SetFontHeight( aCopyFont.GetFontHeight() * GetOutDev()->mnDPIY / nOldDPIY );
+ aCopyFont.SetAverageFontWidth( aCopyFont.GetAverageFontWidth() * GetOutDev()->mnDPIX / nOldDPIX );
+ }
+ SetFont( aCopyFont );
+ SetTextColor( GetTextColor() );
+ if( GetOutDev()->IsLineColor() )
+ GetOutDev()->SetLineColor( GetOutDev()->GetLineColor() );
+ else
+ GetOutDev()->SetLineColor();
+ if( GetOutDev()->IsFillColor() )
+ GetOutDev()->SetFillColor( GetOutDev()->GetFillColor() );
+ else
+ GetOutDev()->SetFillColor();
+ if( IsTextLineColor() )
+ SetTextLineColor( GetTextLineColor() );
+ else
+ SetTextLineColor();
+ if( IsOverlineColor() )
+ SetOverlineColor( GetOverlineColor() );
+ else
+ SetOverlineColor();
+ if( IsTextFillColor() )
+ SetTextFillColor( GetTextFillColor() );
+ else
+ SetTextFillColor();
+ SetTextAlign( GetTextAlign() );
+ GetOutDev()->SetRasterOp( GetOutDev()->GetRasterOp() );
+ if( GetOutDev()->IsRefPoint() )
+ GetOutDev()->SetRefPoint( GetOutDev()->GetRefPoint() );
+ else
+ GetOutDev()->SetRefPoint();
+ GetOutDev()->SetLayoutMode( GetOutDev()->GetLayoutMode() );
+
+ GetOutDev()->SetDigitLanguage( GetOutDev()->GetDigitLanguage() );
+ tools::Rectangle aPaintRect(Point(0, 0), GetOutputSizePixel());
+ aClipRegion.Intersect( aPaintRect );
+ GetOutDev()->SetClipRegion( aClipRegion );
+
+ // do the actual paint
+
+ // background
+ if( ! IsPaintTransparent() && IsBackground() && ! (GetParentClipMode() & ParentClipMode::NoClip ) )
+ {
+ Erase(*GetOutDev());
+ }
+ // foreground
+ Paint(*GetOutDev(), aPaintRect);
+ // put a pop action to metafile
+ GetOutDev()->Pop();
+
+ GetOutDev()->SetConnectMetaFile( pOldMtf );
+ GetOutDev()->EnableOutput( bOutput );
+ mpWindowImpl->mbReallyVisible = bRVisible;
+
+ // paint metafile to VDev
+ VclPtrInstance<VirtualDevice> pMaskedDevice(*i_pTargetOutDev,
+ DeviceFormat::WITH_ALPHA);
+
+ pMaskedDevice->SetOutputSizePixel( GetOutputSizePixel() );
+ pMaskedDevice->EnableRTL( IsRTLEnabled() );
+ aMtf.WindStart();
+ aMtf.Play(*pMaskedDevice);
+ BitmapEx aBmpEx( pMaskedDevice->GetBitmapEx( Point( 0, 0 ), aPaintRect.GetSize() ) );
+ i_pTargetOutDev->DrawBitmapEx( i_rPos, aBmpEx );
+ // get rid of virtual device now so they don't pile up during recursive calls
+ pMaskedDevice.disposeAndClear();
+
+ for( vcl::Window* pChild = mpWindowImpl->mpFirstChild; pChild; pChild = pChild->mpWindowImpl->mpNext )
+ {
+ if( pChild->mpWindowImpl->mpFrame == mpWindowImpl->mpFrame && pChild->IsVisible() )
+ {
+ tools::Long nDeltaX = pChild->GetOutDev()->mnOutOffX - GetOutDev()->mnOutOffX;
+
+ if( pOutDev->HasMirroredGraphics() )
+ nDeltaX = GetOutDev()->mnOutWidth - nDeltaX - pChild->GetOutDev()->mnOutWidth;
+ tools::Long nDeltaY = pChild->GetOutOffYPixel() - GetOutOffYPixel();
+ Point aPos( i_rPos );
+ Point aDelta( nDeltaX, nDeltaY );
+ aPos += aDelta;
+ pChild->ImplPaintToDevice( i_pTargetOutDev, aPos );
+ }
+ }
+
+ // restore graphics state
+ GetOutDev()->Pop();
+
+ GetOutDev()->EnableOutput( bOutput );
+ mpWindowImpl->mbReallyVisible = bRVisible;
+ GetOutDev()->mbDevOutput = bDevOutput;
+ GetOutDev()->mnDPIX = nOldDPIX;
+ GetOutDev()->mnDPIY = nOldDPIY;
+}
+
+void Window::PaintToDevice(OutputDevice* pDev, const Point& rPos)
+{
+ if( !mpWindowImpl )
+ return;
+
+ SAL_WARN_IF( pDev->HasMirroredGraphics(), "vcl.window", "PaintToDevice to mirroring graphics" );
+ SAL_WARN_IF( pDev->IsRTLEnabled(), "vcl.window", "PaintToDevice to mirroring device" );
+
+ vcl::Window* pRealParent = nullptr;
+ if( ! mpWindowImpl->mbVisible )
+ {
+ vcl::Window* pTempParent = ImplGetDefaultWindow();
+ pTempParent->EnableChildTransparentMode();
+ pRealParent = GetParent();
+ SetParent( pTempParent );
+ // trigger correct visibility flags for children
+ Show();
+ Hide();
+ }
+
+ bool bVisible = mpWindowImpl->mbVisible;
+ mpWindowImpl->mbVisible = true;
+
+ if( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->ImplPaintToDevice( pDev, rPos );
+ else
+ ImplPaintToDevice( pDev, rPos );
+
+ mpWindowImpl->mbVisible = bVisible;
+
+ if( pRealParent )
+ SetParent( pRealParent );
+}
+
+void Window::Erase(vcl::RenderContext& rRenderContext)
+{
+ if (!GetOutDev()->IsDeviceOutputNecessary() || GetOutDev()->ImplIsRecordLayout())
+ return;
+
+ bool bNativeOK = false;
+
+ ControlPart aCtrlPart = ImplGetWindowImpl()->mnNativeBackground;
+
+ if (aCtrlPart == ControlPart::Entire && IsControlBackground())
+ {
+ // nothing to do here; background is drawn in corresponding drawNativeControl implementation
+ bNativeOK = true;
+ }
+ else if (aCtrlPart != ControlPart::NONE && ! IsControlBackground())
+ {
+ tools::Rectangle aCtrlRegion(Point(), GetOutputSizePixel());
+ ControlState nState = ControlState::NONE;
+
+ if (IsEnabled())
+ nState |= ControlState::ENABLED;
+
+ bNativeOK = rRenderContext.DrawNativeControl(ControlType::WindowBackground, aCtrlPart, aCtrlRegion,
+ nState, ImplControlValue(), OUString());
+ }
+
+ if (GetOutDev()->mbBackground && !bNativeOK)
+ {
+ RasterOp eRasterOp = GetOutDev()->GetRasterOp();
+ if (eRasterOp != RasterOp::OverPaint)
+ GetOutDev()->SetRasterOp(RasterOp::OverPaint);
+ rRenderContext.DrawWallpaper(0, 0, GetOutDev()->mnOutWidth, GetOutDev()->mnOutHeight, GetOutDev()->maBackground);
+ if (eRasterOp != RasterOp::OverPaint)
+ rRenderContext.SetRasterOp(eRasterOp);
+ }
+
+ if (GetOutDev()->mpAlphaVDev)
+ GetOutDev()->mpAlphaVDev->Erase();
+}
+
+void Window::ImplScroll( const tools::Rectangle& rRect,
+ tools::Long nHorzScroll, tools::Long nVertScroll, ScrollFlags nFlags )
+{
+ if ( !GetOutDev()->IsDeviceOutputNecessary() )
+ return;
+
+ nHorzScroll = GetOutDev()->ImplLogicWidthToDevicePixel( nHorzScroll );
+ nVertScroll = GetOutDev()->ImplLogicHeightToDevicePixel( nVertScroll );
+
+ if ( !nHorzScroll && !nVertScroll )
+ return;
+
+ // There will be no CopyArea() call below, so invalidate the whole visible
+ // area, not only the smaller one that was just scrolled in.
+ // Do this when we have a double buffer anyway, or (tdf#152094) the device has a map mode enabled which
+ // makes the conversion to pixel inaccurate
+ const bool bCopyExistingAreaAndElideInvalidate = !SupportsDoubleBuffering() && !GetOutDev()->IsMapModeEnabled();
+
+ if ( mpWindowImpl->mpCursor )
+ mpWindowImpl->mpCursor->ImplSuspend();
+
+ ScrollFlags nOrgFlags = nFlags;
+ if ( !(nFlags & (ScrollFlags::Children | ScrollFlags::NoChildren)) )
+ {
+ if ( GetStyle() & WB_CLIPCHILDREN )
+ nFlags |= ScrollFlags::NoChildren;
+ else
+ nFlags |= ScrollFlags::Children;
+ }
+
+ vcl::Region aInvalidateRegion;
+ bool bScrollChildren(nFlags & ScrollFlags::Children);
+
+ if ( !mpWindowImpl->mpFirstChild )
+ bScrollChildren = false;
+
+ OutputDevice *pOutDev = GetOutDev();
+
+ // RTL: check if this window requires special action
+ bool bReMirror = GetOutDev()->ImplIsAntiparallel();
+
+ tools::Rectangle aRectMirror( rRect );
+ if( bReMirror )
+ {
+ // make sure the invalidate region of this window is
+ // computed in the same coordinate space as the one from the overlap windows
+ pOutDev->ReMirror( aRectMirror );
+ }
+
+ // adapt paint areas
+ ImplMoveAllInvalidateRegions( aRectMirror, nHorzScroll, nVertScroll, bScrollChildren );
+
+ ImplCalcOverlapRegion( aRectMirror, aInvalidateRegion, !bScrollChildren, false );
+
+ // if the scrolling on the device is performed in the opposite direction
+ // then move the overlaps in that direction to compute the invalidate region
+ // on the correct side, i.e., revert nHorzScroll
+ if (!aInvalidateRegion.IsEmpty())
+ {
+ aInvalidateRegion.Move(bReMirror ? -nHorzScroll : nHorzScroll, nVertScroll);
+ }
+
+ tools::Rectangle aDestRect(aRectMirror);
+ aDestRect.Move(bReMirror ? -nHorzScroll : nHorzScroll, nVertScroll);
+ vcl::Region aWinInvalidateRegion(aRectMirror);
+ if (bCopyExistingAreaAndElideInvalidate)
+ aWinInvalidateRegion.Exclude(aDestRect);
+
+ aInvalidateRegion.Union(aWinInvalidateRegion);
+
+ vcl::Region aRegion( GetOutputRectPixel() );
+ if ( nFlags & ScrollFlags::Clip )
+ aRegion.Intersect( rRect );
+ if ( mpWindowImpl->mbWinRegion )
+ aRegion.Intersect( GetOutDev()->ImplPixelToDevicePixel( mpWindowImpl->maWinRegion ) );
+
+ aRegion.Exclude( aInvalidateRegion );
+
+ ImplClipBoundaries( aRegion, false, true );
+ if ( !bScrollChildren )
+ {
+ if ( nOrgFlags & ScrollFlags::NoChildren )
+ ImplClipAllChildren( aRegion );
+ else
+ ImplClipChildren( aRegion );
+ }
+ if ( GetOutDev()->mbClipRegion && (nFlags & ScrollFlags::UseClipRegion) )
+ aRegion.Intersect( GetOutDev()->maRegion );
+ if ( !aRegion.IsEmpty() )
+ {
+ if ( mpWindowImpl->mpWinData )
+ {
+ if ( mpWindowImpl->mbFocusVisible )
+ ImplInvertFocus( *mpWindowImpl->mpWinData->mpFocusRect );
+ if ( mpWindowImpl->mbTrackVisible && (mpWindowImpl->mpWinData->mnTrackFlags & ShowTrackFlags::TrackWindow) )
+ InvertTracking( *mpWindowImpl->mpWinData->mpTrackRect, mpWindowImpl->mpWinData->mnTrackFlags );
+ }
+#ifndef IOS
+ // This seems completely unnecessary with tiled rendering, and
+ // causes the "AquaSalGraphics::copyArea() for non-layered
+ // graphics" message. Presumably we should bypass this on all
+ // platforms when dealing with a "window" that uses tiled
+ // rendering at the moment. Unclear how to figure that out,
+ // though. Also unclear whether we actually could just not
+ // create a "frame window", whatever that exactly is, in the
+ // tiled rendering case, or at least for platforms where tiles
+ // rendering is all there is.
+
+ SalGraphics* pGraphics = ImplGetFrameGraphics();
+ // The invalidation area contains the area what would be copied here,
+ // so avoid copying in case of double buffering.
+ if (pGraphics && bCopyExistingAreaAndElideInvalidate)
+ {
+ if( bReMirror )
+ {
+ pOutDev->ReMirror( aRegion );
+ }
+
+ pOutDev->SelectClipRegion( aRegion, pGraphics );
+ pGraphics->CopyArea( rRect.Left()+nHorzScroll, rRect.Top()+nVertScroll,
+ rRect.Left(), rRect.Top(),
+ rRect.GetWidth(), rRect.GetHeight(),
+ *GetOutDev() );
+ }
+#endif
+ if ( mpWindowImpl->mpWinData )
+ {
+ if ( mpWindowImpl->mbFocusVisible )
+ ImplInvertFocus( *mpWindowImpl->mpWinData->mpFocusRect );
+ if ( mpWindowImpl->mbTrackVisible && (mpWindowImpl->mpWinData->mnTrackFlags & ShowTrackFlags::TrackWindow) )
+ InvertTracking( *mpWindowImpl->mpWinData->mpTrackRect, mpWindowImpl->mpWinData->mnTrackFlags );
+ }
+ }
+
+ if ( !aInvalidateRegion.IsEmpty() )
+ {
+ // RTL: the invalidate region for this windows is already computed in frame coordinates
+ // so it has to be re-mirrored before calling the Paint-handler
+ mpWindowImpl->mnPaintFlags |= ImplPaintFlags::CheckRtl;
+
+ if ( !bScrollChildren )
+ {
+ if ( nOrgFlags & ScrollFlags::NoChildren )
+ ImplClipAllChildren( aInvalidateRegion );
+ else
+ ImplClipChildren( aInvalidateRegion );
+ }
+ ImplInvalidateFrameRegion( &aInvalidateRegion, InvalidateFlags::Children );
+ }
+
+ if ( bScrollChildren )
+ {
+ vcl::Window* pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ Point aPos = pWindow->GetPosPixel();
+ aPos += Point( nHorzScroll, nVertScroll );
+ pWindow->SetPosPixel( aPos );
+
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+ }
+
+ if ( nFlags & ScrollFlags::Update )
+ PaintImmediately();
+
+ if ( mpWindowImpl->mpCursor )
+ mpWindowImpl->mpCursor->ImplResume();
+}
+
+} /* namespace vcl */
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/printdlg.cxx b/vcl/source/window/printdlg.cxx
new file mode 100644
index 0000000000..9c5f519f7c
--- /dev/null
+++ b/vcl/source/window/printdlg.cxx
@@ -0,0 +1,2254 @@
+/* -*- 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/.
+ *
+ * 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 <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <utility>
+#include <vcl/QueueInfo.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/help.hxx>
+#include <vcl/naturalsort.hxx>
+#include <vcl/print.hxx>
+#include <vcl/printer/Options.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/wall.hxx>
+#include <vcl/weldutils.hxx>
+#include <vcl/windowstate.hxx>
+
+#include <bitmaps.hlst>
+#include <configsettings.hxx>
+#include <printdlg.hxx>
+#include <strings.hrc>
+#include <svdata.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+using namespace vcl;
+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;
+
+enum
+{
+ ORIENTATION_AUTOMATIC,
+ ORIENTATION_PORTRAIT,
+ ORIENTATION_LANDSCAPE
+};
+
+namespace {
+ bool lcl_ListBoxCompare( const OUString& rStr1, const OUString& rStr2 )
+ {
+ return vcl::NaturalSortCompare( rStr1, rStr2 ) < 0;
+ }
+}
+
+PrintDialog::PrintPreviewWindow::PrintPreviewWindow(PrintDialog* pDialog)
+ : mpDialog(pDialog)
+ , maOrigSize( 10, 10 )
+ , mnDPIX(Application::GetDefaultDevice()->GetDPIX())
+ , mnDPIY(Application::GetDefaultDevice()->GetDPIY())
+ , mbGreyscale( false )
+{
+}
+
+PrintDialog::PrintPreviewWindow::~PrintPreviewWindow()
+{
+}
+
+void PrintDialog::PrintPreviewWindow::Resize()
+{
+ Size aNewSize(GetOutputSizePixel());
+ tools::Long nTextHeight = GetDrawingArea()->get_text_height();
+ // leave small space for decoration
+ aNewSize.AdjustWidth( -(nTextHeight + 2) );
+ aNewSize.AdjustHeight( -(nTextHeight + 2) );
+ Size aScaledSize;
+ double fScale = 1.0;
+
+ // #i106435# catch corner case of Size(0,0)
+ Size aOrigSize( maOrigSize );
+ if( aOrigSize.Width() < 1 )
+ aOrigSize.setWidth( aNewSize.Width() );
+ if( aOrigSize.Height() < 1 )
+ aOrigSize.setHeight( aNewSize.Height() );
+ if( aOrigSize.Width() > aOrigSize.Height() )
+ {
+ aScaledSize = Size( aNewSize.Width(), aNewSize.Width() * aOrigSize.Height() / aOrigSize.Width() );
+ if( aScaledSize.Height() > aNewSize.Height() )
+ fScale = double(aNewSize.Height())/double(aScaledSize.Height());
+ }
+ else
+ {
+ aScaledSize = Size( aNewSize.Height() * aOrigSize.Width() / aOrigSize.Height(), aNewSize.Height() );
+ if( aScaledSize.Width() > aNewSize.Width() )
+ fScale = double(aNewSize.Width())/double(aScaledSize.Width());
+ }
+ aScaledSize.setWidth( tools::Long(aScaledSize.Width()*fScale) );
+ aScaledSize.setHeight( tools::Long(aScaledSize.Height()*fScale) );
+
+ maPreviewSize = aScaledSize;
+
+ // check and evtl. recreate preview bitmap
+ preparePreviewBitmap();
+}
+
+void PrintDialog::PrintPreviewWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.Push();
+ weld::SetPointFont(rRenderContext, rRenderContext.GetSettings().GetStyleSettings().GetLabelFont());
+
+ rRenderContext.SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetDialogColor()));
+ rRenderContext.Erase();
+
+ auto nTextHeight = rRenderContext.GetTextHeight();
+ Size aSize(GetOutputSizePixel());
+ Point aOffset((aSize.Width() - maPreviewSize.Width() + nTextHeight) / 2,
+ (aSize.Height() - maPreviewSize.Height() + nTextHeight) / 2);
+
+ // horizontal line
+ {
+ auto nWidth = rRenderContext.GetTextWidth(maHorzText);
+
+ auto nStart = aOffset.X() + (maPreviewSize.Width() - nWidth) / 2;
+ rRenderContext.DrawText(Point(nStart, aOffset.Y() - nTextHeight), maHorzText, 0, maHorzText.getLength());
+
+ DecorationView aDecoView(&rRenderContext);
+ auto nTop = aOffset.Y() - (nTextHeight / 2);
+ aDecoView.DrawSeparator(Point(aOffset.X(), nTop), Point(nStart - 2, nTop), false);
+ aDecoView.DrawSeparator(Point(nStart + nWidth + 2, nTop), Point(aOffset.X() + maPreviewSize.Width(), nTop), false);
+ }
+
+ // vertical line
+ {
+ rRenderContext.Push(PushFlags::FONT);
+ vcl::Font aFont(rRenderContext.GetFont());
+ aFont.SetOrientation(900_deg10);
+ rRenderContext.SetFont(aFont);
+
+ auto nLeft = aOffset.X() - nTextHeight;
+
+ auto nWidth = rRenderContext.GetTextWidth(maVertText);
+ auto nStart = aOffset.Y() + (maPreviewSize.Height() + nWidth) / 2;
+
+ rRenderContext.DrawText(Point(nLeft, nStart), maVertText, 0, maVertText.getLength());
+
+ DecorationView aDecoView(&rRenderContext);
+ nLeft = aOffset.X() - (nTextHeight / 2);
+ aDecoView.DrawSeparator(Point(nLeft, aOffset.Y()), Point(nLeft, nStart - nWidth - 2), true);
+ aDecoView.DrawSeparator(Point(nLeft, nStart + 2), Point(nLeft, aOffset.Y() + maPreviewSize.Height()), true);
+
+ rRenderContext.Pop();
+ }
+
+ if (!maReplacementString.isEmpty())
+ {
+ // replacement is active
+ tools::Rectangle aTextRect(aOffset + Point(2, 2), Size(maPreviewSize.Width() - 4, maPreviewSize.Height() - 4));
+ rRenderContext.DrawText(aTextRect, maReplacementString,
+ DrawTextFlags::Center | DrawTextFlags::VCenter |
+ DrawTextFlags::WordBreak | DrawTextFlags::MultiLine);
+ }
+ else
+ {
+ BitmapEx aPreviewBitmap(maPreviewBitmap);
+
+ // This explicit force-to-scale allows us to get the
+ // mentioned best quality here. Unfortunately this is
+ // currently not sure when using just ::DrawBitmap with
+ // a defined size or ::DrawOutDev
+ aPreviewBitmap.Scale(maPreviewSize, BmpScaleFlag::BestQuality);
+ rRenderContext.DrawBitmapEx(aOffset, aPreviewBitmap);
+ }
+
+ tools::Rectangle aFrameRect(aOffset + Point(-1, -1), Size(maPreviewSize.Width() + 2, maPreviewSize.Height() + 2));
+ DecorationView aDecorationView(&rRenderContext);
+ aDecorationView.DrawFrame(aFrameRect, DrawFrameStyle::Group);
+
+ rRenderContext.Pop();
+}
+
+bool PrintDialog::PrintPreviewWindow::Command( const CommandEvent& rEvt )
+{
+ if( rEvt.GetCommand() == CommandEventId::Wheel )
+ {
+ const CommandWheelData* pWheelData = rEvt.GetWheelData();
+ if(pWheelData->GetDelta() > 0)
+ mpDialog->previewForward();
+ else if (pWheelData->GetDelta() < 0)
+ mpDialog->previewBackward();
+ return true;
+ }
+ return CustomWidgetController::Command(rEvt);
+}
+
+void PrintDialog::PrintPreviewWindow::setPreview( const GDIMetaFile& i_rNewPreview,
+ const Size& i_rOrigSize,
+ std::u16string_view i_rPaperName,
+ const OUString& i_rReplacement,
+ sal_Int32 i_nDPIX,
+ sal_Int32 i_nDPIY,
+ bool i_bGreyscale
+ )
+{
+ maMtf = i_rNewPreview;
+ mnDPIX = i_nDPIX;
+ mnDPIY = i_nDPIY;
+ maOrigSize = i_rOrigSize;
+ maReplacementString = i_rReplacement;
+ mbGreyscale = i_bGreyscale;
+
+ // use correct measurements
+ const LocaleDataWrapper& rLocWrap(Application::GetSettings().GetLocaleDataWrapper());
+ o3tl::Length eUnit = o3tl::Length::mm;
+ int nDigits = 0;
+ if( rLocWrap.getMeasurementSystemEnum() == MeasurementSystem::US )
+ {
+ eUnit = o3tl::Length::in100;
+ nDigits = 2;
+ }
+ Size aLogicPaperSize(o3tl::convert(i_rOrigSize, o3tl::Length::mm100, eUnit));
+ OUString aNumText( rLocWrap.getNum( aLogicPaperSize.Width(), nDigits ) );
+ OUStringBuffer aBuf( aNumText + " " );
+ aBuf.appendAscii( eUnit == o3tl::Length::mm ? "mm" : "in" );
+ if( !i_rPaperName.empty() )
+ {
+ aBuf.append( OUString::Concat(" (") + i_rPaperName + ")" );
+ }
+ maHorzText = aBuf.makeStringAndClear();
+
+ aNumText = rLocWrap.getNum( aLogicPaperSize.Height(), nDigits );
+ aBuf.append( aNumText + " " );
+ aBuf.appendAscii( eUnit == o3tl::Length::mm ? "mm" : "in" );
+ maVertText = aBuf.makeStringAndClear();
+
+ // We have a new Metafile and evtl. a new page, so we need to reset
+ // the PreviewBitmap to force new creation
+ maPreviewBitmap = Bitmap();
+
+ // sets/calculates e.g. maPreviewSize
+ // also triggers preparePreviewBitmap()
+ Resize();
+
+ Invalidate();
+}
+
+void PrintDialog::PrintPreviewWindow::preparePreviewBitmap()
+{
+ if(maPreviewSize.IsEmpty())
+ {
+ // not yet fully initialized, no need to prepare anything
+ return;
+ }
+
+ // define an allowed number of pixels, also see
+ // defaults for primitive renderers and similar. This
+ // might be centralized and made dependent of 32/64bit
+ const sal_uInt32 nMaxSquarePixels(500000);
+
+ // check how big (squarePixels) the preview is currently (with
+ // max value of MaxSquarePixels)
+ const sal_uInt32 nCurrentSquarePixels(
+ std::min(
+ nMaxSquarePixels,
+ static_cast<sal_uInt32>(maPreviewBitmap.GetSizePixel().getWidth())
+ * static_cast<sal_uInt32>(maPreviewBitmap.GetSizePixel().getHeight())));
+
+ // check how big (squarePixels) the preview needs to be (with
+ // max value of MaxSquarePixels)
+ const sal_uInt32 nRequiredSquarePixels(
+ std::min(
+ nMaxSquarePixels,
+ static_cast<sal_uInt32>(maPreviewSize.getWidth())
+ * static_cast<sal_uInt32>(maPreviewSize.getHeight())));
+
+ // check if preview is big enough. Use a scaling value in
+ // the comparison to not get bigger at the last possible moment
+ // what may look awkward and pixelated (again). This means
+ // to use a percentage value - if we have at least
+ // that value of required pixels, we are good.
+ static const double fPreventAwkwardFactor(1.35); // 35%
+ if(nCurrentSquarePixels >= static_cast<sal_uInt32>(nRequiredSquarePixels * fPreventAwkwardFactor))
+ {
+ // at this place we also could add a mechanism to let the preview
+ // bitmap 'shrink' again if it is currently 'too big' -> bigger
+ // than required. I think this is not necessary for now.
+
+ // already sufficient, done.
+ return;
+ }
+
+ // check if we have enough square pixels e.g for 8x8 pixels
+ if(nRequiredSquarePixels < 64)
+ {
+ // too small preview - let it empty
+ return;
+ }
+
+ // Calculate nPlannedSquarePixels which is the required size
+ // expanded by a percentage (with max value of MaxSquarePixels)
+ static const double fExtraSpaceFactor(1.65); // 65%
+ const sal_uInt32 nPlannedSquarePixels(
+ std::min(
+ nMaxSquarePixels,
+ static_cast<sal_uInt32>(maPreviewSize.getWidth() * fExtraSpaceFactor)
+ * static_cast<sal_uInt32>(maPreviewSize.getHeight() * fExtraSpaceFactor)));
+
+ // calculate back new width and height - it might have been
+ // truncated by MaxSquarePixels.
+ // We know that w*h == nPlannedSquarePixels and w/h == ratio
+ const double fRatio(static_cast<double>(maPreviewSize.getWidth()) / static_cast<double>(maPreviewSize.getHeight()));
+ const double fNewWidth(sqrt(static_cast<double>(nPlannedSquarePixels) * fRatio));
+ const double fNewHeight(sqrt(static_cast<double>(nPlannedSquarePixels) / fRatio));
+ const Size aScaledSize(basegfx::fround(fNewWidth), basegfx::fround(fNewHeight));
+
+ // check if this eventual maximum is already reached
+ // due to having hit the MaxSquarePixels. Due to using
+ // an integer AspectRatio, we cannot make a numeric exact
+ // comparison - we need to compare if we are close
+ const double fScaledSizeSquare(static_cast<double>(aScaledSize.getWidth() * aScaledSize.getHeight()));
+ const double fPreviewSizeSquare(static_cast<double>(maPreviewBitmap.GetSizePixel().getWidth() * maPreviewBitmap.GetSizePixel().getHeight()));
+
+ // test as equal up to 0.1% (0.001)
+ if(fPreviewSizeSquare != 0.0 && fabs((fScaledSizeSquare / fPreviewSizeSquare) - 1.0) < 0.001)
+ {
+ // maximum is reached, avoid bigger scaling
+ return;
+ }
+
+ // create temporary VDev with requested Size and DPI.
+ // CAUTION: DPI *is* important here - it DIFFERS from 75x75, usually 600x600 is used
+ ScopedVclPtrInstance<VirtualDevice> pPrerenderVDev(*Application::GetDefaultDevice());
+ pPrerenderVDev->SetOutputSizePixel(aScaledSize, false);
+ pPrerenderVDev->SetReferenceDevice( mnDPIX, mnDPIY );
+
+ // calculate needed Scale for Metafile (using Size and DPI from VDev)
+ Size aLogicSize( pPrerenderVDev->PixelToLogic( pPrerenderVDev->GetOutputSizePixel(), MapMode( MapUnit::Map100thMM ) ) );
+ Size aOrigSize( maOrigSize );
+ if( aOrigSize.Width() < 1 )
+ aOrigSize.setWidth( aLogicSize.Width() );
+ if( aOrigSize.Height() < 1 )
+ aOrigSize.setHeight( aLogicSize.Height() );
+ double fScale = double(aLogicSize.Width())/double(aOrigSize.Width());
+
+ // tdf#141761
+ // The display quality of the Preview is pretty ugly when
+ // FormControls are used. I made a deep-dive why this happens,
+ // and in principle the reason is the Mteafile::Scale used
+ // below. Since Metafile actions are integer, that floating point
+ // scale leads to rounding errors that make the lines painting
+ // the FormControls disappear in the surrounding ClipRegions.
+ // That Scale cannot be avoided since the Metafile contains it's
+ // own SetMapMode commands which *will* be executed on ::Play,
+ // so the ::Scale is the only possibility fr Metafile currently:
+ // Giving a Size as parameter in ::Play will *not* work due to
+ // the relativeMapMode that gets created will fail on
+ // ::SetMapMode actions in the Metafile - and FormControls DO
+ // use ::SetMapMode(MapPixel).
+ // This can only be solved better in the future using Primitives
+ // which would allow any scale by embedding to a Transformation,
+ // but that would be a bigger rework.
+ // Until then, use this little 'trick' to improve quality.
+ // It uses the fact to empirically having tested that the quality
+ // gets really bad for FormControls starting by a scale factor
+ // smaller than 0.2 - that makes the ClipRegion overlap start.
+ // So - for now - try not to go below that.
+ static const double fMinimumScale(0.2);
+ double fFactor(0.0);
+ if(fScale < fMinimumScale)
+ {
+ fFactor = fMinimumScale / fScale;
+ fScale = fMinimumScale;
+
+ double fWidth(aScaledSize.getWidth() * fFactor);
+ double fHeight(aScaledSize.getHeight() * fFactor);
+ const double fNewNeededPixels(fWidth * fHeight);
+
+ // to not risk using too big bitmaps and running into
+ // memory problems, still limit to a useful factor is
+ // necessary, also empirically estimated to
+ // avoid the quality from collapsing (using a direct
+ // in-between , ceil'd result)
+ static const double fMaximumQualitySquare(1396221.0);
+
+ if(fNewNeededPixels > fMaximumQualitySquare)
+ {
+ const double fCorrection(fMaximumQualitySquare/fNewNeededPixels);
+ fWidth *= fCorrection;
+ fHeight *= fCorrection;
+ fScale *= fCorrection;
+ }
+
+ const Size aScaledSize2(basegfx::fround(fWidth), basegfx::fround(fHeight));
+ pPrerenderVDev->SetOutputSizePixel(aScaledSize2, false);
+ aLogicSize = pPrerenderVDev->PixelToLogic( aScaledSize2, MapMode( MapUnit::Map100thMM ) );
+ }
+
+ pPrerenderVDev->EnableOutput();
+ pPrerenderVDev->SetBackground( Wallpaper(COL_WHITE) );
+ pPrerenderVDev->Erase();
+ pPrerenderVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
+ if( mbGreyscale )
+ pPrerenderVDev->SetDrawMode( pPrerenderVDev->GetDrawMode() |
+ ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText |
+ DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) );
+
+ // Copy, Scale and Paint Metafile
+ GDIMetaFile aMtf( maMtf );
+ aMtf.WindStart();
+ aMtf.Scale( fScale, fScale );
+ aMtf.WindStart();
+ aMtf.Play(*pPrerenderVDev, Point(0, 0), aLogicSize);
+
+ pPrerenderVDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ maPreviewBitmap = pPrerenderVDev->GetBitmapEx(Point(0, 0), pPrerenderVDev->GetOutputSizePixel());
+
+ if(0.0 != fFactor)
+ {
+ // Correct to needed size, BmpScaleFlag::Interpolate is acceptable,
+ // but BmpScaleFlag::BestQuality is just better. In case of time
+ // constraints, change to Interpolate would be possible
+ maPreviewBitmap.Scale(aScaledSize, BmpScaleFlag::BestQuality);
+ }
+}
+
+PrintDialog::ShowNupOrderWindow::ShowNupOrderWindow()
+ : mnOrderMode( NupOrderType::LRTB )
+ , mnRows( 1 )
+ , mnColumns( 1 )
+{
+}
+
+void PrintDialog::ShowNupOrderWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Size aSize(70, 70);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ SetOutputSizePixel(aSize);
+}
+
+void PrintDialog::ShowNupOrderWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*i_rRect*/)
+{
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
+ rRenderContext.SetTextColor(rRenderContext.GetSettings().GetStyleSettings().GetFieldTextColor());
+ rRenderContext.SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetFieldColor()));
+ rRenderContext.Erase();
+
+ int nPages = mnRows * mnColumns;
+ Font aFont(rRenderContext.GetSettings().GetStyleSettings().GetFieldFont());
+ aFont.SetFontSize(Size(0, 24));
+ rRenderContext.SetFont(aFont);
+ Size aSampleTextSize(rRenderContext.GetTextWidth(OUString::number(nPages + 1)), rRenderContext.GetTextHeight());
+ Size aOutSize(GetOutputSizePixel());
+ Size aSubSize(aOutSize.Width() / mnColumns, aOutSize.Height() / mnRows);
+ // calculate font size: shrink the sample text so it fits
+ double fX = double(aSubSize.Width()) / double(aSampleTextSize.Width());
+ double fY = double(aSubSize.Height()) / double(aSampleTextSize.Height());
+ double fScale = (fX < fY) ? fX : fY;
+ tools::Long nFontHeight = tools::Long(24.0 * fScale) - 3;
+ if (nFontHeight < 5)
+ nFontHeight = 5;
+ aFont.SetFontSize(Size( 0, nFontHeight));
+ rRenderContext.SetFont(aFont);
+ tools::Long nTextHeight = rRenderContext.GetTextHeight();
+ for (int i = 0; i < nPages; i++)
+ {
+ OUString aPageText(OUString::number(i + 1));
+ int nX = 0, nY = 0;
+ switch (mnOrderMode)
+ {
+ case NupOrderType::LRTB:
+ nX = (i % mnColumns);
+ nY = (i / mnColumns);
+ break;
+ case NupOrderType::TBLR:
+ nX = (i / mnRows);
+ nY = (i % mnRows);
+ break;
+ case NupOrderType::RLTB:
+ nX = mnColumns - 1 - (i % mnColumns);
+ nY = (i / mnColumns);
+ break;
+ case NupOrderType::TBRL:
+ nX = mnColumns - 1 - (i / mnRows);
+ nY = (i % mnRows);
+ break;
+ }
+ Size aTextSize(rRenderContext.GetTextWidth(aPageText), nTextHeight);
+ int nDeltaX = (aSubSize.Width() - aTextSize.Width()) / 2;
+ int nDeltaY = (aSubSize.Height() - aTextSize.Height()) / 2;
+ rRenderContext.DrawText(Point(nX * aSubSize.Width() + nDeltaX,
+ nY * aSubSize.Height() + nDeltaY), aPageText);
+ }
+ DecorationView aDecorationView(&rRenderContext);
+ aDecorationView.DrawFrame(tools::Rectangle(Point(0, 0), aOutSize), DrawFrameStyle::Group);
+}
+
+Size const & PrintDialog::getJobPageSize()
+{
+ if( maFirstPageSize.IsEmpty() )
+ {
+ maFirstPageSize = maNupPortraitSize;
+ GDIMetaFile aMtf;
+ if( maPController->getPageCountProtected() > 0 )
+ {
+ PrinterController::PageSize aPageSize = maPController->getPageFile( 0, aMtf, true );
+ maFirstPageSize = aPageSize.aSize;
+ }
+ }
+ return maFirstPageSize;
+}
+
+PrintDialog::PrintDialog(weld::Window* i_pWindow, std::shared_ptr<PrinterController> i_xController)
+ : GenericDialogController(i_pWindow, "vcl/ui/printdialog.ui", "PrintDialog")
+ , maPController(std::move( i_xController ))
+ , mxTabCtrl(m_xBuilder->weld_notebook("tabcontrol"))
+ , mxScrolledWindow(m_xBuilder->weld_scrolled_window("scrolledwindow"))
+ , mxPageLayoutFrame(m_xBuilder->weld_frame("layoutframe"))
+ , mxPrinters(m_xBuilder->weld_combo_box("printersbox"))
+ , mxStatusTxt(m_xBuilder->weld_label("status"))
+ , mxSetupButton(m_xBuilder->weld_button("setup"))
+ , mxCopyCountField(m_xBuilder->weld_spin_button("copycount"))
+ , mxCollateBox(m_xBuilder->weld_check_button("collate"))
+ , mxCollateImage(m_xBuilder->weld_image("collateimage"))
+ , mxPageRangeEdit(m_xBuilder->weld_entry("pagerange"))
+ , mxPageRangesRadioButton(m_xBuilder->weld_radio_button("rbRangePages"))
+ , mxPaperSidesBox(m_xBuilder->weld_combo_box("sidesbox"))
+ , mxSingleJobsBox(m_xBuilder->weld_check_button("singlejobs"))
+ , mxReverseOrderBox(m_xBuilder->weld_check_button("reverseorder"))
+ , mxOKButton(m_xBuilder->weld_button("ok"))
+ , mxCancelButton(m_xBuilder->weld_button("cancel"))
+ , mxBackwardBtn(m_xBuilder->weld_button("backward"))
+ , mxForwardBtn(m_xBuilder->weld_button("forward"))
+ , mxFirstBtn(m_xBuilder->weld_button("btnFirst"))
+ , mxLastBtn(m_xBuilder->weld_button("btnLast"))
+ , mxPreviewBox(m_xBuilder->weld_check_button("previewbox"))
+ , mxNumPagesText(m_xBuilder->weld_label("totalnumpages"))
+ , mxPreview(new PrintPreviewWindow(this))
+ , mxPreviewWindow(new weld::CustomWeld(*m_xBuilder, "preview", *mxPreview))
+ , mxPageEdit(m_xBuilder->weld_entry("pageedit"))
+ , mxPagesBtn(m_xBuilder->weld_radio_button("pagespersheetbtn"))
+ , mxBrochureBtn(m_xBuilder->weld_radio_button("brochure"))
+ , mxPagesBoxTitleTxt(m_xBuilder->weld_label("pagespersheettxt"))
+ , mxNupPagesBox(m_xBuilder->weld_combo_box("pagespersheetbox"))
+ , mxNupNumPagesTxt(m_xBuilder->weld_label("pagestxt"))
+ , mxNupColEdt(m_xBuilder->weld_spin_button("pagecols"))
+ , mxNupTimesTxt(m_xBuilder->weld_label("by"))
+ , mxNupRowsEdt(m_xBuilder->weld_spin_button("pagerows"))
+ , mxPageMarginTxt1(m_xBuilder->weld_label("pagemargintxt1"))
+ , mxPageMarginEdt(m_xBuilder->weld_metric_spin_button("pagemarginsb", FieldUnit::MM))
+ , mxPageMarginTxt2(m_xBuilder->weld_label("pagemargintxt2"))
+ , mxSheetMarginTxt1(m_xBuilder->weld_label("sheetmargintxt1"))
+ , mxSheetMarginEdt(m_xBuilder->weld_metric_spin_button("sheetmarginsb", FieldUnit::MM))
+ , mxSheetMarginTxt2(m_xBuilder->weld_label("sheetmargintxt2"))
+ , mxPaperSizeBox(m_xBuilder->weld_combo_box("papersizebox"))
+ , mxOrientationBox(m_xBuilder->weld_combo_box("pageorientationbox"))
+ , mxNupOrderTxt(m_xBuilder->weld_label("labelorder"))
+ , mxNupOrderBox(m_xBuilder->weld_combo_box("orderbox"))
+ , mxNupOrder(new ShowNupOrderWindow)
+ , mxNupOrderWin(new weld::CustomWeld(*m_xBuilder, "orderpreview", *mxNupOrder))
+ , mxBorderCB(m_xBuilder->weld_check_button("bordercb"))
+ , mxRangeExpander(m_xBuilder->weld_expander("exRangeExpander"))
+ , mxLayoutExpander(m_xBuilder->weld_expander("exLayoutExpander"))
+ , mxCustom(m_xBuilder->weld_widget("customcontents"))
+ , maPrintToFileText( VclResId( SV_PRINT_TOFILE_TXT ) )
+ , maDefPrtText( VclResId( SV_PRINT_DEFPRT_TXT ) )
+ , maNoPageStr( VclResId( SV_PRINT_NOPAGES ) )
+ , maNoPreviewStr( VclResId( SV_PRINT_NOPREVIEW ) )
+ , mnCurPage( 0 )
+ , mnCachedPages( 0 )
+ , mbCollateAlwaysOff(false)
+ , mbShowLayoutFrame( true )
+ , maUpdatePreviewIdle("Print Dialog Update Preview Idle")
+ , maUpdatePreviewNoCacheIdle("Print Dialog Update Preview (no cache) Idle")
+{
+ // save printbutton text, gets exchanged occasionally with print to file
+ maPrintText = mxOKButton->get_label();
+
+ maPageStr = mxNumPagesText->get_label();
+
+ Printer::updatePrinters();
+
+ mxPrinters->append_text(maPrintToFileText);
+ // fill printer listbox
+ std::vector< OUString > rQueues( Printer::GetPrinterQueues() );
+ std::sort( rQueues.begin(), rQueues.end(), lcl_ListBoxCompare );
+ for( const auto& rQueue : rQueues )
+ {
+ mxPrinters->append_text(rQueue);
+ }
+ // select current printer
+ if (mxPrinters->find_text(maPController->getPrinter()->GetName()) != -1)
+ mxPrinters->set_active_text(maPController->getPrinter()->GetName());
+ else
+ {
+ // fall back to last printer
+ SettingsConfigItem* pItem = SettingsConfigItem::get();
+ OUString aValue( pItem->getValue( "PrintDialog",
+ "LastPrinter" ) );
+ if (mxPrinters->find_text(aValue) != -1)
+ {
+ mxPrinters->set_active_text(aValue);
+ maPController->setPrinter( VclPtrInstance<Printer>( aValue ) );
+ }
+ else
+ {
+ // fall back to default printer
+ mxPrinters->set_active_text(Printer::GetDefaultPrinterName());
+ maPController->setPrinter( VclPtrInstance<Printer>( Printer::GetDefaultPrinterName() ) );
+ }
+ }
+
+ // not printing to file
+ maPController->resetPrinterOptions( false );
+
+ // update the text fields for the printer
+ updatePrinterText();
+
+ // setup dependencies
+ checkControlDependencies();
+
+ // setup paper sides box
+ setupPaperSidesBox();
+
+ // set initial focus to "Number of copies"
+ mxCopyCountField->grab_focus();
+ mxCopyCountField->select_region(0, -1);
+
+ // setup sizes for N-Up
+ Size aNupSize( maPController->getPrinter()->PixelToLogic(
+ maPController->getPrinter()->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) ) );
+ if( maPController->getPrinter()->GetOrientation() == Orientation::Landscape )
+ {
+ maNupLandscapeSize = aNupSize;
+ // coverity[swapped_arguments : FALSE] - this is in the correct order
+ maNupPortraitSize = Size( aNupSize.Height(), aNupSize.Width() );
+ }
+ else
+ {
+ maNupPortraitSize = aNupSize;
+ // coverity[swapped_arguments : FALSE] - this is in the correct order
+ maNupLandscapeSize = Size( aNupSize.Height(), aNupSize.Width() );
+ }
+
+ maUpdatePreviewIdle.SetPriority(TaskPriority::POST_PAINT);
+ maUpdatePreviewIdle.SetInvokeHandler(LINK( this, PrintDialog, updatePreviewIdle));
+ maUpdatePreviewNoCacheIdle.SetPriority(TaskPriority::POST_PAINT);
+ maUpdatePreviewNoCacheIdle.SetInvokeHandler(LINK(this, PrintDialog, updatePreviewNoCacheIdle));
+
+ initFromMultiPageSetup( maPController->getMultipage() );
+
+ // setup optional UI options set by application
+ setupOptionalUI();
+
+ // hide layout frame if unwanted
+ mxPageLayoutFrame->set_visible(mbShowLayoutFrame);
+
+ // restore settings from last run
+ readFromSettings();
+
+ // setup click hdl
+ mxOKButton->connect_clicked(LINK(this, PrintDialog, ClickHdl));
+ mxCancelButton->connect_clicked(LINK(this, PrintDialog, ClickHdl));
+ mxSetupButton->connect_clicked( LINK( this, PrintDialog, ClickHdl ) );
+ mxBackwardBtn->connect_clicked(LINK(this, PrintDialog, ClickHdl));
+ mxForwardBtn->connect_clicked(LINK(this, PrintDialog, ClickHdl));
+ mxFirstBtn->connect_clicked(LINK(this, PrintDialog, ClickHdl));
+ mxLastBtn->connect_clicked( LINK( this, PrintDialog, ClickHdl ) );
+
+ // setup toggle hdl
+ mxReverseOrderBox->connect_toggled( LINK( this, PrintDialog, ToggleHdl ) );
+ mxCollateBox->connect_toggled( LINK( this, PrintDialog, ToggleHdl ) );
+ mxSingleJobsBox->connect_toggled( LINK( this, PrintDialog, ToggleHdl ) );
+ mxBrochureBtn->connect_toggled( LINK( this, PrintDialog, ToggleHdl ) );
+ mxPreviewBox->connect_toggled( LINK( this, PrintDialog, ToggleHdl ) );
+ mxBorderCB->connect_toggled( LINK( this, PrintDialog, ToggleHdl ) );
+
+ // setup select hdl
+ mxPrinters->connect_changed( LINK( this, PrintDialog, SelectHdl ) );
+ mxPaperSidesBox->connect_changed( LINK( this, PrintDialog, SelectHdl ) );
+ mxNupPagesBox->connect_changed( LINK( this, PrintDialog, SelectHdl ) );
+ mxOrientationBox->connect_changed( LINK( this, PrintDialog, SelectHdl ) );
+ mxNupOrderBox->connect_changed( LINK( this, PrintDialog, SelectHdl ) );
+ mxPaperSizeBox->connect_changed( LINK( this, PrintDialog, SelectHdl ) );
+
+ // setup modify hdl
+ mxPageEdit->connect_activate( LINK( this, PrintDialog, ActivateHdl ) );
+ mxPageEdit->connect_focus_out( LINK( this, PrintDialog, FocusOutHdl ) );
+ mxCopyCountField->connect_value_changed( LINK( this, PrintDialog, SpinModifyHdl ) );
+ mxNupColEdt->connect_value_changed( LINK( this, PrintDialog, SpinModifyHdl ) );
+ mxNupRowsEdt->connect_value_changed( LINK( this, PrintDialog, SpinModifyHdl ) );
+ mxPageMarginEdt->connect_value_changed( LINK( this, PrintDialog, MetricSpinModifyHdl ) );
+ mxSheetMarginEdt->connect_value_changed( LINK( this, PrintDialog, MetricSpinModifyHdl ) );
+
+ updateNupFromPages();
+
+ // tdf#129180 Delay setting the default value in the Paper Size list
+ // set paper sizes listbox
+ setPaperSizes();
+
+ mxRangeExpander->set_expanded(
+ officecfg::Office::Common::Print::Dialog::RangeSectionExpanded::get());
+ mxLayoutExpander->set_expanded(
+ officecfg::Office::Common::Print::Dialog::LayoutSectionExpanded::get());
+
+ // lock the dialog height, regardless of later expander state
+ mxScrolledWindow->set_size_request(
+ mxScrolledWindow->get_preferred_size().Width() + mxScrolledWindow->get_scroll_thickness(),
+ 450);
+
+ m_xDialog->set_centered_on_parent(true);
+}
+
+PrintDialog::~PrintDialog()
+{
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Print::Dialog::RangeSectionExpanded::set(mxRangeExpander->get_expanded(), batch);
+ officecfg::Office::Common::Print::Dialog::LayoutSectionExpanded::set(mxLayoutExpander->get_expanded(), batch);
+ batch->commit();
+}
+
+void PrintDialog::setupPaperSidesBox()
+{
+ DuplexMode eDuplex = maPController->getPrinter()->GetDuplexMode();
+
+ if ( eDuplex == DuplexMode::Unknown || isPrintToFile() )
+ {
+ mxPaperSidesBox->set_active( 0 );
+ mxPaperSidesBox->set_sensitive( false );
+ }
+ else
+ {
+ mxPaperSidesBox->set_active( static_cast<sal_Int32>(eDuplex) - 1 );
+ mxPaperSidesBox->set_sensitive( true );
+ }
+}
+
+void PrintDialog::storeToSettings()
+{
+ SettingsConfigItem* pItem = SettingsConfigItem::get();
+
+ pItem->setValue( "PrintDialog",
+ "LastPrinter",
+ isPrintToFile() ? Printer::GetDefaultPrinterName()
+ : mxPrinters->get_active_text() );
+
+ pItem->setValue( "PrintDialog",
+ "LastPage",
+ mxTabCtrl->get_tab_label_text(mxTabCtrl->get_current_page_ident()));
+
+ pItem->setValue( "PrintDialog",
+ "WindowState",
+ m_xDialog->get_window_state(vcl::WindowDataMask::All) );
+
+ pItem->setValue( "PrintDialog",
+ "CopyCount",
+ mxCopyCountField->get_text() );
+
+ pItem->setValue( "PrintDialog",
+ "Collate",
+ mxCollateBox->get_active() ? OUString("true") :
+ OUString("false") );
+
+ pItem->setValue( "PrintDialog",
+ "CollateSingleJobs",
+ mxSingleJobsBox->get_active() ? OUString("true") :
+ OUString("false") );
+
+ pItem->setValue( "PrintDialog",
+ "HasPreview",
+ hasPreview() ? OUString("true") :
+ OUString("false") );
+
+ pItem->Commit();
+}
+
+void PrintDialog::readFromSettings()
+{
+ SettingsConfigItem* pItem = SettingsConfigItem::get();
+
+ // read last selected tab page; if it exists, activate it
+ OUString aValue = pItem->getValue( "PrintDialog",
+ "LastPage" );
+ sal_uInt16 nCount = mxTabCtrl->get_n_pages();
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ OUString sPageId = mxTabCtrl->get_page_ident(i);
+ if (aValue == mxTabCtrl->get_tab_label_text(sPageId))
+ {
+ mxTabCtrl->set_current_page(sPageId);
+ break;
+ }
+ }
+
+ // persistent window state
+ aValue = pItem->getValue( "PrintDialog",
+ "WindowState" );
+ if (!aValue.isEmpty())
+ m_xDialog->set_window_state(aValue);
+
+ // collate
+ aValue = pItem->getValue( "PrintDialog",
+ "CollateBox" );
+ if( aValue.equalsIgnoreAsciiCase("alwaysoff") )
+ {
+ mbCollateAlwaysOff = true;
+ mxCollateBox->set_active( false );
+ mxCollateBox->set_sensitive( false );
+ }
+ else
+ {
+ mbCollateAlwaysOff = false;
+ aValue = pItem->getValue( "PrintDialog",
+ "Collate" );
+ mxCollateBox->set_active( aValue.equalsIgnoreAsciiCase("true") );
+ }
+
+ // collate single jobs
+ aValue = pItem->getValue( "PrintDialog",
+ "CollateSingleJobs" );
+ mxSingleJobsBox->set_active(aValue.equalsIgnoreAsciiCase("true"));
+
+ // preview box
+ aValue = pItem->getValue( "PrintDialog",
+ "HasPreview" );
+ if ( aValue.equalsIgnoreAsciiCase("false") )
+ mxPreviewBox->set_active( false );
+ else
+ mxPreviewBox->set_active( true );
+
+}
+
+void PrintDialog::setPaperSizes()
+{
+ mxPaperSizeBox->clear();
+
+ VclPtr<Printer> aPrt( maPController->getPrinter() );
+ mePaper = aPrt->GetPaper();
+
+ if ( isPrintToFile() )
+ {
+ mxPaperSizeBox->set_sensitive( false );
+ }
+ else
+ {
+ Size aSizeOfPaper = aPrt->GetSizeOfPaper();
+ PaperInfo aPaperInfo(aSizeOfPaper.getWidth(), aSizeOfPaper.getHeight());
+ const LocaleDataWrapper& rLocWrap(Application::GetSettings().GetLocaleDataWrapper());
+ o3tl::Length eUnit = o3tl::Length::mm;
+ int nDigits = 0;
+ if( rLocWrap.getMeasurementSystemEnum() == MeasurementSystem::US )
+ {
+ eUnit = o3tl::Length::in100;
+ nDigits = 2;
+ }
+ for (int nPaper = 0; nPaper < aPrt->GetPaperInfoCount(); nPaper++)
+ {
+ PaperInfo aInfo = aPrt->GetPaperInfo( nPaper );
+ aInfo.doSloppyFit(true);
+ Paper ePaper = aInfo.getPaper();
+
+ Size aSize = aPrt->GetPaperSize( nPaper );
+ Size aLogicPaperSize( o3tl::convert(aSize, o3tl::Length::mm100, eUnit) );
+
+ OUString aWidth( rLocWrap.getNum( aLogicPaperSize.Width(), nDigits ) );
+ OUString aHeight( rLocWrap.getNum( aLogicPaperSize.Height(), nDigits ) );
+ OUString aUnit = eUnit == o3tl::Length::mm ? OUString("mm") : OUString("in");
+ OUString aPaperName;
+
+ // Paper sizes that we don't know of but the system printer driver lists are not "User
+ // Defined". Displaying them as such is just confusing.
+ if (ePaper != PAPER_USER)
+ aPaperName = Printer::GetPaperName( ePaper ) + " ";
+
+ aPaperName += aWidth + aUnit + " x " + aHeight + aUnit;
+
+ mxPaperSizeBox->append_text(aPaperName);
+
+ if ( (ePaper != PAPER_USER && ePaper == mePaper) ||
+ (ePaper == PAPER_USER && aInfo.sloppyEqual(aPaperInfo) ) )
+ mxPaperSizeBox->set_active( nPaper );
+ }
+
+ mxPaperSizeBox->set_sensitive( true );
+ }
+}
+
+void PrintDialog::updatePrinterText()
+{
+ const OUString aDefPrt( Printer::GetDefaultPrinterName() );
+ const QueueInfo* pInfo = Printer::GetQueueInfo( mxPrinters->get_active_text(), true );
+ if( pInfo )
+ {
+ // FIXME: status text
+ OUString aStatus;
+ if( aDefPrt == pInfo->GetPrinterName() )
+ aStatus = maDefPrtText;
+ mxStatusTxt->set_label( aStatus );
+ }
+ else
+ {
+ mxStatusTxt->set_label( OUString() );
+ }
+}
+
+void PrintDialog::setPreviewText()
+{
+ OUString aNewText( maPageStr.replaceFirst( "%n", OUString::number( mnCachedPages ) ) );
+ mxNumPagesText->set_label( aNewText );
+}
+
+IMPL_LINK_NOARG(PrintDialog, updatePreviewIdle, Timer*, void)
+{
+ preparePreview(true);
+}
+
+IMPL_LINK_NOARG(PrintDialog, updatePreviewNoCacheIdle, Timer*, void)
+{
+ preparePreview(false);
+}
+
+void PrintDialog::preparePreview( bool i_bMayUseCache )
+{
+ VclPtr<Printer> aPrt( maPController->getPrinter() );
+ Size aCurPageSize = aPrt->PixelToLogic( aPrt->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) );
+ // tdf#123076 Get paper size for the preview top label
+ mePaper = aPrt->GetPaper();
+ GDIMetaFile aMtf;
+
+ // page range may have changed depending on options
+ sal_Int32 nPages = maPController->getFilteredPageCount();
+ mnCachedPages = nPages;
+
+ setPreviewText();
+
+ if ( !hasPreview() )
+ {
+ mxPreview->setPreview( aMtf, aCurPageSize,
+ Printer::GetPaperName( mePaper ),
+ maNoPreviewStr,
+ aPrt->GetDPIX(), aPrt->GetDPIY(),
+ aPrt->GetPrinterOptions().IsConvertToGreyscales()
+ );
+
+ mxForwardBtn->set_sensitive( false );
+ mxBackwardBtn->set_sensitive( false );
+ mxFirstBtn->set_sensitive( false );
+ mxLastBtn->set_sensitive( false );
+
+ mxPageEdit->set_sensitive( false );
+
+ return;
+ }
+
+ if( mnCurPage >= nPages )
+ mnCurPage = nPages-1;
+ if( mnCurPage < 0 )
+ mnCurPage = 0;
+ mxPageEdit->set_text(OUString::number(mnCurPage + 1));
+
+ if( nPages > 0 )
+ {
+ PrinterController::PageSize aPageSize =
+ maPController->getFilteredPageFile( mnCurPage, aMtf, i_bMayUseCache );
+ aCurPageSize = aPrt->PixelToLogic(aPrt->GetPaperSizePixel(), MapMode(MapUnit::Map100thMM));
+ if( ! aPageSize.bFullPaper )
+ {
+ const MapMode aMapMode( MapUnit::Map100thMM );
+ Point aOff( aPrt->PixelToLogic( aPrt->GetPageOffsetPixel(), aMapMode ) );
+ aMtf.Move( aOff.X(), aOff.Y() );
+ }
+ // tdf#150561: page size may have changed so sync mePaper with it
+ mePaper = aPrt->GetPaper();
+ }
+
+ mxPreview->setPreview( aMtf, aCurPageSize,
+ Printer::GetPaperName( mePaper ),
+ nPages > 0 ? OUString() : maNoPageStr,
+ aPrt->GetDPIX(), aPrt->GetDPIY(),
+ aPrt->GetPrinterOptions().IsConvertToGreyscales()
+ );
+
+ mxForwardBtn->set_sensitive( mnCurPage < nPages-1 );
+ mxBackwardBtn->set_sensitive( mnCurPage != 0 );
+ mxFirstBtn->set_sensitive( mnCurPage != 0 );
+ mxLastBtn->set_sensitive( mnCurPage < nPages-1 );
+ mxPageEdit->set_sensitive( nPages > 1 );
+}
+
+void PrintDialog::updateOrientationBox( const bool bAutomatic )
+{
+ if ( !bAutomatic )
+ {
+ Orientation eOrientation = maPController->getPrinter()->GetOrientation();
+ mxOrientationBox->set_active( static_cast<sal_Int32>(eOrientation) + 1 );
+ }
+ else if ( hasOrientationChanged() )
+ {
+ mxOrientationBox->set_active( ORIENTATION_AUTOMATIC );
+ }
+}
+
+bool PrintDialog::hasOrientationChanged() const
+{
+ const int nOrientation = mxOrientationBox->get_active();
+ const Orientation eOrientation = maPController->getPrinter()->GetOrientation();
+
+ return (nOrientation == ORIENTATION_LANDSCAPE && eOrientation == Orientation::Portrait)
+ || (nOrientation == ORIENTATION_PORTRAIT && eOrientation == Orientation::Landscape);
+}
+
+// Always use this function to set paper orientation to make sure everything behaves well
+void PrintDialog::setPaperOrientation( Orientation eOrientation, bool fromUser )
+{
+ VclPtr<Printer> aPrt( maPController->getPrinter() );
+ aPrt->SetOrientation( eOrientation );
+ maPController->setOrientationFromUser( eOrientation, fromUser );
+}
+
+void PrintDialog::checkControlDependencies()
+{
+ if (mxCopyCountField->get_value() > 1)
+ {
+ mxCollateBox->set_sensitive( !mbCollateAlwaysOff );
+ mxSingleJobsBox->set_sensitive( mxCollateBox->get_active() );
+ }
+ else
+ {
+ mxCollateBox->set_sensitive( false );
+ mxSingleJobsBox->set_sensitive( false );
+ }
+
+ OUString aImg(mxCollateBox->get_active() ? SV_PRINT_COLLATE_BMP : SV_PRINT_NOCOLLATE_BMP);
+
+ mxCollateImage->set_from_icon_name(aImg);
+
+ // enable setup button only for printers that can be setup
+ bool bHaveSetup = maPController->getPrinter()->HasSupport( PrinterSupport::SetupDialog );
+ mxSetupButton->set_sensitive(bHaveSetup);
+}
+
+void PrintDialog::checkOptionalControlDependencies()
+{
+ for( const auto& rEntry : maControlToPropertyMap )
+ {
+ bool bShouldbeEnabled = maPController->isUIOptionEnabled( rEntry.second );
+
+ if (bShouldbeEnabled && dynamic_cast<weld::RadioButton*>(rEntry.first))
+ {
+ auto r_it = maControlToNumValMap.find( rEntry.first );
+ if( r_it != maControlToNumValMap.end() )
+ {
+ bShouldbeEnabled = maPController->isUIChoiceEnabled( rEntry.second, r_it->second );
+ }
+ }
+
+ bool bIsEnabled = rEntry.first->get_sensitive();
+ // Enable does not do a change check first, so can be less cheap than expected
+ if (bShouldbeEnabled != bIsEnabled)
+ rEntry.first->set_sensitive( bShouldbeEnabled );
+ }
+}
+
+void PrintDialog::initFromMultiPageSetup( const vcl::PrinterController::MultiPageSetup& i_rMPS )
+{
+ mxNupOrderWin->show();
+ mxPagesBtn->set_active(true);
+ mxBrochureBtn->hide();
+
+ // setup field units for metric fields
+ const LocaleDataWrapper& rLocWrap(Application::GetSettings().GetLocaleDataWrapper());
+ FieldUnit eUnit = FieldUnit::MM;
+ sal_uInt16 nDigits = 0;
+ if( rLocWrap.getMeasurementSystemEnum() == MeasurementSystem::US )
+ {
+ eUnit = FieldUnit::INCH;
+ nDigits = 2;
+ }
+ // set units
+ mxPageMarginEdt->set_unit( eUnit );
+ mxSheetMarginEdt->set_unit( eUnit );
+
+ // set precision
+ mxPageMarginEdt->set_digits( nDigits );
+ mxSheetMarginEdt->set_digits( nDigits );
+
+ mxSheetMarginEdt->set_value( mxSheetMarginEdt->normalize( i_rMPS.nLeftMargin ), FieldUnit::MM_100TH );
+ mxPageMarginEdt->set_value( mxPageMarginEdt->normalize( i_rMPS.nHorizontalSpacing ), FieldUnit::MM_100TH );
+ mxBorderCB->set_active( i_rMPS.bDrawBorder );
+ mxNupRowsEdt->set_value( i_rMPS.nRows );
+ mxNupColEdt->set_value( i_rMPS.nColumns );
+ mxNupOrderBox->set_active( static_cast<sal_Int32>(i_rMPS.nOrder) );
+ if( i_rMPS.nRows != 1 || i_rMPS.nColumns != 1 )
+ {
+ mxNupPagesBox->set_active( mxNupPagesBox->get_count()-1 );
+ showAdvancedControls( true );
+ mxNupOrder->setValues( i_rMPS.nOrder, i_rMPS.nColumns, i_rMPS.nRows );
+ }
+}
+
+void PrintDialog::updateNup( bool i_bMayUseCache )
+{
+ int nRows = mxNupRowsEdt->get_value();
+ int nCols = mxNupColEdt->get_value();
+ tools::Long nPageMargin = mxPageMarginEdt->denormalize(mxPageMarginEdt->get_value( FieldUnit::MM_100TH ));
+ tools::Long nSheetMargin = mxSheetMarginEdt->denormalize(mxSheetMarginEdt->get_value( FieldUnit::MM_100TH ));
+
+ PrinterController::MultiPageSetup aMPS;
+ aMPS.nRows = nRows;
+ aMPS.nColumns = nCols;
+ aMPS.nLeftMargin =
+ aMPS.nTopMargin =
+ aMPS.nRightMargin =
+ aMPS.nBottomMargin = nSheetMargin;
+
+ aMPS.nHorizontalSpacing =
+ aMPS.nVerticalSpacing = nPageMargin;
+
+ aMPS.bDrawBorder = mxBorderCB->get_active();
+
+ aMPS.nOrder = static_cast<NupOrderType>(mxNupOrderBox->get_active());
+
+ int nOrientationMode = mxOrientationBox->get_active();
+ if( nOrientationMode == ORIENTATION_LANDSCAPE )
+ aMPS.aPaperSize = maNupLandscapeSize;
+ else if( nOrientationMode == ORIENTATION_PORTRAIT )
+ aMPS.aPaperSize = maNupPortraitSize;
+ else // automatic mode
+ {
+ // get size of first real page to see if it is portrait or landscape
+ // we assume same page sizes for all the pages for this
+ Size aPageSize = getJobPageSize();
+
+ Size aMultiSize( aPageSize.Width() * nCols, aPageSize.Height() * nRows );
+ if( aMultiSize.Width() > aMultiSize.Height() ) // fits better on landscape
+ {
+ aMPS.aPaperSize = maNupLandscapeSize;
+ setPaperOrientation( Orientation::Landscape, false );
+ }
+ else
+ {
+ aMPS.aPaperSize = maNupPortraitSize;
+ setPaperOrientation( Orientation::Portrait, false );
+ }
+ }
+
+ maPController->setMultipage( aMPS );
+
+ mxNupOrder->setValues( aMPS.nOrder, nCols, nRows );
+
+ if (i_bMayUseCache)
+ maUpdatePreviewIdle.Start();
+ else
+ maUpdatePreviewNoCacheIdle.Start();
+}
+
+void PrintDialog::updateNupFromPages( bool i_bMayUseCache )
+{
+ int nPages = mxNupPagesBox->get_active_id().toInt32();
+ int nRows = mxNupRowsEdt->get_value();
+ int nCols = mxNupColEdt->get_value();
+ tools::Long nPageMargin = mxPageMarginEdt->denormalize(mxPageMarginEdt->get_value( FieldUnit::MM_100TH ));
+ tools::Long nSheetMargin = mxSheetMarginEdt->denormalize(mxSheetMarginEdt->get_value( FieldUnit::MM_100TH ));
+ bool bCustom = false;
+
+ if( nPages == 1 )
+ {
+ nRows = nCols = 1;
+ nSheetMargin = 0;
+ nPageMargin = 0;
+ }
+ else if( nPages == 2 || nPages == 4 || nPages == 6 || nPages == 9 || nPages == 16 )
+ {
+ Size aJobPageSize( getJobPageSize() );
+ bool bPortrait = aJobPageSize.Width() < aJobPageSize.Height();
+ if( nPages == 2 )
+ {
+ if( bPortrait )
+ {
+ nRows = 1;
+ nCols = 2;
+ }
+ else
+ {
+ nRows = 2;
+ nCols = 1;
+ }
+ }
+ else if( nPages == 4 )
+ nRows = nCols = 2;
+ else if( nPages == 6 )
+ {
+ if( bPortrait )
+ {
+ nRows = 2;
+ nCols = 3;
+ }
+ else
+ {
+ nRows = 3;
+ nCols = 2;
+ }
+ }
+ else if( nPages == 9 )
+ nRows = nCols = 3;
+ else if( nPages == 16 )
+ nRows = nCols = 4;
+ nPageMargin = 0;
+ nSheetMargin = 0;
+ }
+ else
+ bCustom = true;
+
+ if( nPages > 1 )
+ {
+ // set upper limits for margins based on job page size and rows/columns
+ Size aSize( getJobPageSize() );
+
+ // maximum sheet distance: 1/2 sheet
+ tools::Long nHorzMax = aSize.Width()/2;
+ tools::Long nVertMax = aSize.Height()/2;
+ if( nSheetMargin > nHorzMax )
+ nSheetMargin = nHorzMax;
+ if( nSheetMargin > nVertMax )
+ nSheetMargin = nVertMax;
+
+ mxSheetMarginEdt->set_max(
+ mxSheetMarginEdt->normalize(
+ std::min(nHorzMax, nVertMax) ), FieldUnit::MM_100TH );
+
+ // maximum page distance
+ nHorzMax = (aSize.Width() - 2*nSheetMargin);
+ if( nCols > 1 )
+ nHorzMax /= (nCols-1);
+ nVertMax = (aSize.Height() - 2*nSheetMargin);
+ if( nRows > 1 )
+ nHorzMax /= (nRows-1);
+
+ if( nPageMargin > nHorzMax )
+ nPageMargin = nHorzMax;
+ if( nPageMargin > nVertMax )
+ nPageMargin = nVertMax;
+
+ mxPageMarginEdt->set_max(
+ mxSheetMarginEdt->normalize(
+ std::min(nHorzMax, nVertMax ) ), FieldUnit::MM_100TH );
+ }
+
+ mxNupRowsEdt->set_value( nRows );
+ mxNupColEdt->set_value( nCols );
+ mxPageMarginEdt->set_value( mxPageMarginEdt->normalize( nPageMargin ), FieldUnit::MM_100TH );
+ mxSheetMarginEdt->set_value( mxSheetMarginEdt->normalize( nSheetMargin ), FieldUnit::MM_100TH );
+
+ showAdvancedControls( bCustom );
+ updateNup( i_bMayUseCache );
+}
+
+void PrintDialog::enableNupControls( bool bEnable )
+{
+ mxNupPagesBox->set_sensitive( bEnable );
+ mxNupNumPagesTxt->set_sensitive( bEnable );
+ mxNupColEdt->set_sensitive( bEnable );
+ mxNupTimesTxt->set_sensitive( bEnable );
+ mxNupRowsEdt->set_sensitive( bEnable );
+ mxPageMarginTxt1->set_sensitive( bEnable );
+ mxPageMarginEdt->set_sensitive( bEnable );
+ mxPageMarginTxt2->set_sensitive( bEnable );
+ mxSheetMarginTxt1->set_sensitive( bEnable );
+ mxSheetMarginEdt->set_sensitive( bEnable );
+ mxSheetMarginTxt2->set_sensitive( bEnable );
+ mxNupOrderTxt->set_sensitive( bEnable );
+ mxNupOrderBox->set_sensitive( bEnable );
+ mxNupOrderWin->set_sensitive( bEnable );
+ mxBorderCB->set_sensitive( bEnable );
+}
+
+void PrintDialog::showAdvancedControls( bool i_bShow )
+{
+ mxNupNumPagesTxt->set_visible( i_bShow );
+ mxNupColEdt->set_visible( i_bShow );
+ mxNupTimesTxt->set_visible( i_bShow );
+ mxNupRowsEdt->set_visible( i_bShow );
+ mxPageMarginTxt1->set_visible( i_bShow );
+ mxPageMarginEdt->set_visible( i_bShow );
+ mxPageMarginTxt2->set_visible( i_bShow );
+ mxSheetMarginTxt1->set_visible( i_bShow );
+ mxSheetMarginEdt->set_visible( i_bShow );
+ mxSheetMarginTxt2->set_visible( i_bShow );
+}
+
+namespace
+{
+ void setHelpId( weld::Widget* i_pWindow, const Sequence< OUString >& i_rHelpIds, sal_Int32 i_nIndex )
+ {
+ if( i_nIndex >= 0 && i_nIndex < i_rHelpIds.getLength() )
+ i_pWindow->set_help_id( i_rHelpIds.getConstArray()[i_nIndex] );
+ }
+
+ void setHelpText( weld::Widget* i_pWindow, const Sequence< OUString >& i_rHelpTexts, sal_Int32 i_nIndex )
+ {
+ // without a help text set and the correct smartID,
+ // help texts will be retrieved from the online help system
+ if( i_nIndex >= 0 && i_nIndex < i_rHelpTexts.getLength() )
+ i_pWindow->set_tooltip_text(i_rHelpTexts.getConstArray()[i_nIndex]);
+ }
+}
+
+void PrintDialog::setupOptionalUI()
+{
+ const Sequence< PropertyValue >& rOptions( maPController->getUIOptions() );
+ for( const auto& rOption : rOptions )
+ {
+ if (rOption.Name == "OptionsUIFile")
+ {
+ OUString sOptionsUIFile;
+ rOption.Value >>= sOptionsUIFile;
+ mxCustomOptionsUIBuilder = Application::CreateBuilder(mxCustom.get(), sOptionsUIFile);
+ std::unique_ptr<weld::Container> xWindow = mxCustomOptionsUIBuilder->weld_container("box");
+ xWindow->set_help_id("vcl/ui/printdialog/PrintDialog");
+ xWindow->show();
+ continue;
+ }
+
+ Sequence< beans::PropertyValue > aOptProp;
+ rOption.Value >>= aOptProp;
+
+ // extract ui element
+ OUString aCtrlType;
+ OUString aID;
+ OUString aText;
+ OUString aPropertyName;
+ Sequence< OUString > aChoices;
+ Sequence< sal_Bool > aChoicesDisabled;
+ Sequence< OUString > aHelpTexts;
+ Sequence< OUString > aIDs;
+ Sequence< OUString > aHelpIds;
+ sal_Int64 nMinValue = 0, nMaxValue = 0;
+ OUString aGroupingHint;
+
+ for( const beans::PropertyValue& rEntry : std::as_const(aOptProp) )
+ {
+ if ( rEntry.Name == "ID" )
+ {
+ rEntry.Value >>= aIDs;
+ aID = aIDs[0];
+ }
+ if ( rEntry.Name == "Text" )
+ {
+ rEntry.Value >>= aText;
+ }
+ else if ( rEntry.Name == "ControlType" )
+ {
+ rEntry.Value >>= aCtrlType;
+ }
+ else if ( rEntry.Name == "Choices" )
+ {
+ rEntry.Value >>= aChoices;
+ }
+ else if ( rEntry.Name == "ChoicesDisabled" )
+ {
+ rEntry.Value >>= aChoicesDisabled;
+ }
+ else if ( rEntry.Name == "Property" )
+ {
+ PropertyValue aVal;
+ rEntry.Value >>= aVal;
+ aPropertyName = aVal.Name;
+ }
+ else if ( rEntry.Name == "Enabled" )
+ {
+ }
+ else if ( rEntry.Name == "GroupingHint" )
+ {
+ rEntry.Value >>= aGroupingHint;
+ }
+ else if ( rEntry.Name == "DependsOnName" )
+ {
+ }
+ else if ( rEntry.Name == "DependsOnEntry" )
+ {
+ }
+ else if ( rEntry.Name == "AttachToDependency" )
+ {
+ }
+ else if ( rEntry.Name == "MinValue" )
+ {
+ rEntry.Value >>= nMinValue;
+ }
+ else if ( rEntry.Name == "MaxValue" )
+ {
+ rEntry.Value >>= nMaxValue;
+ }
+ else if ( rEntry.Name == "HelpText" )
+ {
+ if( ! (rEntry.Value >>= aHelpTexts) )
+ {
+ OUString aHelpText;
+ if( rEntry.Value >>= aHelpText )
+ {
+ aHelpTexts.realloc( 1 );
+ *aHelpTexts.getArray() = aHelpText;
+ }
+ }
+ }
+ else if ( rEntry.Name == "HelpId" )
+ {
+ if( ! (rEntry.Value >>= aHelpIds ) )
+ {
+ OUString aHelpId;
+ if( rEntry.Value >>= aHelpId )
+ {
+ aHelpIds.realloc( 1 );
+ *aHelpIds.getArray() = aHelpId;
+ }
+ }
+ }
+ else if ( rEntry.Name == "HintNoLayoutPage" )
+ {
+ bool bHasLayoutFrame = false;
+ rEntry.Value >>= bHasLayoutFrame;
+ mbShowLayoutFrame = !bHasLayoutFrame;
+ }
+ }
+
+ if (aCtrlType == "Group")
+ {
+ aID = "custom";
+
+ weld::Container* pPage = mxTabCtrl->get_page(aID);
+ if (!pPage)
+ continue;
+
+ mxTabCtrl->set_tab_label_text(aID, aText);
+
+ // set help id
+ if (aHelpIds.hasElements())
+ pPage->set_help_id(aHelpIds[0]);
+
+ // set help text
+ if (aHelpTexts.hasElements())
+ pPage->set_tooltip_text(aHelpTexts[0]);
+
+ pPage->show();
+ }
+ else if (aCtrlType == "Subgroup" && !aID.isEmpty())
+ {
+ std::unique_ptr<weld::Widget> xWidget;
+ // since 'New Print Dialog Design' fromwhich in calc is not a frame anymore
+ if (aID == "fromwhich")
+ {
+ std::unique_ptr<weld::Label> xLabel = m_xBuilder->weld_label(aID);
+ xLabel->set_label(aText);
+ xWidget = std::move(xLabel);
+ }
+ else
+ {
+ std::unique_ptr<weld::Frame> xFrame = m_xBuilder->weld_frame(aID);
+ if (!xFrame && mxCustomOptionsUIBuilder)
+ xFrame = mxCustomOptionsUIBuilder->weld_frame(aID);
+ if (xFrame)
+ {
+ xFrame->set_label(aText);
+ xWidget = std::move(xFrame);
+ }
+ }
+
+ if (!xWidget)
+ continue;
+
+ // set help id
+ setHelpId(xWidget.get(), aHelpIds, 0);
+ // set help text
+ setHelpText(xWidget.get(), aHelpTexts, 0);
+
+ xWidget->show();
+ }
+ // EVIL
+ else if( aCtrlType == "Bool" && aGroupingHint == "LayoutPage" && aPropertyName == "PrintProspect" )
+ {
+ mxBrochureBtn->set_label(aText);
+ mxBrochureBtn->show();
+
+ bool bVal = false;
+ PropertyValue* pVal = maPController->getValue( aPropertyName );
+ if( pVal )
+ pVal->Value >>= bVal;
+ mxBrochureBtn->set_active( bVal );
+ mxBrochureBtn->set_sensitive( maPController->isUIOptionEnabled( aPropertyName ) && pVal != nullptr );
+
+ maPropertyToWindowMap[aPropertyName].emplace_back(mxBrochureBtn.get());
+ maControlToPropertyMap[mxBrochureBtn.get()] = aPropertyName;
+
+ // set help id
+ setHelpId( mxBrochureBtn.get(), aHelpIds, 0 );
+ // set help text
+ setHelpText( mxBrochureBtn.get(), aHelpTexts, 0 );
+ }
+ else if (aCtrlType == "Bool")
+ {
+ // add a check box
+ std::unique_ptr<weld::CheckButton> xNewBox = m_xBuilder->weld_check_button(aID);
+ if (!xNewBox && mxCustomOptionsUIBuilder)
+ xNewBox = mxCustomOptionsUIBuilder->weld_check_button(aID);
+ if (!xNewBox)
+ continue;
+
+ xNewBox->set_label( aText );
+ xNewBox->show();
+
+ bool bVal = false;
+ PropertyValue* pVal = maPController->getValue( aPropertyName );
+ if( pVal )
+ pVal->Value >>= bVal;
+ xNewBox->set_active( bVal );
+ xNewBox->connect_toggled( LINK( this, PrintDialog, UIOption_CheckHdl ) );
+
+ maExtraControls.emplace_back(std::move(xNewBox));
+
+ weld::Widget* pWidget = maExtraControls.back().get();
+
+ maPropertyToWindowMap[aPropertyName].emplace_back(pWidget);
+ maControlToPropertyMap[pWidget] = aPropertyName;
+
+ // set help id
+ setHelpId(pWidget, aHelpIds, 0);
+ // set help text
+ setHelpText(pWidget, aHelpTexts, 0);
+ }
+ else if (aCtrlType == "Radio")
+ {
+ sal_Int32 nCurHelpText = 0;
+
+ // iterate options
+ sal_Int32 nSelectVal = 0;
+ PropertyValue* pVal = maPController->getValue( aPropertyName );
+ if( pVal && pVal->Value.hasValue() )
+ pVal->Value >>= nSelectVal;
+ for( sal_Int32 m = 0; m < aChoices.getLength(); m++ )
+ {
+ aID = aIDs[m];
+ std::unique_ptr<weld::RadioButton> xBtn = m_xBuilder->weld_radio_button(aID);
+ if (!xBtn && mxCustomOptionsUIBuilder)
+ xBtn = mxCustomOptionsUIBuilder->weld_radio_button(aID);
+ if (!xBtn)
+ continue;
+
+ xBtn->set_label( aChoices[m] );
+ xBtn->set_active( m == nSelectVal );
+ xBtn->connect_toggled( LINK( this, PrintDialog, UIOption_RadioHdl ) );
+ if( aChoicesDisabled.getLength() > m && aChoicesDisabled[m] )
+ xBtn->set_sensitive( false );
+ xBtn->show();
+
+ maExtraControls.emplace_back(std::move(xBtn));
+
+ weld::Widget* pWidget = maExtraControls.back().get();
+
+ maPropertyToWindowMap[ aPropertyName ].emplace_back(pWidget);
+ maControlToPropertyMap[pWidget] = aPropertyName;
+ maControlToNumValMap[pWidget] = m;
+
+ // set help id
+ setHelpId( pWidget, aHelpIds, nCurHelpText );
+ // set help text
+ setHelpText( pWidget, aHelpTexts, nCurHelpText );
+ nCurHelpText++;
+ }
+ }
+ else if ( aCtrlType == "List" )
+ {
+ std::unique_ptr<weld::ComboBox> xList = m_xBuilder->weld_combo_box(aID);
+ if (!xList && mxCustomOptionsUIBuilder)
+ xList = mxCustomOptionsUIBuilder->weld_combo_box(aID);
+ if (!xList)
+ continue;
+
+ // iterate options
+ for( const auto& rChoice : std::as_const(aChoices) )
+ xList->append_text(rChoice);
+
+ sal_Int32 nSelectVal = 0;
+ PropertyValue* pVal = maPController->getValue( aPropertyName );
+ if( pVal && pVal->Value.hasValue() )
+ pVal->Value >>= nSelectVal;
+ xList->set_active(nSelectVal);
+ xList->connect_changed( LINK( this, PrintDialog, UIOption_SelectHdl ) );
+ xList->show();
+
+ maExtraControls.emplace_back(std::move(xList));
+
+ weld::Widget* pWidget = maExtraControls.back().get();
+
+ maPropertyToWindowMap[ aPropertyName ].emplace_back(pWidget);
+ maControlToPropertyMap[pWidget] = aPropertyName;
+
+ // set help id
+ setHelpId( pWidget, aHelpIds, 0 );
+ // set help text
+ setHelpText( pWidget, aHelpTexts, 0 );
+ }
+ else if ( aCtrlType == "Range" )
+ {
+ std::unique_ptr<weld::SpinButton> xField = m_xBuilder->weld_spin_button(aID);
+ if (!xField && mxCustomOptionsUIBuilder)
+ xField = mxCustomOptionsUIBuilder->weld_spin_button(aID);
+ if (!xField)
+ continue;
+
+ // set min/max and current value
+ if(nMinValue != nMaxValue)
+ xField->set_range(nMinValue, nMaxValue);
+
+ sal_Int64 nCurVal = 0;
+ PropertyValue* pVal = maPController->getValue( aPropertyName );
+ if( pVal && pVal->Value.hasValue() )
+ pVal->Value >>= nCurVal;
+ xField->set_value( nCurVal );
+ xField->connect_value_changed( LINK( this, PrintDialog, UIOption_SpinModifyHdl ) );
+ xField->show();
+
+ maExtraControls.emplace_back(std::move(xField));
+
+ weld::Widget* pWidget = maExtraControls.back().get();
+
+ maPropertyToWindowMap[ aPropertyName ].emplace_back(pWidget);
+ maControlToPropertyMap[pWidget] = aPropertyName;
+
+ // set help id
+ setHelpId( pWidget, aHelpIds, 0 );
+ // set help text
+ setHelpText( pWidget, aHelpTexts, 0 );
+ }
+ else if (aCtrlType == "Edit")
+ {
+ std::unique_ptr<weld::Entry> xField = m_xBuilder->weld_entry(aID);
+ if (!xField && mxCustomOptionsUIBuilder)
+ xField = mxCustomOptionsUIBuilder->weld_entry(aID);
+ if (!xField)
+ continue;
+
+ OUString aCurVal;
+ PropertyValue* pVal = maPController->getValue( aPropertyName );
+ if( pVal && pVal->Value.hasValue() )
+ pVal->Value >>= aCurVal;
+ xField->set_text( aCurVal );
+ xField->connect_changed( LINK( this, PrintDialog, UIOption_EntryModifyHdl ) );
+ xField->show();
+
+ maExtraControls.emplace_back(std::move(xField));
+
+ weld::Widget* pWidget = maExtraControls.back().get();
+
+ maPropertyToWindowMap[ aPropertyName ].emplace_back(pWidget);
+ maControlToPropertyMap[pWidget] = aPropertyName;
+
+ // set help id
+ setHelpId( pWidget, aHelpIds, 0 );
+ // set help text
+ setHelpText( pWidget, aHelpTexts, 0 );
+ }
+ else
+ {
+ SAL_WARN( "vcl", "Unsupported UI option: \"" << aCtrlType << '"');
+ }
+ }
+
+ // #i106506# if no brochure button, then the singular Pages radio button
+ // makes no sense, so replace it by a FixedText label
+ if (!mxBrochureBtn->get_visible() && mxPagesBtn->get_visible())
+ {
+ mxPagesBoxTitleTxt->set_label(mxPagesBtn->get_label());
+ mxPagesBoxTitleTxt->show();
+ mxPagesBtn->hide();
+
+ mxNupPagesBox->set_accessible_relation_labeled_by(mxPagesBoxTitleTxt.get());
+ }
+
+ // update enable states
+ checkOptionalControlDependencies();
+
+ // print range not shown (currently math only) -> hide spacer line and reverse order
+ if (!mxPageRangeEdit->get_visible())
+ {
+ mxReverseOrderBox->hide();
+ }
+
+ if (!mxCustomOptionsUIBuilder)
+ mxTabCtrl->remove_page(mxTabCtrl->get_page_ident(1));
+}
+
+void PrintDialog::makeEnabled( weld::Widget* i_pWindow )
+{
+ auto it = maControlToPropertyMap.find( i_pWindow );
+ if( it != maControlToPropertyMap.end() )
+ {
+ OUString aDependency( maPController->makeEnabled( it->second ) );
+ if( !aDependency.isEmpty() )
+ updateWindowFromProperty( aDependency );
+ }
+}
+
+void PrintDialog::updateWindowFromProperty( const OUString& i_rProperty )
+{
+ beans::PropertyValue* pValue = maPController->getValue( i_rProperty );
+ auto it = maPropertyToWindowMap.find( i_rProperty );
+ if( !(pValue && it != maPropertyToWindowMap.end()) )
+ return;
+
+ const auto& rWindows( it->second );
+ if( rWindows.empty() )
+ return;
+
+ bool bVal = false;
+ sal_Int32 nVal = -1;
+ if( pValue->Value >>= bVal )
+ {
+ // we should have a CheckBox for this one
+ weld::CheckButton* pBox = dynamic_cast<weld::CheckButton*>(rWindows.front());
+ if( pBox )
+ {
+ pBox->set_active( bVal );
+ }
+ else if ( i_rProperty == "PrintProspect" )
+ {
+ // EVIL special case
+ if( bVal )
+ mxBrochureBtn->set_active(true);
+ else
+ mxPagesBtn->set_active(true);
+ }
+ else
+ {
+ SAL_WARN( "vcl", "missing a checkbox" );
+ }
+ }
+ else if( pValue->Value >>= nVal )
+ {
+ // this could be a ListBox or a RadioButtonGroup
+ weld::ComboBox* pList = dynamic_cast<weld::ComboBox*>(rWindows.front());
+ if( pList )
+ {
+ pList->set_active( static_cast< sal_uInt16 >(nVal) );
+ }
+ else if( nVal >= 0 && o3tl::make_unsigned(nVal) < rWindows.size() )
+ {
+ weld::RadioButton* pBtn = dynamic_cast<weld::RadioButton*>(rWindows[nVal]);
+ SAL_WARN_IF( !pBtn, "vcl", "unexpected control for property" );
+ if( pBtn )
+ pBtn->set_active(true);
+ }
+ }
+}
+
+bool PrintDialog::isPrintToFile() const
+{
+ return ( mxPrinters->get_active() == 0 );
+}
+
+bool PrintDialog::isCollate() const
+{
+ return mxCopyCountField->get_value() > 1 && mxCollateBox->get_active();
+}
+
+bool PrintDialog::isSingleJobs() const
+{
+ return mxSingleJobsBox->get_active();
+}
+
+bool PrintDialog::hasPreview() const
+{
+ return mxPreviewBox->get_active();
+}
+
+PropertyValue* PrintDialog::getValueForWindow( weld::Widget* i_pWindow ) const
+{
+ PropertyValue* pVal = nullptr;
+ auto it = maControlToPropertyMap.find( i_pWindow );
+ if( it != maControlToPropertyMap.end() )
+ {
+ pVal = maPController->getValue( it->second );
+ SAL_WARN_IF( !pVal, "vcl", "property value not found" );
+ }
+ else
+ {
+ OSL_FAIL( "changed control not in property map" );
+ }
+ return pVal;
+}
+
+IMPL_LINK(PrintDialog, ToggleHdl, weld::Toggleable&, rButton, void)
+{
+ if (&rButton == mxPreviewBox.get())
+ {
+ maUpdatePreviewIdle.Start();
+ }
+ else if( &rButton == mxBorderCB.get() )
+ {
+ updateNup();
+ }
+ else if (&rButton == mxSingleJobsBox.get())
+ {
+ maPController->setValue( "SinglePrintJobs",
+ Any( isSingleJobs() ) );
+ checkControlDependencies();
+ }
+ else if( &rButton == mxCollateBox.get() )
+ {
+ maPController->setValue( "Collate",
+ Any( isCollate() ) );
+ checkControlDependencies();
+ }
+ else if( &rButton == mxReverseOrderBox.get() )
+ {
+ bool bChecked = mxReverseOrderBox->get_active();
+ maPController->setReversePrint( bChecked );
+ maPController->setValue( "PrintReverse",
+ Any( bChecked ) );
+ maUpdatePreviewIdle.Start();
+ }
+ else if (&rButton == mxBrochureBtn.get())
+ {
+ PropertyValue* pVal = getValueForWindow(mxBrochureBtn.get());
+ if( pVal )
+ {
+ bool bVal = mxBrochureBtn->get_active();
+ pVal->Value <<= bVal;
+
+ checkOptionalControlDependencies();
+
+ // update preview and page settings
+ maUpdatePreviewNoCacheIdle.Start();
+ }
+ if (mxBrochureBtn->get_active())
+ {
+ mxOrientationBox->set_sensitive( false );
+ mxOrientationBox->set_active( ORIENTATION_LANDSCAPE );
+ mxNupPagesBox->set_active( 0 );
+ updateNupFromPages();
+ showAdvancedControls( false );
+ enableNupControls( false );
+ }
+ else
+ {
+ mxOrientationBox->set_sensitive( true );
+ mxOrientationBox->set_active( ORIENTATION_AUTOMATIC );
+ enableNupControls( true );
+ updateNupFromPages();
+ }
+
+ }
+}
+
+IMPL_LINK(PrintDialog, ClickHdl, weld::Button&, rButton, void)
+{
+ if (&rButton == mxOKButton.get() || &rButton == mxCancelButton.get())
+ {
+ storeToSettings();
+ m_xDialog->response(&rButton == mxOKButton.get() ? RET_OK : RET_CANCEL);
+ }
+ else if( &rButton == mxForwardBtn.get() )
+ {
+ previewForward();
+ }
+ else if( &rButton == mxBackwardBtn.get() )
+ {
+ previewBackward();
+ }
+ else if( &rButton == mxFirstBtn.get() )
+ {
+ previewFirst();
+ }
+ else if( &rButton == mxLastBtn.get() )
+ {
+ previewLast();
+ }
+ else
+ {
+ if( &rButton == mxSetupButton.get() )
+ {
+ maPController->setupPrinter(m_xDialog.get());
+
+ if ( !isPrintToFile() )
+ {
+ VclPtr<Printer> aPrt( maPController->getPrinter() );
+ mePaper = aPrt->GetPaper();
+
+ for (int nPaper = 0; nPaper < aPrt->GetPaperInfoCount(); nPaper++ )
+ {
+ PaperInfo aInfo = aPrt->GetPaperInfo( nPaper );
+ aInfo.doSloppyFit(true);
+ Paper ePaper = aInfo.getPaper();
+
+ if ( mePaper == ePaper )
+ {
+ mxPaperSizeBox->set_active( nPaper );
+ break;
+ }
+ }
+ }
+
+ updateOrientationBox( false );
+ setupPaperSidesBox();
+
+ // tdf#63905 don't use cache: page size may change
+ maUpdatePreviewNoCacheIdle.Start();
+ }
+ checkControlDependencies();
+ }
+
+}
+
+IMPL_LINK( PrintDialog, SelectHdl, weld::ComboBox&, rBox, void )
+{
+ if (&rBox == mxPrinters.get())
+ {
+ if ( !isPrintToFile() )
+ {
+ OUString aNewPrinter(rBox.get_active_text());
+ // set new printer
+ maPController->setPrinter( VclPtrInstance<Printer>( aNewPrinter ) );
+ maPController->resetPrinterOptions( false );
+
+ updateOrientationBox();
+
+ // update text fields
+ mxOKButton->set_label(maPrintText);
+ updatePrinterText();
+ setPaperSizes();
+ maUpdatePreviewIdle.Start();
+ }
+ else // print to file
+ {
+ // use the default printer or FIXME: the last used one?
+ maPController->setPrinter( VclPtrInstance<Printer>( Printer::GetDefaultPrinterName() ) );
+ mxOKButton->set_label(maPrintToFileText);
+ maPController->resetPrinterOptions( true );
+
+ setPaperSizes();
+ updateOrientationBox();
+ maUpdatePreviewIdle.Start();
+ }
+
+ setupPaperSidesBox();
+ }
+ else if ( &rBox == mxPaperSidesBox.get() )
+ {
+ DuplexMode eDuplex = static_cast<DuplexMode>(mxPaperSidesBox->get_active() + 1);
+ maPController->getPrinter()->SetDuplexMode( eDuplex );
+ }
+ else if( &rBox == mxOrientationBox.get() )
+ {
+ int nOrientation = mxOrientationBox->get_active();
+ if ( nOrientation != ORIENTATION_AUTOMATIC )
+ setPaperOrientation( static_cast<Orientation>( nOrientation - 1 ), true );
+
+ updateNup( false );
+ }
+ else if ( &rBox == mxNupOrderBox.get() )
+ {
+ updateNup();
+ }
+ else if( &rBox == mxNupPagesBox.get() )
+ {
+ if( !mxPagesBtn->get_active() )
+ mxPagesBtn->set_active(true);
+ updateNupFromPages( false );
+ }
+ else if ( &rBox == mxPaperSizeBox.get() )
+ {
+ VclPtr<Printer> aPrt( maPController->getPrinter() );
+ PaperInfo aInfo = aPrt->GetPaperInfo( rBox.get_active() );
+ aInfo.doSloppyFit(true);
+ mePaper = aInfo.getPaper();
+
+ if ( mePaper == PAPER_USER )
+ aPrt->SetPaperSizeUser( Size( aInfo.getWidth(), aInfo.getHeight() ) );
+ else
+ aPrt->SetPaper( mePaper );
+
+ maPController->setPaperSizeFromUser( Size( aInfo.getWidth(), aInfo.getHeight() ) );
+
+ maUpdatePreviewIdle.Start();
+ }
+}
+
+IMPL_LINK_NOARG(PrintDialog, MetricSpinModifyHdl, weld::MetricSpinButton&, void)
+{
+ checkControlDependencies();
+ updateNupFromPages();
+}
+
+IMPL_LINK_NOARG(PrintDialog, FocusOutHdl, weld::Widget&, void)
+{
+ ActivateHdl(*mxPageEdit);
+}
+
+IMPL_LINK_NOARG(PrintDialog, ActivateHdl, weld::Entry&, bool)
+{
+ sal_Int32 nPage = mxPageEdit->get_text().toInt32();
+ if (nPage < 1)
+ {
+ nPage = 1;
+ mxPageEdit->set_text("1");
+ }
+ else if (nPage > mnCachedPages)
+ {
+ nPage = mnCachedPages;
+ mxPageEdit->set_text(OUString::number(mnCachedPages));
+ }
+ int nNewCurPage = nPage - 1;
+ if (nNewCurPage != mnCurPage)
+ {
+ mnCurPage = nNewCurPage;
+ maUpdatePreviewIdle.Start();
+ }
+ return true;
+}
+
+IMPL_LINK( PrintDialog, SpinModifyHdl, weld::SpinButton&, rEdit, void )
+{
+ checkControlDependencies();
+ if (&rEdit == mxNupRowsEdt.get() || &rEdit == mxNupColEdt.get())
+ {
+ updateNupFromPages();
+ }
+ else if( &rEdit == mxCopyCountField.get() )
+ {
+ maPController->setValue( "CopyCount",
+ Any( sal_Int32(mxCopyCountField->get_value()) ) );
+ maPController->setValue( "Collate",
+ Any( isCollate() ) );
+ }
+}
+
+IMPL_LINK( PrintDialog, UIOption_CheckHdl, weld::Toggleable&, i_rBox, void )
+{
+ PropertyValue* pVal = getValueForWindow( &i_rBox );
+ if( pVal )
+ {
+ makeEnabled( &i_rBox );
+
+ bool bVal = i_rBox.get_active();
+ pVal->Value <<= bVal;
+
+ checkOptionalControlDependencies();
+
+ // update preview and page settings
+ maUpdatePreviewNoCacheIdle.Start();
+ }
+}
+
+IMPL_LINK( PrintDialog, UIOption_RadioHdl, weld::Toggleable&, i_rBtn, void )
+{
+ // this handler gets called for all radiobuttons that get unchecked, too
+ // however we only want one notification for the new value (that is for
+ // the button that gets checked)
+ if( !i_rBtn.get_active() )
+ return;
+
+ PropertyValue* pVal = getValueForWindow( &i_rBtn );
+ auto it = maControlToNumValMap.find( &i_rBtn );
+ if( !(pVal && it != maControlToNumValMap.end()) )
+ return;
+
+ makeEnabled( &i_rBtn );
+
+ sal_Int32 nVal = it->second;
+ pVal->Value <<= nVal;
+
+ updateOrientationBox();
+
+ checkOptionalControlDependencies();
+
+ // tdf#41205 give focus to the page range edit if the corresponding radio button was selected
+ if (pVal->Name == "PrintContent" && mxPageRangesRadioButton->get_active())
+ mxPageRangeEdit->grab_focus();
+
+ // update preview and page settings
+ maUpdatePreviewNoCacheIdle.Start();
+}
+
+IMPL_LINK( PrintDialog, UIOption_SelectHdl, weld::ComboBox&, i_rBox, void )
+{
+ PropertyValue* pVal = getValueForWindow( &i_rBox );
+ if( !pVal )
+ return;
+
+ makeEnabled( &i_rBox );
+
+ sal_Int32 nVal( i_rBox.get_active() );
+ pVal->Value <<= nVal;
+
+ //If we are in impress we start in print slides mode and get a
+ //maFirstPageSize for slides which are usually landscape mode, if we
+ //change to notes which are usually in portrait mode, and then visit
+ //n-up print, we will assume notes are in landscape unless we throw
+ //away maFirstPageSize when we change page content type
+ if (pVal->Name == "PageContentType")
+ maFirstPageSize = Size();
+
+ checkOptionalControlDependencies();
+
+ // update preview and page settings
+ maUpdatePreviewNoCacheIdle.Start();
+}
+
+IMPL_LINK( PrintDialog, UIOption_SpinModifyHdl, weld::SpinButton&, i_rBox, void )
+{
+ PropertyValue* pVal = getValueForWindow( &i_rBox );
+ if( pVal )
+ {
+ makeEnabled( &i_rBox );
+
+ sal_Int64 nVal = i_rBox.get_value();
+ pVal->Value <<= nVal;
+
+ checkOptionalControlDependencies();
+
+ // update preview and page settings
+ maUpdatePreviewNoCacheIdle.Start();
+ }
+}
+
+IMPL_LINK( PrintDialog, UIOption_EntryModifyHdl, weld::Entry&, i_rBox, void )
+{
+ PropertyValue* pVal = getValueForWindow( &i_rBox );
+ if( pVal )
+ {
+ makeEnabled( &i_rBox );
+
+ OUString aVal( i_rBox.get_text() );
+ pVal->Value <<= aVal;
+
+ checkOptionalControlDependencies();
+
+ // update preview and page settings
+ maUpdatePreviewNoCacheIdle.Start();
+ }
+}
+
+void PrintDialog::previewForward()
+{
+ sal_Int32 nValue = mxPageEdit->get_text().toInt32() + 1;
+ if (nValue <= mnCachedPages)
+ {
+ mxPageEdit->set_text(OUString::number(nValue));
+ ActivateHdl(*mxPageEdit);
+ }
+}
+
+void PrintDialog::previewBackward()
+{
+ sal_Int32 nValue = mxPageEdit->get_text().toInt32() - 1;
+ if (nValue >= 1)
+ {
+ mxPageEdit->set_text(OUString::number(nValue));
+ ActivateHdl(*mxPageEdit);
+ }
+}
+
+void PrintDialog::previewFirst()
+{
+ mxPageEdit->set_text("1");
+ ActivateHdl(*mxPageEdit);
+}
+
+void PrintDialog::previewLast()
+{
+ mxPageEdit->set_text(OUString::number(mnCachedPages));
+ ActivateHdl(*mxPageEdit);
+}
+
+
+static OUString getNewLabel(const OUString& aLabel, int i_nCurr, int i_nMax)
+{
+ OUString aNewText( aLabel.replaceFirst( "%p", OUString::number( i_nCurr ) ) );
+ aNewText = aNewText.replaceFirst( "%n", OUString::number( i_nMax ) );
+
+ return aNewText;
+}
+
+// PrintProgressDialog
+PrintProgressDialog::PrintProgressDialog(weld::Window* i_pParent, int i_nMax)
+ : GenericDialogController(i_pParent, "vcl/ui/printprogressdialog.ui", "PrintProgressDialog")
+ , mbCanceled(false)
+ , mnCur(0)
+ , mnMax(i_nMax)
+ , mxText(m_xBuilder->weld_label("label"))
+ , mxProgress(m_xBuilder->weld_progress_bar("progressbar"))
+ , mxButton(m_xBuilder->weld_button("cancel"))
+{
+ if( mnMax < 1 )
+ mnMax = 1;
+
+ maStr = mxText->get_label();
+
+ //just multiply largest value by 10 and take the width of that string as
+ //the max size we will want
+ mxText->set_label(getNewLabel(maStr, mnMax * 10, mnMax * 10));
+ mxText->set_size_request(mxText->get_preferred_size().Width(), -1);
+
+ //Pick a useful max width
+ mxProgress->set_size_request(mxProgress->get_approximate_digit_width() * 25, -1);
+
+ mxButton->connect_clicked( LINK( this, PrintProgressDialog, ClickHdl ) );
+
+ // after this patch f7157f04fab298423e2c4f6a7e5f8e361164b15f, we have seen the calc Max string (sometimes) look above
+ // now init to the right start values
+ mxText->set_label(getNewLabel(maStr, mnCur, mnMax));
+}
+
+PrintProgressDialog::~PrintProgressDialog()
+{
+}
+
+IMPL_LINK_NOARG(PrintProgressDialog, ClickHdl, weld::Button&, void)
+{
+ mbCanceled = true;
+}
+
+void PrintProgressDialog::setProgress( int i_nCurrent )
+{
+ mnCur = i_nCurrent;
+
+ if( mnMax < 1 )
+ mnMax = 1;
+
+ mxText->set_label(getNewLabel(maStr, mnCur, mnMax));
+
+ // here view the dialog, with the right label
+ mxProgress->set_percentage(mnCur*100/mnMax);
+}
+
+void PrintProgressDialog::tick()
+{
+ if( mnCur < mnMax )
+ setProgress( ++mnCur );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/scrwnd.cxx b/vcl/source/window/scrwnd.cxx
new file mode 100644
index 0000000000..9efc020a6a
--- /dev/null
+++ b/vcl/source/window/scrwnd.cxx
@@ -0,0 +1,379 @@
+/* -*- 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 <limits.h>
+
+#include <o3tl/float_int_conversion.hxx>
+#include <tools/time.hxx>
+
+#include <bitmaps.hlst>
+#include <svdata.hxx>
+#include <scrwnd.hxx>
+
+#include <vcl/timer.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <sal/log.hxx>
+
+#include <math.h>
+
+#define WHEEL_WIDTH 25
+#define WHEEL_RADIUS ((WHEEL_WIDTH) >> 1 )
+#define MAX_TIME 300
+#define MIN_TIME 20
+#define DEF_TIMEOUT 50
+
+ImplWheelWindow::ImplWheelWindow( vcl::Window* pParent ) :
+ FloatingWindow ( pParent, 0 ),
+ mnRepaintTime ( 1 ),
+ mnTimeout ( DEF_TIMEOUT ),
+ mnWheelMode ( WheelMode::NONE ),
+ mnActDist ( 0 ),
+ mnActDeltaX ( 0 ),
+ mnActDeltaY ( 0 )
+{
+ // we need a parent
+ SAL_WARN_IF( !pParent, "vcl", "ImplWheelWindow::ImplWheelWindow(): Parent not set!" );
+
+ const Size aSize( pParent->GetOutputSizePixel() );
+ const StartAutoScrollFlags nFlags = ImplGetSVData()->mpWinData->mnAutoScrollFlags;
+ const bool bHorz( nFlags & StartAutoScrollFlags::Horz );
+ const bool bVert( nFlags & StartAutoScrollFlags::Vert );
+
+ // calculate maximum speed distance
+ mnMaxWidth = static_cast<sal_uLong>( 0.4 * hypot( static_cast<double>(aSize.Width()), aSize.Height() ) );
+
+ // create wheel window
+ SetTitleType( FloatWinTitleType::NONE );
+ ImplCreateImageList();
+ BitmapEx aBmp(SV_RESID_BITMAP_SCROLLMSK);
+ ImplSetRegion(aBmp.GetBitmap());
+
+ // set wheel mode
+ if( bHorz && bVert )
+ ImplSetWheelMode( WheelMode::VH );
+ else if( bHorz )
+ ImplSetWheelMode( WheelMode::H );
+ else
+ ImplSetWheelMode( WheelMode::V );
+
+ // init timer
+ mpTimer.reset(new Timer("WheelWindowTimer"));
+ mpTimer->SetInvokeHandler( LINK( this, ImplWheelWindow, ImplScrollHdl ) );
+ mpTimer->SetTimeout( mnTimeout );
+ mpTimer->Start();
+
+ CaptureMouse();
+}
+
+ImplWheelWindow::~ImplWheelWindow()
+{
+ disposeOnce();
+}
+
+void ImplWheelWindow::dispose()
+{
+ ImplStop();
+ mpTimer.reset();
+ FloatingWindow::dispose();
+}
+
+void ImplWheelWindow::ImplStop()
+{
+ ReleaseMouse();
+ mpTimer->Stop();
+ Show(false);
+}
+
+void ImplWheelWindow::ImplSetRegion( const Bitmap& rRegionBmp )
+{
+ Point aPos( GetPointerPosPixel() );
+ const Size aSize( rRegionBmp.GetSizePixel() );
+ const tools::Rectangle aRect( Point(), aSize );
+
+ maCenter = maLastMousePos = aPos;
+ aPos.AdjustX( -(aSize.Width() >> 1) );
+ aPos.AdjustY( -(aSize.Height() >> 1) );
+
+ SetPosSizePixel( aPos, aSize );
+ SetWindowRegionPixel( rRegionBmp.CreateRegion( COL_BLACK, aRect ) );
+}
+
+void ImplWheelWindow::ImplCreateImageList()
+{
+ maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_SCROLLVH);
+ maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_SCROLLV);
+ maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_SCROLLH);
+ maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_WHEELVH);
+ maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_WHEELV);
+ maImgList.emplace_back(StockImage::Yes, SV_RESID_BITMAP_WHEELH);
+}
+
+void ImplWheelWindow::ImplSetWheelMode( WheelMode nWheelMode )
+{
+ if( nWheelMode == mnWheelMode )
+ return;
+
+ mnWheelMode = nWheelMode;
+
+ if( WheelMode::NONE == mnWheelMode )
+ {
+ if( IsVisible() )
+ Hide();
+ }
+ else
+ {
+ if( !IsVisible() )
+ Show();
+
+ Invalidate();
+ }
+}
+
+void ImplWheelWindow::ImplDrawWheel(vcl::RenderContext& rRenderContext)
+{
+ int nIndex;
+
+ switch (mnWheelMode)
+ {
+ case WheelMode::VH:
+ nIndex = 0;
+ break;
+ case WheelMode::V:
+ nIndex = 1;
+ break;
+ case WheelMode::H:
+ nIndex = 2;
+ break;
+ case WheelMode::ScrollVH:
+ nIndex = 3;
+ break;
+ case WheelMode::ScrollV:
+ nIndex = 4;
+ break;
+ case WheelMode::ScrollH:
+ nIndex = 5;
+ break;
+ default:
+ nIndex = -1;
+ break;
+ }
+
+ if (nIndex >= 0)
+ rRenderContext.DrawImage(Point(), maImgList[nIndex]);
+}
+
+void ImplWheelWindow::ImplRecalcScrollValues()
+{
+ if( mnActDist < WHEEL_RADIUS )
+ {
+ mnActDeltaX = mnActDeltaY = 0;
+ mnTimeout = DEF_TIMEOUT;
+ }
+ else
+ {
+ sal_uInt64 nCurTime;
+
+ // calc current time
+ if( mnMaxWidth )
+ {
+ const double fExp = ( static_cast<double>(mnActDist) / mnMaxWidth ) * log10( double(MAX_TIME) / MIN_TIME );
+ nCurTime = static_cast<sal_uInt64>( MAX_TIME / pow( 10., fExp ) );
+ }
+ else
+ nCurTime = MAX_TIME;
+
+ if( !nCurTime )
+ nCurTime = 1;
+
+ if( mnRepaintTime <= nCurTime )
+ mnTimeout = nCurTime - mnRepaintTime;
+ else
+ {
+ sal_uInt64 nMult = mnRepaintTime / nCurTime;
+
+ if( !( mnRepaintTime % nCurTime ) )
+ mnTimeout = 0;
+ else
+ mnTimeout = ++nMult * nCurTime - mnRepaintTime;
+
+ double fValX = static_cast<double>(mnActDeltaX) * nMult;
+ double fValY = static_cast<double>(mnActDeltaY) * nMult;
+
+ mnActDeltaX = o3tl::saturating_cast<tools::Long>(fValX);
+ mnActDeltaY = o3tl::saturating_cast<tools::Long>(fValY);
+ }
+ }
+}
+
+PointerStyle ImplWheelWindow::ImplGetMousePointer( tools::Long nDistX, tools::Long nDistY ) const
+{
+ PointerStyle eStyle;
+ const StartAutoScrollFlags nFlags = ImplGetSVData()->mpWinData->mnAutoScrollFlags;
+ const bool bHorz( nFlags & StartAutoScrollFlags::Horz );
+ const bool bVert( nFlags & StartAutoScrollFlags::Vert );
+
+ if( bHorz || bVert )
+ {
+ if( mnActDist < WHEEL_RADIUS )
+ {
+ if( bHorz && bVert )
+ eStyle = PointerStyle::AutoScrollNSWE;
+ else if( bHorz )
+ eStyle = PointerStyle::AutoScrollWE;
+ else
+ eStyle = PointerStyle::AutoScrollNS;
+ }
+ else
+ {
+ double fAngle = basegfx::rad2deg(atan2(static_cast<double>(-nDistY), nDistX));
+
+ if( fAngle < 0.0 )
+ fAngle += 360.;
+
+ if( bHorz && bVert )
+ {
+ if( fAngle >= 22.5 && fAngle <= 67.5 )
+ eStyle = PointerStyle::AutoScrollNE;
+ else if( fAngle >= 67.5 && fAngle <= 112.5 )
+ eStyle = PointerStyle::AutoScrollN;
+ else if( fAngle >= 112.5 && fAngle <= 157.5 )
+ eStyle = PointerStyle::AutoScrollNW;
+ else if( fAngle >= 157.5 && fAngle <= 202.5 )
+ eStyle = PointerStyle::AutoScrollW;
+ else if( fAngle >= 202.5 && fAngle <= 247.5 )
+ eStyle = PointerStyle::AutoScrollSW;
+ else if( fAngle >= 247.5 && fAngle <= 292.5 )
+ eStyle = PointerStyle::AutoScrollS;
+ else if( fAngle >= 292.5 && fAngle <= 337.5 )
+ eStyle = PointerStyle::AutoScrollSE;
+ else
+ eStyle = PointerStyle::AutoScrollE;
+ }
+ else if( bHorz )
+ {
+ if( fAngle >= 270. || fAngle <= 90. )
+ eStyle = PointerStyle::AutoScrollE;
+ else
+ eStyle = PointerStyle::AutoScrollW;
+ }
+ else
+ {
+ if( fAngle >= 0. && fAngle <= 180. )
+ eStyle = PointerStyle::AutoScrollN;
+ else
+ eStyle = PointerStyle::AutoScrollS;
+ }
+ }
+ }
+ else
+ eStyle = PointerStyle::Arrow;
+
+ return eStyle;
+}
+
+void ImplWheelWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ ImplDrawWheel(rRenderContext);
+}
+
+void ImplWheelWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ FloatingWindow::MouseMove( rMEvt );
+
+ const Point aMousePos( OutputToScreenPixel( rMEvt.GetPosPixel() ) );
+ const tools::Long nDistX = aMousePos.X() - maCenter.X();
+ const tools::Long nDistY = aMousePos.Y() - maCenter.Y();
+
+ mnActDist = static_cast<sal_uLong>(hypot( static_cast<double>(nDistX), nDistY ));
+
+ const PointerStyle eActStyle = ImplGetMousePointer( nDistX, nDistY );
+ const StartAutoScrollFlags nFlags = ImplGetSVData()->mpWinData->mnAutoScrollFlags;
+ const bool bHorz( nFlags & StartAutoScrollFlags::Horz );
+ const bool bVert( nFlags & StartAutoScrollFlags::Vert );
+ const bool bOuter = mnActDist > WHEEL_RADIUS;
+
+ if( bOuter && ( maLastMousePos != aMousePos ) )
+ {
+ switch( eActStyle )
+ {
+ case PointerStyle::AutoScrollN: mnActDeltaX = +0; mnActDeltaY = +1; break;
+ case PointerStyle::AutoScrollS: mnActDeltaX = +0; mnActDeltaY = -1; break;
+ case PointerStyle::AutoScrollW: mnActDeltaX = +1; mnActDeltaY = +0; break;
+ case PointerStyle::AutoScrollE: mnActDeltaX = -1; mnActDeltaY = +0; break;
+ case PointerStyle::AutoScrollNW: mnActDeltaX = +1; mnActDeltaY = +1; break;
+ case PointerStyle::AutoScrollNE: mnActDeltaX = -1; mnActDeltaY = +1; break;
+ case PointerStyle::AutoScrollSW: mnActDeltaX = +1; mnActDeltaY = -1; break;
+ case PointerStyle::AutoScrollSE: mnActDeltaX = -1; mnActDeltaY = -1; break;
+
+ default:
+ break;
+ }
+ }
+
+ ImplRecalcScrollValues();
+ maLastMousePos = aMousePos;
+ SetPointer( eActStyle );
+
+ if( bHorz && bVert )
+ ImplSetWheelMode( bOuter ? WheelMode::ScrollVH : WheelMode::VH );
+ else if( bHorz )
+ ImplSetWheelMode( bOuter ? WheelMode::ScrollH : WheelMode::H );
+ else
+ ImplSetWheelMode( bOuter ? WheelMode::ScrollV : WheelMode::V );
+}
+
+void ImplWheelWindow::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ if( mnActDist > WHEEL_RADIUS )
+ GetParent()->EndAutoScroll();
+ else
+ FloatingWindow::MouseButtonUp( rMEvt );
+}
+
+IMPL_LINK_NOARG(ImplWheelWindow, ImplScrollHdl, Timer *, void)
+{
+ if ( mnActDeltaX || mnActDeltaY )
+ {
+ vcl::Window* pWindow = GetParent();
+ const Point aMousePos( pWindow->OutputToScreenPixel( pWindow->GetPointerPosPixel() ) );
+ Point aCmdMousePos( pWindow->ScreenToOutputPixel( aMousePos ) );
+ CommandScrollData aScrollData( mnActDeltaX, mnActDeltaY );
+ CommandEvent aCEvt( aCmdMousePos, CommandEventId::AutoScroll, true, &aScrollData );
+ NotifyEvent aNCmdEvt( NotifyEventType::COMMAND, pWindow, &aCEvt );
+
+ if ( !ImplCallPreNotify( aNCmdEvt ) )
+ {
+ const sal_uInt64 nTime = tools::Time::GetSystemTicks();
+ VclPtr<ImplWheelWindow> xWin(this);
+ pWindow->Command( aCEvt );
+ if( xWin->isDisposed() )
+ return;
+ mnRepaintTime = std::max( tools::Time::GetSystemTicks() - nTime, sal_uInt64(1) );
+ ImplRecalcScrollValues();
+ }
+ }
+
+ if ( mnTimeout != mpTimer->GetTimeout() )
+ mpTimer->SetTimeout( mnTimeout );
+ mpTimer->Start();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/seleng.cxx b/vcl/source/window/seleng.cxx
new file mode 100644
index 0000000000..a22ecaa7c5
--- /dev/null
+++ b/vcl/source/window/seleng.cxx
@@ -0,0 +1,422 @@
+/* -*- 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 <vcl/commandevent.hxx>
+#include <vcl/window.hxx>
+#include <vcl/seleng.hxx>
+#include <comphelper/lok.hxx>
+#include <sal/log.hxx>
+
+FunctionSet::~FunctionSet()
+{
+}
+
+inline bool SelectionEngine::ShouldDeselect( bool bModifierKey1 ) const
+{
+ return eSelMode != SelectionMode::Multiple || !bModifierKey1;
+}
+
+// TODO: throw out FunctionSet::SelectAtPoint
+
+SelectionEngine::SelectionEngine( vcl::Window* pWindow, FunctionSet* pFuncSet ) :
+ pWin( pWindow ),
+ aWTimer( "vcl::SelectionEngine aWTimer" ),
+ nUpdateInterval( SELENG_AUTOREPEAT_INTERVAL )
+{
+ eSelMode = SelectionMode::Single;
+ pFunctionSet = pFuncSet;
+ nFlags = SelectionEngineFlags::EXPANDONMOVE;
+ nLockedMods = 0;
+
+ aWTimer.SetInvokeHandler( LINK( this, SelectionEngine, ImpWatchDog ) );
+ aWTimer.SetTimeout( nUpdateInterval );
+}
+
+SelectionEngine::~SelectionEngine()
+{
+ aWTimer.Stop();
+}
+
+IMPL_LINK_NOARG(SelectionEngine, ImpWatchDog, Timer *, void)
+{
+ if ( !aArea.Contains( aLastMove.GetPosPixel() ) )
+ SelMouseMove( aLastMove );
+}
+
+void SelectionEngine::SetSelectionMode( SelectionMode eMode )
+{
+ eSelMode = eMode;
+}
+
+void SelectionEngine::CursorPosChanging( bool bShift, bool bMod1 )
+{
+ if ( !pFunctionSet )
+ return;
+
+ if ( bShift && eSelMode != SelectionMode::Single )
+ {
+ if ( IsAddMode() )
+ {
+ if ( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
+ {
+ pFunctionSet->CreateAnchor();
+ nFlags |= SelectionEngineFlags::HAS_ANCH;
+ }
+ }
+ else
+ {
+ if ( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
+ {
+ if( ShouldDeselect( bMod1 ) )
+ pFunctionSet->DeselectAll();
+ pFunctionSet->CreateAnchor();
+ nFlags |= SelectionEngineFlags::HAS_ANCH;
+ }
+ }
+ }
+ else
+ {
+ if ( IsAddMode() )
+ {
+ if ( nFlags & SelectionEngineFlags::HAS_ANCH )
+ {
+ // pFunctionSet->CreateCursor();
+ pFunctionSet->DestroyAnchor();
+ nFlags &= ~SelectionEngineFlags::HAS_ANCH;
+ }
+ }
+ else
+ {
+ if( ShouldDeselect( bMod1 ) )
+ pFunctionSet->DeselectAll();
+ else
+ pFunctionSet->DestroyAnchor();
+ nFlags &= ~SelectionEngineFlags::HAS_ANCH;
+ }
+ }
+}
+
+bool SelectionEngine::SelMouseButtonDown( const MouseEvent& rMEvt )
+{
+ nFlags &= ~SelectionEngineFlags::CMDEVT;
+ if ( !pFunctionSet || rMEvt.GetClicks() > 1 )
+ return false;
+
+ sal_uInt16 nModifier = rMEvt.GetModifier() | nLockedMods;
+ bool nSwap = comphelper::LibreOfficeKit::isActive() && (nModifier & KEY_MOD1) && (nModifier & KEY_MOD2);
+
+ if ( !nSwap && (nModifier & KEY_MOD2) )
+ return false;
+ // in SingleSelection: filter Control-Key,
+ // so that a D&D can be also started with a Ctrl-Click
+ if ( nModifier == KEY_MOD1 && eSelMode == SelectionMode::Single )
+ nModifier = 0;
+
+ Point aPos = rMEvt.GetPosPixel();
+ aLastMove = rMEvt;
+
+ if( !rMEvt.IsRight() )
+ {
+ CaptureMouse();
+ nFlags |= SelectionEngineFlags::IN_SEL;
+ }
+ else
+ {
+ nModifier = 0;
+ }
+
+ if (nSwap)
+ {
+ pFunctionSet->CreateAnchor();
+ pFunctionSet->SetCursorAtPoint( aPos );
+ return true;
+ }
+
+ switch ( nModifier )
+ {
+ case 0: // KEY_NO_KEY
+ {
+ bool bSelAtPoint = pFunctionSet->IsSelectionAtPoint( aPos );
+ nFlags &= ~SelectionEngineFlags::IN_ADD;
+ if ( (nFlags & SelectionEngineFlags::DRG_ENAB) && bSelAtPoint )
+ {
+ nFlags |= SelectionEngineFlags::WAIT_UPEVT;
+ nFlags &= ~SelectionEngineFlags::IN_SEL;
+ ReleaseMouse();
+ return true; // wait for STARTDRAG-Command-Event
+ }
+ if ( eSelMode != SelectionMode::Single )
+ {
+ if( !IsAddMode() )
+ pFunctionSet->DeselectAll();
+ else
+ pFunctionSet->DestroyAnchor();
+ nFlags &= ~SelectionEngineFlags::HAS_ANCH; // bHasAnchor = false;
+ }
+ pFunctionSet->SetCursorAtPoint( aPos );
+ // special case Single-Selection, to enable simple Select+Drag
+ if (eSelMode == SelectionMode::Single && (nFlags & SelectionEngineFlags::DRG_ENAB))
+ nFlags |= SelectionEngineFlags::WAIT_UPEVT;
+ return true;
+ }
+
+ case KEY_SHIFT:
+ if ( eSelMode == SelectionMode::Single )
+ {
+ ReleaseMouse();
+ nFlags &= ~SelectionEngineFlags::IN_SEL;
+ pFunctionSet->SetCursorAtPoint(aPos);
+ return false;
+ }
+ if ( nFlags & SelectionEngineFlags::ADD_ALW )
+ nFlags |= SelectionEngineFlags::IN_ADD;
+ else
+ nFlags &= ~SelectionEngineFlags::IN_ADD;
+
+ if( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
+ {
+ if ( !(nFlags & SelectionEngineFlags::IN_ADD) )
+ pFunctionSet->DeselectAll();
+ pFunctionSet->CreateAnchor();
+ nFlags |= SelectionEngineFlags::HAS_ANCH;
+ }
+ pFunctionSet->SetCursorAtPoint( aPos );
+ return true;
+
+ case KEY_MOD1:
+ // allow Control only for Multi-Select
+ if ( eSelMode != SelectionMode::Multiple )
+ {
+ nFlags &= ~SelectionEngineFlags::IN_SEL;
+ ReleaseMouse();
+ return true; // skip Mouse-Click
+ }
+ if ( nFlags & SelectionEngineFlags::HAS_ANCH )
+ {
+ // pFunctionSet->CreateCursor();
+ pFunctionSet->DestroyAnchor();
+ nFlags &= ~SelectionEngineFlags::HAS_ANCH;
+ }
+ if ( pFunctionSet->IsSelectionAtPoint( aPos ) )
+ {
+ pFunctionSet->DeselectAtPoint( aPos );
+ pFunctionSet->SetCursorAtPoint( aPos, true );
+ }
+ else
+ {
+ pFunctionSet->SetCursorAtPoint( aPos );
+ }
+ return true;
+
+ case KEY_SHIFT + KEY_MOD1:
+ if ( eSelMode != SelectionMode::Multiple )
+ {
+ ReleaseMouse();
+ nFlags &= ~SelectionEngineFlags::IN_SEL;
+ return false;
+ }
+ nFlags |= SelectionEngineFlags::IN_ADD; //bIsInAddMode = true;
+ if ( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
+ {
+ pFunctionSet->CreateAnchor();
+ nFlags |= SelectionEngineFlags::HAS_ANCH;
+ }
+ pFunctionSet->SetCursorAtPoint( aPos );
+ return true;
+ }
+
+ return false;
+}
+
+bool SelectionEngine::SelMouseButtonUp( const MouseEvent& rMEvt )
+{
+ aWTimer.Stop();
+ if (!pFunctionSet)
+ {
+ const SelectionEngineFlags nMask = SelectionEngineFlags::CMDEVT | SelectionEngineFlags::WAIT_UPEVT | SelectionEngineFlags::IN_SEL;
+ nFlags &= ~nMask;
+ return false;
+ }
+
+ if (!rMEvt.IsRight())
+ ReleaseMouse();
+
+#if defined IOS || defined ANDROID
+ const bool bDoMessWithSelection = !rMEvt.IsRight();
+#else
+ constexpr bool bDoMessWithSelection = true;
+#endif
+
+ if( (nFlags & SelectionEngineFlags::WAIT_UPEVT) && !(nFlags & SelectionEngineFlags::CMDEVT) &&
+ eSelMode != SelectionMode::Single)
+ {
+ // MouseButtonDown in Sel but no CommandEvent yet
+ // ==> deselect
+ sal_uInt16 nModifier = aLastMove.GetModifier() | nLockedMods;
+ if( nModifier == KEY_MOD1 || IsAlwaysAdding() )
+ {
+ if( !(nModifier & KEY_SHIFT) )
+ {
+ pFunctionSet->DestroyAnchor();
+ nFlags &= ~SelectionEngineFlags::HAS_ANCH; // uncheck anchor
+ }
+ pFunctionSet->DeselectAtPoint( aLastMove.GetPosPixel() );
+ nFlags &= ~SelectionEngineFlags::HAS_ANCH; // uncheck anchor
+ if (bDoMessWithSelection)
+ pFunctionSet->SetCursorAtPoint( aLastMove.GetPosPixel(), true );
+ }
+ else
+ {
+ if (bDoMessWithSelection)
+ pFunctionSet->DeselectAll();
+ nFlags &= ~SelectionEngineFlags::HAS_ANCH; // uncheck anchor
+ if (bDoMessWithSelection)
+ pFunctionSet->SetCursorAtPoint( aLastMove.GetPosPixel() );
+ }
+ }
+
+ const SelectionEngineFlags nMask = SelectionEngineFlags::CMDEVT | SelectionEngineFlags::WAIT_UPEVT | SelectionEngineFlags::IN_SEL;
+ nFlags &= ~nMask;
+ return true;
+}
+
+void SelectionEngine::ReleaseMouse()
+{
+ if (!pWin || !pWin->IsMouseCaptured())
+ return;
+ pWin->ReleaseMouse();
+}
+
+void SelectionEngine::CaptureMouse()
+{
+ if (!pWin || pWin->IsMouseCaptured())
+ return;
+ pWin->CaptureMouse();
+}
+
+bool SelectionEngine::SelMouseMove( const MouseEvent& rMEvt )
+{
+
+ if ( !pFunctionSet || !(nFlags & SelectionEngineFlags::IN_SEL) ||
+ (nFlags & (SelectionEngineFlags::CMDEVT | SelectionEngineFlags::WAIT_UPEVT)) )
+ return false;
+
+ if( !(nFlags & SelectionEngineFlags::EXPANDONMOVE) )
+ return false; // wait for DragEvent!
+
+ aLastMove = rMEvt;
+ // if the mouse is outside the area, the frequency of
+ // SetCursorAtPoint() is only set by the Timer
+ if( aWTimer.IsActive() && !aArea.Contains( rMEvt.GetPosPixel() ))
+ return true;
+
+ aWTimer.SetTimeout( nUpdateInterval );
+ if (!comphelper::LibreOfficeKit::isActive())
+ // Generating fake mouse moves does not work with LOK.
+ aWTimer.Start();
+ if ( eSelMode != SelectionMode::Single )
+ {
+ if ( !(nFlags & SelectionEngineFlags::HAS_ANCH) )
+ {
+ pFunctionSet->CreateAnchor();
+ nFlags |= SelectionEngineFlags::HAS_ANCH;
+ }
+ }
+
+ pFunctionSet->SetCursorAtPoint( rMEvt.GetPosPixel() );
+
+ return true;
+}
+
+void SelectionEngine::SetWindow( vcl::Window* pNewWin )
+{
+ if( pNewWin != pWin )
+ {
+ if (nFlags & SelectionEngineFlags::IN_SEL)
+ ReleaseMouse();
+ pWin = pNewWin;
+ if (nFlags & SelectionEngineFlags::IN_SEL)
+ CaptureMouse();
+ }
+}
+
+void SelectionEngine::Reset()
+{
+ aWTimer.Stop();
+ if (nFlags & SelectionEngineFlags::IN_SEL)
+ ReleaseMouse();
+ nFlags &= ~SelectionEngineFlags(SelectionEngineFlags::HAS_ANCH | SelectionEngineFlags::IN_SEL);
+ nLockedMods = 0;
+}
+
+bool SelectionEngine::Command( const CommandEvent& rCEvt )
+{
+ // Timer aWTimer is active during enlarging a selection
+ if ( !pFunctionSet || aWTimer.IsActive() )
+ return false;
+ aWTimer.Stop();
+ if ( rCEvt.GetCommand() != CommandEventId::StartDrag )
+ return false;
+
+ nFlags |= SelectionEngineFlags::CMDEVT;
+ if ( nFlags & SelectionEngineFlags::DRG_ENAB )
+ {
+ SAL_WARN_IF( !rCEvt.IsMouseEvent(), "vcl", "STARTDRAG: Not a MouseEvent" );
+ if ( pFunctionSet->IsSelectionAtPoint( rCEvt.GetMousePosPixel() ) )
+ {
+ aLastMove = MouseEvent( rCEvt.GetMousePosPixel(),
+ aLastMove.GetClicks(), aLastMove.GetMode(),
+ aLastMove.GetButtons(), aLastMove.GetModifier() );
+ pFunctionSet->BeginDrag();
+ const SelectionEngineFlags nMask = SelectionEngineFlags::CMDEVT|SelectionEngineFlags::WAIT_UPEVT|SelectionEngineFlags::IN_SEL;
+ nFlags &= ~nMask;
+ }
+ else
+ nFlags &= ~SelectionEngineFlags::CMDEVT;
+ }
+ else
+ nFlags &= ~SelectionEngineFlags::CMDEVT;
+ return true;
+}
+
+void SelectionEngine::SetUpdateInterval( sal_uInt64 nInterval )
+{
+ if (nInterval < SELENG_AUTOREPEAT_INTERVAL_MIN)
+ // Set a lower threshold. On Windows, setting this value too low
+ // would cause selection to get updated indefinitely.
+ nInterval = SELENG_AUTOREPEAT_INTERVAL_MIN;
+
+ if (nUpdateInterval == nInterval)
+ // no update needed.
+ return;
+
+ if (aWTimer.IsActive())
+ {
+ // reset the timer right away on interval change.
+ aWTimer.Stop();
+ aWTimer.SetTimeout(nInterval);
+ aWTimer.Start();
+ }
+ else
+ aWTimer.SetTimeout(nInterval);
+
+ nUpdateInterval = nInterval;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/settings.cxx b/vcl/source/window/settings.cxx
new file mode 100644
index 0000000000..329b63038f
--- /dev/null
+++ b/vcl/source/window/settings.cxx
@@ -0,0 +1,255 @@
+/* -*- 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 <i18nlangtag/languagetag.hxx>
+#include <i18nlangtag/mslangid.hxx>
+
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <vcl/settings.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+#include <unotools/configmgr.hxx>
+#include <unotools/confignode.hxx>
+
+#include <comphelper/processfactory.hxx>
+
+#include <salframe.hxx>
+#include <brdwin.hxx>
+
+#include <window.h>
+
+namespace vcl {
+
+void WindowOutputDevice::SetSettings( const AllSettings& rSettings )
+{
+ SetSettings( rSettings, false );
+}
+
+void WindowOutputDevice::SetSettings( const AllSettings& rSettings, bool bChild )
+{
+
+ if ( auto pBorderWindow = mxOwnerWindow->mpWindowImpl->mpBorderWindow.get() )
+ {
+ static_cast<vcl::WindowOutputDevice*>(pBorderWindow->GetOutDev())->SetSettings( rSettings, false );
+ if ( (pBorderWindow->GetType() == WindowType::BORDERWINDOW) &&
+ static_cast<ImplBorderWindow*>(pBorderWindow)->mpMenuBarWindow )
+ static_cast<vcl::WindowOutputDevice*>(static_cast<ImplBorderWindow*>(pBorderWindow)->mpMenuBarWindow->GetOutDev())->SetSettings( rSettings, true );
+ }
+
+ AllSettings aOldSettings(*moSettings);
+ OutputDevice::SetSettings( rSettings );
+ AllSettingsFlags nChangeFlags = aOldSettings.GetChangeFlags( rSettings );
+
+ // recalculate AppFont-resolution and DPI-resolution
+ mxOwnerWindow->ImplInitResolutionSettings();
+
+ if ( bool(nChangeFlags) )
+ {
+ DataChangedEvent aDCEvt( DataChangedEventType::SETTINGS, &aOldSettings, nChangeFlags );
+ mxOwnerWindow->DataChanged( aDCEvt );
+ }
+
+ if ( bChild )
+ {
+ vcl::Window* pChild = mxOwnerWindow->mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ static_cast<vcl::WindowOutputDevice*>(pChild->GetOutDev())->SetSettings( rSettings, bChild );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+ }
+}
+
+void Window::UpdateSettings( const AllSettings& rSettings, bool bChild )
+{
+
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ mpWindowImpl->mpBorderWindow->UpdateSettings( rSettings );
+ if (mpWindowImpl->mpBorderWindow->GetType() == WindowType::BORDERWINDOW)
+ {
+ ImplBorderWindow* pImpl = static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get());
+ if (pImpl->mpMenuBarWindow)
+ pImpl->mpMenuBarWindow->UpdateSettings(rSettings, true);
+ if (pImpl->mpNotebookBar)
+ pImpl->mpNotebookBar->UpdateSettings(rSettings, true);
+ }
+ }
+
+ AllSettings aOldSettings(*mpWindowImpl->mxOutDev->moSettings);
+ AllSettingsFlags nChangeFlags = mpWindowImpl->mxOutDev->moSettings->Update( AllSettings::GetWindowUpdate(), rSettings );
+
+ // recalculate AppFont-resolution and DPI-resolution
+ ImplInitResolutionSettings();
+
+ /* #i73785#
+ * do not overwrite a WheelBehavior with false
+ * this looks kind of a hack, but WheelBehavior
+ * is always a local change, not a system property,
+ * so we can spare all our users the hassle of reacting on
+ * this in their respective DataChanged.
+ */
+ MouseSettings aSet( mpWindowImpl->mxOutDev->moSettings->GetMouseSettings() );
+ aSet.SetWheelBehavior( aOldSettings.GetMouseSettings().GetWheelBehavior() );
+ mpWindowImpl->mxOutDev->moSettings->SetMouseSettings( aSet );
+
+ if( (nChangeFlags & AllSettingsFlags::STYLE) && IsBackground() )
+ {
+ Wallpaper aWallpaper = GetBackground();
+ if( !aWallpaper.IsBitmap() && !aWallpaper.IsGradient() )
+ {
+ if ( mpWindowImpl->mnStyle & WB_3DLOOK )
+ {
+ if (aOldSettings.GetStyleSettings().GetFaceColor() != rSettings.GetStyleSettings().GetFaceColor())
+ SetBackground( Wallpaper( rSettings.GetStyleSettings().GetFaceColor() ) );
+ }
+ else
+ {
+ if (aOldSettings.GetStyleSettings().GetWindowColor() != rSettings.GetStyleSettings().GetWindowColor())
+ SetBackground( Wallpaper( rSettings.GetStyleSettings().GetWindowColor() ) );
+ }
+ }
+ }
+
+ if ( bool(nChangeFlags) )
+ {
+ DataChangedEvent aDCEvt( DataChangedEventType::SETTINGS, &aOldSettings, nChangeFlags );
+ DataChanged( aDCEvt );
+ // notify data change handler
+ CallEventListeners( VclEventId::WindowDataChanged, &aDCEvt);
+ }
+
+ if ( bChild )
+ {
+ vcl::Window* pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->UpdateSettings( rSettings, bChild );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+ }
+}
+
+void Window::ImplUpdateGlobalSettings( AllSettings& rSettings, bool bCallHdl ) const
+{
+ StyleSettings aTmpSt( rSettings.GetStyleSettings() );
+ aTmpSt.SetHighContrastMode( false );
+ rSettings.SetStyleSettings( aTmpSt );
+ ImplGetFrame()->UpdateSettings( rSettings );
+
+ StyleSettings aStyleSettings = rSettings.GetStyleSettings();
+
+ vcl::Font aFont = aStyleSettings.GetMenuFont();
+ int defFontheight = aFont.GetFontHeight();
+
+ // if the UI is korean, chinese or another locale
+ // where the system font size is known to be often too small to
+ // generate readable fonts enforce a minimum font size of 9 points
+ bool bBrokenLangFontHeight = MsLangId::isCJK(Application::GetSettings().GetUILanguageTag().getLanguageType());
+ if (bBrokenLangFontHeight)
+ defFontheight = std::max(9, defFontheight);
+
+ // i22098, toolfont will be scaled differently to avoid bloated rulers and status bars for big fonts
+ int toolfontheight = defFontheight;
+ if( toolfontheight > 9 )
+ toolfontheight = (defFontheight+8) / 2;
+
+ aFont = aStyleSettings.GetAppFont();
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetAppFont( aFont );
+ aFont = aStyleSettings.GetTitleFont();
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetTitleFont( aFont );
+ aFont = aStyleSettings.GetFloatTitleFont();
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetFloatTitleFont( aFont );
+ // keep menu and help font size from system unless in broken locale size
+ if( bBrokenLangFontHeight )
+ {
+ aFont = aStyleSettings.GetMenuFont();
+ if( aFont.GetFontHeight() < defFontheight )
+ {
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetMenuFont( aFont );
+ }
+ aFont = aStyleSettings.GetHelpFont();
+ if( aFont.GetFontHeight() < defFontheight )
+ {
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetHelpFont( aFont );
+ }
+ }
+
+ // use different height for toolfont
+ aFont = aStyleSettings.GetToolFont();
+ aFont.SetFontHeight( toolfontheight );
+ aStyleSettings.SetToolFont( aFont );
+
+ aFont = aStyleSettings.GetLabelFont();
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetLabelFont( aFont );
+ aFont = aStyleSettings.GetRadioCheckFont();
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetRadioCheckFont( aFont );
+ aFont = aStyleSettings.GetPushButtonFont();
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetPushButtonFont( aFont );
+ aFont = aStyleSettings.GetFieldFont();
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetFieldFont( aFont );
+ aFont = aStyleSettings.GetIconFont();
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetIconFont( aFont );
+ aFont = aStyleSettings.GetTabFont();
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetTabFont( aFont );
+ aFont = aStyleSettings.GetGroupFont();
+ aFont.SetFontHeight( defFontheight );
+ aStyleSettings.SetGroupFont( aFont );
+
+ static const bool bFuzzing = utl::ConfigManager::IsFuzzing();
+ if (!bFuzzing)
+ {
+ static const char* pEnvHC = getenv( "SAL_FORCE_HC" );
+ const bool bForceHCMode = pEnvHC && *pEnvHC;
+ if (bForceHCMode)
+ aStyleSettings.SetHighContrastMode( true );
+ else
+ {
+ sal_Int32 nHighContrastMode = officecfg::Office::Common::Accessibility::HighContrast::get();
+ if (nHighContrastMode != 0) // 0 Automatic, 1 Disable, 2 Enable
+ {
+ const bool bEnable = nHighContrastMode == 2;
+ aStyleSettings.SetHighContrastMode(bEnable);
+ }
+ }
+ }
+
+ rSettings.SetStyleSettings( aStyleSettings );
+
+ if ( bCallHdl )
+ GetpApp()->OverrideSystemSettings( rSettings );
+}
+
+} /*namespace vcl*/
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/split.cxx b/vcl/source/window/split.cxx
new file mode 100644
index 0000000000..df631b270b
--- /dev/null
+++ b/vcl/source/window/split.cxx
@@ -0,0 +1,698 @@
+/* -*- 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 <tools/poly.hxx>
+
+#include <vcl/event.hxx>
+#include <vcl/split.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syswin.hxx>
+#include <vcl/taskpanelist.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/lazydelete.hxx>
+
+#include <window.h>
+
+namespace
+{
+ Wallpaper& ImplBlackWall()
+ {
+ static vcl::DeleteOnDeinit< Wallpaper > SINGLETON(COL_BLACK);
+ return *SINGLETON.get();
+ }
+ Wallpaper& ImplWhiteWall()
+ {
+ static vcl::DeleteOnDeinit< Wallpaper > SINGLETON(COL_LIGHTGRAY);
+ return *SINGLETON.get();
+ }
+}
+
+// Should only be called from an ImplInit method for initialization or
+// after checking bNew is different from the current mbHorzSplit value.
+// The public method that does that check is Splitter::SetHorizontal().
+void Splitter::ImplInitHorVer(bool bNew)
+{
+ mbHorzSplit = bNew;
+
+ PointerStyle ePointerStyle;
+ const StyleSettings& rSettings = GetSettings().GetStyleSettings();
+
+ if ( mbHorzSplit )
+ {
+ ePointerStyle = PointerStyle::HSplit;
+ SetSizePixel( Size( StyleSettings::GetSplitSize(), rSettings.GetScrollBarSize() ) );
+ }
+ else
+ {
+ ePointerStyle = PointerStyle::VSplit;
+ SetSizePixel( Size( rSettings.GetScrollBarSize(), StyleSettings::GetSplitSize() ) );
+ }
+
+ SetPointer( ePointerStyle );
+}
+
+void Splitter::ImplInit( vcl::Window* pParent, WinBits nWinStyle )
+{
+ Window::ImplInit( pParent, nWinStyle, nullptr );
+
+ mpRefWin = pParent;
+
+ ImplInitHorVer(nWinStyle & WB_HSCROLL);
+
+ if( GetSettings().GetStyleSettings().GetFaceColor().IsDark() )
+ SetBackground( ImplWhiteWall() );
+ else
+ SetBackground( ImplBlackWall() );
+
+ TaskPaneList *pTList = GetSystemWindow()->GetTaskPaneList();
+ pTList->AddWindow( this );
+}
+
+void Splitter::ImplSplitMousePos( Point& rPos )
+{
+ if ( mbHorzSplit )
+ {
+ if ( rPos.X() > maDragRect.Right()-1 )
+ rPos.setX( maDragRect.Right()-1 );
+ if ( rPos.X() < maDragRect.Left()+1 )
+ rPos.setX( maDragRect.Left()+1 );
+ }
+ else
+ {
+ if ( rPos.Y() > maDragRect.Bottom()-1 )
+ rPos.setY( maDragRect.Bottom()-1 );
+ if ( rPos.Y() < maDragRect.Top()+1 )
+ rPos.setY( maDragRect.Top()+1 );
+ }
+}
+
+void Splitter::ImplDrawSplitter()
+{
+ tools::Rectangle aInvRect( maDragRect );
+
+ if ( mbHorzSplit )
+ {
+ aInvRect.SetLeft( maDragPos.X() - 1 );
+ aInvRect.SetRight( maDragPos.X() + 1 );
+ }
+ else
+ {
+ aInvRect.SetTop( maDragPos.Y() - 1 );
+ aInvRect.SetBottom( maDragPos.Y() + 1 );
+ }
+
+ mpRefWin->InvertTracking( mpRefWin->PixelToLogic(aInvRect), ShowTrackFlags::Split );
+}
+
+Splitter::Splitter( vcl::Window* pParent, WinBits nStyle ) :
+ Window( WindowType::SPLITTER ),
+ mpRefWin( nullptr ),
+ mnSplitPos( 0 ),
+ mnLastSplitPos( 0 ),
+ mnStartSplitPos( 0 ),
+ mbDragFull( false ),
+ mbKbdSplitting( false ),
+ mbInKeyEvent( false ),
+ mnKeyboardStepSize( SPLITTER_DEFAULTSTEPSIZE )
+{
+ ImplGetWindowImpl()->mbSplitter = true;
+
+ ImplInit( pParent, nStyle );
+
+ GetOutDev()->SetLineColor();
+ GetOutDev()->SetFillColor();
+}
+
+Splitter::~Splitter()
+{
+ disposeOnce();
+}
+
+void Splitter::dispose()
+{
+ SystemWindow *pSysWin = GetSystemWindow();
+ if(pSysWin)
+ {
+ TaskPaneList *pTList = pSysWin->GetTaskPaneList();
+ pTList->RemoveWindow(this);
+ }
+ mpRefWin.clear();
+ Window::dispose();
+}
+
+void Splitter::SetHorizontal(bool bNew)
+{
+ if(bNew != mbHorzSplit)
+ {
+ ImplInitHorVer(bNew);
+ }
+}
+
+void Splitter::SetKeyboardStepSize( tools::Long nStepSize )
+{
+ mnKeyboardStepSize = nStepSize;
+}
+
+Splitter* Splitter::ImplFindSibling()
+{
+ // look for another splitter with the same parent but different orientation
+ vcl::Window *pWin = GetParent()->GetWindow( GetWindowType::FirstChild );
+ Splitter *pSplitter = nullptr;
+ while( pWin )
+ {
+ if( pWin->ImplIsSplitter() )
+ {
+ pSplitter = static_cast<Splitter*>(pWin);
+ if( pSplitter != this && IsHorizontal() != pSplitter->IsHorizontal() )
+ return pSplitter;
+ }
+ pWin = pWin->GetWindow( GetWindowType::Next );
+ }
+ return nullptr;
+}
+
+bool Splitter::ImplSplitterActive()
+{
+ // is splitter in document or at scrollbar handle ?
+
+ bool bActive = true;
+ const StyleSettings& rSettings = GetSettings().GetStyleSettings();
+ tools::Long nA = rSettings.GetScrollBarSize();
+ tools::Long nB = StyleSettings::GetSplitSize();
+
+ Size aSize = GetOutDev()->GetOutputSize();
+ if ( mbHorzSplit )
+ {
+ if( aSize.Width() == nB && aSize.Height() == nA )
+ bActive = false;
+ }
+ else
+ {
+ if( aSize.Width() == nA && aSize.Height() == nB )
+ bActive = false;
+ }
+ return bActive;
+}
+
+void Splitter::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( rMEvt.GetClicks() == 2 )
+ {
+ if ( mnLastSplitPos != mnSplitPos )
+ {
+ StartSplit();
+ Point aPos = rMEvt.GetPosPixel();
+ if ( mbHorzSplit )
+ aPos.setX( mnLastSplitPos );
+ else
+ aPos.setY( mnLastSplitPos );
+ ImplSplitMousePos( aPos );
+ tools::Long nTemp = mnSplitPos;
+ if ( mbHorzSplit )
+ SetSplitPosPixel( aPos.X() );
+ else
+ SetSplitPosPixel( aPos.Y() );
+ mnLastSplitPos = nTemp;
+ Split();
+ EndSplit();
+ }
+ }
+ else
+ StartDrag();
+}
+
+void Splitter::Tracking( const TrackingEvent& rTEvt )
+{
+ if ( rTEvt.IsTrackingEnded() )
+ {
+ if ( !mbDragFull )
+ ImplDrawSplitter();
+
+ if ( !rTEvt.IsTrackingCanceled() )
+ {
+ tools::Long nNewPos;
+ if ( mbHorzSplit )
+ nNewPos = maDragPos.X();
+ else
+ nNewPos = maDragPos.Y();
+ if ( nNewPos != mnStartSplitPos )
+ {
+ SetSplitPosPixel( nNewPos );
+ mnLastSplitPos = 0;
+ Split();
+ }
+ EndSplit();
+ }
+ else if ( mbDragFull )
+ {
+ SetSplitPosPixel( mnStartSplitPos );
+ Split();
+ }
+ mnStartSplitPos = 0;
+ }
+ else
+ {
+ //Point aNewPos = mpRefWin->ScreenToOutputPixel( OutputToScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
+ Point aNewPos = mpRefWin->NormalizedScreenToOutputPixel( OutputToNormalizedScreenPixel( rTEvt.GetMouseEvent().GetPosPixel() ) );
+ ImplSplitMousePos( aNewPos );
+
+ if ( mbHorzSplit )
+ {
+ if ( aNewPos.X() == maDragPos.X() )
+ return;
+ }
+ else
+ {
+ if ( aNewPos.Y() == maDragPos.Y() )
+ return;
+ }
+
+ if ( mbDragFull )
+ {
+ maDragPos = aNewPos;
+ tools::Long nNewPos;
+ if ( mbHorzSplit )
+ nNewPos = maDragPos.X();
+ else
+ nNewPos = maDragPos.Y();
+ if ( nNewPos != mnSplitPos )
+ {
+ SetSplitPosPixel( nNewPos );
+ mnLastSplitPos = 0;
+ Split();
+ }
+
+ GetParent()->PaintImmediately();
+ }
+ else
+ {
+ ImplDrawSplitter();
+ maDragPos = aNewPos;
+ ImplDrawSplitter();
+ }
+ }
+}
+
+void Splitter::ImplKbdTracking( vcl::KeyCode aKeyCode )
+{
+ sal_uInt16 nCode = aKeyCode.GetCode();
+ if ( nCode == KEY_ESCAPE || nCode == KEY_RETURN )
+ {
+ if( !mbKbdSplitting )
+ return;
+ else
+ mbKbdSplitting = false;
+
+ if ( nCode != KEY_ESCAPE )
+ {
+ tools::Long nNewPos;
+ if ( mbHorzSplit )
+ nNewPos = maDragPos.X();
+ else
+ nNewPos = maDragPos.Y();
+ if ( nNewPos != mnStartSplitPos )
+ {
+ SetSplitPosPixel( nNewPos );
+ mnLastSplitPos = 0;
+ Split();
+ }
+ }
+ else
+ {
+ SetSplitPosPixel( mnStartSplitPos );
+ Split();
+ EndSplit();
+ }
+ mnStartSplitPos = 0;
+ }
+ else
+ {
+ Point aNewPos;
+ Size aSize = mpRefWin->GetOutDev()->GetOutputSize();
+ Point aPos = GetPosPixel();
+ // depending on the position calc allows continuous moves or snaps to row/columns
+ // continuous mode is active when position is at the origin or end of the splitter
+ // otherwise snap mode is active
+ // default here is snap, holding shift sets continuous mode
+ if( mbHorzSplit )
+ aNewPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aKeyCode.IsShift() ? 0 : aSize.Height()/2);
+ else
+ aNewPos = Point( aKeyCode.IsShift() ? 0 : aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos );
+
+ Point aOldWindowPos = GetPosPixel();
+
+ int maxiter = 500; // avoid endless loop
+ int delta=0;
+ int delta_step = mbHorzSplit ? aSize.Width()/10 : aSize.Height()/10;
+
+ // use the specified step size if it was set
+ if( mnKeyboardStepSize != SPLITTER_DEFAULTSTEPSIZE )
+ delta_step = mnKeyboardStepSize;
+
+ while( maxiter-- && aOldWindowPos == GetPosPixel() )
+ {
+ // inc/dec position until application performs changes
+ // thus a single key press really moves the splitter
+ if( aKeyCode.IsShift() )
+ delta++;
+ else
+ delta += delta_step;
+
+ switch( nCode )
+ {
+ case KEY_LEFT:
+ aNewPos.AdjustX( -delta );
+ break;
+ case KEY_RIGHT:
+ aNewPos.AdjustX(delta );
+ break;
+ case KEY_UP:
+ aNewPos.AdjustY( -delta );
+ break;
+ case KEY_DOWN:
+ aNewPos.AdjustY(delta );
+ break;
+ default:
+ maxiter = 0; // leave loop
+ break;
+ }
+ ImplSplitMousePos( aNewPos );
+
+ if ( mbHorzSplit )
+ {
+ if ( aNewPos.X() == maDragPos.X() )
+ continue;
+ }
+ else
+ {
+ if ( aNewPos.Y() == maDragPos.Y() )
+ continue;
+ }
+
+ maDragPos = aNewPos;
+ tools::Long nNewPos;
+ if ( mbHorzSplit )
+ nNewPos = maDragPos.X();
+ else
+ nNewPos = maDragPos.Y();
+ if ( nNewPos != mnSplitPos )
+ {
+ SetSplitPosPixel( nNewPos );
+ mnLastSplitPos = 0;
+ Split();
+ }
+ GetParent()->PaintImmediately();
+ }
+ }
+}
+
+void Splitter::StartSplit()
+{
+ maStartSplitHdl.Call( this );
+}
+
+void Splitter::Split()
+{
+ maSplitHdl.Call( this );
+}
+
+void Splitter::EndSplit()
+{
+ maEndSplitHdl.Call( this );
+}
+
+void Splitter::SetDragRectPixel( const tools::Rectangle& rDragRect, vcl::Window* _pRefWin )
+{
+ maDragRect = rDragRect;
+ if ( !_pRefWin )
+ mpRefWin = GetParent();
+ else
+ mpRefWin = _pRefWin;
+}
+
+void Splitter::SetSplitPosPixel( tools::Long nNewPos )
+{
+ mnSplitPos = nNewPos;
+}
+
+void Splitter::StartDrag()
+{
+ if ( IsTracking() )
+ return;
+
+ StartSplit();
+
+ // Tracking starten
+ StartTracking();
+
+ // Start-Position ermitteln
+ maDragPos = mpRefWin->GetPointerPosPixel();
+ ImplSplitMousePos( maDragPos );
+ if ( mbHorzSplit )
+ mnStartSplitPos = maDragPos.X();
+ else
+ mnStartSplitPos = maDragPos.Y();
+
+ mbDragFull = bool(Application::GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Split);
+ if ( !mbDragFull )
+ ImplDrawSplitter();
+}
+
+void Splitter::ImplStartKbdSplitting()
+{
+ if( mbKbdSplitting )
+ return;
+
+ mbKbdSplitting = true;
+
+ StartSplit();
+
+ // determine start position
+ // because we have no mouse position we take either the position
+ // of the splitter window or the last split position
+ // the other coordinate is just the center of the reference window
+ Size aSize = mpRefWin->GetOutDev()->GetOutputSize();
+ Point aPos = GetPosPixel();
+ if( mbHorzSplit )
+ maDragPos = Point( ImplSplitterActive() ? aPos.X() : mnSplitPos, aSize.Height()/2 );
+ else
+ maDragPos = Point( aSize.Width()/2, ImplSplitterActive() ? aPos.Y() : mnSplitPos );
+ ImplSplitMousePos( maDragPos );
+ if ( mbHorzSplit )
+ mnStartSplitPos = maDragPos.X();
+ else
+ mnStartSplitPos = maDragPos.Y();
+}
+
+void Splitter::ImplRestoreSplitter()
+{
+ // set splitter in the center of the ref window
+ StartSplit();
+ Size aSize = mpRefWin->GetOutDev()->GetOutputSize();
+ Point aPos( aSize.Width()/2 , aSize.Height()/2);
+ if ( mnLastSplitPos != mnSplitPos && mnLastSplitPos > 5 )
+ {
+ // restore last pos if it was a useful position (>5)
+ if ( mbHorzSplit )
+ aPos.setX( mnLastSplitPos );
+ else
+ aPos.setY( mnLastSplitPos );
+ }
+
+ ImplSplitMousePos( aPos );
+ tools::Long nTemp = mnSplitPos;
+ if ( mbHorzSplit )
+ SetSplitPosPixel( aPos.X() );
+ else
+ SetSplitPosPixel( aPos.Y() );
+ mnLastSplitPos = nTemp;
+ Split();
+ EndSplit();
+}
+
+void Splitter::GetFocus()
+{
+ if( !ImplSplitterActive() )
+ ImplRestoreSplitter();
+
+ Invalidate();
+}
+
+void Splitter::LoseFocus()
+{
+ if( mbKbdSplitting )
+ {
+ vcl::KeyCode aReturnKey( KEY_RETURN );
+ ImplKbdTracking( aReturnKey );
+ mbKbdSplitting = false;
+ }
+ Invalidate();
+}
+
+void Splitter::KeyInput( const KeyEvent& rKEvt )
+{
+ if( mbInKeyEvent )
+ return;
+
+ mbInKeyEvent = true;
+
+ Splitter *pSibling = ImplFindSibling();
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = aKeyCode.GetCode();
+ switch ( nCode )
+ {
+ case KEY_UP:
+ case KEY_DOWN:
+ if( !mbHorzSplit )
+ {
+ ImplStartKbdSplitting();
+ ImplKbdTracking( aKeyCode );
+ }
+ else
+ {
+ if( pSibling )
+ {
+ pSibling->GrabFocus();
+ pSibling->KeyInput( rKEvt );
+ }
+ }
+ break;
+ case KEY_RIGHT:
+ case KEY_LEFT:
+ if( mbHorzSplit )
+ {
+ ImplStartKbdSplitting();
+ ImplKbdTracking( aKeyCode );
+ }
+ else
+ {
+ if( pSibling )
+ {
+ pSibling->GrabFocus();
+ pSibling->KeyInput( rKEvt );
+ }
+ }
+ break;
+
+ case KEY_DELETE:
+ if( ImplSplitterActive() )
+ {
+ if( mbKbdSplitting )
+ {
+ vcl::KeyCode aKey( KEY_ESCAPE );
+ ImplKbdTracking( aKey );
+ }
+
+ StartSplit();
+ Point aPos;
+ if ( mbHorzSplit )
+ aPos.setX( 0 );
+ else
+ aPos.setY( 0 );
+ ImplSplitMousePos( aPos );
+ tools::Long nTemp = mnSplitPos;
+ if ( mbHorzSplit )
+ SetSplitPosPixel( aPos.X() );
+ else
+ SetSplitPosPixel( aPos.Y() );
+ mnLastSplitPos = nTemp;
+ Split();
+ EndSplit();
+
+ // Shift-Del deletes both splitters
+ if( aKeyCode.IsShift() && pSibling )
+ pSibling->KeyInput( rKEvt );
+
+ GrabFocusToDocument();
+ }
+ break;
+
+ case KEY_ESCAPE:
+ if( mbKbdSplitting )
+ ImplKbdTracking( aKeyCode );
+ else
+ GrabFocusToDocument();
+ break;
+
+ case KEY_RETURN:
+ ImplKbdTracking( aKeyCode );
+ GrabFocusToDocument();
+ break;
+ default: // let any key input fix the splitter
+ Window::KeyInput( rKEvt );
+ GrabFocusToDocument();
+ break;
+ }
+ mbInKeyEvent = false;
+}
+
+void Splitter::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+ if( rDCEvt.GetType() != DataChangedEventType::SETTINGS )
+ return;
+
+ const AllSettings* pOldSettings = rDCEvt.GetOldSettings();
+ if(!pOldSettings)
+ return;
+
+ Color oldFaceColor = pOldSettings->GetStyleSettings().GetFaceColor();
+ Color newFaceColor = Application::GetSettings().GetStyleSettings().GetFaceColor();
+ if( oldFaceColor.IsDark() != newFaceColor.IsDark() )
+ {
+ if( newFaceColor.IsDark() )
+ SetBackground( ImplWhiteWall() );
+ else
+ SetBackground( ImplBlackWall() );
+ }
+}
+
+void Splitter::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rPaintRect)
+{
+ rRenderContext.DrawRect(rPaintRect);
+
+ tools::Polygon aPoly(rPaintRect);
+ tools::PolyPolygon aPolyPoly(aPoly);
+ rRenderContext.DrawTransparent(aPolyPoly, 85);
+
+ if (mbKbdSplitting)
+ {
+ LineInfo aInfo( LineStyle::Dash );
+ //aInfo.SetDashLen( 2 );
+ //aInfo.SetDashCount( 1 );
+ aInfo.SetDistance( 1 );
+ aInfo.SetDotLen( 2 );
+ aInfo.SetDotCount( 3 );
+
+ rRenderContext.DrawPolyLine( aPoly, aInfo );
+ }
+ else
+ {
+ rRenderContext.DrawRect(rPaintRect);
+ }
+}
+
+Size Splitter::GetOptimalSize() const
+{
+ return LogicToPixel(Size(3, 3), MapMode(MapUnit::MapAppFont));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/splitwin.cxx b/vcl/source/window/splitwin.cxx
new file mode 100644
index 0000000000..f2bba6b2ce
--- /dev/null
+++ b/vcl/source/window/splitwin.cxx
@@ -0,0 +1,2717 @@
+/* -*- 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/safeint.hxx>
+#include <sal/log.hxx>
+
+#include <vcl/event.hxx>
+#include <vcl/wall.hxx>
+#include <vcl/help.hxx>
+#include <vcl/splitwin.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/ptrstyle.hxx>
+
+#include <svdata.hxx>
+#include <strings.hrc>
+
+
+#define SPLITWIN_SPLITSIZEEX 4
+#define SPLITWIN_SPLITSIZEAUTOHIDE 72
+#define SPLITWIN_SPLITSIZEFADE 72
+
+#define SPLIT_HORZ (sal_uInt16(0x0001))
+#define SPLIT_VERT (sal_uInt16(0x0002))
+#define SPLIT_WINDOW (sal_uInt16(0x0004))
+#define SPLIT_NOSPLIT (sal_uInt16(0x8000))
+
+namespace {
+
+class ImplSplitItem
+{
+public:
+ ImplSplitItem();
+
+ tools::Long mnSize;
+ tools::Long mnPixSize;
+ tools::Long mnLeft;
+ tools::Long mnTop;
+ tools::Long mnWidth;
+ tools::Long mnHeight;
+ tools::Long mnSplitPos;
+ tools::Long mnSplitSize;
+ tools::Long mnOldSplitPos;
+ tools::Long mnOldSplitSize;
+ tools::Long mnOldWidth;
+ tools::Long mnOldHeight;
+ std::unique_ptr<ImplSplitSet> mpSet;
+ VclPtr<vcl::Window> mpWindow;
+ VclPtr<vcl::Window> mpOrgParent;
+ sal_uInt16 mnId;
+ SplitWindowItemFlags mnBits;
+ bool mbFixed;
+ bool mbSubSize;
+ /// Minimal width or height of the item. -1 means no restriction.
+ tools::Long mnMinSize;
+ /// Maximal width or height of the item. -1 means no restriction.
+ tools::Long mnMaxSize;
+};
+
+}
+
+class ImplSplitSet
+{
+public:
+ ImplSplitSet();
+
+ std::vector< ImplSplitItem > mvItems;
+ tools::Long mnLastSize;
+ tools::Long mnSplitSize;
+ sal_uInt16 mnId;
+ bool mbCalcPix;
+};
+
+ImplSplitItem::ImplSplitItem()
+ : mnSize(0)
+ , mnPixSize(0)
+ , mnLeft(0)
+ , mnTop(0)
+ , mnWidth(0)
+ , mnHeight(0)
+ , mnSplitPos(0)
+ , mnSplitSize(0)
+ , mnOldSplitPos(0)
+ , mnOldSplitSize(0)
+ , mnOldWidth(0)
+ , mnOldHeight(0)
+ , mnId(0)
+ , mnBits(SplitWindowItemFlags::NONE)
+ , mbFixed(false)
+ , mbSubSize(false)
+ , mnMinSize(-1)
+ , mnMaxSize(-1)
+{
+}
+
+ImplSplitSet::ImplSplitSet() :
+ mnLastSize( 0 ),
+ mnSplitSize( SPLITWIN_SPLITSIZE ),
+ mnId( 0 ),
+ mbCalcPix( true )
+{
+}
+
+/** Check whether the given size is inside the valid range defined by
+ [rItem.mnMinSize,rItem.mnMaxSize]. When it is not inside it then return
+ the upper or lower bound, respectively. Otherwise return the given size
+ unmodified.
+ Note that either mnMinSize and/or mnMaxSize can be -1 in which case the
+ size has not lower or upper bound.
+*/
+namespace {
+ tools::Long ValidateSize (const tools::Long nSize, const ImplSplitItem & rItem)
+ {
+ if (rItem.mnMinSize>=0 && nSize<rItem.mnMinSize)
+ return rItem.mnMinSize;
+ else if (rItem.mnMaxSize>0 && nSize>rItem.mnMaxSize)
+ return rItem.mnMaxSize;
+ else
+ return nSize;
+ }
+}
+
+static void ImplCalcBorder( WindowAlign eAlign,
+ tools::Long& rLeft, tools::Long& rTop,
+ tools::Long& rRight, tools::Long& rBottom )
+{
+ switch ( eAlign )
+ {
+ case WindowAlign::Top:
+ rLeft = 2;
+ rTop = 2;
+ rRight = 2;
+ rBottom = 0;
+ break;
+ case WindowAlign::Left:
+ rLeft = 0;
+ rTop = 2;
+ rRight = 2;
+ rBottom = 2;
+ break;
+ case WindowAlign::Bottom:
+ rLeft = 2;
+ rTop = 0;
+ rRight = 2;
+ rBottom = 2;
+ break;
+ default:
+ rLeft = 0;
+ rTop = 2;
+ rRight = 2;
+ rBottom = 2;
+ break;
+ }
+}
+
+void SplitWindow::ImplDrawBorder(vcl::RenderContext& rRenderContext)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ tools::Long nDX = mnDX;
+ tools::Long nDY = mnDY;
+
+ switch (meAlign)
+ {
+ case WindowAlign::Bottom:
+ break;
+ case WindowAlign::Top:
+ break;
+ case WindowAlign::Left:
+ break;
+ default:
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawLine(Point(0, 0), Point( 0, nDY));
+ rRenderContext.DrawLine(Point(0, nDY), Point(nDX, nDY));
+ }
+}
+
+void SplitWindow::ImplDrawBorderLine(vcl::RenderContext& rRenderContext)
+{
+ if (!mbFadeOut)
+ return;
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ tools::Long nDX = mnDX;
+ tools::Long nDY = mnDY;
+
+ switch (meAlign)
+ {
+ case WindowAlign::Left:
+ rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
+ rRenderContext.DrawLine( Point( nDX-SPLITWIN_SPLITSIZEEXLN-1, 1 ), Point( nDX-SPLITWIN_SPLITSIZEEXLN-1, nDY-2 ) );
+
+ rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
+ rRenderContext.DrawLine( Point( nDX-SPLITWIN_SPLITSIZEEXLN, 1 ), Point( nDX-SPLITWIN_SPLITSIZEEXLN, nDY-3 ) );
+ break;
+ case WindowAlign::Right:
+ break;
+ case WindowAlign::Top:
+ rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
+ rRenderContext.DrawLine( Point( 0, nDY-SPLITWIN_SPLITSIZEEXLN-1 ), Point( nDX-1, nDY-SPLITWIN_SPLITSIZEEXLN-1 ) );
+
+ rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
+ rRenderContext.DrawLine( Point( 0, nDY-SPLITWIN_SPLITSIZEEXLN ), Point( nDX-1, nDY-SPLITWIN_SPLITSIZEEXLN ) );
+ break;
+ case WindowAlign::Bottom:
+ rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
+ rRenderContext.DrawLine( Point( 0, 5 ), Point( nDX-1, 5 ) );
+
+ rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
+ rRenderContext.DrawLine( Point( 0, SPLITWIN_SPLITSIZEEXLN ), Point( nDX-1, SPLITWIN_SPLITSIZEEXLN ) );
+ break;
+ }
+}
+
+static ImplSplitSet* ImplFindSet( ImplSplitSet* pSet, sal_uInt16 nId )
+{
+ if ( pSet->mnId == nId )
+ return pSet;
+
+ std::vector< ImplSplitItem >& rItems = pSet->mvItems;
+
+ for ( const auto& rItem : rItems )
+ {
+ if ( rItem.mnId == nId )
+ return rItem.mpSet.get();
+ }
+
+ for ( auto& rItem : rItems )
+ {
+ if ( rItem.mpSet )
+ {
+ ImplSplitSet* pFindSet = ImplFindSet( rItem.mpSet.get(), nId );
+ if ( pFindSet )
+ return pFindSet;
+ }
+ }
+
+ return nullptr;
+}
+
+static ImplSplitSet* ImplFindItem( ImplSplitSet* pSet, sal_uInt16 nId, sal_uInt16& rPos )
+{
+ size_t nItems = pSet->mvItems.size();
+ std::vector< ImplSplitItem >& rItems = pSet->mvItems;
+
+ for ( size_t i = 0; i < nItems; i++ )
+ {
+ if ( rItems[i].mnId == nId )
+ {
+ rPos = i;
+ return pSet;
+ }
+ }
+
+ for ( auto& rItem : rItems )
+ {
+ if ( rItem.mpSet )
+ {
+ ImplSplitSet* pFindSet = ImplFindItem( rItem.mpSet.get(), nId, rPos );
+ if ( pFindSet )
+ return pFindSet;
+ }
+ }
+
+ return nullptr;
+}
+
+static sal_uInt16 ImplFindItem( ImplSplitSet* pSet, vcl::Window* pWindow )
+{
+ std::vector< ImplSplitItem >& rItems = pSet->mvItems;
+
+ for ( auto& rItem : rItems )
+ {
+ if ( rItem.mpWindow == pWindow )
+ return rItem.mnId;
+ else
+ {
+ if ( rItem.mpSet )
+ {
+ sal_uInt16 nId = ImplFindItem( rItem.mpSet.get(), pWindow );
+ if ( nId )
+ return nId;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static sal_uInt16 ImplFindItem( ImplSplitSet* pSet, const Point& rPos,
+ bool bRows, bool bDown = true )
+{
+ std::vector< ImplSplitItem >& rItems = pSet->mvItems;
+
+ for ( auto& rItem : rItems )
+ {
+ if ( rItem.mnWidth && rItem.mnHeight )
+ {
+ Point aPoint( rItem.mnLeft, rItem.mnTop );
+ Size aSize( rItem.mnWidth, rItem.mnHeight );
+ tools::Rectangle aRect( aPoint, aSize );
+ if ( bRows )
+ {
+ if ( bDown )
+ aRect.AdjustBottom(pSet->mnSplitSize );
+ else
+ aRect.AdjustTop( -(pSet->mnSplitSize) );
+ }
+ else
+ {
+ if ( bDown )
+ aRect.AdjustRight(pSet->mnSplitSize );
+ else
+ aRect.AdjustLeft( -(pSet->mnSplitSize) );
+ }
+
+ if ( aRect.Contains( rPos ) )
+ {
+ if ( rItem.mpSet && !rItem.mpSet->mvItems.empty() )
+ {
+ return ImplFindItem( rItem.mpSet.get(), rPos,
+ !(rItem.mnBits & SplitWindowItemFlags::ColSet) );
+ }
+ else
+ return rItem.mnId;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void ImplCalcSet( ImplSplitSet* pSet,
+ tools::Long nSetLeft, tools::Long nSetTop,
+ tools::Long nSetWidth, tools::Long nSetHeight,
+ bool bRows, bool bDown = true )
+{
+ if ( pSet->mvItems.empty() )
+ return;
+
+ sal_uInt16 nMins;
+ sal_uInt16 nCalcItems;
+ size_t nItems = pSet->mvItems.size();
+ sal_uInt16 nAbsItems;
+ tools::Long nCalcSize;
+ tools::Long nPos;
+ tools::Long nMaxPos;
+ std::vector< ImplSplitItem >& rItems = pSet->mvItems;
+ bool bEmpty;
+
+ // calculate sizes
+ if ( bRows )
+ nCalcSize = nSetHeight;
+ else
+ nCalcSize = nSetWidth;
+ nCalcSize -= (rItems.size()-1)*pSet->mnSplitSize;
+ if ( pSet->mbCalcPix || (pSet->mnLastSize != nCalcSize) )
+ {
+ tools::Long nPercentFactor = 10;
+ tools::Long nRelCount = 0;
+ tools::Long nPercent = 0;
+ tools::Long nRelPercent = 0;
+ tools::Long nAbsSize = 0;
+ tools::Long nCurSize = 0;
+ for ( const auto& rItem : rItems )
+ {
+ if ( rItem.mnBits & SplitWindowItemFlags::RelativeSize )
+ nRelCount += rItem.mnSize;
+ else if ( rItem.mnBits & SplitWindowItemFlags::PercentSize )
+ nPercent += rItem.mnSize;
+ else
+ nAbsSize += rItem.mnSize;
+ }
+ // map relative values to percentages (percentage here one tenth of a percent)
+ nPercent *= nPercentFactor;
+ if ( nRelCount )
+ {
+ tools::Long nRelPercentBase = 1000;
+ while ( (nRelCount > nRelPercentBase) && (nPercentFactor < 100000) )
+ {
+ nRelPercentBase *= 10;
+ nPercentFactor *= 10;
+ }
+ if ( nPercent < nRelPercentBase )
+ {
+ nRelPercent = (nRelPercentBase-nPercent)/nRelCount;
+ nPercent += nRelPercent*nRelCount;
+ }
+ else
+ nRelPercent = 0;
+ }
+ if ( !nPercent )
+ nPercent = 1;
+ tools::Long nSizeDelta = nCalcSize-nAbsSize;
+ for ( auto& rItem : rItems )
+ {
+ if ( rItem.mnBits & SplitWindowItemFlags::RelativeSize )
+ {
+ if ( nSizeDelta <= 0 )
+ rItem.mnPixSize = 0;
+ else
+ rItem.mnPixSize = (nSizeDelta*rItem.mnSize*nRelPercent)/nPercent;
+ }
+ else if ( rItem.mnBits & SplitWindowItemFlags::PercentSize )
+ {
+ if ( nSizeDelta <= 0 )
+ rItem.mnPixSize = 0;
+ else
+ rItem.mnPixSize = (nSizeDelta*rItem.mnSize*nPercentFactor)/nPercent;
+ }
+ else
+ rItem.mnPixSize = rItem.mnSize;
+ nCurSize += rItem.mnPixSize;
+ }
+
+ pSet->mbCalcPix = false;
+ pSet->mnLastSize = nCalcSize;
+
+ // adapt window
+ nSizeDelta = nCalcSize-nCurSize;
+ if ( nSizeDelta )
+ {
+ nAbsItems = 0;
+ tools::Long nSizeWinSize = 0;
+
+ // first resize absolute items relative
+ for ( const auto& rItem : rItems )
+ {
+ if ( !(rItem.mnBits & (SplitWindowItemFlags::RelativeSize | SplitWindowItemFlags::PercentSize)) )
+ {
+ nAbsItems++;
+ nSizeWinSize += rItem.mnPixSize;
+ }
+ }
+ // do not compensate rounding errors here
+ if ( (nAbsItems < o3tl::make_unsigned(std::abs( nSizeDelta ))) && nSizeWinSize )
+ {
+ tools::Long nNewSizeWinSize = 0;
+
+ for ( auto& rItem : rItems )
+ {
+ if ( !(rItem.mnBits & (SplitWindowItemFlags::RelativeSize | SplitWindowItemFlags::PercentSize)) )
+ {
+ rItem.mnPixSize += (nSizeDelta*rItem.mnPixSize)/nSizeWinSize;
+ nNewSizeWinSize += rItem.mnPixSize;
+ }
+ }
+
+ nSizeDelta -= nNewSizeWinSize-nSizeWinSize;
+ }
+
+ // compensate rounding errors now
+ sal_uInt16 j = 0;
+ nMins = 0;
+ while ( nSizeDelta && (nItems != nMins) )
+ {
+ // determine which items we can calculate
+ nCalcItems = 0;
+ while ( !nCalcItems )
+ {
+ for ( auto& rItem : rItems )
+ {
+ rItem.mbSubSize = false;
+
+ if ( j >= 2 )
+ rItem.mbSubSize = true;
+ else
+ {
+ if ( (nSizeDelta > 0) || rItem.mnPixSize )
+ {
+ if ( j >= 1 )
+ rItem.mbSubSize = true;
+ else
+ {
+ if ( (j == 0) && (rItem.mnBits & (SplitWindowItemFlags::RelativeSize | SplitWindowItemFlags::PercentSize)) )
+ rItem.mbSubSize = true;
+ }
+ }
+ }
+
+ if ( rItem.mbSubSize )
+ nCalcItems++;
+ }
+
+ j++;
+ }
+
+ // subtract size of individual items
+ tools::Long nErrorSum = nSizeDelta % nCalcItems;
+ tools::Long nCurSizeDelta = nSizeDelta / nCalcItems;
+ nMins = 0;
+ for ( auto& rItem : rItems )
+ {
+ if ( rItem.mbSubSize )
+ {
+ tools::Long* pSize = &(rItem.mnPixSize);
+ tools::Long nTempErr;
+
+ if ( nErrorSum )
+ {
+ if ( nErrorSum < 0 )
+ nTempErr = -1;
+ else
+ nTempErr = 1;
+ }
+ else
+ nTempErr = 0;
+
+ if ( (*pSize+nCurSizeDelta+nTempErr) <= 0 )
+ {
+ tools::Long nTemp = *pSize;
+ if ( nTemp )
+ {
+ *pSize -= nTemp;
+ nSizeDelta += nTemp;
+ }
+ nMins++;
+ }
+ else
+ {
+ *pSize += nCurSizeDelta;
+ nSizeDelta -= nCurSizeDelta;
+ if ( nTempErr && (*pSize || (nTempErr > 0)) )
+ {
+ *pSize += nTempErr;
+ nSizeDelta -= nTempErr;
+ nErrorSum -= nTempErr;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // calculate maximum size
+ if ( bRows )
+ {
+ nPos = nSetTop;
+ if ( !bDown )
+ nMaxPos = nSetTop-nSetHeight;
+ else
+ nMaxPos = nSetTop+nSetHeight;
+ }
+ else
+ {
+ nPos = nSetLeft;
+ if ( !bDown )
+ nMaxPos = nSetLeft-nSetWidth;
+ else
+ nMaxPos = nSetLeft+nSetWidth;
+ }
+
+ // order windows and adapt values
+ for ( size_t i = 0; i < nItems; i++ )
+ {
+ rItems[i].mnOldSplitPos = rItems[i].mnSplitPos;
+ rItems[i].mnOldSplitSize = rItems[i].mnSplitSize;
+ rItems[i].mnOldWidth = rItems[i].mnWidth;
+ rItems[i].mnOldHeight = rItems[i].mnHeight;
+
+ bEmpty = false;
+ if ( bDown )
+ {
+ if ( nPos+rItems[i].mnPixSize > nMaxPos )
+ bEmpty = true;
+ }
+ else
+ {
+ nPos -= rItems[i].mnPixSize;
+ if ( nPos < nMaxPos )
+ bEmpty = true;
+ }
+
+ if ( bEmpty )
+ {
+ rItems[i].mnWidth = 0;
+ rItems[i].mnHeight = 0;
+ rItems[i].mnSplitSize = 0;
+ }
+ else
+ {
+ if ( bRows )
+ {
+ rItems[i].mnLeft = nSetLeft;
+ rItems[i].mnTop = nPos;
+ rItems[i].mnWidth = nSetWidth;
+ rItems[i].mnHeight = rItems[i].mnPixSize;
+ }
+ else
+ {
+ rItems[i].mnLeft = nPos;
+ rItems[i].mnTop = nSetTop;
+ rItems[i].mnWidth = rItems[i].mnPixSize;
+ rItems[i].mnHeight = nSetHeight;
+ }
+
+ if ( i > nItems-1 )
+ rItems[i].mnSplitSize = 0;
+ else
+ {
+ rItems[i].mnSplitSize = pSet->mnSplitSize;
+ if ( bDown )
+ {
+ rItems[i].mnSplitPos = nPos+rItems[i].mnPixSize;
+ if ( rItems[i].mnSplitPos+rItems[i].mnSplitSize > nMaxPos )
+ rItems[i].mnSplitSize = nMaxPos-rItems[i].mnSplitPos;
+ }
+ else
+ {
+ rItems[i].mnSplitPos = nPos-pSet->mnSplitSize;
+ if ( rItems[i].mnSplitPos < nMaxPos )
+ rItems[i].mnSplitSize = rItems[i].mnSplitPos+pSet->mnSplitSize-nMaxPos;
+ }
+ }
+ }
+
+ if ( !bDown )
+ nPos -= pSet->mnSplitSize;
+ else
+ nPos += rItems[i].mnPixSize+pSet->mnSplitSize;
+ }
+
+ // calculate Sub-Set's
+ for ( auto& rItem : rItems )
+ {
+ if ( rItem.mpSet && rItem.mnWidth && rItem.mnHeight )
+ {
+ ImplCalcSet( rItem.mpSet.get(),
+ rItem.mnLeft, rItem.mnTop,
+ rItem.mnWidth, rItem.mnHeight,
+ !(rItem.mnBits & SplitWindowItemFlags::ColSet) );
+ }
+ }
+
+ // set fixed
+ for ( auto& rItem : rItems )
+ {
+ rItem.mbFixed = false;
+ if ( rItem.mnBits & SplitWindowItemFlags::Fixed )
+ rItem.mbFixed = true;
+ else
+ {
+ // this item is also fixed if Child-Set is available,
+ // if a child is fixed
+ if ( rItem.mpSet )
+ {
+ for ( auto const & j: rItem.mpSet->mvItems )
+ {
+ if ( j.mbFixed )
+ {
+ rItem.mbFixed = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void SplitWindow::ImplCalcSet2( SplitWindow* pWindow, ImplSplitSet* pSet, bool bHide,
+ bool bRows )
+{
+ std::vector< ImplSplitItem >& rItems = pSet->mvItems;
+
+ if ( pWindow->IsReallyVisible() && pWindow->IsUpdateMode() && pWindow->mbInvalidate )
+ {
+ for ( const auto& rItem : rItems )
+ {
+ if ( rItem.mnSplitSize )
+ {
+ // invalidate all, if applicable or only a small part
+ if ( (rItem.mnOldSplitPos != rItem.mnSplitPos) ||
+ (rItem.mnOldSplitSize != rItem.mnSplitSize) ||
+ (rItem.mnOldWidth != rItem.mnWidth) ||
+ (rItem.mnOldHeight != rItem.mnHeight) )
+ {
+ tools::Rectangle aRect;
+
+ // invalidate old rectangle
+ if ( bRows )
+ {
+ aRect.SetLeft( rItem.mnLeft );
+ aRect.SetRight( rItem.mnLeft+rItem.mnOldWidth-1 );
+ aRect.SetTop( rItem.mnOldSplitPos );
+ aRect.SetBottom( aRect.Top() + rItem.mnOldSplitSize );
+ }
+ else
+ {
+ aRect.SetTop( rItem.mnTop );
+ aRect.SetBottom( rItem.mnTop+rItem.mnOldHeight-1 );
+ aRect.SetLeft( rItem.mnOldSplitPos );
+ aRect.SetRight( aRect.Left() + rItem.mnOldSplitSize );
+ }
+ pWindow->Invalidate( aRect );
+ // invalidate new rectangle
+ if ( bRows )
+ {
+ aRect.SetLeft( rItem.mnLeft );
+ aRect.SetRight( rItem.mnLeft+rItem.mnWidth-1 );
+ aRect.SetTop( rItem.mnSplitPos );
+ aRect.SetBottom( aRect.Top() + rItem.mnSplitSize );
+ }
+ else
+ {
+ aRect.SetTop( rItem.mnTop );
+ aRect.SetBottom( rItem.mnTop+rItem.mnHeight-1 );
+ aRect.SetLeft( rItem.mnSplitPos );
+ aRect.SetRight( aRect.Left() + rItem.mnSplitSize );
+ }
+ pWindow->Invalidate( aRect );
+
+ // invalidate complete set, as these areas
+ // are not cluttered by windows
+ if ( rItem.mpSet && rItem.mpSet->mvItems.empty() )
+ {
+ aRect.SetLeft( rItem.mnLeft );
+ aRect.SetTop( rItem.mnTop );
+ aRect.SetRight( rItem.mnLeft+rItem.mnWidth-1 );
+ aRect.SetBottom( rItem.mnTop+rItem.mnHeight-1 );
+ pWindow->Invalidate( aRect );
+ }
+ }
+ }
+ }
+ }
+
+ // position windows
+ for ( auto& rItem : rItems )
+ {
+ if ( rItem.mpSet )
+ {
+ bool bTempHide = bHide;
+ if ( !rItem.mnWidth || !rItem.mnHeight )
+ bTempHide = true;
+ ImplCalcSet2( pWindow, rItem.mpSet.get(), bTempHide,
+ !(rItem.mnBits & SplitWindowItemFlags::ColSet) );
+ }
+ else
+ {
+ if ( rItem.mnWidth && rItem.mnHeight && !bHide )
+ {
+ Point aPos( rItem.mnLeft, rItem.mnTop );
+ Size aSize( rItem.mnWidth, rItem.mnHeight );
+ rItem.mpWindow->SetPosSizePixel( aPos, aSize );
+ }
+ else
+ rItem.mpWindow->Hide();
+ }
+ }
+
+ // show windows and reset flag
+ for ( auto& rItem : rItems )
+ {
+ if ( rItem.mpWindow && rItem.mnWidth && rItem.mnHeight && !bHide )
+ rItem.mpWindow->Show();
+ }
+}
+
+static void ImplCalcLogSize( std::vector< ImplSplitItem > & rItems, size_t nItems )
+{
+ // update original sizes
+ size_t i;
+ tools::Long nRelSize = 0;
+ tools::Long nPerSize = 0;
+
+ for ( i = 0; i < nItems; i++ )
+ {
+ if ( rItems[i].mnBits & SplitWindowItemFlags::RelativeSize )
+ nRelSize += rItems[i].mnPixSize;
+ else if ( rItems[i].mnBits & SplitWindowItemFlags::PercentSize )
+ nPerSize += rItems[i].mnPixSize;
+ }
+ nPerSize += nRelSize;
+ for ( i = 0; i < nItems; i++ )
+ {
+ if ( rItems[i].mnBits & SplitWindowItemFlags::RelativeSize )
+ {
+ if ( nRelSize )
+ rItems[i].mnSize = (rItems[i].mnPixSize+(nRelSize/2))/nRelSize;
+ else
+ rItems[i].mnSize = 1;
+ }
+ else if ( rItems[i].mnBits & SplitWindowItemFlags::PercentSize )
+ {
+ if ( nPerSize )
+ rItems[i].mnSize = (rItems[i].mnPixSize*100)/nPerSize;
+ else
+ rItems[i].mnSize = 1;
+ }
+ else
+ rItems[i].mnSize = rItems[i].mnPixSize;
+ }
+}
+
+static void ImplDrawSplit(vcl::RenderContext& rRenderContext, ImplSplitSet* pSet, bool bRows, bool bDown)
+{
+ if (pSet->mvItems.empty())
+ return;
+
+ size_t nItems = pSet->mvItems.size();
+ tools::Long nPos;
+ tools::Long nTop;
+ tools::Long nBottom;
+ std::vector< ImplSplitItem >& rItems = pSet->mvItems;
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ for (size_t i = 0; i < nItems-1; i++)
+ {
+ if (rItems[i].mnSplitSize)
+ {
+ nPos = rItems[i].mnSplitPos;
+
+ tools::Long nItemSplitSize = rItems[i].mnSplitSize;
+ tools::Long nSplitSize = pSet->mnSplitSize;
+ if (bRows)
+ {
+ nTop = rItems[i].mnLeft;
+ nBottom = rItems[i].mnLeft+rItems[i].mnWidth-1;
+
+ if (bDown || (nItemSplitSize >= nSplitSize))
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
+ rRenderContext.DrawLine(Point(nTop, nPos + 1), Point(nBottom, nPos + 1));
+ }
+ nPos += nSplitSize-2;
+ if ((!bDown && (nItemSplitSize >= 2)) ||
+ (bDown && (nItemSplitSize >= nSplitSize - 1)))
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawLine(Point(nTop, nPos), Point(nBottom, nPos));
+ }
+ nPos++;
+ if (!bDown || (nItemSplitSize >= nSplitSize))
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
+ rRenderContext.DrawLine(Point(nTop, nPos), Point(nBottom, nPos));
+ }
+ }
+ else
+ {
+ nTop = rItems[i].mnTop;
+ nBottom = rItems[i].mnTop+pSet->mvItems[i].mnHeight-1;
+
+ if (bDown || (nItemSplitSize >= nSplitSize))
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetLightColor());
+ rRenderContext.DrawLine(Point(nPos + 1, nTop), Point(nPos+1, nBottom));
+ }
+ nPos += pSet->mnSplitSize - 2;
+ if ((!bDown && (nItemSplitSize >= 2)) ||
+ (bDown && (nItemSplitSize >= nSplitSize - 1)))
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawLine(Point(nPos, nTop), Point(nPos, nBottom));
+ }
+ nPos++;
+ if (!bDown || (nItemSplitSize >= nSplitSize))
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
+ rRenderContext.DrawLine(Point(nPos, nTop), Point(nPos, nBottom));
+ }
+ }
+ }
+ }
+
+ for ( auto& rItem : rItems )
+ {
+ if (rItem.mpSet && rItem.mnWidth && rItem.mnHeight)
+ {
+ ImplDrawSplit(rRenderContext, rItem.mpSet.get(), !(rItem.mnBits & SplitWindowItemFlags::ColSet), true/*bDown*/);
+ }
+ }
+}
+
+sal_uInt16 SplitWindow::ImplTestSplit( ImplSplitSet* pSet, const Point& rPos,
+ tools::Long& rMouseOff, ImplSplitSet** ppFoundSet, sal_uInt16& rFoundPos,
+ bool bRows )
+{
+ if ( pSet->mvItems.empty() )
+ return 0;
+
+ sal_uInt16 nSplitTest;
+ size_t nItems = pSet->mvItems.size();
+ tools::Long nMPos1;
+ tools::Long nMPos2;
+ tools::Long nPos;
+ tools::Long nTop;
+ tools::Long nBottom;
+ std::vector< ImplSplitItem >& rItems = pSet->mvItems;
+
+ if ( bRows )
+ {
+ nMPos1 = rPos.X();
+ nMPos2 = rPos.Y();
+ }
+ else
+ {
+ nMPos1 = rPos.Y();
+ nMPos2 = rPos.X();
+ }
+
+ for ( size_t i = 0; i < nItems-1; i++ )
+ {
+ if ( rItems[i].mnSplitSize )
+ {
+ if ( bRows )
+ {
+ nTop = rItems[i].mnLeft;
+ nBottom = rItems[i].mnLeft+rItems[i].mnWidth-1;
+ }
+ else
+ {
+ nTop = rItems[i].mnTop;
+ nBottom = rItems[i].mnTop+rItems[i].mnHeight-1;
+ }
+ nPos = rItems[i].mnSplitPos;
+
+ if ( (nMPos1 >= nTop) && (nMPos1 <= nBottom) &&
+ (nMPos2 >= nPos) && (nMPos2 <= nPos+rItems[i].mnSplitSize) )
+ {
+ if ( !rItems[i].mbFixed && !rItems[i+1].mbFixed )
+ {
+ rMouseOff = nMPos2-nPos;
+ *ppFoundSet = pSet;
+ rFoundPos = i;
+ if ( bRows )
+ return SPLIT_VERT;
+ else
+ return SPLIT_HORZ;
+ }
+ else
+ return SPLIT_NOSPLIT;
+ }
+ }
+ }
+
+ for ( auto& rItem : rItems )
+ {
+ if ( rItem.mpSet )
+ {
+ nSplitTest = ImplTestSplit( rItem.mpSet.get(), rPos,
+ rMouseOff, ppFoundSet, rFoundPos,
+ !(rItem.mnBits & SplitWindowItemFlags::ColSet) );
+ if ( nSplitTest )
+ return nSplitTest;
+ }
+ }
+
+ return 0;
+}
+
+sal_uInt16 SplitWindow::ImplTestSplit( const SplitWindow* pWindow, const Point& rPos,
+ tools::Long& rMouseOff, ImplSplitSet** ppFoundSet, sal_uInt16& rFoundPos )
+{
+ // Resizable SplitWindow should be treated different
+ if ( pWindow->mnWinStyle & WB_SIZEABLE )
+ {
+ tools::Long nTPos;
+ tools::Long nPos;
+ tools::Long nBorder;
+
+ if ( pWindow->mbHorz )
+ {
+ if ( pWindow->mbBottomRight )
+ {
+ nBorder = pWindow->mnBottomBorder;
+ nPos = 0;
+ }
+ else
+ {
+ nBorder = pWindow->mnTopBorder;
+ nPos = pWindow->mnDY-nBorder;
+ }
+ nTPos = rPos.Y();
+ }
+ else
+ {
+ if ( pWindow->mbBottomRight )
+ {
+ nBorder = pWindow->mnRightBorder;
+ nPos = 0;
+ }
+ else
+ {
+ nBorder = pWindow->mnLeftBorder;
+ nPos = pWindow->mnDX-nBorder;
+ }
+ nTPos = rPos.X();
+ }
+ tools::Long nSplitSize = pWindow->mpMainSet->mnSplitSize-2;
+ if (pWindow->mbFadeOut)
+ nSplitSize += SPLITWIN_SPLITSIZEEXLN;
+ if ( !pWindow->mbBottomRight )
+ nPos -= nSplitSize;
+ if ( (nTPos >= nPos) && (nTPos <= nPos+nSplitSize+nBorder) )
+ {
+ rMouseOff = nTPos-nPos;
+ *ppFoundSet = pWindow->mpMainSet.get();
+ if ( !pWindow->mpMainSet->mvItems.empty() )
+ rFoundPos = pWindow->mpMainSet->mvItems.size() - 1;
+ else
+ rFoundPos = 0;
+ if ( pWindow->mbHorz )
+ return SPLIT_VERT | SPLIT_WINDOW;
+ else
+ return SPLIT_HORZ | SPLIT_WINDOW;
+ }
+ }
+
+ return ImplTestSplit( pWindow->mpMainSet.get(), rPos, rMouseOff, ppFoundSet, rFoundPos,
+ pWindow->mbHorz );
+}
+
+void SplitWindow::ImplDrawSplitTracking(const Point& rPos)
+{
+ tools::Rectangle aRect;
+
+ if (mnSplitTest & SPLIT_HORZ)
+ {
+ aRect.SetTop( maDragRect.Top() );
+ aRect.SetBottom( maDragRect.Bottom() );
+ aRect.SetLeft( rPos.X() );
+ aRect.SetRight( aRect.Left() + mpSplitSet->mnSplitSize - 1 );
+ if (!(mnWinStyle & WB_NOSPLITDRAW))
+ aRect.AdjustRight( -1 );
+ if ((mnSplitTest & SPLIT_WINDOW) && mbFadeOut)
+ {
+ aRect.AdjustLeft(SPLITWIN_SPLITSIZEEXLN );
+ aRect.AdjustRight(SPLITWIN_SPLITSIZEEXLN );
+ }
+ }
+ else
+ {
+ aRect.SetLeft( maDragRect.Left() );
+ aRect.SetRight( maDragRect.Right() );
+ aRect.SetTop( rPos.Y() );
+ aRect.SetBottom( aRect.Top() + mpSplitSet->mnSplitSize - 1 );
+ if (!(mnWinStyle & WB_NOSPLITDRAW))
+ aRect.AdjustBottom( -1 );
+ if ((mnSplitTest & SPLIT_WINDOW) && mbFadeOut)
+ {
+ aRect.AdjustTop(SPLITWIN_SPLITSIZEEXLN );
+ aRect.AdjustBottom(SPLITWIN_SPLITSIZEEXLN );
+ }
+ }
+ ShowTracking(aRect, ShowTrackFlags::Split);
+}
+
+void SplitWindow::ImplInit( vcl::Window* pParent, WinBits nStyle )
+{
+ mpMainSet.reset(new ImplSplitSet());
+ mpBaseSet = mpMainSet.get();
+ mpSplitSet = nullptr;
+ mpLastSizes = nullptr;
+ mnDX = 0;
+ mnDY = 0;
+ mnLeftBorder = 0;
+ mnTopBorder = 0;
+ mnRightBorder = 0;
+ mnBottomBorder = 0;
+ mnMaxSize = 0;
+ mnMouseOff = 0;
+ meAlign = WindowAlign::Top;
+ mnWinStyle = nStyle;
+ mnSplitTest = 0;
+ mnSplitPos = 0;
+ mnMouseModifier = 0;
+ mnMStartPos = 0;
+ mnMSplitPos = 0;
+ mbDragFull = false;
+ mbHorz = true;
+ mbBottomRight = false;
+ mbCalc = false;
+ mbRecalc = true;
+ mbInvalidate = true;
+ mbFadeIn = false;
+ mbFadeOut = false;
+ mbFadeInDown = false;
+ mbFadeOutDown = false;
+ mbFadeInPressed = false;
+ mbFadeOutPressed = false;
+ mbFadeNoButtonMode = false;
+
+ if ( nStyle & WB_NOSPLITDRAW )
+ {
+ mpMainSet->mnSplitSize -= 2;
+ mbInvalidate = false;
+ }
+
+ if ( nStyle & WB_BORDER )
+ {
+ ImplCalcBorder( meAlign, mnLeftBorder, mnTopBorder,
+ mnRightBorder, mnBottomBorder );
+ }
+ else
+ {
+ mnLeftBorder = 0;
+ mnTopBorder = 0;
+ mnRightBorder = 0;
+ mnBottomBorder = 0;
+ }
+
+ DockingWindow::ImplInit( pParent, (nStyle | WB_CLIPCHILDREN) & ~(WB_BORDER | WB_SIZEABLE) );
+
+ ImplInitSettings();
+}
+
+void SplitWindow::ImplInitSettings()
+{
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+
+ Color aColor;
+ if ( IsControlBackground() )
+ aColor = GetControlBackground();
+ else if ( Window::GetStyle() & WB_3DLOOK )
+ aColor = rStyleSettings.GetFaceColor();
+ else
+ aColor = rStyleSettings.GetWindowColor();
+ SetBackground( aColor );
+}
+
+SplitWindow::SplitWindow( vcl::Window* pParent, WinBits nStyle ) :
+ DockingWindow( WindowType::SPLITWINDOW, "vcl::SplitWindow maLayoutIdle" )
+{
+ ImplInit( pParent, nStyle );
+}
+
+SplitWindow::~SplitWindow()
+{
+ disposeOnce();
+}
+
+void SplitWindow::dispose()
+{
+ // delete Sets
+ mpMainSet.reset();
+ DockingWindow::dispose();
+}
+
+void SplitWindow::ImplSetWindowSize( tools::Long nDelta )
+{
+ if ( !nDelta )
+ return;
+
+ Size aSize = GetSizePixel();
+ switch ( meAlign )
+ {
+ case WindowAlign::Top:
+ aSize.AdjustHeight(nDelta );
+ SetSizePixel( aSize );
+ break;
+ case WindowAlign::Bottom:
+ {
+ maDragRect.AdjustTop(nDelta );
+ Point aPos = GetPosPixel();
+ aPos.AdjustY( -nDelta );
+ aSize.AdjustHeight(nDelta );
+ SetPosSizePixel( aPos, aSize );
+ break;
+ }
+ case WindowAlign::Left:
+ aSize.AdjustWidth(nDelta );
+ SetSizePixel( aSize );
+ break;
+ case WindowAlign::Right:
+ default:
+ {
+ maDragRect.AdjustLeft(nDelta );
+ Point aPos = GetPosPixel();
+ aPos.AdjustX( -nDelta );
+ aSize.AdjustWidth(nDelta );
+ SetPosSizePixel( aPos, aSize );
+ break;
+ }
+ }
+
+ SplitResize();
+}
+
+Size SplitWindow::CalcLayoutSizePixel( const Size& aNewSize )
+{
+ Size aSize( aNewSize );
+ tools::Long nSplitSize = mpMainSet->mnSplitSize-2;
+
+ if (mbFadeOut)
+ nSplitSize += SPLITWIN_SPLITSIZEEXLN;
+
+ // if the window is sizeable and if it does not contain a relative window,
+ // the size is determined according to MainSet
+ if ( mnWinStyle & WB_SIZEABLE )
+ {
+ tools::Long nCalcSize = 0;
+ std::vector< ImplSplitItem* >::size_type i;
+
+ for ( i = 0; i < mpMainSet->mvItems.size(); i++ )
+ {
+ if ( mpMainSet->mvItems[i].mnBits & (SplitWindowItemFlags::RelativeSize | SplitWindowItemFlags::PercentSize) )
+ break;
+ else
+ nCalcSize += mpMainSet->mvItems[i].mnSize;
+ }
+
+ if ( i == mpMainSet->mvItems.size() )
+ {
+ tools::Long nDelta = 0;
+ tools::Long nCurSize;
+
+ if ( mbHorz )
+ nCurSize = aNewSize.Height()-mnTopBorder-mnBottomBorder;
+ else
+ nCurSize = aNewSize.Width()-mnLeftBorder-mnRightBorder;
+ nCurSize -= nSplitSize;
+ nCurSize -= (mpMainSet->mvItems.size()-1)*mpMainSet->mnSplitSize;
+
+ nDelta = nCalcSize-nCurSize;
+ if ( !nDelta )
+ return aSize;
+
+ switch ( meAlign )
+ {
+ case WindowAlign::Top:
+ aSize.AdjustHeight(nDelta );
+ break;
+ case WindowAlign::Bottom:
+ aSize.AdjustHeight(nDelta );
+ break;
+ case WindowAlign::Left:
+ aSize.AdjustWidth(nDelta );
+ break;
+ case WindowAlign::Right:
+ default:
+ aSize.AdjustWidth(nDelta );
+ break;
+ }
+ }
+ }
+
+ return aSize;
+}
+
+void SplitWindow::ImplCalcLayout()
+{
+ if ( !mbCalc || !mbRecalc || mpMainSet->mvItems.empty() )
+ return;
+
+ tools::Long nSplitSize = mpMainSet->mnSplitSize-2;
+ if (mbFadeOut)
+ nSplitSize += SPLITWIN_SPLITSIZEEXLN;
+
+ // if the window is sizeable and if it does not contain a relative window,
+ // the size is determined according to MainSet
+ if ( mnWinStyle & WB_SIZEABLE )
+ {
+ tools::Long nCalcSize = 0;
+ std::vector<ImplSplitItem *>::size_type i;
+
+ for ( i = 0; i < mpMainSet->mvItems.size(); i++ )
+ {
+ if ( mpMainSet->mvItems[i].mnBits & (SplitWindowItemFlags::RelativeSize | SplitWindowItemFlags::PercentSize) )
+ break;
+ else
+ nCalcSize += mpMainSet->mvItems[i].mnSize;
+ }
+
+ if ( i == mpMainSet->mvItems.size() )
+ {
+ tools::Long nCurSize;
+ if ( mbHorz )
+ nCurSize = mnDY-mnTopBorder-mnBottomBorder;
+ else
+ nCurSize = mnDX-mnLeftBorder-mnRightBorder;
+ nCurSize -= nSplitSize;
+ nCurSize -= (mpMainSet->mvItems.size()-1)*mpMainSet->mnSplitSize;
+
+ mbRecalc = false;
+ ImplSetWindowSize( nCalcSize-nCurSize );
+ mbRecalc = true;
+ }
+ }
+
+ if ( (mnDX <= 0) || (mnDY <= 0) )
+ return;
+
+ // pre-calculate sizes/position
+ tools::Long nL;
+ tools::Long nT;
+ tools::Long nW;
+ tools::Long nH;
+
+ if ( mbHorz )
+ {
+ if ( mbBottomRight )
+ nT = mnDY-mnBottomBorder;
+ else
+ nT = mnTopBorder;
+ nL = mnLeftBorder;
+ }
+ else
+ {
+ if ( mbBottomRight )
+ nL = mnDX-mnRightBorder;
+ else
+ nL = mnLeftBorder;
+ nT = mnTopBorder;
+ }
+ nW = mnDX-mnLeftBorder-mnRightBorder;
+ nH = mnDY-mnTopBorder-mnBottomBorder;
+ if ( mnWinStyle & WB_SIZEABLE )
+ {
+ if ( mbHorz )
+ nH -= nSplitSize;
+ else
+ nW -= nSplitSize;
+ }
+
+ // calculate sets recursive
+ ImplCalcSet( mpMainSet.get(), nL, nT, nW, nH, mbHorz, !mbBottomRight );
+ ImplCalcSet2( this, mpMainSet.get(), false, mbHorz );
+ mbCalc = false;
+}
+
+void SplitWindow::ImplUpdate()
+{
+ mbCalc = true;
+
+ if ( IsReallyShown() && IsUpdateMode() && mbRecalc )
+ {
+ if ( !mpMainSet->mvItems.empty() )
+ ImplCalcLayout();
+ else
+ Invalidate();
+ }
+}
+
+void SplitWindow::ImplSplitMousePos( Point& rMousePos )
+{
+ if ( mnSplitTest & SPLIT_HORZ )
+ {
+ rMousePos.AdjustX( -mnMouseOff );
+ if ( rMousePos.X() < maDragRect.Left() )
+ rMousePos.setX( maDragRect.Left() );
+ else if ( rMousePos.X()+mpSplitSet->mnSplitSize+1 > maDragRect.Right() )
+ rMousePos.setX( maDragRect.Right()-mpSplitSet->mnSplitSize+1 );
+ // store in screen coordinates due to FullDrag
+ mnMSplitPos = OutputToScreenPixel( rMousePos ).X();
+ }
+ else
+ {
+ rMousePos.AdjustY( -mnMouseOff );
+ if ( rMousePos.Y() < maDragRect.Top() )
+ rMousePos.setY( maDragRect.Top() );
+ else if ( rMousePos.Y()+mpSplitSet->mnSplitSize+1 > maDragRect.Bottom() )
+ rMousePos.setY( maDragRect.Bottom()-mpSplitSet->mnSplitSize+1 );
+ mnMSplitPos = OutputToScreenPixel( rMousePos ).Y();
+ }
+}
+
+void SplitWindow::ImplGetButtonRect( tools::Rectangle& rRect, bool bTest ) const
+{
+ tools::Long nSplitSize = mpMainSet->mnSplitSize-1;
+ if (mbFadeOut || mbFadeIn)
+ nSplitSize += SPLITWIN_SPLITSIZEEX;
+
+ tools::Long nButtonSize = 0;
+ if ( mbFadeIn )
+ nButtonSize += SPLITWIN_SPLITSIZEFADE+1;
+ if ( mbFadeOut )
+ nButtonSize += SPLITWIN_SPLITSIZEFADE+1;
+ tools::Long nCenterEx = 0;
+ if ( mbHorz )
+ nCenterEx += ((mnDX-mnLeftBorder-mnRightBorder)-nButtonSize)/2;
+ else
+ nCenterEx += ((mnDY-mnTopBorder-mnBottomBorder)-nButtonSize)/2;
+ tools::Long nEx = 0;
+ if ( nCenterEx > 0 )
+ nEx += nCenterEx;
+
+ switch ( meAlign )
+ {
+ case WindowAlign::Top:
+ rRect.SetLeft( mnLeftBorder+nEx );
+ rRect.SetTop( mnDY-mnBottomBorder-nSplitSize );
+ rRect.SetRight( rRect.Left()+SPLITWIN_SPLITSIZEAUTOHIDE );
+ rRect.SetBottom( mnDY-mnBottomBorder-1 );
+ if ( bTest )
+ {
+ rRect.AdjustTop( -mnTopBorder );
+ rRect.AdjustBottom(mnBottomBorder );
+ }
+ break;
+ case WindowAlign::Bottom:
+ rRect.SetLeft( mnLeftBorder+nEx );
+ rRect.SetTop( mnTopBorder );
+ rRect.SetRight( rRect.Left()+SPLITWIN_SPLITSIZEAUTOHIDE );
+ rRect.SetBottom( mnTopBorder+nSplitSize-1 );
+ if ( bTest )
+ {
+ rRect.AdjustTop( -mnTopBorder );
+ rRect.AdjustBottom(mnBottomBorder );
+ }
+ break;
+ case WindowAlign::Left:
+ rRect.SetLeft( mnDX-mnRightBorder-nSplitSize );
+ rRect.SetTop( mnTopBorder+nEx );
+ rRect.SetRight( mnDX-mnRightBorder-1 );
+ rRect.SetBottom( rRect.Top()+SPLITWIN_SPLITSIZEAUTOHIDE );
+ if ( bTest )
+ {
+ rRect.AdjustLeft( -mnLeftBorder );
+ rRect.AdjustRight(mnRightBorder );
+ }
+ break;
+ case WindowAlign::Right:
+ rRect.SetLeft( mnLeftBorder );
+ rRect.SetTop( mnTopBorder+nEx );
+ rRect.SetRight( mnLeftBorder+nSplitSize-1 );
+ rRect.SetBottom( rRect.Top()+SPLITWIN_SPLITSIZEAUTOHIDE );
+ if ( bTest )
+ {
+ rRect.AdjustLeft( -mnLeftBorder );
+ rRect.AdjustRight(mnRightBorder );
+ }
+ break;
+ }
+}
+
+void SplitWindow::ImplGetFadeInRect( tools::Rectangle& rRect, bool bTest ) const
+{
+ tools::Rectangle aRect;
+
+ if ( mbFadeIn )
+ ImplGetButtonRect( aRect, bTest );
+
+ rRect = aRect;
+}
+
+void SplitWindow::ImplGetFadeOutRect( tools::Rectangle& rRect ) const
+{
+ tools::Rectangle aRect;
+
+ if ( mbFadeOut )
+ ImplGetButtonRect( aRect, false );
+
+ rRect = aRect;
+}
+
+void SplitWindow::ImplDrawGrip(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, bool bHorizontal, bool bLeft)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ Color aColor;
+
+ if (rRect.Contains(GetPointerPosPixel()))
+ {
+ vcl::RenderTools::DrawSelectionBackground(rRenderContext, *this, rRect, 2, false, false, false);
+
+ aColor = rStyleSettings.GetDarkShadowColor();
+ }
+ else
+ {
+ rRenderContext.SetLineColor(rStyleSettings.GetDarkShadowColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetDarkShadowColor());
+
+ rRenderContext.DrawRect(rRect);
+
+ aColor = rStyleSettings.GetFaceColor();
+ }
+
+ AntialiasingFlags nAA = rRenderContext.GetAntialiasing();
+ rRenderContext.SetAntialiasing(nAA | AntialiasingFlags::PixelSnapHairline | AntialiasingFlags::Enable);
+
+ tools::Long nWidth = rRect.getOpenWidth();
+ tools::Long nWidthHalf = nWidth / 2;
+ tools::Long nHeight = rRect.getOpenHeight();
+ tools::Long nHeightHalf = nHeight / 2;
+
+ tools::Long nLeft = rRect.Left();
+ tools::Long nRight = rRect.Right();
+ tools::Long nTop = rRect.Top();
+ tools::Long nBottom = rRect.Bottom();
+ tools::Long nMargin = 1;
+
+ rRenderContext.SetLineColor(aColor);
+ rRenderContext.SetFillColor(aColor);
+
+ tools::Polygon aPoly(3);
+
+ if (bHorizontal)
+ {
+ tools::Long nCenter = nLeft + nWidthHalf;
+
+ if (bLeft)
+ {
+ aPoly.SetPoint(Point(nCenter, nTop + nMargin), 0);
+ aPoly.SetPoint(Point(nCenter - nHeightHalf, nBottom - nMargin), 1);
+ aPoly.SetPoint(Point(nCenter - nHeightHalf, nBottom - nMargin), 2);
+ }
+ else
+ {
+ aPoly.SetPoint(Point(nCenter, nBottom - nMargin), 0);
+ aPoly.SetPoint(Point(nCenter - nHeightHalf, nTop + nMargin), 1);
+ aPoly.SetPoint(Point(nCenter + nHeightHalf, nTop + nMargin), 2);
+ }
+ rRenderContext.DrawPolygon(aPoly);
+ }
+ else
+ {
+ tools::Long nCenter = nTop + nHeightHalf;
+
+ if (bLeft)
+ {
+ aPoly.SetPoint(Point(nLeft + nMargin, nCenter), 0);
+ aPoly.SetPoint(Point(nRight - nMargin, nCenter - nWidthHalf), 1);
+ aPoly.SetPoint(Point(nRight - nMargin, nCenter + nWidthHalf), 2);
+ }
+ else
+ {
+ aPoly.SetPoint(Point(nRight - nMargin, nCenter), 0);
+ aPoly.SetPoint(Point(nLeft + nMargin, nCenter - nWidthHalf), 1);
+ aPoly.SetPoint(Point(nLeft + nMargin, nCenter + nWidthHalf), 2);
+ }
+ rRenderContext.DrawPolygon(aPoly);
+ }
+
+ rRenderContext.SetAntialiasing(nAA);
+}
+
+void SplitWindow::ImplDrawFadeIn(vcl::RenderContext& rRenderContext)
+{
+ if (!mbFadeIn)
+ return;
+
+ tools::Rectangle aTempRect;
+ ImplGetFadeInRect(aTempRect);
+
+ bool bLeft = true;
+ switch (meAlign)
+ {
+ case WindowAlign::Top:
+ case WindowAlign::Left:
+ bLeft = false;
+ break;
+ case WindowAlign::Bottom:
+ case WindowAlign::Right:
+ default:
+ bLeft = true;
+ break;
+ }
+
+ ImplDrawGrip(rRenderContext, aTempRect, (meAlign == WindowAlign::Top) || (meAlign == WindowAlign::Bottom), bLeft);
+}
+
+void SplitWindow::ImplDrawFadeOut(vcl::RenderContext& rRenderContext)
+{
+ if (!mbFadeOut)
+ return;
+
+ tools::Rectangle aTempRect;
+ ImplGetFadeOutRect(aTempRect);
+
+ bool bLeft = true;
+ switch (meAlign)
+ {
+ case WindowAlign::Bottom:
+ case WindowAlign::Right:
+ bLeft = false;
+ break;
+ case WindowAlign::Top:
+ case WindowAlign::Left:
+ default:
+ bLeft = true;
+ break;
+ }
+
+ ImplDrawGrip(rRenderContext, aTempRect, (meAlign == WindowAlign::Top) || (meAlign == WindowAlign::Bottom), bLeft);
+}
+
+void SplitWindow::ImplStartSplit( const MouseEvent& rMEvt )
+{
+ Point aMousePosPixel = rMEvt.GetPosPixel();
+ mnSplitTest = ImplTestSplit( this, aMousePosPixel, mnMouseOff, &mpSplitSet, mnSplitPos );
+
+ if ( !mnSplitTest || (mnSplitTest & SPLIT_NOSPLIT) )
+ return;
+
+ ImplSplitItem* pSplitItem;
+ tools::Long nCurMaxSize;
+ bool bPropSmaller;
+
+ mnMouseModifier = rMEvt.GetModifier();
+ bPropSmaller = (mnMouseModifier & KEY_SHIFT) && (o3tl::make_unsigned(mnSplitPos+1) < mpSplitSet->mvItems.size());
+
+ // here we can set the maximum size
+ StartSplit();
+
+ if ( mnMaxSize )
+ nCurMaxSize = mnMaxSize;
+ else
+ {
+ Size aSize = GetParent()->GetOutputSizePixel();
+ if ( mbHorz )
+ nCurMaxSize = aSize.Height();
+ else
+ nCurMaxSize = aSize.Width();
+ }
+
+ if ( !mpSplitSet->mvItems.empty() )
+ {
+ bool bDown = true;
+ if ( (mpSplitSet == mpMainSet.get()) && mbBottomRight )
+ bDown = false;
+
+ pSplitItem = &mpSplitSet->mvItems[mnSplitPos];
+ maDragRect.SetLeft( pSplitItem->mnLeft );
+ maDragRect.SetTop( pSplitItem->mnTop );
+ maDragRect.SetRight( pSplitItem->mnLeft+pSplitItem->mnWidth-1 );
+ maDragRect.SetBottom( pSplitItem->mnTop+pSplitItem->mnHeight-1 );
+
+ if ( mnSplitTest & SPLIT_HORZ )
+ {
+ if ( bDown )
+ maDragRect.AdjustRight(mpSplitSet->mnSplitSize );
+ else
+ maDragRect.AdjustLeft( -(mpSplitSet->mnSplitSize) );
+ }
+ else
+ {
+ if ( bDown )
+ maDragRect.AdjustBottom(mpSplitSet->mnSplitSize );
+ else
+ maDragRect.AdjustTop( -(mpSplitSet->mnSplitSize) );
+ }
+
+ if ( mnSplitPos )
+ {
+ tools::Long nTemp = mnSplitPos;
+ while ( nTemp )
+ {
+ pSplitItem = &mpSplitSet->mvItems[nTemp-1];
+ if ( pSplitItem->mbFixed )
+ break;
+ else
+ {
+ if ( mnSplitTest & SPLIT_HORZ )
+ {
+ if ( bDown )
+ maDragRect.AdjustLeft( -(pSplitItem->mnPixSize) );
+ else
+ maDragRect.AdjustRight(pSplitItem->mnPixSize );
+ }
+ else
+ {
+ if ( bDown )
+ maDragRect.AdjustTop( -(pSplitItem->mnPixSize) );
+ else
+ maDragRect.AdjustBottom(pSplitItem->mnPixSize );
+ }
+ }
+ nTemp--;
+ }
+ }
+
+ if ( (mpSplitSet == mpMainSet.get()) && (mnWinStyle & WB_SIZEABLE) && !bPropSmaller )
+ {
+ if ( bDown )
+ {
+ if ( mbHorz )
+ maDragRect.AdjustBottom(nCurMaxSize-mnDY-mnTopBorder );
+ else
+ maDragRect.AdjustRight(nCurMaxSize-mnDX-mnLeftBorder );
+ }
+ else
+ {
+ if ( mbHorz )
+ maDragRect.AdjustTop( -(nCurMaxSize-mnDY-mnBottomBorder) );
+ else
+ maDragRect.AdjustLeft( -(nCurMaxSize-mnDX-mnRightBorder) );
+ }
+ }
+ else
+ {
+ std::vector<ImplSplitItem *>::size_type nTemp = mnSplitPos+1;
+ while ( nTemp < mpSplitSet->mvItems.size() )
+ {
+ pSplitItem = &mpSplitSet->mvItems[nTemp];
+ if ( pSplitItem->mbFixed )
+ break;
+ else
+ {
+ if ( mnSplitTest & SPLIT_HORZ )
+ {
+ if ( bDown )
+ maDragRect.AdjustRight(pSplitItem->mnPixSize );
+ else
+ maDragRect.AdjustLeft( -(pSplitItem->mnPixSize) );
+ }
+ else
+ {
+ if ( bDown )
+ maDragRect.AdjustBottom(pSplitItem->mnPixSize );
+ else
+ maDragRect.AdjustTop( -(pSplitItem->mnPixSize) );
+ }
+ }
+ nTemp++;
+ }
+ }
+ }
+ else
+ {
+ maDragRect.SetLeft( mnLeftBorder );
+ maDragRect.SetTop( mnTopBorder );
+ maDragRect.SetRight( mnDX-mnRightBorder-1 );
+ maDragRect.SetBottom( mnDY-mnBottomBorder-1 );
+ if ( mbHorz )
+ {
+ if ( mbBottomRight )
+ maDragRect.AdjustTop( -(nCurMaxSize-mnDY-mnBottomBorder) );
+ else
+ maDragRect.AdjustBottom(nCurMaxSize-mnDY-mnTopBorder );
+ }
+ else
+ {
+ if ( mbBottomRight )
+ maDragRect.AdjustLeft( -(nCurMaxSize-mnDX-mnRightBorder) );
+ else
+ maDragRect.AdjustRight(nCurMaxSize-mnDX-mnLeftBorder );
+ }
+ }
+
+ StartTracking();
+
+ mbDragFull = bool(GetSettings().GetStyleSettings().GetDragFullOptions() & DragFullOptions::Split);
+
+ ImplSplitMousePos( aMousePosPixel );
+
+ if (!mbDragFull)
+ {
+ ImplDrawSplitTracking(aMousePosPixel);
+ }
+ else
+ {
+ std::vector< ImplSplitItem >& rItems = mpSplitSet->mvItems;
+ sal_uInt16 nItems = mpSplitSet->mvItems.size();
+ mpLastSizes.reset(new tools::Long[nItems*2]);
+ for ( sal_uInt16 i = 0; i < nItems; i++ )
+ {
+ mpLastSizes[i*2] = rItems[i].mnSize;
+ mpLastSizes[i*2+1] = rItems[i].mnPixSize;
+ }
+ }
+ mnMStartPos = mnMSplitPos;
+
+ PointerStyle eStyle = PointerStyle::Arrow;
+ if ( mnSplitTest & SPLIT_HORZ )
+ eStyle = PointerStyle::HSplit;
+ else if ( mnSplitTest & SPLIT_VERT )
+ eStyle = PointerStyle::VSplit;
+
+ SetPointer( eStyle );
+}
+
+void SplitWindow::StartSplit()
+{
+}
+
+void SplitWindow::Split()
+{
+ maSplitHdl.Call( this );
+}
+
+void SplitWindow::SplitResize()
+{
+}
+
+void SplitWindow::FadeIn()
+{
+}
+
+void SplitWindow::FadeOut()
+{
+}
+
+void SplitWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( !rMEvt.IsLeft() || rMEvt.IsMod2() )
+ {
+ DockingWindow::MouseButtonDown( rMEvt );
+ return;
+ }
+
+ Point aMousePosPixel = rMEvt.GetPosPixel();
+ tools::Rectangle aTestRect;
+
+ mbFadeNoButtonMode = false;
+
+ ImplGetFadeOutRect( aTestRect );
+ if ( aTestRect.Contains( aMousePosPixel ) )
+ {
+ mbFadeOutDown = true;
+ mbFadeOutPressed = true;
+ Invalidate();
+ }
+ else
+ {
+ ImplGetFadeInRect( aTestRect, true );
+ if ( aTestRect.Contains( aMousePosPixel ) )
+ {
+ mbFadeInDown = true;
+ mbFadeInPressed = true;
+ Invalidate();
+ }
+ else if ( !aTestRect.IsEmpty() && !(mnWinStyle & WB_SIZEABLE) )
+ {
+ mbFadeNoButtonMode = true;
+ FadeIn();
+ return;
+ }
+ }
+
+ if ( mbFadeInDown || mbFadeOutDown )
+ StartTracking();
+ else
+ ImplStartSplit( rMEvt );
+}
+
+void SplitWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ if ( IsTracking() )
+ return;
+
+ Point aPos = rMEvt.GetPosPixel();
+ tools::Long nTemp;
+ ImplSplitSet* pTempSplitSet;
+ sal_uInt16 nTempSplitPos;
+ sal_uInt16 nSplitTest = ImplTestSplit( this, aPos, nTemp, &pTempSplitSet, nTempSplitPos );
+ PointerStyle eStyle = PointerStyle::Arrow;
+ tools::Rectangle aFadeInRect;
+ tools::Rectangle aFadeOutRect;
+
+ ImplGetFadeInRect( aFadeInRect );
+ ImplGetFadeOutRect( aFadeOutRect );
+ if ( !aFadeInRect.Contains( aPos ) &&
+ !aFadeOutRect.Contains( aPos ) )
+ {
+ if ( nSplitTest && !(nSplitTest & SPLIT_NOSPLIT) )
+ {
+ if ( nSplitTest & SPLIT_HORZ )
+ eStyle = PointerStyle::HSplit;
+ else if ( nSplitTest & SPLIT_VERT )
+ eStyle = PointerStyle::VSplit;
+ }
+ }
+
+ SetPointer( eStyle );
+}
+
+void SplitWindow::Tracking( const TrackingEvent& rTEvt )
+{
+ Point aMousePosPixel = rTEvt.GetMouseEvent().GetPosPixel();
+
+ if ( mbFadeInDown )
+ {
+ if ( rTEvt.IsTrackingEnded() )
+ {
+ mbFadeInDown = false;
+ if ( mbFadeInPressed )
+ {
+ mbFadeInPressed = false;
+ Invalidate();
+
+ if ( !rTEvt.IsTrackingCanceled() )
+ FadeIn();
+ }
+ }
+ else
+ {
+ tools::Rectangle aTestRect;
+ ImplGetFadeInRect( aTestRect, true );
+ bool bNewPressed = aTestRect.Contains( aMousePosPixel );
+ if ( bNewPressed != mbFadeInPressed )
+ {
+ mbFadeInPressed = bNewPressed;
+ Invalidate();
+ }
+ }
+ }
+ else if ( mbFadeOutDown )
+ {
+ if ( rTEvt.IsTrackingEnded() )
+ {
+ mbFadeOutDown = false;
+ if ( mbFadeOutPressed )
+ {
+ mbFadeOutPressed = false;
+ Invalidate();
+
+ if ( !rTEvt.IsTrackingCanceled() )
+ FadeOut();
+ }
+ }
+ else
+ {
+ tools::Rectangle aTestRect;
+ ImplGetFadeOutRect( aTestRect );
+ bool bNewPressed = aTestRect.Contains( aMousePosPixel );
+ if ( !bNewPressed )
+ {
+ mbFadeOutPressed = bNewPressed;
+ Invalidate();
+
+ // We need a mouseevent with a position inside the button for the
+ // ImplStartSplit function!
+ MouseEvent aOrgMEvt = rTEvt.GetMouseEvent();
+ MouseEvent aNewMEvt( aTestRect.Center(), aOrgMEvt.GetClicks(),
+ aOrgMEvt.GetMode(), aOrgMEvt.GetButtons(),
+ aOrgMEvt.GetModifier() );
+
+ ImplStartSplit( aNewMEvt );
+ mbFadeOutDown = false;
+ }
+ }
+ }
+ else
+ {
+ ImplSplitMousePos( aMousePosPixel );
+ bool bSplit = true;
+ if ( mbDragFull )
+ {
+ if ( rTEvt.IsTrackingEnded() )
+ {
+ if ( rTEvt.IsTrackingCanceled() )
+ {
+ std::vector< ImplSplitItem >& rItems = mpSplitSet->mvItems;
+ size_t nItems = rItems.size();
+ for ( size_t i = 0; i < nItems; i++ )
+ {
+ rItems[i].mnSize = mpLastSizes[i*2];
+ rItems[i].mnPixSize = mpLastSizes[i*2+1];
+ }
+ ImplUpdate();
+ Split();
+ }
+ bSplit = false;
+ }
+ }
+ else
+ {
+ if ( rTEvt.IsTrackingEnded() )
+ {
+ HideTracking();
+ bSplit = !rTEvt.IsTrackingCanceled();
+ }
+ else
+ {
+ ImplDrawSplitTracking(aMousePosPixel);
+ bSplit = false;
+ }
+ }
+
+ if ( bSplit )
+ {
+ bool bPropSmaller = (mnMouseModifier & KEY_SHIFT) != 0;
+ bool bPropGreater = (mnMouseModifier & KEY_MOD1) != 0;
+ tools::Long nDelta = mnMSplitPos-mnMStartPos;
+
+ if ( (mnSplitTest & SPLIT_WINDOW) && mpMainSet->mvItems.empty() )
+ {
+ if ( (mpSplitSet == mpMainSet.get()) && mbBottomRight )
+ nDelta *= -1;
+ ImplSetWindowSize( nDelta );
+ }
+ else
+ {
+ tools::Long nNewSize = mpSplitSet->mvItems[mnSplitPos].mnPixSize;
+ if ( (mpSplitSet == mpMainSet.get()) && mbBottomRight )
+ nNewSize -= nDelta;
+ else
+ nNewSize += nDelta;
+ SplitItem( mpSplitSet->mvItems[mnSplitPos].mnId, nNewSize,
+ bPropSmaller, bPropGreater );
+ }
+
+ Split();
+
+ if ( mbDragFull )
+ {
+ PaintImmediately();
+ mnMStartPos = mnMSplitPos;
+ }
+ }
+
+ if ( rTEvt.IsTrackingEnded() )
+ {
+ mpLastSizes.reset();
+ mpSplitSet = nullptr;
+ mnMouseOff = 0;
+ mnMStartPos = 0;
+ mnMSplitPos = 0;
+ mnMouseModifier = 0;
+ mnSplitTest = 0;
+ mnSplitPos = 0;
+ }
+ }
+}
+
+bool SplitWindow::PreNotify( NotifyEvent& rNEvt )
+{
+ if( rNEvt.GetType() == NotifyEventType::MOUSEMOVE )
+ {
+ const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
+ if( pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged() )
+ {
+ // trigger redraw if mouse over state has changed
+ tools::Rectangle aFadeInRect;
+ tools::Rectangle aFadeOutRect;
+ ImplGetFadeInRect( aFadeInRect );
+ ImplGetFadeOutRect( aFadeOutRect );
+
+ if ( aFadeInRect.Contains( GetPointerPosPixel() ) != aFadeInRect.Contains( GetLastPointerPosPixel() ) )
+ Invalidate( aFadeInRect );
+ if ( aFadeOutRect.Contains( GetPointerPosPixel() ) != aFadeOutRect.Contains( GetLastPointerPosPixel() ) )
+ Invalidate( aFadeOutRect );
+
+ if( pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow() )
+ {
+ Invalidate( aFadeInRect );
+ Invalidate( aFadeOutRect );
+ }
+ }
+ }
+ return Window::PreNotify( rNEvt );
+}
+
+void SplitWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ if (mnWinStyle & WB_BORDER)
+ ImplDrawBorder(rRenderContext);
+
+ ImplDrawBorderLine(rRenderContext);
+ ImplDrawFadeOut(rRenderContext);
+ ImplDrawFadeIn(rRenderContext);
+
+ // draw splitter
+ if (!(mnWinStyle & WB_NOSPLITDRAW))
+ {
+ ImplDrawSplit(rRenderContext, mpMainSet.get(), mbHorz, !mbBottomRight);
+ }
+}
+
+void SplitWindow::Resize()
+{
+ Size aSize = GetOutputSizePixel();
+ mnDX = aSize.Width();
+ mnDY = aSize.Height();
+
+ ImplUpdate();
+ Invalidate();
+}
+
+void SplitWindow::RequestHelp( const HelpEvent& rHEvt )
+{
+ // no keyboard help for splitwin
+ if ( rHEvt.GetMode() & (HelpEventMode::BALLOON | HelpEventMode::QUICK) && !rHEvt.KeyboardActivated() )
+ {
+ Point aMousePosPixel = ScreenToOutputPixel( rHEvt.GetMousePosPixel() );
+ tools::Rectangle aHelpRect;
+ TranslateId pHelpResId;
+
+ ImplGetFadeInRect( aHelpRect, true );
+ if ( aHelpRect.Contains( aMousePosPixel ) )
+ pHelpResId = SV_HELPTEXT_FADEIN;
+ else
+ {
+ ImplGetFadeOutRect( aHelpRect );
+ if ( aHelpRect.Contains( aMousePosPixel ) )
+ pHelpResId = SV_HELPTEXT_FADEOUT;
+ }
+
+ // get rectangle
+ if (pHelpResId)
+ {
+ Point aPt = OutputToScreenPixel( aHelpRect.TopLeft() );
+ aHelpRect.SetLeft( aPt.X() );
+ aHelpRect.SetTop( aPt.Y() );
+ aPt = OutputToScreenPixel( aHelpRect.BottomRight() );
+ aHelpRect.SetRight( aPt.X() );
+ aHelpRect.SetBottom( aPt.Y() );
+
+ // get and draw text
+ OUString aStr = VclResId(pHelpResId);
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ Help::ShowBalloon( this, aHelpRect.Center(), aHelpRect, aStr );
+ else
+ Help::ShowQuickHelp( this, aHelpRect, aStr );
+ return;
+ }
+ }
+
+ DockingWindow::RequestHelp( rHEvt );
+}
+
+void SplitWindow::StateChanged( StateChangedType nType )
+{
+ switch ( nType )
+ {
+ case StateChangedType::InitShow:
+ if ( IsUpdateMode() )
+ ImplCalcLayout();
+ break;
+ case StateChangedType::UpdateMode:
+ if ( IsUpdateMode() && IsReallyShown() )
+ ImplCalcLayout();
+ break;
+ case StateChangedType::ControlBackground:
+ ImplInitSettings();
+ Invalidate();
+ break;
+ default:;
+ }
+
+ DockingWindow::StateChanged( nType );
+}
+
+void SplitWindow::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ ImplInitSettings();
+ Invalidate();
+ }
+ else
+ DockingWindow::DataChanged( rDCEvt );
+}
+
+void SplitWindow::InsertItem( sal_uInt16 nId, vcl::Window* pWindow, tools::Long nSize,
+ sal_uInt16 nPos, sal_uInt16 nIntoSetId,
+ SplitWindowItemFlags nBits )
+{
+#ifdef DBG_UTIL
+ sal_uInt16 nDbgDummy;
+ SAL_WARN_IF( ImplFindItem( mpMainSet.get(), nId, nDbgDummy ), "vcl", "SplitWindow::InsertItem() - Id already exists" );
+#endif
+
+ // Size has to be at least 1.
+ if ( nSize < 1 )
+ nSize = 1;
+
+ ImplSplitSet* pSet = ImplFindSet( mpMainSet.get(), nIntoSetId );
+#ifdef DBG_UTIL
+ SAL_WARN_IF( !pSet, "vcl", "SplitWindow::InsertItem() - Set not exists" );
+#endif
+ if(!pSet)
+ {
+ return;
+ }
+
+ // Don't insert further than the end
+ if ( nPos > pSet->mvItems.size() )
+ nPos = pSet->mvItems.size();
+
+ // Insert in set
+ pSet->mvItems.emplace( pSet->mvItems.begin() + nPos );
+
+ // init new item
+ ImplSplitItem & aItem = pSet->mvItems[nPos];
+ aItem.mnSize = nSize;
+ aItem.mnPixSize = 0;
+ aItem.mnId = nId;
+ aItem.mnBits = nBits;
+ aItem.mnMinSize=-1;
+ aItem.mnMaxSize=-1;
+
+ if ( pWindow )
+ {
+ // New VclPtr reference
+ aItem.mpWindow = pWindow;
+ aItem.mpOrgParent = pWindow->GetParent();
+
+ // Attach window to SplitWindow.
+ pWindow->Hide();
+ pWindow->SetParent( this );
+ }
+ else
+ {
+ ImplSplitSet * pNewSet = new ImplSplitSet();
+ pNewSet->mnId = nId;
+ pNewSet->mnSplitSize = pSet->mnSplitSize;
+
+ aItem.mpSet.reset(pNewSet);
+ }
+
+ pSet->mbCalcPix = true;
+
+ ImplUpdate();
+}
+
+void SplitWindow::InsertItem( sal_uInt16 nId, tools::Long nSize,
+ sal_uInt16 nPos, sal_uInt16 nIntoSetId,
+ SplitWindowItemFlags nBits )
+{
+ InsertItem( nId, nullptr, nSize, nPos, nIntoSetId, nBits );
+}
+
+void SplitWindow::RemoveItem( sal_uInt16 nId )
+{
+#ifdef DBG_UTIL
+ sal_uInt16 nDbgDummy;
+ SAL_WARN_IF( !ImplFindItem( mpMainSet.get(), nId, nDbgDummy ), "vcl", "SplitWindow::RemoveItem() - Id not found" );
+#endif
+
+ // search set
+ sal_uInt16 nPos;
+ ImplSplitSet* pSet = ImplFindItem( mpMainSet.get(), nId, nPos );
+
+ if (!pSet)
+ return;
+
+ ImplSplitItem* pItem = &pSet->mvItems[nPos];
+ VclPtr<vcl::Window> pWindow = pItem->mpWindow;
+ VclPtr<vcl::Window> pOrgParent = pItem->mpOrgParent;
+
+ // delete set if required
+ if ( !pWindow )
+ pItem->mpSet.reset();
+
+ // remove item
+ pSet->mbCalcPix = true;
+ pSet->mvItems.erase( pSet->mvItems.begin() + nPos );
+
+ ImplUpdate();
+
+ // to have the least amounts of paints delete window only here
+ if ( pWindow )
+ {
+ // restore window
+ pWindow->Hide();
+ pWindow->SetParent( pOrgParent );
+ }
+
+ // Clear and delete
+ pWindow.clear();
+ pOrgParent.clear();
+}
+
+void SplitWindow::SplitItem( sal_uInt16 nId, tools::Long nNewSize,
+ bool bPropSmall, bool bPropGreat )
+{
+ sal_uInt16 nPos;
+ ImplSplitSet* pSet = ImplFindItem( mpBaseSet, nId, nPos );
+
+ if (!pSet)
+ return;
+
+ size_t nItems = pSet->mvItems.size();
+ std::vector< ImplSplitItem >& rItems = pSet->mvItems;
+
+ // When there is an explicit minimum or maximum size then move nNewSize
+ // into that range (when it is not yet already in it.)
+ nNewSize = ValidateSize(nNewSize, rItems[nPos]);
+
+ if ( mbCalc )
+ {
+ rItems[nPos].mnSize = nNewSize;
+ return;
+ }
+
+ tools::Long nDelta = nNewSize-rItems[nPos].mnPixSize;
+ if ( !nDelta )
+ return;
+
+ // calculate area, which could be affected by splitting
+ sal_uInt16 nMin = 0;
+ sal_uInt16 nMax = nItems;
+ for (size_t i = 0; i < nItems; ++i)
+ {
+ if ( rItems[i].mbFixed )
+ {
+ if ( i < nPos )
+ nMin = i+1;
+ else
+ nMax = i;
+ }
+ }
+
+ // treat TopSet different if the window is sizeable
+ bool bSmall = true;
+ bool bGreat = true;
+ if ( (pSet == mpMainSet.get()) && (mnWinStyle & WB_SIZEABLE) )
+ {
+ if ( nPos < pSet->mvItems.size()-1 )
+ {
+ if ( !((bPropSmall && bPropGreat) ||
+ ((nDelta > 0) && bPropSmall) ||
+ ((nDelta < 0) && bPropGreat)) )
+ {
+ if ( nDelta < 0 )
+ bGreat = false;
+ else
+ bSmall = false;
+ }
+ }
+ else
+ {
+ if ( nDelta < 0 )
+ bGreat = false;
+ else
+ bSmall = false;
+ }
+ }
+ else if ( nPos >= nMax )
+ {
+ bSmall = false;
+ bGreat = false;
+ }
+ else if ( nPos && (nPos >= pSet->mvItems.size()-1) )
+ {
+ nPos--;
+ nDelta *= -1;
+ std::swap( bPropSmall, bPropGreat );
+ }
+
+ sal_uInt16 n;
+ // now splitt the windows
+ if ( nDelta < 0 )
+ {
+ if ( bGreat )
+ {
+ if ( bPropGreat )
+ {
+ tools::Long nTempDelta = nDelta;
+ do
+ {
+ n = nPos+1;
+ do
+ {
+ if ( nTempDelta )
+ {
+ rItems[n].mnPixSize++;
+ nTempDelta++;
+ }
+ n++;
+ }
+ while ( n < nMax );
+ }
+ while ( nTempDelta );
+ }
+ else
+ rItems[nPos+1].mnPixSize -= nDelta;
+ }
+
+ if ( bSmall )
+ {
+ if ( bPropSmall )
+ {
+ do
+ {
+ n = nPos+1;
+ do
+ {
+ if ( nDelta && rItems[n-1].mnPixSize )
+ {
+ rItems[n-1].mnPixSize--;
+ nDelta++;
+ }
+
+ n--;
+ }
+ while ( n > nMin );
+ }
+ while ( nDelta );
+ }
+ else
+ {
+ n = nPos+1;
+ do
+ {
+ if ( rItems[n-1].mnPixSize+nDelta < 0 )
+ {
+ nDelta += rItems[n-1].mnPixSize;
+ rItems[n-1].mnPixSize = 0;
+ }
+ else
+ {
+ rItems[n-1].mnPixSize += nDelta;
+ break;
+ }
+ n--;
+ }
+ while ( n > nMin );
+ }
+ }
+ }
+ else
+ {
+ if ( bGreat )
+ {
+ if ( bPropGreat )
+ {
+ tools::Long nTempDelta = nDelta;
+ do
+ {
+ n = nPos+1;
+ do
+ {
+ if ( nTempDelta )
+ {
+ rItems[n-1].mnPixSize++;
+ nTempDelta--;
+ }
+ n--;
+ }
+ while ( n > nMin );
+ }
+ while ( nTempDelta );
+ }
+ else
+ rItems[nPos].mnPixSize += nDelta;
+ }
+
+ if ( bSmall )
+ {
+ if ( bPropSmall )
+ {
+ do
+ {
+ n = nPos+1;
+ do
+ {
+ if ( nDelta && rItems[n].mnPixSize )
+ {
+ rItems[n].mnPixSize--;
+ nDelta--;
+ }
+
+ n++;
+ }
+ while ( n < nMax );
+ }
+ while ( nDelta );
+ }
+ else
+ {
+ n = nPos+1;
+ do
+ {
+ if ( rItems[n].mnPixSize-nDelta < 0 )
+ {
+ nDelta -= rItems[n].mnPixSize;
+ rItems[n].mnPixSize = 0;
+ }
+ else
+ {
+ rItems[n].mnPixSize -= nDelta;
+ break;
+ }
+ n++;
+ }
+ while ( n < nMax );
+ }
+ }
+ }
+
+ // update original sizes
+ ImplCalcLogSize( rItems, nItems );
+
+ ImplUpdate();
+}
+
+void SplitWindow::SetItemSize( sal_uInt16 nId, tools::Long nNewSize )
+{
+ sal_uInt16 nPos;
+ ImplSplitSet* pSet = ImplFindItem( mpBaseSet, nId, nPos );
+ ImplSplitItem* pItem;
+
+ if ( !pSet )
+ return;
+
+ // check if size is changed
+ pItem = &pSet->mvItems[nPos];
+ if ( pItem->mnSize != nNewSize )
+ {
+ // set new size and re-calculate
+ pItem->mnSize = nNewSize;
+ pSet->mbCalcPix = true;
+ ImplUpdate();
+ }
+}
+
+tools::Long SplitWindow::GetItemSize( sal_uInt16 nId ) const
+{
+ sal_uInt16 nPos;
+ ImplSplitSet* pSet = ImplFindItem( mpBaseSet, nId, nPos );
+
+ if ( pSet )
+ return pSet->mvItems[nPos].mnSize;
+ else
+ return 0;
+}
+
+tools::Long SplitWindow::GetItemSize( sal_uInt16 nId, SplitWindowItemFlags nBits ) const
+{
+ sal_uInt16 nPos;
+ ImplSplitSet* pSet = ImplFindItem( mpBaseSet, nId, nPos );
+
+ if ( pSet )
+ {
+ if ( nBits == pSet->mvItems[nPos].mnBits )
+ return pSet->mvItems[nPos].mnSize;
+ else
+ {
+ const_cast<SplitWindow*>(this)->ImplCalcLayout();
+
+ tools::Long nRelSize = 0;
+ tools::Long nPerSize = 0;
+ size_t nItems;
+ SplitWindowItemFlags nTempBits;
+ nItems = pSet->mvItems.size();
+ std::vector< ImplSplitItem >& rItems = pSet->mvItems;
+ for ( size_t i = 0; i < nItems; i++ )
+ {
+ if ( i == nPos )
+ nTempBits = nBits;
+ else
+ nTempBits = rItems[i].mnBits;
+ if ( nTempBits & SplitWindowItemFlags::RelativeSize )
+ nRelSize += rItems[i].mnPixSize;
+ else if ( nTempBits & SplitWindowItemFlags::PercentSize )
+ nPerSize += rItems[i].mnPixSize;
+ }
+ nPerSize += nRelSize;
+ if ( nBits & SplitWindowItemFlags::RelativeSize )
+ {
+ if ( nRelSize )
+ return (rItems[nPos].mnPixSize+(nRelSize/2))/nRelSize;
+ else
+ return 1;
+ }
+ else if ( nBits & SplitWindowItemFlags::PercentSize )
+ {
+ if ( nPerSize )
+ return (rItems[nPos].mnPixSize*100)/nPerSize;
+ else
+ return 1;
+ }
+ else
+ return rItems[nPos].mnPixSize;
+ }
+ }
+ else
+ return 0;
+}
+
+void SplitWindow::SetItemSizeRange (sal_uInt16 nId, const Range& rRange)
+{
+ sal_uInt16 nPos;
+ ImplSplitSet* pSet = ImplFindItem(mpBaseSet, nId, nPos);
+
+ if (pSet != nullptr)
+ {
+ pSet->mvItems[nPos].mnMinSize = rRange.Min();
+ pSet->mvItems[nPos].mnMaxSize = rRange.Max();
+ }
+}
+
+sal_uInt16 SplitWindow::GetSet( sal_uInt16 nId ) const
+{
+ sal_uInt16 nPos;
+ ImplSplitSet* pSet = ImplFindItem( mpBaseSet, nId, nPos );
+
+ if ( pSet )
+ return pSet->mnId;
+ else
+ return 0;
+}
+
+bool SplitWindow::IsItemValid( sal_uInt16 nId ) const
+{
+ sal_uInt16 nPos;
+ ImplSplitSet* pSet = mpBaseSet ? ImplFindItem(mpBaseSet, nId, nPos) : nullptr;
+
+ return pSet != nullptr;
+}
+
+sal_uInt16 SplitWindow::GetItemId( vcl::Window* pWindow ) const
+{
+ return ImplFindItem( mpBaseSet, pWindow );
+}
+
+sal_uInt16 SplitWindow::GetItemId( const Point& rPos ) const
+{
+ return ImplFindItem( mpBaseSet, rPos, mbHorz, !mbBottomRight );
+}
+
+sal_uInt16 SplitWindow::GetItemPos( sal_uInt16 nId, sal_uInt16 nSetId ) const
+{
+ ImplSplitSet* pSet = ImplFindSet( mpBaseSet, nSetId );
+ sal_uInt16 nPos = SPLITWINDOW_ITEM_NOTFOUND;
+
+ if ( pSet )
+ {
+ for ( size_t i = 0; i < pSet->mvItems.size(); i++ )
+ {
+ if ( pSet->mvItems[i].mnId == nId )
+ {
+ nPos = i;
+ break;
+ }
+ }
+ }
+
+ return nPos;
+}
+
+sal_uInt16 SplitWindow::GetItemId( sal_uInt16 nPos ) const
+{
+ ImplSplitSet* pSet = ImplFindSet( mpBaseSet, 0/*nSetId*/ );
+ if ( pSet && (nPos < pSet->mvItems.size()) )
+ return pSet->mvItems[nPos].mnId;
+ else
+ return 0;
+}
+
+sal_uInt16 SplitWindow::GetItemCount( sal_uInt16 nSetId ) const
+{
+ ImplSplitSet* pSet = ImplFindSet( mpBaseSet, nSetId );
+ if ( pSet )
+ return pSet->mvItems.size();
+ else
+ return 0;
+}
+
+void SplitWindow::ImplNewAlign()
+{
+ switch ( meAlign )
+ {
+ case WindowAlign::Top:
+ mbHorz = true;
+ mbBottomRight = false;
+ break;
+ case WindowAlign::Bottom:
+ mbHorz = true;
+ mbBottomRight = true;
+ break;
+ case WindowAlign::Left:
+ mbHorz = false;
+ mbBottomRight = false;
+ break;
+ case WindowAlign::Right:
+ mbHorz = false;
+ mbBottomRight = true;
+ break;
+ }
+
+ if ( mnWinStyle & WB_BORDER )
+ {
+ ImplCalcBorder( meAlign, mnLeftBorder, mnTopBorder,
+ mnRightBorder, mnBottomBorder );
+ }
+
+ if ( IsReallyVisible() && IsUpdateMode() )
+ Invalidate();
+ ImplUpdate();
+}
+
+void SplitWindow::SetAlign( WindowAlign eNewAlign )
+{
+ if ( meAlign != eNewAlign )
+ {
+ meAlign = eNewAlign;
+ ImplNewAlign();
+ }
+}
+
+void SplitWindow::ShowFadeInHideButton()
+{
+ mbFadeIn = true;
+ ImplUpdate();
+}
+
+void SplitWindow::ShowFadeOutButton()
+{
+ mbFadeOut = true;
+ ImplUpdate();
+}
+
+tools::Long SplitWindow::GetFadeInSize() const
+{
+ tools::Long n = 0;
+
+ if ( mbHorz )
+ n = mnTopBorder+mnBottomBorder;
+ else
+ n = mnLeftBorder+mnRightBorder;
+
+ return n+SPLITWIN_SPLITSIZE+SPLITWIN_SPLITSIZEEX-2;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/stacking.cxx b/vcl/source/window/stacking.cxx
new file mode 100644
index 0000000000..c144eaa9bb
--- /dev/null
+++ b/vcl/source/window/stacking.cxx
@@ -0,0 +1,1152 @@
+/* -*- 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 <vcl/syswin.hxx>
+#include <vcl/window.hxx>
+#include <vcl/taskpanelist.hxx>
+#include <sal/log.hxx>
+
+#include <salframe.hxx>
+#include <salobj.hxx>
+#include <svdata.hxx>
+#include <window.h>
+#include <brdwin.hxx>
+
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::datatransfer::clipboard;
+using namespace ::com::sun::star::datatransfer::dnd;
+using namespace ::com::sun::star;
+
+using ::com::sun::star::awt::XTopWindow;
+
+struct ImplCalcToTopData
+{
+ std::unique_ptr<ImplCalcToTopData> mpNext;
+ VclPtr<vcl::Window> mpWindow;
+ std::unique_ptr<vcl::Region> mpInvalidateRegion;
+};
+
+namespace vcl {
+
+vcl::Window* Window::ImplGetTopmostFrameWindow()
+{
+ vcl::Window *pTopmostParent = this;
+ while( pTopmostParent->ImplGetParent() )
+ pTopmostParent = pTopmostParent->ImplGetParent();
+ return pTopmostParent->mpWindowImpl->mpFrameWindow;
+}
+
+void Window::ImplInsertWindow( vcl::Window* pParent )
+{
+ mpWindowImpl->mpParent = pParent;
+ mpWindowImpl->mpRealParent = pParent;
+
+ if ( !pParent || mpWindowImpl->mbFrame )
+ return;
+
+ // search frame window and set window frame data
+ vcl::Window* pFrameParent = pParent->mpWindowImpl->mpFrameWindow;
+ mpWindowImpl->mpFrameData = pFrameParent->mpWindowImpl->mpFrameData;
+ if (mpWindowImpl->mpFrame != pFrameParent->mpWindowImpl->mpFrame)
+ {
+ mpWindowImpl->mpFrame = pFrameParent->mpWindowImpl->mpFrame;
+ if (mpWindowImpl->mpSysObj)
+ mpWindowImpl->mpSysObj->Reparent(mpWindowImpl->mpFrame);
+ }
+ mpWindowImpl->mpFrameWindow = pFrameParent;
+ mpWindowImpl->mbFrame = false;
+
+ // search overlap window and insert window in list
+ if ( ImplIsOverlapWindow() )
+ {
+ vcl::Window* pFirstOverlapParent = pParent;
+ while ( !pFirstOverlapParent->ImplIsOverlapWindow() )
+ pFirstOverlapParent = pFirstOverlapParent->ImplGetParent();
+ mpWindowImpl->mpOverlapWindow = pFirstOverlapParent;
+
+ mpWindowImpl->mpNextOverlap = mpWindowImpl->mpFrameData->mpFirstOverlap;
+ mpWindowImpl->mpFrameData->mpFirstOverlap = this;
+
+ // Overlap-Windows are by default the uppermost
+ mpWindowImpl->mpNext = pFirstOverlapParent->mpWindowImpl->mpFirstOverlap;
+ pFirstOverlapParent->mpWindowImpl->mpFirstOverlap = this;
+ if ( !pFirstOverlapParent->mpWindowImpl->mpLastOverlap )
+ pFirstOverlapParent->mpWindowImpl->mpLastOverlap = this;
+ else
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = this;
+ }
+ else
+ {
+ if ( pParent->ImplIsOverlapWindow() )
+ mpWindowImpl->mpOverlapWindow = pParent;
+ else
+ mpWindowImpl->mpOverlapWindow = pParent->mpWindowImpl->mpOverlapWindow;
+ mpWindowImpl->mpPrev = pParent->mpWindowImpl->mpLastChild;
+ pParent->mpWindowImpl->mpLastChild = this;
+ if ( !pParent->mpWindowImpl->mpFirstChild )
+ pParent->mpWindowImpl->mpFirstChild = this;
+ else
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
+ }
+}
+
+void Window::ImplRemoveWindow( bool bRemoveFrameData )
+{
+ // remove window from the lists
+ if ( !mpWindowImpl->mbFrame )
+ {
+ if ( ImplIsOverlapWindow() )
+ {
+ if ( mpWindowImpl->mpFrameData->mpFirstOverlap.get() == this )
+ mpWindowImpl->mpFrameData->mpFirstOverlap = mpWindowImpl->mpNextOverlap;
+ else
+ {
+ vcl::Window* pTempWin = mpWindowImpl->mpFrameData->mpFirstOverlap;
+ while ( pTempWin->mpWindowImpl->mpNextOverlap.get() != this )
+ pTempWin = pTempWin->mpWindowImpl->mpNextOverlap;
+ pTempWin->mpWindowImpl->mpNextOverlap = mpWindowImpl->mpNextOverlap;
+ }
+
+ if ( mpWindowImpl->mpPrev )
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
+ else
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap = mpWindowImpl->mpNext;
+ if ( mpWindowImpl->mpNext )
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
+ else
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = mpWindowImpl->mpPrev;
+ }
+ else
+ {
+ if ( mpWindowImpl->mpPrev )
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
+ else if ( mpWindowImpl->mpParent )
+ mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = mpWindowImpl->mpNext;
+ if ( mpWindowImpl->mpNext )
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
+ else if ( mpWindowImpl->mpParent )
+ mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = mpWindowImpl->mpPrev;
+ }
+
+ mpWindowImpl->mpPrev = nullptr;
+ mpWindowImpl->mpNext = nullptr;
+ }
+
+ if ( bRemoveFrameData )
+ {
+ // release the graphic
+ OutputDevice *pOutDev = GetOutDev();
+ pOutDev->ReleaseGraphics();
+ }
+}
+
+void Window::reorderWithinParent(sal_uInt16 nNewPosition)
+{
+ sal_uInt16 nChildCount = 0;
+ vcl::Window *pSource = mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild;
+ while (pSource)
+ {
+ if (nChildCount == nNewPosition)
+ break;
+ pSource = pSource->mpWindowImpl->mpNext;
+ nChildCount++;
+ }
+
+ if (pSource == this) //already at the right place
+ return;
+
+ ImplRemoveWindow(false);
+
+ if (pSource)
+ {
+ mpWindowImpl->mpNext = pSource;
+ mpWindowImpl->mpPrev = pSource->mpWindowImpl->mpPrev;
+ pSource->mpWindowImpl->mpPrev = this;
+ }
+ else
+ mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = this;
+
+ if (mpWindowImpl->mpPrev)
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
+ else
+ mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = this;
+}
+
+void Window::ImplToBottomChild()
+{
+ if ( ImplIsOverlapWindow() || mpWindowImpl->mbReallyVisible || (mpWindowImpl->mpParent->mpWindowImpl->mpLastChild.get() == this) )
+ return;
+
+ // put the window to the end of the list
+ if ( mpWindowImpl->mpPrev )
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
+ else
+ mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = mpWindowImpl->mpNext;
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
+ mpWindowImpl->mpPrev = mpWindowImpl->mpParent->mpWindowImpl->mpLastChild;
+ mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = this;
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
+ mpWindowImpl->mpNext = nullptr;
+}
+
+void Window::ImplCalcToTop( ImplCalcToTopData* pPrevData )
+{
+ SAL_WARN_IF( !ImplIsOverlapWindow(), "vcl", "Window::ImplCalcToTop(): Is not an OverlapWindow" );
+
+ if ( mpWindowImpl->mbFrame )
+ return;
+
+ if ( !IsReallyVisible() )
+ return;
+
+ // calculate region, where the window overlaps with other windows
+ vcl::Region aRegion( GetOutputRectPixel() );
+ vcl::Region aInvalidateRegion;
+ ImplCalcOverlapRegionOverlaps( aRegion, aInvalidateRegion );
+
+ if ( !aInvalidateRegion.IsEmpty() )
+ {
+ ImplCalcToTopData* pData = new ImplCalcToTopData;
+ pPrevData->mpNext.reset(pData);
+ pData->mpWindow = this;
+ pData->mpInvalidateRegion.reset(new vcl::Region( aInvalidateRegion ));
+ }
+}
+
+void Window::ImplToTop( ToTopFlags nFlags )
+{
+ SAL_WARN_IF( !ImplIsOverlapWindow(), "vcl", "Window::ImplToTop(): Is not an OverlapWindow" );
+
+ if ( mpWindowImpl->mbFrame )
+ {
+ // on a mouse click in the external window, it is the latter's
+ // responsibility to assure our frame is put in front
+ if ( !mpWindowImpl->mpFrameData->mbHasFocus &&
+ !mpWindowImpl->mpFrameData->mbSysObjFocus &&
+ !mpWindowImpl->mpFrameData->mbInSysObjFocusHdl &&
+ !mpWindowImpl->mpFrameData->mbInSysObjToTopHdl )
+ {
+ // do not bring floating windows on the client to top
+ if( !ImplGetClientWindow() || !(ImplGetClientWindow()->GetStyle() & WB_SYSTEMFLOATWIN) )
+ {
+ SalFrameToTop nSysFlags = SalFrameToTop::NONE;
+ if ( nFlags & ToTopFlags::RestoreWhenMin )
+ nSysFlags |= SalFrameToTop::RestoreWhenMin;
+ if ( nFlags & ToTopFlags::ForegroundTask )
+ nSysFlags |= SalFrameToTop::ForegroundTask;
+ if ( nFlags & ToTopFlags::GrabFocusOnly )
+ nSysFlags |= SalFrameToTop::GrabFocusOnly;
+ mpWindowImpl->mpFrame->ToTop( nSysFlags );
+ }
+ }
+ }
+ else
+ {
+ if ( mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap.get() != this )
+ {
+ // remove window from the list
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
+ if ( mpWindowImpl->mpNext )
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
+ else
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = mpWindowImpl->mpPrev;
+
+ // take AlwaysOnTop into account
+ bool bOnTop = IsAlwaysOnTopEnabled();
+ vcl::Window* pNextWin = mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap;
+ if ( !bOnTop )
+ {
+ while ( pNextWin )
+ {
+ if ( !pNextWin->IsAlwaysOnTopEnabled() )
+ break;
+ pNextWin = pNextWin->mpWindowImpl->mpNext;
+ }
+ }
+
+ // add the window to the list again
+ mpWindowImpl->mpNext = pNextWin;
+ if ( pNextWin )
+ {
+ mpWindowImpl->mpPrev = pNextWin->mpWindowImpl->mpPrev;
+ pNextWin->mpWindowImpl->mpPrev = this;
+ }
+ else
+ {
+ mpWindowImpl->mpPrev = mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap;
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = this;
+ }
+ if ( mpWindowImpl->mpPrev )
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
+ else
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap = this;
+
+ // recalculate ClipRegion of this and all overlapping windows
+ if ( IsReallyVisible() )
+ {
+ mpWindowImpl->mpOverlapWindow->ImplSetClipFlagOverlapWindows();
+ }
+ }
+ }
+}
+
+void Window::ImplStartToTop( ToTopFlags nFlags )
+{
+ ImplCalcToTopData aStartData;
+ ImplCalcToTopData* pCurData;
+ vcl::Window* pOverlapWindow;
+ if ( ImplIsOverlapWindow() )
+ pOverlapWindow = this;
+ else
+ pOverlapWindow = mpWindowImpl->mpOverlapWindow;
+
+ // first calculate paint areas
+ vcl::Window* pTempOverlapWindow = pOverlapWindow;
+ aStartData.mpNext = nullptr;
+ pCurData = &aStartData;
+ do
+ {
+ pTempOverlapWindow->ImplCalcToTop( pCurData );
+ if ( pCurData->mpNext )
+ pCurData = pCurData->mpNext.get();
+ pTempOverlapWindow = pTempOverlapWindow->mpWindowImpl->mpOverlapWindow;
+ }
+ while ( !pTempOverlapWindow->mpWindowImpl->mbFrame );
+ // next calculate the paint areas of the ChildOverlap windows
+ pTempOverlapWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pTempOverlapWindow )
+ {
+ pTempOverlapWindow->ImplCalcToTop( pCurData );
+ if ( pCurData->mpNext )
+ pCurData = pCurData->mpNext.get();
+ pTempOverlapWindow = pTempOverlapWindow->mpWindowImpl->mpNext;
+ }
+
+ // and next change the windows list
+ pTempOverlapWindow = pOverlapWindow;
+ do
+ {
+ pTempOverlapWindow->ImplToTop( nFlags );
+ pTempOverlapWindow = pTempOverlapWindow->mpWindowImpl->mpOverlapWindow;
+ }
+ while ( !pTempOverlapWindow->mpWindowImpl->mbFrame );
+ // as last step invalidate the invalid areas
+ pCurData = aStartData.mpNext.get();
+ while ( pCurData )
+ {
+ pCurData->mpWindow->ImplInvalidateFrameRegion( pCurData->mpInvalidateRegion.get(), InvalidateFlags::Children );
+ pCurData = pCurData->mpNext.get();
+ }
+}
+
+void Window::ImplFocusToTop( ToTopFlags nFlags, bool bReallyVisible )
+{
+ // do we need to fetch the focus?
+ if ( !(nFlags & ToTopFlags::NoGrabFocus) )
+ {
+ // first window with GrabFocus-Activate gets the focus
+ vcl::Window* pFocusWindow = this;
+ while ( !pFocusWindow->ImplIsOverlapWindow() )
+ {
+ // if the window has no BorderWindow, we
+ // should always find the belonging BorderWindow
+ if ( !pFocusWindow->mpWindowImpl->mpBorderWindow )
+ {
+ if ( pFocusWindow->mpWindowImpl->mnActivateMode & ActivateModeFlags::GrabFocus )
+ break;
+ }
+ pFocusWindow = pFocusWindow->ImplGetParent();
+ }
+ if ( (pFocusWindow->mpWindowImpl->mnActivateMode & ActivateModeFlags::GrabFocus) &&
+ !pFocusWindow->HasChildPathFocus( true ) )
+ pFocusWindow->GrabFocus();
+ }
+
+ if ( bReallyVisible )
+ ImplGenerateMouseMove();
+}
+
+void Window::ImplShowAllOverlaps()
+{
+ vcl::Window* pOverlapWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pOverlapWindow )
+ {
+ if ( pOverlapWindow->mpWindowImpl->mbOverlapVisible )
+ {
+ pOverlapWindow->Show( true, ShowFlags::NoActivate );
+ pOverlapWindow->mpWindowImpl->mbOverlapVisible = false;
+ }
+
+ pOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplHideAllOverlaps()
+{
+ vcl::Window* pOverlapWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pOverlapWindow )
+ {
+ if ( pOverlapWindow->IsVisible() )
+ {
+ pOverlapWindow->mpWindowImpl->mbOverlapVisible = true;
+ pOverlapWindow->Show( false );
+ }
+
+ pOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ToTop( ToTopFlags nFlags )
+{
+ if (!mpWindowImpl)
+ return;
+
+ ImplStartToTop( nFlags );
+ ImplFocusToTop( nFlags, IsReallyVisible() );
+}
+
+void Window::SetZOrder( vcl::Window* pRefWindow, ZOrderFlags nFlags )
+{
+
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ mpWindowImpl->mpBorderWindow->SetZOrder( pRefWindow, nFlags );
+ return;
+ }
+
+ if ( nFlags & ZOrderFlags::First )
+ {
+ if ( ImplIsOverlapWindow() )
+ pRefWindow = mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap;
+ else
+ pRefWindow = mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild;
+ nFlags |= ZOrderFlags::Before;
+ }
+ else if ( nFlags & ZOrderFlags::Last )
+ {
+ if ( ImplIsOverlapWindow() )
+ pRefWindow = mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap;
+ else
+ pRefWindow = mpWindowImpl->mpParent->mpWindowImpl->mpLastChild;
+ nFlags |= ZOrderFlags::Behind;
+ }
+
+ while ( pRefWindow && pRefWindow->mpWindowImpl->mpBorderWindow )
+ pRefWindow = pRefWindow->mpWindowImpl->mpBorderWindow;
+ if (!pRefWindow || pRefWindow == this || mpWindowImpl->mbFrame)
+ return;
+
+ SAL_WARN_IF( pRefWindow->mpWindowImpl->mpParent != mpWindowImpl->mpParent, "vcl", "Window::SetZOrder() - pRefWindow has other parent" );
+ if ( nFlags & ZOrderFlags::Before )
+ {
+ if ( pRefWindow->mpWindowImpl->mpPrev.get() == this )
+ return;
+
+ if ( ImplIsOverlapWindow() )
+ {
+ if ( mpWindowImpl->mpPrev )
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
+ else
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap = mpWindowImpl->mpNext;
+ if ( mpWindowImpl->mpNext )
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
+ else
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = mpWindowImpl->mpPrev;
+ if ( !pRefWindow->mpWindowImpl->mpPrev )
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap = this;
+ }
+ else
+ {
+ if ( mpWindowImpl->mpPrev )
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
+ else
+ mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = mpWindowImpl->mpNext;
+ if ( mpWindowImpl->mpNext )
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
+ else
+ mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = mpWindowImpl->mpPrev;
+ if ( !pRefWindow->mpWindowImpl->mpPrev )
+ mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = this;
+ }
+
+ mpWindowImpl->mpPrev = pRefWindow->mpWindowImpl->mpPrev;
+ mpWindowImpl->mpNext = pRefWindow;
+ if ( mpWindowImpl->mpPrev )
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = this;
+ }
+ else if ( nFlags & ZOrderFlags::Behind )
+ {
+ if ( pRefWindow->mpWindowImpl->mpNext.get() == this )
+ return;
+
+ if ( ImplIsOverlapWindow() )
+ {
+ if ( mpWindowImpl->mpPrev )
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
+ else
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap = mpWindowImpl->mpNext;
+ if ( mpWindowImpl->mpNext )
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
+ else
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = mpWindowImpl->mpPrev;
+ if ( !pRefWindow->mpWindowImpl->mpNext )
+ mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpLastOverlap = this;
+ }
+ else
+ {
+ if ( mpWindowImpl->mpPrev )
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = mpWindowImpl->mpNext;
+ else
+ mpWindowImpl->mpParent->mpWindowImpl->mpFirstChild = mpWindowImpl->mpNext;
+ if ( mpWindowImpl->mpNext )
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = mpWindowImpl->mpPrev;
+ else
+ mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = mpWindowImpl->mpPrev;
+ if ( !pRefWindow->mpWindowImpl->mpNext )
+ mpWindowImpl->mpParent->mpWindowImpl->mpLastChild = this;
+ }
+
+ mpWindowImpl->mpPrev = pRefWindow;
+ mpWindowImpl->mpNext = pRefWindow->mpWindowImpl->mpNext;
+ if ( mpWindowImpl->mpNext )
+ mpWindowImpl->mpNext->mpWindowImpl->mpPrev = this;
+ mpWindowImpl->mpPrev->mpWindowImpl->mpNext = this;
+ }
+
+ if ( !IsReallyVisible() )
+ return;
+
+ if ( !mpWindowImpl->mbInitWinClipRegion && mpWindowImpl->maWinClipRegion.IsEmpty() )
+ return;
+
+ bool bInitWinClipRegion = mpWindowImpl->mbInitWinClipRegion;
+ ImplSetClipFlag();
+
+ // When ClipRegion was not initialised, assume
+ // the window has not been sent, therefore do not
+ // trigger any Invalidates. This is an optimization
+ // for HTML documents with many controls. If this
+ // check gives problems, a flag should be introduced
+ // which tracks whether the window has already been
+ // emitted after Show
+ if ( bInitWinClipRegion )
+ return;
+
+ // Invalidate all windows which are next to each other
+ // Is INCOMPLETE !!!
+ tools::Rectangle aWinRect = GetOutputRectPixel();
+ vcl::Window* pWindow = nullptr;
+ if ( ImplIsOverlapWindow() )
+ {
+ if ( mpWindowImpl->mpOverlapWindow )
+ pWindow = mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpFirstOverlap;
+ }
+ else
+ pWindow = ImplGetParent()->mpWindowImpl->mpFirstChild;
+ // Invalidate all windows in front of us and which are covered by us
+ while ( pWindow )
+ {
+ if ( pWindow == this )
+ break;
+ tools::Rectangle aCompRect = pWindow->GetOutputRectPixel();
+ if ( aWinRect.Overlaps( aCompRect ) )
+ pWindow->Invalidate( InvalidateFlags::Children | InvalidateFlags::NoTransparent );
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+
+ // If we are covered by a window in the background
+ // we should redraw it
+ while ( pWindow )
+ {
+ if ( pWindow != this )
+ {
+ tools::Rectangle aCompRect = pWindow->GetOutputRectPixel();
+ if ( aWinRect.Overlaps( aCompRect ) )
+ {
+ Invalidate( InvalidateFlags::Children | InvalidateFlags::NoTransparent );
+ break;
+ }
+ }
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::EnableAlwaysOnTop( bool bEnable )
+{
+
+ mpWindowImpl->mbAlwaysOnTop = bEnable;
+
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->EnableAlwaysOnTop( bEnable );
+ else if ( bEnable && IsReallyVisible() )
+ ToTop();
+
+ if ( mpWindowImpl->mbFrame )
+ mpWindowImpl->mpFrame->SetAlwaysOnTop( bEnable );
+}
+
+bool Window::IsTopWindow() const
+{
+ if ( !mpWindowImpl || mpWindowImpl->mbInDispose )
+ return false;
+
+ // topwindows must be frames or they must have a borderwindow which is a frame
+ if( !mpWindowImpl->mbFrame && (!mpWindowImpl->mpBorderWindow || !mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame ) )
+ return false;
+
+ ImplGetWinData();
+ if( mpWindowImpl->mpWinData->mnIsTopWindow == sal_uInt16(~0)) // still uninitialized
+ {
+ // #113722#, cache result of expensive queryInterface call
+ vcl::Window *pThisWin = const_cast<vcl::Window*>(this);
+ uno::Reference< XTopWindow > xTopWindow( pThisWin->GetComponentInterface(), UNO_QUERY );
+ pThisWin->mpWindowImpl->mpWinData->mnIsTopWindow = xTopWindow.is() ? 1 : 0;
+ }
+ return mpWindowImpl->mpWinData->mnIsTopWindow == 1;
+}
+
+vcl::Window* Window::ImplFindWindow( const Point& rFramePos )
+{
+ vcl::Window* pTempWindow;
+ vcl::Window* pFindWindow;
+
+ // first check all overlapping windows
+ pTempWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pTempWindow )
+ {
+ pFindWindow = pTempWindow->ImplFindWindow( rFramePos );
+ if ( pFindWindow )
+ return pFindWindow;
+ pTempWindow = pTempWindow->mpWindowImpl->mpNext;
+ }
+
+ // then we check our window
+ if ( !mpWindowImpl->mbVisible )
+ return nullptr;
+
+ WindowHitTest nHitTest = ImplHitTest( rFramePos );
+ if ( nHitTest & WindowHitTest::Inside )
+ {
+ // and then we check all child windows
+ pTempWindow = mpWindowImpl->mpFirstChild;
+ while ( pTempWindow )
+ {
+ pFindWindow = pTempWindow->ImplFindWindow( rFramePos );
+ if ( pFindWindow )
+ return pFindWindow;
+ pTempWindow = pTempWindow->mpWindowImpl->mpNext;
+ }
+
+ if ( nHitTest & WindowHitTest::Transparent )
+ return nullptr;
+ else
+ return this;
+ }
+
+ return nullptr;
+}
+
+bool Window::ImplIsRealParentPath( const vcl::Window* pWindow ) const
+{
+ pWindow = pWindow->GetParent();
+ while ( pWindow )
+ {
+ if ( pWindow == this )
+ return true;
+ pWindow = pWindow->GetParent();
+ }
+
+ return false;
+}
+
+bool Window::ImplIsChild( const vcl::Window* pWindow, bool bSystemWindow ) const
+{
+ do
+ {
+ if ( !bSystemWindow && pWindow->ImplIsOverlapWindow() )
+ break;
+
+ pWindow = pWindow->ImplGetParent();
+
+ if ( pWindow == this )
+ return true;
+ }
+ while ( pWindow );
+
+ return false;
+}
+
+bool Window::ImplIsWindowOrChild( const vcl::Window* pWindow, bool bSystemWindow ) const
+{
+ if ( this == pWindow )
+ return true;
+ return ImplIsChild( pWindow, bSystemWindow );
+}
+
+void Window::ImplResetReallyVisible()
+{
+ bool bBecameReallyInvisible = mpWindowImpl->mbReallyVisible;
+
+ GetOutDev()->mbDevOutput = false;
+ mpWindowImpl->mbReallyVisible = false;
+ mpWindowImpl->mbReallyShown = false;
+
+ // the SHOW/HIDE events serve as indicators to send child creation/destroy events to the access bridge.
+ // For this, the data member of the event must not be NULL.
+ // Previously, we did this in Window::Show, but there some events got lost in certain situations.
+ if( bBecameReallyInvisible && ImplIsAccessibleCandidate() )
+ CallEventListeners( VclEventId::WindowHide, this );
+ // TODO. It's kind of a hack that we're re-using the VclEventId::WindowHide. Normally, we should
+ // introduce another event which explicitly triggers the Accessibility implementations.
+
+ vcl::Window* pWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbReallyVisible )
+ pWindow->ImplResetReallyVisible();
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+
+ pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbReallyVisible )
+ pWindow->ImplResetReallyVisible();
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplUpdateWindowPtr( vcl::Window* pWindow )
+{
+ if ( mpWindowImpl->mpFrameWindow != pWindow->mpWindowImpl->mpFrameWindow )
+ {
+ // release graphic
+ OutputDevice *pOutDev = GetOutDev();
+ pOutDev->ReleaseGraphics();
+ }
+
+ mpWindowImpl->mpFrameData = pWindow->mpWindowImpl->mpFrameData;
+ if (mpWindowImpl->mpFrame != pWindow->mpWindowImpl->mpFrame)
+ {
+ mpWindowImpl->mpFrame = pWindow->mpWindowImpl->mpFrame;
+ if (mpWindowImpl->mpSysObj)
+ mpWindowImpl->mpSysObj->Reparent(mpWindowImpl->mpFrame);
+ }
+ mpWindowImpl->mpFrameWindow = pWindow->mpWindowImpl->mpFrameWindow;
+ if ( pWindow->ImplIsOverlapWindow() )
+ mpWindowImpl->mpOverlapWindow = pWindow;
+ else
+ mpWindowImpl->mpOverlapWindow = pWindow->mpWindowImpl->mpOverlapWindow;
+
+ vcl::Window* pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->ImplUpdateWindowPtr( pWindow );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplUpdateWindowPtr()
+{
+ vcl::Window* pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->ImplUpdateWindowPtr( this );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplUpdateOverlapWindowPtr( bool bNewFrame )
+{
+ bool bVisible = IsVisible();
+ Show( false );
+ ImplRemoveWindow( bNewFrame );
+ vcl::Window* pRealParent = mpWindowImpl->mpRealParent;
+ ImplInsertWindow( ImplGetParent() );
+ mpWindowImpl->mpRealParent = pRealParent;
+ ImplUpdateWindowPtr();
+ if ( ImplUpdatePos() )
+ ImplUpdateSysObjPos();
+
+ if ( bNewFrame )
+ {
+ vcl::Window* pOverlapWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pOverlapWindow )
+ {
+ vcl::Window* pNextOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
+ pOverlapWindow->ImplUpdateOverlapWindowPtr( bNewFrame );
+ pOverlapWindow = pNextOverlapWindow;
+ }
+ }
+
+ if ( bVisible )
+ Show();
+}
+
+SystemWindow* Window::GetSystemWindow() const
+{
+
+ const vcl::Window* pWin = this;
+ while ( pWin && !pWin->IsSystemWindow() )
+ pWin = pWin->GetParent();
+ return static_cast<SystemWindow*>(const_cast<Window*>(pWin));
+}
+
+static SystemWindow *ImplGetLastSystemWindow( vcl::Window *pWin )
+{
+ // get the most top-level system window, the one that contains the taskpanelist
+ SystemWindow *pSysWin = nullptr;
+ if( !pWin )
+ return pSysWin;
+ vcl::Window *pMyParent = pWin;
+ while ( pMyParent )
+ {
+ if ( pMyParent->IsSystemWindow() )
+ pSysWin = static_cast<SystemWindow*>(pMyParent);
+ pMyParent = pMyParent->GetParent();
+ }
+ return pSysWin;
+}
+
+void Window::SetParent( vcl::Window* pNewParent )
+{
+ SAL_WARN_IF( !pNewParent, "vcl", "Window::SetParent(): pParent == NULL" );
+ SAL_WARN_IF( pNewParent == this, "vcl", "someone tried to reparent a window to itself" );
+
+ if( !pNewParent || pNewParent == this )
+ return;
+
+ // check if the taskpanelist would change and move the window pointer accordingly
+ SystemWindow *pSysWin = ImplGetLastSystemWindow(this);
+ SystemWindow *pNewSysWin = nullptr;
+ bool bChangeTaskPaneList = false;
+ if( pSysWin && pSysWin->ImplIsInTaskPaneList( this ) )
+ {
+ pNewSysWin = ImplGetLastSystemWindow( pNewParent );
+ if( pNewSysWin && pNewSysWin != pSysWin )
+ {
+ bChangeTaskPaneList = true;
+ pSysWin->GetTaskPaneList()->RemoveWindow( this );
+ }
+ }
+ // remove ownerdraw decorated windows from list in the top-most frame window
+ if( (GetStyle() & WB_OWNERDRAWDECORATION) && mpWindowImpl->mbFrame )
+ {
+ ::std::vector< VclPtr<vcl::Window> >& rList = ImplGetOwnerDrawList();
+ auto p = ::std::find( rList.begin(), rList.end(), VclPtr<vcl::Window>(this) );
+ if( p != rList.end() )
+ rList.erase( p );
+ }
+
+ ImplSetFrameParent( pNewParent );
+
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ mpWindowImpl->mpRealParent = pNewParent;
+ mpWindowImpl->mpBorderWindow->SetParent( pNewParent );
+ return;
+ }
+
+ if ( mpWindowImpl->mpParent.get() == pNewParent )
+ return;
+
+ if ( mpWindowImpl->mbFrame )
+ mpWindowImpl->mpFrame->SetParent( pNewParent->mpWindowImpl->mpFrame );
+
+ bool bVisible = IsVisible();
+ Show( false, ShowFlags::NoFocusChange );
+
+ // check if the overlap window changes
+ vcl::Window* pOldOverlapWindow;
+ vcl::Window* pNewOverlapWindow = nullptr;
+ if ( ImplIsOverlapWindow() )
+ pOldOverlapWindow = nullptr;
+ else
+ {
+ pNewOverlapWindow = pNewParent->ImplGetFirstOverlapWindow();
+ if ( mpWindowImpl->mpOverlapWindow.get() != pNewOverlapWindow )
+ pOldOverlapWindow = mpWindowImpl->mpOverlapWindow;
+ else
+ pOldOverlapWindow = nullptr;
+ }
+
+ // convert windows in the hierarchy
+ bool bFocusOverlapWin = HasChildPathFocus( true );
+ bool bFocusWin = HasChildPathFocus();
+ bool bNewFrame = pNewParent->mpWindowImpl->mpFrameWindow != mpWindowImpl->mpFrameWindow;
+ if ( bNewFrame )
+ {
+ if ( mpWindowImpl->mpFrameData->mpFocusWin )
+ {
+ if ( IsWindowOrChild( mpWindowImpl->mpFrameData->mpFocusWin ) )
+ mpWindowImpl->mpFrameData->mpFocusWin = nullptr;
+ }
+ if ( mpWindowImpl->mpFrameData->mpMouseMoveWin )
+ {
+ if ( IsWindowOrChild( mpWindowImpl->mpFrameData->mpMouseMoveWin ) )
+ mpWindowImpl->mpFrameData->mpMouseMoveWin = nullptr;
+ }
+ if ( mpWindowImpl->mpFrameData->mpMouseDownWin )
+ {
+ if ( IsWindowOrChild( mpWindowImpl->mpFrameData->mpMouseDownWin ) )
+ mpWindowImpl->mpFrameData->mpMouseDownWin = nullptr;
+ }
+ }
+ ImplRemoveWindow( bNewFrame );
+ ImplInsertWindow( pNewParent );
+ if ( mpWindowImpl->mnParentClipMode & ParentClipMode::Clip )
+ pNewParent->mpWindowImpl->mbClipChildren = true;
+ ImplUpdateWindowPtr();
+ if ( ImplUpdatePos() )
+ ImplUpdateSysObjPos();
+
+ // If the Overlap-Window has changed, we need to test whether
+ // OverlapWindows that had the Child window as their parent
+ // need to be put into the window hierarchy.
+ if ( ImplIsOverlapWindow() )
+ {
+ if ( bNewFrame )
+ {
+ vcl::Window* pOverlapWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pOverlapWindow )
+ {
+ vcl::Window* pNextOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
+ pOverlapWindow->ImplUpdateOverlapWindowPtr( bNewFrame );
+ pOverlapWindow = pNextOverlapWindow;
+ }
+ }
+ }
+ else if ( pOldOverlapWindow )
+ {
+ // reset Focus-Save
+ if ( bFocusWin ||
+ (pOldOverlapWindow->mpWindowImpl->mpLastFocusWindow &&
+ IsWindowOrChild( pOldOverlapWindow->mpWindowImpl->mpLastFocusWindow )) )
+ pOldOverlapWindow->mpWindowImpl->mpLastFocusWindow = nullptr;
+
+ vcl::Window* pOverlapWindow = pOldOverlapWindow->mpWindowImpl->mpFirstOverlap;
+ while ( pOverlapWindow )
+ {
+ vcl::Window* pNextOverlapWindow = pOverlapWindow->mpWindowImpl->mpNext;
+ if ( ImplIsRealParentPath( pOverlapWindow->ImplGetWindow() ) )
+ pOverlapWindow->ImplUpdateOverlapWindowPtr( bNewFrame );
+ pOverlapWindow = pNextOverlapWindow;
+ }
+
+ // update activate-status at next overlap window
+ if ( HasChildPathFocus( true ) )
+ ImplCallFocusChangeActivate( pNewOverlapWindow, pOldOverlapWindow );
+ }
+
+ // also convert Activate-Status
+ if ( bNewFrame )
+ {
+ if ( (GetType() == WindowType::BORDERWINDOW) &&
+ (ImplGetWindow()->GetType() == WindowType::FLOATINGWINDOW) )
+ static_cast<ImplBorderWindow*>(this)->SetDisplayActive( mpWindowImpl->mpFrameData->mbHasFocus );
+ }
+
+ // when required give focus to new frame if
+ // FocusWindow is changed with SetParent()
+ if ( bFocusOverlapWin )
+ {
+ mpWindowImpl->mpFrameData->mpFocusWin = Application::GetFocusWindow();
+ if ( !mpWindowImpl->mpFrameData->mbHasFocus )
+ {
+ mpWindowImpl->mpFrame->ToTop( SalFrameToTop::NONE );
+ }
+ }
+
+ // Assure DragSource and DropTarget members are created
+ if ( bNewFrame )
+ {
+ GetDropTarget();
+ }
+
+ if( bChangeTaskPaneList )
+ pNewSysWin->GetTaskPaneList()->AddWindow( this );
+
+ if( (GetStyle() & WB_OWNERDRAWDECORATION) && mpWindowImpl->mbFrame )
+ ImplGetOwnerDrawList().emplace_back(this );
+
+ if ( bVisible )
+ Show( true, ShowFlags::NoFocusChange | ShowFlags::NoActivate );
+}
+
+bool Window::IsAncestorOf( const vcl::Window& rWindow ) const
+{
+ return ImplIsRealParentPath(&rWindow);
+}
+
+sal_uInt16 Window::GetChildCount() const
+{
+ if (!mpWindowImpl)
+ return 0;
+
+ sal_uInt16 nChildCount = 0;
+ vcl::Window* pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ nChildCount++;
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+
+ return nChildCount;
+}
+
+vcl::Window* Window::GetChild( sal_uInt16 nChild ) const
+{
+ if (!mpWindowImpl)
+ return nullptr;
+
+ sal_uInt16 nChildCount = 0;
+ vcl::Window* pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ if ( nChild == nChildCount )
+ return pChild;
+ pChild = pChild->mpWindowImpl->mpNext;
+ nChildCount++;
+ }
+
+ return nullptr;
+}
+
+vcl::Window* Window::GetWindow( GetWindowType nType ) const
+{
+ if (!mpWindowImpl)
+ return nullptr;
+
+ switch ( nType )
+ {
+ case GetWindowType::Parent:
+ return mpWindowImpl->mpRealParent;
+
+ case GetWindowType::FirstChild:
+ return mpWindowImpl->mpFirstChild;
+
+ case GetWindowType::LastChild:
+ return mpWindowImpl->mpLastChild;
+
+ case GetWindowType::Prev:
+ return mpWindowImpl->mpPrev;
+
+ case GetWindowType::Next:
+ return mpWindowImpl->mpNext;
+
+ case GetWindowType::FirstOverlap:
+ return mpWindowImpl->mpFirstOverlap;
+
+ case GetWindowType::Overlap:
+ if ( ImplIsOverlapWindow() )
+ return const_cast<vcl::Window*>(this);
+ else
+ return mpWindowImpl->mpOverlapWindow;
+
+ case GetWindowType::ParentOverlap:
+ if ( ImplIsOverlapWindow() )
+ return mpWindowImpl->mpOverlapWindow;
+ else
+ return mpWindowImpl->mpOverlapWindow->mpWindowImpl->mpOverlapWindow;
+
+ case GetWindowType::Client:
+ return this->ImplGetWindow();
+
+ case GetWindowType::RealParent:
+ return ImplGetParent();
+
+ case GetWindowType::Frame:
+ return mpWindowImpl->mpFrameWindow;
+
+ case GetWindowType::Border:
+ if ( mpWindowImpl->mpBorderWindow )
+ return mpWindowImpl->mpBorderWindow->GetWindow( GetWindowType::Border );
+ return const_cast<vcl::Window*>(this);
+
+ case GetWindowType::FirstTopWindowChild:
+ return ImplGetWinData()->maTopWindowChildren.empty() ? nullptr : (*ImplGetWinData()->maTopWindowChildren.begin()).get();
+
+ case GetWindowType::NextTopWindowSibling:
+ {
+ if ( !mpWindowImpl->mpRealParent )
+ return nullptr;
+ const ::std::list< VclPtr<vcl::Window> >& rTopWindows( mpWindowImpl->mpRealParent->ImplGetWinData()->maTopWindowChildren );
+ ::std::list< VclPtr<vcl::Window> >::const_iterator myPos =
+ ::std::find( rTopWindows.begin(), rTopWindows.end(), this );
+ if ( ( myPos == rTopWindows.end() ) || ( ++myPos == rTopWindows.end() ) )
+ return nullptr;
+ return *myPos;
+ }
+
+ }
+
+ return nullptr;
+}
+
+bool Window::IsChild( const vcl::Window* pWindow ) const
+{
+ do
+ {
+ if ( pWindow->ImplIsOverlapWindow() )
+ break;
+
+ pWindow = pWindow->ImplGetParent();
+
+ if ( pWindow == this )
+ return true;
+ }
+ while ( pWindow );
+
+ return false;
+}
+
+bool Window::IsWindowOrChild( const vcl::Window* pWindow, bool bSystemWindow ) const
+{
+
+ if ( this == pWindow )
+ return true;
+ return ImplIsChild( pWindow, bSystemWindow );
+}
+
+void Window::ImplSetFrameParent( const vcl::Window* pParent )
+{
+ vcl::Window* pFrameWindow = ImplGetSVData()->maFrameData.mpFirstFrame;
+ while( pFrameWindow )
+ {
+ // search all frames that are children of this window
+ // and reparent them
+ if( ImplIsRealParentPath( pFrameWindow ) )
+ {
+ SAL_WARN_IF( mpWindowImpl->mpFrame == pFrameWindow->mpWindowImpl->mpFrame, "vcl", "SetFrameParent to own" );
+ SAL_WARN_IF( !mpWindowImpl->mpFrame, "vcl", "no frame" );
+ SalFrame* pParentFrame = pParent ? pParent->mpWindowImpl->mpFrame : nullptr;
+ pFrameWindow->mpWindowImpl->mpFrame->SetParent( pParentFrame );
+ }
+ pFrameWindow = pFrameWindow->mpWindowImpl->mpFrameData->mpNextFrame;
+ }
+}
+
+} /* namespace vcl */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/status.cxx b/vcl/source/window/status.cxx
new file mode 100644
index 0000000000..fc14762bef
--- /dev/null
+++ b/vcl/source/window/status.cxx
@@ -0,0 +1,1494 @@
+/* -*- 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/log.hxx>
+#include <comphelper/string.hxx>
+#include <vcl/event.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/glyphitemcache.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/help.hxx>
+#include <vcl/vcllayout.hxx>
+#include <vcl/status.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/settings.hxx>
+#include <config_features.h>
+#include <svdata.hxx>
+#include <window.h>
+
+#define STATUSBAR_OFFSET_X STATUSBAR_OFFSET
+#define STATUSBAR_OFFSET_Y 2
+#define STATUSBAR_OFFSET_TEXTY 3
+
+#define STATUSBAR_PRGS_OFFSET 3
+#define STATUSBAR_PRGS_COUNT 100
+#define STATUSBAR_PRGS_MIN 5
+
+#define STATUSBAR_MIN_HEIGHT 16 // icons height, tdf#153344
+
+class StatusBar::ImplData
+{
+public:
+ ImplData();
+
+ VclPtr<VirtualDevice> mpVirDev;
+};
+
+StatusBar::ImplData::ImplData()
+{
+ mpVirDev = nullptr;
+}
+
+struct ImplStatusItem
+{
+ sal_uInt16 mnId;
+ StatusBarItemBits mnBits;
+ tools::Long mnWidth;
+ tools::Long mnOffset;
+ tools::Long mnExtraWidth;
+ tools::Long mnX;
+ OUString maText;
+ OUString maHelpText;
+ OUString maQuickHelpText;
+ OUString maHelpId;
+ void* mpUserData;
+ bool mbVisible;
+ OUString maAccessibleName;
+ OUString maCommand;
+ std::optional<SalLayoutGlyphs> mLayoutGlyphsCache;
+ SalLayoutGlyphs* GetTextGlyphs(const OutputDevice* pOutputDevice);
+};
+
+SalLayoutGlyphs* ImplStatusItem::GetTextGlyphs(const OutputDevice* outputDevice)
+{
+ if(!mLayoutGlyphsCache.has_value())
+ {
+ std::unique_ptr<SalLayout> pSalLayout = outputDevice->ImplLayout(
+ maText, 0, -1, Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly);
+ mLayoutGlyphsCache = pSalLayout ? pSalLayout->GetGlyphs() : SalLayoutGlyphs();
+ }
+ return mLayoutGlyphsCache->IsValid() ? &mLayoutGlyphsCache.value() : nullptr;
+}
+
+static tools::Long ImplCalcProgressWidth( sal_uInt16 nMax, tools::Long nSize )
+{
+ return ((nMax*(nSize+(nSize/2)))-(nSize/2)+(STATUSBAR_PRGS_OFFSET*2));
+}
+
+static Point ImplGetItemTextPos( const Size& rRectSize, const Size& rTextSize,
+ StatusBarItemBits nStyle )
+{
+ tools::Long nX;
+ tools::Long nY;
+ tools::Long delta = (rTextSize.Height()/4) + 1;
+ if( delta + rTextSize.Width() > rRectSize.Width() )
+ delta = 0;
+
+ if ( nStyle & StatusBarItemBits::Left )
+ nX = delta;
+ else if ( nStyle & StatusBarItemBits::Right )
+ nX = rRectSize.Width()-rTextSize.Width()-delta;
+ else // StatusBarItemBits::Center
+ nX = (rRectSize.Width()-rTextSize.Width())/2;
+ nY = (rRectSize.Height()-rTextSize.Height())/2 + 1;
+ return Point( nX, nY );
+}
+
+bool StatusBar::ImplIsItemUpdate() const
+{
+ return !mbProgressMode && IsReallyVisible() && IsUpdateMode();
+}
+
+void StatusBar::ImplInit( vcl::Window* pParent, WinBits nStyle )
+{
+ mpImplData.reset(new ImplData);
+
+ // default: RightAlign
+ if ( !(nStyle & (WB_LEFT | WB_RIGHT)) )
+ nStyle |= WB_RIGHT;
+
+ Window::ImplInit( pParent, nStyle & ~WB_BORDER, nullptr );
+
+ // remember WinBits
+ mpImplData->mpVirDev = VclPtr<VirtualDevice>::Create( *GetOutDev() );
+ mnCurItemId = 0;
+ mbFormat = true;
+ mbProgressMode = false;
+ mbInUserDraw = false;
+ mbAdjustHiDPI = false;
+ mnItemsWidth = STATUSBAR_OFFSET_X;
+ mnDX = 0;
+ mnDY = 0;
+ mnCalcHeight = 0;
+ mnTextY = STATUSBAR_OFFSET_TEXTY;
+
+ ImplInitSettings();
+
+ SetOutputSizePixel( CalcWindowSizePixel() );
+}
+
+StatusBar::StatusBar( vcl::Window* pParent, WinBits nStyle ) :
+ Window( WindowType::STATUSBAR ),
+ mnLastProgressPaint_ms(osl_getGlobalTimer())
+{
+ ImplInit( pParent, nStyle );
+}
+
+StatusBar::~StatusBar()
+{
+ disposeOnce();
+}
+
+void StatusBar::dispose()
+{
+ // delete all items
+ mvItemList.clear();
+
+ // delete VirtualDevice
+ mpImplData->mpVirDev.disposeAndClear();
+ mpImplData.reset();
+ Window::dispose();
+}
+
+void StatusBar::AdjustItemWidthsForHiDPI()
+{
+ mbAdjustHiDPI = true;
+}
+
+void StatusBar::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ rRenderContext.SetLineColor();
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ ApplyControlFont(rRenderContext, rStyleSettings.GetToolFont());
+
+ Color aColor;
+ if (IsControlForeground())
+ aColor = GetControlForeground();
+ else if (GetStyle() & WB_3DLOOK)
+ aColor = rStyleSettings.GetButtonTextColor();
+ else
+ aColor = rStyleSettings.GetWindowTextColor();
+ rRenderContext.SetTextColor(aColor);
+
+ rRenderContext.SetTextFillColor();
+
+ if (IsControlBackground())
+ aColor = GetControlBackground();
+ else if (GetStyle() & WB_3DLOOK)
+ aColor = rStyleSettings.GetFaceColor();
+ else
+ aColor = rStyleSettings.GetWindowColor();
+ rRenderContext.SetBackground(aColor);
+
+ // NWF background
+ if (!IsControlBackground() &&
+ rRenderContext.IsNativeControlSupported(ControlType::WindowBackground, ControlPart::BackgroundWindow))
+ {
+ ImplGetWindowImpl()->mnNativeBackground = ControlPart::BackgroundWindow;
+ EnableChildTransparentMode();
+ }
+}
+
+void StatusBar::ImplInitSettings()
+{
+ ApplySettings(*GetOutDev());
+
+ mpImplData->mpVirDev->SetFont(GetFont());
+ mpImplData->mpVirDev->SetTextColor(GetTextColor());
+ mpImplData->mpVirDev->SetTextAlign(GetTextAlign());
+ mpImplData->mpVirDev->SetTextFillColor();
+ mpImplData->mpVirDev->SetBackground(GetBackground());
+}
+
+void StatusBar::ImplFormat()
+{
+ tools::Long nExtraWidth;
+ tools::Long nExtraWidth2;
+ tools::Long nX;
+ sal_uInt16 nAutoSizeItems;
+ bool bChanged;
+
+ do {
+ // sum up widths
+ nAutoSizeItems = 0;
+ mnItemsWidth = STATUSBAR_OFFSET_X;
+ bChanged = false;
+ tools::Long nOffset = 0;
+ for ( const auto & pItem : mvItemList ) {
+ if ( pItem->mbVisible )
+ {
+ if ( pItem->mnBits & StatusBarItemBits::AutoSize ) {
+ nAutoSizeItems++;
+ }
+
+ mnItemsWidth += pItem->mnWidth + nOffset;
+ nOffset = pItem->mnOffset;
+ }
+ }
+
+ if ( mnDX > 0 && mnDX < mnItemsWidth )
+ {
+ // Total width of items is more than available width
+ // Try to hide secondary elements, if any
+ for ( auto & pItem : mvItemList )
+ {
+ if ( pItem->mbVisible && !(pItem->mnBits & StatusBarItemBits::Mandatory) )
+ {
+ pItem->mbVisible = false;
+ bChanged = true;
+ break;
+ }
+ }
+ }
+ else if ( mnDX > mnItemsWidth )
+ {
+ // Width of statusbar is sufficient.
+ // Try to restore hidden items, if any
+ for ( auto & pItem : mvItemList )
+ {
+ if ( !pItem->mbVisible &&
+ !(pItem->mnBits & StatusBarItemBits::Mandatory) &&
+ pItem->mnWidth + nOffset + mnItemsWidth < mnDX )
+ {
+ pItem->mbVisible = true;
+ bChanged = true;
+ break;
+ }
+ }
+ }
+ } while ( bChanged );
+
+ if ( GetStyle() & WB_RIGHT )
+ {
+ // AutoSize isn't computed for right-alignment,
+ // because we show the text that is declared by SetText on the left side
+ nX = mnDX - mnItemsWidth;
+ nExtraWidth = 0;
+ nExtraWidth2 = 0;
+ }
+ else
+ {
+ mnItemsWidth += STATUSBAR_OFFSET_X;
+
+ // calling AutoSize is potentially necessary for left-aligned text,
+ if ( nAutoSizeItems && (mnDX > (mnItemsWidth - STATUSBAR_OFFSET)) )
+ {
+ nExtraWidth = (mnDX - mnItemsWidth - 1) / nAutoSizeItems;
+ nExtraWidth2 = (mnDX - mnItemsWidth - 1) % nAutoSizeItems;
+ }
+ else
+ {
+ nExtraWidth = 0;
+ nExtraWidth2 = 0;
+ }
+ nX = STATUSBAR_OFFSET_X;
+
+ if( GetOutDev()->HasMirroredGraphics() && IsRTLEnabled() )
+ nX += ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset;
+ }
+
+ for (auto & pItem : mvItemList) {
+ if ( pItem->mbVisible ) {
+ if ( pItem->mnBits & StatusBarItemBits::AutoSize ) {
+ pItem->mnExtraWidth = nExtraWidth;
+ if ( nExtraWidth2 ) {
+ pItem->mnExtraWidth++;
+ nExtraWidth2--;
+ }
+ } else {
+ pItem->mnExtraWidth = 0;
+ }
+
+ pItem->mnX = nX;
+ nX += pItem->mnWidth + pItem->mnExtraWidth + pItem->mnOffset;
+ }
+ }
+
+ mbFormat = false;
+}
+
+tools::Rectangle StatusBar::ImplGetItemRectPos( sal_uInt16 nPos ) const
+{
+ tools::Rectangle aRect;
+ ImplStatusItem* pItem = ( nPos < mvItemList.size() ) ? mvItemList[ nPos ].get() : nullptr;
+ if ( pItem && pItem->mbVisible )
+ {
+ aRect.SetLeft( pItem->mnX );
+ aRect.SetRight( aRect.Left() + pItem->mnWidth + pItem->mnExtraWidth );
+ aRect.SetTop( STATUSBAR_OFFSET_Y );
+ aRect.SetBottom( mnCalcHeight - STATUSBAR_OFFSET_Y );
+ }
+
+ return aRect;
+}
+
+sal_uInt16 StatusBar::ImplGetFirstVisiblePos() const
+{
+ for( size_t nPos = 0; nPos < mvItemList.size(); nPos++ )
+ {
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+ if ( pItem->mbVisible )
+ return sal_uInt16(nPos);
+ }
+
+ return SAL_MAX_UINT16;
+}
+
+void StatusBar::ImplDrawText(vcl::RenderContext& rRenderContext)
+{
+ // prevent item box from being overwritten
+ tools::Rectangle aTextRect;
+ aTextRect.SetLeft( STATUSBAR_OFFSET_X + 1 );
+ aTextRect.SetTop( mnTextY );
+ if (GetStyle() & WB_RIGHT)
+ aTextRect.SetRight( mnDX - mnItemsWidth - 1 );
+ else
+ aTextRect.SetRight( mnDX - 1 );
+ if (aTextRect.Right() > aTextRect.Left())
+ {
+ // compute position
+ OUString aStr = GetText();
+ sal_Int32 nPos = aStr.indexOf('\n');
+ if (nPos != -1)
+ aStr = aStr.copy(0, nPos);
+
+ aTextRect.SetBottom( aTextRect.Top()+GetTextHeight()+1 );
+
+ rRenderContext.DrawText(aTextRect, aStr, DrawTextFlags::Left | DrawTextFlags::Top | DrawTextFlags::Clip | DrawTextFlags::EndEllipsis);
+ }
+}
+
+void StatusBar::ImplDrawItem(vcl::RenderContext& rRenderContext, bool bOffScreen, sal_uInt16 nPos)
+{
+ tools::Rectangle aRect = ImplGetItemRectPos(nPos);
+
+ if (aRect.IsEmpty())
+ return;
+
+ // compute output region
+ ImplStatusItem* pItem = mvItemList[nPos].get();
+ tools::Long nW = 1;
+ tools::Rectangle aTextRect(aRect.Left() + nW, aRect.Top() + nW,
+ aRect.Right() - nW, aRect.Bottom() - nW);
+
+ Size aTextRectSize(aTextRect.GetSize());
+
+ if (bOffScreen)
+ {
+ mpImplData->mpVirDev->SetOutputSizePixel(aTextRectSize);
+ }
+ else
+ {
+ vcl::Region aRegion(aTextRect);
+ rRenderContext.SetClipRegion(aRegion);
+ }
+
+ // if the framework code is drawing status, let it do all the work
+ if (!(pItem->mnBits & StatusBarItemBits::UserDraw))
+ {
+ SalLayoutGlyphs* pGlyphs = pItem->GetTextGlyphs(&rRenderContext);
+ Size aTextSize(rRenderContext.GetTextWidth(pItem->maText,0,-1,nullptr,pGlyphs),
+ rRenderContext.GetTextHeight());
+ Point aTextPos = ImplGetItemTextPos(aTextRectSize, aTextSize, pItem->mnBits);
+
+ if (bOffScreen)
+ {
+ mpImplData->mpVirDev->DrawText(
+ aTextPos,
+ pItem->maText,
+ 0, -1, nullptr, nullptr,
+ pGlyphs );
+ }
+ else
+ {
+ aTextPos.AdjustX(aTextRect.Left() );
+ aTextPos.AdjustY(aTextRect.Top() );
+ rRenderContext.DrawText(
+ aTextPos,
+ pItem->maText,
+ 0, -1, nullptr, nullptr,
+ pGlyphs );
+ }
+ }
+
+ // call DrawItem if necessary
+ if (pItem->mnBits & StatusBarItemBits::UserDraw)
+ {
+ if (bOffScreen)
+ {
+ mbInUserDraw = true;
+ mpImplData->mpVirDev->EnableRTL( IsRTLEnabled() );
+ UserDrawEvent aODEvt(mpImplData->mpVirDev, tools::Rectangle(Point(), aTextRectSize), pItem->mnId);
+ UserDraw(aODEvt);
+ mpImplData->mpVirDev->EnableRTL(false);
+ mbInUserDraw = false;
+ }
+ else
+ {
+ UserDrawEvent aODEvt(&rRenderContext, aTextRect, pItem->mnId);
+ UserDraw(aODEvt);
+ }
+ }
+
+ if (bOffScreen)
+ rRenderContext.DrawOutDev(aTextRect.TopLeft(), aTextRectSize, Point(), aTextRectSize, *mpImplData->mpVirDev);
+ else
+ rRenderContext.SetClipRegion();
+
+ if (nPos != ImplGetFirstVisiblePos())
+ {
+ // draw separator
+ Point aFrom(aRect.TopLeft());
+ aFrom.AdjustX( -4 );
+ aFrom.AdjustY( 1 );
+ Point aTo(aRect.BottomLeft());
+ aTo.AdjustX( -4 );
+ aTo.AdjustY( -1 );
+
+ DecorationView aDecoView(&rRenderContext);
+ aDecoView.DrawSeparator(aFrom, aTo);
+ }
+
+ if (!rRenderContext.ImplIsRecordLayout())
+ CallEventListeners(VclEventId::StatusbarDrawItem, reinterpret_cast<void*>(pItem->mnId));
+}
+
+void DrawProgress(vcl::Window* pWindow, vcl::RenderContext& rRenderContext, const Point& rPos,
+ tools::Long nOffset, tools::Long nPrgsWidth, tools::Long nPrgsHeight,
+ sal_uInt16 nPercent1, sal_uInt16 nPercent2, sal_uInt16 nPercentCount,
+ const tools::Rectangle& rFramePosSize, ControlType eControlType)
+{
+ if (rRenderContext.IsNativeControlSupported(eControlType, ControlPart::Entire))
+ {
+ bool bNeedErase = ImplGetSVData()->maNWFData.mbProgressNeedsErase;
+
+ tools::Long nFullWidth = (nPrgsWidth + nOffset) * (10000 / nPercentCount);
+ tools::Long nPerc = std::min<sal_uInt16>(nPercent2, 10000);
+ ImplControlValue aValue(nFullWidth * nPerc / 10000);
+ tools::Rectangle aDrawRect(rPos, Size(nFullWidth, nPrgsHeight));
+ tools::Rectangle aControlRegion(aDrawRect);
+
+ if(bNeedErase)
+ {
+ vcl::Window* pEraseWindow = pWindow;
+ while (pEraseWindow->IsPaintTransparent() && !pEraseWindow->ImplGetWindowImpl()->mbFrame)
+ {
+ pEraseWindow = pEraseWindow->ImplGetWindowImpl()->mpParent;
+ }
+
+ if (pEraseWindow == pWindow)
+ {
+ // restore background of pWindow
+ rRenderContext.Erase(rFramePosSize);
+ }
+ else
+ {
+ // restore transparent background
+ AbsoluteScreenPixelPoint aTL1(pWindow->OutputToAbsoluteScreenPixel(rFramePosSize.TopLeft()));
+ Point aTL = pEraseWindow->AbsoluteScreenToOutputPixel(aTL1);
+ tools::Rectangle aRect(aTL, rFramePosSize.GetSize());
+ pEraseWindow->Invalidate(aRect, InvalidateFlags::NoChildren |
+ InvalidateFlags::NoClipChildren |
+ InvalidateFlags::Transparent);
+ pEraseWindow->PaintImmediately();
+ }
+ rRenderContext.Push(vcl::PushFlags::CLIPREGION);
+ rRenderContext.IntersectClipRegion(rFramePosSize);
+ }
+
+ bool bNativeOK = rRenderContext.DrawNativeControl(eControlType, ControlPart::Entire, aControlRegion,
+ ControlState::ENABLED, aValue, OUString());
+ if (bNeedErase)
+ rRenderContext.Pop();
+ if (bNativeOK)
+ return;
+ }
+
+ if (eControlType == ControlType::LevelBar)
+ {
+ if (nPercent2 < 2500)
+ rRenderContext.SetFillColor({ 0xFF0000 });
+ else if (nPercent2 < 5000)
+ rRenderContext.SetFillColor({ 0xFFFF00 });
+ else if (nPercent2 < 7500)
+ rRenderContext.SetFillColor({ 0x0000FF });
+ else
+ rRenderContext.SetFillColor({ 0x00FF00 });
+ }
+
+ // precompute values
+ sal_uInt16 nPerc1 = nPercent1 / nPercentCount;
+ sal_uInt16 nPerc2 = nPercent2 / nPercentCount;
+
+ if (nPerc1 > nPerc2)
+ {
+ // support progress that can also decrease
+
+ // compute rectangle
+ tools::Long nDX = nPrgsWidth + nOffset;
+ tools::Long nLeft = rPos.X() + ((nPerc1 - 1) * nDX);
+ tools::Rectangle aRect(nLeft, rPos.Y(), nLeft + nPrgsWidth, rPos.Y() + nPrgsHeight);
+
+ do
+ {
+ rRenderContext.Erase(aRect);
+ aRect.AdjustLeft( -nDX );
+ aRect.AdjustRight( -nDX );
+ nPerc1--;
+ }
+ while (nPerc1 > nPerc2);
+ }
+ else if (nPerc1 < nPerc2)
+ {
+ // draw Percent rectangle
+ // if Percent2 greater than 100%, adapt values
+ if (nPercent2 > 10000)
+ {
+ nPerc2 = 10000 / nPercentCount;
+ if (nPerc1 >= nPerc2)
+ nPerc1 = nPerc2 - 1;
+ }
+
+ // compute rectangle
+ tools::Long nDX = nPrgsWidth + nOffset;
+ tools::Long nLeft = rPos.X() + (nPerc1 * nDX);
+ tools::Rectangle aRect(nLeft, rPos.Y(), nLeft + nPrgsWidth, rPos.Y() + nPrgsHeight);
+
+ do
+ {
+ rRenderContext.DrawRect(aRect);
+ aRect.AdjustLeft(nDX );
+ aRect.AdjustRight(nDX );
+ nPerc1++;
+ }
+ while (nPerc1 < nPerc2);
+
+ // if greater than 100%, set rectangle to blink
+ if (nPercent2 > 10000 && eControlType == ControlType::Progress)
+ {
+ // define on/off status
+ if (((nPercent2 / nPercentCount) & 0x01) == (nPercentCount & 0x01))
+ {
+ aRect.AdjustLeft( -nDX );
+ aRect.AdjustRight( -nDX );
+ rRenderContext.Erase(aRect);
+ }
+ }
+ }
+}
+
+void StatusBar::ImplDrawProgress(vcl::RenderContext& rRenderContext, sal_uInt16 nPercent2)
+{
+ bool bNative = rRenderContext.IsNativeControlSupported(ControlType::Progress, ControlPart::Entire);
+ // bPaint: draw text also, else only update progress
+ rRenderContext.DrawText(maPrgsTxtPos, maPrgsTxt);
+ if (!bNative)
+ {
+ DecorationView aDecoView(&rRenderContext);
+ aDecoView.DrawFrame(maPrgsFrameRect, DrawFrameStyle::In);
+ }
+
+ Point aPos(maPrgsFrameRect.Left() + STATUSBAR_PRGS_OFFSET,
+ maPrgsFrameRect.Top() + STATUSBAR_PRGS_OFFSET);
+ tools::Long nPrgsHeight = mnPrgsSize;
+ if (bNative)
+ {
+ aPos = maPrgsFrameRect.TopLeft();
+ nPrgsHeight = maPrgsFrameRect.GetHeight();
+ }
+ DrawProgress(this, rRenderContext, aPos, mnPrgsSize / 2, mnPrgsSize, nPrgsHeight,
+ 0, nPercent2 * 100, mnPercentCount, maPrgsFrameRect, ControlType::Progress);
+}
+
+void StatusBar::ImplCalcProgressRect()
+{
+ // calculate text size
+ Size aPrgsTxtSize( GetTextWidth( maPrgsTxt ), GetTextHeight() );
+ maPrgsTxtPos.setX( STATUSBAR_OFFSET_X+1 );
+
+ // calculate progress frame
+ maPrgsFrameRect.SetLeft( maPrgsTxtPos.X()+aPrgsTxtSize.Width()+STATUSBAR_OFFSET );
+ maPrgsFrameRect.SetTop( STATUSBAR_OFFSET_Y );
+ maPrgsFrameRect.SetBottom( mnCalcHeight - STATUSBAR_OFFSET_Y );
+
+ // calculate size of progress rects
+ mnPrgsSize = maPrgsFrameRect.Bottom()-maPrgsFrameRect.Top()-(STATUSBAR_PRGS_OFFSET*2);
+ sal_uInt16 nMaxPercent = STATUSBAR_PRGS_COUNT;
+
+ tools::Long nMaxWidth = mnDX-STATUSBAR_OFFSET-1;
+
+ // make smaller if there are too many rects
+ while ( maPrgsFrameRect.Left()+ImplCalcProgressWidth( nMaxPercent, mnPrgsSize ) > nMaxWidth )
+ {
+ nMaxPercent--;
+ if ( nMaxPercent <= STATUSBAR_PRGS_MIN )
+ break;
+ }
+ maPrgsFrameRect.SetRight( maPrgsFrameRect.Left() + ImplCalcProgressWidth( nMaxPercent, mnPrgsSize ) );
+
+ // save the divisor for later
+ mnPercentCount = 10000 / nMaxPercent;
+ bool bNativeOK = false;
+ if( IsNativeControlSupported( ControlType::Progress, ControlPart::Entire ) )
+ {
+ ImplControlValue aValue;
+ tools::Rectangle aControlRegion( tools::Rectangle( Point(), maPrgsFrameRect.GetSize() ) );
+ tools::Rectangle aNativeControlRegion, aNativeContentRegion;
+ if( (bNativeOK = GetNativeControlRegion( ControlType::Progress, ControlPart::Entire, aControlRegion,
+ ControlState::ENABLED, aValue,
+ aNativeControlRegion, aNativeContentRegion ) ) )
+ {
+ tools::Long nProgressHeight = aNativeControlRegion.GetHeight();
+ if( nProgressHeight > maPrgsFrameRect.GetHeight() )
+ {
+ tools::Long nDelta = nProgressHeight - maPrgsFrameRect.GetHeight();
+ maPrgsFrameRect.AdjustTop( -(nDelta - nDelta/2) );
+ maPrgsFrameRect.AdjustBottom(nDelta/2 );
+ }
+ maPrgsTxtPos.setY( maPrgsFrameRect.Top() + (nProgressHeight - GetTextHeight())/2 );
+ }
+ }
+ if( ! bNativeOK )
+ maPrgsTxtPos.setY( mnTextY );
+}
+
+void StatusBar::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ // trigger toolbox only for left mouse button
+ if ( !rMEvt.IsLeft() )
+ return;
+
+ Point aMousePos = rMEvt.GetPosPixel();
+
+ // search for clicked item
+ for ( size_t i = 0; i < mvItemList.size(); ++i )
+ {
+ ImplStatusItem* pItem = mvItemList[ i ].get();
+ // check item for being clicked
+ if ( ImplGetItemRectPos( sal_uInt16(i) ).Contains( aMousePos ) )
+ {
+ mnCurItemId = pItem->mnId;
+ if ( rMEvt.GetClicks() == 2 )
+ DoubleClick();
+ else
+ Click();
+ mnCurItemId = 0;
+
+ // Item found
+ return;
+ }
+ }
+
+ // if there's no item, trigger Click or DoubleClick
+ if ( rMEvt.GetClicks() == 2 )
+ DoubleClick();
+ else
+ Click();
+}
+
+void StatusBar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ if (mbFormat)
+ ImplFormat();
+
+ sal_uInt16 nItemCount = sal_uInt16( mvItemList.size() );
+
+ if (mbProgressMode)
+ {
+ rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ Color aProgressColor = rStyleSettings.GetHighlightColor();
+ if (aProgressColor == rStyleSettings.GetFaceColor())
+ aProgressColor = rStyleSettings.GetDarkShadowColor();
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(aProgressColor);
+
+ ImplDrawProgress(rRenderContext, mnPercent);
+
+ rRenderContext.Pop();
+ }
+ else
+ {
+ // draw text
+ if (GetStyle() & WB_RIGHT)
+ ImplDrawText(rRenderContext);
+
+ // draw items
+
+ // Do offscreen only when we are not recording layout...
+ bool bOffscreen = !rRenderContext.ImplIsRecordLayout();
+
+ if (!bOffscreen)
+ rRenderContext.Erase(rRect);
+
+ for (sal_uInt16 i = 0; i < nItemCount; i++)
+ ImplDrawItem(rRenderContext, bOffscreen, i);
+ }
+
+ // draw line at the top of the status bar (to visually distinguish it from
+ // shell / docking area)
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.DrawLine(Point(0, 0), Point(mnDX-1, 0));
+}
+
+void StatusBar::Resize()
+{
+ // save width and height
+ Size aSize = GetOutputSizePixel();
+ mnDX = aSize.Width() - ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset;
+ mnDY = aSize.Height();
+ mnCalcHeight = mnDY;
+
+ mnTextY = (mnCalcHeight-GetTextHeight())/2;
+
+ // provoke re-formatting
+ mbFormat = true;
+
+ if ( mbProgressMode )
+ ImplCalcProgressRect();
+
+ Invalidate();
+}
+
+void StatusBar::RequestHelp( const HelpEvent& rHEvt )
+{
+ // no keyboard help in status bar
+ if( rHEvt.KeyboardActivated() )
+ return;
+
+ sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
+
+ if ( nItemId )
+ {
+ tools::Rectangle aItemRect = GetItemRect( nItemId );
+ Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
+ aItemRect.SetLeft( aPt.X() );
+ aItemRect.SetTop( aPt.Y() );
+ aPt = OutputToScreenPixel( aItemRect.BottomRight() );
+ aItemRect.SetRight( aPt.X() );
+ aItemRect.SetBottom( aPt.Y() );
+
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ {
+ OUString aStr = GetHelpText( nItemId );
+ Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aStr );
+ return;
+ }
+ else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
+ {
+ OUString aStr(GetQuickHelpText(nItemId));
+ // show quickhelp if available
+ if (!aStr.isEmpty())
+ {
+ Help::ShowQuickHelp( this, aItemRect, aStr );
+ return;
+ }
+ aStr = GetItemText( nItemId );
+ // show a quick help if item text doesn't fit
+ if ( GetTextWidth( aStr ) > aItemRect.GetWidth() )
+ {
+ Help::ShowQuickHelp( this, aItemRect, aStr );
+ return;
+ }
+ }
+ }
+
+ Window::RequestHelp( rHEvt );
+}
+
+void StatusBar::StateChanged( StateChangedType nType )
+{
+ Window::StateChanged( nType );
+
+ if ( nType == StateChangedType::InitShow )
+ ImplFormat();
+ else if ( nType == StateChangedType::UpdateMode )
+ Invalidate();
+ else if ( (nType == StateChangedType::Zoom) ||
+ (nType == StateChangedType::ControlFont) )
+ {
+ mbFormat = true;
+ ImplInitSettings();
+ Invalidate();
+ }
+ else if ( nType == StateChangedType::ControlForeground )
+ {
+ ImplInitSettings();
+ Invalidate();
+ }
+ else if ( nType == StateChangedType::ControlBackground )
+ {
+ ImplInitSettings();
+ Invalidate();
+ }
+
+ //invalidate layout cache
+ for (auto & pItem : mvItemList)
+ {
+ pItem->mLayoutGlyphsCache.reset();
+ }
+
+}
+
+void StatusBar::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ if ( !((rDCEvt.GetType() == DataChangedEventType::DISPLAY )
+ || (rDCEvt.GetType() == DataChangedEventType::FONTS )
+ || (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION)
+ || ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS)
+ && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE )
+ ))
+ )
+ return;
+
+ mbFormat = true;
+ ImplInitSettings();
+ tools::Long nFudge = GetTextHeight() / 4;
+ for (auto & pItem : mvItemList)
+ {
+ tools::Long nWidth = GetTextWidth( pItem->maText ) + nFudge;
+ if( nWidth > pItem->mnWidth + STATUSBAR_OFFSET )
+ pItem->mnWidth = nWidth + STATUSBAR_OFFSET;
+
+ pItem->mLayoutGlyphsCache.reset();
+ }
+ Size aSize = GetSizePixel();
+ // do not disturb current width, since
+ // CalcWindowSizePixel calculates a minimum width
+ aSize.setHeight( CalcWindowSizePixel().Height() );
+ SetSizePixel( aSize );
+ Invalidate();
+}
+
+void StatusBar::Click()
+{
+ maClickHdl.Call( this );
+}
+
+void StatusBar::DoubleClick()
+{
+ maDoubleClickHdl.Call( this );
+}
+
+void StatusBar::UserDraw( const UserDrawEvent& )
+{
+}
+
+void StatusBar::InsertItem( sal_uInt16 nItemId, sal_uLong nWidth,
+ StatusBarItemBits nBits,
+ tools::Long nOffset, sal_uInt16 nPos )
+{
+ SAL_WARN_IF( !nItemId, "vcl", "StatusBar::InsertItem(): ItemId == 0" );
+ SAL_WARN_IF( GetItemPos( nItemId ) != STATUSBAR_ITEM_NOTFOUND, "vcl",
+ "StatusBar::InsertItem(): ItemId already exists" );
+
+ // default: IN and CENTER
+ if ( !(nBits & (StatusBarItemBits::In | StatusBarItemBits::Out | StatusBarItemBits::Flat)) )
+ nBits |= StatusBarItemBits::In;
+ if ( !(nBits & (StatusBarItemBits::Left | StatusBarItemBits::Right | StatusBarItemBits::Center)) )
+ nBits |= StatusBarItemBits::Center;
+
+ // create item
+ if (mbAdjustHiDPI)
+ {
+ nWidth *= GetDPIScaleFactor();
+ }
+ tools::Long nFudge = GetTextHeight()/4;
+ std::unique_ptr<ImplStatusItem> pItem(new ImplStatusItem);
+ pItem->mnId = nItemId;
+ pItem->mnBits = nBits;
+ pItem->mnWidth = static_cast<tools::Long>(nWidth)+nFudge+STATUSBAR_OFFSET;
+ pItem->mnOffset = nOffset;
+ pItem->mpUserData = nullptr;
+ pItem->mbVisible = true;
+
+ // add item to list
+ if ( nPos < mvItemList.size() ) {
+ mvItemList.insert( mvItemList.begin() + nPos, std::move(pItem) );
+ } else {
+ mvItemList.push_back( std::move(pItem) );
+ }
+
+ mbFormat = true;
+ if ( ImplIsItemUpdate() )
+ Invalidate();
+
+ CallEventListeners( VclEventId::StatusbarItemAdded, reinterpret_cast<void*>(nItemId) );
+}
+
+void StatusBar::RemoveItem( sal_uInt16 nItemId )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ {
+ mvItemList.erase( mvItemList.begin() + nPos );
+
+ mbFormat = true;
+ if ( ImplIsItemUpdate() )
+ Invalidate();
+
+ CallEventListeners( VclEventId::StatusbarItemRemoved, reinterpret_cast<void*>(nItemId) );
+ }
+}
+
+void StatusBar::ShowItem( sal_uInt16 nItemId )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos == STATUSBAR_ITEM_NOTFOUND )
+ return;
+
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+ if ( !pItem->mbVisible )
+ {
+ pItem->mbVisible = true;
+
+ mbFormat = true;
+ if ( ImplIsItemUpdate() )
+ Invalidate();
+
+ CallEventListeners( VclEventId::StatusbarShowItem, reinterpret_cast<void*>(nItemId) );
+ }
+}
+
+void StatusBar::HideItem( sal_uInt16 nItemId )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos == STATUSBAR_ITEM_NOTFOUND )
+ return;
+
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+ if ( pItem->mbVisible )
+ {
+ pItem->mbVisible = false;
+
+ mbFormat = true;
+ if ( ImplIsItemUpdate() )
+ Invalidate();
+
+ CallEventListeners( VclEventId::StatusbarHideItem, reinterpret_cast<void*>(nItemId) );
+ }
+}
+
+bool StatusBar::IsItemVisible( sal_uInt16 nItemId ) const
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ return mvItemList[ nPos ]->mbVisible;
+ else
+ return false;
+}
+
+void StatusBar::Clear()
+{
+ // delete all items
+ mvItemList.clear();
+
+ mbFormat = true;
+ if ( ImplIsItemUpdate() )
+ Invalidate();
+
+ CallEventListeners( VclEventId::StatusbarAllItemsRemoved );
+}
+
+sal_uInt16 StatusBar::GetItemCount() const
+{
+ return static_cast<sal_uInt16>(mvItemList.size());
+}
+
+sal_uInt16 StatusBar::GetItemId( sal_uInt16 nPos ) const
+{
+ if ( nPos < mvItemList.size() )
+ return mvItemList[ nPos ]->mnId;
+ return 0;
+}
+
+sal_uInt16 StatusBar::GetItemPos( sal_uInt16 nItemId ) const
+{
+ for ( size_t i = 0, n = mvItemList.size(); i < n; ++i ) {
+ if ( mvItemList[ i ]->mnId == nItemId ) {
+ return sal_uInt16( i );
+ }
+ }
+
+ return STATUSBAR_ITEM_NOTFOUND;
+}
+
+sal_uInt16 StatusBar::GetItemId( const Point& rPos ) const
+{
+ if ( !mbFormat )
+ {
+ sal_uInt16 nItemCount = GetItemCount();
+ sal_uInt16 nPos;
+ for ( nPos = 0; nPos < nItemCount; nPos++ )
+ {
+ // get rectangle
+ tools::Rectangle aRect = ImplGetItemRectPos( nPos );
+ if ( aRect.Contains( rPos ) )
+ return mvItemList[ nPos ]->mnId;
+ }
+ }
+
+ return 0;
+}
+
+tools::Rectangle StatusBar::GetItemRect( sal_uInt16 nItemId ) const
+{
+ tools::Rectangle aRect;
+
+ if ( !mbFormat )
+ {
+ sal_uInt16 nPos = GetItemPos( nItemId );
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ {
+ // get rectangle and subtract frame
+ aRect = ImplGetItemRectPos( nPos );
+ tools::Long nW = 1;
+ aRect.AdjustTop(nW-1 );
+ aRect.AdjustBottom( -(nW-1) );
+ aRect.AdjustLeft(nW );
+ aRect.AdjustRight( -nW );
+ return aRect;
+ }
+ }
+
+ return aRect;
+}
+
+Point StatusBar::GetItemTextPos( sal_uInt16 nItemId ) const
+{
+ if ( !mbFormat )
+ {
+ sal_uInt16 nPos = GetItemPos( nItemId );
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ {
+ // get rectangle
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+ tools::Rectangle aRect = ImplGetItemRectPos( nPos );
+ tools::Long nW = 1;
+ tools::Rectangle aTextRect( aRect.Left()+nW, aRect.Top()+nW,
+ aRect.Right()-nW, aRect.Bottom()-nW );
+ Point aPos = ImplGetItemTextPos( aTextRect.GetSize(),
+ Size( GetTextWidth( pItem->maText ), GetTextHeight() ),
+ pItem->mnBits );
+ if ( !mbInUserDraw )
+ {
+ aPos.AdjustX(aTextRect.Left() );
+ aPos.AdjustY(aTextRect.Top() );
+ }
+ return aPos;
+ }
+ }
+
+ return Point();
+}
+
+sal_uLong StatusBar::GetItemWidth( sal_uInt16 nItemId ) const
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ return mvItemList[ nPos ]->mnWidth;
+
+ return 0;
+}
+
+StatusBarItemBits StatusBar::GetItemBits( sal_uInt16 nItemId ) const
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ return mvItemList[ nPos ]->mnBits;
+
+ return StatusBarItemBits::NONE;
+}
+
+tools::Long StatusBar::GetItemOffset( sal_uInt16 nItemId ) const
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ return mvItemList[ nPos ]->mnOffset;
+
+ return 0;
+}
+
+void StatusBar::PaintSelfAndChildrenImmediately()
+{
+ WindowImpl* pWindowImpl = ImplGetWindowImpl();
+ const bool bOrigOverlapWin = pWindowImpl->mbOverlapWin;
+ // Temporarily set mbOverlapWin so that any parent windows of StatusBar
+ // that happen to have accumulated Invalidates are not taken as the root
+ // paint candidate from which to paint the paint hierarchy. So we limit the
+ // paint here to this statusbar and its children, disabling the
+ // optimization to bundle pending paints together and suppressing any
+ // unexpected side effects of entering parent window paint handlers if this
+ // call is not from the primordial thread.
+ pWindowImpl->mbOverlapWin = true;
+ PaintImmediately();
+ pWindowImpl->mbOverlapWin = bOrigOverlapWin;
+}
+
+void StatusBar::SetItemText( sal_uInt16 nItemId, const OUString& rText, int nCharsWidth )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos == STATUSBAR_ITEM_NOTFOUND )
+ return;
+
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+
+ if ( pItem->maText == rText )
+ return;
+
+ pItem->maText = rText;
+
+ // adjust item width - see also DataChanged()
+ tools::Long nFudge = GetTextHeight()/4;
+
+ tools::Long nWidth;
+ if (nCharsWidth != -1)
+ {
+ nWidth = GetTextWidth("0",0,-1,nullptr,
+ SalLayoutGlyphsCache::self()->GetLayoutGlyphs(GetOutDev(),"0"));
+ nWidth = nWidth * nCharsWidth + nFudge;
+ }
+ else
+ {
+ pItem->mLayoutGlyphsCache.reset();
+ nWidth = GetTextWidth( pItem->maText,0,-1,nullptr, pItem->GetTextGlyphs(GetOutDev())) + nFudge;
+ }
+
+ if( (nWidth > pItem->mnWidth + STATUSBAR_OFFSET) ||
+ ((nWidth < pItem->mnWidth) && (mnDX - STATUSBAR_OFFSET) < mnItemsWidth ))
+ {
+ pItem->mnWidth = nWidth + STATUSBAR_OFFSET;
+ ImplFormat();
+ Invalidate();
+ }
+
+ // re-draw item if StatusBar is visible and UpdateMode active
+ if ( pItem->mbVisible && !mbFormat && ImplIsItemUpdate() )
+ {
+ tools::Rectangle aRect = ImplGetItemRectPos(nPos);
+ Invalidate(aRect);
+ PaintSelfAndChildrenImmediately();
+ }
+}
+
+const OUString& StatusBar::GetItemText( sal_uInt16 nItemId ) const
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ assert( nPos != STATUSBAR_ITEM_NOTFOUND );
+
+ return mvItemList[ nPos ]->maText;
+}
+
+void StatusBar::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ {
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+
+ if ( pItem->maCommand != rCommand )
+ pItem->maCommand = rCommand;
+ }
+}
+
+OUString StatusBar::GetItemCommand( sal_uInt16 nItemId )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ return mvItemList[ nPos ]->maCommand;
+
+ return OUString();
+}
+
+void StatusBar::SetItemData( sal_uInt16 nItemId, void* pNewData )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos == STATUSBAR_ITEM_NOTFOUND )
+ return;
+
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+ // invalidate cache
+ pItem->mLayoutGlyphsCache.reset();
+ pItem->mpUserData = pNewData;
+
+ // call Draw-Item if it's a User-Item
+ if ( (pItem->mnBits & StatusBarItemBits::UserDraw) && pItem->mbVisible &&
+ !mbFormat && ImplIsItemUpdate() )
+ {
+ tools::Rectangle aRect = ImplGetItemRectPos(nPos);
+ Invalidate(aRect, InvalidateFlags::NoErase);
+ PaintSelfAndChildrenImmediately();
+ }
+}
+
+void* StatusBar::GetItemData( sal_uInt16 nItemId ) const
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ return mvItemList[ nPos ]->mpUserData;
+
+ return nullptr;
+}
+
+void StatusBar::RedrawItem(sal_uInt16 nItemId)
+{
+ if ( mbFormat )
+ return;
+
+ sal_uInt16 nPos = GetItemPos(nItemId);
+ if ( nPos == STATUSBAR_ITEM_NOTFOUND )
+ return;
+
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+ if ((pItem->mnBits & StatusBarItemBits::UserDraw) &&
+ pItem->mbVisible && ImplIsItemUpdate())
+ {
+ tools::Rectangle aRect = ImplGetItemRectPos(nPos);
+ Invalidate(aRect);
+ PaintSelfAndChildrenImmediately();
+ }
+}
+
+void StatusBar::SetHelpText( sal_uInt16 nItemId, const OUString& rText )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ mvItemList[ nPos ]->maHelpText = rText;
+}
+
+const OUString& StatusBar::GetHelpText( sal_uInt16 nItemId ) const
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
+
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+ if ( pItem->maHelpText.isEmpty() && ( !pItem->maHelpId.isEmpty() || !pItem->maCommand.isEmpty() ))
+ {
+ Help* pHelp = Application::GetHelp();
+ if ( pHelp )
+ {
+ if ( !pItem->maCommand.isEmpty() )
+ pItem->maHelpText = pHelp->GetHelpText( pItem->maCommand, this );
+ if ( pItem->maHelpText.isEmpty() && !pItem->maHelpId.isEmpty() )
+ pItem->maHelpText = pHelp->GetHelpText( pItem->maHelpId, this );
+ }
+ }
+
+ return pItem->maHelpText;
+}
+
+void StatusBar::SetQuickHelpText( sal_uInt16 nItemId, const OUString& rText )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ mvItemList[ nPos ]->maQuickHelpText = rText;
+}
+
+const OUString& StatusBar::GetQuickHelpText( sal_uInt16 nItemId ) const
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
+
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+ return pItem->maQuickHelpText;
+}
+
+void StatusBar::SetHelpId( sal_uInt16 nItemId, const OUString& rHelpId )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ mvItemList[ nPos ]->maHelpId = rHelpId;
+}
+
+void StatusBar::StartProgressMode( const OUString& rText )
+{
+ SAL_WARN_IF( mbProgressMode, "vcl", "StatusBar::StartProgressMode(): progress mode is active" );
+
+ mbProgressMode = true;
+ mnPercent = 0;
+ maPrgsTxt = rText;
+
+ // compute size
+ ImplCalcProgressRect();
+
+ // trigger Paint, which draws text and frame
+ if ( IsReallyVisible() )
+ {
+ Invalidate();
+ PaintSelfAndChildrenImmediately();
+ }
+}
+
+void StatusBar::SetProgressValue( sal_uInt16 nNewPercent )
+{
+ SAL_WARN_IF( !mbProgressMode, "vcl", "StatusBar::SetProgressValue(): no progress mode" );
+ SAL_WARN_IF( nNewPercent > 100, "vcl", "StatusBar::SetProgressValue(): nPercent > 100" );
+
+ bool bInvalidate = mbProgressMode && IsReallyVisible() && (!mnPercent || (mnPercent != nNewPercent));
+
+ mnPercent = nNewPercent;
+
+ if (bInvalidate)
+ {
+ // Rate limit how often we paint, otherwise in some loading scenarios we can spend significant
+ // time just painting progress bars.
+ sal_uInt32 nTime_ms = osl_getGlobalTimer();
+ if ((nTime_ms - mnLastProgressPaint_ms) > 100)
+ {
+ Invalidate(maPrgsFrameRect);
+ PaintSelfAndChildrenImmediately();
+ mnLastProgressPaint_ms = nTime_ms;
+ }
+ }
+}
+
+void StatusBar::EndProgressMode()
+{
+ SAL_WARN_IF( !mbProgressMode, "vcl", "StatusBar::EndProgressMode(): no progress mode" );
+
+ mbProgressMode = false;
+ maPrgsTxt.clear();
+
+ if ( IsReallyVisible() )
+ {
+ Invalidate();
+ PaintSelfAndChildrenImmediately();
+ }
+}
+
+void StatusBar::SetText(const OUString& rText)
+{
+ if ((GetStyle() & WB_RIGHT) && !mbProgressMode && IsReallyVisible() && IsUpdateMode())
+ {
+ if (mbFormat)
+ {
+ Invalidate();
+ Window::SetText(rText);
+ }
+ else
+ {
+ Invalidate();
+ Window::SetText(rText);
+ PaintSelfAndChildrenImmediately();
+ }
+ }
+ else if (mbProgressMode)
+ {
+ maPrgsTxt = rText;
+ if (IsReallyVisible())
+ {
+ Invalidate();
+ PaintSelfAndChildrenImmediately();
+ }
+ }
+ else
+ {
+ Window::SetText(rText);
+ }
+}
+
+Size StatusBar::CalcWindowSizePixel() const
+{
+ size_t i = 0;
+ size_t nCount = mvItemList.size();
+ tools::Long nOffset = 0;
+ tools::Long nCalcWidth = STATUSBAR_OFFSET_X*2;
+ tools::Long nCalcHeight;
+
+ while ( i < nCount )
+ {
+ ImplStatusItem* pItem = mvItemList[ i ].get();
+ nCalcWidth += pItem->mnWidth + nOffset;
+ nOffset = pItem->mnOffset;
+ i++;
+ }
+
+ tools::Long nMinHeight = std::max( static_cast<int>(GetTextHeight()), STATUSBAR_MIN_HEIGHT);
+ const tools::Long nBarTextOffset = STATUSBAR_OFFSET_TEXTY*2;
+ tools::Long nProgressHeight = nMinHeight + nBarTextOffset;
+
+ if( IsNativeControlSupported( ControlType::Progress, ControlPart::Entire ) )
+ {
+ ImplControlValue aValue;
+ tools::Rectangle aControlRegion( Point(), Size( nCalcWidth, nMinHeight ) );
+ tools::Rectangle aNativeControlRegion, aNativeContentRegion;
+ if( GetNativeControlRegion( ControlType::Progress, ControlPart::Entire,
+ aControlRegion, ControlState::ENABLED, aValue,
+ aNativeControlRegion, aNativeContentRegion ) )
+ {
+ nProgressHeight = aNativeControlRegion.GetHeight();
+ }
+ }
+
+ nCalcHeight = nMinHeight+nBarTextOffset;
+ if( nCalcHeight < nProgressHeight+2 )
+ nCalcHeight = nProgressHeight+2;
+
+ return Size( nCalcWidth, nCalcHeight );
+}
+
+void StatusBar::SetAccessibleName( sal_uInt16 nItemId, const OUString& rName )
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ if ( nPos != STATUSBAR_ITEM_NOTFOUND )
+ {
+ ImplStatusItem* pItem = mvItemList[ nPos ].get();
+
+ if ( pItem->maAccessibleName != rName )
+ {
+ pItem->maAccessibleName = rName;
+ CallEventListeners( VclEventId::StatusbarNameChanged, reinterpret_cast<void*>(pItem->mnId) );
+ }
+ }
+}
+
+const OUString& StatusBar::GetAccessibleName( sal_uInt16 nItemId ) const
+{
+ sal_uInt16 nPos = GetItemPos( nItemId );
+
+ assert ( nPos != STATUSBAR_ITEM_NOTFOUND );
+
+ return mvItemList[ nPos ]->maAccessibleName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/syschild.cxx b/vcl/source/window/syschild.cxx
new file mode 100644
index 0000000000..644aa1f095
--- /dev/null
+++ b/vcl/source/window/syschild.cxx
@@ -0,0 +1,200 @@
+/* -*- 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 <vcl/window.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syschild.hxx>
+
+#include <window.h>
+#include <salframe.hxx>
+#include <salinst.hxx>
+#include <salobj.hxx>
+#include <svdata.hxx>
+
+using namespace ::com::sun::star;
+
+static void ImplSysChildProc( SystemChildWindow* pInst, SalObjEvent nEvent )
+{
+ VclPtr<SystemChildWindow> pWindow = pInst;
+
+ switch ( nEvent )
+ {
+ case SalObjEvent::GetFocus:
+ // get focus, such that all handlers are called,
+ // as if this window gets the focus assuring
+ // that the frame does not steal it
+ pWindow->ImplGetFrameData()->mbSysObjFocus = true;
+ pWindow->ImplGetFrameData()->mbInSysObjToTopHdl = true;
+ pWindow->ToTop( ToTopFlags::NoGrabFocus );
+ if( pWindow->isDisposed() )
+ break;
+ pWindow->ImplGetFrameData()->mbInSysObjToTopHdl = false;
+ pWindow->ImplGetFrameData()->mbInSysObjFocusHdl = true;
+ pWindow->GrabFocus();
+ if( pWindow->isDisposed() )
+ break;
+ pWindow->ImplGetFrameData()->mbInSysObjFocusHdl = false;
+ break;
+
+ case SalObjEvent::LoseFocus:
+ // trigger a LoseFocus which matches the status
+ // of the window with matching Activate-Status
+ if (pWindow->isDisposed())
+ break;
+ pWindow->ImplGetFrameData()->mbSysObjFocus = false;
+ if ( !pWindow->ImplGetFrameData()->mnFocusId )
+ {
+ pWindow->ImplGetFrameData()->mbStartFocusState = true;
+ pWindow->ImplGetFrameData()->mnFocusId = Application::PostUserEvent( LINK( pWindow->ImplGetFrameWindow(), vcl::Window, ImplAsyncFocusHdl ), nullptr, true );
+ }
+ break;
+
+ case SalObjEvent::ToTop:
+ pWindow->ImplGetFrameData()->mbInSysObjToTopHdl = true;
+ if ( !Application::GetFocusWindow() || pWindow->HasChildPathFocus() )
+ pWindow->ToTop( ToTopFlags::NoGrabFocus );
+ else
+ pWindow->ToTop();
+ if( pWindow->isDisposed() )
+ break;
+ pWindow->GrabFocus();
+ if( pWindow->isDisposed() )
+ break;
+ pWindow->ImplGetFrameData()->mbInSysObjToTopHdl = false;
+ break;
+
+ default: break;
+ }
+}
+
+void SystemChildWindow::ImplInitSysChild( vcl::Window* pParent, WinBits nStyle, SystemWindowData *pData, bool bShow )
+{
+ mpWindowImpl->mpSysObj = ImplGetSVData()->mpDefInst->CreateObject( pParent->ImplGetFrame(), pData, bShow );
+
+ Window::ImplInit( pParent, nStyle, nullptr );
+
+ // we do not paint if it is the right SysChild
+ if ( GetSystemData() )
+ {
+ mpWindowImpl->mpSysObj->SetCallback( this, ImplSysChildProc );
+ SetParentClipMode( ParentClipMode::Clip );
+ SetBackground();
+ }
+}
+
+SystemChildWindow::SystemChildWindow( vcl::Window* pParent, WinBits nStyle ) :
+ Window( WindowType::SYSTEMCHILDWINDOW )
+{
+ ImplInitSysChild( pParent, nStyle, nullptr );
+}
+
+SystemChildWindow::SystemChildWindow( vcl::Window* pParent, WinBits nStyle, SystemWindowData *pData, bool bShow ) :
+ Window( WindowType::SYSTEMCHILDWINDOW )
+{
+ ImplInitSysChild( pParent, nStyle, pData, bShow );
+}
+
+SystemChildWindow::~SystemChildWindow()
+{
+ disposeOnce();
+}
+
+void SystemChildWindow::dispose()
+{
+ Hide();
+ if ( mpWindowImpl && mpWindowImpl->mpSysObj )
+ {
+ ImplGetSVData()->mpDefInst->DestroyObject( mpWindowImpl->mpSysObj );
+ mpWindowImpl->mpSysObj = nullptr;
+ }
+ Window::dispose();
+}
+
+const SystemEnvData* SystemChildWindow::GetSystemData() const
+{
+ if ( mpWindowImpl->mpSysObj )
+ return mpWindowImpl->mpSysObj->GetSystemData();
+ else
+ return nullptr;
+}
+
+void SystemChildWindow::EnableEraseBackground( bool bEnable )
+{
+ if ( mpWindowImpl->mpSysObj )
+ mpWindowImpl->mpSysObj->EnableEraseBackground( bEnable );
+}
+
+Size SystemChildWindow::GetOptimalSize() const
+{
+ if (mpWindowImpl->mpSysObj)
+ return mpWindowImpl->mpSysObj->GetOptimalSize();
+ return vcl::Window::GetOptimalSize();
+}
+
+void SystemChildWindow::SetLeaveEnterBackgrounds(const css::uno::Sequence<css::uno::Any>& rLeaveArgs, const css::uno::Sequence<css::uno::Any>& rEnterArgs)
+{
+ if (mpWindowImpl->mpSysObj)
+ mpWindowImpl->mpSysObj->SetLeaveEnterBackgrounds(rLeaveArgs, rEnterArgs);
+}
+
+void SystemChildWindow::SetForwardKey( bool bEnable )
+{
+ if ( mpWindowImpl->mpSysObj )
+ mpWindowImpl->mpSysObj->SetForwardKey( bEnable );
+}
+
+sal_IntPtr SystemChildWindow::GetParentWindowHandle() const
+{
+ sal_IntPtr nRet = 0;
+
+#if defined(_WIN32)
+ nRet = reinterpret_cast< sal_IntPtr >( GetSystemData()->hWnd );
+#elif defined MACOSX
+ // FIXME: this is wrong
+ nRet = reinterpret_cast< sal_IntPtr >( GetSystemData()->mpNSView );
+#elif defined ANDROID
+ // Nothing
+#elif defined IOS
+ // Nothing
+#elif defined UNX
+ nRet = GetSystemData()->GetWindowHandle(ImplGetFrame());
+#endif
+
+ return nRet;
+}
+
+void* SystemChildWindow::CreateGStreamerSink()
+{
+ return ImplGetSVData()->mpDefInst->CreateGStreamerSink(this);
+}
+
+#if defined( MACOSX )
+#elif defined( ANDROID )
+#elif defined( IOS )
+#elif defined( UNX )
+sal_uIntPtr SystemEnvData::GetWindowHandle(const SalFrame* pReference) const
+{
+ if (!aWindow && pReference)
+ pReference->ResolveWindowHandle(const_cast<SystemEnvData&>(*this));
+ return aWindow;
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/syswin.cxx b/vcl/source/window/syswin.cxx
new file mode 100644
index 0000000000..6584c1e3b0
--- /dev/null
+++ b/vcl/source/window/syswin.cxx
@@ -0,0 +1,1171 @@
+/* -*- 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 <o3tl/safeint.hxx>
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <vcl/layout.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/event.hxx>
+#include <vcl/syswin.hxx>
+#include <vcl/taskpanelist.hxx>
+#include <vcl/tabctrl.hxx>
+#include <vcl/tabpage.hxx>
+#include <vcl/virdev.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <accel.hxx>
+#include <salframe.hxx>
+#include <svdata.hxx>
+#include <brdwin.hxx>
+#include <window.h>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+class SystemWindow::ImplData
+{
+public:
+ ImplData();
+
+ std::unique_ptr<TaskPaneList>
+ mpTaskPaneList;
+ Size maMaxOutSize;
+ OUString maRepresentedURL;
+ Link<SystemWindow&,void> maCloseHdl;
+};
+
+SystemWindow::ImplData::ImplData()
+{
+ mpTaskPaneList = nullptr;
+ maMaxOutSize = Size( SHRT_MAX, SHRT_MAX );
+}
+
+SystemWindow::SystemWindow(WindowType nType, const char* pIdleDebugName)
+ : Window(nType)
+ , mbDockBtn(false)
+ , mbHideBtn(false)
+ , mbSysChild(false)
+ , mbIsCalculatingInitialLayoutSize(false)
+ , mbInitialLayoutSizeCalculated(false)
+ , mbPaintComplete(false)
+ , mnMenuBarMode(MenuBarMode::Normal)
+ , mnIcon(0)
+ , mpImplData(new ImplData)
+ , maLayoutIdle( pIdleDebugName )
+ , mbIsDeferredInit(false)
+{
+ mpWindowImpl->mbSysWin = true;
+ mpWindowImpl->mnActivateMode = ActivateModeFlags::GrabFocus;
+
+ //To-Do, reuse maResizeTimer
+ maLayoutIdle.SetPriority(TaskPriority::RESIZE);
+ maLayoutIdle.SetInvokeHandler( LINK( this, SystemWindow, ImplHandleLayoutTimerHdl ) );
+}
+
+void SystemWindow::loadUI(vcl::Window* pParent, const OUString& rID, const OUString& rUIXMLDescription,
+ const css::uno::Reference<css::frame::XFrame> &rFrame)
+{
+ mbIsDeferredInit = true;
+ mpDialogParent = pParent; //should be unset in doDeferredInit
+ m_pUIBuilder.reset( new VclBuilder(this, AllSettings::GetUIRootDir(), rUIXMLDescription, rID, rFrame) );
+}
+
+SystemWindow::~SystemWindow()
+{
+ disposeOnce();
+}
+
+void SystemWindow::dispose()
+{
+ maLayoutIdle.Stop();
+ mpImplData.reset();
+
+ // Hack to make sure code called from base ~Window does not interpret this
+ // as a SystemWindow (which it no longer is by then):
+ mpWindowImpl->mbSysWin = false;
+ disposeBuilder();
+ mpDialogParent.clear();
+ mpMenuBar.clear();
+ Window::dispose();
+}
+
+static void ImplHandleControlAccelerator( const vcl::Window* pWindow, bool bShow )
+{
+ Control *pControl = dynamic_cast<Control*>(pWindow->ImplGetWindow());
+ if (pControl && pControl->GetText().indexOf('~') != -1)
+ {
+ pControl->SetShowAccelerator( bShow );
+ pControl->Invalidate(InvalidateFlags::Update);
+ }
+}
+
+namespace
+{
+ void processChildren(const vcl::Window *pParent, bool bShowAccel)
+ {
+ // go through its children
+ vcl::Window* pChild = firstLogicalChildOfParent(pParent);
+ while (pChild)
+ {
+ if (pChild->GetType() == WindowType::TABCONTROL)
+ {
+ // find currently shown tab page
+ TabControl* pTabControl = static_cast<TabControl*>(pChild);
+ TabPage* pTabPage = pTabControl->GetTabPage( pTabControl->GetCurPageId() );
+ processChildren(pTabPage, bShowAccel);
+ }
+ else if (pChild->GetType() == WindowType::TABPAGE)
+ {
+ // bare tabpage without tabcontrol parent (options dialog)
+ processChildren(pChild, bShowAccel);
+ }
+ else if ((pChild->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL)
+ {
+ // special controls that manage their children outside of widget layout
+ processChildren(pChild, bShowAccel);
+ }
+ else
+ {
+ ImplHandleControlAccelerator(pChild, bShowAccel);
+ }
+ pChild = nextLogicalChildOfParent(pParent, pChild);
+ }
+ }
+}
+
+namespace
+{
+ bool ToggleMnemonicsOnHierarchy(const CommandEvent& rCEvent, const vcl::Window *pWindow)
+ {
+ if (rCEvent.GetCommand() == CommandEventId::ModKeyChange && ImplGetSVData()->maNWFData.mbAutoAccel)
+ {
+ const CommandModKeyData *pCData = rCEvent.GetModKeyData();
+ const bool bShowAccel = pCData && pCData->IsMod2() && pCData->IsDown();
+ processChildren(pWindow, bShowAccel);
+ return true;
+ }
+ return false;
+ }
+}
+
+bool SystemWindow::EventNotify( NotifyEvent& rNEvt )
+{
+ if (rNEvt.GetType() == NotifyEventType::COMMAND)
+ ToggleMnemonicsOnHierarchy(*rNEvt.GetCommandEvent(), this);
+
+ // capture KeyEvents for menu handling
+ if (rNEvt.GetType() == NotifyEventType::KEYINPUT)
+ {
+ MenuBar* pMBar = mpMenuBar;
+ if ( !pMBar && ( GetType() == WindowType::FLOATINGWINDOW ) )
+ {
+ vcl::Window* pWin = ImplGetFrameWindow()->ImplGetWindow();
+ if( pWin && pWin->IsSystemWindow() )
+ pMBar = static_cast<SystemWindow*>(pWin)->GetMenuBar();
+ }
+ if (pMBar && pMBar->ImplHandleKeyEvent(*rNEvt.GetKeyEvent()))
+ return true;
+ }
+
+ return Window::EventNotify( rNEvt );
+}
+
+bool SystemWindow::PreNotify( NotifyEvent& rNEvt )
+{
+ // capture KeyEvents for taskpane cycling
+ if ( rNEvt.GetType() == NotifyEventType::KEYINPUT )
+ {
+ if( rNEvt.GetKeyEvent()->GetKeyCode().GetCode() == KEY_F6 &&
+ rNEvt.GetKeyEvent()->GetKeyCode().IsMod1() &&
+ !rNEvt.GetKeyEvent()->GetKeyCode().IsShift() )
+ {
+ // Ctrl-F6 goes directly to the document
+ GrabFocusToDocument();
+ return true;
+ }
+ else
+ {
+ TaskPaneList *pTList = mpImplData->mpTaskPaneList.get();
+ if( !pTList && ( GetType() == WindowType::FLOATINGWINDOW ) )
+ {
+ vcl::Window* pWin = ImplGetFrameWindow()->ImplGetWindow();
+ if( pWin && pWin->IsSystemWindow() )
+ pTList = static_cast<SystemWindow*>(pWin)->mpImplData->mpTaskPaneList.get();
+ }
+ if( !pTList )
+ {
+ // search topmost system window which is the one to handle dialog/toolbar cycling
+ SystemWindow *pSysWin = this;
+ vcl::Window *pWin = this;
+ while( pWin )
+ {
+ pWin = pWin->GetParent();
+ if( pWin && pWin->IsSystemWindow() )
+ pSysWin = static_cast<SystemWindow*>(pWin);
+ }
+ pTList = pSysWin->mpImplData->mpTaskPaneList.get();
+ }
+ if( pTList && pTList->HandleKeyEvent( *rNEvt.GetKeyEvent() ) )
+ return true;
+ }
+ }
+ return Window::PreNotify( rNEvt );
+}
+
+TaskPaneList* SystemWindow::GetTaskPaneList()
+{
+ if( !mpImplData )
+ return nullptr;
+ if( mpImplData->mpTaskPaneList )
+ return mpImplData->mpTaskPaneList.get();
+ else
+ {
+ mpImplData->mpTaskPaneList.reset( new TaskPaneList );
+ MenuBar* pMBar = mpMenuBar;
+ if ( !pMBar && ( GetType() == WindowType::FLOATINGWINDOW ) )
+ {
+ vcl::Window* pWin = ImplGetFrameWindow()->ImplGetWindow();
+ if ( pWin && pWin->IsSystemWindow() )
+ pMBar = static_cast<SystemWindow*>(pWin)->GetMenuBar();
+ }
+ if( pMBar )
+ mpImplData->mpTaskPaneList->AddWindow( pMBar->ImplGetWindow() );
+ return mpImplData->mpTaskPaneList.get();
+ }
+}
+
+bool SystemWindow::Close()
+{
+ VclPtr<vcl::Window> xWindow = this;
+ CallEventListeners( VclEventId::WindowClose );
+ if ( xWindow->isDisposed() )
+ return false;
+
+ if ( mpWindowImpl->mxWindowPeer.is() && IsCreatedWithToolkit() )
+ return false;
+
+ // Is Window not closeable, ignore close
+ vcl::Window* pBorderWin = ImplGetBorderWindow();
+ WinBits nStyle;
+ if ( pBorderWin )
+ nStyle = pBorderWin->GetStyle();
+ else
+ nStyle = GetStyle();
+ if ( !(nStyle & WB_CLOSEABLE) )
+ return false;
+
+ Hide();
+
+ return true;
+}
+
+void SystemWindow::TitleButtonClick( TitleButton )
+{
+}
+
+void SystemWindow::Resizing( Size& )
+{
+}
+
+void SystemWindow::SetRepresentedURL( const OUString& i_rURL )
+{
+ bool bChanged = (i_rURL != mpImplData->maRepresentedURL);
+ mpImplData->maRepresentedURL = i_rURL;
+ if ( !mbSysChild && bChanged )
+ {
+ const vcl::Window* pWindow = this;
+ while ( pWindow->mpWindowImpl->mpBorderWindow )
+ pWindow = pWindow->mpWindowImpl->mpBorderWindow;
+
+ if ( pWindow->mpWindowImpl->mbFrame )
+ pWindow->mpWindowImpl->mpFrame->SetRepresentedURL( i_rURL );
+ }
+}
+
+void SystemWindow::SetIcon( sal_uInt16 nIcon )
+{
+ if ( mnIcon == nIcon )
+ return;
+
+ mnIcon = nIcon;
+
+ if ( !mbSysChild )
+ {
+ const vcl::Window* pWindow = this;
+ while ( pWindow->mpWindowImpl->mpBorderWindow )
+ pWindow = pWindow->mpWindowImpl->mpBorderWindow;
+
+ if ( pWindow->mpWindowImpl->mbFrame )
+ pWindow->mpWindowImpl->mpFrame->SetIcon( nIcon );
+ }
+}
+
+void SystemWindow::ShowTitleButton( TitleButton nButton, bool bVisible )
+{
+ if ( nButton == TitleButton::Docking )
+ {
+ if ( mbDockBtn != bVisible )
+ {
+ mbDockBtn = bVisible;
+ if ( mpWindowImpl->mpBorderWindow )
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetDockButton( bVisible );
+ }
+ }
+ else if ( nButton == TitleButton::Hide )
+ {
+ if ( mbHideBtn != bVisible )
+ {
+ mbHideBtn = bVisible;
+ if ( mpWindowImpl->mpBorderWindow )
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetHideButton( bVisible );
+ }
+ }
+ else if ( nButton == TitleButton::Menu )
+ {
+ if ( mpWindowImpl->mpBorderWindow )
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetMenuButton( bVisible );
+ }
+ else
+ return;
+}
+
+bool SystemWindow::IsTitleButtonVisible( TitleButton nButton ) const
+{
+ if ( nButton == TitleButton::Docking )
+ return mbDockBtn;
+ else /* if ( nButton == TitleButton::Hide ) */
+ return mbHideBtn;
+}
+
+void SystemWindow::SetMinOutputSizePixel( const Size& rSize )
+{
+ maMinOutSize = rSize;
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetMinOutputSize( rSize.Width(), rSize.Height() );
+ if ( mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame )
+ mpWindowImpl->mpBorderWindow->mpWindowImpl->mpFrame->SetMinClientSize( rSize.Width(), rSize.Height() );
+ }
+ else if ( mpWindowImpl->mbFrame )
+ mpWindowImpl->mpFrame->SetMinClientSize( rSize.Width(), rSize.Height() );
+}
+
+void SystemWindow::SetMaxOutputSizePixel( const Size& rSize )
+{
+ Size aSize( rSize );
+ if( aSize.Width() > SHRT_MAX || aSize.Width() <= 0 )
+ aSize.setWidth( SHRT_MAX );
+ if( aSize.Height() > SHRT_MAX || aSize.Height() <= 0 )
+ aSize.setHeight( SHRT_MAX );
+
+ mpImplData->maMaxOutSize = aSize;
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetMaxOutputSize( aSize.Width(), aSize.Height() );
+ if ( mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame )
+ mpWindowImpl->mpBorderWindow->mpWindowImpl->mpFrame->SetMaxClientSize( aSize.Width(), aSize.Height() );
+ }
+ else if ( mpWindowImpl->mbFrame )
+ mpWindowImpl->mpFrame->SetMaxClientSize( aSize.Width(), aSize.Height() );
+}
+
+const Size& SystemWindow::GetMaxOutputSizePixel() const
+{
+ return mpImplData->maMaxOutSize;
+}
+
+vcl::WindowData::WindowData(std::u16string_view rStr)
+{
+ vcl::WindowData& rData = *this;
+ vcl::WindowDataMask nValidMask = vcl::WindowDataMask::NONE;
+ sal_Int32 nIndex = 0;
+
+ std::u16string_view aTokenStr = o3tl::getToken(rStr, 0, ',', nIndex);
+ if (!aTokenStr.empty())
+ {
+ rData.setX(o3tl::toInt32(aTokenStr));
+ if (rData.x() > -16384 && rData.x() < 16384)
+ nValidMask |= vcl::WindowDataMask::X;
+ else
+ rData.setX(0);
+ }
+ else
+ rData.setX(0);
+ aTokenStr = o3tl::getToken(rStr, 0, ',', nIndex);
+ if (!aTokenStr.empty())
+ {
+ rData.setY(o3tl::toInt32(aTokenStr));
+ if (rData.y() > -16384 && rData.y() < 16384)
+ nValidMask |= vcl::WindowDataMask::Y;
+ else
+ rData.setY(0);
+ }
+ else
+ rData.setY(0);
+ aTokenStr = o3tl::getToken(rStr, 0, ',', nIndex);
+ if (!aTokenStr.empty())
+ {
+ sal_Int32 nWidth = o3tl::toInt32(aTokenStr);
+ if (nWidth >= 0)
+ {
+ rData.setWidth(nWidth);
+ }
+ if (rData.width() > 0 && rData.width() < 16384)
+ nValidMask |= vcl::WindowDataMask::Width;
+ else
+ rData.setWidth(0);
+ }
+ else
+ rData.setWidth(0);
+ aTokenStr = o3tl::getToken(rStr, 0, ';', nIndex);
+ if (!aTokenStr.empty())
+ {
+ sal_Int32 nHeight = o3tl::toInt32(aTokenStr);
+ if (nHeight >= 0)
+ {
+ rData.setHeight(nHeight);
+ }
+ if (rData.height() > 0 && rData.height() < 16384)
+ nValidMask |= vcl::WindowDataMask::Height;
+ else
+ rData.setHeight(0);
+ }
+ else
+ rData.setHeight(0);
+ aTokenStr = o3tl::getToken(rStr, 0, ';', nIndex);
+ if (!aTokenStr.empty())
+ {
+ // #94144# allow Minimize again, should be masked out when read from configuration
+ // 91625 - ignore Minimize
+ vcl::WindowState nState = static_cast<vcl::WindowState>(o3tl::toInt32(aTokenStr));
+ //nState &= ~vcl::WindowState::Minimized;
+ rData.setState(nState);
+ nValidMask |= vcl::WindowDataMask::State;
+ }
+ else
+ rData.setState(vcl::WindowState::NONE);
+
+ // read maximized pos/size
+ aTokenStr = o3tl::getToken(rStr, 0, ',', nIndex);
+ if (!aTokenStr.empty())
+ {
+ rData.SetMaximizedX(o3tl::toInt32(aTokenStr));
+ if (rData.GetMaximizedX() > -16384 && rData.GetMaximizedX() < 16384)
+ nValidMask |= vcl::WindowDataMask::MaximizedX;
+ else
+ rData.SetMaximizedX(0);
+ }
+ else
+ rData.SetMaximizedX(0);
+ aTokenStr = o3tl::getToken(rStr, 0, ',', nIndex);
+ if (!aTokenStr.empty())
+ {
+ rData.SetMaximizedY(o3tl::toInt32(aTokenStr));
+ if (rData.GetMaximizedY() > -16384 && rData.GetMaximizedY() < 16384)
+ nValidMask |= vcl::WindowDataMask::MaximizedY;
+ else
+ rData.SetMaximizedY(0);
+ }
+ else
+ rData.SetMaximizedY(0);
+ aTokenStr = o3tl::getToken(rStr, 0, ',', nIndex);
+ if (!aTokenStr.empty())
+ {
+ rData.SetMaximizedWidth(o3tl::toInt32(aTokenStr));
+ if (rData.GetMaximizedWidth() > 0 && rData.GetMaximizedWidth() < 16384)
+ nValidMask |= vcl::WindowDataMask::MaximizedWidth;
+ else
+ rData.SetMaximizedWidth(0);
+ }
+ else
+ rData.SetMaximizedWidth(0);
+ aTokenStr = o3tl::getToken(rStr, 0, ';', nIndex);
+ if (!aTokenStr.empty())
+ {
+ rData.SetMaximizedHeight(o3tl::toInt32(aTokenStr));
+ if (rData.GetMaximizedHeight() > 0 && rData.GetMaximizedHeight() < 16384)
+ nValidMask |= vcl::WindowDataMask::MaximizedHeight;
+ else
+ rData.SetMaximizedHeight(0);
+ }
+ else
+ rData.SetMaximizedHeight(0);
+
+ // mark valid fields
+ rData.setMask(nValidMask);
+}
+
+OUString vcl::WindowData::toStr() const
+{
+ const vcl::WindowDataMask nValidMask = mask();
+ if ( nValidMask == vcl::WindowDataMask::NONE )
+ return {};
+
+ OUStringBuffer rStrBuf(64);
+
+ tools::Rectangle aRect = posSize();
+
+ if (nValidMask & vcl::WindowDataMask::X)
+ rStrBuf.append(static_cast<sal_Int32>(aRect.Left()));
+ rStrBuf.append(',');
+ if (nValidMask & vcl::WindowDataMask::Y)
+ rStrBuf.append(static_cast<sal_Int32>(aRect.Top()));
+ rStrBuf.append(',');
+ if (nValidMask & vcl::WindowDataMask::Width)
+ rStrBuf.append(static_cast<sal_Int32>(aRect.GetWidth()));
+ rStrBuf.append(',');
+ if (nValidMask & vcl::WindowDataMask::Height)
+ rStrBuf.append(static_cast<sal_Int32>(aRect.GetHeight()));
+ rStrBuf.append( ';' );
+ if (nValidMask & vcl::WindowDataMask::State)
+ {
+ // #94144# allow Minimize again, should be masked out when read from configuration
+ // 91625 - ignore Minimize
+ rStrBuf.append(static_cast<sal_Int32>(state()));
+ }
+ rStrBuf.append(';');
+ if (nValidMask & vcl::WindowDataMask::MaximizedX)
+ rStrBuf.append(static_cast<sal_Int32>(GetMaximizedX()));
+ rStrBuf.append(',');
+ if (nValidMask & vcl::WindowDataMask::MaximizedY)
+ rStrBuf.append(static_cast<sal_Int32>(GetMaximizedY()));
+ rStrBuf.append( ',' );
+ if (nValidMask & vcl::WindowDataMask::MaximizedWidth)
+ rStrBuf.append(static_cast<sal_Int32>(GetMaximizedWidth()));
+ rStrBuf.append(',');
+ if (nValidMask & vcl::WindowDataMask::MaximizedHeight)
+ rStrBuf.append(static_cast<sal_Int32>(GetMaximizedHeight()));
+ rStrBuf.append(';');
+
+ return rStrBuf.makeStringAndClear();
+}
+
+void SystemWindow::ImplMoveToScreen( tools::Long& io_rX, tools::Long& io_rY, tools::Long i_nWidth, tools::Long i_nHeight, vcl::Window const * i_pConfigureWin )
+{
+ AbsoluteScreenPixelRectangle aScreenRect = Application::GetScreenPosSizePixel( 0 );
+ for( unsigned int i = 1; i < Application::GetScreenCount(); i++ )
+ aScreenRect.Union( Application::GetScreenPosSizePixel( i ) );
+ // unfortunately most of the time width and height are not really known
+ if( i_nWidth < 1 )
+ i_nWidth = 50;
+ if( i_nHeight < 1 )
+ i_nHeight = 50;
+
+ // check left border
+ bool bMove = false;
+ if( io_rX + i_nWidth < aScreenRect.Left() )
+ {
+ bMove = true;
+ io_rX = aScreenRect.Left();
+ }
+ // check right border
+ if( io_rX > aScreenRect.Right() - i_nWidth )
+ {
+ bMove = true;
+ io_rX = aScreenRect.Right() - i_nWidth;
+ }
+ // check top border
+ if( io_rY + i_nHeight < aScreenRect.Top() )
+ {
+ bMove = true;
+ io_rY = aScreenRect.Top();
+ }
+ // check bottom border
+ if( io_rY > aScreenRect.Bottom() - i_nHeight )
+ {
+ bMove = true;
+ io_rY = aScreenRect.Bottom() - i_nHeight;
+ }
+ vcl::Window* pParent = i_pConfigureWin->GetParent();
+ if( bMove && pParent )
+ {
+ // calculate absolute screen pos here, since that is what is contained in WindowData
+ Point aParentAbsPos( pParent->OutputToAbsoluteScreenPixel( Point(0,0) ) );
+ Size aParentSizePixel( pParent->GetOutputSizePixel() );
+ Point aPos( (aParentSizePixel.Width() - i_nWidth) / 2,
+ (aParentSizePixel.Height() - i_nHeight) / 2 );
+ io_rX = aParentAbsPos.X() + aPos.X();
+ io_rY = aParentAbsPos.Y() + aPos.Y();
+ }
+}
+
+void SystemWindow::SetWindowState(const vcl::WindowData& rData)
+{
+ const vcl::WindowDataMask nValidMask = rData.mask();
+ if ( nValidMask == vcl::WindowDataMask::NONE )
+ return;
+
+ if ( mbSysChild )
+ return;
+
+ vcl::Window* pWindow = this;
+ while ( pWindow->mpWindowImpl->mpBorderWindow )
+ pWindow = pWindow->mpWindowImpl->mpBorderWindow;
+
+ if ( pWindow->mpWindowImpl->mbFrame )
+ {
+ const vcl::WindowState nState = rData.state();
+ vcl::WindowData aState = rData;
+
+ if (rData.mask() & vcl::WindowDataMask::Size)
+ {
+ // #i43799# adjust window state sizes if a minimal output size was set
+ // otherwise the frame and the client might get different sizes
+ if (maMinOutSize.Width() > static_cast<tools::Long>(aState.width()))
+ aState.setWidth(maMinOutSize.Width());
+ if (maMinOutSize.Height() > static_cast<tools::Long>(aState.width()))
+ aState.setHeight(maMinOutSize.Height());
+ }
+
+ // #94144# allow Minimize again, should be masked out when read from configuration
+ // 91625 - ignore Minimize
+ //nState &= ~(WindowState::Minimized);
+ aState.rState() &= vcl::WindowState::SystemMask;
+
+ // normalize window positions onto screen
+ tools::Long nX = aState.x(), nY = aState.y();
+ ImplMoveToScreen(nX, nY, aState.width(), aState.height(), pWindow);
+ aState.setPos({ nX, nY });
+ nX = aState.GetMaximizedX();
+ nY = aState.GetMaximizedY();
+ ImplMoveToScreen(nX, nY, aState.GetMaximizedWidth(), aState.GetMaximizedHeight(), pWindow);
+ aState.SetMaximizedX(nX);
+ aState.SetMaximizedY(nY);
+
+ // #96568# avoid having multiple frames at the same screen location
+ // do the check only if not maximized
+ if( !((rData.mask() & vcl::WindowDataMask::State) && (nState & vcl::WindowState::Maximized)) )
+ if (rData.mask() & vcl::WindowDataMask::PosSize)
+ {
+ AbsoluteScreenPixelRectangle aDesktop = GetDesktopRectPixel();
+ ImplSVData *pSVData = ImplGetSVData();
+ vcl::Window *pWin = pSVData->maFrameData.mpFirstFrame;
+ bool bWrapped = false;
+ while( pWin )
+ {
+ if( !pWin->ImplIsRealParentPath( this ) && ( pWin != this ) &&
+ pWin->ImplGetWindow()->IsTopWindow() && pWin->mpWindowImpl->mbReallyVisible )
+ {
+ SalFrameGeometry g = pWin->mpWindowImpl->mpFrame->GetGeometry();
+ if( std::abs(g.x()-aState.x()) < 2 && std::abs(g.y()-aState.y()) < 5 )
+ {
+ tools::Long displacement = g.topDecoration() ? g.topDecoration() : 20;
+ if( static_cast<tools::Long>(aState.x() + displacement + aState.width() + g.rightDecoration()) > aDesktop.Right() ||
+ static_cast<tools::Long>(aState.y() + displacement + aState.height() + g.bottomDecoration()) > aDesktop.Bottom() )
+ {
+ // displacing would leave screen
+ aState.setX(g.leftDecoration() ? g.leftDecoration() : 10); // should result in (0,0)
+ aState.setY(displacement);
+ if( bWrapped ||
+ static_cast<tools::Long>(aState.x() + displacement + aState.width() + g.rightDecoration()) > aDesktop.Right() ||
+ static_cast<tools::Long>(aState.y() + displacement + aState.height() + g.bottomDecoration()) > aDesktop.Bottom() )
+ break; // further displacement not possible -> break
+ // avoid endless testing
+ bWrapped = true;
+ }
+ else
+ aState.move(displacement, displacement);
+ pWin = pSVData->maFrameData.mpFirstFrame; // check new pos again
+ }
+ }
+ pWin = pWin->mpWindowImpl->mpFrameData->mpNextFrame;
+ }
+ }
+
+ mpWindowImpl->mpFrame->SetWindowState( &aState );
+
+ // do a synchronous resize for layout reasons
+ // but use rData only when the window is not to be maximized (#i38089#)
+ // otherwise we have no useful size information
+ if( (rData.mask() & vcl::WindowDataMask::State) && (nState & vcl::WindowState::Maximized) )
+ {
+ // query maximized size from frame
+ SalFrameGeometry aGeometry = mpWindowImpl->mpFrame->GetGeometry();
+
+ // but use it only if it is different from the restore size (rData)
+ // as currently only on windows the exact size of a maximized window
+ // can be computed without actually showing the window
+ if (aGeometry.width() != rData.width() || aGeometry.height() != rData.height())
+ ImplHandleResize(pWindow, aGeometry.width(), aGeometry.height());
+ }
+ else
+ if (rData.mask() & vcl::WindowDataMask::Size)
+ ImplHandleResize(pWindow, aState.width(), aState.height()); // #i43799# use aState and not rData, see above
+ }
+ else
+ {
+ PosSizeFlags nPosSize = PosSizeFlags::NONE;
+ if ( nValidMask & vcl::WindowDataMask::X )
+ nPosSize |= PosSizeFlags::X;
+ if ( nValidMask & vcl::WindowDataMask::Y )
+ nPosSize |= PosSizeFlags::Y;
+ if ( nValidMask & vcl::WindowDataMask::Width )
+ nPosSize |= PosSizeFlags::Width;
+ if ( nValidMask & vcl::WindowDataMask::Height )
+ nPosSize |= PosSizeFlags::Height;
+
+ tools::Long nX = rData.x();
+ tools::Long nY = rData.y();
+ tools::Long nWidth = rData.width();
+ tools::Long nHeight = rData.height();
+ const SalFrameGeometry& rGeom = pWindow->mpWindowImpl->mpFrame->GetGeometry();
+ if( nX < 0 )
+ nX = 0;
+ if( nX + nWidth > static_cast<tools::Long>(rGeom.width()) )
+ nX = rGeom.width() - nWidth;
+ if( nY < 0 )
+ nY = 0;
+ if( nY + nHeight > static_cast<tools::Long>(rGeom.height()) )
+ nY = rGeom.height() - nHeight;
+ setPosSizePixel( nX, nY, nWidth, nHeight, nPosSize );
+ }
+
+ // tdf#146648 if an explicit size state was set, then use it as the preferred
+ // size for layout
+ if (nValidMask & vcl::WindowDataMask::Size)
+ mbInitialLayoutSizeCalculated = true;
+}
+
+void SystemWindow::GetWindowState(vcl::WindowData& rData) const
+{
+ vcl::WindowDataMask nValidMask = rData.mask();
+ if ( nValidMask == vcl::WindowDataMask::NONE )
+ return;
+
+ if ( mbSysChild )
+ {
+ rData.setMask( vcl::WindowDataMask::NONE );
+ return;
+ }
+
+ const vcl::Window* pWindow = this;
+ while ( pWindow->mpWindowImpl->mpBorderWindow )
+ pWindow = pWindow->mpWindowImpl->mpBorderWindow;
+
+ if ( pWindow->mpWindowImpl->mbFrame )
+ {
+ vcl::WindowData aState;
+ if ( mpWindowImpl->mpFrame->GetWindowState( &aState ) )
+ {
+ // Limit mask only to what we've received, the rest is not set.
+ nValidMask &= aState.mask();
+ rData.setMask( nValidMask );
+ if ( nValidMask & vcl::WindowDataMask::X )
+ rData.setX( aState.x() );
+ if ( nValidMask & vcl::WindowDataMask::Y )
+ rData.setY( aState.y() );
+ if ( nValidMask & vcl::WindowDataMask::Width )
+ rData.setWidth( aState.width() );
+ if ( nValidMask & vcl::WindowDataMask::Height )
+ rData.setHeight( aState.height() );
+ if ( nValidMask & vcl::WindowDataMask::MaximizedX )
+ rData.SetMaximizedX( aState.GetMaximizedX() );
+ if ( nValidMask & vcl::WindowDataMask::MaximizedY )
+ rData.SetMaximizedY( aState.GetMaximizedY() );
+ if ( nValidMask & vcl::WindowDataMask::MaximizedWidth )
+ rData.SetMaximizedWidth( aState.GetMaximizedWidth() );
+ if ( nValidMask & vcl::WindowDataMask::MaximizedHeight )
+ rData.SetMaximizedHeight( aState.GetMaximizedHeight() );
+ if ( nValidMask & vcl::WindowDataMask::State )
+ {
+ // #94144# allow Minimize again, should be masked out when read from configuration
+ // 91625 - ignore Minimize
+ if (!(nValidMask & vcl::WindowDataMask::Minimized))
+ aState.rState() &= ~vcl::WindowState::Minimized;
+ rData.setState(aState.state());
+ }
+ rData.setMask( nValidMask );
+ }
+ else
+ rData.setMask(vcl::WindowDataMask::NONE);
+ }
+ else
+ {
+ Point aPos = GetPosPixel();
+ Size aSize = GetSizePixel();
+ vcl::WindowState nState = vcl::WindowState::NONE;
+
+ nValidMask &= vcl::WindowDataMask::PosSizeState;
+ rData.setMask( nValidMask );
+ if (nValidMask & vcl::WindowDataMask::X)
+ rData.setX(aPos.X());
+ if (nValidMask & vcl::WindowDataMask::Y)
+ rData.setY(aPos.Y());
+ if (nValidMask & vcl::WindowDataMask::Width)
+ rData.setWidth(aSize.Width());
+ if (nValidMask & vcl::WindowDataMask::Height)
+ rData.setHeight(aSize.Height());
+ if (nValidMask & vcl::WindowDataMask::State)
+ rData.setState(nState);
+ }
+}
+
+void SystemWindow::SetWindowState(std::u16string_view rStr)
+{
+ if (rStr.empty())
+ return;
+ SetWindowState(vcl::WindowData(rStr));
+}
+
+OUString SystemWindow::GetWindowState(vcl::WindowDataMask nMask) const
+{
+ vcl::WindowData aData;
+ aData.setMask(nMask);
+ GetWindowState(aData);
+ return aData.toStr();
+}
+
+void SystemWindow::SetMenuBar(MenuBar* pMenuBar)
+{
+ if ( mpMenuBar == pMenuBar )
+ return;
+
+ MenuBar* pOldMenuBar = mpMenuBar;
+ vcl::Window* pOldWindow = nullptr;
+ VclPtr<vcl::Window> pNewWindow;
+ mpMenuBar = pMenuBar;
+
+ if ( mpWindowImpl->mpBorderWindow && (mpWindowImpl->mpBorderWindow->GetType() == WindowType::BORDERWINDOW) )
+ {
+ if ( pOldMenuBar )
+ pOldWindow = pOldMenuBar->ImplGetWindow();
+ else
+ pOldWindow = nullptr;
+ if ( pOldWindow )
+ {
+ CallEventListeners( VclEventId::WindowMenubarRemoved, static_cast<void*>(pOldMenuBar) );
+ pOldWindow->SetAccessible( css::uno::Reference< css::accessibility::XAccessible >() );
+ }
+ if ( pMenuBar )
+ {
+ SAL_WARN_IF( pMenuBar->pWindow, "vcl", "SystemWindow::SetMenuBar() - MenuBars can only set in one SystemWindow at time" );
+
+ pNewWindow = MenuBar::ImplCreate(mpWindowImpl->mpBorderWindow, pOldWindow, pMenuBar);
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetMenuBarWindow(pNewWindow);
+
+ CallEventListeners( VclEventId::WindowMenubarAdded, static_cast<void*>(pMenuBar) );
+ }
+ else
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetMenuBarWindow( nullptr );
+ ImplToBottomChild();
+ if ( pOldMenuBar )
+ {
+ bool bDelete = (pMenuBar == nullptr);
+ if( bDelete && pOldWindow )
+ {
+ if( mpImplData->mpTaskPaneList )
+ mpImplData->mpTaskPaneList->RemoveWindow( pOldWindow );
+ }
+ MenuBar::ImplDestroy( pOldMenuBar, bDelete );
+ if( bDelete )
+ pOldWindow = nullptr; // will be deleted in MenuBar::ImplDestroy,
+ }
+
+ }
+ else
+ {
+ if( pMenuBar )
+ pNewWindow = pMenuBar->ImplGetWindow();
+ if( pOldMenuBar )
+ pOldWindow = pOldMenuBar->ImplGetWindow();
+ }
+
+ // update taskpane list to make menubar accessible
+ if( mpImplData->mpTaskPaneList )
+ {
+ if( pOldWindow )
+ mpImplData->mpTaskPaneList->RemoveWindow( pOldWindow );
+ if( pNewWindow )
+ mpImplData->mpTaskPaneList->AddWindow( pNewWindow );
+ }
+}
+
+void SystemWindow::SetNotebookBar(const OUString& rUIXMLDescription,
+ const css::uno::Reference<css::frame::XFrame>& rFrame,
+ const NotebookBarAddonsItem& aNotebookBarAddonsItem,
+ bool bReloadNotebookbar)
+{
+ if (rUIXMLDescription != maNotebookBarUIFile || bReloadNotebookbar)
+ {
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())
+ ->SetNotebookBar(rUIXMLDescription, rFrame, aNotebookBarAddonsItem);
+ maNotebookBarUIFile = rUIXMLDescription;
+ if(GetNotebookBar())
+ GetNotebookBar()->SetSystemWindow(this);
+ }
+}
+
+void SystemWindow::CloseNotebookBar()
+{
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->CloseNotebookBar();
+ maNotebookBarUIFile.clear();
+}
+
+VclPtr<NotebookBar> const & SystemWindow::GetNotebookBar() const
+{
+ return static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->GetNotebookBar();
+}
+
+void SystemWindow::SetMenuBarMode( MenuBarMode nMode )
+{
+ if ( mnMenuBarMode != nMode )
+ {
+ mnMenuBarMode = nMode;
+ if ( mpWindowImpl->mpBorderWindow && (mpWindowImpl->mpBorderWindow->GetType() == WindowType::BORDERWINDOW) )
+ {
+ if ( nMode == MenuBarMode::Hide )
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetMenuBarMode( true );
+ else
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetMenuBarMode( false );
+ }
+ }
+}
+
+bool SystemWindow::ImplIsInTaskPaneList( vcl::Window* pWin )
+{
+ if( mpImplData && mpImplData->mpTaskPaneList )
+ return mpImplData->mpTaskPaneList->IsInList( pWin );
+ return false;
+}
+
+unsigned int SystemWindow::GetScreenNumber() const
+{
+ return mpWindowImpl->mpFrame->maGeometry.screen();
+}
+
+void SystemWindow::SetScreenNumber(unsigned int nDisplayScreen)
+{
+ mpWindowImpl->mpFrame->SetScreenNumber( nDisplayScreen );
+}
+
+void SystemWindow::SetApplicationID(const OUString &rApplicationID)
+{
+ mpWindowImpl->mpFrame->SetApplicationID( rApplicationID );
+}
+
+void SystemWindow::SetCloseHdl(const Link<SystemWindow&,void>& rLink)
+{
+ mpImplData->maCloseHdl = rLink;
+}
+
+const Link<SystemWindow&,void>& SystemWindow::GetCloseHdl() const
+{
+ return mpImplData->maCloseHdl;
+}
+
+void SystemWindow::queue_resize(StateChangedType /*eReason*/)
+{
+ if (!isLayoutEnabled())
+ return;
+ if (isCalculatingInitialLayoutSize())
+ return;
+ InvalidateSizeCache();
+ if (hasPendingLayout())
+ return;
+ maLayoutIdle.Start();
+}
+
+void SystemWindow::Resize()
+{
+ queue_resize();
+}
+
+bool SystemWindow::isLayoutEnabled() const
+{
+ //pre dtor called, and single child is a container => we're layout enabled
+ return mpImplData && ::isLayoutEnabled(this);
+}
+
+Size SystemWindow::GetOptimalSize() const
+{
+ if (!isLayoutEnabled())
+ return Window::GetOptimalSize();
+
+ Window *pBox = GetWindow(GetWindowType::FirstChild);
+ // tdf#141318 Do the same as SystemWindow::setOptimalLayoutSize in case we're called before initial layout
+ const_cast<SystemWindow*>(this)->settingOptimalLayoutSize(pBox);
+ Size aSize = VclContainer::getLayoutRequisition(*pBox);
+
+ sal_Int32 nBorderWidth = get_border_width();
+
+ aSize.AdjustHeight(2 * nBorderWidth );
+ aSize.AdjustWidth(2 * nBorderWidth );
+
+ return Window::CalcWindowSize(aSize);
+}
+
+void SystemWindow::setPosSizeOnContainee(Size aSize, Window &rBox)
+{
+ sal_Int32 nBorderWidth = get_border_width();
+
+ aSize.AdjustWidth( -(2 * nBorderWidth) );
+ aSize.AdjustHeight( -(2 * nBorderWidth) );
+
+ Point aPos(nBorderWidth, nBorderWidth);
+ VclContainer::setLayoutAllocation(rBox, aPos, CalcOutputSize(aSize));
+}
+
+IMPL_LINK_NOARG( SystemWindow, ImplHandleLayoutTimerHdl, Timer*, void )
+{
+ Window *pBox = GetWindow(GetWindowType::FirstChild);
+ if (!isLayoutEnabled())
+ {
+ SAL_WARN_IF(pBox, "vcl.layout", "SystemWindow has become non-layout because extra children have been added directly to it.");
+ return;
+ }
+ assert(pBox);
+ setPosSizeOnContainee(GetSizePixel(), *pBox);
+}
+
+void SystemWindow::SetText(const OUString& rStr)
+{
+ setDeferredProperties();
+ Window::SetText(rStr);
+}
+
+OUString SystemWindow::GetText() const
+{
+ const_cast<SystemWindow*>(this)->setDeferredProperties();
+ return Window::GetText();
+}
+
+void SystemWindow::settingOptimalLayoutSize(Window* /*pBox*/)
+{
+}
+
+void SystemWindow::setOptimalLayoutSize(bool bAllowWindowShrink)
+{
+ maLayoutIdle.Stop();
+
+ //resize SystemWindow to fit requisition on initial show
+ Window *pBox = GetWindow(GetWindowType::FirstChild);
+
+ settingOptimalLayoutSize(pBox);
+
+ Size aSize = get_preferred_size();
+
+ Size aMax(bestmaxFrameSizeForScreenSize(Size(GetDesktopRectPixel().GetSize())));
+
+ aSize.setWidth( std::min(aMax.Width(), aSize.Width()) );
+ aSize.setHeight( std::min(aMax.Height(), aSize.Height()) );
+
+ SetMinOutputSizePixel(aSize);
+
+ if (!bAllowWindowShrink)
+ {
+ Size aCurrentSize = GetSizePixel();
+ aSize.setWidth(std::max(aSize.Width(), aCurrentSize.Width()));
+ aSize.setHeight(std::max(aSize.Height(), aCurrentSize.Height()));
+ }
+
+ SetSizePixel(aSize);
+ setPosSizeOnContainee(aSize, *pBox);
+}
+
+void SystemWindow::DoInitialLayout()
+{
+ if (GetSettings().GetStyleSettings().GetAutoMnemonic())
+ GenerateAutoMnemonicsOnHierarchy(this);
+
+ if (isLayoutEnabled())
+ {
+ mbIsCalculatingInitialLayoutSize = true;
+ setDeferredProperties();
+ setOptimalLayoutSize(!mbInitialLayoutSizeCalculated);
+ mbInitialLayoutSizeCalculated = true;
+ mbIsCalculatingInitialLayoutSize = false;
+ }
+}
+
+void SystemWindow::doDeferredInit(WinBits /*nBits*/)
+{
+ SAL_WARN("vcl.layout", "SystemWindow in layout without doDeferredInit impl");
+}
+
+VclPtr<VirtualDevice> SystemWindow::createScreenshot()
+{
+ // same prerequisites as in Execute()
+ setDeferredProperties();
+ ImplAdjustNWFSizes();
+ Show();
+ ToTop();
+ ensureRepaint();
+
+ Size aSize(GetOutputSizePixel());
+
+ VclPtr<VirtualDevice> xOutput(VclPtr<VirtualDevice>::Create(DeviceFormat::WITHOUT_ALPHA));
+ xOutput->SetOutputSizePixel(aSize);
+
+ Point aPos;
+ xOutput->DrawOutDev(aPos, aSize, aPos, aSize, *GetOutDev());
+
+ return xOutput;
+}
+
+void SystemWindow::PrePaint(vcl::RenderContext& rRenderContext)
+{
+ Window::PrePaint(rRenderContext);
+ mbPaintComplete = false;
+}
+
+void SystemWindow::PostPaint(vcl::RenderContext& rRenderContext)
+{
+ Window::PostPaint(rRenderContext);
+ mbPaintComplete = true;
+}
+
+void SystemWindow::ensureRepaint()
+{
+ // ensure repaint
+ Invalidate();
+ mbPaintComplete = false;
+
+ while (!mbPaintComplete && !Application::IsQuit())
+ {
+ Application::Yield();
+ }
+}
+
+void SystemWindow::CollectMenuBarMnemonics(MnemonicGenerator& rMnemonicGenerator) const
+{
+ if (MenuBar* pMenu = GetMenuBar())
+ {
+ sal_uInt16 nMenuItems = pMenu->GetItemCount();
+ for ( sal_uInt16 i = 0; i < nMenuItems; ++i )
+ rMnemonicGenerator.RegisterMnemonic( pMenu->GetItemText( pMenu->GetItemId( i ) ) );
+ }
+}
+
+int SystemWindow::GetMenuBarHeight() const
+{
+ if (MenuBar* pMenuBar = GetMenuBar())
+ return pMenuBar->GetMenuBarHeight();
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/tabdlg.cxx b/vcl/source/window/tabdlg.cxx
new file mode 100644
index 0000000000..f132300a07
--- /dev/null
+++ b/vcl/source/window/tabdlg.cxx
@@ -0,0 +1,184 @@
+/* -*- 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 <vcl/toolkit/fixed.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/tabctrl.hxx>
+#include <vcl/toolkit/tabdlg.hxx>
+#include <vcl/tabpage.hxx>
+
+void TabDialog::ImplInitTabDialogData()
+{
+ mpFixedLine = nullptr;
+ mbPosControls = true;
+}
+
+void TabDialog::ImplPosControls()
+{
+ if (isLayoutEnabled())
+ return;
+
+ Size aCtrlSize( IMPL_MINSIZE_BUTTON_WIDTH, IMPL_MINSIZE_BUTTON_HEIGHT );
+ tools::Long nDownCtrl = 0;
+ tools::Long nOffY = 0;
+ vcl::Window* pTabControl = nullptr;
+
+ vcl::Window* pChild = GetWindow( GetWindowType::FirstChild );
+ while ( pChild )
+ {
+ if ( pChild->IsVisible() )
+ {
+ if (pChild->GetType() == WindowType::TABCONTROL || isContainerWindow(*pChild))
+ pTabControl = pChild;
+ else if ( pTabControl )
+ {
+ Size aOptimalSize(pChild->get_preferred_size());
+ tools::Long nTxtWidth = aOptimalSize.Width();
+ if ( nTxtWidth > aCtrlSize.Width() )
+ aCtrlSize.setWidth( nTxtWidth );
+ tools::Long nTxtHeight = aOptimalSize.Height();
+ if ( nTxtHeight > aCtrlSize.Height() )
+ aCtrlSize.setHeight( nTxtHeight );
+ nDownCtrl++;
+ }
+ else
+ {
+ tools::Long nHeight = pChild->GetSizePixel().Height();
+ if ( nHeight > nOffY )
+ nOffY = nHeight;
+ }
+ }
+
+ pChild = pChild->GetWindow( GetWindowType::Next );
+ }
+
+ // do we have a TabControl ?
+ if ( pTabControl )
+ {
+ // adapt offset for other controls by an extra distance
+ if ( nOffY )
+ nOffY += IMPL_DIALOG_BAR_OFFSET*2 + 2;
+
+ Point aTabOffset( IMPL_DIALOG_OFFSET, IMPL_DIALOG_OFFSET+nOffY );
+
+ if (isContainerWindow(*pTabControl))
+ pTabControl->SetSizePixel(pTabControl->get_preferred_size());
+
+ Size aTabSize = pTabControl->GetSizePixel();
+
+ Size aDlgSize( aTabSize.Width() + IMPL_DIALOG_OFFSET*2,
+ aTabSize.Height() + IMPL_DIALOG_OFFSET*2 + nOffY );
+
+ // adapt positioning
+ pTabControl->SetPosPixel( aTabOffset );
+
+ // position all other Children
+ bool bTabCtrl = false;
+ int nLines = 0;
+ tools::Long nX;
+ tools::Long nY = aDlgSize.Height();
+ tools::Long nTopX = IMPL_DIALOG_OFFSET;
+
+ // all buttons are right aligned under Windows 95
+ nX = IMPL_DIALOG_OFFSET;
+ tools::Long nCtrlBarWidth = ((aCtrlSize.Width()+IMPL_DIALOG_OFFSET)*nDownCtrl)-IMPL_DIALOG_OFFSET;
+ if ( nCtrlBarWidth <= aTabSize.Width() )
+ nX = aTabSize.Width() - nCtrlBarWidth + IMPL_DIALOG_OFFSET;
+
+ vcl::Window* pChild2 = GetWindow( GetWindowType::FirstChild );
+ while ( pChild2 )
+ {
+ if ( pChild2->IsVisible() )
+ {
+ if ( pChild2 == pTabControl )
+ bTabCtrl = true;
+ else if ( bTabCtrl )
+ {
+ if ( !nLines )
+ nLines = 1;
+
+ if ( nX+aCtrlSize.Width()-IMPL_DIALOG_OFFSET > aTabSize.Width() )
+ {
+ nY += aCtrlSize.Height()+IMPL_DIALOG_OFFSET;
+ nX = IMPL_DIALOG_OFFSET;
+ nLines++;
+ }
+
+ pChild2->SetPosSizePixel( Point( nX, nY ), aCtrlSize );
+ nX += aCtrlSize.Width()+IMPL_DIALOG_OFFSET;
+ }
+ else
+ {
+ Size aChildSize = pChild2->GetSizePixel();
+ pChild2->SetPosPixel( Point( nTopX, (nOffY-aChildSize.Height())/2 ) );
+ nTopX += aChildSize.Width()+2;
+ }
+ }
+
+ pChild2 = pChild2->GetWindow( GetWindowType::Next );
+ }
+
+ aDlgSize.AdjustHeight(nLines * (aCtrlSize.Height()+IMPL_DIALOG_OFFSET) );
+ SetOutputSizePixel( aDlgSize );
+ }
+
+ // store offset
+ if ( nOffY )
+ {
+ Size aDlgSize = GetOutputSizePixel();
+ if ( !mpFixedLine )
+ mpFixedLine = VclPtr<FixedLine>::Create( this );
+ mpFixedLine->SetPosSizePixel( Point( 0, nOffY ),
+ Size( aDlgSize.Width(), 2 ) );
+ mpFixedLine->Show();
+ }
+
+ mbPosControls = false;
+}
+
+TabDialog::TabDialog( vcl::Window* pParent, WinBits nStyle ) :
+ Dialog( WindowType::TABDIALOG )
+{
+ ImplInitTabDialogData();
+ ImplInitDialog( pParent, nStyle );
+}
+
+TabDialog::~TabDialog()
+{
+ disposeOnce();
+}
+
+void TabDialog::dispose()
+{
+ mpFixedLine.disposeAndClear();
+ Dialog::dispose();
+}
+
+void TabDialog::StateChanged( StateChangedType nType )
+{
+ if ( nType == StateChangedType::InitShow )
+ {
+ // Calculate the Layout only for the initialized state
+ if ( mbPosControls )
+ ImplPosControls();
+ }
+ Dialog::StateChanged( nType );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/tabpage.cxx b/vcl/source/window/tabpage.cxx
new file mode 100644
index 0000000000..612b17d272
--- /dev/null
+++ b/vcl/source/window/tabpage.cxx
@@ -0,0 +1,309 @@
+/* -*- 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 <vcl/event.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/tabpage.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/toolkit/scrbar.hxx>
+#include <svdata.hxx>
+
+void TabPage::ImplInit( vcl::Window* pParent, WinBits nStyle )
+{
+ if ( !(nStyle & WB_NODIALOGCONTROL) )
+ nStyle |= WB_DIALOGCONTROL;
+
+ Window::ImplInit( pParent, nStyle, nullptr );
+
+ bool bHasHoriBar = false;
+ bool bHasVertBar = false;
+
+ Link<ScrollBar*,void> aLink( LINK( this, TabPage, ScrollBarHdl ) );
+
+ if ( nStyle & ( WB_AUTOHSCROLL | WB_AUTOVSCROLL ) )
+ {
+ if ( nStyle & WB_AUTOHSCROLL )
+ {
+ bHasHoriBar = true;
+ m_pHScroll.set(VclPtr<ScrollBar>::Create(this, (WB_HSCROLL | WB_DRAG)));
+ m_pHScroll->Show();
+ m_pHScroll->SetScrollHdl(aLink);
+ }
+ if ( nStyle & WB_AUTOVSCROLL )
+ {
+ bHasVertBar = true;
+ m_pVScroll.set(VclPtr<ScrollBar>::Create(this, (WB_VSCROLL | WB_DRAG)));
+ m_pVScroll->Show();
+ m_pVScroll->SetScrollHdl(aLink);
+ }
+ }
+
+ if ( bHasHoriBar || bHasVertBar )
+ {
+ SetStyle( GetStyle() | WB_CLIPCHILDREN );
+ }
+
+ mnScrWidth = Application::GetSettings().GetStyleSettings().GetScrollBarSize();
+
+ ImplInitSettings();
+
+ // if the tabpage is drawn (ie filled) by a native widget, make sure all controls will have transparent background
+ // otherwise they will paint with a wrong background
+ if( IsNativeControlSupported(ControlType::TabBody, ControlPart::Entire) && GetParent() && (GetParent()->GetType() == WindowType::TABCONTROL) )
+ EnableChildTransparentMode();
+}
+
+void TabPage::ImplInitSettings()
+{
+ vcl::Window* pParent = GetParent();
+ if (pParent && pParent->IsChildTransparentModeEnabled() && !IsControlBackground())
+ {
+ EnableChildTransparentMode();
+ SetParentClipMode( ParentClipMode::NoClip );
+ SetPaintTransparent( true );
+ SetBackground();
+ }
+ else
+ {
+ EnableChildTransparentMode( false );
+ SetParentClipMode();
+ SetPaintTransparent( false );
+
+ if (IsControlBackground() || !pParent)
+ SetBackground( GetControlBackground() );
+ else
+ SetBackground( pParent->GetBackground() );
+ }
+}
+
+TabPage::TabPage( vcl::Window* pParent, WinBits nStyle ) :
+ Window( WindowType::TABPAGE )
+{
+ ImplInit( pParent, nStyle );
+}
+
+TabPage::~TabPage()
+{
+ disposeOnce();
+}
+
+void TabPage::dispose()
+{
+ m_pVScroll.disposeAndClear();
+ m_pHScroll.disposeAndClear();
+ vcl::Window::dispose();
+}
+
+void TabPage::StateChanged( StateChangedType nType )
+{
+ Window::StateChanged( nType );
+
+ if ( nType == StateChangedType::InitShow )
+ {
+ if (GetSettings().GetStyleSettings().GetAutoMnemonic())
+ GenerateAutoMnemonicsOnHierarchy(this);
+ // FIXME: no layouting, workaround some clipping issues
+ ImplAdjustNWFSizes();
+ }
+ else if ( nType == StateChangedType::ControlBackground )
+ {
+ ImplInitSettings();
+ Invalidate();
+ }
+}
+
+void TabPage::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ Window::DataChanged( rDCEvt );
+
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ ImplInitSettings();
+ Invalidate();
+ }
+}
+
+void TabPage::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
+{
+ // draw native tabpage only inside tabcontrols, standalone tabpages look ugly (due to bad dialog design)
+ if( !(IsNativeControlSupported(ControlType::TabBody, ControlPart::Entire) && GetParent() && (GetParent()->GetType() == WindowType::TABCONTROL)) )
+ return;
+
+ const ImplControlValue aControlValue;
+
+ ControlState nState = ControlState::ENABLED;
+ if ( !IsEnabled() )
+ nState &= ~ControlState::ENABLED;
+ if ( HasFocus() )
+ nState |= ControlState::FOCUSED;
+ // pass the whole window region to NWF as the tab body might be a gradient or bitmap
+ // that has to be scaled properly, clipping makes sure that we do not paint too much
+ tools::Rectangle aCtrlRegion( Point(), GetOutputSizePixel() );
+ rRenderContext.DrawNativeControl( ControlType::TabBody, ControlPart::Entire, aCtrlRegion, nState,
+ aControlValue, OUString() );
+}
+
+void TabPage::Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags )
+{
+ Size aSize = GetSizePixel();
+
+ Wallpaper aWallpaper = GetBackground();
+ if ( !aWallpaper.IsBitmap() )
+ ImplInitSettings();
+
+ pDev->Push();
+ pDev->SetMapMode();
+ pDev->SetLineColor();
+
+ if ( aWallpaper.IsBitmap() )
+ pDev->DrawBitmapEx( rPos, aSize, aWallpaper.GetBitmap() );
+ else
+ {
+ if( aWallpaper.GetColor() == COL_AUTO )
+ pDev->SetFillColor( GetSettings().GetStyleSettings().GetDialogColor() );
+ else
+ pDev->SetFillColor( aWallpaper.GetColor() );
+ pDev->DrawRect( tools::Rectangle( rPos, aSize ) );
+ }
+
+ pDev->Pop();
+}
+
+Size TabPage::GetOptimalSize() const
+{
+ if (isLayoutEnabled(this))
+ return VclContainer::getLayoutRequisition(*GetWindow(GetWindowType::FirstChild));
+ return getLegacyBestSizeForChildren(*this);
+}
+
+void TabPage::SetPosSizePixel(const Point& rAllocPos, const Size& rAllocation)
+{
+ Window::SetPosSizePixel(rAllocPos, rAllocation);
+ if (isLayoutEnabled(this) && rAllocation.Width() && rAllocation.Height())
+ VclContainer::setLayoutAllocation(*GetWindow(GetWindowType::FirstChild), Point(0, 0), rAllocation);
+}
+
+void TabPage::SetSizePixel(const Size& rAllocation)
+{
+ Window::SetSizePixel(rAllocation);
+ if (isLayoutEnabled(this) && rAllocation.Width() && rAllocation.Height())
+ VclContainer::setLayoutAllocation(*GetWindow(GetWindowType::FirstChild), Point(0, 0), rAllocation);
+}
+
+void TabPage::SetPosPixel(const Point& rAllocPos)
+{
+ Window::SetPosPixel(rAllocPos);
+ Size aAllocation(GetOutputSizePixel());
+ if (isLayoutEnabled(this) && aAllocation.Width() && aAllocation.Height())
+ {
+ VclContainer::setLayoutAllocation(*GetWindow(GetWindowType::FirstChild), Point(0, 0), aAllocation);
+ }
+}
+
+void TabPage::lcl_Scroll( tools::Long nX, tools::Long nY )
+{
+ tools::Long nXScroll = mnScrollPos.X() - nX;
+ tools::Long nYScroll = mnScrollPos.Y() - nY;
+ mnScrollPos = Point( nX, nY );
+
+ tools::Rectangle aScrollableArea( 0, 0, maScrollArea.Width(), maScrollArea.Height() );
+ Scroll(nXScroll, nYScroll, aScrollableArea );
+ // Manually scroll all children ( except the scrollbars )
+ for ( int index = 0; index < GetChildCount(); ++index )
+ {
+ vcl::Window* pChild = GetChild( index );
+ if ( pChild && pChild != m_pVScroll.get() && pChild != m_pHScroll.get() )
+ {
+ Point aPos = pChild->GetPosPixel();
+ aPos += Point( nXScroll, nYScroll );
+ pChild->SetPosPixel( aPos );
+ }
+ }
+}
+
+IMPL_LINK( TabPage, ScrollBarHdl, ScrollBar*, pSB, void )
+{
+ sal_uInt16 nPos = static_cast<sal_uInt16>(pSB->GetThumbPos());
+ if( pSB == m_pVScroll.get() )
+ lcl_Scroll(mnScrollPos.X(), nPos );
+ else if( pSB == m_pHScroll.get() )
+ lcl_Scroll(nPos, mnScrollPos.Y() );
+}
+
+void TabPage::SetScrollTop( tools::Long nTop )
+{
+ Point aOld = mnScrollPos;
+ lcl_Scroll( mnScrollPos.X() , mnScrollPos.Y() - nTop );
+ if( m_pHScroll )
+ m_pHScroll->SetThumbPos( 0 );
+ // new pos is 0,0
+ mnScrollPos = aOld;
+}
+void TabPage::SetScrollLeft( tools::Long nLeft )
+{
+ Point aOld = mnScrollPos;
+ lcl_Scroll( mnScrollPos.X() - nLeft , mnScrollPos.Y() );
+ if( m_pVScroll )
+ m_pVScroll->SetThumbPos( 0 );
+ // new pos is 0,0
+ mnScrollPos = aOld;
+}
+
+void TabPage::SetScrollWidth( tools::Long nWidth )
+{
+ maScrollArea.setWidth( nWidth );
+ ResetScrollBars();
+}
+
+void TabPage::SetScrollHeight( tools::Long nHeight )
+{
+ maScrollArea.setHeight( nHeight );
+ ResetScrollBars();
+}
+
+void TabPage::Resize()
+{
+ ResetScrollBars();
+}
+
+void TabPage::ResetScrollBars()
+{
+ Size aOutSz = GetOutputSizePixel();
+
+ Point aVPos( aOutSz.Width() - mnScrWidth, 0 );
+ Point aHPos( 0, aOutSz.Height() - mnScrWidth );
+
+ if( m_pVScroll )
+ {
+ m_pVScroll->SetPosSizePixel( aVPos, Size( mnScrWidth, GetSizePixel().Height() - mnScrWidth ) );
+ m_pVScroll->SetRangeMax( maScrollArea.Height() + mnScrWidth );
+ m_pVScroll->SetVisibleSize( GetSizePixel().Height() );
+ }
+
+ if( m_pHScroll )
+ {
+ m_pHScroll->SetPosSizePixel( aHPos, Size( GetSizePixel().Width() - mnScrWidth, mnScrWidth ) );
+ m_pHScroll->SetRangeMax( maScrollArea.Width() + mnScrWidth );
+ m_pHScroll->SetVisibleSize( GetSizePixel().Width() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/taskpanelist.cxx b/vcl/source/window/taskpanelist.cxx
new file mode 100644
index 0000000000..16747f0c08
--- /dev/null
+++ b/vcl/source/window/taskpanelist.cxx
@@ -0,0 +1,287 @@
+/* -*- 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 <vcl/dockwin.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/taskpanelist.hxx>
+
+#include <svdata.hxx>
+#include "menubarwindow.hxx"
+
+#include <algorithm>
+
+namespace {
+
+AbsoluteScreenPixelPoint ImplTaskPaneListGetPos( const vcl::Window *w )
+{
+ AbsoluteScreenPixelPoint pos;
+ if( w->IsDockingWindow() )
+ {
+ Point pos1 = static_cast<const DockingWindow*>(w)->GetPosPixel();
+ vcl::Window *pF = static_cast<const DockingWindow*>(w)->GetFloatingWindow();
+ if( pF )
+ pos = pF->OutputToAbsoluteScreenPixel( pF->ScreenToOutputPixel( pos1 ) );
+ else
+ pos = w->OutputToAbsoluteScreenPixel( pos1 );
+ }
+ else
+ pos = w->OutputToAbsoluteScreenPixel( w->GetPosPixel() );
+
+ return pos;
+}
+
+// compares window pos left-to-right
+struct LTRSort
+{
+ bool operator()( const vcl::Window* w1, const vcl::Window* w2 ) const
+ {
+ Point pos1(ImplTaskPaneListGetPos( w1 ));
+ Point pos2(ImplTaskPaneListGetPos( w2 ));
+
+ if( pos1.X() == pos2.X() )
+ return ( pos1.Y() < pos2.Y() );
+ else
+ return ( pos1.X() < pos2.X() );
+ }
+};
+
+}
+
+static void ImplTaskPaneListGrabFocus( vcl::Window *pWindow, bool bForward )
+{
+ // put focus in child of floating windows which is typically a toolbar
+ // that can deal with the focus
+ if( pWindow->ImplIsFloatingWindow() && pWindow->GetWindow( GetWindowType::FirstChild ) )
+ pWindow = pWindow->GetWindow( GetWindowType::FirstChild );
+ pWindow->ImplGrabFocus( GetFocusFlags::F6 | (bForward ? GetFocusFlags::Forward : GetFocusFlags::Backward));
+}
+
+TaskPaneList::TaskPaneList()
+{
+}
+
+TaskPaneList::~TaskPaneList()
+{
+}
+
+void TaskPaneList::AddWindow( vcl::Window *pWindow )
+{
+ if( !pWindow )
+ return;
+
+ auto insertionPos = dynamic_cast<MenuBarWindow*>(pWindow) ? mTaskPanes.begin() : mTaskPanes.end();
+ for ( auto p = mTaskPanes.begin(); p != mTaskPanes.end(); ++p )
+ {
+ if ( *p == pWindow )
+ // avoid duplicates
+ return;
+
+ // If the new window is the child of an existing pane window, or vice versa,
+ // ensure that in our pane list, *first* the child window appears, *then*
+ // the ancestor window.
+ // This is necessary for HandleKeyEvent: There, the list is traveled from the
+ // beginning, until the first window is found which has the ChildPathFocus. Now
+ // if this would be the ancestor window of another pane window, this would fudge
+ // the result
+ if ( pWindow->IsWindowOrChild( *p ) )
+ {
+ insertionPos = p + 1;
+ break;
+ }
+ if ( (*p)->IsWindowOrChild( pWindow ) )
+ {
+ insertionPos = p;
+ break;
+ }
+ }
+
+ mTaskPanes.insert( insertionPos, pWindow );
+ pWindow->ImplIsInTaskPaneList( true );
+}
+
+void TaskPaneList::RemoveWindow( vcl::Window *pWindow )
+{
+ auto p = ::std::find( mTaskPanes.begin(), mTaskPanes.end(), VclPtr<vcl::Window>(pWindow) );
+ if( p != mTaskPanes.end() )
+ {
+ mTaskPanes.erase( p );
+ pWindow->ImplIsInTaskPaneList( false );
+ }
+}
+
+bool TaskPaneList::IsInList( vcl::Window *pWindow )
+{
+ auto p = ::std::find( mTaskPanes.begin(), mTaskPanes.end(), VclPtr<vcl::Window>(pWindow) );
+ return p != mTaskPanes.end();
+}
+
+bool TaskPaneList::IsCycleKey(const vcl::KeyCode& rKeyCode)
+{
+ return rKeyCode.GetCode() == KEY_F6 && !rKeyCode.IsMod2(); // F6
+}
+
+bool TaskPaneList::HandleKeyEvent(const KeyEvent& rKeyEvent)
+{
+
+ // F6 cycles through everything and works always
+
+ // MAV, #i104204#
+ // The old design was the following one:
+ // < Ctrl-TAB cycles through Menubar, Toolbars and Floatingwindows only and is
+ // < only active if one of those items has the focus
+
+ // Since the design of Ctrl-Tab looks to be inconsistent ( non-modal dialogs are not reachable
+ // and the shortcut conflicts with tab-control shortcut ), it is no more supported
+ vcl::KeyCode aKeyCode = rKeyEvent.GetKeyCode();
+ bool bForward = !aKeyCode.IsShift();
+ if (TaskPaneList::IsCycleKey(aKeyCode))
+ {
+ bool bSplitterOnly = aKeyCode.IsMod1() && aKeyCode.IsShift();
+
+ // is the focus in the list ?
+ auto p = std::find_if(mTaskPanes.begin(), mTaskPanes.end(),
+ [](const VclPtr<vcl::Window>& rWinPtr) { return rWinPtr->HasChildPathFocus( true ); });
+ if( p != mTaskPanes.end() )
+ {
+ vcl::Window *pWin = p->get();
+
+ // Ctrl-F6 goes directly to the document
+ if( !pWin->IsDialog() && aKeyCode.IsMod1() && !aKeyCode.IsShift() )
+ {
+ pWin->ImplGrabFocusToDocument( GetFocusFlags::F6 );
+ return true;
+ }
+
+ // activate next task pane
+ vcl::Window *pNextWin = nullptr;
+
+ if( bSplitterOnly )
+ pNextWin = FindNextSplitter( *p );
+ else
+ pNextWin = FindNextFloat( *p, bForward );
+
+ if( pNextWin != pWin )
+ {
+ ImplGetSVData()->mpWinData->mbNoSaveFocus = true;
+ ImplTaskPaneListGrabFocus( pNextWin, bForward );
+ ImplGetSVData()->mpWinData->mbNoSaveFocus = false;
+ }
+ else
+ {
+ // forward key if no splitter found
+ if( bSplitterOnly )
+ return false;
+
+ // we did not find another taskpane, so
+ // put focus back into document
+ pWin->ImplGrabFocusToDocument( GetFocusFlags::F6 | (bForward ? GetFocusFlags::Forward : GetFocusFlags::Backward));
+ }
+
+ return true;
+ }
+
+ // the focus is not in the list: activate first float if F6 was pressed
+ vcl::Window *pWin;
+ if( bSplitterOnly )
+ pWin = FindNextSplitter( nullptr );
+ else
+ pWin = FindNextFloat( nullptr, bForward );
+ if( pWin )
+ {
+ ImplTaskPaneListGrabFocus( pWin, bForward );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// returns next splitter
+vcl::Window* TaskPaneList::FindNextSplitter( vcl::Window *pWindow )
+{
+ ::std::stable_sort( mTaskPanes.begin(), mTaskPanes.end(), LTRSort() );
+
+ auto p = mTaskPanes.begin();
+ if( pWindow )
+ p = std::find(mTaskPanes.begin(), mTaskPanes.end(), pWindow);
+
+ if( p != mTaskPanes.end() )
+ {
+ unsigned n = mTaskPanes.size();
+ while( --n )
+ {
+ if( pWindow ) // increment before test
+ ++p;
+ if( p == mTaskPanes.end() )
+ p = mTaskPanes.begin();
+ if( (*p)->ImplIsSplitter() && (*p)->IsReallyVisible() && !(*p)->IsDialog() && (*p)->GetParent()->HasChildPathFocus() )
+ {
+ pWindow = (*p).get();
+ break;
+ }
+ if( !pWindow ) // increment after test, otherwise first element is skipped
+ ++p;
+ }
+ }
+
+ return pWindow;
+}
+
+// returns first valid item (regardless of type) if pWindow==0, otherwise returns next valid float
+vcl::Window* TaskPaneList::FindNextFloat( vcl::Window *pWindow, bool bForward )
+{
+ ::std::stable_sort( mTaskPanes.begin(), mTaskPanes.end(), LTRSort() );
+
+ if ( !bForward )
+ ::std::reverse( mTaskPanes.begin(), mTaskPanes.end() );
+
+ auto p = mTaskPanes.begin();
+ if( pWindow )
+ p = std::find(mTaskPanes.begin(), mTaskPanes.end(), pWindow);
+
+ while( p != mTaskPanes.end() )
+ {
+ if( pWindow ) // increment before test
+ ++p;
+ if( p == mTaskPanes.end() )
+ break; // do not wrap, send focus back to document at end of list
+ /* #i83908# do not use the menubar if it is native and invisible
+ */
+
+ bool bSkip = false; // used to skip infobar when it has no children
+ if( (*p)->GetType() == WindowType::WINDOW && (*p)->GetChildCount() == 0 )
+ bSkip = true;
+
+ if( !bSkip && (*p)->IsReallyVisible() && !(*p)->ImplIsSplitter() &&
+ ( (*p)->GetType() != WindowType::MENUBARWINDOW || static_cast<MenuBarWindow*>(p->get())->CanGetFocus() ) )
+ {
+ pWindow = (*p).get();
+ break;
+ }
+ if( !pWindow ) // increment after test, otherwise first element is skipped
+ ++p;
+ }
+
+ if ( !bForward )
+ ::std::reverse( mTaskPanes.begin(), mTaskPanes.end() );
+
+ return pWindow;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/toolbox.cxx b/vcl/source/window/toolbox.cxx
new file mode 100644
index 0000000000..fc9effe580
--- /dev/null
+++ b/vcl/source/window/toolbox.cxx
@@ -0,0 +1,4810 @@
+/* -*- 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 <vcl/toolbox.hxx>
+#include <vcl/event.hxx>
+#include <vcl/decoview.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/help.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/gradient.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <bitmaps.hlst>
+#include <toolbarvalue.hxx>
+
+#include <tools/poly.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include <osl/diagnose.h>
+
+#include <accel.hxx>
+#include <svdata.hxx>
+#include <window.h>
+#include <toolbox.h>
+#include <spin.hxx>
+#if defined(_WIN32)
+#include <svsys.h>
+#endif
+
+#include <cstdlib>
+#include <map>
+#include <string_view>
+#include <vector>
+#include <math.h>
+
+#include "impldockingwrapper.hxx"
+
+#define SMALLBUTTON_HSIZE 7
+#define SMALLBUTTON_VSIZE 7
+
+#define SMALLBUTTON_OFF_NORMAL_X 3
+#define SMALLBUTTON_OFF_NORMAL_Y 3
+
+#define TB_TEXTOFFSET 2
+#define TB_IMAGETEXTOFFSET 3
+#define TB_LINESPACING 3
+#define TB_SPIN_SIZE 14
+#define TB_SPIN_OFFSET 2
+#define TB_BORDER_OFFSET1 4
+#define TB_BORDER_OFFSET2 2
+#define TB_MAXLINES 5
+#define TB_MAXNOSCROLL 32765
+
+#define TB_DRAGWIDTH 8 // the default width of the drag grip
+
+#define TB_CALCMODE_HORZ 1
+#define TB_CALCMODE_VERT 2
+#define TB_CALCMODE_FLOAT 3
+
+#define TB_WBLINESIZING (WB_SIZEABLE | WB_DOCKABLE | WB_SCROLL)
+
+#define DOCK_LINEHSIZE (sal_uInt16(0x0001))
+#define DOCK_LINEVSIZE (sal_uInt16(0x0002))
+#define DOCK_LINERIGHT (sal_uInt16(0x1000))
+#define DOCK_LINEBOTTOM (sal_uInt16(0x2000))
+#define DOCK_LINELEFT (sal_uInt16(0x4000))
+#define DOCK_LINETOP (sal_uInt16(0x8000))
+#define DOCK_LINEOFFSET 3
+
+class ImplTBDragMgr
+{
+private:
+ VclPtr<ToolBox> mpDragBox;
+ Point maMouseOff;
+ tools::Rectangle maRect;
+ tools::Rectangle maStartRect;
+ Accelerator maAccel;
+ sal_uInt16 mnLineMode;
+ ToolBox::ImplToolItems::size_type mnStartLines;
+
+ ImplTBDragMgr(const ImplTBDragMgr&) = delete;
+ ImplTBDragMgr& operator=(const ImplTBDragMgr&) = delete;
+
+public:
+ ImplTBDragMgr();
+
+ void StartDragging( ToolBox* pDragBox, const Point& rPos, const tools::Rectangle& rRect, sal_uInt16 nLineMode );
+ void Dragging( const Point& rPos );
+ void EndDragging( bool bOK = true );
+ DECL_LINK( SelectHdl, Accelerator&, void );
+};
+
+
+static ImplTBDragMgr* ImplGetTBDragMgr()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ if ( !pSVData->maCtrlData.mpTBDragMgr )
+ pSVData->maCtrlData.mpTBDragMgr = new ImplTBDragMgr;
+ return pSVData->maCtrlData.mpTBDragMgr;
+}
+
+int ToolBox::ImplGetDragWidth( const vcl::Window& rWindow, bool bHorz )
+{
+ return ImplGetDragWidth(*rWindow.GetOutDev(), bHorz);
+}
+int ToolBox::ImplGetDragWidth( const vcl::RenderContext& rRenderContext, bool bHorz )
+{
+ int nWidth = TB_DRAGWIDTH;
+ if( rRenderContext.IsNativeControlSupported( ControlType::Toolbar, ControlPart::Entire ) )
+ {
+
+ ImplControlValue aControlValue;
+ tools::Rectangle aContent, aBound;
+ tools::Rectangle aArea( Point(), rRenderContext.GetOutputSizePixel() );
+
+ if ( rRenderContext.GetNativeControlRegion(ControlType::Toolbar,
+ bHorz ? ControlPart::ThumbVert : ControlPart::ThumbHorz,
+ aArea, ControlState::NONE, aControlValue, aBound, aContent) )
+ {
+ nWidth = bHorz ? aContent.GetWidth() : aContent.GetHeight();
+ }
+ }
+
+ // increase the hit area of the drag handle according to DPI scale factor
+ nWidth *= rRenderContext.GetDPIScaleFactor();
+
+ return nWidth;
+}
+
+int ToolBox::ImplGetDragWidth() const
+{
+ return ToolBox::ImplGetDragWidth( *this, mbHorz );
+}
+
+static ButtonType determineButtonType( ImplToolItem const * pItem, ButtonType defaultType )
+{
+ ButtonType tmpButtonType = defaultType;
+ ToolBoxItemBits nBits = pItem->mnBits & ( ToolBoxItemBits::TEXT_ONLY | ToolBoxItemBits::ICON_ONLY );
+ if ( nBits != ToolBoxItemBits::NONE ) // item has custom setting
+ {
+ tmpButtonType = ButtonType::SYMBOLTEXT;
+ if ( nBits == ToolBoxItemBits::TEXT_ONLY )
+ tmpButtonType = ButtonType::TEXT;
+ else if ( nBits == ToolBoxItemBits::ICON_ONLY )
+ tmpButtonType = ButtonType::SYMBOLONLY;
+ }
+ return tmpButtonType;
+}
+
+void ToolBox::ImplUpdateDragArea() const
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ {
+ if ( ImplIsFloatingMode() || pWrapper->IsLocked() )
+ pWrapper->SetDragArea( tools::Rectangle() );
+ else
+ {
+ if( meAlign == WindowAlign::Top || meAlign == WindowAlign::Bottom )
+ pWrapper->SetDragArea( tools::Rectangle( 0, 0, ImplGetDragWidth(), GetOutputSizePixel().Height() ) );
+ else
+ pWrapper->SetDragArea( tools::Rectangle( 0, 0, GetOutputSizePixel().Width(), ImplGetDragWidth() ) );
+ }
+ }
+}
+
+void ToolBox::ImplCalcBorder( WindowAlign eAlign, tools::Long& rLeft, tools::Long& rTop,
+ tools::Long& rRight, tools::Long& rBottom ) const
+{
+ if( ImplIsFloatingMode() || !(mnWinStyle & WB_BORDER) )
+ {
+ // no border in floating mode
+ rLeft = rTop = rRight = rBottom = 0;
+ return;
+ }
+
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+
+ // reserve DragArea only for dockable toolbars
+ int dragwidth = ( pWrapper && !pWrapper->IsLocked() ) ? ImplGetDragWidth() : 0;
+
+ // no shadow border for dockable toolbars and toolbars with WB_NOSHADOW bit set, e.g. Calc's formulabar
+ int borderwidth = ( pWrapper || mnWinStyle & WB_NOSHADOW ) ? 0 : 2;
+
+ if ( eAlign == WindowAlign::Top )
+ {
+ rLeft = borderwidth+dragwidth;
+ rTop = borderwidth;
+ rRight = borderwidth;
+ rBottom = 0;
+ }
+ else if ( eAlign == WindowAlign::Left )
+ {
+ rLeft = borderwidth;
+ rTop = borderwidth+dragwidth;
+ rRight = 0;
+ rBottom = borderwidth;
+ }
+ else if ( eAlign == WindowAlign::Bottom )
+ {
+ rLeft = borderwidth+dragwidth;
+ rTop = 0;
+ rRight = borderwidth;
+ rBottom = borderwidth;
+ }
+ else
+ {
+ rLeft = 0;
+ rTop = borderwidth+dragwidth;
+ rRight = borderwidth;
+ rBottom = borderwidth;
+ }
+}
+
+void ToolBox::ImplCheckUpdate()
+{
+ // remove any pending invalidates to avoid
+ // have them triggered when paint is locked (see mpData->mbIsPaintLocked)
+ // which would result in erasing the background only and not painting any items
+ // this must not be done when we're already in Paint()
+
+ // this is only required for transparent toolbars (see ImplDrawTransparentBackground() )
+ if( !IsBackground() && HasPaintEvent() && !IsInPaint() )
+ PaintImmediately();
+}
+
+void ToolBox::ImplDrawGrip(vcl::RenderContext& rRenderContext,
+ const tools::Rectangle &aDragArea, int nDragWidth, WindowAlign eAlign, bool bHorz)
+{
+ bool bNativeOk = false;
+ const ControlPart ePart = bHorz ? ControlPart::ThumbVert : ControlPart::ThumbHorz;
+ const Size aSz( rRenderContext.GetOutputSizePixel() );
+ if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ePart))
+ {
+ ToolbarValue aToolbarValue;
+ aToolbarValue.maGripRect = aDragArea;
+
+ tools::Rectangle aCtrlRegion(Point(), aSz);
+
+ bNativeOk = rRenderContext.DrawNativeControl( ControlType::Toolbar, ePart,
+ aCtrlRegion, ControlState::ENABLED, aToolbarValue, OUString() );
+ }
+
+ if( bNativeOk )
+ return;
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ rRenderContext.SetLineColor(rStyleSettings.GetShadowColor());
+ rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
+
+ float fScaleFactor = rRenderContext.GetDPIScaleFactor();
+
+ if (eAlign == WindowAlign::Top || eAlign == WindowAlign::Bottom)
+ {
+ int height = static_cast<int>(0.6 * aSz.Height() + 0.5);
+ int i = (aSz.Height() - height) / 2;
+ height += i;
+ while (i <= height)
+ {
+ int x = nDragWidth / 2;
+ rRenderContext.DrawEllipse(tools::Rectangle(Point(x, i), Size(2 * fScaleFactor, 2 * fScaleFactor)));
+ i += 4 * fScaleFactor;
+ }
+ }
+ else
+ {
+ int width = static_cast<int>(0.6 * aSz.Width() + 0.5);
+ int i = (aSz.Width() - width) / 2;
+ width += i;
+ while (i <= width)
+ {
+ int y = nDragWidth / 2;
+ rRenderContext.DrawEllipse(tools::Rectangle(Point(i, y), Size(2 * fScaleFactor, 2 * fScaleFactor)));
+ i += 4 * fScaleFactor;
+ }
+ }
+}
+
+void ToolBox::ImplDrawGrip(vcl::RenderContext& rRenderContext)
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper(this);
+ if( pWrapper && !pWrapper->GetDragArea().IsEmpty() )
+ {
+ // execute pending paint requests
+ ImplCheckUpdate();
+ ImplDrawGrip( rRenderContext, pWrapper->GetDragArea(),
+ ImplGetDragWidth(), meAlign, mbHorz );
+ }
+}
+
+void ToolBox::ImplDrawGradientBackground(vcl::RenderContext& rRenderContext)
+{
+ // draw a nice gradient
+
+ Color startCol, endCol;
+ const StyleSettings rSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ startCol = rSettings.GetFaceGradientColor();
+ endCol = rSettings.GetFaceColor();
+ if (rSettings.GetHighContrastMode())
+ // no 'extreme' gradient when high contrast
+ startCol = endCol;
+
+ Gradient g;
+ g.SetAngle(Degree10(mbHorz ? 0 : 900));
+ g.SetStyle(css::awt::GradientStyle_LINEAR);
+
+ g.SetStartColor(startCol);
+ g.SetEndColor(endCol);
+
+ bool bLineColor = rRenderContext.IsLineColor();
+ Color aOldCol = rRenderContext.GetLineColor();
+ rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetShadowColor());
+
+ Size aFullSz(GetOutputSizePixel());
+ Size aLineSz(aFullSz);
+
+ // use the linesize only when floating
+ // full window height is used when docked (single line)
+ if (ImplIsFloatingMode())
+ {
+ tools::Long nLineSize;
+ if (mbHorz)
+ {
+ nLineSize = mnMaxItemHeight;
+ if (mnWinHeight > mnMaxItemHeight)
+ nLineSize = mnWinHeight;
+
+ aLineSz.setHeight( nLineSize );
+ }
+ else
+ {
+ nLineSize = mnMaxItemWidth;
+ aLineSz.setWidth( nLineSize );
+ }
+ }
+
+ tools::Long nLeft, nTop, nRight, nBottom;
+ ImplCalcBorder(meAlign, nLeft, nTop, nRight, nBottom);
+
+ Size aTopLineSz(aLineSz);
+ Size aBottomLineSz(aLineSz);
+
+ if (mnWinStyle & WB_BORDER)
+ {
+ if (mbHorz)
+ {
+ aTopLineSz.AdjustHeight(TB_BORDER_OFFSET2 + nTop );
+ aBottomLineSz.AdjustHeight(TB_BORDER_OFFSET2 + nBottom );
+
+ if (mnCurLines == 1)
+ aTopLineSz.AdjustHeight(TB_BORDER_OFFSET2 + nBottom );
+ }
+ else
+ {
+ aTopLineSz.AdjustWidth(TB_BORDER_OFFSET1 + nLeft );
+ aBottomLineSz.AdjustWidth(TB_BORDER_OFFSET1 + nRight );
+
+ if (mnCurLines == 1)
+ aTopLineSz.AdjustWidth(TB_BORDER_OFFSET1 + nLeft );
+ }
+ }
+
+ if (mbLineSpacing)
+ {
+ if (mbHorz)
+ {
+ aLineSz.AdjustHeight(TB_LINESPACING );
+ if (mnCurLines > 1)
+ aTopLineSz.AdjustHeight(TB_LINESPACING );
+ }
+ else
+ {
+ aLineSz.AdjustWidth(TB_LINESPACING );
+ if (mnCurLines > 1)
+ aTopLineSz.AdjustWidth(TB_LINESPACING );
+ }
+ }
+
+ if (mbHorz)
+ {
+ tools::Long y = 0;
+
+ rRenderContext.DrawGradient(tools::Rectangle(0, y, aTopLineSz.Width(), y + aTopLineSz.Height()), g);
+ y += aTopLineSz.Height();
+
+ while (y < (mnDY - aBottomLineSz.Height()))
+ {
+ rRenderContext.DrawGradient(tools::Rectangle(0, y, aLineSz.Width(), y + aLineSz.Height()), g);
+ y += aLineSz.Height();
+ }
+
+ rRenderContext.DrawGradient(tools::Rectangle(0, y, aBottomLineSz.Width(), y + aBottomLineSz.Height()), g);
+ }
+ else
+ {
+ tools::Long x = 0;
+
+ rRenderContext.DrawGradient(tools::Rectangle(x, 0, x + aTopLineSz.Width(), aTopLineSz.Height()), g);
+ x += aTopLineSz.Width();
+
+ while (x < (mnDX - aBottomLineSz.Width()))
+ {
+ rRenderContext.DrawGradient(tools::Rectangle(x, 0, x + aLineSz.Width(), aLineSz.Height()), g);
+ x += aLineSz.Width();
+ }
+
+ rRenderContext.DrawGradient(tools::Rectangle( x, 0, x + aBottomLineSz.Width(), aBottomLineSz.Height()), g);
+ }
+
+ if( bLineColor )
+ rRenderContext.SetLineColor( aOldCol );
+
+}
+
+bool ToolBox::ImplDrawNativeBackground(vcl::RenderContext& rRenderContext) const
+{
+ // use NWF
+ tools::Rectangle aCtrlRegion(Point(), GetOutputSizePixel());
+
+ return rRenderContext.DrawNativeControl( ControlType::Toolbar, mbHorz ? ControlPart::DrawBackgroundHorz : ControlPart::DrawBackgroundVert,
+ aCtrlRegion, ControlState::ENABLED, ImplControlValue(), OUString() );
+}
+
+void ToolBox::ImplDrawTransparentBackground(const vcl::Region &rRegion)
+{
+ // just invalidate to trigger paint of the parent
+ const bool bOldPaintLock = mpData->mbIsPaintLocked;
+ mpData->mbIsPaintLocked = true;
+
+ // send an invalidate to the first opaque parent and invalidate the whole hierarchy from there (noclipchildren)
+ Invalidate(rRegion, InvalidateFlags::Update | InvalidateFlags::NoClipChildren);
+
+ mpData->mbIsPaintLocked = bOldPaintLock;
+}
+
+void ToolBox::ImplDrawConstantBackground(vcl::RenderContext& rRenderContext, const vcl::Region &rRegion, bool bIsInPopupMode)
+{
+ // draw a constant color
+ if (!bIsInPopupMode)
+ {
+ // default background
+ rRenderContext.Erase(rRegion.GetBoundRect());
+ }
+ else
+ {
+ // use different color in popupmode
+ const StyleSettings rSettings = rRenderContext.GetSettings().GetStyleSettings();
+ Wallpaper aWallpaper(rSettings.GetFaceGradientColor());
+ rRenderContext.DrawWallpaper(rRegion.GetBoundRect(), aWallpaper);
+ }
+}
+
+void ToolBox::ImplDrawBackground(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ // execute pending paint requests
+ ImplCheckUpdate();
+
+ ImplDockingWindowWrapper* pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper(this);
+ bool bIsInPopupMode = ImplIsInPopupMode();
+
+ vcl::Region aPaintRegion(rRect);
+
+ // make sure we do not invalidate/erase too much
+ if (IsInPaint())
+ aPaintRegion.Intersect(GetOutDev()->GetActiveClipRegion());
+
+ rRenderContext.Push(vcl::PushFlags::CLIPREGION);
+ rRenderContext.IntersectClipRegion( aPaintRegion );
+
+ if (!pWrapper)
+ {
+ // no gradient for ordinary toolbars (not dockable)
+ if( !IsBackground() && !IsInPaint() )
+ ImplDrawTransparentBackground(aPaintRegion);
+ else
+ ImplDrawConstantBackground(rRenderContext, aPaintRegion, bIsInPopupMode);
+ }
+ else
+ {
+ // toolbars known to the dockingmanager will be drawn using NWF or a gradient
+ // docked toolbars are transparent and NWF is already used in the docking area which is their common background
+ // so NWF is used here for floating toolbars only
+ bool bNativeOk = false;
+ if( ImplIsFloatingMode() && rRenderContext.IsNativeControlSupported( ControlType::Toolbar, ControlPart::Entire) )
+ bNativeOk = ImplDrawNativeBackground(rRenderContext);
+ if (!bNativeOk)
+ {
+ const StyleSettings rSetting = Application::GetSettings().GetStyleSettings();
+ const bool isHeader = GetAlign() == WindowAlign::Top && !rSetting.GetPersonaHeader().IsEmpty();
+ const bool isFooter = GetAlign() == WindowAlign::Bottom && !rSetting.GetPersonaFooter().IsEmpty();
+ if (!IsBackground() || isHeader || isFooter)
+ {
+ if (!IsInPaint())
+ ImplDrawTransparentBackground(aPaintRegion);
+ }
+ else
+ ImplDrawGradientBackground(rRenderContext);
+ }
+ }
+
+ // restore clip region
+ rRenderContext.Pop();
+}
+
+void ToolBox::ImplErase(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect, bool bHighlight, bool bHasOpenPopup)
+{
+ // the background of non NWF buttons is painted in a constant color
+ // to have the same highlight color (transparency in DrawSelectionBackground())
+ // items with open popups will also painted using a constant color
+ if (!mpData->mbNativeButtons &&
+ (bHighlight || !(GetStyle() & WB_3DLOOK)))
+ {
+ if (GetStyle() & WB_3DLOOK)
+ {
+ rRenderContext.Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
+ rRenderContext.SetLineColor();
+ if (bHasOpenPopup)
+ // choose the same color as the popup will use
+ rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetFaceColor());
+ else
+ rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor());
+
+ rRenderContext.DrawRect(rRect);
+ rRenderContext.Pop();
+ }
+ else
+ ImplDrawBackground(rRenderContext, rRect);
+ }
+ else
+ ImplDrawBackground(rRenderContext, rRect);
+}
+
+void ToolBox::ImplDrawBorder(vcl::RenderContext& rRenderContext)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ tools::Long nDX = mnDX;
+ tools::Long nDY = mnDY;
+
+ ImplDockingWindowWrapper* pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper(this);
+
+ // draw borders for ordinary toolbars only (not dockable), do not draw borders for toolbars with WB_NOSHADOW bit set,
+ // e.g. Calc's formulabar
+
+ if( pWrapper || mnWinStyle & WB_NOSHADOW )
+ return;
+
+ if (meAlign == WindowAlign::Bottom)
+ {
+ // draw bottom border
+ rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
+ rRenderContext.DrawLine( Point( 0, nDY-2 ), Point( nDX-1, nDY-2 ) );
+ rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
+ rRenderContext.DrawLine( Point( 0, nDY-1 ), Point( nDX-1, nDY-1 ) );
+ }
+ else
+ {
+ // draw top border
+ rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
+ rRenderContext.DrawLine( Point( 0, 0 ), Point( nDX-1, 0 ) );
+ rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
+ rRenderContext.DrawLine( Point( 0, 1 ), Point( nDX-1, 1 ) );
+
+ if (meAlign == WindowAlign::Left || meAlign == WindowAlign::Right)
+ {
+ if (meAlign == WindowAlign::Left)
+ {
+ // draw left-bottom border
+ rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
+ rRenderContext.DrawLine( Point( 0, 0 ), Point( 0, nDY-1 ) );
+ rRenderContext.DrawLine( Point( 0, nDY-2 ), Point( nDX-1, nDY-2 ) );
+ rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
+ rRenderContext.DrawLine( Point( 1, 1 ), Point( 1, nDY-3 ) );
+ rRenderContext.DrawLine( Point( 0, nDY-1 ), Point( nDX-1, nDY-1 ) );
+ }
+ else
+ {
+ // draw right-bottom border
+ rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
+ rRenderContext.DrawLine( Point( nDX-2, 0 ), Point( nDX-2, nDY-3 ) );
+ rRenderContext.DrawLine( Point( 0, nDY-2 ), Point( nDX-2, nDY-2 ) );
+ rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
+ rRenderContext.DrawLine( Point( nDX-1, 0 ), Point( nDX-1, nDY-1 ) );
+ rRenderContext.DrawLine( Point( 0, nDY-1 ), Point( nDX-1, nDY-1 ) );
+ }
+ }
+ }
+
+ if ( meAlign == WindowAlign::Bottom || meAlign == WindowAlign::Top )
+ {
+ // draw right border
+ rRenderContext.SetLineColor( rStyleSettings.GetShadowColor() );
+ rRenderContext.DrawLine( Point( nDX-2, 0 ), Point( nDX-2, nDY-1 ) );
+ rRenderContext.SetLineColor( rStyleSettings.GetLightColor() );
+ rRenderContext.DrawLine( Point( nDX-1, 0 ), Point( nDX-1, nDY-1 ) );
+ }
+}
+
+static bool ImplIsFixedControl( const ImplToolItem *pItem )
+{
+ return ( pItem->mpWindow &&
+ (pItem->mbNonInteractiveWindow ||
+ pItem->mpWindow->GetType() == WindowType::FIXEDTEXT ||
+ pItem->mpWindow->GetType() == WindowType::FIXEDLINE ||
+ pItem->mpWindow->GetType() == WindowType::GROUPBOX) );
+}
+
+const ImplToolItem *ToolBox::ImplGetFirstClippedItem() const
+{
+ for (auto & item : mpData->m_aItems)
+ {
+ if( item.IsClipped() )
+ return &item;
+ }
+ return nullptr;
+}
+
+Size ToolBox::ImplCalcSize( ImplToolItems::size_type nCalcLines, sal_uInt16 nCalcMode )
+{
+ sal_Int32 nMax;
+ tools::Long nLeft = 0;
+ tools::Long nTop = 0;
+ tools::Long nRight = 0;
+ tools::Long nBottom = 0;
+ Size aSize;
+ WindowAlign eOldAlign = meAlign;
+ bool bOldHorz = mbHorz;
+ bool bOldAssumeDocked = mpData->mbAssumeDocked;
+ bool bOldAssumeFloating = mpData->mbAssumeFloating;
+
+ if ( nCalcMode )
+ {
+ bool bOldFloatingMode = ImplIsFloatingMode();
+
+ mpData->mbAssumeDocked = false;
+ mpData->mbAssumeFloating = false;
+
+ if ( nCalcMode == TB_CALCMODE_HORZ )
+ {
+ mpData->mbAssumeDocked = true; // force non-floating mode during calculation
+ ImplCalcBorder( WindowAlign::Top, nLeft, nTop, nRight, nBottom );
+ mbHorz = true;
+ if ( mbHorz != bOldHorz )
+ meAlign = WindowAlign::Top;
+ }
+ else if ( nCalcMode == TB_CALCMODE_VERT )
+ {
+ mpData->mbAssumeDocked = true; // force non-floating mode during calculation
+ ImplCalcBorder( WindowAlign::Left, nLeft, nTop, nRight, nBottom );
+ mbHorz = false;
+ if ( mbHorz != bOldHorz )
+ meAlign = WindowAlign::Left;
+ }
+ else if ( nCalcMode == TB_CALCMODE_FLOAT )
+ {
+ mpData->mbAssumeFloating = true; // force non-floating mode during calculation
+ nLeft = nTop = nRight = nBottom = 0;
+ mbHorz = true;
+ if ( mbHorz != bOldHorz )
+ meAlign = WindowAlign::Top;
+ }
+
+ if ( (meAlign != eOldAlign) || (mbHorz != bOldHorz) ||
+ (ImplIsFloatingMode() != bOldFloatingMode ) )
+ mbCalc = true;
+ }
+ else
+ ImplCalcBorder( meAlign, nLeft, nTop, nRight, nBottom );
+
+ ImplCalcItem();
+
+ if( !nCalcMode && ImplIsFloatingMode() )
+ {
+ aSize = ImplCalcFloatSize( nCalcLines );
+ }
+ else
+ {
+ if ( mbHorz )
+ {
+ if ( mnWinHeight > mnMaxItemHeight )
+ aSize.setHeight( nCalcLines * mnWinHeight );
+ else
+ aSize.setHeight( nCalcLines * mnMaxItemHeight );
+
+ if ( mbLineSpacing )
+ aSize.AdjustHeight((nCalcLines-1)*TB_LINESPACING );
+
+ if ( mnWinStyle & WB_BORDER )
+ aSize.AdjustHeight((TB_BORDER_OFFSET2*2) + nTop + nBottom );
+
+ nMax = 0;
+ ImplCalcBreaks( TB_MAXNOSCROLL, &nMax, mbHorz );
+ if ( nMax )
+ aSize.AdjustWidth(nMax );
+
+ if ( mnWinStyle & WB_BORDER )
+ aSize.AdjustWidth((TB_BORDER_OFFSET1*2) + nLeft + nRight );
+ }
+ else
+ {
+ aSize.setWidth( nCalcLines * mnMaxItemWidth );
+
+ if ( mbLineSpacing )
+ aSize.AdjustWidth((nCalcLines-1)*TB_LINESPACING );
+
+ if ( mnWinStyle & WB_BORDER )
+ aSize.AdjustWidth((TB_BORDER_OFFSET2*2) + nLeft + nRight );
+
+ nMax = 0;
+ ImplCalcBreaks( TB_MAXNOSCROLL, &nMax, mbHorz );
+ if ( nMax )
+ aSize.AdjustHeight(nMax );
+
+ if ( mnWinStyle & WB_BORDER )
+ aSize.AdjustHeight((TB_BORDER_OFFSET1*2) + nTop + nBottom );
+ }
+ }
+ // restore previous values
+ if ( nCalcMode )
+ {
+ mpData->mbAssumeDocked = bOldAssumeDocked;
+ mpData->mbAssumeFloating = bOldAssumeFloating;
+ if ( (meAlign != eOldAlign) || (mbHorz != bOldHorz) )
+ {
+ meAlign = eOldAlign;
+ mbHorz = bOldHorz;
+ mbCalc = true;
+ }
+ }
+
+ return aSize;
+}
+
+void ToolBox::ImplCalcFloatSizes()
+{
+ if ( !maFloatSizes.empty() )
+ return;
+
+ // calculate the minimal size, i.e. where the biggest item just fits
+ tools::Long nCalcSize = 0;
+
+ for (auto const& item : mpData->m_aItems)
+ {
+ if ( item.mbVisible )
+ {
+ if ( item.mpWindow )
+ {
+ tools::Long nTempSize = item.mpWindow->GetSizePixel().Width();
+ if ( nTempSize > nCalcSize )
+ nCalcSize = nTempSize;
+ }
+ else
+ {
+ if( item.maItemSize.Width() > nCalcSize )
+ nCalcSize = item.maItemSize.Width();
+ }
+ }
+ }
+
+ // calc an upper bound for ImplCalcBreaks below
+ tools::Long upperBoundWidth = nCalcSize * mpData->m_aItems.size();
+
+ ImplToolItems::size_type nLines;
+ ImplToolItems::size_type nCalcLines;
+ ImplToolItems::size_type nTempLines;
+ sal_Int32 nMaxLineWidth;
+ nCalcLines = ImplCalcBreaks( nCalcSize, &nMaxLineWidth, true );
+
+ maFloatSizes.reserve( nCalcLines );
+
+ nTempLines = nLines = nCalcLines;
+ while ( nLines )
+ {
+ tools::Long nHeight = ImplCalcSize( nTempLines, TB_CALCMODE_FLOAT ).Height();
+
+ ImplToolSize aSize;
+ aSize.mnWidth = nMaxLineWidth+(TB_BORDER_OFFSET1*2);
+ aSize.mnHeight = nHeight;
+ aSize.mnLines = nTempLines;
+ maFloatSizes.push_back( aSize );
+ nLines--;
+ if ( nLines )
+ {
+ do
+ {
+ nCalcSize += mnMaxItemWidth;
+ nTempLines = ImplCalcBreaks( nCalcSize, &nMaxLineWidth, true );
+ }
+ while ((nCalcSize < upperBoundWidth) && (nLines < nTempLines)); // implies nTempLines>1
+ if ( nTempLines < nLines )
+ nLines = nTempLines;
+ }
+ }
+}
+
+Size ToolBox::ImplCalcFloatSize( ImplToolItems::size_type& rLines )
+{
+ ImplCalcFloatSizes();
+
+ if ( !rLines )
+ {
+ rLines = mnFloatLines;
+ if ( !rLines )
+ rLines = mnLines;
+ }
+
+ sal_uInt16 i = 0;
+ while ( i + 1u < maFloatSizes.size() && rLines < maFloatSizes[i].mnLines )
+ {
+ i++;
+ }
+
+ Size aSize( maFloatSizes[i].mnWidth, maFloatSizes[i].mnHeight );
+ rLines = maFloatSizes[i].mnLines;
+
+ return aSize;
+}
+
+void ToolBox::ImplCalcMinMaxFloatSize( Size& rMinSize, Size& rMaxSize )
+{
+ ImplCalcFloatSizes();
+
+ sal_uInt16 i = 0;
+ rMinSize = Size( maFloatSizes[i].mnWidth, maFloatSizes[i].mnHeight );
+ rMaxSize = Size( maFloatSizes[i].mnWidth, maFloatSizes[i].mnHeight );
+ while ( ++i < maFloatSizes.size() )
+ {
+ if( maFloatSizes[i].mnWidth < rMinSize.Width() )
+ rMinSize.setWidth( maFloatSizes[i].mnWidth );
+ if( maFloatSizes[i].mnHeight < rMinSize.Height() )
+ rMinSize.setHeight( maFloatSizes[i].mnHeight );
+
+ if( maFloatSizes[i].mnWidth > rMaxSize.Width() )
+ rMaxSize.setWidth( maFloatSizes[i].mnWidth );
+ if( maFloatSizes[i].mnHeight > rMaxSize.Height() )
+ rMaxSize.setHeight( maFloatSizes[i].mnHeight );
+ }
+}
+
+void ToolBox::ImplSetMinMaxFloatSize()
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ Size aMinSize, aMaxSize;
+ ImplCalcMinMaxFloatSize( aMinSize, aMaxSize );
+ if( pWrapper )
+ {
+ pWrapper->SetMinOutputSizePixel( aMinSize );
+ pWrapper->SetMaxOutputSizePixel( aMaxSize );
+ pWrapper->ShowMenuTitleButton( bool( GetMenuType() & ToolBoxMenuType::Customize) );
+ }
+ else
+ {
+ // TODO: change SetMinOutputSizePixel to be not inline
+ SetMinOutputSizePixel( aMinSize );
+ SetMaxOutputSizePixel( aMaxSize );
+ }
+}
+
+ToolBox::ImplToolItems::size_type ToolBox::ImplCalcLines( tools::Long nToolSize ) const
+{
+ tools::Long nLineHeight;
+
+ if ( mbHorz )
+ {
+ if ( mnWinHeight > mnMaxItemHeight )
+ nLineHeight = mnWinHeight;
+ else
+ nLineHeight = mnMaxItemHeight;
+ }
+ else
+ nLineHeight = mnMaxItemWidth;
+
+ if ( mnWinStyle & WB_BORDER )
+ nToolSize -= TB_BORDER_OFFSET2*2;
+
+ if ( mbLineSpacing )
+ {
+ nLineHeight += TB_LINESPACING;
+ nToolSize += TB_LINESPACING;
+ }
+
+ // #i91917# always report at least one line
+ tools::Long nLines = nToolSize/nLineHeight;
+ if( nLines < 1 )
+ nLines = 1;
+
+ return nLines;
+}
+
+sal_uInt16 ToolBox::ImplTestLineSize( const Point& rPos ) const
+{
+ if ( !ImplIsFloatingMode() &&
+ (!mbScroll || (mnLines > 1) || (mnCurLines > mnVisLines)) )
+ {
+ WindowAlign eAlign = GetAlign();
+
+ if ( eAlign == WindowAlign::Left )
+ {
+ if ( rPos.X() > mnDX-DOCK_LINEOFFSET )
+ return DOCK_LINEHSIZE | DOCK_LINERIGHT;
+ }
+ else if ( eAlign == WindowAlign::Top )
+ {
+ if ( rPos.Y() > mnDY-DOCK_LINEOFFSET )
+ return DOCK_LINEVSIZE | DOCK_LINEBOTTOM;
+ }
+ else if ( eAlign == WindowAlign::Right )
+ {
+ if ( rPos.X() < DOCK_LINEOFFSET )
+ return DOCK_LINEHSIZE | DOCK_LINELEFT;
+ }
+ else if ( eAlign == WindowAlign::Bottom )
+ {
+ if ( rPos.Y() < DOCK_LINEOFFSET )
+ return DOCK_LINEVSIZE | DOCK_LINETOP;
+ }
+ }
+
+ return 0;
+}
+
+void ToolBox::ImplLineSizing( const Point& rPos, tools::Rectangle& rRect, sal_uInt16 nLineMode )
+{
+ bool bHorz;
+ tools::Long nOneLineSize;
+ tools::Long nCurSize;
+ tools::Long nMaxSize;
+ tools::Long nSize;
+ Size aSize;
+
+ if ( nLineMode & DOCK_LINERIGHT )
+ {
+ nCurSize = rPos.X() - rRect.Left();
+ bHorz = false;
+ }
+ else if ( nLineMode & DOCK_LINEBOTTOM )
+ {
+ nCurSize = rPos.Y() - rRect.Top();
+ bHorz = true;
+ }
+ else if ( nLineMode & DOCK_LINELEFT )
+ {
+ nCurSize = rRect.Right() - rPos.X();
+ bHorz = false;
+ }
+ else if ( nLineMode & DOCK_LINETOP )
+ {
+ nCurSize = rRect.Bottom() - rPos.Y();
+ bHorz = true;
+ }
+ else {
+ OSL_FAIL( "ImplLineSizing: Trailing else" );
+ nCurSize = 0;
+ bHorz = false;
+ }
+
+ Size aWinSize = GetSizePixel();
+ ImplToolItems::size_type nMaxLines = std::max(mnLines, mnCurLines);
+ if ( nMaxLines > TB_MAXLINES )
+ nMaxLines = TB_MAXLINES;
+ if ( bHorz )
+ {
+ nOneLineSize = ImplCalcSize( 1 ).Height();
+ nMaxSize = - 20;
+ if ( nMaxSize < aWinSize.Height() )
+ nMaxSize = aWinSize.Height();
+ }
+ else
+ {
+ nOneLineSize = ImplCalcSize( 1 ).Width();
+ nMaxSize = - 20;
+ if ( nMaxSize < aWinSize.Width() )
+ nMaxSize = aWinSize.Width();
+ }
+
+ ImplToolItems::size_type i = 1;
+ if ( nCurSize <= nOneLineSize )
+ nSize = nOneLineSize;
+ else
+ {
+ nSize = 0;
+ while ( (nSize < nCurSize) && (i < nMaxLines) )
+ {
+ i++;
+ aSize = ImplCalcSize( i );
+ if ( bHorz )
+ nSize = aSize.Height();
+ else
+ nSize = aSize.Width();
+ if ( nSize > nMaxSize )
+ {
+ i--;
+ aSize = ImplCalcSize( i );
+ if ( bHorz )
+ nSize = aSize.Height();
+ else
+ nSize = aSize.Width();
+ break;
+ }
+ }
+ }
+
+ if ( nLineMode & DOCK_LINERIGHT )
+ rRect.SetRight( rRect.Left()+nSize-1 );
+ else if ( nLineMode & DOCK_LINEBOTTOM )
+ rRect.SetBottom( rRect.Top()+nSize-1 );
+ else if ( nLineMode & DOCK_LINELEFT )
+ rRect.SetLeft( rRect.Right()-nSize );
+ else
+ rRect.SetTop( rRect.Bottom()-nSize );
+
+ mnDockLines = i;
+}
+
+ImplTBDragMgr::ImplTBDragMgr()
+ : mpDragBox(nullptr)
+ , mnLineMode(0)
+ , mnStartLines(0)
+{
+ maAccel.InsertItem( KEY_RETURN, vcl::KeyCode( KEY_RETURN ) );
+ maAccel.InsertItem( KEY_ESCAPE, vcl::KeyCode( KEY_ESCAPE ) );
+ maAccel.SetSelectHdl( LINK( this, ImplTBDragMgr, SelectHdl ) );
+}
+
+void ImplTBDragMgr::StartDragging( ToolBox* pToolBox,
+ const Point& rPos, const tools::Rectangle& rRect,
+ sal_uInt16 nDragLineMode )
+{
+ mpDragBox = pToolBox;
+ pToolBox->CaptureMouse();
+ pToolBox->mbDragging = true;
+ Application::InsertAccel( &maAccel );
+
+ mnLineMode = nDragLineMode;
+ mnStartLines = pToolBox->mnDockLines;
+
+ // calculate MouseOffset
+ maMouseOff.setX( rRect.Left() - rPos.X() );
+ maMouseOff.setY( rRect.Top() - rPos.Y() );
+ maRect = rRect;
+ maStartRect = rRect;
+ pToolBox->ShowTracking( maRect );
+}
+
+void ImplTBDragMgr::Dragging( const Point& rPos )
+{
+ mpDragBox->ImplLineSizing( rPos, maRect, mnLineMode );
+ Point aOff = mpDragBox->OutputToScreenPixel( Point() );
+ maRect.Move( aOff.X(), aOff.Y() );
+ mpDragBox->Docking( rPos, maRect );
+ maRect.Move( -aOff.X(), -aOff.Y() );
+ mpDragBox->ShowTracking( maRect );
+}
+
+void ImplTBDragMgr::EndDragging( bool bOK )
+{
+ mpDragBox->HideTracking();
+ if (mpDragBox->IsMouseCaptured())
+ mpDragBox->ReleaseMouse();
+ mpDragBox->mbDragging = false;
+ Application::RemoveAccel( &maAccel );
+
+ if ( !bOK )
+ {
+ mpDragBox->mnDockLines = mnStartLines;
+ mpDragBox->EndDocking( maStartRect, false );
+ }
+ else
+ mpDragBox->EndDocking( maRect, false );
+ mnStartLines = 0;
+
+ mpDragBox = nullptr;
+}
+
+IMPL_LINK( ImplTBDragMgr, SelectHdl, Accelerator&, rAccel, void )
+{
+ if ( rAccel.GetCurItemId() == KEY_ESCAPE )
+ EndDragging( false );
+ else
+ EndDragging();
+}
+
+void ToolBox::ImplInitToolBoxData()
+{
+ // initialize variables
+ ImplGetWindowImpl()->mbToolBox = true;
+ mpData.reset(new ImplToolBoxPrivateData);
+
+ mpFloatWin = nullptr;
+ mnDX = 0;
+ mnDY = 0;
+ mnMaxItemWidth = 0;
+ mnMaxItemHeight = 0;
+ mnWinHeight = 0;
+ mnLeftBorder = 0;
+ mnTopBorder = 0;
+ mnRightBorder = 0;
+ mnBottomBorder = 0;
+ mnLastResizeDY = 0;
+ mnHighItemId = ToolBoxItemId(0);
+ mnCurItemId = ToolBoxItemId(0);
+ mnDownItemId = ToolBoxItemId(0);
+ mnCurPos = ITEM_NOTFOUND;
+ mnLines = 1;
+ mnCurLine = 1;
+ mnCurLines = 1;
+ mnVisLines = 1;
+ mnFloatLines = 0;
+ mnDockLines = 0;
+ mnMouseModifier = 0;
+ mbDrag = false;
+ mbUpper = false;
+ mbLower = false;
+ mbIn = false;
+ mbCalc = true;
+ mbFormat = false;
+ mbFullPaint = false;
+ mbHorz = true;
+ mbScroll = false;
+ mbLastFloatMode = false;
+ mbCustomize = false;
+ mbDragging = false;
+ mbIsKeyEvent = false;
+ mbChangingHighlight = false;
+ mbLineSpacing = false;
+ mbIsArranged = false;
+ meButtonType = ButtonType::SYMBOLONLY;
+ meAlign = WindowAlign::Top;
+ meDockAlign = WindowAlign::Top;
+ meLastStyle = PointerStyle::Arrow;
+ mnWinStyle = 0;
+ meLayoutMode = ToolBoxLayoutMode::Normal;
+ meTextPosition = ToolBoxTextPosition::Right;
+ mnLastFocusItemId = ToolBoxItemId(0);
+ mnActivateCount = 0;
+
+ mpIdle.reset(new Idle("vcl::ToolBox maIdle update"));
+ mpIdle->SetPriority( TaskPriority::RESIZE );
+ mpIdle->SetInvokeHandler( LINK( this, ToolBox, ImplUpdateHdl ) );
+
+ // set timeout and handler for dropdown items
+ mpData->maDropdownTimer.SetTimeout( 250 );
+ mpData->maDropdownTimer.SetInvokeHandler( LINK( this, ToolBox, ImplDropdownLongClickHdl ) );
+}
+
+void ToolBox::ImplInit( vcl::Window* pParent, WinBits nStyle )
+{
+ // initialize variables
+ mbScroll = (nStyle & WB_SCROLL) != 0;
+ mnWinStyle = nStyle;
+
+ DockingWindow::ImplInit( pParent, nStyle & ~WB_BORDER );
+
+ // dockingwindow's ImplInit removes some bits, so restore them here to allow keyboard handling for toolbars
+ ImplGetWindowImpl()->mnStyle |= WB_TABSTOP|WB_NODIALOGCONTROL; // always set WB_TABSTOP for ToolBars
+ ImplGetWindowImpl()->mnStyle &= ~WB_DIALOGCONTROL;
+
+ ImplInitSettings(true, true, true);
+}
+
+void ToolBox::ApplyForegroundSettings(vcl::RenderContext& rRenderContext, const StyleSettings& rStyleSettings)
+{
+ Color aColor;
+ if (IsControlForeground())
+ aColor = GetControlForeground();
+ else if (Window::GetStyle() & WB_3DLOOK)
+ aColor = rStyleSettings.GetButtonTextColor();
+ else
+ aColor = rStyleSettings.GetWindowTextColor();
+ rRenderContext.SetTextColor(aColor);
+ rRenderContext.SetTextFillColor();
+}
+
+void ToolBox::ApplyBackgroundSettings(vcl::RenderContext& rRenderContext, const StyleSettings& rStyleSettings)
+{
+ if (IsControlBackground())
+ {
+ rRenderContext.SetBackground(GetControlBackground());
+ SetPaintTransparent(false);
+ SetParentClipMode();
+ }
+ else
+ {
+ if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Entire)
+ || (GetAlign() == WindowAlign::Top && !Application::GetSettings().GetStyleSettings().GetPersonaHeader().IsEmpty())
+ || (GetAlign() == WindowAlign::Bottom && !Application::GetSettings().GetStyleSettings().GetPersonaFooter().IsEmpty()))
+ {
+ rRenderContext.SetBackground();
+ rRenderContext.SetTextColor(rStyleSettings.GetToolTextColor());
+ SetPaintTransparent(true);
+ SetParentClipMode(ParentClipMode::NoClip);
+ mpData->maDisplayBackground = Wallpaper(rStyleSettings.GetFaceColor());
+ }
+ else
+ {
+ Color aColor;
+ if (Window::GetStyle() & WB_3DLOOK)
+ aColor = rStyleSettings.GetFaceColor();
+ else
+ aColor = rStyleSettings.GetWindowColor();
+ rRenderContext.SetBackground(aColor);
+ SetPaintTransparent(false);
+ SetParentClipMode();
+ }
+ }
+}
+
+void ToolBox::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ mpData->mbNativeButtons = rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Button);
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ ApplyControlFont(rRenderContext, rStyleSettings.GetToolFont());
+ ApplyForegroundSettings(rRenderContext, rStyleSettings);
+ ApplyBackgroundSettings(rRenderContext, rStyleSettings);
+}
+
+void ToolBox::ImplInitSettings(bool bFont, bool bForeground, bool bBackground)
+{
+ mpData->mbNativeButtons = IsNativeControlSupported( ControlType::Toolbar, ControlPart::Button );
+
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+
+ if (bFont)
+ ApplyControlFont(*GetOutDev(), rStyleSettings.GetToolFont());
+ if (bForeground || bFont)
+ ApplyForegroundSettings(*GetOutDev(), rStyleSettings);
+ if (bBackground)
+ {
+ ApplyBackgroundSettings(*GetOutDev(), rStyleSettings);
+ EnableChildTransparentMode(IsPaintTransparent());
+ }
+}
+
+void ToolBox::doDeferredInit(WinBits nBits)
+{
+ VclPtr<vcl::Window> pParent = mpDialogParent;
+ mpDialogParent = nullptr;
+ ImplInit(pParent, nBits);
+ mbIsDeferredInit = false;
+}
+
+void ToolBox::queue_resize(StateChangedType eReason)
+{
+ Window::queue_resize(eReason);
+}
+
+ToolBox::ToolBox( vcl::Window* pParent, WinBits nStyle ) :
+ DockingWindow( WindowType::TOOLBOX, "vcl::ToolBox maLayoutIdle" )
+{
+ ImplInitToolBoxData();
+ ImplInit( pParent, nStyle );
+}
+
+ToolBox::ToolBox(vcl::Window* pParent, const OUString& rID,
+ const OUString& rUIXMLDescription, const css::uno::Reference<css::frame::XFrame> &rFrame)
+ : DockingWindow(WindowType::TOOLBOX, "vcl::ToolBox maLayoutIdle")
+{
+ ImplInitToolBoxData();
+
+ loadUI(pParent, rID, rUIXMLDescription, rFrame);
+
+ // calculate size of floating windows and switch if the
+ // toolbox is initially in floating mode
+ if ( ImplIsFloatingMode() )
+ mbHorz = true;
+ else
+ Resize();
+
+ if (!(GetStyle() & WB_HIDE))
+ Show();
+}
+
+ToolBox::~ToolBox()
+{
+ disposeOnce();
+}
+
+void ToolBox::dispose()
+{
+ // #103005# make sure our activate/deactivate balance is right
+ while( mnActivateCount > 0 )
+ Deactivate();
+
+ // terminate popupmode if the floating window is
+ // still connected
+ if ( mpFloatWin )
+ mpFloatWin->EndPopupMode( FloatWinPopupEndFlags::Cancel );
+ mpFloatWin = nullptr;
+
+ // delete private data
+ mpData.reset();
+
+ ImplSVData* pSVData = ImplGetSVData();
+ delete pSVData->maCtrlData.mpTBDragMgr;
+ pSVData->maCtrlData.mpTBDragMgr = nullptr;
+
+ mpFloatWin.clear();
+
+ mpIdle.reset();
+
+ DockingWindow::dispose();
+}
+
+ImplToolItem* ToolBox::ImplGetItem( ToolBoxItemId nItemId ) const
+{
+ if (!mpData)
+ return nullptr;
+
+ for (auto & item : mpData->m_aItems)
+ {
+ if ( item.mnId == nItemId )
+ return &item;
+ }
+
+ return nullptr;
+}
+
+static void ImplAddButtonBorder( tools::Long &rWidth, tools::Long& rHeight, bool bNativeButtons )
+{
+ rWidth += SMALLBUTTON_HSIZE;
+ rHeight += SMALLBUTTON_VSIZE;
+
+ if( bNativeButtons )
+ {
+ // give more border space for rounded buttons
+ rWidth += 2;
+ rHeight += 4;
+ }
+}
+
+bool ToolBox::ImplCalcItem()
+{
+ // recalc required ?
+ if ( !mbCalc )
+ return false;
+
+ OutputDevice *pDefault = Application::GetDefaultDevice();
+ float fScaleFactor = pDefault ? pDefault->GetDPIScaleFactor() : 1.0;
+
+ tools::Long nDefWidth;
+ tools::Long nDefHeight;
+ tools::Long nMaxWidth = 0;
+ tools::Long nMaxHeight = 0;
+ tools::Long nMinWidth = 6;
+ tools::Long nMinHeight = 6;
+ tools::Long nDropDownArrowWidth = TB_DROPDOWNARROWWIDTH * fScaleFactor;
+#ifdef IOS
+ nDropDownArrowWidth *= 3;
+#endif
+
+ // set defaults if image or text is needed but empty
+ nDefWidth = GetDefaultImageSize().Width();
+ nDefHeight = GetDefaultImageSize().Height();
+
+ mnWinHeight = 0;
+ // determine minimum size necessary in NWF
+ {
+ tools::Rectangle aRect( Point( 0, 0 ), Size( nMinWidth, nMinHeight ) );
+ tools::Rectangle aReg( aRect );
+ ImplControlValue aVal;
+ tools::Rectangle aNativeBounds, aNativeContent;
+ if( IsNativeControlSupported( ControlType::Toolbar, ControlPart::Button ) )
+ {
+ if( GetNativeControlRegion( ControlType::Toolbar, ControlPart::Button,
+ aReg,
+ ControlState::ENABLED | ControlState::ROLLOVER,
+ aVal,
+ aNativeBounds, aNativeContent ) )
+ {
+ aRect = aNativeBounds;
+ if( aRect.GetWidth() > nMinWidth )
+ nMinWidth = aRect.GetWidth();
+ if( aRect.GetHeight() > nMinHeight )
+ nMinHeight = aRect.GetHeight();
+ if( nDropDownArrowWidth < nMinWidth )
+ nDropDownArrowWidth = nMinWidth;
+ if( nMinWidth > mpData->mnMenuButtonWidth )
+ mpData->mnMenuButtonWidth = nMinWidth;
+ else if( nMinWidth < TB_MENUBUTTON_SIZE )
+ mpData->mnMenuButtonWidth = TB_MENUBUTTON_SIZE;
+ }
+ }
+
+ // also calculate the area for comboboxes, drop down list boxes and spinfields
+ // as these are often inserted into toolboxes; set mnWinHeight to the
+ // greater of those values to prevent toolbar flickering (#i103385#)
+ aRect = tools::Rectangle( Point( 0, 0 ), Size( nMinWidth, nMinHeight ) );
+ aReg = aRect;
+ if( GetNativeControlRegion( ControlType::Combobox, ControlPart::Entire,
+ aReg,
+ ControlState::ENABLED | ControlState::ROLLOVER,
+ aVal,
+ aNativeBounds, aNativeContent ) )
+ {
+ aRect = aNativeBounds;
+ if( aRect.GetHeight() > mnWinHeight )
+ mnWinHeight = aRect.GetHeight();
+ }
+ aRect = tools::Rectangle( Point( 0, 0 ), Size( nMinWidth, nMinHeight ) );
+ aReg = aRect;
+ if( GetNativeControlRegion( ControlType::Listbox, ControlPart::Entire,
+ aReg,
+ ControlState::ENABLED | ControlState::ROLLOVER,
+ aVal,
+ aNativeBounds, aNativeContent ) )
+ {
+ aRect = aNativeBounds;
+ if( aRect.GetHeight() > mnWinHeight )
+ mnWinHeight = aRect.GetHeight();
+ }
+ aRect = tools::Rectangle( Point( 0, 0 ), Size( nMinWidth, nMinHeight ) );
+ aReg = aRect;
+ if( GetNativeControlRegion( ControlType::Spinbox, ControlPart::Entire,
+ aReg,
+ ControlState::ENABLED | ControlState::ROLLOVER,
+ aVal,
+ aNativeBounds, aNativeContent ) )
+ {
+ aRect = aNativeBounds;
+ if( aRect.GetHeight() > mnWinHeight )
+ mnWinHeight = aRect.GetHeight();
+ }
+ }
+
+ if ( ! mpData->m_aItems.empty() )
+ {
+ for (auto & item : mpData->m_aItems)
+ {
+ item.mbVisibleText = false; // indicates if text will definitely be drawn, influences dropdown pos
+
+ if ( item.meType == ToolBoxItemType::BUTTON )
+ {
+ bool bImage;
+ bool bText;
+
+ // check if image and/or text exists
+ bImage = !!item.maImage;
+ bText = !item.maText.isEmpty();
+ ButtonType tmpButtonType = determineButtonType( &item, meButtonType ); // default to toolbox setting
+ if ( bImage || bText )
+ {
+
+ item.mbEmptyBtn = false;
+
+ if ( tmpButtonType == ButtonType::SYMBOLONLY )
+ {
+ // we're drawing images only
+ if ( bImage || !bText )
+ {
+ item.maItemSize = item.maImage.GetSizePixel();
+ }
+ else
+ {
+ item.maItemSize = Size( GetOutDev()->GetCtrlTextWidth( item.maText )+TB_TEXTOFFSET,
+ GetTextHeight() );
+ item.mbVisibleText = true;
+ }
+ }
+ else if ( tmpButtonType == ButtonType::TEXT )
+ {
+ // we're drawing text only
+ if ( bText || !bImage )
+ {
+ item.maItemSize = Size( GetOutDev()->GetCtrlTextWidth( item.maText )+TB_TEXTOFFSET,
+ GetTextHeight() );
+ item.mbVisibleText = true;
+ }
+ else
+ {
+ item.maItemSize = item.maImage.GetSizePixel();
+ }
+ }
+ else
+ {
+ // we're drawing images and text
+ item.maItemSize.setWidth( bText ? GetOutDev()->GetCtrlTextWidth( item.maText )+TB_TEXTOFFSET : 0 );
+ item.maItemSize.setHeight( bText ? GetTextHeight() : 0 );
+
+ if ( meTextPosition == ToolBoxTextPosition::Right )
+ {
+ // leave space between image and text
+ if( bText )
+ item.maItemSize.AdjustWidth(TB_IMAGETEXTOFFSET );
+
+ // image and text side by side
+ item.maItemSize.AdjustWidth(item.maImage.GetSizePixel().Width() );
+ if ( item.maImage.GetSizePixel().Height() > item.maItemSize.Height() )
+ item.maItemSize.setHeight( item.maImage.GetSizePixel().Height() );
+ }
+ else
+ {
+ // leave space between image and text
+ if( bText )
+ item.maItemSize.AdjustHeight(TB_IMAGETEXTOFFSET );
+
+ // text below image
+ item.maItemSize.AdjustHeight(item.maImage.GetSizePixel().Height() );
+ if ( item.maImage.GetSizePixel().Width() > item.maItemSize.Width() )
+ item.maItemSize.setWidth( item.maImage.GetSizePixel().Width() );
+ }
+
+ item.mbVisibleText = bText;
+ }
+ }
+ else
+ { // no image and no text
+ item.maItemSize = Size( nDefWidth, nDefHeight );
+ item.mbEmptyBtn = true;
+ }
+
+ // save the content size
+ item.maContentSize = item.maItemSize;
+
+ // if required, take window height into consideration
+ if ( item.mpWindow )
+ {
+ tools::Long nHeight = item.mpWindow->GetSizePixel().Height();
+ if ( nHeight > mnWinHeight )
+ mnWinHeight = nHeight;
+ }
+
+ // add in drop down arrow
+ if( item.mnBits & ToolBoxItemBits::DROPDOWN )
+ {
+ item.maItemSize.AdjustWidth(nDropDownArrowWidth );
+ item.mnDropDownArrowWidth = nDropDownArrowWidth;
+ }
+
+ // text items will be rotated in vertical mode
+ // -> swap width and height
+ if( item.mbVisibleText && !mbHorz )
+ {
+ tools::Long tmp = item.maItemSize.Width();
+ item.maItemSize.setWidth( item.maItemSize.Height() );
+ item.maItemSize.setHeight( tmp );
+
+ tmp = item.maContentSize.Width();
+ item.maContentSize.setWidth( item.maContentSize.Height() );
+ item.maContentSize.setHeight( tmp );
+ }
+ }
+ else if ( item.meType == ToolBoxItemType::SPACE )
+ {
+ item.maItemSize = Size( nDefWidth, nDefHeight );
+ item.maContentSize = item.maItemSize;
+ }
+
+ if ( item.meType == ToolBoxItemType::BUTTON || item.meType == ToolBoxItemType::SPACE )
+ {
+ // add borders
+ tools::Long w = item.maItemSize.Width();
+ tools::Long h = item.maItemSize.Height();
+ ImplAddButtonBorder( w, h, mpData->mbNativeButtons );
+ item.maItemSize.setWidth(w);
+ item.maItemSize.setHeight(h);
+
+ if( item.meType == ToolBoxItemType::BUTTON )
+ {
+ tools::Long nMinW = std::max(nMinWidth, item.maMinimalItemSize.Width());
+ tools::Long nMinH = std::max(nMinHeight, item.maMinimalItemSize.Height());
+
+ tools::Long nGrowContentWidth = 0;
+ tools::Long nGrowContentHeight = 0;
+
+ if( item.maItemSize.Width() < nMinW )
+ {
+ nGrowContentWidth = nMinW - item.maItemSize.Width();
+ item.maItemSize.setWidth( nMinW );
+ }
+ if( item.maItemSize.Height() < nMinH )
+ {
+ nGrowContentHeight = nMinH - item.maItemSize.Height();
+ item.maItemSize.setHeight( nMinH );
+ }
+
+ // grow the content size by the additional available space
+ item.maContentSize.AdjustWidth(nGrowContentWidth );
+ item.maContentSize.AdjustHeight(nGrowContentHeight );
+ }
+
+ // keep track of max item size
+ if ( item.maItemSize.Width() > nMaxWidth )
+ nMaxWidth = item.maItemSize.Width();
+ if ( item.maItemSize.Height() > nMaxHeight )
+ nMaxHeight = item.maItemSize.Height();
+ }
+ }
+ }
+ else
+ {
+ nMaxWidth = nDefWidth;
+ nMaxHeight = nDefHeight;
+
+ ImplAddButtonBorder( nMaxWidth, nMaxHeight, mpData->mbNativeButtons );
+ }
+
+ if( !ImplIsFloatingMode() && GetToolboxButtonSize() != ToolBoxButtonSize::DontCare
+ && ( meTextPosition == ToolBoxTextPosition::Right ) )
+ {
+ // make sure all vertical toolbars have the same width and horizontal have the same height
+ // this depends on the used button sizes
+ // as this is used for alignment of multiple toolbars
+ // it is only required for docked toolbars
+
+ tools::Long nFixedWidth = nDefWidth+nDropDownArrowWidth;
+ tools::Long nFixedHeight = nDefHeight;
+ ImplAddButtonBorder( nFixedWidth, nFixedHeight, mpData->mbNativeButtons );
+
+ if( mbHorz )
+ nMaxHeight = nFixedHeight;
+ else
+ nMaxWidth = nFixedWidth;
+ }
+
+ mbCalc = false;
+ mbFormat = true;
+
+ // do we have to recalc the sizes ?
+ if ( (nMaxWidth != mnMaxItemWidth) || (nMaxHeight != mnMaxItemHeight) )
+ {
+ mnMaxItemWidth = nMaxWidth;
+ mnMaxItemHeight = nMaxHeight;
+
+ return true;
+ }
+ else
+ return false;
+}
+
+ToolBox::ImplToolItems::size_type ToolBox::ImplCalcBreaks( tools::Long nWidth, sal_Int32* pMaxLineWidth, bool bCalcHorz ) const
+{
+ sal_uLong nLineStart = 0;
+ sal_uLong nGroupStart = 0;
+ tools::Long nLineWidth = 0;
+ tools::Long nCurWidth;
+ tools::Long nLastGroupLineWidth = 0;
+ tools::Long nMaxLineWidth = 0;
+ ImplToolItems::size_type nLines = 1;
+ bool bWindow;
+ bool bBreak = false;
+ tools::Long nWidthTotal = nWidth;
+ tools::Long nMenuWidth = 0;
+
+ // when docked the menubutton will be in the first line
+ if( IsMenuEnabled() && !ImplIsFloatingMode() )
+ nMenuWidth = mpData->maMenubuttonItem.maItemSize.Width();
+
+ // we need to know which item is the last visible one to be able to add
+ // the menu width in case we are unable to show all the items
+ ImplToolItems::iterator it, lastVisible;
+ for ( it = mpData->m_aItems.begin(); it != mpData->m_aItems.end(); ++it )
+ {
+ if ( it->mbVisible )
+ lastVisible = it;
+ }
+
+ it = mpData->m_aItems.begin();
+ while ( it != mpData->m_aItems.end() )
+ {
+ it->mbBreak = bBreak;
+ bBreak = false;
+
+ if ( it->mbVisible )
+ {
+ bWindow = false;
+ bBreak = false;
+ nCurWidth = 0;
+
+ if ( it->meType == ToolBoxItemType::BUTTON || it->meType == ToolBoxItemType::SPACE )
+ {
+ if ( bCalcHorz )
+ nCurWidth = it->maItemSize.Width();
+ else
+ nCurWidth = it->maItemSize.Height();
+
+ if ( it->mpWindow && bCalcHorz )
+ {
+ tools::Long nWinItemWidth = it->mpWindow->GetSizePixel().Width();
+ if ( !mbScroll || (nWinItemWidth <= nWidthTotal) )
+ {
+ nCurWidth = nWinItemWidth;
+ bWindow = true;
+ }
+ else
+ {
+ if ( it->mbEmptyBtn )
+ {
+ nCurWidth = 0;
+ }
+ }
+ }
+
+ // in case we are able to show all the items, we do not want
+ // to show the toolbar's menu; otherwise yes
+ if ( ( ( it == lastVisible ) && (nLineWidth+nCurWidth > nWidthTotal) && mbScroll ) ||
+ ( ( it != lastVisible ) && (nLineWidth+nCurWidth+nMenuWidth > nWidthTotal) && mbScroll ) )
+ bBreak = true;
+ }
+ else if ( it->meType == ToolBoxItemType::SEPARATOR )
+ {
+ nCurWidth = it->mnSepSize;
+ if ( !ImplIsFloatingMode() && ( it != lastVisible ) && (nLineWidth+nCurWidth+nMenuWidth > nWidthTotal) )
+ bBreak = true;
+ }
+ // treat breaks as separators, except when using old style toolbars (ie. no menu button)
+ else if ( (it->meType == ToolBoxItemType::BREAK) && !IsMenuEnabled() )
+ bBreak = true;
+
+ if ( bBreak )
+ {
+ nLines++;
+
+ // Add break before the entire group or take group apart?
+ if ( (it->meType == ToolBoxItemType::BREAK) ||
+ (nLineStart == nGroupStart) )
+ {
+ if ( nLineWidth > nMaxLineWidth )
+ nMaxLineWidth = nLineWidth;
+
+ nLineWidth = 0;
+ nLineStart = it - mpData->m_aItems.begin();
+ nGroupStart = nLineStart;
+ it->mbBreak = true;
+ bBreak = false;
+ }
+ else
+ {
+ if ( nLastGroupLineWidth > nMaxLineWidth )
+ nMaxLineWidth = nLastGroupLineWidth;
+
+ // if the break is added before the group, set it to
+ // beginning of line and re-calculate
+ nLineWidth = 0;
+ nLineStart = nGroupStart;
+ it = mpData->m_aItems.begin() + nGroupStart;
+ continue;
+ }
+ }
+ else
+ {
+ if( ImplIsFloatingMode() || !IsMenuEnabled() ) // no group breaking when being docked single-line
+ {
+ if ( (it->meType != ToolBoxItemType::BUTTON) || bWindow )
+ {
+ // found separator or break
+ nLastGroupLineWidth = nLineWidth;
+ nGroupStart = it - mpData->m_aItems.begin();
+ if ( !bWindow )
+ nGroupStart++;
+ }
+ }
+ }
+
+ nLineWidth += nCurWidth;
+ }
+
+ ++it;
+ }
+
+ if ( pMaxLineWidth )
+ {
+ if ( nLineWidth > nMaxLineWidth )
+ nMaxLineWidth = nLineWidth;
+
+ if( ImplIsFloatingMode() && !ImplIsInPopupMode() )
+ {
+ // leave enough space to display buttons in the decoration
+ tools::Long aMinWidth = 2 * GetSettings().GetStyleSettings().GetFloatTitleHeight();
+ if( nMaxLineWidth < aMinWidth )
+ nMaxLineWidth = aMinWidth;
+ }
+ *pMaxLineWidth = nMaxLineWidth;
+ }
+
+ return nLines;
+}
+
+Size ToolBox::ImplGetOptimalFloatingSize()
+{
+ if( !ImplIsFloatingMode() )
+ return Size();
+
+ Size aCurrentSize( mnDX, mnDY );
+ Size aSize1( aCurrentSize );
+ Size aSize2( aCurrentSize );
+
+ // try to preserve current height
+
+ // calc number of floating lines for current window height
+ ImplToolItems::size_type nFloatLinesHeight = ImplCalcLines( mnDY );
+ // calc window size according to this number
+ aSize1 = ImplCalcFloatSize( nFloatLinesHeight );
+
+ if( aCurrentSize == aSize1 )
+ return aSize1;
+
+ // try to preserve current width
+
+ tools::Long nLineHeight = std::max( mnWinHeight, mnMaxItemHeight );
+ int nBorderX = 2*TB_BORDER_OFFSET1 + mnLeftBorder + mnRightBorder;
+ int nBorderY = 2*TB_BORDER_OFFSET2 + mnTopBorder + mnBottomBorder;
+ Size aSz( aCurrentSize );
+ sal_Int32 maxX;
+ ImplToolItems::size_type nLines = ImplCalcBreaks( aSz.Width()-nBorderX, &maxX, mbHorz );
+
+ ImplToolItems::size_type manyLines = 1000;
+ Size aMinimalFloatSize = ImplCalcFloatSize( manyLines );
+
+ aSz.setHeight( nBorderY + nLineHeight * nLines );
+ // line space when more than one line
+ if ( mbLineSpacing )
+ aSz.AdjustHeight((nLines-1)*TB_LINESPACING );
+
+ aSz.setWidth( nBorderX + maxX );
+
+ // avoid clipping of any items
+ if( aSz.Width() < aMinimalFloatSize.Width() )
+ aSize2 = ImplCalcFloatSize( nLines );
+ else
+ aSize2 = aSz;
+
+ if( aCurrentSize == aSize2 )
+ return aSize2;
+
+ // set the size with the smallest delta as the current size
+ tools::Long dx1 = std::abs( mnDX - aSize1.Width() );
+ tools::Long dy1 = std::abs( mnDY - aSize1.Height() );
+
+ tools::Long dx2 = std::abs( mnDX - aSize2.Width() );
+ tools::Long dy2 = std::abs( mnDY - aSize2.Height() );
+
+ if( dx1*dy1 < dx2*dy2 )
+ aCurrentSize = aSize1;
+ else
+ aCurrentSize = aSize2;
+
+ return aCurrentSize;
+}
+
+namespace
+{
+void lcl_hideDoubleSeparators( ToolBox::ImplToolItems& rItems )
+{
+ bool bLastSep( true );
+ ToolBox::ImplToolItems::iterator it;
+ for ( it = rItems.begin(); it != rItems.end(); ++it )
+ {
+ if ( it->meType == ToolBoxItemType::SEPARATOR )
+ {
+ it->mbVisible = false;
+ if ( !bLastSep )
+ {
+ // check if any visible items have to appear behind it
+ if (std::any_of(it + 1, rItems.end(), [](const ImplToolItem& rItem) {
+ return (rItem.meType == ToolBoxItemType::BUTTON) && rItem.mbVisible; }))
+ it->mbVisible = true;
+ }
+ bLastSep = true;
+ }
+ else if ( it->mbVisible )
+ bLastSep = false;
+ }
+}
+}
+
+void ToolBox::ImplFormat( bool bResize )
+{
+ // Has to re-formatted
+ if ( !mbFormat )
+ return;
+
+ mpData->ImplClearLayoutData();
+
+ // recalculate positions and sizes
+ tools::Rectangle aEmptyRect;
+ tools::Long nLineSize;
+ tools::Long nLeft;
+ tools::Long nTop;
+ tools::Long nMax; // width of layoutarea in pixels
+ ImplToolItems::size_type nFormatLine;
+ bool bMustFullPaint;
+
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ bool bIsInPopupMode = ImplIsInPopupMode();
+
+ maFloatSizes.clear();
+
+ // compute border sizes
+ ImplCalcBorder( meAlign, mnLeftBorder, mnTopBorder, mnRightBorder, mnBottomBorder );
+
+ // update drag area (where the 'grip' will be placed)
+ tools::Rectangle aOldDragRect;
+ if( pWrapper )
+ aOldDragRect = pWrapper->GetDragArea();
+ ImplUpdateDragArea();
+
+ bMustFullPaint = ImplCalcItem();
+
+ // calculate new size during interactive resize or
+ // set computed size when formatting only
+ if ( ImplIsFloatingMode() )
+ {
+ if ( bResize )
+ mnFloatLines = ImplCalcLines( mnDY );
+ else
+ SetOutputSizePixel( ImplGetOptimalFloatingSize() );
+ }
+
+ // Horizontal
+ if ( mbHorz )
+ {
+ tools::Long nBottom;
+ // nLineSize: height of a single line, will fit highest item
+ nLineSize = mnMaxItemHeight;
+
+ if ( mnWinHeight > mnMaxItemHeight )
+ nLineSize = mnWinHeight;
+
+ if ( mbScroll )
+ {
+ nMax = mnDX;
+ mnVisLines = ImplCalcLines( mnDY );
+ }
+ else
+ {
+ // layout over all lines
+ mnVisLines = mnLines;
+ nMax = TB_MAXNOSCROLL;
+ }
+
+ // add in all border offsets
+ if ( mnWinStyle & WB_BORDER )
+ {
+ nLeft = TB_BORDER_OFFSET1 + mnLeftBorder;
+ nTop = TB_BORDER_OFFSET2 + mnTopBorder;
+ nBottom = TB_BORDER_OFFSET1 + mnBottomBorder;
+ nMax -= nLeft + TB_BORDER_OFFSET1 + mnRightBorder;
+ }
+ else
+ {
+ nLeft = 0;
+ nTop = 0;
+ nBottom = 0;
+ }
+
+ // adjust linesize if docked in single-line mode (i.e. when using a clipped item menu)
+ // we have to center all items in the window height
+ if( IsMenuEnabled() && !ImplIsFloatingMode() )
+ {
+ tools::Long nWinHeight = mnDY - nTop - nBottom;
+ if( nWinHeight > nLineSize )
+ nLineSize = nWinHeight;
+ }
+ }
+ else
+ {
+ tools::Long nRight;
+ nLineSize = mnMaxItemWidth;
+
+ if ( mbScroll )
+ {
+ mnVisLines = ImplCalcLines( mnDX );
+ nMax = mnDY;
+ }
+ else
+ {
+ mnVisLines = mnLines;
+ nMax = TB_MAXNOSCROLL;
+ }
+
+ if ( mnWinStyle & WB_BORDER )
+ {
+ nTop = TB_BORDER_OFFSET1 + mnTopBorder;
+ nLeft = TB_BORDER_OFFSET2 + mnLeftBorder;
+ nRight = TB_BORDER_OFFSET2 + mnRightBorder;
+ nMax -= nTop + TB_BORDER_OFFSET1 + mnBottomBorder;
+ }
+ else
+ {
+ nLeft = 0;
+ nTop = 0;
+ nRight = 0;
+ }
+
+ // adjust linesize if docked in single-line mode (i.e. when using a clipped item menu)
+ // we have to center all items in the window height
+ if( !ImplIsFloatingMode() && IsMenuEnabled() )
+ {
+ tools::Long nWinWidth = mnDX - nLeft - nRight;
+ if( nWinWidth > nLineSize )
+ nLineSize = nWinWidth;
+ }
+ }
+
+ // no calculation if the window has no size (nMax=0)
+ // non scrolling toolboxes must be computed though
+ if ( (nMax <= 0) && mbScroll )
+ {
+ mnVisLines = 1;
+ mnCurLine = 1;
+ mnCurLines = 1;
+
+ for (auto & item : mpData->m_aItems)
+ {
+ item.maRect = aEmptyRect;
+ }
+
+ maLowerRect = aEmptyRect;
+ maUpperRect = aEmptyRect;
+ }
+ else
+ {
+ // init start values
+ tools::Long nX = nLeft; // top-left offset
+ tools::Long nY = nTop;
+ nFormatLine = 1;
+
+ // save old scroll rectangles and reset them
+ tools::Rectangle aOldLowerRect = maLowerRect;
+ tools::Rectangle aOldUpperRect = maUpperRect;
+ tools::Rectangle aOldMenubuttonRect = mpData->maMenubuttonItem.maRect;
+ maUpperRect = aEmptyRect;
+ maLowerRect = aEmptyRect;
+ mpData->maMenubuttonItem.maRect = aEmptyRect;
+
+ // do we have any toolbox items at all ?
+ if ( !mpData->m_aItems.empty() || IsMenuEnabled() )
+ {
+ lcl_hideDoubleSeparators( mpData->m_aItems );
+
+ // compute line breaks and visible lines give the current window width (nMax)
+ // the break indicators will be stored within each item (it->mbBreak)
+ mnCurLines = ImplCalcBreaks( nMax, nullptr, mbHorz );
+
+ // check for scrollbar buttons or dropdown menu
+ // (if a menu is enabled, this will be used to store clipped
+ // items and no scroll buttons will appear)
+ if ( (!ImplIsFloatingMode() && (mnCurLines > mnVisLines) && mbScroll ) ||
+ IsMenuEnabled() )
+ {
+ // compute linebreaks again, incorporating scrollbar buttons
+ if( !IsMenuEnabled() )
+ {
+ nMax -= TB_SPIN_SIZE+TB_SPIN_OFFSET;
+ mnCurLines = ImplCalcBreaks( nMax, nullptr, mbHorz );
+ }
+
+ // compute scroll rectangles or menu button
+ if ( mbHorz )
+ {
+ if( IsMenuEnabled() && !ImplHasExternalMenubutton() && !bIsInPopupMode )
+ {
+ if( !ImplIsFloatingMode() )
+ {
+ mpData->maMenubuttonItem.maRect.SetRight( mnDX - 2 );
+ mpData->maMenubuttonItem.maRect.SetTop( nTop );
+ mpData->maMenubuttonItem.maRect.SetBottom( mnDY-mnBottomBorder-TB_BORDER_OFFSET2-1 );
+ }
+ else
+ {
+ mpData->maMenubuttonItem.maRect.SetRight( mnDX - mnRightBorder-TB_BORDER_OFFSET1-1 );
+ mpData->maMenubuttonItem.maRect.SetTop( nTop );
+ mpData->maMenubuttonItem.maRect.SetBottom( mnDY-mnBottomBorder-TB_BORDER_OFFSET2-1 );
+ }
+ mpData->maMenubuttonItem.maRect.SetLeft( mpData->maMenubuttonItem.maRect.Right() - mpData->mnMenuButtonWidth );
+ }
+ else
+ {
+ maUpperRect.SetLeft( nLeft+nMax+TB_SPIN_OFFSET );
+ maUpperRect.SetRight( maUpperRect.Left()+TB_SPIN_SIZE-1 );
+ maUpperRect.SetTop( nTop );
+ maLowerRect.SetBottom( mnDY-mnBottomBorder-TB_BORDER_OFFSET2-1 );
+ maLowerRect.SetLeft( maUpperRect.Left() );
+ maLowerRect.SetRight( maUpperRect.Right() );
+ maUpperRect.SetBottom( maUpperRect.Top() +
+ (maLowerRect.Bottom()-maUpperRect.Top())/2 );
+ maLowerRect.SetTop( maUpperRect.Bottom() );
+ }
+ }
+ else
+ {
+ if( IsMenuEnabled() && !ImplHasExternalMenubutton() && !bIsInPopupMode )
+ {
+ if( !ImplIsFloatingMode() )
+ {
+ mpData->maMenubuttonItem.maRect.SetBottom( mnDY - 2 );
+ mpData->maMenubuttonItem.maRect.SetLeft( nLeft );
+ mpData->maMenubuttonItem.maRect.SetRight( mnDX-mnRightBorder-TB_BORDER_OFFSET2-1 );
+ }
+ else
+ {
+ mpData->maMenubuttonItem.maRect.SetBottom( mnDY - mnBottomBorder-TB_BORDER_OFFSET1-1 );
+ mpData->maMenubuttonItem.maRect.SetLeft( nLeft );
+ mpData->maMenubuttonItem.maRect.SetRight( mnDX-mnRightBorder-TB_BORDER_OFFSET2-1 );
+ }
+ mpData->maMenubuttonItem.maRect.SetTop( mpData->maMenubuttonItem.maRect.Bottom() - mpData->mnMenuButtonWidth );
+ }
+ else
+ {
+ maUpperRect.SetTop( nTop+nMax+TB_SPIN_OFFSET );
+ maUpperRect.SetBottom( maUpperRect.Top()+TB_SPIN_SIZE-1 );
+ maUpperRect.SetLeft( nLeft );
+ maLowerRect.SetRight( mnDX-mnRightBorder-TB_BORDER_OFFSET2-1 );
+ maLowerRect.SetTop( maUpperRect.Top() );
+ maLowerRect.SetBottom( maUpperRect.Bottom() );
+ maUpperRect.SetRight( maUpperRect.Left() +
+ (maLowerRect.Right()-maUpperRect.Left())/2 );
+ maLowerRect.SetLeft( maUpperRect.Right() );
+ }
+ }
+ }
+
+ // no scrolling when there is a "more"-menu
+ // anything will "fit" in a single line then
+ if( IsMenuEnabled() )
+ mnCurLines = 1;
+
+ // determine the currently visible line
+ if ( mnVisLines >= mnCurLines )
+ mnCurLine = 1;
+ else if ( mnCurLine+mnVisLines-1 > mnCurLines )
+ mnCurLine = mnCurLines - (mnVisLines-1);
+
+ tools::Long firstItemCenter = 0;
+ for (auto & item : mpData->m_aItems)
+ {
+ item.mbShowWindow = false;
+
+ // check for line break and advance nX/nY accordingly
+ if ( item.mbBreak )
+ {
+ nFormatLine++;
+
+ // increment starting with the second line
+ if ( nFormatLine > mnCurLine )
+ {
+ if ( mbHorz )
+ {
+ nX = nLeft;
+ if ( mbLineSpacing )
+ nY += nLineSize+TB_LINESPACING;
+ else
+ nY += nLineSize;
+ }
+ else
+ {
+ nY = nTop;
+ if ( mbLineSpacing )
+ nX += nLineSize+TB_LINESPACING;
+ else
+ nX += nLineSize;
+ }
+ }
+ }
+
+ if ( !item.mbVisible || (nFormatLine < mnCurLine) ||
+ (nFormatLine > mnCurLine+mnVisLines-1) )
+ // item is not visible
+ item.maCalcRect = aEmptyRect;
+ else
+ {
+ // 1. determine current item width/height
+ // take window size and orientation into account, because this affects the size of item windows
+
+ Size aCurrentItemSize( item.GetSize( mbHorz, mbScroll, nMax, Size(mnMaxItemWidth, mnMaxItemHeight) ) );
+
+ // 2. position item rect and use size from step 1
+ // items will be centered horizontally (if mbHorz) or vertically
+ // advance nX and nY accordingly
+
+ if ( mbHorz )
+ {
+ // In special mode Locked horizontal positions of all items remain unchanged.
+
+ if ( mbIsArranged && meLayoutMode == ToolBoxLayoutMode::Locked && mnLines == 1 && item.maRect.Left() > 0 )
+ nX = item.maRect.Left();
+ item.maCalcRect.SetLeft( nX );
+
+ // In special mode Locked first item's vertical position remains unchanged. Consecutive items vertical
+ // positions are centered around first item's vertical position. If an item's height exceeds available
+ // space, item's vertical position remains unchanged too.
+
+ if ( mbIsArranged && meLayoutMode == ToolBoxLayoutMode::Locked && mnLines == 1 )
+ if ( firstItemCenter > 0 )
+ if ( firstItemCenter-aCurrentItemSize.Height()/2 > nY )
+ item.maCalcRect.SetTop( firstItemCenter-aCurrentItemSize.Height()/2 );
+ else
+ item.maCalcRect.SetTop( item.maRect.Top() );
+ else
+ {
+ item.maCalcRect.SetTop( item.maRect.Top() );
+ firstItemCenter = item.maRect.Top()+aCurrentItemSize.Height()/2;
+ }
+ else
+ item.maCalcRect.SetTop( nY+(nLineSize-aCurrentItemSize.Height())/2 );
+ item.maCalcRect.SetRight( nX+aCurrentItemSize.Width()-1 );
+ item.maCalcRect.SetBottom( item.maCalcRect.Top()+aCurrentItemSize.Height()-1 );
+ nX += aCurrentItemSize.Width();
+ }
+ else
+ {
+ item.maCalcRect.SetLeft( nX+(nLineSize-aCurrentItemSize.Width())/2 );
+ item.maCalcRect.SetTop( nY );
+ item.maCalcRect.SetRight( item.maCalcRect.Left()+aCurrentItemSize.Width()-1 );
+ item.maCalcRect.SetBottom( nY+aCurrentItemSize.Height()-1 );
+ nY += aCurrentItemSize.Height();
+ }
+ }
+
+ // position window items into calculated item rect
+ if ( item.mpWindow )
+ {
+ if ( item.mbShowWindow )
+ {
+ Point aPos( item.maCalcRect.Left(), item.maCalcRect.Top() );
+
+ assert( item.maCalcRect.Top() >= 0 );
+
+ item.mpWindow->SetPosPixel( aPos );
+ item.mpWindow->Show();
+ }
+ else
+ item.mpWindow->Hide();
+ }
+ } // end of loop over all items
+ mbIsArranged = true;
+ }
+ else
+ // we have no toolbox items
+ mnCurLines = 1;
+
+ if( IsMenuEnabled() && ImplIsFloatingMode() && !ImplHasExternalMenubutton() && !bIsInPopupMode )
+ {
+ // custom menu will be the last button in floating mode
+ ImplToolItem &rIt = mpData->maMenubuttonItem;
+
+ if ( mbHorz )
+ {
+ rIt.maRect.SetLeft( nX+TB_MENUBUTTON_OFFSET );
+ rIt.maRect.SetTop( nY );
+ rIt.maRect.SetRight( rIt.maRect.Left() + mpData->mnMenuButtonWidth );
+ rIt.maRect.SetBottom( nY+nLineSize-1 );
+ nX += rIt.maItemSize.Width();
+ }
+ else
+ {
+ rIt.maRect.SetLeft( nX );
+ rIt.maRect.SetTop( nY+TB_MENUBUTTON_OFFSET );
+ rIt.maRect.SetRight( nX+nLineSize-1 );
+ rIt.maRect.SetBottom( rIt.maRect.Top() + mpData->mnMenuButtonWidth );
+ nY += rIt.maItemSize.Height();
+ }
+ }
+
+ // if toolbox visible trigger paint for changed regions
+ if ( IsVisible() && !mbFullPaint )
+ {
+ if ( bMustFullPaint )
+ {
+ maPaintRect = tools::Rectangle( mnLeftBorder, mnTopBorder,
+ mnDX-mnRightBorder, mnDY-mnBottomBorder );
+ }
+ else
+ {
+ if ( aOldLowerRect != maLowerRect )
+ {
+ maPaintRect.Union( maLowerRect );
+ maPaintRect.Union( aOldLowerRect );
+ }
+ if ( aOldUpperRect != maUpperRect )
+ {
+ maPaintRect.Union( maUpperRect );
+ maPaintRect.Union( aOldUpperRect );
+ }
+ if ( aOldMenubuttonRect != mpData->maMenubuttonItem.maRect )
+ {
+ maPaintRect.Union( mpData->maMenubuttonItem.maRect );
+ maPaintRect.Union( aOldMenubuttonRect );
+ }
+ if ( pWrapper && aOldDragRect != pWrapper->GetDragArea() )
+ {
+ maPaintRect.Union( pWrapper->GetDragArea() );
+ maPaintRect.Union( aOldDragRect );
+ }
+
+ for (auto const& item : mpData->m_aItems)
+ {
+ if ( item.maRect != item.maCalcRect )
+ {
+ maPaintRect.Union( item.maRect );
+ maPaintRect.Union( item.maCalcRect );
+ }
+ }
+ }
+
+ Invalidate( maPaintRect );
+ }
+
+ // store the new calculated item rects
+ maPaintRect = aEmptyRect;
+ for (auto & item : mpData->m_aItems)
+ item.maRect = item.maCalcRect;
+ }
+
+ // indicate formatting is done
+ mbFormat = false;
+}
+
+IMPL_LINK_NOARG(ToolBox, ImplDropdownLongClickHdl, Timer *, void)
+{
+ if (mnCurPos == ITEM_NOTFOUND ||
+ !(mpData->m_aItems[ mnCurPos ].mnBits & ToolBoxItemBits::DROPDOWN))
+ return;
+
+ mpData->mbDropDownByKeyboard = false;
+ mpData->maDropdownClickHdl.Call( this );
+
+ // do not reset data if the dropdown handler opened a floating window
+ // see ImplFloatControl()
+ if( !mpFloatWin )
+ {
+ // no floater was opened
+ Deactivate();
+ InvalidateItem(mnCurPos);
+
+ mnCurPos = ITEM_NOTFOUND;
+ mnCurItemId = ToolBoxItemId(0);
+ mnDownItemId = ToolBoxItemId(0);
+ mnMouseModifier = 0;
+ mnHighItemId = ToolBoxItemId(0);
+ }
+}
+
+IMPL_LINK_NOARG(ToolBox, ImplUpdateHdl, Timer *, void)
+{
+
+ if( mbFormat && mpData )
+ ImplFormat();
+}
+
+static void ImplDrawMoreIndicator(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ const Image pImage(StockImage::Yes, CHEVRON);
+ Size aImageSize = pImage.GetSizePixel();
+ tools::Long x = rRect.Left() + (rRect.getOpenWidth() - aImageSize.Width())/2;
+ tools::Long y = rRect.Top() + (rRect.getOpenHeight() - aImageSize.Height())/2;
+ DrawImageFlags nImageStyle = DrawImageFlags::NONE;
+
+ rRenderContext.DrawImage(Point(x,y), pImage, nImageStyle);
+}
+
+static void ImplDrawDropdownArrow(vcl::RenderContext& rRenderContext, const tools::Rectangle& rDropDownRect, bool bSetColor, bool bRotate )
+{
+ bool bLineColor = rRenderContext.IsLineColor();
+ bool bFillColor = rRenderContext.IsFillColor();
+ Color aOldFillColor = rRenderContext.GetFillColor();
+ Color aOldLineColor = rRenderContext.GetLineColor();
+ rRenderContext.SetLineColor();
+
+ if ( bSetColor )
+ {
+ if (rRenderContext.GetSettings().GetStyleSettings().GetFaceColor().IsDark())
+ rRenderContext.SetFillColor(COL_WHITE);
+ else
+ rRenderContext.SetFillColor(COL_BLACK);
+ }
+
+ tools::Polygon aPoly(4);
+
+ // the assumption is, that the width always specifies the size of the expected arrow.
+ const tools::Long nMargin = round(2 * rRenderContext.GetDPIScaleFactor());
+ const tools::Long nSize = rDropDownRect.getOpenWidth() - 2 * nMargin;
+ const tools::Long nHalfSize = (nSize + 1) / 2;
+ const tools::Long x = rDropDownRect.Left() + nMargin + (bRotate ? (rDropDownRect.getOpenWidth() - nHalfSize) / 2 : 0);
+ const tools::Long y = rDropDownRect.Top() + nMargin + (rDropDownRect.getOpenHeight() - (bRotate ? nSize : nHalfSize)) / 2;
+
+ aPoly.SetPoint(Point(x, y), 0);
+ if (bRotate) // >
+ {
+ aPoly.SetPoint(Point(x, y + nSize), 1);
+ aPoly.SetPoint(Point(x + nHalfSize, y + nHalfSize), 2);
+ }
+ else // v
+ {
+ aPoly.SetPoint(Point(x + nHalfSize, y + nHalfSize), 1);
+ aPoly.SetPoint(Point(x + nSize, y), 2);
+ }
+ aPoly.SetPoint(Point(x, y), 3);
+
+ auto aaflags = rRenderContext.GetAntialiasing();
+ rRenderContext.SetAntialiasing(AntialiasingFlags::Enable);
+ rRenderContext.DrawPolygon( aPoly );
+ rRenderContext.SetAntialiasing(aaflags);
+
+ if( bFillColor )
+ rRenderContext.SetFillColor(aOldFillColor);
+ else
+ rRenderContext.SetFillColor();
+ if( bLineColor )
+ rRenderContext.SetLineColor(aOldLineColor);
+ else
+ rRenderContext.SetLineColor();
+}
+
+void ToolBox::ImplDrawMenuButton(vcl::RenderContext& rRenderContext, bool bHighlight)
+{
+ if (mpData->maMenubuttonItem.maRect.IsEmpty())
+ return;
+
+ // #i53937# paint menu button only if necessary
+ if (!ImplHasClippedItems())
+ return;
+
+ // execute pending paint requests
+ ImplCheckUpdate();
+
+ rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
+
+ // draw the 'more' indicator / button (>>)
+ ImplErase(rRenderContext, mpData->maMenubuttonItem.maRect, bHighlight);
+
+ if (bHighlight)
+ ImplDrawButton(rRenderContext, mpData->maMenubuttonItem.maRect, 2, false, true, false );
+
+ if (ImplHasClippedItems())
+ ImplDrawMoreIndicator(rRenderContext, mpData->maMenubuttonItem.maRect);
+
+ // store highlight state
+ mpData->mbMenubuttonSelected = bHighlight;
+
+ // restore colors
+ rRenderContext.Pop();
+}
+
+void ToolBox::ImplDrawSpin(vcl::RenderContext& rRenderContext)
+{
+ bool bTmpUpper;
+ bool bTmpLower;
+
+ if ( maUpperRect.IsEmpty() || maLowerRect.IsEmpty() )
+ return;
+
+ bTmpUpper = mnCurLine > 1;
+
+ bTmpLower = mnCurLine+mnVisLines-1 < mnCurLines;
+
+ if ( !IsEnabled() )
+ {
+ bTmpUpper = false;
+ bTmpLower = false;
+ }
+
+ ImplDrawUpDownButtons(rRenderContext, maUpperRect, maLowerRect,
+ false/*bUpperIn*/, false/*bLowerIn*/, bTmpUpper, bTmpLower, !mbHorz);
+}
+
+void ToolBox::ImplDrawSeparator(vcl::RenderContext& rRenderContext, ImplToolItems::size_type nPos, const tools::Rectangle& rRect)
+{
+ if ( nPos >= mpData->m_aItems.size() - 1 )
+ // no separator if it's the last item
+ return;
+
+ ImplToolItem* pItem = &mpData->m_aItems[nPos];
+ ImplToolItem* pPreviousItem = &mpData->m_aItems[nPos-1];
+ ImplToolItem* pNextItem = &mpData->m_aItems[nPos+1];
+
+ if ( ( pPreviousItem->mbShowWindow && pNextItem->mbShowWindow ) || pNextItem->mbBreak )
+ // no separator between two windows or before a break
+ return;
+
+ bool bNativeOk = false;
+ ControlPart nPart = IsHorizontal() ? ControlPart::SeparatorVert : ControlPart::SeparatorHorz;
+ if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, nPart))
+ {
+ ImplControlValue aControlValue;
+ bNativeOk = rRenderContext.DrawNativeControl(ControlType::Toolbar, nPart, rRect, ControlState::NONE, aControlValue, OUString());
+ }
+
+ /* Draw the widget only if it can't be drawn natively. */
+ if (bNativeOk)
+ return;
+
+ tools::Long nCenterPos, nSlim;
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ rRenderContext.SetLineColor(rStyleSettings.GetSeparatorColor());
+ if (IsHorizontal())
+ {
+ nSlim = (pItem->maRect.Bottom() - pItem->maRect.Top ()) / 4;
+ nCenterPos = pItem->maRect.Center().X();
+ rRenderContext.DrawLine(Point(nCenterPos, pItem->maRect.Top() + nSlim),
+ Point(nCenterPos, pItem->maRect.Bottom() - nSlim));
+ }
+ else
+ {
+ nSlim = (pItem->maRect.Right() - pItem->maRect.Left ()) / 4;
+ nCenterPos = pItem->maRect.Center().Y();
+ rRenderContext.DrawLine(Point(pItem->maRect.Left() + nSlim, nCenterPos),
+ Point(pItem->maRect.Right() - nSlim, nCenterPos));
+ }
+}
+
+void ToolBox::ImplDrawButton(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect, sal_uInt16 highlight,
+ bool bChecked, bool bEnabled, bool bIsWindow )
+{
+ // draws toolbar button background either native or using a coloured selection
+ // if bIsWindow is true, the corresponding item is a control and only a selection border will be drawn
+
+ bool bNativeOk = false;
+ if( !bIsWindow && rRenderContext.IsNativeControlSupported( ControlType::Toolbar, ControlPart::Button ) )
+ {
+ ImplControlValue aControlValue;
+ ControlState nState = ControlState::NONE;
+
+ if ( highlight == 1 ) nState |= ControlState::PRESSED;
+ if ( highlight == 2 ) nState |= ControlState::ROLLOVER;
+ if ( bEnabled ) nState |= ControlState::ENABLED;
+
+ aControlValue.setTristateVal( bChecked ? ButtonValue::On : ButtonValue::Off );
+
+ bNativeOk = rRenderContext.DrawNativeControl( ControlType::Toolbar, ControlPart::Button,
+ rRect, nState, aControlValue, OUString() );
+ }
+
+ if (!bNativeOk)
+ vcl::RenderTools::DrawSelectionBackground(rRenderContext, *this, rRect, bIsWindow ? 3 : highlight,
+ bChecked, true, bIsWindow, nullptr, 2);
+}
+
+void ToolBox::ImplDrawItem(vcl::RenderContext& rRenderContext, ImplToolItems::size_type nPos, sal_uInt16 nHighlight)
+{
+ if (nPos >= mpData->m_aItems.size())
+ return;
+
+ // execute pending paint requests
+ ImplCheckUpdate();
+
+ rRenderContext.SetFillColor();
+
+ ImplToolItem* pItem = &mpData->m_aItems[nPos];
+
+ if (!pItem->mbEnabled)
+ nHighlight = 0;
+
+ // if the rectangle is outside visible area
+ if (pItem->maRect.IsEmpty())
+ return;
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ // no gradient background for items that have a popup open
+ bool bHasOpenPopup = mpFloatWin && (mnDownItemId==pItem->mnId);
+
+ bool bHighContrastWhite = false;
+ // check the face color as highcontrast indicator
+ // because the toolbox itself might have a gradient
+ if (rStyleSettings.GetFaceColor() == COL_WHITE)
+ bHighContrastWhite = true;
+
+ // Compute buttons area.
+ Size aBtnSize = pItem->maRect.GetSize();
+
+ /* Compute the button/separator rectangle here, we'll need it for
+ * both the buttons and the separators. */
+ tools::Rectangle aButtonRect( pItem->maRect.TopLeft(), aBtnSize );
+ tools::Long nOffX = SMALLBUTTON_OFF_NORMAL_X;
+ tools::Long nOffY = SMALLBUTTON_OFF_NORMAL_Y;
+ tools::Long nImageOffX = 0;
+ tools::Long nImageOffY = 0;
+ DrawButtonFlags nStyle = DrawButtonFlags::NONE;
+
+ // draw separators
+ if ( (pItem->meType == ToolBoxItemType::SEPARATOR) && nPos > 0 )
+ {
+ ImplDrawSeparator(rRenderContext, nPos, aButtonRect);
+ }
+
+ // do nothing if item is no button or will be displayed as window
+ if ( (pItem->meType != ToolBoxItemType::BUTTON) || pItem->mbShowWindow )
+ return;
+
+ if ( pItem->meState == TRISTATE_TRUE )
+ {
+ nStyle |= DrawButtonFlags::Checked;
+ }
+ else if ( pItem->meState == TRISTATE_INDET )
+ {
+ nStyle |= DrawButtonFlags::DontKnow;
+ }
+ if ( nHighlight == 1 )
+ {
+ nStyle |= DrawButtonFlags::Pressed;
+ }
+
+ ImplErase(rRenderContext, pItem->maRect, nHighlight != 0, bHasOpenPopup );
+
+ nOffX += pItem->maRect.Left();
+ nOffY += pItem->maRect.Top();
+
+ // determine what has to be drawn on the button: image, text or both
+ bool bImage;
+ bool bText;
+ ButtonType tmpButtonType = determineButtonType( pItem, meButtonType ); // default to toolbox setting
+ pItem->DetermineButtonDrawStyle( tmpButtonType, bImage, bText );
+
+ // compute output values
+ tools::Long nBtnWidth = aBtnSize.Width()-SMALLBUTTON_HSIZE;
+ tools::Long nBtnHeight = aBtnSize.Height()-SMALLBUTTON_VSIZE;
+ Size aImageSize;
+
+ const bool bDropDown = (pItem->mnBits & ToolBoxItemBits::DROPDOWN) == ToolBoxItemBits::DROPDOWN;
+ tools::Rectangle aDropDownRect;
+ if (bDropDown)
+ aDropDownRect = pItem->GetDropDownRect(mbHorz);
+
+ if ( bImage )
+ {
+ const Image* pImage = &(pItem->maImage);
+ aImageSize = pImage->GetSizePixel();
+
+ // determine drawing flags
+ DrawImageFlags nImageStyle = DrawImageFlags::NONE;
+
+ if ( !pItem->mbEnabled || !IsEnabled() )
+ nImageStyle |= DrawImageFlags::Disable;
+
+ // #i35563# the dontknow state indicates different states at the same time
+ // which should not be rendered disabled but normal
+
+ // draw the image
+ nImageOffX = nOffX;
+ nImageOffY = nOffY;
+ if ( ( (pItem->mnBits & (ToolBoxItemBits::LEFT|ToolBoxItemBits::DROPDOWN)) || bText )
+ && ( meTextPosition == ToolBoxTextPosition::Right ) )
+ {
+ // left align also to leave space for drop down arrow
+ // and when drawing text+image
+ // just center in y, except for vertical (ie rotated text)
+ if( mbHorz || !bText )
+ nImageOffY += (nBtnHeight-aImageSize.Height())/2;
+ }
+ else
+ {
+ nImageOffX += (nBtnWidth-(bDropDown ? aDropDownRect.getOpenWidth() : 0)+SMALLBUTTON_OFF_NORMAL_X-aImageSize.Width())/2;
+ if ( meTextPosition == ToolBoxTextPosition::Right )
+ nImageOffY += (nBtnHeight-aImageSize.Height())/2;
+ }
+ if ( nHighlight != 0 || (pItem->meState == TRISTATE_TRUE) )
+ {
+ if( bHasOpenPopup )
+ ImplDrawFloatwinBorder(rRenderContext, pItem);
+ else
+ ImplDrawButton(rRenderContext, aButtonRect, nHighlight, pItem->meState == TRISTATE_TRUE,
+ pItem->mbEnabled && IsEnabled(), pItem->mbShowWindow);
+
+ if( nHighlight != 0 )
+ {
+ if( bHighContrastWhite )
+ nImageStyle |= DrawImageFlags::ColorTransform;
+ }
+ }
+ rRenderContext.DrawImage(Point( nImageOffX, nImageOffY ), *pImage, nImageStyle);
+ }
+
+ // draw the text
+ bool bRotate = false;
+ if ( bText )
+ {
+ const Size aTxtSize(GetOutDev()->GetCtrlTextWidth(pItem->maText), GetTextHeight());
+ tools::Long nTextOffX = nOffX;
+ tools::Long nTextOffY = nOffY;
+
+ // rotate text when vertically docked
+ vcl::Font aOldFont = rRenderContext.GetFont();
+ if( pItem->mbVisibleText && !ImplIsFloatingMode() &&
+ ((meAlign == WindowAlign::Left) || (meAlign == WindowAlign::Right)) )
+ {
+ bRotate = true;
+
+ vcl::Font aRotateFont = aOldFont;
+ aRotateFont.SetOrientation( 2700_deg10 );
+
+ // center horizontally
+ nTextOffX += aTxtSize.Height();
+ nTextOffX += (nBtnWidth-aTxtSize.Height())/2;
+
+ // add in image offset
+ if( bImage )
+ nTextOffY = nImageOffY + aImageSize.Height() + TB_IMAGETEXTOFFSET;
+
+ rRenderContext.SetFont(aRotateFont);
+ }
+ else
+ {
+ if ( meTextPosition == ToolBoxTextPosition::Right )
+ {
+ // center vertically
+ nTextOffY += (nBtnHeight-aTxtSize.Height())/2;
+
+ // add in image offset
+ if( bImage )
+ nTextOffX = nImageOffX + aImageSize.Width() + TB_IMAGETEXTOFFSET;
+ }
+ else
+ {
+ // center horizontally
+ nTextOffX += (nBtnWidth-(bDropDown ? aDropDownRect.getOpenWidth() : 0)+SMALLBUTTON_OFF_NORMAL_X-aTxtSize.Width() - TB_IMAGETEXTOFFSET)/2;
+ // set vertical position
+ nTextOffY += nBtnHeight - aTxtSize.Height();
+ }
+ }
+
+ // draw selection only if not already drawn during image output (see above)
+ if ( !bImage && (nHighlight != 0 || (pItem->meState == TRISTATE_TRUE) ) )
+ {
+ if( bHasOpenPopup )
+ ImplDrawFloatwinBorder(rRenderContext, pItem);
+ else
+ ImplDrawButton(rRenderContext, pItem->maRect, nHighlight, pItem->meState == TRISTATE_TRUE,
+ pItem->mbEnabled && IsEnabled(), pItem->mbShowWindow );
+ }
+
+ DrawTextFlags nTextStyle = DrawTextFlags::NONE;
+ if ( !pItem->mbEnabled )
+ nTextStyle |= DrawTextFlags::Disable;
+ rRenderContext.DrawCtrlText( Point( nTextOffX, nTextOffY ), pItem->maText,
+ 0, pItem->maText.getLength(), nTextStyle );
+ if ( bRotate )
+ SetFont( aOldFont );
+ }
+
+ // paint optional drop down arrow
+ if (!bDropDown)
+ return;
+
+ bool bSetColor = true;
+ if ( !pItem->mbEnabled || !IsEnabled() )
+ {
+ bSetColor = false;
+ rRenderContext.SetFillColor(rStyleSettings.GetShadowColor());
+ }
+
+ // dropdown only will be painted without inner border
+ if( (pItem->mnBits & ToolBoxItemBits::DROPDOWNONLY) != ToolBoxItemBits::DROPDOWNONLY )
+ {
+ ImplErase(rRenderContext, aDropDownRect, nHighlight != 0, bHasOpenPopup);
+
+ if( nHighlight != 0 || (pItem->meState == TRISTATE_TRUE) )
+ {
+ if( bHasOpenPopup )
+ ImplDrawFloatwinBorder(rRenderContext, pItem);
+ else
+ ImplDrawButton(rRenderContext, aDropDownRect, nHighlight, pItem->meState == TRISTATE_TRUE,
+ pItem->mbEnabled && IsEnabled(), false);
+ }
+ }
+ ImplDrawDropdownArrow(rRenderContext, aDropDownRect, bSetColor, bRotate);
+}
+
+void ToolBox::ImplDrawFloatwinBorder(vcl::RenderContext& rRenderContext, ImplToolItem const * pItem)
+{
+ if ( pItem->maRect.IsEmpty() )
+ return;
+
+ rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetShadowColor());
+ Point p1, p2;
+
+ p1 = pItem->maRect.TopLeft();
+ p1.AdjustX( 1 );
+ p2 = pItem->maRect.TopRight();
+ p2.AdjustX( -1 );
+ rRenderContext.DrawLine( p1, p2);
+ p1 = pItem->maRect.BottomLeft();
+ p1.AdjustX( 1 );
+ p2 = pItem->maRect.BottomRight();
+ p2.AdjustX( -1 );
+ rRenderContext.DrawLine( p1, p2);
+
+ p1 = pItem->maRect.TopLeft();
+ p1.AdjustY( 1 );
+ p2 = pItem->maRect.BottomLeft();
+ p2.AdjustY( -1 );
+ rRenderContext.DrawLine( p1, p2);
+ p1 = pItem->maRect.TopRight();
+ p1.AdjustY( 1 );
+ p2 = pItem->maRect.BottomRight();
+ p2.AdjustY( -1 );
+ rRenderContext.DrawLine( p1, p2);
+
+}
+
+void ToolBox::ImplFloatControl( bool bStart, FloatingWindow* pFloatWindow )
+{
+
+ if ( bStart )
+ {
+ mpFloatWin = pFloatWindow;
+
+ // redraw item, to trigger drawing of a special border
+ InvalidateItem(mnCurPos);
+
+ mbDrag = false;
+ EndTracking();
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ }
+ else
+ {
+ mpFloatWin = nullptr;
+
+ // if focus is still in this toolbox, then the floater was opened by keyboard
+ // draw current item with highlight and keep old state
+ bool bWasKeyboardActivate = mpData->mbDropDownByKeyboard;
+
+ if ( mnCurPos != ITEM_NOTFOUND )
+ InvalidateItem(mnCurPos);
+ Deactivate();
+
+ if( !bWasKeyboardActivate )
+ {
+ mnCurPos = ITEM_NOTFOUND;
+ mnCurItemId = ToolBoxItemId(0);
+ mnHighItemId = ToolBoxItemId(0);
+ }
+ mnDownItemId = ToolBoxItemId(0);
+
+ }
+}
+
+void ToolBox::ShowLine( bool bNext )
+{
+ mbFormat = true;
+
+ if ( bNext )
+ mnCurLine++;
+ else
+ mnCurLine--;
+
+ ImplFormat();
+}
+
+bool ToolBox::ImplHandleMouseMove( const MouseEvent& rMEvt, bool bRepeat )
+{
+ Point aMousePos = rMEvt.GetPosPixel();
+
+ if ( !mpData )
+ return false;
+
+ // ToolBox active?
+ if ( mbDrag && mnCurPos != ITEM_NOTFOUND )
+ {
+ // is the cursor over the item?
+ ImplToolItem* pItem = &mpData->m_aItems[mnCurPos];
+ if ( pItem->maRect.Contains( aMousePos ) )
+ {
+ if ( !mnCurItemId )
+ {
+ InvalidateItem(mnCurPos);
+ mnCurItemId = pItem->mnId;
+ Highlight();
+ }
+
+ if ( (pItem->mnBits & ToolBoxItemBits::REPEAT) && bRepeat )
+ Select();
+ }
+ else
+ {
+ if ( mnCurItemId )
+ {
+ InvalidateItem(mnCurPos);
+ mnCurItemId = ToolBoxItemId(0);
+ InvalidateItem(mnCurPos);
+ Highlight();
+ }
+ }
+
+ return true;
+ }
+
+ if ( mbUpper )
+ {
+ bool bNewIn = maUpperRect.Contains( aMousePos );
+ if ( bNewIn != mbIn )
+ {
+ mbIn = bNewIn;
+ InvalidateSpin(true, false);
+ }
+ return true;
+ }
+
+ if ( mbLower )
+ {
+ bool bNewIn = maLowerRect.Contains( aMousePos );
+ if ( bNewIn != mbIn )
+ {
+ mbIn = bNewIn;
+ InvalidateSpin(false);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool ToolBox::ImplHandleMouseButtonUp( const MouseEvent& rMEvt, bool bCancel )
+{
+ if ( !mpData )
+ return false;
+
+ // stop eventual running dropdown timer
+ if( mnCurPos < mpData->m_aItems.size() &&
+ (mpData->m_aItems[mnCurPos].mnBits & ToolBoxItemBits::DROPDOWN ) )
+ {
+ mpData->maDropdownTimer.Stop();
+ }
+
+ if ( mbDrag )
+ {
+ Deactivate();
+
+ if ( mbDrag )
+ mbDrag = false;
+ else
+ {
+ if ( mnCurPos == ITEM_NOTFOUND )
+ return true;
+ }
+
+ // has mouse been released on top of item?
+ if( mnCurPos < mpData->m_aItems.size() )
+ {
+ ImplToolItem* pItem = &mpData->m_aItems[mnCurPos];
+ if ( pItem->maRect.Contains( rMEvt.GetPosPixel() ) )
+ {
+ mnCurItemId = pItem->mnId;
+ if ( !bCancel )
+ {
+ // execute AutoCheck if required
+ if ( pItem->mnBits & ToolBoxItemBits::AUTOCHECK )
+ {
+ if ( pItem->mnBits & ToolBoxItemBits::RADIOCHECK )
+ {
+ if ( pItem->meState != TRISTATE_TRUE )
+ SetItemState( pItem->mnId, TRISTATE_TRUE );
+ }
+ else
+ {
+ if ( pItem->meState != TRISTATE_TRUE )
+ pItem->meState = TRISTATE_TRUE;
+ else
+ pItem->meState = TRISTATE_FALSE;
+ }
+ }
+
+ // do not call Select when Repeat is active, as in this
+ // case that was triggered already in MouseButtonDown
+ if ( !(pItem->mnBits & ToolBoxItemBits::REPEAT) )
+ {
+ // prevent from being destroyed in the select handler
+ VclPtr<vcl::Window> xWindow = this;
+ Select();
+ if ( xWindow->isDisposed() )
+ return true;
+ }
+ }
+
+ {
+ }
+
+ // Items not destroyed, in Select handler
+ if ( mnCurItemId )
+ {
+ // Get current pos for the case that items are inserted/removed
+ // in the toolBox
+ mnCurPos = GetItemPos( mnCurItemId );
+ if ( mnCurPos != ITEM_NOTFOUND )
+ {
+ InvalidateItem(mnCurPos);
+ GetOutDev()->Flush();
+ }
+ }
+ }
+ }
+
+ mnCurPos = ITEM_NOTFOUND;
+ mnCurItemId = ToolBoxItemId(0);
+ mnDownItemId = ToolBoxItemId(0);
+ mnMouseModifier = 0;
+ return true;
+ }
+ else if ( mbUpper || mbLower )
+ {
+ if ( mbIn )
+ ShowLine( !mbUpper );
+ mbUpper = false;
+ mbLower = false;
+ mbIn = false;
+ InvalidateSpin();
+ return true;
+ }
+
+ return false;
+}
+
+void ToolBox::MouseMove( const MouseEvent& rMEvt )
+{
+ // pressing a modifier generates synthetic mouse moves
+ // ignore it if keyboard selection is active
+ if( HasFocus() && ( rMEvt.GetMode() & MouseEventModifiers::MODIFIERCHANGED ) )
+ return;
+
+ if ( ImplHandleMouseMove( rMEvt ) )
+ return;
+
+ Point aMousePos = rMEvt.GetPosPixel();
+
+ // only highlight when the focus is not inside a child window of a toolbox
+ // eg, in an edit control
+ // and do not highlight when focus is in a different toolbox
+ bool bDrawHotSpot = true;
+ vcl::Window *pFocusWin = Application::GetFocusWindow();
+
+ bool bFocusWindowIsAToolBoxChild = false;
+ if (pFocusWin)
+ {
+ vcl::Window *pWin = pFocusWin->GetParent();
+ while (pWin)
+ {
+ if(pWin->ImplGetWindowImpl() && pWin->ImplGetWindowImpl()->mbToolBox)
+ {
+ bFocusWindowIsAToolBoxChild = true;
+ break;
+ }
+ pWin = pWin->GetParent();
+ }
+ }
+
+ if( bFocusWindowIsAToolBoxChild || (pFocusWin && pFocusWin->ImplGetWindowImpl() && pFocusWin->ImplGetWindowImpl()->mbToolBox && pFocusWin != this) )
+ bDrawHotSpot = false;
+
+ if ( mbDragging )
+ {
+ ImplTBDragMgr* pMgr = ImplGetTBDragMgr();
+ pMgr->Dragging( aMousePos );
+ return;
+ }
+
+ PointerStyle eStyle = PointerStyle::Arrow;
+
+ // change mouse cursor over drag area
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper && pWrapper->GetDragArea().Contains( rMEvt.GetPosPixel() ) )
+ eStyle = PointerStyle::Move;
+
+ if ( (mnWinStyle & TB_WBLINESIZING) == TB_WBLINESIZING )
+ {
+ if ( rMEvt.GetMode() & MouseEventModifiers::SIMPLEMOVE )
+ {
+ sal_uInt16 nLinePtr = ImplTestLineSize( rMEvt.GetPosPixel() );
+ if ( nLinePtr & DOCK_LINEHSIZE )
+ {
+ if ( meAlign == WindowAlign::Left )
+ eStyle = PointerStyle::WindowESize;
+ else
+ eStyle = PointerStyle::WindowWSize;
+ }
+ else if ( nLinePtr & DOCK_LINEVSIZE )
+ {
+ if ( meAlign == WindowAlign::Top )
+ eStyle = PointerStyle::WindowSSize;
+ else
+ eStyle = PointerStyle::WindowNSize;
+ }
+ }
+ }
+
+ if ( bDrawHotSpot )
+ {
+ bool bClearHigh = true;
+ if ( !rMEvt.IsLeaveWindow() && (mnCurPos == ITEM_NOTFOUND) )
+ {
+ ImplToolItems::size_type nTempPos = 0;
+ for (auto const& item : mpData->m_aItems)
+ {
+ if ( item.maRect.Contains( aMousePos ) )
+ {
+ if ( (item.meType == ToolBoxItemType::BUTTON) && item.mbEnabled )
+ {
+ bClearHigh = false;
+ if ( mnHighItemId != item.mnId )
+ {
+ if ( mnHighItemId )
+ {
+ ImplHideFocus();
+ ImplToolItems::size_type nPos = GetItemPos( mnHighItemId );
+ InvalidateItem(nPos);
+ CallEventListeners( VclEventId::ToolboxHighlightOff, reinterpret_cast< void* >( nPos ) );
+ }
+ if ( mpData->mbMenubuttonSelected )
+ {
+ // remove highlight from menubutton
+ InvalidateMenuButton();
+ }
+ mnHighItemId = item.mnId;
+ InvalidateItem(nTempPos);
+ ImplShowFocus();
+ CallEventListeners( VclEventId::ToolboxHighlight );
+ }
+ }
+ break;
+ }
+ ++nTempPos;
+ }
+ }
+
+ // only clear highlight when focus is not in toolbar
+ bool bMenuButtonHit = mpData->maMenubuttonItem.maRect.Contains( aMousePos ) && ImplHasClippedItems();
+ if ( !HasFocus() && (bClearHigh || bMenuButtonHit) )
+ {
+ if ( !bMenuButtonHit && mpData->mbMenubuttonSelected )
+ {
+ // remove highlight from menubutton
+ InvalidateMenuButton();
+ }
+
+ if( mnHighItemId )
+ {
+ ImplToolItems::size_type nClearPos = GetItemPos( mnHighItemId );
+ if ( nClearPos != ITEM_NOTFOUND )
+ {
+ InvalidateItem(nClearPos);
+ if( nClearPos != mnCurPos )
+ CallEventListeners( VclEventId::ToolboxHighlightOff, reinterpret_cast< void* >( nClearPos ) );
+ }
+ ImplHideFocus();
+ mnHighItemId = ToolBoxItemId(0);
+ }
+
+ if( bMenuButtonHit )
+ {
+ InvalidateMenuButton();
+ }
+ }
+ }
+
+ if ( meLastStyle != eStyle )
+ {
+ meLastStyle = eStyle;
+ SetPointer( eStyle );
+ }
+
+ DockingWindow::MouseMove( rMEvt );
+}
+
+void ToolBox::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ // only trigger toolbox for left mouse button and when
+ // we're not in normal operation
+ if ( rMEvt.IsLeft() && !mbDrag && (mnCurPos == ITEM_NOTFOUND) )
+ {
+ // call activate already here, as items could
+ // be exchanged
+ Activate();
+
+ // update ToolBox here, such that user knows it
+ if ( mbFormat )
+ {
+ ImplFormat();
+ PaintImmediately();
+ }
+
+ Point aMousePos = rMEvt.GetPosPixel();
+ ImplToolItems::size_type i = 0;
+ ImplToolItems::size_type nNewPos = ITEM_NOTFOUND;
+
+ // search for item that was clicked
+ for (auto const& item : mpData->m_aItems)
+ {
+ // is this the item?
+ if ( item.maRect.Contains( aMousePos ) )
+ {
+ // do nothing if it is a separator or
+ // if the item has been disabled
+ if ( (item.meType == ToolBoxItemType::BUTTON) &&
+ !item.mbShowWindow )
+ nNewPos = i;
+
+ break;
+ }
+
+ i++;
+ }
+
+ // item found
+ if ( nNewPos != ITEM_NOTFOUND )
+ {
+ if ( !mpData->m_aItems[nNewPos].mbEnabled )
+ {
+ Deactivate();
+ return;
+ }
+
+ // update actual data
+ StartTrackingFlags nTrackFlags = StartTrackingFlags::NONE;
+ mnCurPos = i;
+ mnCurItemId = mpData->m_aItems[nNewPos].mnId;
+ mnDownItemId = mnCurItemId;
+ mnMouseModifier = rMEvt.GetModifier();
+ if ( mpData->m_aItems[nNewPos].mnBits & ToolBoxItemBits::REPEAT )
+ nTrackFlags |= StartTrackingFlags::ButtonRepeat;
+
+ // update bDrag here, as it is evaluated in the EndSelection
+ mbDrag = true;
+
+ // on double-click: only call the handler, but do so before the button
+ // is hit, as in the handler dragging
+ // can be terminated
+ if ( rMEvt.GetClicks() == 2 )
+ DoubleClick();
+
+ if ( mbDrag )
+ {
+ InvalidateItem(mnCurPos);
+ Highlight();
+ }
+
+ // was dropdown arrow pressed
+ if( mpData->m_aItems[nNewPos].mnBits & ToolBoxItemBits::DROPDOWN )
+ {
+ if( ( (mpData->m_aItems[nNewPos].mnBits & ToolBoxItemBits::DROPDOWNONLY) == ToolBoxItemBits::DROPDOWNONLY)
+ || mpData->m_aItems[nNewPos].GetDropDownRect( mbHorz ).Contains( aMousePos ))
+ {
+ // dropdownonly always triggers the dropdown handler, over the whole button area
+
+ // the drop down arrow should not trigger the item action
+ mpData->mbDropDownByKeyboard = false;
+ mpData->maDropdownClickHdl.Call( this );
+
+ // do not reset data if the dropdown handler opened a floating window
+ // see ImplFloatControl()
+ if( !mpFloatWin )
+ {
+ // no floater was opened
+ Deactivate();
+ InvalidateItem(mnCurPos);
+
+ mnCurPos = ITEM_NOTFOUND;
+ mnCurItemId = ToolBoxItemId(0);
+ mnDownItemId = ToolBoxItemId(0);
+ mnMouseModifier = 0;
+ mnHighItemId = ToolBoxItemId(0);
+ }
+ return;
+ }
+ else // activate long click timer
+ mpData->maDropdownTimer.Start();
+ }
+
+ // call Click handler
+ if ( rMEvt.GetClicks() != 2 )
+ Click();
+
+ // also call Select handler at repeat
+ if ( nTrackFlags & StartTrackingFlags::ButtonRepeat )
+ Select();
+
+ // if the actions was not aborted in Click handler
+ if ( mbDrag )
+ StartTracking( nTrackFlags );
+
+ // if mouse was clicked over an item we
+ // can abort here
+ return;
+ }
+
+ Deactivate();
+
+ // menu button hit ?
+ if( mpData->maMenubuttonItem.maRect.Contains( aMousePos ) && ImplHasClippedItems() )
+ {
+ if ( maMenuButtonHdl.IsSet() )
+ maMenuButtonHdl.Call( this );
+ else
+ ExecuteCustomMenu( mpData->maMenubuttonItem.maRect );
+ return;
+ }
+
+ // check scroll- and next-buttons here
+ if ( maUpperRect.Contains( aMousePos ) )
+ {
+ if ( mnCurLine > 1 )
+ {
+ StartTracking();
+ mbUpper = true;
+ mbIn = true;
+ InvalidateSpin(true, false);
+ }
+ return;
+ }
+ if ( maLowerRect.Contains( aMousePos ) )
+ {
+ if ( mnCurLine+mnVisLines-1 < mnCurLines )
+ {
+ StartTracking();
+ mbLower = true;
+ mbIn = true;
+ InvalidateSpin(false);
+ }
+ return;
+ }
+
+ // Linesizing testen
+ if ( (mnWinStyle & TB_WBLINESIZING) == TB_WBLINESIZING )
+ {
+ sal_uInt16 nLineMode = ImplTestLineSize( aMousePos );
+ if ( nLineMode )
+ {
+ ImplTBDragMgr* pMgr = ImplGetTBDragMgr();
+
+ // call handler, such that we can set the
+ // dock rectangles
+ StartDocking();
+
+ Point aPos = GetParent()->OutputToScreenPixel( GetPosPixel() );
+ Size aSize = GetSizePixel();
+ aPos = ScreenToOutputPixel( aPos );
+
+ // start dragging
+ pMgr->StartDragging( this, aMousePos, tools::Rectangle( aPos, aSize ),
+ nLineMode );
+ return;
+ }
+ }
+
+ // no item, then only click or double click
+ if ( rMEvt.GetClicks() == 2 )
+ DoubleClick();
+ else
+ Click();
+ }
+
+ if ( !mbDrag && (mnCurPos == ITEM_NOTFOUND) )
+ DockingWindow::MouseButtonDown( rMEvt );
+}
+
+void ToolBox::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ if ( ImplHandleMouseButtonUp( rMEvt ) )
+ return;
+
+ if ( mbDragging && rMEvt.IsLeft() )
+ {
+ ImplTBDragMgr* pMgr = ImplGetTBDragMgr();
+ pMgr->EndDragging();
+ return;
+ }
+
+ DockingWindow::MouseButtonUp( rMEvt );
+}
+
+void ToolBox::Tracking( const TrackingEvent& rTEvt )
+{
+ VclPtr<vcl::Window> xWindow = this;
+
+ if ( rTEvt.IsTrackingEnded() )
+ ImplHandleMouseButtonUp( rTEvt.GetMouseEvent(), rTEvt.IsTrackingCanceled() );
+ else
+ ImplHandleMouseMove( rTEvt.GetMouseEvent(), rTEvt.IsTrackingRepeat() );
+
+ if ( xWindow->isDisposed() )
+ // toolbox was deleted
+ return;
+ DockingWindow::Tracking( rTEvt );
+}
+
+void ToolBox::InvalidateItem(ImplToolItems::size_type nPosition)
+{
+ if (mpData && nPosition < mpData->m_aItems.size())
+ {
+ ImplToolItem* pItem = &mpData->m_aItems[nPosition];
+ Invalidate(pItem->maRect);
+ }
+}
+
+void ToolBox::InvalidateMenuButton()
+{
+ if (!mpData->maMenubuttonItem.maRect.IsEmpty())
+ Invalidate(mpData->maMenubuttonItem.maRect);
+}
+
+void ToolBox::InvalidateSpin(bool bUpperIn, bool bLowerIn)
+{
+ if (bUpperIn && !maUpperRect.IsEmpty())
+ Invalidate(maUpperRect);
+
+ if (bLowerIn && !maLowerRect.IsEmpty())
+ Invalidate(maLowerRect);
+}
+
+void ToolBox::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rPaintRect)
+{
+ if( mpData->mbIsPaintLocked )
+ return;
+
+ if (rPaintRect == tools::Rectangle(0, 0, mnDX-1, mnDY-1))
+ mbFullPaint = true;
+ ImplFormat();
+ mbFullPaint = false;
+
+ ImplDrawBackground(rRenderContext, rPaintRect);
+
+ if ( (mnWinStyle & WB_BORDER) && !ImplIsFloatingMode() )
+ ImplDrawBorder(rRenderContext);
+
+ if( !ImplIsFloatingMode() )
+ ImplDrawGrip(rRenderContext);
+
+ ImplDrawMenuButton(rRenderContext, mpData->mbMenubuttonSelected);
+
+ // draw SpinButtons
+ if (mnWinStyle & WB_SCROLL)
+ {
+ if (mnCurLines > mnLines)
+ ImplDrawSpin(rRenderContext);
+ }
+
+ // draw buttons
+ ImplToolItems::size_type nHighPos;
+ if ( mnHighItemId )
+ nHighPos = GetItemPos( mnHighItemId );
+ else
+ nHighPos = ITEM_NOTFOUND;
+
+ ImplToolItems::size_type nCount = mpData->m_aItems.size();
+ for( ImplToolItems::size_type i = 0; i < nCount; i++ )
+ {
+ ImplToolItem* pItem = &mpData->m_aItems[i];
+
+ // only draw when the rectangle is in the draw rectangle
+ if ( !pItem->maRect.IsEmpty() && rPaintRect.Overlaps( pItem->maRect ) )
+ {
+ sal_uInt16 nHighlight = 0;
+ if ( i == mnCurPos )
+ nHighlight = 1;
+ else if ( i == nHighPos )
+ nHighlight = 2;
+ ImplDrawItem(rRenderContext, i, nHighlight);
+ }
+ }
+ ImplShowFocus();
+}
+
+void ToolBox::Resize()
+{
+ Size aSize = GetOutputSizePixel();
+ // #i31422# some WindowManagers send (0,0) sizes when
+ // switching virtual desktops - ignore this and avoid reformatting
+ if( !aSize.Width() && !aSize.Height() )
+ return;
+
+ tools::Long nOldDX = mnDX;
+ tools::Long nOldDY = mnDY;
+ mnDX = aSize.Width();
+ mnDY = aSize.Height();
+
+ mnLastResizeDY = 0;
+
+ // invalidate everything to have gradient backgrounds properly drawn
+ Invalidate();
+
+ // If we have any expandable entries, then force a reformat first using
+ // their optimal sizes, then share out the excess space evenly across those
+ // expandables and reformat again
+ std::vector<size_t> aExpandables;
+ for (size_t i = 0; i < mpData->m_aItems.size(); ++i)
+ {
+ if (mpData->m_aItems[i].mbExpand)
+ {
+ vcl::Window *pWindow = mpData->m_aItems[i].mpWindow;
+ SAL_INFO_IF(!pWindow, "vcl.layout", "only tabitems with window supported at the moment");
+ if (!pWindow)
+ continue;
+ Size aWinSize(pWindow->GetSizePixel());
+ Size aPrefSize(pWindow->get_preferred_size());
+ aWinSize.setWidth( aPrefSize.Width() );
+ pWindow->SetSizePixel(aWinSize);
+ aExpandables.push_back(i);
+ }
+ }
+
+ // re-format or re-draw
+ if ( mbScroll || !aExpandables.empty() )
+ {
+ if ( !mbFormat || !aExpandables.empty() )
+ {
+ mbFormat = true;
+ if( IsReallyVisible() || !aExpandables.empty() )
+ {
+ ImplFormat(true);
+
+ if (!aExpandables.empty())
+ {
+ //Get how big the optimal size is
+ tools::Rectangle aBounds;
+ for (const ImplToolItem & rItem : mpData->m_aItems)
+ {
+ aBounds.Union( rItem.maRect );
+ }
+
+ auto nOptimalWidth = aBounds.GetWidth();
+ auto nDiff = aSize.Width() - nOptimalWidth;
+ decltype(nDiff) nExpandablesSize = aExpandables.size();
+ nDiff /= nExpandablesSize;
+
+ //share out the diff from optimal to real across
+ //expandable entries
+ for (size_t nIndex : aExpandables)
+ {
+ vcl::Window *pWindow = mpData->m_aItems[nIndex].mpWindow;
+ Size aWinSize(pWindow->GetSizePixel());
+ Size aPrefSize(pWindow->get_preferred_size());
+ aWinSize.setWidth( aPrefSize.Width() + nDiff );
+ pWindow->SetSizePixel(aWinSize);
+ }
+
+ //now reformat with final sizes
+ mbFormat = true;
+ ImplFormat(true);
+ }
+ }
+ }
+ }
+
+ // redraw border
+ if ( !(mnWinStyle & WB_BORDER) )
+ return;
+
+ // as otherwise, when painting we might think we have to re-draw everything
+ if ( mbFormat && IsReallyVisible() )
+ Invalidate();
+ else
+ {
+ if ( mnRightBorder )
+ {
+ if ( nOldDX > mnDX )
+ Invalidate( tools::Rectangle( mnDX-mnRightBorder-1, 0, mnDX, mnDY ) );
+ else
+ Invalidate( tools::Rectangle( nOldDX-mnRightBorder-1, 0, nOldDX, nOldDY ) );
+ }
+
+ if ( mnBottomBorder )
+ {
+ if ( nOldDY > mnDY )
+ Invalidate( tools::Rectangle( 0, mnDY-mnBottomBorder-1, mnDX, mnDY ) );
+ else
+ Invalidate( tools::Rectangle( 0, nOldDY-mnBottomBorder-1, nOldDX, nOldDY ) );
+ }
+ }
+}
+
+namespace
+{
+ bool DispatchableCommand(std::u16string_view rName)
+ {
+ return o3tl::starts_with(rName, u".uno") ||
+ o3tl::starts_with(rName, u"slot:") ||
+ o3tl::starts_with(rName, u"macro:") ||
+ o3tl::starts_with(rName, u"vnd.sun.star.script");
+ }
+}
+
+const OUString& ToolBox::ImplGetHelpText( ToolBoxItemId nItemId ) const
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ assert( pItem );
+
+ if ( pItem->maHelpText.isEmpty() && ( !pItem->maHelpId.isEmpty() || pItem->maCommandStr.getLength() ))
+ {
+ Help* pHelp = Application::GetHelp();
+ if ( pHelp )
+ {
+ if (DispatchableCommand(pItem->maCommandStr))
+ pItem->maHelpText = pHelp->GetHelpText( pItem->maCommandStr, this );
+ if ( pItem->maHelpText.isEmpty() && !pItem->maHelpId.isEmpty() )
+ pItem->maHelpText = pHelp->GetHelpText( pItem->maHelpId, this );
+ }
+ }
+
+ return pItem->maHelpText;
+}
+
+void ToolBox::RequestHelp( const HelpEvent& rHEvt )
+{
+ ToolBoxItemId nItemId;
+ Point aHelpPos;
+
+ if( !rHEvt.KeyboardActivated() )
+ {
+ nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
+ aHelpPos = rHEvt.GetMousePosPixel();
+ }
+ else
+ {
+ if( !mnHighItemId )
+ return;
+ else
+ nItemId = mnHighItemId;
+ tools::Rectangle aRect( GetItemRect( nItemId ) );
+ if( aRect.IsEmpty() )
+ return;
+ else
+ aHelpPos = OutputToScreenPixel( aRect.Center() );
+ }
+
+ if ( nItemId )
+ {
+ if ( rHEvt.GetMode() & (HelpEventMode::BALLOON | HelpEventMode::QUICK) )
+ {
+ // get rectangle
+ tools::Rectangle aTempRect = GetItemRect( nItemId );
+ Point aPt = OutputToScreenPixel( aTempRect.TopLeft() );
+ aTempRect.SetLeft( aPt.X() );
+ aTempRect.SetTop( aPt.Y() );
+ aPt = OutputToScreenPixel( aTempRect.BottomRight() );
+ aTempRect.SetRight( aPt.X() );
+ aTempRect.SetBottom( aPt.Y() );
+
+ // get text and display it
+ OUString aStr = GetQuickHelpText( nItemId );
+ if (aStr.isEmpty())
+ aStr = MnemonicGenerator::EraseAllMnemonicChars( GetItemText( nItemId ) );
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ {
+ const OUString& rHelpStr = GetHelpText( nItemId );
+ if (!rHelpStr.isEmpty())
+ aStr = rHelpStr;
+ Help::ShowBalloon( this, aHelpPos, aTempRect, aStr );
+ }
+ else
+ Help::ShowQuickHelp( this, aTempRect, aStr, QuickHelpFlags::CtrlText );
+ return;
+ }
+ }
+
+ DockingWindow::RequestHelp( rHEvt );
+}
+
+bool ToolBox::EventNotify( NotifyEvent& rNEvt )
+{
+ if ( rNEvt.GetType() == NotifyEventType::KEYINPUT )
+ {
+ KeyEvent aKEvt = *rNEvt.GetKeyEvent();
+ vcl::KeyCode aKeyCode = aKEvt.GetKeyCode();
+ sal_uInt16 nKeyCode = aKeyCode.GetCode();
+ switch( nKeyCode )
+ {
+ case KEY_TAB:
+ {
+ // internal TAB cycling only if parent is not a dialog or if we are the only child
+ // otherwise the dialog control will take over
+ vcl::Window *pParent = ImplGetParent();
+ bool bOldSchoolContainer =
+ ((pParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL &&
+ pParent->GetChildCount() != 1);
+ bool bNoTabCycling = bOldSchoolContainer || isContainerWindow(pParent);
+
+ if( bNoTabCycling )
+ return DockingWindow::EventNotify( rNEvt );
+ else if( ImplChangeHighlightUpDn( aKeyCode.IsShift() , bNoTabCycling ) )
+ return true;
+ else
+ return DockingWindow::EventNotify( rNEvt );
+ }
+ default:
+ break;
+ }
+ }
+ else if( rNEvt.GetType() == NotifyEventType::GETFOCUS )
+ {
+ if( rNEvt.GetWindow() == this )
+ {
+ // the toolbar itself got the focus
+ if( mnLastFocusItemId != ToolBoxItemId(0) || mpData->mbMenubuttonWasLastSelected )
+ {
+ // restore last item
+ if( mpData->mbMenubuttonWasLastSelected )
+ {
+ ImplChangeHighlight( nullptr );
+ mpData->mbMenubuttonSelected = true;
+ InvalidateMenuButton();
+ }
+ else
+ {
+ ImplChangeHighlight( ImplGetItem( mnLastFocusItemId ) );
+ mnLastFocusItemId = ToolBoxItemId(0);
+ }
+ }
+ else if( (GetGetFocusFlags() & (GetFocusFlags::Backward|GetFocusFlags::Tab) ) == (GetFocusFlags::Backward|GetFocusFlags::Tab))
+ // Shift-TAB was pressed in the parent
+ ImplChangeHighlightUpDn( false );
+ else
+ ImplChangeHighlightUpDn( true );
+
+ mnLastFocusItemId = ToolBoxItemId(0);
+
+ return true;
+ }
+ else
+ {
+ // a child window got the focus so update current item to
+ // allow for proper lose focus handling in keyboard navigation
+ for (auto const& item : mpData->m_aItems)
+ {
+ if ( item.mbVisible )
+ {
+ if ( item.mpWindow && item.mpWindow->ImplIsWindowOrChild( rNEvt.GetWindow() ) )
+ {
+ mnHighItemId = item.mnId;
+ break;
+ }
+ }
+ }
+ return DockingWindow::EventNotify( rNEvt );
+ }
+ }
+ else if( rNEvt.GetType() == NotifyEventType::LOSEFOCUS )
+ {
+ // deselect
+ ImplHideFocus();
+ mpData->mbMenubuttonWasLastSelected = false;
+ mnHighItemId = ToolBoxItemId(0);
+ mnCurPos = ITEM_NOTFOUND;
+ }
+
+ return DockingWindow::EventNotify( rNEvt );
+}
+
+void ToolBox::Command( const CommandEvent& rCEvt )
+{
+ if ( rCEvt.GetCommand() == CommandEventId::Wheel )
+ {
+ if ( (mnCurLine > 1) || (mnCurLine+mnVisLines-1 < mnCurLines) )
+ {
+ const CommandWheelData* pData = rCEvt.GetWheelData();
+ if ( pData->GetMode() == CommandWheelMode::SCROLL )
+ {
+ if ( (mnCurLine > 1) && (pData->GetDelta() > 0) )
+ ShowLine( false );
+ else if ( (mnCurLine+mnVisLines-1 < mnCurLines) && (pData->GetDelta() < 0) )
+ ShowLine( true );
+ InvalidateSpin();
+ return;
+ }
+ }
+ }
+ else if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
+ {
+ ExecuteCustomMenu( tools::Rectangle( rCEvt.GetMousePosPixel(), rCEvt.GetMousePosPixel() ) );
+ return;
+ }
+
+ DockingWindow::Command( rCEvt );
+}
+
+void ToolBox::StateChanged( StateChangedType nType )
+{
+ DockingWindow::StateChanged( nType );
+
+ if ( nType == StateChangedType::InitShow )
+ ImplFormat();
+ else if ( nType == StateChangedType::Enable )
+ ImplUpdateItem();
+ else if ( nType == StateChangedType::UpdateMode )
+ {
+ if ( IsUpdateMode() )
+ Invalidate();
+ }
+ else if ( (nType == StateChangedType::Zoom) ||
+ (nType == StateChangedType::ControlFont) )
+ {
+ mbCalc = true;
+ mbFormat = true;
+ ImplInitSettings( true, false, false );
+ Invalidate();
+ }
+ else if ( nType == StateChangedType::ControlForeground )
+ {
+ ImplInitSettings( false, true, false );
+ Invalidate();
+ }
+ else if ( nType == StateChangedType::ControlBackground )
+ {
+ ImplInitSettings( false, false, true ); // font, foreground, background
+ Invalidate();
+ }
+
+ maStateChangedHandler.Call( &nType );
+}
+
+void ToolBox::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ DockingWindow::DataChanged( rDCEvt );
+
+ if ( (rDCEvt.GetType() == DataChangedEventType::DISPLAY) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
+ (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
+ ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
+ {
+ mbCalc = true;
+ mbFormat = true;
+ ImplInitSettings( true, true, true );
+ Invalidate();
+ }
+
+ maDataChangedHandler.Call( &rDCEvt );
+}
+
+void ToolBox::SetStyle(WinBits nNewStyle)
+{
+ mnWinStyle = nNewStyle;
+ if (!ImplIsFloatingMode())
+ {
+ bool bOldScroll = mbScroll;
+ mbScroll = (mnWinStyle & WB_SCROLL) != 0;
+ if (mbScroll != bOldScroll)
+ {
+ mbFormat = true;
+ ImplFormat();
+ }
+ }
+}
+
+void ToolBox::ToggleFloatingMode()
+{
+ DockingWindow::ToggleFloatingMode();
+
+ if (!mpData)
+ return;
+
+ bool bOldHorz = mbHorz;
+
+ if ( ImplIsFloatingMode() )
+ {
+ mbHorz = true;
+ meAlign = WindowAlign::Top;
+ mbScroll = true;
+
+ if( bOldHorz != mbHorz )
+ mbCalc = true; // orientation was changed !
+
+ ImplSetMinMaxFloatSize();
+ SetOutputSizePixel( ImplCalcFloatSize( mnFloatLines ) );
+ }
+ else
+ {
+ mbScroll = (mnWinStyle & WB_SCROLL) != 0;
+ if ( (meAlign == WindowAlign::Top) || (meAlign == WindowAlign::Bottom) )
+ mbHorz = true;
+ else
+ mbHorz = false;
+
+ // set focus back to document
+ ImplGetFrameWindow()->GetWindow( GetWindowType::Client )->GrabFocus();
+ }
+
+ if( bOldHorz != mbHorz )
+ {
+ // if orientation changes, the toolbox has to be initialized again
+ // to update the direction of the gradient
+ mbCalc = true;
+ ImplInitSettings( true, true, true );
+ }
+
+ mbFormat = true;
+ ImplFormat();
+}
+
+void ToolBox::StartDocking()
+{
+ meDockAlign = meAlign;
+ mnDockLines = mnLines;
+ mbLastFloatMode = ImplIsFloatingMode();
+ DockingWindow::StartDocking();
+}
+
+bool ToolBox::Docking( const Point& rPos, tools::Rectangle& rRect )
+{
+ // do nothing during dragging, it was calculated before
+ if ( mbDragging )
+ return false;
+
+ bool bFloatMode = false;
+
+ DockingWindow::Docking( rPos, rRect );
+
+ // if the mouse is outside the area, it can only become a floating window
+ tools::Rectangle aDockingRect( rRect );
+ if ( !ImplIsFloatingMode() )
+ {
+ // don't use tracking rectangle for alignment check, because it will be too large
+ // to get a floating mode as result - switch to floating size
+ // so the calculation only depends on the position of the rectangle, not the current
+ // docking state of the window
+ ImplToolItems::size_type nTemp = 0;
+ aDockingRect.SetSize( ImplCalcFloatSize( nTemp ) );
+
+ // in this mode docking is never done by keyboard, so it's OK to use the mouse position
+ aDockingRect.SetPos( ImplGetFrameWindow()->GetPointerPosPixel() );
+ }
+
+ bFloatMode = true;
+
+ meDockAlign = meAlign;
+ if ( !mbLastFloatMode )
+ {
+ ImplToolItems::size_type nTemp = 0;
+ aDockingRect.SetSize( ImplCalcFloatSize( nTemp ) );
+ }
+
+ rRect = aDockingRect;
+ mbLastFloatMode = bFloatMode;
+
+ return bFloatMode;
+}
+
+void ToolBox::EndDocking( const tools::Rectangle& rRect, bool bFloatMode )
+{
+ if ( !IsDockingCanceled() )
+ {
+ if ( mnLines != mnDockLines )
+ SetLineCount( mnDockLines );
+ if ( meAlign != meDockAlign )
+ SetAlign( meDockAlign );
+ }
+ if ( bFloatMode || (bFloatMode != ImplIsFloatingMode()) )
+ DockingWindow::EndDocking( rRect, bFloatMode );
+}
+
+void ToolBox::Resizing( Size& rSize )
+{
+ ImplToolItems::size_type nCalcLines;
+ ImplToolItems::size_type nTemp;
+
+ // calculate all floating sizes
+ ImplCalcFloatSizes();
+
+ if ( !mnLastResizeDY )
+ mnLastResizeDY = mnDY;
+
+ // is vertical resizing needed
+ if ( (mnLastResizeDY != rSize.Height()) && (mnDY != rSize.Height()) )
+ {
+ nCalcLines = ImplCalcLines( rSize.Height() );
+ if ( nCalcLines < 1 )
+ nCalcLines = 1;
+ rSize = ImplCalcFloatSize( nCalcLines );
+ }
+ else
+ {
+ nCalcLines = 1;
+ nTemp = nCalcLines;
+ Size aTempSize = ImplCalcFloatSize( nTemp );
+ while ( (aTempSize.Width() > rSize.Width()) &&
+ (nCalcLines <= maFloatSizes[0].mnLines) )
+ {
+ nCalcLines++;
+ nTemp = nCalcLines;
+ aTempSize = ImplCalcFloatSize( nTemp );
+ }
+ rSize = aTempSize;
+ }
+
+ mnLastResizeDY = rSize.Height();
+}
+
+Size ToolBox::GetOptimalSize() const
+{
+ // If we have any expandable entries, then force them to their
+ // optimal sizes, then reset them afterwards
+ std::map<vcl::Window*, Size> aExpandables;
+ for (const ImplToolItem & rItem : mpData->m_aItems)
+ {
+ if (rItem.mbExpand)
+ {
+ vcl::Window *pWindow = rItem.mpWindow;
+ SAL_INFO_IF(!pWindow, "vcl.layout", "only tabitems with window supported at the moment");
+ if (!pWindow)
+ continue;
+ Size aWinSize(pWindow->GetSizePixel());
+ aExpandables[pWindow] = aWinSize;
+ Size aPrefSize(pWindow->get_preferred_size());
+ aWinSize.setWidth( aPrefSize.Width() );
+ pWindow->SetSizePixel(aWinSize);
+ }
+ }
+
+ Size aSize(const_cast<ToolBox *>(this)->ImplCalcSize( mnLines ));
+
+ for (auto const& [pWindow, aWinSize] : aExpandables)
+ pWindow->SetSizePixel(aWinSize);
+
+ return aSize;
+}
+
+Size ToolBox::CalcWindowSizePixel( ImplToolItems::size_type nCalcLines )
+{
+ return ImplCalcSize( nCalcLines );
+}
+
+Size ToolBox::CalcWindowSizePixel( ImplToolItems::size_type nCalcLines, WindowAlign eAlign )
+{
+ return ImplCalcSize( nCalcLines,
+ (eAlign == WindowAlign::Top || eAlign == WindowAlign::Bottom) ? TB_CALCMODE_HORZ : TB_CALCMODE_VERT );
+}
+
+ToolBox::ImplToolItems::size_type ToolBox::ImplCountLineBreaks() const
+{
+ ImplToolItems::size_type nLines = 0;
+
+ for (auto const& item : mpData->m_aItems)
+ {
+ if( item.meType == ToolBoxItemType::BREAK )
+ ++nLines;
+ }
+ return nLines;
+}
+
+Size ToolBox::CalcPopupWindowSizePixel()
+{
+ // count number of breaks and calc corresponding floating window size
+ ImplToolItems::size_type nLines = ImplCountLineBreaks();
+
+ if( nLines )
+ ++nLines; // add the first line
+ else
+ {
+ // no breaks found: use quadratic layout
+ nLines = static_cast<ImplToolItems::size_type>(ceil( sqrt( static_cast<double>(GetItemCount()) ) ));
+ }
+
+ bool bPopup = mpData->mbAssumePopupMode;
+ mpData->mbAssumePopupMode = true;
+
+ Size aSize = CalcFloatingWindowSizePixel( nLines );
+
+ mpData->mbAssumePopupMode = bPopup;
+ return aSize;
+}
+
+Size ToolBox::CalcFloatingWindowSizePixel()
+{
+ ImplToolItems::size_type nLines = ImplCountLineBreaks();
+ ++nLines; // add the first line
+ return CalcFloatingWindowSizePixel( nLines );
+}
+
+Size ToolBox::CalcFloatingWindowSizePixel( ImplToolItems::size_type nCalcLines )
+{
+ bool bFloat = mpData->mbAssumeFloating;
+ bool bDocking = mpData->mbAssumeDocked;
+
+ // simulate floating mode and force reformat before calculating
+ mpData->mbAssumeFloating = true;
+ mpData->mbAssumeDocked = false;
+
+ Size aSize = ImplCalcFloatSize( nCalcLines );
+
+ mbFormat = true;
+ mpData->mbAssumeFloating = bFloat;
+ mpData->mbAssumeDocked = bDocking;
+
+ return aSize;
+}
+
+Size ToolBox::CalcMinimumWindowSizePixel()
+{
+ if( ImplIsFloatingMode() )
+ return ImplCalcSize( mnFloatLines );
+ else
+ {
+ // create dummy toolbox for measurements
+ VclPtrInstance< ToolBox > pToolBox( GetParent(), GetStyle() );
+
+ // copy until first useful item
+ for (auto const& item : mpData->m_aItems)
+ {
+ pToolBox->CopyItem( *this, item.mnId );
+ if( (item.meType == ToolBoxItemType::BUTTON) &&
+ item.mbVisible && !ImplIsFixedControl( &item ) )
+ break;
+ }
+
+ // add to docking manager if required to obtain a drag area
+ // (which is accounted for in calcwindowsizepixel)
+ if( ImplGetDockingManager()->GetDockingWindowWrapper( this ) )
+ ImplGetDockingManager()->AddWindow( pToolBox );
+
+ // account for menu
+ if( IsMenuEnabled() )
+ pToolBox->SetMenuType( GetMenuType() );
+
+ pToolBox->SetAlign( GetAlign() );
+ Size aSize = pToolBox->CalcWindowSizePixel( 1 );
+
+ ImplGetDockingManager()->RemoveWindow( pToolBox );
+ pToolBox->Clear();
+
+ pToolBox.disposeAndClear();
+
+ return aSize;
+ }
+}
+
+void ToolBox::EnableCustomize( bool bEnable )
+{
+ mbCustomize = bEnable;
+}
+
+void ToolBox::LoseFocus()
+{
+ ImplChangeHighlight( nullptr, true );
+
+ DockingWindow::LoseFocus();
+}
+
+// performs the action associated with an item, ie simulates clicking the item
+void ToolBox::TriggerItem( ToolBoxItemId nItemId )
+{
+ mnHighItemId = nItemId;
+ vcl::KeyCode aKeyCode( 0, 0 );
+ ImplActivateItem( aKeyCode );
+}
+
+// calls the button's action handler
+// returns true if action was called
+bool ToolBox::ImplActivateItem( vcl::KeyCode aKeyCode )
+{
+ bool bRet = true;
+ if( mnHighItemId )
+ {
+ ImplToolItem *pToolItem = ImplGetItem( mnHighItemId );
+
+ // #107712#, activate can also be called for disabled entries
+ if( pToolItem && !pToolItem->mbEnabled )
+ return true;
+
+ if( pToolItem && pToolItem->mpWindow && HasFocus() )
+ {
+ ImplHideFocus();
+ mbChangingHighlight = true; // avoid focus change due to loss of focus
+ pToolItem->mpWindow->ImplControlFocus( GetFocusFlags::Tab );
+ mbChangingHighlight = false;
+ }
+ else
+ {
+ mnDownItemId = mnCurItemId = mnHighItemId;
+ if (pToolItem && (pToolItem->mnBits & ToolBoxItemBits::AUTOCHECK))
+ {
+ if ( pToolItem->mnBits & ToolBoxItemBits::RADIOCHECK )
+ {
+ if ( pToolItem->meState != TRISTATE_TRUE )
+ SetItemState( pToolItem->mnId, TRISTATE_TRUE );
+ }
+ else
+ {
+ if ( pToolItem->meState != TRISTATE_TRUE )
+ pToolItem->meState = TRISTATE_TRUE;
+ else
+ pToolItem->meState = TRISTATE_FALSE;
+ }
+ }
+ mnMouseModifier = aKeyCode.GetModifier();
+ mbIsKeyEvent = true;
+ Activate();
+ Click();
+
+ // #107776# we might be destroyed in the selecthandler
+ VclPtr<vcl::Window> xWindow = this;
+ Select();
+ if ( xWindow->isDisposed() )
+ return bRet;
+
+ Deactivate();
+ mbIsKeyEvent = false;
+ mnMouseModifier = 0;
+ }
+ }
+ else
+ bRet = false;
+ return bRet;
+}
+
+static bool ImplCloseLastPopup( vcl::Window const *pParent )
+{
+ // close last popup toolbox (see also:
+ // ImplHandleMouseFloatMode(...) in winproc.cxx )
+
+ if (ImplGetSVData()->mpWinData->mpFirstFloat)
+ {
+ FloatingWindow* pLastLevelFloat = ImplGetSVData()->mpWinData->mpFirstFloat->ImplFindLastLevelFloat();
+ // only close the floater if it is not our direct parent, which would kill ourself
+ if( pLastLevelFloat && pLastLevelFloat != pParent )
+ {
+ pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
+ return true;
+ }
+ }
+ return false;
+}
+
+// opens a drop down toolbox item
+// returns true if item was opened
+bool ToolBox::ImplOpenItem( vcl::KeyCode aKeyCode )
+{
+ sal_uInt16 nCode = aKeyCode.GetCode();
+ bool bRet = true;
+
+ // arrow keys should work only in the opposite direction of alignment (to not break cursor travelling)
+ if ( ((nCode == KEY_LEFT || nCode == KEY_RIGHT) && IsHorizontal())
+ || ((nCode == KEY_UP || nCode == KEY_DOWN) && !IsHorizontal()) )
+ return false;
+
+ if( mpData->mbMenubuttonSelected )
+ {
+ if( ImplCloseLastPopup( GetParent() ) )
+ return bRet;
+ mbIsKeyEvent = true;
+ if ( maMenuButtonHdl.IsSet() )
+ maMenuButtonHdl.Call( this );
+ else
+ ExecuteCustomMenu( mpData->maMenubuttonItem.maRect );
+ mpData->mbMenubuttonWasLastSelected = true;
+ mbIsKeyEvent = false;
+ }
+ else if( mnHighItemId && ImplGetItem( mnHighItemId ) &&
+ (ImplGetItem( mnHighItemId )->mnBits & ToolBoxItemBits::DROPDOWN) )
+ {
+ mnDownItemId = mnCurItemId = mnHighItemId;
+ mnCurPos = GetItemPos( mnCurItemId );
+ mnLastFocusItemId = mnCurItemId; // save item id for possible later focus restore
+ mnMouseModifier = aKeyCode.GetModifier();
+ mbIsKeyEvent = true;
+ Activate();
+
+ mpData->mbDropDownByKeyboard = true;
+ mpData->maDropdownClickHdl.Call( this );
+
+ mbIsKeyEvent = false;
+ mnMouseModifier = 0;
+ }
+ else
+ bRet = false;
+
+ return bRet;
+}
+
+void ToolBox::KeyInput( const KeyEvent& rKEvt )
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = aKeyCode.GetCode();
+
+ vcl::Window *pParent = ImplGetParent();
+ bool bOldSchoolContainer = ((pParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL);
+ bool bParentIsContainer = bOldSchoolContainer || isContainerWindow(pParent);
+
+ bool bForwardKey = false;
+ bool bGrabFocusToDocument = false;
+
+ // #107776# we might be destroyed in the keyhandler
+ VclPtr<vcl::Window> xWindow = this;
+
+ switch ( nCode )
+ {
+ case KEY_UP:
+ {
+ // Ctrl-Cursor activates next toolbox, indicated by a blue arrow pointing to the left/up
+ if( aKeyCode.GetModifier() ) // allow only pure cursor keys
+ break;
+ if( !IsHorizontal() )
+ ImplChangeHighlightUpDn( true );
+ else
+ ImplOpenItem( aKeyCode );
+ }
+ break;
+ case KEY_LEFT:
+ {
+ if( aKeyCode.GetModifier() ) // allow only pure cursor keys
+ break;
+ if( IsHorizontal() )
+ ImplChangeHighlightUpDn( true );
+ else
+ ImplOpenItem( aKeyCode );
+ }
+ break;
+ case KEY_DOWN:
+ {
+ if( aKeyCode.GetModifier() ) // allow only pure cursor keys
+ break;
+ if( !IsHorizontal() )
+ ImplChangeHighlightUpDn( false );
+ else
+ ImplOpenItem( aKeyCode );
+ }
+ break;
+ case KEY_RIGHT:
+ {
+ if( aKeyCode.GetModifier() ) // allow only pure cursor keys
+ break;
+ if( IsHorizontal() )
+ ImplChangeHighlightUpDn( false );
+ else
+ ImplOpenItem( aKeyCode );
+ }
+ break;
+ case KEY_PAGEUP:
+ if ( mnCurLine > 1 )
+ {
+ if( mnCurLine > mnVisLines )
+ mnCurLine = mnCurLine - mnVisLines;
+ else
+ mnCurLine = 1;
+ mbFormat = true;
+ ImplFormat();
+ InvalidateSpin();
+ ImplChangeHighlight( ImplGetFirstValidItem( mnCurLine ) );
+ }
+ break;
+ case KEY_PAGEDOWN:
+ if ( mnCurLine+mnVisLines-1 < mnCurLines )
+ {
+ if( mnCurLine + 2*mnVisLines-1 < mnCurLines )
+ mnCurLine = mnCurLine + mnVisLines;
+ else
+ mnCurLine = mnCurLines;
+ mbFormat = true;
+ ImplFormat();
+ InvalidateSpin();
+ ImplChangeHighlight( ImplGetFirstValidItem( mnCurLine ) );
+ }
+ break;
+ case KEY_END:
+ {
+ ImplChangeHighlight( nullptr );
+ ImplChangeHighlightUpDn( false );
+ }
+ break;
+ case KEY_HOME:
+ {
+ ImplChangeHighlight( nullptr );
+ ImplChangeHighlightUpDn( true );
+ }
+ break;
+ case KEY_ESCAPE:
+ {
+ if( !ImplIsFloatingMode() && bParentIsContainer )
+ DockingWindow::KeyInput( rKEvt );
+ else
+ {
+ // send focus to document pane
+ vcl::Window *pWin = this;
+ while( pWin )
+ {
+ if( !pWin->GetParent() )
+ {
+ pWin->ImplGetFrameWindow()->GetWindow( GetWindowType::Client )->GrabFocus();
+ break;
+ }
+ pWin = pWin->GetParent();
+ }
+ }
+ }
+ break;
+ case KEY_RETURN:
+ {
+ // #107712#, disabled entries are selectable now
+ // leave toolbox and move focus to document
+ if( mnHighItemId )
+ {
+ ImplToolItem *pItem = ImplGetItem(mnHighItemId);
+ if (!pItem || !pItem->mbEnabled)
+ {
+ bGrabFocusToDocument = true;
+ }
+ }
+ if( !bGrabFocusToDocument )
+ bForwardKey = !ImplActivateItem( aKeyCode );
+ }
+ break;
+ case KEY_SPACE:
+ {
+ ImplOpenItem( aKeyCode );
+ }
+ break;
+ default:
+ {
+ sal_uInt16 aKeyGroup = aKeyCode.GetGroup();
+ ImplToolItem *pItem = nullptr;
+ if( mnHighItemId )
+ pItem = ImplGetItem( mnHighItemId );
+ // #i13931# forward alphanum keyinput into embedded control
+ if( (aKeyGroup == KEYGROUP_NUM || aKeyGroup == KEYGROUP_ALPHA ) && pItem && pItem->mpWindow && pItem->mbEnabled )
+ {
+ vcl::Window *pFocusWindow = Application::GetFocusWindow();
+ ImplHideFocus();
+ mbChangingHighlight = true; // avoid focus change due to loss of focus
+ pItem->mpWindow->ImplControlFocus( GetFocusFlags::Tab );
+ mbChangingHighlight = false;
+ if( pFocusWindow != Application::GetFocusWindow() )
+ Application::GetFocusWindow()->KeyInput( rKEvt );
+ }
+ else
+ {
+ // do nothing to avoid key presses going into the document
+ // while the toolbox has the focus
+ // just forward function and special keys and combinations with Alt-key
+ if( aKeyGroup == KEYGROUP_FKEYS || aKeyGroup == KEYGROUP_MISC || aKeyCode.IsMod2() )
+ bForwardKey = true;
+ }
+ }
+ }
+
+ if ( xWindow->isDisposed() )
+ return;
+
+ // #107251# move focus away if this toolbox was disabled during keyinput
+ if (HasFocus() && mpData->mbKeyInputDisabled && bParentIsContainer)
+ {
+ vcl::Window *pFocusControl = pParent->ImplGetDlgWindow( 0, GetDlgWindowType::First );
+ if ( pFocusControl && pFocusControl != this )
+ pFocusControl->ImplControlFocus( GetFocusFlags::Init );
+ }
+
+ // #107712#, leave toolbox
+ if( bGrabFocusToDocument )
+ {
+ GrabFocusToDocument();
+ return;
+ }
+
+ if( bForwardKey )
+ DockingWindow::KeyInput( rKEvt );
+}
+
+// returns the current toolbox line of the item
+ToolBox::ImplToolItems::size_type ToolBox::ImplGetItemLine( ImplToolItem const * pCurrentItem )
+{
+ ImplToolItems::size_type nLine = 1;
+ for (auto const& item : mpData->m_aItems)
+ {
+ if ( item.mbBreak )
+ ++nLine;
+ if( &item == pCurrentItem)
+ break;
+ }
+ return nLine;
+}
+
+// returns the first displayable item in the given line
+ImplToolItem* ToolBox::ImplGetFirstValidItem( ImplToolItems::size_type nLine )
+{
+ if( !nLine || nLine > mnCurLines )
+ return nullptr;
+
+ nLine--;
+
+ ImplToolItems::iterator it = mpData->m_aItems.begin();
+ while( it != mpData->m_aItems.end() )
+ {
+ // find correct line
+ if ( it->mbBreak )
+ nLine--;
+ if( !nLine )
+ {
+ // find first useful item
+ while( it != mpData->m_aItems.end() && ((it->meType != ToolBoxItemType::BUTTON) ||
+ /*!it->mbEnabled ||*/ !it->mbVisible || ImplIsFixedControl( &(*it) )) )
+ {
+ ++it;
+ if( it == mpData->m_aItems.end() || it->mbBreak )
+ return nullptr; // no valid items in this line
+ }
+ return &(*it);
+ }
+ ++it;
+ }
+
+ return (it == mpData->m_aItems.end()) ? nullptr : &(*it);
+}
+
+ToolBox::ImplToolItems::size_type ToolBox::ImplFindItemPos( const ImplToolItem* pItem, const ImplToolItems& rList )
+{
+ if( pItem )
+ {
+ for( ImplToolItems::size_type nPos = 0; nPos < rList.size(); ++nPos )
+ if( &rList[ nPos ] == pItem )
+ return nPos;
+ }
+ return ITEM_NOTFOUND;
+}
+
+void ToolBox::ChangeHighlight( ImplToolItems::size_type nPos )
+{
+ if ( nPos < GetItemCount() ) {
+ ImplGrabFocus( GetFocusFlags::NONE );
+ ImplChangeHighlight ( ImplGetItem ( GetItemId ( nPos ) ) );
+ }
+}
+
+void ToolBox::ImplChangeHighlight( ImplToolItem const * pItem, bool bNoGrabFocus )
+{
+ // avoid recursion due to focus change
+ if( mbChangingHighlight )
+ return;
+
+ mbChangingHighlight = true;
+
+ ImplToolItem* pOldItem = nullptr;
+
+ if ( mnHighItemId )
+ {
+ ImplHideFocus();
+ ImplToolItems::size_type nPos = GetItemPos( mnHighItemId );
+ pOldItem = ImplGetItem( mnHighItemId );
+ // #i89962# ImplDrawItem can cause Invalidate/Update
+ // which will in turn ImplShowFocus again
+ // set mnHighItemId to 0 already to prevent this hen/egg problem
+ mnHighItemId = ToolBoxItemId(0);
+ InvalidateItem(nPos);
+ CallEventListeners( VclEventId::ToolboxHighlightOff, reinterpret_cast< void* >( nPos ) );
+ }
+
+ if( !bNoGrabFocus && pItem != pOldItem && pOldItem && pOldItem->mpWindow )
+ {
+ // move focus into toolbox
+ GrabFocus();
+ }
+
+ if( pItem )
+ {
+ ImplToolItems::size_type aPos = ToolBox::ImplFindItemPos( pItem, mpData->m_aItems );
+ if( aPos != ITEM_NOTFOUND)
+ {
+ // check for line breaks
+ ImplToolItems::size_type nLine = ImplGetItemLine( pItem );
+
+ if( nLine >= mnCurLine + mnVisLines )
+ {
+ mnCurLine = nLine - mnVisLines + 1;
+ mbFormat = true;
+ }
+ else if ( nLine < mnCurLine )
+ {
+ mnCurLine = nLine;
+ mbFormat = true;
+ }
+
+ if( mbFormat )
+ {
+ ImplFormat();
+ }
+
+ mnHighItemId = pItem->mnId;
+ InvalidateItem(aPos);
+
+ ImplShowFocus();
+
+ if( pItem->mpWindow )
+ pItem->mpWindow->GrabFocus();
+ if( pItem != pOldItem )
+ CallEventListeners( VclEventId::ToolboxHighlight );
+ }
+ }
+ else
+ {
+ ImplHideFocus();
+ mnHighItemId = ToolBoxItemId(0);
+ mnCurPos = ITEM_NOTFOUND;
+ }
+
+ mbChangingHighlight = false;
+}
+
+// check for keyboard accessible items
+static bool ImplIsValidItem( const ImplToolItem* pItem, bool bNotClipped )
+{
+ bool bValid = (pItem && pItem->meType == ToolBoxItemType::BUTTON && pItem->mbVisible && !ImplIsFixedControl( pItem )
+ && pItem->mbEnabled);
+ if( bValid && bNotClipped && pItem->IsClipped() )
+ bValid = false;
+ return bValid;
+}
+
+bool ToolBox::ImplChangeHighlightUpDn( bool bUp, bool bNoCycle )
+{
+ ImplToolItem* pToolItem = ImplGetItem( mnHighItemId );
+
+ if( !pToolItem || !mnHighItemId )
+ {
+ // menubutton highlighted ?
+ if( mpData->mbMenubuttonSelected )
+ {
+ mpData->mbMenubuttonSelected = false;
+ if( bUp )
+ {
+ // select last valid non-clipped item
+ ImplToolItem* pItem = nullptr;
+ auto it = std::find_if(mpData->m_aItems.rbegin(), mpData->m_aItems.rend(),
+ [](const ImplToolItem& rItem) { return ImplIsValidItem( &rItem, true ); });
+ if( it != mpData->m_aItems.rend() )
+ pItem = &(*it);
+
+ InvalidateMenuButton();
+ ImplChangeHighlight( pItem );
+ }
+ else
+ {
+ // select first valid non-clipped item
+ ImplToolItems::iterator it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
+ [](const ImplToolItem& rItem) { return ImplIsValidItem( &rItem, true ); });
+ if( it != mpData->m_aItems.end() )
+ {
+ InvalidateMenuButton();
+ ImplChangeHighlight( &(*it) );
+ }
+ }
+ return true;
+ }
+
+ if( bUp )
+ {
+ // Select first valid item
+ ImplToolItems::iterator it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
+ [](const ImplToolItem& rItem) { return ImplIsValidItem( &rItem, false ); });
+
+ // select the menu button if a clipped item would be selected
+ if( (it != mpData->m_aItems.end() && &(*it) == ImplGetFirstClippedItem()) && IsMenuEnabled() )
+ {
+ ImplChangeHighlight( nullptr );
+ mpData->mbMenubuttonSelected = true;
+ InvalidateMenuButton();
+ }
+ else
+ ImplChangeHighlight( (it != mpData->m_aItems.end()) ? &(*it) : nullptr );
+ return true;
+ }
+ else
+ {
+ // Select last valid item
+
+ // docked toolbars have the menubutton as last item - if this button is enabled
+ if( ImplHasClippedItems() && IsMenuEnabled() && !ImplIsFloatingMode() )
+ {
+ ImplChangeHighlight( nullptr );
+ mpData->mbMenubuttonSelected = true;
+ InvalidateMenuButton();
+ }
+ else
+ {
+ ImplToolItem* pItem = nullptr;
+ auto it = std::find_if(mpData->m_aItems.rbegin(), mpData->m_aItems.rend(),
+ [](const ImplToolItem& rItem) { return ImplIsValidItem( &rItem, false ); });
+ if( it != mpData->m_aItems.rend() )
+ pItem = &(*it);
+
+ ImplChangeHighlight( pItem );
+ }
+ return true;
+ }
+ }
+
+ assert(pToolItem);
+
+ ImplToolItems::size_type pos = ToolBox::ImplFindItemPos( pToolItem, mpData->m_aItems );
+ ImplToolItems::size_type nCount = mpData->m_aItems.size();
+
+ ImplToolItems::size_type i=0;
+ do
+ {
+ if( bUp )
+ {
+ if( !pos-- )
+ {
+ if( bNoCycle )
+ return false;
+
+ // highlight the menu button if it is the last item
+ if( ImplHasClippedItems() && IsMenuEnabled() && !ImplIsFloatingMode() )
+ {
+ ImplChangeHighlight( nullptr );
+ mpData->mbMenubuttonSelected = true;
+ InvalidateMenuButton();
+ return true;
+ }
+ else
+ pos = nCount-1;
+ }
+ }
+ else
+ {
+ if( ++pos >= nCount )
+ {
+ if( bNoCycle )
+ return false;
+
+ // highlight the menu button if it is the last item
+ if( ImplHasClippedItems() && IsMenuEnabled() && !ImplIsFloatingMode() )
+ {
+ ImplChangeHighlight( nullptr );
+ mpData->mbMenubuttonSelected = true;
+ InvalidateMenuButton();
+ return true;
+ }
+ else
+ pos = 0;
+ }
+ }
+
+ pToolItem = &mpData->m_aItems[pos];
+
+ if ( ImplIsValidItem( pToolItem, false ) )
+ break;
+
+ } while( ++i < nCount);
+
+ if( pToolItem->IsClipped() && IsMenuEnabled() )
+ {
+ // select the menu button if a clipped item would be selected
+ ImplChangeHighlight( nullptr );
+ mpData->mbMenubuttonSelected = true;
+ InvalidateMenuButton();
+ }
+ else if( i != nCount )
+ ImplChangeHighlight( pToolItem );
+ else
+ return false;
+
+ return true;
+}
+
+void ToolBox::ImplShowFocus()
+{
+ if( mnHighItemId && HasFocus() )
+ {
+ ImplToolItem* pItem = ImplGetItem( mnHighItemId );
+ if (pItem && pItem->mpWindow && !pItem->mpWindow->isDisposed())
+ {
+ vcl::Window *pWin = pItem->mpWindow->ImplGetWindowImpl()->mpBorderWindow ? pItem->mpWindow->ImplGetWindowImpl()->mpBorderWindow.get() : pItem->mpWindow.get();
+ pWin->ImplGetWindowImpl()->mbDrawSelectionBackground = true;
+ pWin->Invalidate();
+ }
+ }
+}
+
+void ToolBox::ImplHideFocus()
+{
+ if( mnHighItemId )
+ {
+ mpData->mbMenubuttonWasLastSelected = false;
+ ImplToolItem* pItem = ImplGetItem( mnHighItemId );
+ if( pItem && pItem->mpWindow )
+ {
+ vcl::Window *pWin = pItem->mpWindow->ImplGetWindowImpl()->mpBorderWindow ? pItem->mpWindow->ImplGetWindowImpl()->mpBorderWindow.get() : pItem->mpWindow.get();
+ pWin->ImplGetWindowImpl()->mbDrawSelectionBackground = false;
+ pWin->Invalidate();
+ }
+ }
+
+ if ( mpData && mpData->mbMenubuttonSelected )
+ {
+ mpData->mbMenubuttonWasLastSelected = true;
+ // remove highlight from menubutton
+ mpData->mbMenubuttonSelected = false;
+ InvalidateMenuButton();
+ }
+}
+
+void ToolBox::SetToolbarLayoutMode( ToolBoxLayoutMode eLayout )
+{
+ if ( meLayoutMode != eLayout )
+ meLayoutMode = eLayout;
+}
+
+void ToolBox::SetToolBoxTextPosition( ToolBoxTextPosition ePosition )
+{
+ meTextPosition = ePosition;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/toolbox2.cxx b/vcl/source/window/toolbox2.cxx
new file mode 100644
index 0000000000..c799495b9b
--- /dev/null
+++ b/vcl/source/window/toolbox2.cxx
@@ -0,0 +1,1757 @@
+/* -*- 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 <utility>
+#include <vcl/uitest/logger.hxx>
+#include <sal/log.hxx>
+
+#include <comphelper/base64.hxx>
+#include <comphelper/processfactory.hxx>
+#include <boost/property_tree/ptree.hpp>
+
+#include <vcl/cvtgrf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/mnemonic.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/IconThemeInfo.hxx>
+#include <vcl/commandinfoprovider.hxx>
+
+#include <svdata.hxx>
+#include <brdwin.hxx>
+#include <toolbox.h>
+
+#include <unotools/confignode.hxx>
+#include <tools/json_writer.hxx>
+
+#include <vcl/uitest/uiobject.hxx>
+
+#include "impldockingwrapper.hxx"
+
+using namespace vcl;
+
+#define TB_SEP_SIZE 8 // Separator size
+
+
+ImplToolBoxPrivateData::ImplToolBoxPrivateData()
+{
+ meButtonSize = ToolBoxButtonSize::DontCare;
+ mpMenu = VclPtr<PopupMenu>::Create();
+
+ maMenuType = ToolBoxMenuType::NONE;
+ maMenubuttonItem.maItemSize = Size( TB_MENUBUTTON_SIZE+TB_MENUBUTTON_OFFSET, TB_MENUBUTTON_SIZE+TB_MENUBUTTON_OFFSET );
+ maMenubuttonItem.meState = TRISTATE_FALSE;
+ mnMenuButtonWidth = TB_MENUBUTTON_SIZE;
+
+ mbIsLocked = false;
+ mbNativeButtons = false;
+ mbIsPaintLocked = false;
+ mbAssumeDocked = false;
+ mbAssumePopupMode = false;
+ mbAssumeFloating = false;
+ mbKeyInputDisabled = false;
+ mbMenubuttonSelected = false;
+ mbMenubuttonWasLastSelected = false;
+ mbWillUsePopupMode = false;
+ mbDropDownByKeyboard = false;
+}
+
+ImplToolBoxPrivateData::~ImplToolBoxPrivateData()
+{
+ m_pLayoutData.reset();
+ mpMenu.disposeAndClear();
+}
+
+void ImplToolItem::init(ToolBoxItemId nItemId, ToolBoxItemBits nItemBits,
+ bool bEmptyBtn)
+{
+ mnId = nItemId;
+ mpWindow = nullptr;
+ mbNonInteractiveWindow = false;
+ mpUserData = nullptr;
+ meType = ToolBoxItemType::BUTTON;
+ mnBits = nItemBits;
+ meState = TRISTATE_FALSE;
+ mbEnabled = true;
+ mbVisible = true;
+ mbEmptyBtn = bEmptyBtn;
+ mbShowWindow = false;
+ mbBreak = false;
+ mnSepSize = TB_SEP_SIZE;
+ mnDropDownArrowWidth = TB_DROPDOWNARROWWIDTH;
+ mnImageAngle = 0_deg10;
+ mbMirrorMode = false;
+ mbVisibleText = false;
+ mbExpand = false;
+}
+
+ImplToolItem::ImplToolItem()
+{
+ init(ToolBoxItemId(0), ToolBoxItemBits::NONE, true);
+}
+
+ImplToolItem::ImplToolItem( ToolBoxItemId nItemId, Image aImage,
+ ToolBoxItemBits nItemBits ) :
+ maImage(std::move( aImage ))
+{
+ init(nItemId, nItemBits, false);
+}
+
+ImplToolItem::ImplToolItem( ToolBoxItemId nItemId, OUString aText,
+ OUString aCommand, ToolBoxItemBits nItemBits ) :
+ maText(std::move( aText )),
+ maCommandStr(std::move( aCommand ))
+{
+ init(nItemId, nItemBits, false);
+}
+
+ImplToolItem::ImplToolItem( ToolBoxItemId nItemId, Image aImage,
+ OUString aText, ToolBoxItemBits nItemBits ) :
+ maImage(std::move( aImage )),
+ maText(std::move( aText ))
+{
+ init(nItemId, nItemBits, false);
+}
+
+Size ImplToolItem::GetSize( bool bHorz, bool bCheckMaxWidth, tools::Long maxWidth, const Size& rDefaultSize )
+{
+ Size aSize( rDefaultSize ); // the size of 'standard' toolbox items
+ // non-standard items are eg windows or buttons with text
+
+ if ( (meType == ToolBoxItemType::BUTTON) || (meType == ToolBoxItemType::SPACE) )
+ {
+ aSize = maItemSize;
+
+ if ( mpWindow && bHorz )
+ {
+ // get size of item window and check if it fits
+ // no windows in vertical toolbars (the default is mbShowWindow=false)
+ Size aWinSize = mpWindow->GetSizePixel();
+
+ if (mpWindow->GetStyle() & WB_NOLABEL)
+ // Window wants no label? Then don't check width, it'll be just
+ // clipped.
+ bCheckMaxWidth = false;
+
+ if ( !bCheckMaxWidth || (aWinSize.Width() <= maxWidth) )
+ {
+ aSize.setWidth( aWinSize.Width() );
+ aSize.setHeight( aWinSize.Height() );
+ mbShowWindow = true;
+ }
+ else
+ {
+ if ( mbEmptyBtn )
+ {
+ aSize.setWidth( 0 );
+ aSize.setHeight( 0 );
+ }
+ }
+ }
+ }
+ else if ( meType == ToolBoxItemType::SEPARATOR )
+ {
+ if ( bHorz )
+ {
+ aSize.setWidth( mnSepSize );
+ aSize.setHeight( rDefaultSize.Height() );
+ }
+ else
+ {
+ aSize.setWidth( rDefaultSize.Width() );
+ aSize.setHeight( mnSepSize );
+ }
+ }
+ else if ( meType == ToolBoxItemType::BREAK )
+ {
+ aSize.setWidth( 0 );
+ aSize.setHeight( 0 );
+ }
+
+ return aSize;
+}
+
+void ImplToolItem::DetermineButtonDrawStyle( ButtonType eButtonType, bool& rbImage, bool& rbText ) const
+{
+ if ( meType != ToolBoxItemType::BUTTON )
+ {
+ // no button -> draw nothing
+ rbImage = rbText = false;
+ return;
+ }
+
+ bool bHasImage;
+ bool bHasText;
+
+ // check for image and/or text
+ bHasImage = !!maImage;
+ bHasText = !maText.isEmpty();
+
+ // prefer images if symbolonly buttons are drawn
+ // prefer texts if textonly buttons are drawn
+
+ if ( eButtonType == ButtonType::SYMBOLONLY ) // drawing icons only
+ {
+ if( bHasImage || !bHasText )
+ {
+ rbImage = true;
+ rbText = false;
+ }
+ else
+ {
+ rbImage = false;
+ rbText = true;
+ }
+ }
+ else if ( eButtonType == ButtonType::TEXT ) // drawing text only
+ {
+ if( bHasText || !bHasImage )
+ {
+ rbImage = false;
+ rbText = true;
+ }
+ else
+ {
+ rbImage = true;
+ rbText = false;
+ }
+ }
+ else // drawing icons and text both
+ {
+ rbImage = true;
+ rbText = true;
+ }
+}
+
+tools::Rectangle ImplToolItem::GetDropDownRect( bool bHorz ) const
+{
+ tools::Rectangle aRect;
+ if( (mnBits & ToolBoxItemBits::DROPDOWN) && !maRect.IsEmpty() )
+ {
+ aRect = maRect;
+ if( mbVisibleText && !bHorz )
+ // item will be rotated -> place dropdown to the bottom
+ aRect.SetTop( aRect.Bottom() - mnDropDownArrowWidth );
+ else
+ // place dropdown to the right
+ aRect.SetLeft( aRect.Right() - mnDropDownArrowWidth );
+ }
+ return aRect;
+}
+
+bool ImplToolItem::IsClipped() const
+{
+ return ( meType == ToolBoxItemType::BUTTON && mbVisible && maRect.IsEmpty() );
+}
+
+bool ImplToolItem::IsItemHidden() const
+{
+ return ( meType == ToolBoxItemType::BUTTON && !mbVisible );
+}
+
+void ToolBox::ImplInvalidate( bool bNewCalc, bool bFullPaint )
+{
+ ImplUpdateInputEnable();
+
+ if ( bNewCalc )
+ mbCalc = true;
+
+ if ( bFullPaint )
+ {
+ mbFormat = true;
+
+ // do we need to redraw?
+ if ( IsReallyVisible() && IsUpdateMode() )
+ {
+ Invalidate( tools::Rectangle( mnLeftBorder, mnTopBorder,
+ mnDX-mnRightBorder-1, mnDY-mnBottomBorder-1 ) );
+ mpIdle->Stop();
+ }
+ }
+ else
+ {
+ if ( !mbFormat )
+ {
+ mbFormat = true;
+
+ // do we need to redraw?
+ if ( IsReallyVisible() && IsUpdateMode() )
+ mpIdle->Start();
+ }
+ }
+
+ // request new layout by layoutmanager
+ CallEventListeners( VclEventId::ToolboxFormatChanged );
+}
+
+void ToolBox::ImplUpdateItem( ImplToolItems::size_type nIndex )
+{
+ // do we need to redraw?
+ if ( !(IsReallyVisible() && IsUpdateMode()) )
+ return;
+
+ if ( nIndex == ITEM_NOTFOUND )
+ {
+ // #i52217# no immediate draw as this might lead to paint problems
+ Invalidate( tools::Rectangle( mnLeftBorder, mnTopBorder, mnDX-mnRightBorder-1, mnDY-mnBottomBorder-1 ) );
+ }
+ else
+ {
+ if ( !mbFormat )
+ {
+ // #i52217# no immediate draw as this might lead to paint problems
+ Invalidate( mpData->m_aItems[nIndex].maRect );
+ }
+ else
+ maPaintRect.Union( mpData->m_aItems[nIndex].maRect );
+ }
+}
+
+void ToolBox::Click()
+{
+ CallEventListeners( VclEventId::ToolboxClick );
+ maClickHdl.Call( this );
+ UITestLogger::getInstance().logAction( this, VclEventId::ToolboxClick);
+}
+
+void ToolBox::DoubleClick()
+{
+ CallEventListeners( VclEventId::ToolboxDoubleClick );
+ maDoubleClickHdl.Call( this );
+}
+
+void ToolBox::Activate()
+{
+ mnActivateCount++;
+ CallEventListeners( VclEventId::ToolboxActivate );
+ maActivateHdl.Call( this );
+}
+
+void ToolBox::Deactivate()
+{
+ mnActivateCount--;
+ CallEventListeners( VclEventId::ToolboxDeactivate );
+ maDeactivateHdl.Call( this );
+}
+
+void ToolBox::Highlight()
+{
+ CallEventListeners( VclEventId::ToolboxHighlight );
+}
+
+FactoryFunction ToolBox::GetUITestFactory() const
+{
+ return ToolBoxUIObject::create;
+}
+
+void ToolBox::Select()
+{
+ VclPtr<vcl::Window> xWindow = this;
+
+ CallEventListeners( VclEventId::ToolboxSelect );
+ maSelectHdl.Call( this );
+
+ if ( xWindow->isDisposed() )
+ return;
+
+ // TODO: GetFloatingWindow in DockingWindow is currently inline, change it to check dockingwrapper
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper && pWrapper->GetFloatingWindow() && static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->IsInPopupMode() )
+ static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->EndPopupMode();
+}
+
+void ToolBox::InsertItem( ToolBoxItemId nItemId, const Image& rImage, ToolBoxItemBits nBits, ImplToolItems::size_type nPos )
+{
+ SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertItem(): ItemId == 0" );
+ SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
+ "ToolBox::InsertItem(): ItemId already exists" );
+
+ // create item and add to list
+ mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(),
+ ImplToolItem( nItemId, rImage, nBits ) );
+ mpData->ImplClearLayoutData();
+
+ ImplInvalidate( true );
+
+ // Notify
+ ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
+ CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >(nNewPos ) );
+}
+
+void ToolBox::InsertItem( ToolBoxItemId nItemId, const Image& rImage, const OUString& rText, ToolBoxItemBits nBits,
+ ImplToolItems::size_type nPos )
+{
+ SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertItem(): ItemId == 0" );
+ SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
+ "ToolBox::InsertItem(): ItemId already exists" );
+
+ // create item and add to list
+ mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(),
+ ImplToolItem( nItemId, rImage, MnemonicGenerator::EraseAllMnemonicChars(rText), nBits ) );
+ mpData->ImplClearLayoutData();
+
+ ImplInvalidate( true );
+
+ // Notify
+ ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
+ CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
+}
+
+void ToolBox::InsertItem( ToolBoxItemId nItemId, const OUString& rText, const OUString& rCommand, ToolBoxItemBits nBits,
+ ImplToolItems::size_type nPos )
+{
+ SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertItem(): ItemId == 0" );
+ SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
+ "ToolBox::InsertItem(): ItemId already exists" );
+
+ // create item and add to list
+ mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(),
+ ImplToolItem( nItemId, MnemonicGenerator::EraseAllMnemonicChars(rText), rCommand, nBits ) );
+ mpData->ImplClearLayoutData();
+
+ ImplInvalidate( true );
+
+ // Notify
+ ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
+ CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
+}
+
+void ToolBox::InsertItem(const OUString& rCommand, const css::uno::Reference<css::frame::XFrame>& rFrame, ToolBoxItemBits nBits,
+ const Size& rRequestedSize, ImplToolItems::size_type nPos)
+{
+ OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, aModuleName);
+ OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
+ OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(rCommand, aProperties, rFrame));
+ Image aImage(CommandInfoProvider::GetImageForCommand(rCommand, rFrame, GetImageSize()));
+
+ ToolBoxItemId nItemId(GetItemCount() + 1);
+ //TODO: ImplToolItems::size_type -> sal_uInt16!
+ InsertItem(nItemId, aLabel, rCommand, nBits, nPos);
+ SetItemImage(nItemId, aImage);
+ SetQuickHelpText(nItemId, aTooltip);
+
+ // set the minimal size
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+ if ( pItem )
+ pItem->maMinimalItemSize = rRequestedSize;
+}
+
+void ToolBox::InsertWindow( ToolBoxItemId nItemId, vcl::Window* pWindow,
+ ToolBoxItemBits nBits, ImplToolItems::size_type nPos )
+{
+ SAL_WARN_IF( !nItemId, "vcl", "ToolBox::InsertWindow(): ItemId == 0" );
+ SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
+ "ToolBox::InsertWindow(): ItemId already exists" );
+
+ // create item and add to list
+ ImplToolItem aItem;
+ aItem.mnId = nItemId;
+ aItem.meType = ToolBoxItemType::BUTTON;
+ aItem.mnBits = nBits;
+ aItem.mpWindow = pWindow;
+ mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(), aItem );
+ mpData->ImplClearLayoutData();
+
+ if ( pWindow )
+ pWindow->Hide();
+
+ ImplInvalidate( true );
+
+ // Notify
+ ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
+ CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
+}
+
+void ToolBox::InsertSpace()
+{
+ // create item and add to list
+ ImplToolItem aItem;
+ aItem.meType = ToolBoxItemType::SPACE;
+ aItem.mbEnabled = false;
+ mpData->m_aItems.push_back( aItem );
+ mpData->ImplClearLayoutData();
+
+ ImplInvalidate();
+
+ // Notify
+ ImplToolItems::size_type nNewPos = mpData->m_aItems.size() - 1;
+ CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
+}
+
+void ToolBox::InsertSeparator( ImplToolItems::size_type nPos, sal_uInt16 nPixSize )
+{
+ // create item and add to list
+ ImplToolItem aItem;
+ aItem.meType = ToolBoxItemType::SEPARATOR;
+ aItem.mbEnabled = false;
+ if ( nPixSize )
+ aItem.mnSepSize = nPixSize;
+ mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(), aItem );
+ mpData->ImplClearLayoutData();
+
+ ImplInvalidate();
+
+ // Notify
+ ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
+ CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
+}
+
+void ToolBox::InsertBreak( ImplToolItems::size_type nPos )
+{
+ // create item and add to list
+ ImplToolItem aItem;
+ aItem.meType = ToolBoxItemType::BREAK;
+ aItem.mbEnabled = false;
+ mpData->m_aItems.insert( (nPos < mpData->m_aItems.size()) ? mpData->m_aItems.begin()+nPos : mpData->m_aItems.end(), aItem );
+ mpData->ImplClearLayoutData();
+
+ ImplInvalidate();
+
+ // Notify
+ ImplToolItems::size_type nNewPos = ( nPos == APPEND ) ? ( mpData->m_aItems.size() - 1 ) : nPos;
+ CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos ) );
+}
+
+void ToolBox::RemoveItem( ImplToolItems::size_type nPos )
+{
+ if( nPos >= mpData->m_aItems.size() )
+ return;
+
+ bool bMustCalc;
+ bMustCalc = mpData->m_aItems[nPos].meType == ToolBoxItemType::BUTTON;
+
+ if ( mpData->m_aItems[nPos].mpWindow )
+ mpData->m_aItems[nPos].mpWindow->Hide();
+
+ // add the removed item to PaintRect
+ maPaintRect.Union( mpData->m_aItems[nPos].maRect );
+
+ // ensure not to delete in the Select-Handler
+ if ( mpData->m_aItems[nPos].mnId == mnCurItemId )
+ mnCurItemId = ToolBoxItemId(0);
+ if ( mpData->m_aItems[nPos].mnId == mnHighItemId )
+ mnHighItemId = ToolBoxItemId(0);
+
+ ImplInvalidate( bMustCalc );
+
+ mpData->m_aItems.erase( mpData->m_aItems.begin()+nPos );
+ mpData->ImplClearLayoutData();
+
+ // Notify
+ CallEventListeners( VclEventId::ToolboxItemRemoved, reinterpret_cast< void* >( nPos ) );
+}
+
+void ToolBox::CopyItem( const ToolBox& rToolBox, ToolBoxItemId nItemId )
+{
+ SAL_WARN_IF( GetItemPos( nItemId ) != ITEM_NOTFOUND, "vcl",
+ "ToolBox::CopyItem(): ItemId already exists" );
+
+ ImplToolItems::size_type nPos = rToolBox.GetItemPos( nItemId );
+
+ // found item
+ if ( nPos == ITEM_NOTFOUND )
+ return;
+
+ // push ToolBox item onto the list
+ ImplToolItem aNewItem = rToolBox.mpData->m_aItems[nPos];
+ // reset state
+ aNewItem.mpWindow = nullptr;
+ aNewItem.mbShowWindow = false;
+
+ mpData->m_aItems.push_back( aNewItem );
+ mpData->ImplClearLayoutData();
+ // redraw ToolBox
+ ImplInvalidate();
+
+ // Notify
+ ImplToolItems::size_type nNewPos2 = mpData->m_aItems.size() - 1;
+ CallEventListeners( VclEventId::ToolboxItemAdded, reinterpret_cast< void* >( nNewPos2 ) );
+}
+
+void ToolBox::Clear()
+{
+ mpData->m_aItems.clear();
+ mpData->ImplClearLayoutData();
+
+ // ensure not to delete in the Select-Handler
+ mnCurItemId = ToolBoxItemId(0);
+ mnHighItemId = ToolBoxItemId(0);
+
+ ImplInvalidate( true, true );
+
+ // Notify
+ CallEventListeners( VclEventId::ToolboxAllItemsChanged );
+}
+
+void ToolBox::SetButtonType( ButtonType eNewType )
+{
+ if ( meButtonType != eNewType )
+ {
+ meButtonType = eNewType;
+
+ // better redraw everything, as otherwise there might be problems
+ // with regions that were copied with CopyBits
+ ImplInvalidate( true );
+ }
+}
+
+void ToolBox::SetToolboxButtonSize( ToolBoxButtonSize eSize )
+{
+ if( mpData->meButtonSize != eSize )
+ {
+ mpData->meButtonSize = eSize;
+ mbCalc = true;
+ mbFormat = true;
+ }
+}
+
+ToolBoxButtonSize ToolBox::GetToolboxButtonSize() const
+{
+ return mpData->meButtonSize;
+}
+
+ImageType ToolBox::GetImageSize() const
+{
+ ImageType eImageType = ImageType::Size16;
+ if (mpData->meButtonSize == ToolBoxButtonSize::Large)
+ eImageType = ImageType::Size26;
+ else if (mpData->meButtonSize == ToolBoxButtonSize::Size32)
+ eImageType = ImageType::Size32;
+
+ return eImageType;
+}
+
+/*static*/ Size ToolBox::GetDefaultImageSize(ToolBoxButtonSize eToolBoxButtonSize)
+{
+ OutputDevice *pDefault = Application::GetDefaultDevice();
+ float fScaleFactor = pDefault ? pDefault->GetDPIScaleFactor() : 1.0;
+
+ Size aUnscaledSize(16, 16);
+
+ if (eToolBoxButtonSize == ToolBoxButtonSize::Large)
+ {
+ OUString iconTheme = Application::GetSettings().GetStyleSettings().DetermineIconTheme();
+ aUnscaledSize = vcl::IconThemeInfo::SizeByThemeName(iconTheme);
+ }
+ else if (eToolBoxButtonSize == ToolBoxButtonSize::Size32)
+ {
+ aUnscaledSize = Size(32, 32);
+ }
+ return Size(aUnscaledSize.Width() * fScaleFactor,
+ aUnscaledSize.Height() * fScaleFactor);
+}
+
+Size ToolBox::GetDefaultImageSize() const
+{
+ return GetDefaultImageSize(GetToolboxButtonSize());
+}
+
+void ToolBox::SetAlign( WindowAlign eNewAlign )
+{
+ if ( meAlign == eNewAlign )
+ return;
+
+ meAlign = eNewAlign;
+
+ if ( ImplIsFloatingMode() )
+ return;
+
+ // set horizontal/vertical alignment
+ if ( (eNewAlign == WindowAlign::Left) || (eNewAlign == WindowAlign::Right) )
+ mbHorz = false;
+ else
+ mbHorz = true;
+
+ // Update the background according to Persona if necessary
+ ImplInitSettings( false, false, true );
+
+ // redraw everything, as the border has changed
+ mbCalc = true;
+ mbFormat = true;
+ if ( IsReallyVisible() && IsUpdateMode() )
+ Invalidate();
+}
+
+void ToolBox::SetLineCount( ImplToolItems::size_type nNewLines )
+{
+ if ( !nNewLines )
+ nNewLines = 1;
+
+ if ( mnLines != nNewLines )
+ {
+ mnLines = nNewLines;
+
+ // better redraw everything, as otherwise there might be problems
+ // with regions that were copied with CopyBits
+ Invalidate();
+ }
+}
+
+ToolBox::ImplToolItems::size_type ToolBox::GetItemCount() const
+{
+ return mpData ? mpData->m_aItems.size() : 0;
+}
+
+ToolBoxItemType ToolBox::GetItemType( ImplToolItems::size_type nPos ) const
+{
+ return (nPos < mpData->m_aItems.size()) ? mpData->m_aItems[nPos].meType : ToolBoxItemType::DONTKNOW;
+}
+
+ToolBox::ImplToolItems::size_type ToolBox::GetItemPos( ToolBoxItemId nItemId ) const
+{
+ if (mpData)
+ {
+ ImplToolItems::size_type nCount = mpData->m_aItems.size();
+ for( ImplToolItems::size_type nPos = 0; nPos < nCount; nPos++ )
+ if( mpData->m_aItems[nPos].mnId == nItemId )
+ return nPos;
+ }
+ return ITEM_NOTFOUND;
+}
+
+ToolBox::ImplToolItems::size_type ToolBox::GetItemPos( const Point& rPos ) const
+{
+ // search the item position on the given point
+ auto it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
+ [&rPos](const ImplToolItem& rItem) { return rItem.maRect.Contains( rPos ); });
+
+ if( it != mpData->m_aItems.end() )
+ return std::distance(mpData->m_aItems.begin(), it);
+
+ return ITEM_NOTFOUND;
+}
+
+ToolBoxItemId ToolBox::GetItemId( ImplToolItems::size_type nPos ) const
+{
+ return (nPos < mpData->m_aItems.size()) ? mpData->m_aItems[nPos].mnId : ToolBoxItemId(0);
+}
+
+ToolBoxItemId ToolBox::GetItemId( const Point& rPos ) const
+{
+ // find item that was clicked
+ auto it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
+ [&rPos](const ImplToolItem& rItem) { return rItem.maRect.Contains( rPos ); });
+
+ if( (it != mpData->m_aItems.end()) && (it->meType == ToolBoxItemType::BUTTON) )
+ return it->mnId;
+
+ return ToolBoxItemId(0);
+}
+
+Size ToolBox::GetItemContentSize( ToolBoxItemId nItemId )
+{
+ if ( mbCalc || mbFormat )
+ ImplFormat();
+
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+ if ( nPos < mpData->m_aItems.size() )
+ return mpData->m_aItems[nPos].maContentSize;
+ else
+ return Size();
+}
+
+ToolBoxItemId ToolBox::GetItemId(const OUString &rCommand) const
+{
+ if (!mpData)
+ return ToolBoxItemId(0);
+
+ auto it = std::find_if(mpData->m_aItems.begin(), mpData->m_aItems.end(),
+ [&rCommand](const ImplToolItem& rItem) { return rItem.maCommandStr == rCommand; });
+ if (it != mpData->m_aItems.end())
+ return it->mnId;
+
+ return ToolBoxItemId(0);
+}
+
+Point ToolBox::ImplGetPopupPosition( const tools::Rectangle& rRect ) const
+{
+ Point aPos;
+ if( !rRect.IsEmpty() )
+ {
+ AbsoluteScreenPixelRectangle aScreen = GetDesktopRectPixel();
+
+ // the popup should be positioned so that it will not cover
+ // the item rect and that it fits the desktop
+ // the preferred direction is always towards the center of
+ // the application window
+
+ AbsoluteScreenPixelPoint devPos; // the position in device coordinates for screen comparison
+ switch( meAlign )
+ {
+ case WindowAlign::Top:
+ aPos = rRect.BottomLeft();
+ aPos.AdjustY( 1 );
+ devPos = OutputToAbsoluteScreenPixel( aPos );
+ if( devPos.Y() >= aScreen.Bottom() )
+ aPos.setY( rRect.Top() );
+ break;
+ case WindowAlign::Bottom:
+ aPos = rRect.TopLeft();
+ aPos.AdjustY( -1 );
+ devPos = OutputToAbsoluteScreenPixel( aPos );
+ if( devPos.Y() <= aScreen.Top() )
+ aPos.setY( rRect.Bottom() );
+ break;
+ case WindowAlign::Left:
+ aPos = rRect.TopRight();
+ aPos.AdjustX( 1 );
+ devPos = OutputToAbsoluteScreenPixel( aPos );
+ if( devPos.X() >= aScreen.Right() )
+ aPos.setX( rRect.Left() );
+ break;
+ case WindowAlign::Right:
+ aPos = rRect.TopLeft();
+ aPos.AdjustX( -1 );
+ devPos = OutputToAbsoluteScreenPixel( aPos );
+ if( devPos.X() <= aScreen.Left() )
+ aPos.setX( rRect.Right() );
+ break;
+ default:
+ break;
+ }
+ }
+ return aPos;
+}
+
+tools::Rectangle ToolBox::GetItemRect( ToolBoxItemId nItemId )
+{
+ if ( mbCalc || mbFormat )
+ ImplFormat();
+
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+ return GetItemPosRect( nPos );
+}
+
+tools::Rectangle ToolBox::GetItemPosRect( ImplToolItems::size_type nPos )
+{
+ if ( mbCalc || mbFormat )
+ ImplFormat();
+
+ if ( nPos < mpData->m_aItems.size() )
+ return mpData->m_aItems[nPos].maRect;
+ else
+ return tools::Rectangle();
+}
+
+tools::Rectangle const & ToolBox::GetOverflowRect() const
+{
+ return mpData->maMenubuttonItem.maRect;
+}
+
+bool ToolBox::ImplHasExternalMenubutton() const
+{
+ // check if the borderwindow (i.e. the decoration) provides the menu button
+ bool bRet = false;
+ if( ImplIsFloatingMode() )
+ {
+ // custom menu is placed in the decoration
+ ImplBorderWindow *pBorderWin = dynamic_cast<ImplBorderWindow*>( GetWindow( GetWindowType::Border ) );
+ if( pBorderWin && !pBorderWin->GetMenuRect().IsEmpty() )
+ bRet = true;
+ }
+ return bRet;
+}
+
+void ToolBox::SetItemBits( ToolBoxItemId nItemId, ToolBoxItemBits nBits )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos < GetItemCount() )
+ {
+ ToolBoxItemBits nOldBits = mpData->m_aItems[nPos].mnBits;
+ mpData->m_aItems[nPos].mnBits = nBits;
+ nBits &= ToolBoxItemBits::LEFT | ToolBoxItemBits::AUTOSIZE | ToolBoxItemBits::DROPDOWN;
+ nOldBits &= ToolBoxItemBits::LEFT | ToolBoxItemBits::AUTOSIZE | ToolBoxItemBits::DROPDOWN;
+ // trigger reformat when the item width has changed (dropdown arrow)
+ bool bFormat = ToolBoxItemBits(nBits & ToolBoxItemBits::DROPDOWN) != ToolBoxItemBits(nOldBits & ToolBoxItemBits::DROPDOWN);
+ if ( nBits != nOldBits )
+ ImplInvalidate( true, bFormat );
+ }
+}
+
+void ToolBox::SetItemWindowNonInteractive(ToolBoxItemId nItemId, bool bNonInteractive)
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos < GetItemCount() )
+ {
+ mpData->m_aItems[nPos].mbNonInteractiveWindow = bNonInteractive;
+ }
+}
+
+ToolBoxItemBits ToolBox::GetItemBits( ToolBoxItemId nItemId ) const
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ return pItem->mnBits;
+ else
+ return ToolBoxItemBits::NONE;
+}
+
+void ToolBox::SetItemExpand( ToolBoxItemId nItemId, bool bExpand )
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+ if (!pItem)
+ return;
+
+ if (pItem->mbExpand != bExpand)
+ {
+ pItem->mbExpand = bExpand;
+ ImplInvalidate(true, true);
+ }
+}
+
+void ToolBox::SetItemData( ToolBoxItemId nItemId, void* pNewData )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos < mpData->m_aItems.size() )
+ {
+ mpData->m_aItems[nPos].mpUserData = pNewData;
+ ImplUpdateItem( nPos );
+ }
+}
+
+void* ToolBox::GetItemData( ToolBoxItemId nItemId ) const
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ return pItem->mpUserData;
+ else
+ return nullptr;
+}
+
+static Image ImplMirrorImage( const Image& rImage )
+{
+ BitmapEx aMirrBitmapEx( rImage.GetBitmapEx() );
+
+ aMirrBitmapEx.Mirror( BmpMirrorFlags::Horizontal );
+
+ return Image( aMirrBitmapEx );
+}
+
+static Image ImplRotImage( const Image& rImage, Degree10 nAngle10 )
+{
+ BitmapEx aRotBitmapEx( rImage.GetBitmapEx() );
+
+ aRotBitmapEx.Rotate( nAngle10, COL_WHITE );
+
+ return Image( aRotBitmapEx );
+}
+
+void ToolBox::SetItemImage( ToolBoxItemId nItemId, const Image& rImage )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos == ITEM_NOTFOUND )
+ return;
+
+ ImplToolItem* pItem = &mpData->m_aItems[nPos];
+ Size aOldSize = pItem->maImage.GetSizePixel();
+
+ pItem->maImage = pItem->mbMirrorMode ? ImplMirrorImage(rImage) : rImage;
+ if (pItem->mnImageAngle != 0_deg10)
+ pItem->maImage = ImplRotImage(pItem->maImage, pItem->mnImageAngle);
+
+ // only once all is calculated, do extra work
+ if (!mbCalc)
+ {
+ if (aOldSize != pItem->maImage.GetSizePixel())
+ ImplInvalidate( true );
+ else
+ ImplUpdateItem( nPos );
+ }
+}
+
+void ToolBox::SetItemImageAngle( ToolBoxItemId nItemId, Degree10 nAngle10 )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos == ITEM_NOTFOUND )
+ return;
+
+ ImplToolItem* pItem = &mpData->m_aItems[nPos];
+ pItem->mnImageAngle = nAngle10;
+}
+
+void ToolBox::SetItemImageMirrorMode( ToolBoxItemId nItemId, bool bMirror )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos == ITEM_NOTFOUND )
+ return;
+
+ ImplToolItem* pItem = &mpData->m_aItems[nPos];
+ pItem->mbMirrorMode = bMirror;
+}
+
+Image ToolBox::GetItemImage(ToolBoxItemId nItemId) const
+{
+ ImplToolItem* pItem = ImplGetItem(nItemId);
+ return pItem ? pItem->maImage : Image();
+}
+
+void ToolBox::SetItemText( ToolBoxItemId nItemId, const OUString& rText )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos == ITEM_NOTFOUND )
+ return;
+
+ ImplToolItem* pItem = &mpData->m_aItems[nPos];
+ // only once all is calculated, do extra work
+ if ( !mbCalc &&
+ ((meButtonType != ButtonType::SYMBOLONLY) || !pItem->maImage) )
+ {
+ tools::Long nOldWidth = GetOutDev()->GetCtrlTextWidth( pItem->maText );
+ pItem->maText = MnemonicGenerator::EraseAllMnemonicChars(rText);
+ mpData->ImplClearLayoutData();
+ if ( nOldWidth != GetOutDev()->GetCtrlTextWidth( pItem->maText ) )
+ ImplInvalidate( true );
+ else
+ ImplUpdateItem( nPos );
+ }
+ else
+ pItem->maText = MnemonicGenerator::EraseAllMnemonicChars(rText);
+
+ // Notify button changed event to prepare accessibility bridge
+ CallEventListeners( VclEventId::ToolboxButtonStateChanged, reinterpret_cast< void* >( nPos ) );
+
+ // Notify
+ CallEventListeners( VclEventId::ToolboxItemTextChanged, reinterpret_cast< void* >( nPos ) );
+}
+
+const OUString& ToolBox::GetItemText( ToolBoxItemId nItemId ) const
+{
+
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ assert( pItem );
+
+ return pItem->maText;
+}
+
+void ToolBox::SetItemWindow( ToolBoxItemId nItemId, vcl::Window* pNewWindow )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos != ITEM_NOTFOUND )
+ {
+ ImplToolItem* pItem = &mpData->m_aItems[nPos];
+ pItem->mpWindow = pNewWindow;
+ if ( pNewWindow )
+ pNewWindow->Hide();
+ ImplInvalidate( true );
+ CallEventListeners( VclEventId::ToolboxItemWindowChanged, reinterpret_cast< void* >( nPos ) );
+ }
+}
+
+vcl::Window* ToolBox::GetItemWindow( ToolBoxItemId nItemId ) const
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ return pItem->mpWindow;
+ else
+ return nullptr;
+}
+
+void ToolBox::EndSelection()
+{
+ if ( mbDrag )
+ {
+ // reset
+ mbDrag = false;
+ if (mnCurPos != ITEM_NOTFOUND)
+ InvalidateItem(mnCurPos);
+ EndTracking();
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ Deactivate();
+ }
+
+ mnCurPos = ITEM_NOTFOUND;
+ mnCurItemId = ToolBoxItemId(0);
+ mnDownItemId = ToolBoxItemId(0);
+ mnMouseModifier = 0;
+}
+
+void ToolBox::SetItemDown( ToolBoxItemId nItemId, bool bDown )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos == ITEM_NOTFOUND )
+ return;
+
+ if ( bDown )
+ {
+ if ( nPos != mnCurPos )
+ {
+ mnCurPos = nPos;
+ InvalidateItem(mnCurPos);
+ GetOutDev()->Flush();
+ }
+ }
+ else
+ {
+ if ( nPos == mnCurPos )
+ {
+ InvalidateItem(mnCurPos);
+ GetOutDev()->Flush();
+ mnCurPos = ITEM_NOTFOUND;
+ }
+ }
+
+ if ( mbDrag )
+ {
+ mbDrag = false;
+ EndTracking();
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ Deactivate();
+ }
+
+ mnCurItemId = ToolBoxItemId(0);
+ mnDownItemId = ToolBoxItemId(0);
+ mnMouseModifier = 0;
+}
+
+void ToolBox::SetItemState( ToolBoxItemId nItemId, TriState eState )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos == ITEM_NOTFOUND )
+ return;
+
+ ImplToolItem* pItem = &mpData->m_aItems[nPos];
+
+ // the state has changed
+ if ( pItem->meState == eState )
+ return;
+
+ // if RadioCheck, un-check the previous
+ if ( (eState == TRISTATE_TRUE) && (pItem->mnBits & ToolBoxItemBits::AUTOCHECK) &&
+ (pItem->mnBits & ToolBoxItemBits::RADIOCHECK) )
+ {
+ ImplToolItem* pGroupItem;
+ ImplToolItems::size_type nGroupPos;
+ ImplToolItems::size_type nItemCount = GetItemCount();
+
+ nGroupPos = nPos;
+ while ( nGroupPos )
+ {
+ pGroupItem = &mpData->m_aItems[nGroupPos-1];
+ if ( pGroupItem->mnBits & ToolBoxItemBits::RADIOCHECK )
+ {
+ if ( pGroupItem->meState != TRISTATE_FALSE )
+ SetItemState( pGroupItem->mnId, TRISTATE_FALSE );
+ }
+ else
+ break;
+ nGroupPos--;
+ }
+
+ nGroupPos = nPos+1;
+ while ( nGroupPos < nItemCount )
+ {
+ pGroupItem = &mpData->m_aItems[nGroupPos];
+ if ( pGroupItem->mnBits & ToolBoxItemBits::RADIOCHECK )
+ {
+ if ( pGroupItem->meState != TRISTATE_FALSE )
+ SetItemState( pGroupItem->mnId, TRISTATE_FALSE );
+ }
+ else
+ break;
+ nGroupPos++;
+ }
+ }
+
+ pItem->meState = eState;
+ ImplUpdateItem( nPos );
+
+ // Notify button changed event to prepare accessibility bridge
+ CallEventListeners( VclEventId::ToolboxButtonStateChanged, reinterpret_cast< void* >( nPos ) );
+
+ // Call accessible listener to notify state_changed event
+ CallEventListeners( VclEventId::ToolboxItemUpdated, reinterpret_cast< void* >(nPos) );
+}
+
+TriState ToolBox::GetItemState( ToolBoxItemId nItemId ) const
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ return pItem->meState;
+ else
+ return TRISTATE_FALSE;
+}
+
+void ToolBox::EnableItem( ToolBoxItemId nItemId, bool bEnable )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+
+ if ( nPos == ITEM_NOTFOUND )
+ return;
+
+ ImplToolItem* pItem = &mpData->m_aItems[nPos];
+ if ( pItem->mbEnabled == bEnable )
+ return;
+
+ pItem->mbEnabled = bEnable;
+
+ // if existing, also redraw the window
+ if ( pItem->mpWindow )
+ pItem->mpWindow->Enable( pItem->mbEnabled );
+
+ // update item
+ ImplUpdateItem( nPos );
+
+ ImplUpdateInputEnable();
+
+ // Notify button changed event to prepare accessibility bridge
+ CallEventListeners( VclEventId::ToolboxButtonStateChanged, reinterpret_cast< void* >( nPos ) );
+
+ CallEventListeners( bEnable ? VclEventId::ToolboxItemEnabled : VclEventId::ToolboxItemDisabled, reinterpret_cast< void* >( nPos ) );
+}
+
+bool ToolBox::IsItemEnabled( ToolBoxItemId nItemId ) const
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ return pItem->mbEnabled;
+ else
+ return false;
+}
+
+void ToolBox::ShowItem( ToolBoxItemId nItemId, bool bVisible )
+{
+ ImplToolItems::size_type nPos = GetItemPos( nItemId );
+ mpData->ImplClearLayoutData();
+
+ if ( nPos != ITEM_NOTFOUND )
+ {
+ ImplToolItem* pItem = &mpData->m_aItems[nPos];
+ if ( pItem->mbVisible != bVisible )
+ {
+ pItem->mbVisible = bVisible;
+ ImplInvalidate();
+ }
+ }
+}
+
+bool ToolBox::IsItemClipped( ToolBoxItemId nItemId ) const
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ return pItem->IsClipped();
+ else
+ return false;
+}
+
+bool ToolBox::IsItemVisible( ToolBoxItemId nItemId ) const
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ return pItem->mbVisible;
+ else
+ return false;
+}
+
+bool ToolBox::IsItemReallyVisible( ToolBoxItemId nItemId ) const
+{
+ // is the item on the visible area of the toolbox?
+ bool bRet = false;
+ tools::Rectangle aRect( mnLeftBorder, mnTopBorder, mnDX-mnRightBorder, mnDY-mnBottomBorder );
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem && pItem->mbVisible &&
+ !pItem->maRect.IsEmpty() && aRect.Overlaps( pItem->maRect ) )
+ {
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void ToolBox::SetItemCommand(ToolBoxItemId nItemId, const OUString& rCommand)
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if (pItem)
+ pItem->maCommandStr = rCommand;
+}
+
+OUString ToolBox::GetItemCommand( ToolBoxItemId nItemId ) const
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if (pItem)
+ return pItem->maCommandStr;
+
+ return OUString();
+}
+
+void ToolBox::SetQuickHelpText( ToolBoxItemId nItemId, const OUString& rText )
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ pItem->maQuickHelpText = rText;
+}
+
+OUString ToolBox::GetQuickHelpText( ToolBoxItemId nItemId ) const
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ return pItem->maQuickHelpText;
+ else
+ return OUString();
+}
+
+void ToolBox::SetHelpText( ToolBoxItemId nItemId, const OUString& rText )
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ pItem->maHelpText = rText;
+}
+
+const OUString& ToolBox::GetHelpText( ToolBoxItemId nItemId ) const
+{
+ return ImplGetHelpText( nItemId );
+}
+
+void ToolBox::SetHelpId( ToolBoxItemId nItemId, const OUString& rHelpId )
+{
+ ImplToolItem* pItem = ImplGetItem( nItemId );
+
+ if ( pItem )
+ pItem->maHelpId = rHelpId;
+}
+
+// disable key input if all items are disabled
+void ToolBox::ImplUpdateInputEnable()
+{
+ mpData->mbKeyInputDisabled = std::none_of(mpData->m_aItems.begin(), mpData->m_aItems.end(),
+ [](const ImplToolItem& rItem) {
+ // at least one useful entry
+ return rItem.mbEnabled;
+ });
+}
+
+void ToolBox::ImplFillLayoutData()
+{
+ mpData->m_pLayoutData.emplace();
+
+ ImplToolItems::size_type nCount = mpData->m_aItems.size();
+ for( ImplToolItems::size_type i = 0; i < nCount; i++ )
+ {
+ ImplToolItem* pItem = &mpData->m_aItems[i];
+
+ // only draw, if the rectangle is within PaintRectangle
+ if (!pItem->maRect.IsEmpty())
+ InvalidateItem(i);
+ }
+}
+
+OUString ToolBox::GetDisplayText() const
+{
+ if( ! mpData->m_pLayoutData )
+ const_cast<ToolBox *>(this)->ImplFillLayoutData();
+ return mpData->m_pLayoutData ? mpData->m_pLayoutData->m_aDisplayText : OUString();
+}
+
+tools::Rectangle ToolBox::GetCharacterBounds( ToolBoxItemId nItemID, tools::Long nIndex )
+{
+ tools::Long nItemIndex = -1;
+ if( ! mpData->m_pLayoutData )
+ ImplFillLayoutData();
+ if( mpData->m_pLayoutData )
+ {
+ for( size_t i = 0; i < mpData->m_pLayoutData->m_aLineItemIds.size(); i++ )
+ {
+ if( mpData->m_pLayoutData->m_aLineItemIds[i] == nItemID )
+ {
+ nItemIndex = mpData->m_pLayoutData->m_aLineIndices[i];
+ break;
+ }
+ }
+ }
+ return (mpData->m_pLayoutData && nItemIndex != -1) ? mpData->m_pLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : tools::Rectangle();
+}
+
+tools::Long ToolBox::GetIndexForPoint( const Point& rPoint, ToolBoxItemId& rItemID )
+{
+ tools::Long nIndex = -1;
+ rItemID = ToolBoxItemId(0);
+ if( ! mpData->m_pLayoutData )
+ ImplFillLayoutData();
+ if( mpData->m_pLayoutData )
+ {
+ nIndex = mpData->m_pLayoutData->GetIndexForPoint( rPoint );
+ for( size_t i = 0; i < mpData->m_pLayoutData->m_aLineIndices.size(); i++ )
+ {
+ if( mpData->m_pLayoutData->m_aLineIndices[i] <= nIndex &&
+ (i == mpData->m_pLayoutData->m_aLineIndices.size()-1 || mpData->m_pLayoutData->m_aLineIndices[i+1] > nIndex) )
+ {
+ rItemID = mpData->m_pLayoutData->m_aLineItemIds[i];
+ break;
+ }
+ }
+ }
+ return nIndex;
+}
+
+void ToolBox::SetDropdownClickHdl( const Link<ToolBox *, void>& rLink )
+{
+ if (mpData != nullptr) {
+ mpData->maDropdownClickHdl = rLink;
+ }
+}
+
+void ToolBox::SetMenuType( ToolBoxMenuType aType )
+{
+ if( aType == mpData->maMenuType )
+ return;
+
+ mpData->maMenuType = aType;
+ if( IsFloatingMode() )
+ {
+ // the menu button may have to be moved into the decoration which changes the layout
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ pWrapper->ShowMenuTitleButton( bool( aType & ToolBoxMenuType::Customize) );
+
+ mbFormat = true;
+ ImplFormat();
+ ImplSetMinMaxFloatSize();
+ }
+ else
+ {
+ // trigger redraw of menu button
+ if( !mpData->maMenubuttonItem.maRect.IsEmpty() )
+ Invalidate(mpData->maMenubuttonItem.maRect);
+ }
+}
+
+ToolBoxMenuType ToolBox::GetMenuType() const
+{
+ return mpData->maMenuType;
+}
+
+bool ToolBox::IsMenuEnabled() const
+{
+ return mpData->maMenuType != ToolBoxMenuType::NONE;
+}
+
+PopupMenu* ToolBox::GetMenu() const
+{
+ return mpData == nullptr ? nullptr : mpData->mpMenu;
+}
+
+void ToolBox::SetMenuExecuteHdl( const Link<ToolBox *, void>& rLink )
+{
+ mpData->maMenuButtonHdl = rLink;
+}
+
+bool ToolBox::ImplHasClippedItems()
+{
+ // are any items currently clipped ?
+ ImplFormat();
+ return std::any_of(mpData->m_aItems.begin(), mpData->m_aItems.end(),
+ [](const ImplToolItem& rItem) { return rItem.IsClipped(); });
+}
+
+namespace
+{
+ MenuItemBits ConvertBitsFromToolBoxToMenu(ToolBoxItemBits nToolItemBits)
+ {
+ MenuItemBits nMenuItemBits = MenuItemBits::NONE;
+ if ((nToolItemBits & ToolBoxItemBits::CHECKABLE) ||
+ (nToolItemBits & ToolBoxItemBits::DROPDOWN))
+ {
+ nMenuItemBits |= MenuItemBits::CHECKABLE;
+ }
+ return nMenuItemBits;
+ }
+}
+
+void ToolBox::UpdateCustomMenu()
+{
+ // fill clipped items into menu
+ PopupMenu *pMenu = GetMenu();
+ pMenu->Clear();
+
+ // add menu items: first the overflow items, then hidden items, both in the
+ // order they would usually appear in the toolbar. Separators that would be
+ // in the toolbar are ignored as they would introduce too much clutter,
+ // instead we have a single separator to help distinguish between overflow
+ // and hidden items.
+ if ( mpData->m_aItems.empty() )
+ return;
+
+ // nStartPos will hold the number of clipped items appended from first loop
+ for ( const auto& rItem : mpData->m_aItems )
+ {
+ if( rItem.IsClipped() )
+ {
+ sal_uInt16 id = sal_uInt16(rItem.mnId) + TOOLBOX_MENUITEM_START;
+ MenuItemBits nMenuItemBits = ConvertBitsFromToolBoxToMenu(rItem.mnBits);
+ pMenu->InsertItem( id, rItem.maText, rItem.maImage, nMenuItemBits);
+ pMenu->SetItemCommand( id, rItem.maCommandStr );
+ pMenu->EnableItem( id, rItem.mbEnabled );
+ pMenu->CheckItem ( id, rItem.meState == TRISTATE_TRUE );
+ }
+ }
+
+ // add a separator below the inserted clipped-items
+ pMenu->InsertSeparator();
+
+ // now append the items that are explicitly disabled
+ for ( const auto& rItem : mpData->m_aItems )
+ {
+ if( rItem.IsItemHidden() )
+ {
+ sal_uInt16 id = sal_uInt16(rItem.mnId) + TOOLBOX_MENUITEM_START;
+ MenuItemBits nMenuItemBits = ConvertBitsFromToolBoxToMenu(rItem.mnBits);
+ pMenu->InsertItem( id, rItem.maText, rItem.maImage, nMenuItemBits );
+ pMenu->SetItemCommand( id, rItem.maCommandStr );
+ pMenu->EnableItem( id, rItem.mbEnabled );
+ pMenu->CheckItem( id, rItem.meState == TRISTATE_TRUE );
+ }
+ }
+}
+
+IMPL_LINK( ToolBox, ImplCustomMenuListener, VclMenuEvent&, rEvent, void )
+{
+ if( rEvent.GetMenu() == GetMenu() && rEvent.GetId() == VclEventId::MenuSelect )
+ {
+ sal_uInt16 id = GetMenu()->GetItemId( rEvent.GetItemPos() );
+ if( id >= TOOLBOX_MENUITEM_START )
+ TriggerItem( ToolBoxItemId(id - TOOLBOX_MENUITEM_START) );
+ }
+}
+
+void ToolBox::ExecuteCustomMenu( const tools::Rectangle& rRect )
+{
+ if ( !IsMenuEnabled() || ImplIsInPopupMode() )
+ return;
+
+ UpdateCustomMenu();
+
+ if( GetMenuType() & ToolBoxMenuType::Customize )
+ // call button handler to allow for menu customization
+ mpData->maMenuButtonHdl.Call( this );
+
+ GetMenu()->AddEventListener( LINK( this, ToolBox, ImplCustomMenuListener ) );
+
+ // make sure all disabled entries will be shown
+ GetMenu()->SetMenuFlags(
+ GetMenu()->GetMenuFlags() | MenuFlags::AlwaysShowDisabledEntries );
+
+ // toolbox might be destroyed during execute
+ bool bBorderDel = false;
+
+ VclPtr<vcl::Window> pWin = this;
+ tools::Rectangle aMenuRect = rRect;
+ VclPtr<ImplBorderWindow> pBorderWin;
+ if( aMenuRect.IsEmpty() && IsFloatingMode() )
+ {
+ // custom menu is placed in the decoration
+ pBorderWin = dynamic_cast<ImplBorderWindow*>( GetWindow( GetWindowType::Border ) );
+ if( pBorderWin && !pBorderWin->GetMenuRect().IsEmpty() )
+ {
+ pWin = pBorderWin;
+ aMenuRect = pBorderWin->GetMenuRect();
+ bBorderDel = true;
+ }
+ }
+
+ sal_uInt16 uId = GetMenu()->Execute( pWin, tools::Rectangle( ImplGetPopupPosition( aMenuRect ), Size() ),
+ PopupMenuFlags::ExecuteDown | PopupMenuFlags::NoMouseUpClose );
+
+ if ( pWin->isDisposed() )
+ return;
+
+ if( GetMenu() )
+ GetMenu()->RemoveEventListener( LINK( this, ToolBox, ImplCustomMenuListener ) );
+ if( bBorderDel )
+ {
+ if( pBorderWin->isDisposed() )
+ return;
+ }
+
+ pWin->Invalidate( aMenuRect );
+
+ if( uId )
+ GrabFocusToDocument();
+}
+
+// checks override first, useful during calculation of sizes
+bool ToolBox::ImplIsFloatingMode() const
+{
+ SAL_WARN_IF( mpData->mbAssumeDocked && mpData->mbAssumeFloating, "vcl",
+ "cannot assume docked and floating" );
+
+ if( mpData->mbAssumeDocked )
+ return false;
+ else if( mpData->mbAssumeFloating )
+ return true;
+ else
+ return IsFloatingMode();
+}
+
+// checks override first, useful during calculation of sizes
+bool ToolBox::ImplIsInPopupMode() const
+{
+ if( mpData->mbAssumePopupMode )
+ return true;
+ else
+ {
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ return ( pWrapper && pWrapper->GetFloatingWindow() && static_cast<FloatingWindow*>(pWrapper->GetFloatingWindow())->IsInPopupMode() );
+ }
+}
+
+void ToolBox::Lock( bool bLock )
+{
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( !pWrapper )
+ return;
+ if( mpData->mbIsLocked != bLock )
+ {
+ mpData->mbIsLocked = bLock;
+ if( !ImplIsFloatingMode() )
+ {
+ mbCalc = true;
+ mbFormat = true;
+ SetSizePixel( CalcWindowSizePixel(1) );
+ Invalidate();
+ }
+ }
+}
+
+bool ToolBox::AlwaysLocked()
+{
+ // read config item to determine toolbox behaviour, used for subtoolbars
+
+ static int nAlwaysLocked = -1;
+
+ if( nAlwaysLocked == -1 )
+ {
+ nAlwaysLocked = 0; // ask configuration only once
+
+ utl::OConfigurationNode aNode = utl::OConfigurationTreeRoot::tryCreateWithComponentContext(
+ comphelper::getProcessComponentContext(),
+ "/org.openoffice.Office.UI.GlobalSettings/Toolbars" ); // note: case sensitive !
+ if ( aNode.isValid() )
+ {
+ // feature enabled ?
+ bool bStatesEnabled = bool();
+ css::uno::Any aValue = aNode.getNodeValue( "StatesEnabled" );
+ if( aValue >>= bStatesEnabled )
+ {
+ if( bStatesEnabled )
+ {
+ // now read the locking state
+ utl::OConfigurationNode aNode2 = utl::OConfigurationTreeRoot::tryCreateWithComponentContext(
+ comphelper::getProcessComponentContext(),
+ "/org.openoffice.Office.UI.GlobalSettings/Toolbars/States" ); // note: case sensitive !
+
+ bool bLocked = bool();
+ css::uno::Any aValue2 = aNode2.getNodeValue( "Locked" );
+ if( aValue2 >>= bLocked )
+ nAlwaysLocked = bLocked ? 1 : 0;
+ }
+ }
+ }
+ }
+
+ return nAlwaysLocked == 1;
+}
+
+bool ToolBox::WillUsePopupMode() const
+{
+ return mpData->mbWillUsePopupMode;
+}
+
+void ToolBox::WillUsePopupMode( bool b )
+{
+ mpData->mbWillUsePopupMode = b;
+}
+
+void ToolBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ DockingWindow::DumpAsPropertyTree(rJsonWriter);
+
+ auto childrenNode = rJsonWriter.startArray("children");
+ for (ToolBox::ImplToolItems::size_type i = 0; i < GetItemCount(); ++i)
+ {
+ auto childNode = rJsonWriter.startStruct();
+ ToolBoxItemId nId = GetItemId(i);
+
+ vcl::Window* pWindow = GetItemWindow(nId);
+ if (pWindow)
+ {
+ pWindow->DumpAsPropertyTree(rJsonWriter);
+ }
+ else
+ {
+ OUString sCommand = GetItemCommand(nId);
+ rJsonWriter.put("type", "toolitem");
+ rJsonWriter.put("text", GetItemText(nId));
+ rJsonWriter.put("command", sCommand);
+ if (IsItemChecked(nId))
+ rJsonWriter.put("selected", true);
+ if (!IsItemVisible(nId))
+ rJsonWriter.put("visible", false);
+ if (GetItemBits(nId) & ToolBoxItemBits::DROPDOWN)
+ rJsonWriter.put("dropdown", true);
+ if (!IsItemEnabled(nId))
+ rJsonWriter.put("enabled", false);
+
+ Image aImage = GetItemImage(nId);
+ if (!sCommand.startsWith(".uno:") && !!aImage)
+ {
+ SvMemoryStream aOStm(6535, 6535);
+ if(GraphicConverter::Export(aOStm, aImage.GetBitmapEx(), ConvertDataFormat::PNG) == ERRCODE_NONE)
+ {
+ css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
+ OStringBuffer aBuffer("data:image/png;base64,");
+ ::comphelper::Base64::encode(aBuffer, aSeq);
+ rJsonWriter.put("image", aBuffer);
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/window.cxx b/vcl/source/window/window.cxx
new file mode 100644
index 0000000000..c3fa7fb3d7
--- /dev/null
+++ b/vcl/source/window/window.cxx
@@ -0,0 +1,3979 @@
+/* -*- 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 <rtl/strbuf.hxx>
+#include <sal/log.hxx>
+
+#include <sal/types.h>
+#include <comphelper/diagnose_ex.hxx>
+#include <vcl/salgtype.hxx>
+#include <vcl/event.hxx>
+#include <vcl/help.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/vclevent.hxx>
+#include <vcl/window.hxx>
+#include <vcl/syswin.hxx>
+#include <vcl/dockwin.hxx>
+#include <vcl/wall.hxx>
+#include <vcl/toolkit/fixed.hxx>
+#include <vcl/toolkit/button.hxx>
+#include <vcl/taskpanelist.hxx>
+#include <vcl/toolkit/unowrap.hxx>
+#include <vcl/lazydelete.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/IDialogRenderable.hxx>
+
+#include <vcl/uitest/uiobject.hxx>
+
+#include <ImplOutDevData.hxx>
+#include <impfontcache.hxx>
+#include <salframe.hxx>
+#include <salobj.hxx>
+#include <salinst.hxx>
+#include <salgdi.hxx>
+#include <svdata.hxx>
+#include <window.h>
+#include <toolbox.h>
+#include <brdwin.hxx>
+#include <helpwin.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRelation.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
+#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
+#include <com/sun/star/rendering/CanvasFactory.hpp>
+#include <com/sun/star/rendering/XSpriteCanvas.hpp>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/configmgr.hxx>
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+#include <tools/json_writer.hxx>
+#include <boost/property_tree/ptree.hpp>
+
+#include <cassert>
+#include <typeinfo>
+
+#ifdef _WIN32 // see #140456#
+#include <win/salframe.h>
+#endif
+
+#include "impldockingwrapper.hxx"
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::datatransfer::clipboard;
+using namespace ::com::sun::star::datatransfer::dnd;
+
+namespace vcl {
+
+Window::Window( WindowType nType )
+ : mpWindowImpl(new WindowImpl( *this, nType ))
+{
+ // true: this outdev will be mirrored if RTL window layout (UI mirroring) is globally active
+ mpWindowImpl->mxOutDev->mbEnableRTL = AllSettings::GetLayoutRTL();
+}
+
+Window::Window( vcl::Window* pParent, WinBits nStyle )
+ : mpWindowImpl(new WindowImpl( *this, WindowType::WINDOW ))
+{
+ // true: this outdev will be mirrored if RTL window layout (UI mirroring) is globally active
+ mpWindowImpl->mxOutDev->mbEnableRTL = AllSettings::GetLayoutRTL();
+
+ ImplInit( pParent, nStyle, nullptr );
+}
+
+#if OSL_DEBUG_LEVEL > 0
+namespace
+{
+ OString lcl_createWindowInfo(const vcl::Window* pWindow)
+ {
+ // skip border windows, they do not carry information that
+ // would help with diagnosing the problem
+ const vcl::Window* pTempWin( pWindow );
+ while ( pTempWin && pTempWin->GetType() == WindowType::BORDERWINDOW ) {
+ pTempWin = pTempWin->GetWindow( GetWindowType::FirstChild );
+ }
+ // check if pTempWin is not null, otherwise use the
+ // original address
+ if ( pTempWin ) {
+ pWindow = pTempWin;
+ }
+
+ return OString::Concat(" ") +
+ typeid( *pWindow ).name() +
+ "(" +
+ OUStringToOString(
+ pWindow->GetText(),
+ RTL_TEXTENCODING_UTF8
+ ) +
+ ")";
+ }
+}
+#endif
+
+void Window::dispose()
+{
+ assert( mpWindowImpl );
+ assert( !mpWindowImpl->mbInDispose ); // should only be called from disposeOnce()
+ assert( (!mpWindowImpl->mpParent ||
+ mpWindowImpl->mpParent->mpWindowImpl) &&
+ "vcl::Window child should have its parent disposed first" );
+
+ // remove Key and Mouse events issued by Application::PostKey/MouseEvent
+ Application::RemoveMouseAndKeyEvents( this );
+
+ // Dispose of the canvas implementation (which, currently, has an
+ // own wrapper window as a child to this one.
+ GetOutDev()->ImplDisposeCanvas();
+
+ mpWindowImpl->mbInDispose = true;
+
+ CallEventListeners( VclEventId::ObjectDying );
+
+ // do not send child events for frames that were registered as native frames
+ if( !IsNativeFrame() && mpWindowImpl->mbReallyVisible )
+ if ( ImplIsAccessibleCandidate() && GetAccessibleParentWindow() )
+ GetAccessibleParentWindow()->CallEventListeners( VclEventId::WindowChildDestroyed, this );
+
+ // remove associated data structures from dockingmanager
+ ImplGetDockingManager()->RemoveWindow( this );
+
+ // remove ownerdraw decorated windows from list in the top-most frame window
+ if( (GetStyle() & WB_OWNERDRAWDECORATION) && mpWindowImpl->mbFrame )
+ {
+ ::std::vector< VclPtr<vcl::Window> >& rList = ImplGetOwnerDrawList();
+ auto p = ::std::find( rList.begin(), rList.end(), VclPtr<vcl::Window>(this) );
+ if( p != rList.end() )
+ rList.erase( p );
+ }
+
+ // shutdown drag and drop
+ Reference < XComponent > xDnDComponent( mpWindowImpl->mxDNDListenerContainer, UNO_QUERY );
+
+ if( xDnDComponent.is() )
+ xDnDComponent->dispose();
+
+ if( mpWindowImpl->mbFrame && mpWindowImpl->mpFrameData )
+ {
+ try
+ {
+ // deregister drop target listener
+ if( mpWindowImpl->mpFrameData->mxDropTargetListener.is() )
+ {
+ Reference< XDragGestureRecognizer > xDragGestureRecognizer(mpWindowImpl->mpFrameData->mxDragSource, UNO_QUERY);
+ if( xDragGestureRecognizer.is() )
+ {
+ xDragGestureRecognizer->removeDragGestureListener(
+ Reference< XDragGestureListener > (mpWindowImpl->mpFrameData->mxDropTargetListener, UNO_QUERY));
+ }
+
+ mpWindowImpl->mpFrameData->mxDropTarget->removeDropTargetListener( mpWindowImpl->mpFrameData->mxDropTargetListener );
+ mpWindowImpl->mpFrameData->mxDropTargetListener.clear();
+ }
+
+ // shutdown drag and drop for this frame window
+ Reference< XComponent > xComponent( mpWindowImpl->mpFrameData->mxDropTarget, UNO_QUERY );
+
+ // DNDEventDispatcher does not hold a reference of the DropTarget,
+ // so it's ok if it does not support XComponent
+ if( xComponent.is() )
+ xComponent->dispose();
+ }
+ catch (const Exception&)
+ {
+ // can be safely ignored here.
+ }
+ }
+
+ UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper( false );
+ if ( pWrapper )
+ pWrapper->WindowDestroyed( this );
+
+ // MT: Must be called after WindowDestroyed!
+ // Otherwise, if the accessible is a VCLXWindow, it will try to destroy this window again!
+ // But accessibility implementations from applications need this dispose.
+ if ( mpWindowImpl->mxAccessible.is() )
+ {
+ Reference< XComponent> xC( mpWindowImpl->mxAccessible, UNO_QUERY );
+ if ( xC.is() )
+ xC->dispose();
+ }
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ if ( ImplGetSVHelpData().mpHelpWin && (ImplGetSVHelpData().mpHelpWin->GetParent() == this) )
+ ImplDestroyHelpWindow( true );
+
+ SAL_WARN_IF(pSVData->mpWinData->mpTrackWin.get() == this, "vcl.window",
+ "Window::~Window(): Window is in TrackingMode");
+ SAL_WARN_IF(IsMouseCaptured(), "vcl.window",
+ "Window::~Window(): Window has the mouse captured");
+
+ // due to old compatibility
+ if (pSVData->mpWinData->mpTrackWin == this)
+ EndTracking();
+ if (IsMouseCaptured())
+ ReleaseMouse();
+
+#if OSL_DEBUG_LEVEL > 0
+ // always perform these tests in debug builds
+ {
+ OStringBuffer aErrorStr;
+ bool bError = false;
+ vcl::Window* pTempWin;
+
+ if ( mpWindowImpl->mpFirstChild )
+ {
+ OStringBuffer aTempStr = "Window (" +
+ lcl_createWindowInfo(this) +
+ ") with live children destroyed: ";
+ pTempWin = mpWindowImpl->mpFirstChild;
+ while ( pTempWin )
+ {
+ aTempStr.append(lcl_createWindowInfo(pTempWin));
+ pTempWin = pTempWin->mpWindowImpl->mpNext;
+ }
+ OSL_FAIL( aTempStr.getStr() );
+ Application::Abort(OStringToOUString(aTempStr, RTL_TEXTENCODING_UTF8));
+ }
+
+ if (mpWindowImpl->mpFrameData != nullptr)
+ {
+ pTempWin = mpWindowImpl->mpFrameData->mpFirstOverlap;
+ while ( pTempWin )
+ {
+ if ( ImplIsRealParentPath( pTempWin ) )
+ {
+ bError = true;
+ aErrorStr.append(lcl_createWindowInfo(pTempWin));
+ }
+ pTempWin = pTempWin->mpWindowImpl->mpNextOverlap;
+ }
+ if ( bError )
+ {
+ OString aTempStr =
+ "Window (" +
+ lcl_createWindowInfo(this) +
+ ") with live SystemWindows destroyed: " +
+ aErrorStr;
+ OSL_FAIL(aTempStr.getStr());
+ Application::Abort(OStringToOUString(aTempStr, RTL_TEXTENCODING_UTF8));
+ }
+ }
+
+ bError = false;
+ pTempWin = pSVData->maFrameData.mpFirstFrame;
+ while ( pTempWin )
+ {
+ if ( ImplIsRealParentPath( pTempWin ) )
+ {
+ bError = true;
+ aErrorStr.append(lcl_createWindowInfo(pTempWin));
+ }
+ pTempWin = pTempWin->mpWindowImpl->mpFrameData->mpNextFrame;
+ }
+ if ( bError )
+ {
+ OString aTempStr = "Window (" +
+ lcl_createWindowInfo(this) +
+ ") with live SystemWindows destroyed: " +
+ aErrorStr;
+ OSL_FAIL( aTempStr.getStr() );
+ Application::Abort(OStringToOUString(aTempStr, RTL_TEXTENCODING_UTF8));
+ }
+
+ if ( mpWindowImpl->mpFirstOverlap )
+ {
+ OStringBuffer aTempStr = "Window (" +
+ lcl_createWindowInfo(this) +
+ ") with live SystemWindows destroyed: ";
+ pTempWin = mpWindowImpl->mpFirstOverlap;
+ while ( pTempWin )
+ {
+ aTempStr.append(lcl_createWindowInfo(pTempWin));
+ pTempWin = pTempWin->mpWindowImpl->mpNext;
+ }
+ OSL_FAIL( aTempStr.getStr() );
+ Application::Abort(OStringToOUString(aTempStr, RTL_TEXTENCODING_UTF8));
+ }
+
+ vcl::Window* pMyParent = GetParent();
+ SystemWindow* pMySysWin = nullptr;
+
+ while ( pMyParent )
+ {
+ if ( pMyParent->IsSystemWindow() )
+ {
+ pMySysWin = dynamic_cast<SystemWindow *>(pMyParent);
+ }
+ pMyParent = pMyParent->GetParent();
+ }
+ if ( pMySysWin && pMySysWin->ImplIsInTaskPaneList( this ) )
+ {
+ OString aTempStr = "Window (" +
+ lcl_createWindowInfo(this) +
+ ") still in TaskPanelList!";
+ OSL_FAIL( aTempStr.getStr() );
+ Application::Abort(OStringToOUString(aTempStr, RTL_TEXTENCODING_UTF8));
+ }
+ }
+#endif
+
+ if( mpWindowImpl->mbIsInTaskPaneList )
+ {
+ vcl::Window* pMyParent = GetParent();
+ SystemWindow* pMySysWin = nullptr;
+
+ while ( pMyParent )
+ {
+ if ( pMyParent->IsSystemWindow() )
+ {
+ pMySysWin = dynamic_cast<SystemWindow *>(pMyParent);
+ }
+ pMyParent = pMyParent->GetParent();
+ }
+ if ( pMySysWin && pMySysWin->ImplIsInTaskPaneList( this ) )
+ {
+ pMySysWin->GetTaskPaneList()->RemoveWindow( this );
+ }
+ else
+ {
+ SAL_WARN( "vcl", "Window (" << GetText() << ") not found in TaskPanelList");
+ }
+ }
+
+ // remove from size-group if necessary
+ remove_from_all_size_groups();
+
+ // clear mnemonic labels
+ std::vector<VclPtr<FixedText> > aMnemonicLabels(list_mnemonic_labels());
+ for (auto const& mnemonicLabel : aMnemonicLabels)
+ {
+ remove_mnemonic_label(mnemonicLabel);
+ }
+
+ // hide window in order to trigger the Paint-Handling
+ Hide();
+
+ // EndExtTextInputMode
+ if (pSVData->mpWinData->mpExtTextInputWin == this)
+ {
+ EndExtTextInput();
+ if (pSVData->mpWinData->mpExtTextInputWin == this)
+ pSVData->mpWinData->mpExtTextInputWin = nullptr;
+ }
+
+ // check if the focus window is our child
+ bool bHasFocusedChild = false;
+ if (pSVData->mpWinData->mpFocusWin && ImplIsRealParentPath(pSVData->mpWinData->mpFocusWin))
+ {
+ // #122232#, this must not happen and is an application bug ! but we try some cleanup to hopefully avoid crashes, see below
+ bHasFocusedChild = true;
+#if OSL_DEBUG_LEVEL > 0
+ OUString aTempStr = "Window (" + GetText() +
+ ") with focused child window destroyed ! THIS WILL LEAD TO CRASHES AND MUST BE FIXED !";
+ SAL_WARN( "vcl", aTempStr );
+ Application::Abort(aTempStr);
+#endif
+ }
+
+ // if we get focus pass focus to another window
+ vcl::Window* pOverlapWindow = ImplGetFirstOverlapWindow();
+ if (pSVData->mpWinData->mpFocusWin == this
+ || bHasFocusedChild) // #122232#, see above, try some cleanup
+ {
+ if ( mpWindowImpl->mbFrame )
+ {
+ pSVData->mpWinData->mpFocusWin = nullptr;
+ pOverlapWindow->mpWindowImpl->mpLastFocusWindow = nullptr;
+ }
+ else
+ {
+ vcl::Window* pParent = GetParent();
+ vcl::Window* pBorderWindow = mpWindowImpl->mpBorderWindow;
+ // when windows overlap, give focus to the parent
+ // of the next FrameWindow
+ if ( pBorderWindow )
+ {
+ if ( pBorderWindow->ImplIsOverlapWindow() )
+ pParent = pBorderWindow->mpWindowImpl->mpOverlapWindow;
+ }
+ else if ( ImplIsOverlapWindow() )
+ pParent = mpWindowImpl->mpOverlapWindow;
+
+ if ( pParent && pParent->IsEnabled() && pParent->IsInputEnabled() && ! pParent->IsInModalMode() )
+ pParent->GrabFocus();
+ else
+ mpWindowImpl->mpFrameWindow->GrabFocus();
+
+ // If the focus was set back to 'this' set it to nothing
+ if (pSVData->mpWinData->mpFocusWin == this)
+ {
+ pSVData->mpWinData->mpFocusWin = nullptr;
+ pOverlapWindow->mpWindowImpl->mpLastFocusWindow = nullptr;
+ }
+ }
+ }
+
+ if ( pOverlapWindow != nullptr &&
+ pOverlapWindow->mpWindowImpl->mpLastFocusWindow == this )
+ pOverlapWindow->mpWindowImpl->mpLastFocusWindow = nullptr;
+
+ // reset hint for DefModalDialogParent
+ if( pSVData->maFrameData.mpActiveApplicationFrame == this )
+ pSVData->maFrameData.mpActiveApplicationFrame = nullptr;
+
+ // reset hint of what was the last wheeled window
+ if (pSVData->mpWinData->mpLastWheelWindow == this)
+ pSVData->mpWinData->mpLastWheelWindow = nullptr;
+
+ // reset marked windows
+ if ( mpWindowImpl->mpFrameData != nullptr )
+ {
+ if ( mpWindowImpl->mpFrameData->mpFocusWin == this )
+ mpWindowImpl->mpFrameData->mpFocusWin = nullptr;
+ if ( mpWindowImpl->mpFrameData->mpMouseMoveWin == this )
+ mpWindowImpl->mpFrameData->mpMouseMoveWin = nullptr;
+ if ( mpWindowImpl->mpFrameData->mpMouseDownWin == this )
+ mpWindowImpl->mpFrameData->mpMouseDownWin = nullptr;
+ }
+
+ // reset Deactivate-Window
+ if (pSVData->mpWinData->mpLastDeacWin == this)
+ pSVData->mpWinData->mpLastDeacWin = nullptr;
+
+ if ( mpWindowImpl->mbFrame && mpWindowImpl->mpFrameData )
+ {
+ if ( mpWindowImpl->mpFrameData->mnFocusId )
+ Application::RemoveUserEvent( mpWindowImpl->mpFrameData->mnFocusId );
+ mpWindowImpl->mpFrameData->mnFocusId = nullptr;
+ if ( mpWindowImpl->mpFrameData->mnMouseMoveId )
+ Application::RemoveUserEvent( mpWindowImpl->mpFrameData->mnMouseMoveId );
+ mpWindowImpl->mpFrameData->mnMouseMoveId = nullptr;
+ }
+
+ // release SalGraphics
+ VclPtr<OutputDevice> pOutDev = GetOutDev();
+ pOutDev->ReleaseGraphics();
+
+ // remove window from the lists
+ ImplRemoveWindow( true );
+
+ // de-register as "top window child" at our parent, if necessary
+ if ( mpWindowImpl->mbFrame )
+ {
+ bool bIsTopWindow
+ = mpWindowImpl->mpWinData && (mpWindowImpl->mpWinData->mnIsTopWindow == 1);
+ if ( mpWindowImpl->mpRealParent && bIsTopWindow )
+ {
+ ImplWinData* pParentWinData = mpWindowImpl->mpRealParent->ImplGetWinData();
+
+ auto myPos = ::std::find( pParentWinData->maTopWindowChildren.begin(),
+ pParentWinData->maTopWindowChildren.end(), VclPtr<vcl::Window>(this) );
+ SAL_WARN_IF( myPos == pParentWinData->maTopWindowChildren.end(), "vcl.window", "Window::~Window: inconsistency in top window chain!" );
+ if ( myPos != pParentWinData->maTopWindowChildren.end() )
+ pParentWinData->maTopWindowChildren.erase( myPos );
+ }
+ }
+
+ mpWindowImpl->mpWinData.reset();
+
+ // remove BorderWindow or Frame window data
+ mpWindowImpl->mpBorderWindow.disposeAndClear();
+ if ( mpWindowImpl->mbFrame )
+ {
+ if ( pSVData->maFrameData.mpFirstFrame == this )
+ pSVData->maFrameData.mpFirstFrame = mpWindowImpl->mpFrameData->mpNextFrame;
+ else
+ {
+ sal_Int32 nWindows = 0;
+ vcl::Window* pSysWin = pSVData->maFrameData.mpFirstFrame;
+ while ( pSysWin && pSysWin->mpWindowImpl->mpFrameData->mpNextFrame.get() != this )
+ {
+ pSysWin = pSysWin->mpWindowImpl->mpFrameData->mpNextFrame;
+ nWindows++;
+ }
+
+ if ( pSysWin )
+ {
+ assert (mpWindowImpl->mpFrameData->mpNextFrame.get() != pSysWin);
+ pSysWin->mpWindowImpl->mpFrameData->mpNextFrame = mpWindowImpl->mpFrameData->mpNextFrame;
+ }
+ else // if it is not in the list, we can't remove it.
+ SAL_WARN("vcl.window", "Window " << this << " marked as frame window, "
+ "is missing from list of " << nWindows << " frames");
+ }
+ if (mpWindowImpl->mpFrame) // otherwise exception during init
+ {
+ mpWindowImpl->mpFrame->SetCallback( nullptr, nullptr );
+ pSVData->mpDefInst->DestroyFrame( mpWindowImpl->mpFrame );
+ }
+ assert (mpWindowImpl->mpFrameData->mnFocusId == nullptr);
+ assert (mpWindowImpl->mpFrameData->mnMouseMoveId == nullptr);
+
+ mpWindowImpl->mpFrameData->mpBuffer.disposeAndClear();
+ delete mpWindowImpl->mpFrameData;
+ mpWindowImpl->mpFrameData = nullptr;
+ }
+
+ if (mpWindowImpl->mxWindowPeer)
+ mpWindowImpl->mxWindowPeer->dispose();
+
+ // should be the last statements
+ mpWindowImpl.reset();
+
+ pOutDev.disposeAndClear();
+ // just to make loplugin:vclwidgets happy
+ VclReferenceBase::dispose();
+}
+
+Window::~Window()
+{
+ disposeOnce();
+}
+
+// We will eventually being removing the inheritance of OutputDevice
+// from Window. It will be replaced with a transient relationship such
+// that the OutputDevice is only live for the scope of the Paint method.
+// In the meantime this can help move us towards a Window use an
+// OutputDevice, not being one.
+
+::OutputDevice const* Window::GetOutDev() const
+{
+ return mpWindowImpl ? mpWindowImpl->mxOutDev.get() : nullptr;
+}
+
+::OutputDevice* Window::GetOutDev()
+{
+ return mpWindowImpl ? mpWindowImpl->mxOutDev.get() : nullptr;
+}
+
+Color WindowOutputDevice::GetBackgroundColor() const
+{
+ return mxOwnerWindow->GetDisplayBackground().GetColor();
+}
+
+bool WindowOutputDevice::CanEnableNativeWidget() const
+{
+ return mxOwnerWindow->IsNativeWidgetEnabled();
+}
+
+} /* namespace vcl */
+
+WindowImpl::WindowImpl( vcl::Window& rWindow, WindowType nType )
+{
+ mxOutDev = VclPtr<vcl::WindowOutputDevice>::Create(rWindow);
+ maZoom = Fraction( 1, 1 );
+ mfPartialScrollX = 0.0;
+ mfPartialScrollY = 0.0;
+ maWinRegion = vcl::Region(true);
+ maWinClipRegion = vcl::Region(true);
+ mpWinData = nullptr; // Extra Window Data, that we don't need for all windows
+ mpFrameData = nullptr; // Frame Data
+ mpFrame = nullptr; // Pointer to frame window
+ mpSysObj = nullptr;
+ mpFrameWindow = nullptr; // window to top level parent (same as frame window)
+ mpOverlapWindow = nullptr; // first overlap parent
+ mpBorderWindow = nullptr; // Border-Window
+ mpClientWindow = nullptr; // Client-Window of a FrameWindow
+ mpParent = nullptr; // parent (incl. BorderWindow)
+ mpRealParent = nullptr; // real parent (excl. BorderWindow)
+ mpFirstChild = nullptr; // first child window
+ mpLastChild = nullptr; // last child window
+ mpFirstOverlap = nullptr; // first overlap window (only set in overlap windows)
+ mpLastOverlap = nullptr; // last overlap window (only set in overlap windows)
+ mpPrev = nullptr; // prev window
+ mpNext = nullptr; // next window
+ mpNextOverlap = nullptr; // next overlap window of frame
+ mpLastFocusWindow = nullptr; // window for focus restore
+ mpDlgCtrlDownWindow = nullptr; // window for dialog control
+ mnEventListenersIteratingCount = 0;
+ mnChildEventListenersIteratingCount = 0;
+ mpCursor = nullptr; // cursor
+ maPointer = PointerStyle::Arrow;
+ mpVCLXWindow = nullptr;
+ mpAccessibleInfos = nullptr;
+ maControlForeground = COL_TRANSPARENT; // no foreground set
+ maControlBackground = COL_TRANSPARENT; // no background set
+ mnLeftBorder = 0; // left border
+ mnTopBorder = 0; // top border
+ mnRightBorder = 0; // right border
+ mnBottomBorder = 0; // bottom border
+ mnWidthRequest = -1; // width request
+ mnHeightRequest = -1; // height request
+ mnOptimalWidthCache = -1; // optimal width cache
+ mnOptimalHeightCache = -1; // optimal height cache
+ mnX = 0; // X-Position to Parent
+ mnY = 0; // Y-Position to Parent
+ mnAbsScreenX = 0; // absolute X-position on screen, used for RTL window positioning
+ mpChildClipRegion = nullptr; // Child-Clip-Region when ClipChildren
+ mpPaintRegion = nullptr; // Paint-ClipRegion
+ mnStyle = 0; // style (init in ImplInitWindow)
+ mnPrevStyle = 0; // prevstyle (set in SetStyle)
+ mnExtendedStyle = WindowExtendedStyle::NONE; // extended style (init in ImplInitWindow)
+ mnType = nType; // type
+ mnGetFocusFlags = GetFocusFlags::NONE; // Flags for GetFocus()-Call
+ mnWaitCount = 0; // Wait-Count (>1 == "wait" mouse pointer)
+ mnPaintFlags = ImplPaintFlags::NONE; // Flags for ImplCallPaint
+ mnParentClipMode = ParentClipMode::NONE; // Flags for Parent-ClipChildren-Mode
+ mnActivateMode = ActivateModeFlags::NONE; // Will be converted in System/Overlap-Windows
+ mnDlgCtrlFlags = DialogControlFlags::NONE; // DialogControl-Flags
+ meAlwaysInputMode = AlwaysInputNone; // AlwaysEnableInput not called
+ meHalign = VclAlign::Fill;
+ meValign = VclAlign::Fill;
+ mePackType = VclPackType::Start;
+ mnPadding = 0;
+ mnGridHeight = 1;
+ mnGridLeftAttach = -1;
+ mnGridTopAttach = -1;
+ mnGridWidth = 1;
+ mnBorderWidth = 0;
+ mnMarginLeft = 0;
+ mnMarginRight = 0;
+ mnMarginTop = 0;
+ mnMarginBottom = 0;
+ mbFrame = false; // true: Window is a frame window
+ mbBorderWin = false; // true: Window is a border window
+ mbOverlapWin = false; // true: Window is an overlap window
+ mbSysWin = false; // true: SystemWindow is the base class
+ mbDialog = false; // true: Dialog is the base class
+ mbDockWin = false; // true: DockingWindow is the base class
+ mbFloatWin = false; // true: FloatingWindow is the base class
+ mbPushButton = false; // true: PushButton is the base class
+ mbToolBox = false; // true: ToolBox is the base class
+ mbMenuFloatingWindow = false; // true: MenuFloatingWindow is the base class
+ mbToolbarFloatingWindow = false; // true: ImplPopupFloatWin is the base class, used for subtoolbars
+ mbSplitter = false; // true: Splitter is the base class
+ mbVisible = false; // true: Show( true ) called
+ mbOverlapVisible = false; // true: Hide called for visible window from ImplHideAllOverlapWindow()
+ mbDisabled = false; // true: Enable( false ) called
+ mbInputDisabled = false; // true: EnableInput( false ) called
+ mbNoUpdate = false; // true: SetUpdateMode( false ) called
+ mbNoParentUpdate = false; // true: SetParentUpdateMode( false ) called
+ mbActive = false; // true: Window Active
+ mbReallyVisible = false; // true: this and all parents to an overlapped window are visible
+ mbReallyShown = false; // true: this and all parents to an overlapped window are shown
+ mbInInitShow = false; // true: we are in InitShow
+ mbChildPtrOverwrite = false; // true: PointerStyle overwrites Child-Pointer
+ mbNoPtrVisible = false; // true: ShowPointer( false ) called
+ mbPaintFrame = false; // true: Paint is visible, but not painted
+ mbInPaint = false; // true: Inside PaintHdl
+ mbMouseButtonDown = false; // true: BaseMouseButtonDown called
+ mbMouseButtonUp = false; // true: BaseMouseButtonUp called
+ mbKeyInput = false; // true: BaseKeyInput called
+ mbKeyUp = false; // true: BaseKeyUp called
+ mbCommand = false; // true: BaseCommand called
+ mbDefPos = true; // true: Position is not Set
+ mbDefSize = true; // true: Size is not Set
+ mbCallMove = true; // true: Move must be called by Show
+ mbCallResize = true; // true: Resize must be called by Show
+ mbWaitSystemResize = true; // true: Wait for System-Resize
+ mbInitWinClipRegion = true; // true: Calc Window Clip Region
+ mbInitChildRegion = false; // true: InitChildClipRegion
+ mbWinRegion = false; // true: Window Region
+ mbClipChildren = false; // true: Child-window should be clipped
+ mbClipSiblings = false; // true: Adjacent Child-window should be clipped
+ mbChildTransparent = false; // true: Child-windows are allowed to switch to transparent (incl. Parent-CLIPCHILDREN)
+ mbPaintTransparent = false; // true: Paints should be executed on the Parent
+ mbMouseTransparent = false; // true: Window is transparent for Mouse
+ mbDlgCtrlStart = false; // true: From here on own Dialog-Control
+ mbFocusVisible = false; // true: Focus Visible
+ mbUseNativeFocus = false;
+ mbNativeFocusVisible = false; // true: native Focus Visible
+ mbInShowFocus = false; // prevent recursion
+ mbInHideFocus = false; // prevent recursion
+ mbTrackVisible = false; // true: Tracking Visible
+ mbControlForeground = false; // true: Foreground-Property set
+ mbControlBackground = false; // true: Background-Property set
+ mbAlwaysOnTop = false; // true: always visible for all others windows
+ mbCompoundControl = false; // true: Composite Control => Listener...
+ mbCompoundControlHasFocus = false; // true: Composite Control has focus somewhere
+ mbPaintDisabled = false; // true: Paint should not be executed
+ mbAllResize = false; // true: Also sent ResizeEvents with 0,0
+ mbInDispose = false; // true: We're still in Window::dispose()
+ mbExtTextInput = false; // true: ExtTextInput-Mode is active
+ mbInFocusHdl = false; // true: Within GetFocus-Handler
+ mbCreatedWithToolkit = false;
+ mbSuppressAccessibilityEvents = false; // true: do not send any accessibility events
+ mbDrawSelectionBackground = false; // true: draws transparent window background to indicate (toolbox) selection
+ mbIsInTaskPaneList = false; // true: window was added to the taskpanelist in the topmost system window
+ mnNativeBackground = ControlPart::NONE; // initialize later, depends on type
+ mbHelpTextDynamic = false; // true: append help id in HELP_DEBUG case
+ mbFakeFocusSet = false; // true: pretend as if the window has focus.
+ mbHexpand = false;
+ mbVexpand = false;
+ mbExpand = false;
+ mbFill = true;
+ mbSecondary = false;
+ mbNonHomogeneous = false;
+ static bool bDoubleBuffer = getenv("VCL_DOUBLEBUFFERING_FORCE_ENABLE");
+ mbDoubleBufferingRequested = bDoubleBuffer; // when we are not sure, assume it cannot do double-buffering via RenderContext
+ mpLOKNotifier = nullptr;
+ mnLOKWindowId = 0;
+ mbUseFrameData = false;
+}
+
+WindowImpl::~WindowImpl()
+{
+ mpChildClipRegion.reset();
+ mpAccessibleInfos.reset();
+}
+
+ImplWinData::ImplWinData() :
+ mnCursorExtWidth(0),
+ mbVertical(false),
+ mnCompositionCharRects(0),
+ mnTrackFlags(ShowTrackFlags::NONE),
+ mnIsTopWindow(sal_uInt16(~0)), // not initialized yet, 0/1 will indicate TopWindow (see IsTopWindow())
+ mbMouseOver(false),
+ mbEnableNativeWidget(false)
+{
+}
+
+ImplWinData::~ImplWinData()
+{
+ mpCompositionCharRects.reset();
+}
+
+ImplFrameData::ImplFrameData( vcl::Window *pWindow )
+ : maPaintIdle( "vcl::Window maPaintIdle" ),
+ maResizeIdle( "vcl::Window maResizeIdle" )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ assert (pSVData->maFrameData.mpFirstFrame.get() != pWindow);
+ mpNextFrame = pSVData->maFrameData.mpFirstFrame;
+ pSVData->maFrameData.mpFirstFrame = pWindow;
+ mpFirstOverlap = nullptr;
+ mpFocusWin = nullptr;
+ mpMouseMoveWin = nullptr;
+ mpMouseDownWin = nullptr;
+ mpTrackWin = nullptr;
+ mxFontCollection = pSVData->maGDIData.mxScreenFontList;
+ mxFontCache = pSVData->maGDIData.mxScreenFontCache;
+ mnFocusId = nullptr;
+ mnMouseMoveId = nullptr;
+ mnLastMouseX = -1;
+ mnLastMouseY = -1;
+ mnBeforeLastMouseX = -1;
+ mnBeforeLastMouseY = -1;
+ mnFirstMouseX = -1;
+ mnFirstMouseY = -1;
+ mnLastMouseWinX = -1;
+ mnLastMouseWinY = -1;
+ mnModalMode = 0;
+ mnMouseDownTime = 0;
+ mnClickCount = 0;
+ mnFirstMouseCode = 0;
+ mnMouseCode = 0;
+ mnMouseMode = MouseEventModifiers::NONE;
+ mbHasFocus = false;
+ mbInMouseMove = false;
+ mbMouseIn = false;
+ mbStartDragCalled = false;
+ mbNeedSysWindow = false;
+ mbMinimized = false;
+ mbStartFocusState = false;
+ mbInSysObjFocusHdl = false;
+ mbInSysObjToTopHdl = false;
+ mbSysObjFocus = false;
+ maPaintIdle.SetPriority( TaskPriority::REPAINT );
+ maPaintIdle.SetInvokeHandler( LINK( pWindow, vcl::Window, ImplHandlePaintHdl ) );
+ maResizeIdle.SetPriority( TaskPriority::RESIZE );
+ maResizeIdle.SetInvokeHandler( LINK( pWindow, vcl::Window, ImplHandleResizeTimerHdl ) );
+ mbInternalDragGestureRecognizer = false;
+ mbDragging = false;
+ mbInBufferedPaint = false;
+ mnDPIX = 96;
+ mnDPIY = 96;
+ mnTouchPanPosition = -1;
+}
+
+namespace vcl {
+
+bool WindowOutputDevice::AcquireGraphics() const
+{
+ DBG_TESTSOLARMUTEX();
+
+ if (isDisposed())
+ return false;
+
+ if (mpGraphics)
+ return true;
+
+ mbInitLineColor = true;
+ mbInitFillColor = true;
+ mbInitFont = true;
+ mbInitTextColor = true;
+ mbInitClipRegion = true;
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ mpGraphics = mxOwnerWindow->mpWindowImpl->mpFrame->AcquireGraphics();
+ // try harder if no wingraphics was available directly
+ if ( !mpGraphics )
+ {
+ // find another output device in the same frame
+ vcl::WindowOutputDevice* pReleaseOutDev = pSVData->maGDIData.mpLastWinGraphics.get();
+ while ( pReleaseOutDev )
+ {
+ if ( pReleaseOutDev->mxOwnerWindow && pReleaseOutDev->mxOwnerWindow->mpWindowImpl->mpFrame == mxOwnerWindow->mpWindowImpl->mpFrame )
+ break;
+ pReleaseOutDev = static_cast<vcl::WindowOutputDevice*>(pReleaseOutDev->mpPrevGraphics.get());
+ }
+
+ if ( pReleaseOutDev )
+ {
+ // steal the wingraphics from the other outdev
+ mpGraphics = pReleaseOutDev->mpGraphics;
+ pReleaseOutDev->ReleaseGraphics( false );
+ }
+ else
+ {
+ // if needed retry after releasing least recently used wingraphics
+ while ( !mpGraphics )
+ {
+ if ( !pSVData->maGDIData.mpLastWinGraphics )
+ break;
+ pSVData->maGDIData.mpLastWinGraphics->ReleaseGraphics();
+ mpGraphics = mxOwnerWindow->mpWindowImpl->mpFrame->AcquireGraphics();
+ }
+ }
+ }
+
+ if ( mpGraphics )
+ {
+ // update global LRU list of wingraphics
+ mpNextGraphics = pSVData->maGDIData.mpFirstWinGraphics.get();
+ pSVData->maGDIData.mpFirstWinGraphics = const_cast<vcl::WindowOutputDevice*>(this);
+ if ( mpNextGraphics )
+ mpNextGraphics->mpPrevGraphics = const_cast<vcl::WindowOutputDevice*>(this);
+ if ( !pSVData->maGDIData.mpLastWinGraphics )
+ pSVData->maGDIData.mpLastWinGraphics = const_cast<vcl::WindowOutputDevice*>(this);
+
+ mpGraphics->SetXORMode( (RasterOp::Invert == meRasterOp) || (RasterOp::Xor == meRasterOp), RasterOp::Invert == meRasterOp );
+ mpGraphics->setAntiAlias(bool(mnAntialiasing & AntialiasingFlags::Enable));
+ }
+
+ return mpGraphics != nullptr;
+}
+
+void WindowOutputDevice::ReleaseGraphics( bool bRelease )
+{
+ DBG_TESTSOLARMUTEX();
+
+ if ( !mpGraphics )
+ return;
+
+ // release the fonts of the physically released graphics device
+ if( bRelease )
+ ImplReleaseFonts();
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ vcl::Window* pWindow = mxOwnerWindow.get();
+ if (!pWindow)
+ return;
+
+ if ( bRelease )
+ pWindow->mpWindowImpl->mpFrame->ReleaseGraphics( mpGraphics );
+ // remove from global LRU list of window graphics
+ if ( mpPrevGraphics )
+ mpPrevGraphics->mpNextGraphics = mpNextGraphics;
+ else
+ pSVData->maGDIData.mpFirstWinGraphics = static_cast<vcl::WindowOutputDevice*>(mpNextGraphics.get());
+ if ( mpNextGraphics )
+ mpNextGraphics->mpPrevGraphics = mpPrevGraphics;
+ else
+ pSVData->maGDIData.mpLastWinGraphics = static_cast<vcl::WindowOutputDevice*>(mpPrevGraphics.get());
+
+ mpGraphics = nullptr;
+ mpPrevGraphics = nullptr;
+ mpNextGraphics = nullptr;
+}
+
+static sal_Int32 CountDPIScaleFactor(sal_Int32 nDPI)
+{
+#ifndef MACOSX
+ // Setting of HiDPI is unfortunately all only a heuristic; and to add
+ // insult to an injury, the system is constantly lying to us about
+ // the DPI and whatnot
+ // eg. fdo#77059 - set the value from which we do consider the
+ // screen HiDPI to greater than 168
+ if (nDPI > 216) // 96 * 2 + 96 / 4
+ return 250;
+ else if (nDPI > 168) // 96 * 2 - 96 / 4
+ return 200;
+ else if (nDPI > 120) // 96 * 1.5 - 96 / 4
+ return 150;
+#else
+ (void)nDPI;
+#endif
+
+ return 100;
+}
+
+void Window::ImplInit( vcl::Window* pParent, WinBits nStyle, SystemParentData* pSystemParentData )
+{
+ SAL_WARN_IF( !mpWindowImpl->mbFrame && !pParent && GetType() != WindowType::FIXEDIMAGE, "vcl.window",
+ "Window::Window(): pParent == NULL" );
+
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::Window* pRealParent = pParent;
+
+ // inherit 3D look
+ if ( !mpWindowImpl->mbOverlapWin && pParent && (pParent->GetStyle() & WB_3DLOOK) )
+ nStyle |= WB_3DLOOK;
+
+ // create border window if necessary
+ if ( !mpWindowImpl->mbFrame && !mpWindowImpl->mbBorderWin && !mpWindowImpl->mpBorderWindow
+ && (nStyle & (WB_BORDER | WB_SYSTEMCHILDWINDOW) ) )
+ {
+ BorderWindowStyle nBorderTypeStyle = BorderWindowStyle::NONE;
+ if( nStyle & WB_SYSTEMCHILDWINDOW )
+ {
+ // handle WB_SYSTEMCHILDWINDOW
+ // these should be analogous to a top level frame; meaning they
+ // should have a border window with style BorderWindowStyle::Frame
+ // which controls their size
+ nBorderTypeStyle |= BorderWindowStyle::Frame;
+ nStyle |= WB_BORDER;
+ }
+ VclPtrInstance<ImplBorderWindow> pBorderWin( pParent, nStyle & (WB_BORDER | WB_DIALOGCONTROL | WB_NODIALOGCONTROL), nBorderTypeStyle );
+ static_cast<vcl::Window*>(pBorderWin)->mpWindowImpl->mpClientWindow = this;
+ pBorderWin->GetBorder( mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder );
+ mpWindowImpl->mpBorderWindow = pBorderWin;
+ pParent = mpWindowImpl->mpBorderWindow;
+ }
+ else if( !mpWindowImpl->mbFrame && ! pParent )
+ {
+ mpWindowImpl->mbOverlapWin = true;
+ mpWindowImpl->mbFrame = true;
+ }
+
+ // insert window in list
+ ImplInsertWindow( pParent );
+ mpWindowImpl->mnStyle = nStyle;
+
+ if( pParent && ! mpWindowImpl->mbFrame )
+ mpWindowImpl->mxOutDev->mbEnableRTL = AllSettings::GetLayoutRTL();
+
+ // test for frame creation
+ if ( mpWindowImpl->mbFrame )
+ {
+ // create frame
+ SalFrameStyleFlags nFrameStyle = SalFrameStyleFlags::NONE;
+
+ if ( nStyle & WB_MOVEABLE )
+ nFrameStyle |= SalFrameStyleFlags::MOVEABLE;
+ if ( nStyle & WB_SIZEABLE )
+ nFrameStyle |= SalFrameStyleFlags::SIZEABLE;
+ if ( nStyle & WB_CLOSEABLE )
+ nFrameStyle |= SalFrameStyleFlags::CLOSEABLE;
+ if ( nStyle & WB_APP )
+ nFrameStyle |= SalFrameStyleFlags::DEFAULT;
+ // check for undecorated floating window
+ if( // 1. floating windows that are not moveable/sizeable (only closeable allowed)
+ ( !(nFrameStyle & ~SalFrameStyleFlags::CLOSEABLE) &&
+ ( mpWindowImpl->mbFloatWin || ((GetType() == WindowType::BORDERWINDOW) && static_cast<ImplBorderWindow*>(this)->mbFloatWindow) || (nStyle & WB_SYSTEMFLOATWIN) ) ) ||
+ // 2. borderwindows of floaters with ownerdraw decoration
+ ((GetType() == WindowType::BORDERWINDOW) && static_cast<ImplBorderWindow*>(this)->mbFloatWindow && (nStyle & WB_OWNERDRAWDECORATION) ) )
+ {
+ nFrameStyle = SalFrameStyleFlags::FLOAT;
+ if( nStyle & WB_OWNERDRAWDECORATION )
+ nFrameStyle |= SalFrameStyleFlags::OWNERDRAWDECORATION | SalFrameStyleFlags::NOSHADOW;
+ }
+ else if( mpWindowImpl->mbFloatWin )
+ nFrameStyle |= SalFrameStyleFlags::TOOLWINDOW;
+
+ if( nStyle & WB_INTROWIN )
+ nFrameStyle |= SalFrameStyleFlags::INTRO;
+ if( nStyle & WB_TOOLTIPWIN )
+ nFrameStyle |= SalFrameStyleFlags::TOOLTIP;
+
+ if( nStyle & WB_NOSHADOW )
+ nFrameStyle |= SalFrameStyleFlags::NOSHADOW;
+
+ if( nStyle & WB_SYSTEMCHILDWINDOW )
+ nFrameStyle |= SalFrameStyleFlags::SYSTEMCHILD;
+
+ switch (mpWindowImpl->mnType)
+ {
+ case WindowType::DIALOG:
+ case WindowType::TABDIALOG:
+ case WindowType::MODELESSDIALOG:
+ case WindowType::MESSBOX:
+ case WindowType::INFOBOX:
+ case WindowType::WARNINGBOX:
+ case WindowType::ERRORBOX:
+ case WindowType::QUERYBOX:
+ nFrameStyle |= SalFrameStyleFlags::DIALOG;
+ break;
+ default:
+ break;
+ }
+
+ // tdf#144624 for the DefaultWindow, which is never visible, don't
+ // create an icon for it so construction of a DefaultWindow cannot
+ // trigger creation of a VirtualDevice which itself requires a
+ // DefaultWindow to exist
+ if( nStyle & WB_DEFAULTWIN )
+ nFrameStyle |= SalFrameStyleFlags::NOICON;
+
+ SalFrame* pParentFrame = nullptr;
+ if ( pParent )
+ pParentFrame = pParent->mpWindowImpl->mpFrame;
+ SalFrame* pFrame;
+ if ( pSystemParentData )
+ pFrame = pSVData->mpDefInst->CreateChildFrame( pSystemParentData, nFrameStyle | SalFrameStyleFlags::PLUG );
+ else
+ pFrame = pSVData->mpDefInst->CreateFrame( pParentFrame, nFrameStyle );
+ if ( !pFrame )
+ {
+ // do not abort but throw an exception, may be the current thread terminates anyway (plugin-scenario)
+ throw RuntimeException(
+ "Could not create system window!",
+ Reference< XInterface >() );
+ }
+
+ pFrame->SetCallback( this, ImplWindowFrameProc );
+
+ // set window frame data
+ mpWindowImpl->mpFrameData = new ImplFrameData( this );
+ mpWindowImpl->mpFrame = pFrame;
+ mpWindowImpl->mpFrameWindow = this;
+ mpWindowImpl->mpOverlapWindow = this;
+
+ if (!(nStyle & WB_DEFAULTWIN) && mpWindowImpl->mbDoubleBufferingRequested)
+ RequestDoubleBuffering(true);
+
+ if ( pRealParent && IsTopWindow() )
+ {
+ ImplWinData* pParentWinData = pRealParent->ImplGetWinData();
+ pParentWinData->maTopWindowChildren.emplace_back(this );
+ }
+ }
+
+ // init data
+ mpWindowImpl->mpRealParent = pRealParent;
+
+ // #99318: make sure fontcache and list is available before call to SetSettings
+ mpWindowImpl->mxOutDev->mxFontCollection = mpWindowImpl->mpFrameData->mxFontCollection;
+ mpWindowImpl->mxOutDev->mxFontCache = mpWindowImpl->mpFrameData->mxFontCache;
+
+ if ( mpWindowImpl->mbFrame )
+ {
+ if ( pParent )
+ {
+ mpWindowImpl->mpFrameData->mnDPIX = pParent->mpWindowImpl->mpFrameData->mnDPIX;
+ mpWindowImpl->mpFrameData->mnDPIY = pParent->mpWindowImpl->mpFrameData->mnDPIY;
+ }
+ else
+ {
+ OutputDevice *pOutDev = GetOutDev();
+ if ( pOutDev->AcquireGraphics() )
+ {
+ mpWindowImpl->mxOutDev->mpGraphics->GetResolution( mpWindowImpl->mpFrameData->mnDPIX, mpWindowImpl->mpFrameData->mnDPIY );
+ }
+ }
+
+ // add ownerdraw decorated frame windows to list in the top-most frame window
+ // so they can be hidden on lose focus
+ if( nStyle & WB_OWNERDRAWDECORATION )
+ ImplGetOwnerDrawList().emplace_back(this );
+
+ // delay settings initialization until first "real" frame
+ // this relies on the IntroWindow not needing any system settings
+ if ( !pSVData->maAppData.mbSettingsInit &&
+ ! (nStyle & (WB_INTROWIN|WB_DEFAULTWIN))
+ )
+ {
+ // side effect: ImplUpdateGlobalSettings does an ImplGetFrame()->UpdateSettings
+ ImplUpdateGlobalSettings( *pSVData->maAppData.mxSettings );
+ mpWindowImpl->mxOutDev->SetSettings( *pSVData->maAppData.mxSettings );
+ pSVData->maAppData.mbSettingsInit = true;
+ }
+
+ // If we create a Window with default size, query this
+ // size directly, because we want resize all Controls to
+ // the correct size before we display the window
+ if ( nStyle & (WB_MOVEABLE | WB_SIZEABLE | WB_APP) )
+ mpWindowImpl->mpFrame->GetClientSize( mpWindowImpl->mxOutDev->mnOutWidth, mpWindowImpl->mxOutDev->mnOutHeight );
+ }
+ else
+ {
+ if ( pParent )
+ {
+ if ( !ImplIsOverlapWindow() )
+ {
+ mpWindowImpl->mbDisabled = pParent->mpWindowImpl->mbDisabled;
+ mpWindowImpl->mbInputDisabled = pParent->mpWindowImpl->mbInputDisabled;
+ mpWindowImpl->meAlwaysInputMode = pParent->mpWindowImpl->meAlwaysInputMode;
+ }
+
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ // we don't want to call the WindowOutputDevice override of this because
+ // it calls back into us.
+ mpWindowImpl->mxOutDev->OutputDevice::SetSettings( pParent->GetSettings() );
+ }
+ }
+
+ }
+
+ // setup the scale factor for HiDPI displays
+ mpWindowImpl->mxOutDev->mnDPIScalePercentage = CountDPIScaleFactor(mpWindowImpl->mpFrameData->mnDPIY);
+ mpWindowImpl->mxOutDev->mnDPIX = mpWindowImpl->mpFrameData->mnDPIX;
+ mpWindowImpl->mxOutDev->mnDPIY = mpWindowImpl->mpFrameData->mnDPIY;
+
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ const StyleSettings& rStyleSettings = mpWindowImpl->mxOutDev->moSettings->GetStyleSettings();
+ mpWindowImpl->mxOutDev->maFont = rStyleSettings.GetAppFont();
+
+ if ( nStyle & WB_3DLOOK )
+ {
+ SetTextColor( rStyleSettings.GetButtonTextColor() );
+ SetBackground( Wallpaper( rStyleSettings.GetFaceColor() ) );
+ }
+ else
+ {
+ SetTextColor( rStyleSettings.GetWindowTextColor() );
+ SetBackground( Wallpaper( rStyleSettings.GetWindowColor() ) );
+ }
+ }
+ else
+ {
+ mpWindowImpl->mxOutDev->maFont = OutputDevice::GetDefaultFont( DefaultFontType::FIXED, LANGUAGE_ENGLISH_US, GetDefaultFontFlags::NONE );
+ }
+
+ ImplPointToLogic(*GetOutDev(), mpWindowImpl->mxOutDev->maFont);
+
+ (void)ImplUpdatePos();
+
+ // calculate app font res (except for the Intro Window or the default window)
+ if ( mpWindowImpl->mbFrame && !pSVData->maGDIData.mnAppFontX && ! (nStyle & (WB_INTROWIN|WB_DEFAULTWIN)) )
+ ImplInitAppFontData( this );
+}
+
+void Window::ImplInitAppFontData( vcl::Window const * pWindow )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ tools::Long nTextHeight = pWindow->GetTextHeight();
+ tools::Long nTextWidth = pWindow->approximate_char_width() * 8;
+ tools::Long nSymHeight = nTextHeight*4;
+ // Make the basis wider if the font is too narrow
+ // such that the dialog looks symmetrical and does not become too narrow.
+ // Add some extra space when the dialog has the same width,
+ // as a little more space is better.
+ if ( nSymHeight > nTextWidth )
+ nTextWidth = nSymHeight;
+ else if ( nSymHeight+5 > nTextWidth )
+ nTextWidth = nSymHeight+5;
+ pSVData->maGDIData.mnAppFontX = nTextWidth * 10 / 8;
+ pSVData->maGDIData.mnAppFontY = nTextHeight * 10;
+
+#ifdef MACOSX
+ // FIXME: this is currently only on macOS, check with other
+ // platforms
+ if( pSVData->maNWFData.mbNoFocusRects )
+ {
+ // try to find out whether there is a large correction
+ // of control sizes, if yes, make app font scalings larger
+ // so dialog positioning is not completely off
+ ImplControlValue aControlValue;
+ tools::Rectangle aCtrlRegion( Point(), Size( nTextWidth < 10 ? 10 : nTextWidth, nTextHeight < 10 ? 10 : nTextHeight ) );
+ tools::Rectangle aBoundingRgn( aCtrlRegion );
+ tools::Rectangle aContentRgn( aCtrlRegion );
+ if( pWindow->GetNativeControlRegion( ControlType::Editbox, ControlPart::Entire, aCtrlRegion,
+ ControlState::ENABLED, aControlValue,
+ aBoundingRgn, aContentRgn ) )
+ {
+ // comment: the magical +6 is for the extra border in bordered
+ // (which is the standard) edit fields
+ if( aContentRgn.GetHeight() - nTextHeight > (nTextHeight+4)/4 )
+ pSVData->maGDIData.mnAppFontY = (aContentRgn.GetHeight()-4) * 10;
+ }
+ }
+#endif
+}
+
+ImplWinData* Window::ImplGetWinData() const
+{
+ if (!mpWindowImpl->mpWinData)
+ {
+ static const char* pNoNWF = getenv( "SAL_NO_NWF" );
+
+ const_cast<vcl::Window*>(this)->mpWindowImpl->mpWinData.reset(new ImplWinData);
+ mpWindowImpl->mpWinData->mbEnableNativeWidget = !(pNoNWF && *pNoNWF); // true: try to draw this control with native theme API
+ }
+
+ return mpWindowImpl->mpWinData.get();
+}
+
+
+void WindowOutputDevice::CopyDeviceArea( SalTwoRect& aPosAry, bool bWindowInvalidate )
+{
+ if (aPosAry.mnSrcWidth == 0 || aPosAry.mnSrcHeight == 0 || aPosAry.mnDestWidth == 0 || aPosAry.mnDestHeight == 0)
+ return;
+
+ if (bWindowInvalidate)
+ {
+ const tools::Rectangle aSrcRect(Point(aPosAry.mnSrcX, aPosAry.mnSrcY),
+ Size(aPosAry.mnSrcWidth, aPosAry.mnSrcHeight));
+
+ mxOwnerWindow->ImplMoveAllInvalidateRegions(aSrcRect,
+ aPosAry.mnDestX-aPosAry.mnSrcX,
+ aPosAry.mnDestY-aPosAry.mnSrcY,
+ false);
+
+ mpGraphics->CopyArea(aPosAry.mnDestX, aPosAry.mnDestY,
+ aPosAry.mnSrcX, aPosAry.mnSrcY,
+ aPosAry.mnSrcWidth, aPosAry.mnSrcHeight,
+ *this);
+
+ return;
+ }
+
+ OutputDevice::CopyDeviceArea(aPosAry, bWindowInvalidate);
+}
+
+const OutputDevice* WindowOutputDevice::DrawOutDevDirectCheck(const OutputDevice& rSrcDev) const
+{
+ const OutputDevice* pSrcDevChecked;
+ if ( this == &rSrcDev )
+ pSrcDevChecked = nullptr;
+ else if (GetOutDevType() != rSrcDev.GetOutDevType())
+ pSrcDevChecked = &rSrcDev;
+ else if (mxOwnerWindow->mpWindowImpl->mpFrameWindow == static_cast<const vcl::WindowOutputDevice&>(rSrcDev).mxOwnerWindow->mpWindowImpl->mpFrameWindow)
+ pSrcDevChecked = nullptr;
+ else
+ pSrcDevChecked = &rSrcDev;
+
+ return pSrcDevChecked;
+}
+
+void WindowOutputDevice::DrawOutDevDirectProcess( const OutputDevice& rSrcDev, SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
+{
+ if (pSrcGraphics)
+ mpGraphics->CopyBits(rPosAry, *pSrcGraphics, *this, rSrcDev);
+ else
+ mpGraphics->CopyBits(rPosAry, *this);
+}
+
+SalGraphics* Window::ImplGetFrameGraphics() const
+{
+ if ( mpWindowImpl->mpFrameWindow->GetOutDev()->mpGraphics )
+ {
+ mpWindowImpl->mpFrameWindow->GetOutDev()->mbInitClipRegion = true;
+ }
+ else
+ {
+ OutputDevice* pFrameWinOutDev = mpWindowImpl->mpFrameWindow->GetOutDev();
+ if ( ! pFrameWinOutDev->AcquireGraphics() )
+ {
+ return nullptr;
+ }
+ }
+ mpWindowImpl->mpFrameWindow->GetOutDev()->mpGraphics->ResetClipRegion();
+ return mpWindowImpl->mpFrameWindow->GetOutDev()->mpGraphics;
+}
+
+void Window::ImplSetReallyVisible()
+{
+ // #i43594# it is possible that INITSHOW was never send, because the visibility state changed between
+ // ImplCallInitShow() and ImplSetReallyVisible() when called from Show()
+ // mbReallyShown is a useful indicator
+ if( !mpWindowImpl->mbReallyShown )
+ ImplCallInitShow();
+
+ bool bBecameReallyVisible = !mpWindowImpl->mbReallyVisible;
+
+ GetOutDev()->mbDevOutput = true;
+ mpWindowImpl->mbReallyVisible = true;
+ mpWindowImpl->mbReallyShown = true;
+
+ // the SHOW/HIDE events serve as indicators to send child creation/destroy events to the access bridge.
+ // For this, the data member of the event must not be NULL.
+ // Previously, we did this in Window::Show, but there some events got lost in certain situations. Now
+ // we're doing it when the visibility really changes
+ if( bBecameReallyVisible && ImplIsAccessibleCandidate() )
+ CallEventListeners( VclEventId::WindowShow, this );
+ // TODO. It's kind of a hack that we're re-using the VclEventId::WindowShow. Normally, we should
+ // introduce another event which explicitly triggers the Accessibility implementations.
+
+ vcl::Window* pWindow = mpWindowImpl->mpFirstOverlap;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbVisible )
+ pWindow->ImplSetReallyVisible();
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+
+ pWindow = mpWindowImpl->mpFirstChild;
+ while ( pWindow )
+ {
+ if ( pWindow->mpWindowImpl->mbVisible )
+ pWindow->ImplSetReallyVisible();
+ pWindow = pWindow->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplInitResolutionSettings()
+{
+ // recalculate AppFont-resolution and DPI-resolution
+ if (mpWindowImpl->mbFrame)
+ {
+ GetOutDev()->mnDPIX = mpWindowImpl->mpFrameData->mnDPIX;
+ GetOutDev()->mnDPIY = mpWindowImpl->mpFrameData->mnDPIY;
+
+ // setup the scale factor for HiDPI displays
+ GetOutDev()->mnDPIScalePercentage = CountDPIScaleFactor(mpWindowImpl->mpFrameData->mnDPIY);
+ const StyleSettings& rStyleSettings = GetOutDev()->moSettings->GetStyleSettings();
+ SetPointFont(*GetOutDev(), rStyleSettings.GetAppFont());
+ }
+ else if ( mpWindowImpl->mpParent )
+ {
+ GetOutDev()->mnDPIX = mpWindowImpl->mpParent->GetOutDev()->mnDPIX;
+ GetOutDev()->mnDPIY = mpWindowImpl->mpParent->GetOutDev()->mnDPIY;
+ GetOutDev()->mnDPIScalePercentage = mpWindowImpl->mpParent->GetOutDev()->mnDPIScalePercentage;
+ }
+
+ // update the recalculated values for logical units
+ // and also tools belonging to the values
+ if (IsMapModeEnabled())
+ {
+ MapMode aMapMode = GetMapMode();
+ SetMapMode();
+ SetMapMode( aMapMode );
+ }
+}
+
+void Window::ImplPointToLogic(vcl::RenderContext const & rRenderContext, vcl::Font& rFont,
+ bool bUseRenderContextDPI) const
+{
+ Size aSize = rFont.GetFontSize();
+
+ if (aSize.Width())
+ {
+ aSize.setWidth( aSize.Width() *
+ ( bUseRenderContextDPI ? rRenderContext.GetDPIX() : mpWindowImpl->mpFrameData->mnDPIX) );
+ aSize.AdjustWidth(72 / 2 );
+ aSize.setWidth( aSize.Width() / 72 );
+ }
+ aSize.setHeight( aSize.Height()
+ * ( bUseRenderContextDPI ? rRenderContext.GetDPIY() : mpWindowImpl->mpFrameData->mnDPIY) );
+ aSize.AdjustHeight(72/2 );
+ aSize.setHeight( aSize.Height() / 72 );
+
+ aSize = rRenderContext.PixelToLogic(aSize);
+
+ rFont.SetFontSize(aSize);
+}
+
+void Window::ImplLogicToPoint(vcl::RenderContext const & rRenderContext, vcl::Font& rFont) const
+{
+ Size aSize = rFont.GetFontSize();
+ aSize = rRenderContext.LogicToPixel(aSize);
+
+ if (aSize.Width())
+ {
+ aSize.setWidth( aSize.Width() * 72 );
+ aSize.AdjustWidth(mpWindowImpl->mpFrameData->mnDPIX / 2 );
+ aSize.setWidth( aSize.Width() / ( mpWindowImpl->mpFrameData->mnDPIX) );
+ }
+ aSize.setHeight( aSize.Height() * 72 );
+ aSize.AdjustHeight(mpWindowImpl->mpFrameData->mnDPIY / 2 );
+ aSize.setHeight( aSize.Height() / ( mpWindowImpl->mpFrameData->mnDPIY) );
+
+ rFont.SetFontSize(aSize);
+}
+
+bool Window::ImplUpdatePos()
+{
+ bool bSysChild = false;
+
+ if ( ImplIsOverlapWindow() )
+ {
+ GetOutDev()->mnOutOffX = mpWindowImpl->mnX;
+ GetOutDev()->mnOutOffY = mpWindowImpl->mnY;
+ }
+ else
+ {
+ vcl::Window* pParent = ImplGetParent();
+
+ GetOutDev()->mnOutOffX = mpWindowImpl->mnX + pParent->GetOutDev()->mnOutOffX;
+ GetOutDev()->mnOutOffY = mpWindowImpl->mnY + pParent->GetOutDev()->mnOutOffY;
+ }
+
+ VclPtr< vcl::Window > pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ if ( pChild->ImplUpdatePos() )
+ bSysChild = true;
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+
+ if ( mpWindowImpl->mpSysObj )
+ bSysChild = true;
+
+ return bSysChild;
+}
+
+void Window::ImplUpdateSysObjPos()
+{
+ if ( mpWindowImpl->mpSysObj )
+ mpWindowImpl->mpSysObj->SetPosSize( GetOutDev()->mnOutOffX, GetOutDev()->mnOutOffY, GetOutDev()->mnOutWidth, GetOutDev()->mnOutHeight );
+
+ VclPtr< vcl::Window > pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->ImplUpdateSysObjPos();
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::ImplPosSizeWindow( tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags )
+{
+ bool bNewPos = false;
+ bool bNewSize = false;
+ bool bCopyBits = false;
+ tools::Long nOldOutOffX = GetOutDev()->mnOutOffX;
+ tools::Long nOldOutOffY = GetOutDev()->mnOutOffY;
+ tools::Long nOldOutWidth = GetOutDev()->mnOutWidth;
+ tools::Long nOldOutHeight = GetOutDev()->mnOutHeight;
+ std::unique_ptr<vcl::Region> pOverlapRegion;
+ std::unique_ptr<vcl::Region> pOldRegion;
+
+ if ( IsReallyVisible() )
+ {
+ tools::Rectangle aOldWinRect( Point( nOldOutOffX, nOldOutOffY ),
+ Size( nOldOutWidth, nOldOutHeight ) );
+ pOldRegion.reset( new vcl::Region( aOldWinRect ) );
+ if ( mpWindowImpl->mbWinRegion )
+ pOldRegion->Intersect( GetOutDev()->ImplPixelToDevicePixel( mpWindowImpl->maWinRegion ) );
+
+ if ( GetOutDev()->mnOutWidth && GetOutDev()->mnOutHeight && !mpWindowImpl->mbPaintTransparent &&
+ !mpWindowImpl->mbInitWinClipRegion && !mpWindowImpl->maWinClipRegion.IsEmpty() &&
+ !HasPaintEvent() )
+ bCopyBits = true;
+ }
+
+ bool bnXRecycled = false; // avoid duplicate mirroring in RTL case
+ if ( nFlags & PosSizeFlags::Width )
+ {
+ if(!( nFlags & PosSizeFlags::X ))
+ {
+ nX = mpWindowImpl->mnX;
+ nFlags |= PosSizeFlags::X;
+ bnXRecycled = true; // we're using a mnX which was already mirrored in RTL case
+ }
+
+ if ( nWidth < 0 )
+ nWidth = 0;
+ if ( nWidth != GetOutDev()->mnOutWidth )
+ {
+ GetOutDev()->mnOutWidth = nWidth;
+ bNewSize = true;
+ bCopyBits = false;
+ }
+ }
+ if ( nFlags & PosSizeFlags::Height )
+ {
+ if ( nHeight < 0 )
+ nHeight = 0;
+ if ( nHeight != GetOutDev()->mnOutHeight )
+ {
+ GetOutDev()->mnOutHeight = nHeight;
+ bNewSize = true;
+ bCopyBits = false;
+ }
+ }
+
+ if ( nFlags & PosSizeFlags::X )
+ {
+ tools::Long nOrgX = nX;
+ Point aPtDev( nX+GetOutDev()->mnOutOffX, 0 );
+ OutputDevice *pOutDev = GetOutDev();
+ if( pOutDev->HasMirroredGraphics() )
+ {
+ aPtDev.setX( GetOutDev()->mpGraphics->mirror2( aPtDev.X(), *GetOutDev() ) );
+
+ // #106948# always mirror our pos if our parent is not mirroring, even
+ // if we are also not mirroring
+ // RTL: check if parent is in different coordinates
+ if( !bnXRecycled && mpWindowImpl->mpParent && !mpWindowImpl->mpParent->mpWindowImpl->mbFrame && mpWindowImpl->mpParent->GetOutDev()->ImplIsAntiparallel() )
+ {
+ nX = mpWindowImpl->mpParent->GetOutDev()->mnOutWidth - GetOutDev()->mnOutWidth - nX;
+ }
+ /* #i99166# An LTR window in RTL UI that gets sized only would be
+ expected to not moved its upper left point
+ */
+ if( bnXRecycled )
+ {
+ if( GetOutDev()->ImplIsAntiparallel() )
+ {
+ aPtDev.setX( mpWindowImpl->mnAbsScreenX );
+ nOrgX = mpWindowImpl->maPos.X();
+ }
+ }
+ }
+ else if( !bnXRecycled && mpWindowImpl->mpParent && !mpWindowImpl->mpParent->mpWindowImpl->mbFrame && mpWindowImpl->mpParent->GetOutDev()->ImplIsAntiparallel() )
+ {
+ // mirrored window in LTR UI
+ nX = mpWindowImpl->mpParent->GetOutDev()->mnOutWidth - GetOutDev()->mnOutWidth - nX;
+ }
+
+ // check maPos as well, as it could have been changed for client windows (ImplCallMove())
+ if ( mpWindowImpl->mnAbsScreenX != aPtDev.X() || nX != mpWindowImpl->mnX || nOrgX != mpWindowImpl->maPos.X() )
+ {
+ if ( bCopyBits && !pOverlapRegion )
+ {
+ pOverlapRegion.reset( new vcl::Region() );
+ ImplCalcOverlapRegion( GetOutputRectPixel(),
+ *pOverlapRegion, false, true );
+ }
+ mpWindowImpl->mnX = nX;
+ mpWindowImpl->maPos.setX( nOrgX );
+ mpWindowImpl->mnAbsScreenX = aPtDev.X();
+ bNewPos = true;
+ }
+ }
+ if ( nFlags & PosSizeFlags::Y )
+ {
+ // check maPos as well, as it could have been changed for client windows (ImplCallMove())
+ if ( nY != mpWindowImpl->mnY || nY != mpWindowImpl->maPos.Y() )
+ {
+ if ( bCopyBits && !pOverlapRegion )
+ {
+ pOverlapRegion.reset( new vcl::Region() );
+ ImplCalcOverlapRegion( GetOutputRectPixel(),
+ *pOverlapRegion, false, true );
+ }
+ mpWindowImpl->mnY = nY;
+ mpWindowImpl->maPos.setY( nY );
+ bNewPos = true;
+ }
+ }
+
+ if ( !(bNewPos || bNewSize) )
+ return;
+
+ bool bUpdateSysObjPos = false;
+ if ( bNewPos )
+ bUpdateSysObjPos = ImplUpdatePos();
+
+ // the borderwindow always specifies the position for its client window
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->maPos = mpWindowImpl->mpBorderWindow->mpWindowImpl->maPos;
+
+ if ( mpWindowImpl->mpClientWindow )
+ {
+ mpWindowImpl->mpClientWindow->ImplPosSizeWindow( mpWindowImpl->mpClientWindow->mpWindowImpl->mnLeftBorder,
+ mpWindowImpl->mpClientWindow->mpWindowImpl->mnTopBorder,
+ GetOutDev()->mnOutWidth - mpWindowImpl->mpClientWindow->mpWindowImpl->mnLeftBorder-mpWindowImpl->mpClientWindow->mpWindowImpl->mnRightBorder,
+ GetOutDev()->mnOutHeight - mpWindowImpl->mpClientWindow->mpWindowImpl->mnTopBorder-mpWindowImpl->mpClientWindow->mpWindowImpl->mnBottomBorder,
+ PosSizeFlags::X | PosSizeFlags::Y |
+ PosSizeFlags::Width | PosSizeFlags::Height );
+ // If we have a client window, then this is the position
+ // of the Application's floating windows
+ mpWindowImpl->mpClientWindow->mpWindowImpl->maPos = mpWindowImpl->maPos;
+ if ( bNewPos )
+ {
+ if ( mpWindowImpl->mpClientWindow->IsVisible() )
+ {
+ mpWindowImpl->mpClientWindow->ImplCallMove();
+ }
+ else
+ {
+ mpWindowImpl->mpClientWindow->mpWindowImpl->mbCallMove = true;
+ }
+ }
+ }
+
+ // Move()/Resize() will be called only for Show(), such that
+ // at least one is called before Show()
+ if ( IsVisible() )
+ {
+ if ( bNewPos )
+ {
+ ImplCallMove();
+ }
+ if ( bNewSize )
+ {
+ ImplCallResize();
+ }
+ }
+ else
+ {
+ if ( bNewPos )
+ mpWindowImpl->mbCallMove = true;
+ if ( bNewSize )
+ mpWindowImpl->mbCallResize = true;
+ }
+
+ bool bUpdateSysObjClip = false;
+ if ( IsReallyVisible() )
+ {
+ if ( bNewPos || bNewSize )
+ {
+ // set Clip-Flag
+ bUpdateSysObjClip = !ImplSetClipFlag( true );
+ }
+
+ // invalidate window content ?
+ if ( bNewPos || (GetOutDev()->mnOutWidth > nOldOutWidth) || (GetOutDev()->mnOutHeight > nOldOutHeight) )
+ {
+ if ( bNewPos )
+ {
+ bool bInvalidate = false;
+ bool bParentPaint = true;
+ if ( !ImplIsOverlapWindow() )
+ bParentPaint = mpWindowImpl->mpParent->IsPaintEnabled();
+ if ( bCopyBits && bParentPaint && !HasPaintEvent() )
+ {
+ vcl::Region aRegion( GetOutputRectPixel() );
+ if ( mpWindowImpl->mbWinRegion )
+ aRegion.Intersect( GetOutDev()->ImplPixelToDevicePixel( mpWindowImpl->maWinRegion ) );
+ ImplClipBoundaries( aRegion, false, true );
+ if ( !pOverlapRegion->IsEmpty() )
+ {
+ pOverlapRegion->Move( GetOutDev()->mnOutOffX - nOldOutOffX, GetOutDev()->mnOutOffY - nOldOutOffY );
+ aRegion.Exclude( *pOverlapRegion );
+ }
+ if ( !aRegion.IsEmpty() )
+ {
+ // adapt Paint areas
+ ImplMoveAllInvalidateRegions( tools::Rectangle( Point( nOldOutOffX, nOldOutOffY ),
+ Size( nOldOutWidth, nOldOutHeight ) ),
+ GetOutDev()->mnOutOffX - nOldOutOffX, GetOutDev()->mnOutOffY - nOldOutOffY,
+ true );
+ SalGraphics* pGraphics = ImplGetFrameGraphics();
+ if ( pGraphics )
+ {
+
+ OutputDevice *pOutDev = GetOutDev();
+ const bool bSelectClipRegion = pOutDev->SelectClipRegion( aRegion, pGraphics );
+ if ( bSelectClipRegion )
+ {
+ pGraphics->CopyArea( GetOutDev()->mnOutOffX, GetOutDev()->mnOutOffY,
+ nOldOutOffX, nOldOutOffY,
+ nOldOutWidth, nOldOutHeight,
+ *GetOutDev() );
+ }
+ else
+ bInvalidate = true;
+ }
+ else
+ bInvalidate = true;
+ if ( !bInvalidate )
+ {
+ if ( !pOverlapRegion->IsEmpty() )
+ ImplInvalidateFrameRegion( pOverlapRegion.get(), InvalidateFlags::Children );
+ }
+ }
+ else
+ bInvalidate = true;
+ }
+ else
+ bInvalidate = true;
+ if ( bInvalidate )
+ ImplInvalidateFrameRegion( nullptr, InvalidateFlags::Children );
+ }
+ else
+ {
+ vcl::Region aRegion( GetOutputRectPixel() );
+ aRegion.Exclude( *pOldRegion );
+ if ( mpWindowImpl->mbWinRegion )
+ aRegion.Intersect( GetOutDev()->ImplPixelToDevicePixel( mpWindowImpl->maWinRegion ) );
+ ImplClipBoundaries( aRegion, false, true );
+ if ( !aRegion.IsEmpty() )
+ ImplInvalidateFrameRegion( &aRegion, InvalidateFlags::Children );
+ }
+ }
+
+ // invalidate Parent or Overlaps
+ if ( bNewPos ||
+ (GetOutDev()->mnOutWidth < nOldOutWidth) || (GetOutDev()->mnOutHeight < nOldOutHeight) )
+ {
+ vcl::Region aRegion( *pOldRegion );
+ if ( !mpWindowImpl->mbPaintTransparent )
+ ImplExcludeWindowRegion( aRegion );
+ ImplClipBoundaries( aRegion, false, true );
+ if ( !aRegion.IsEmpty() && !mpWindowImpl->mpBorderWindow )
+ ImplInvalidateParentFrameRegion( aRegion );
+ }
+ }
+
+ // adapt system objects
+ if ( bUpdateSysObjClip )
+ ImplUpdateSysObjClip();
+ if ( bUpdateSysObjPos )
+ ImplUpdateSysObjPos();
+ if ( bNewSize && mpWindowImpl->mpSysObj )
+ mpWindowImpl->mpSysObj->SetPosSize( GetOutDev()->mnOutOffX, GetOutDev()->mnOutOffY, GetOutDev()->mnOutWidth, GetOutDev()->mnOutHeight );
+}
+
+void Window::ImplNewInputContext()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::Window* pFocusWin = pSVData->mpWinData->mpFocusWin;
+ if ( !pFocusWin || !pFocusWin->mpWindowImpl || pFocusWin->isDisposed() )
+ return;
+
+ // Is InputContext changed?
+ const InputContext& rInputContext = pFocusWin->GetInputContext();
+ if ( rInputContext == pFocusWin->mpWindowImpl->mpFrameData->maOldInputContext )
+ return;
+
+ pFocusWin->mpWindowImpl->mpFrameData->maOldInputContext = rInputContext;
+
+ SalInputContext aNewContext;
+ const vcl::Font& rFont = rInputContext.GetFont();
+ const OUString& rFontName = rFont.GetFamilyName();
+ rtl::Reference<LogicalFontInstance> pFontInstance;
+ aNewContext.mpFont = nullptr;
+ if (!rFontName.isEmpty())
+ {
+ OutputDevice *pFocusWinOutDev = pFocusWin->GetOutDev();
+ Size aSize = pFocusWinOutDev->ImplLogicToDevicePixel( rFont.GetFontSize() );
+ if ( !aSize.Height() )
+ {
+ // only set default sizes if the font height in logical
+ // coordinates equals 0
+ if ( rFont.GetFontSize().Height() )
+ aSize.setHeight( 1 );
+ else
+ aSize.setHeight( (12*pFocusWin->GetOutDev()->mnDPIY)/72 );
+ }
+ pFontInstance = pFocusWin->GetOutDev()->mxFontCache->GetFontInstance(
+ pFocusWin->GetOutDev()->mxFontCollection.get(),
+ rFont, aSize, static_cast<float>(aSize.Height()) );
+ if ( pFontInstance )
+ aNewContext.mpFont = pFontInstance;
+ }
+ aNewContext.mnOptions = rInputContext.GetOptions();
+ pFocusWin->ImplGetFrame()->SetInputContext( &aNewContext );
+}
+
+void Window::SetDumpAsPropertyTreeHdl(const Link<tools::JsonWriter&, void>& rLink)
+{
+ if (mpWindowImpl) // may be called after dispose
+ {
+ mpWindowImpl->maDumpAsPropertyTreeHdl = rLink;
+ }
+}
+
+void Window::SetModalHierarchyHdl(const Link<bool, void>& rLink)
+{
+ ImplGetFrame()->SetModalHierarchyHdl(rLink);
+}
+
+KeyIndicatorState Window::GetIndicatorState() const
+{
+ return mpWindowImpl->mpFrame->GetIndicatorState();
+}
+
+void Window::SimulateKeyPress( sal_uInt16 nKeyCode ) const
+{
+ mpWindowImpl->mpFrame->SimulateKeyPress(nKeyCode);
+}
+
+void Window::KeyInput( const KeyEvent& rKEvt )
+{
+ KeyCode cod = rKEvt.GetKeyCode ();
+ bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
+
+ // do not respond to accelerators unless Alt or Ctrl is held
+ if (cod.GetCode () >= 0x200 && cod.GetCode () <= 0x219)
+ {
+ if (autoacc && cod.GetModifier () != KEY_MOD2 && !(cod.GetModifier() & KEY_MOD1))
+ return;
+ }
+
+ NotifyEvent aNEvt( NotifyEventType::KEYINPUT, this, &rKEvt );
+ if ( !CompatNotify( aNEvt ) )
+ mpWindowImpl->mbKeyInput = true;
+}
+
+void Window::KeyUp( const KeyEvent& rKEvt )
+{
+ NotifyEvent aNEvt( NotifyEventType::KEYUP, this, &rKEvt );
+ if ( !CompatNotify( aNEvt ) )
+ mpWindowImpl->mbKeyUp = true;
+}
+
+void Window::Draw( OutputDevice*, const Point&, SystemTextColorFlags )
+{
+}
+
+void Window::Move() {}
+
+void Window::Resize() {}
+
+void Window::Activate() {}
+
+void Window::Deactivate() {}
+
+void Window::GetFocus()
+{
+ if ( HasFocus() && mpWindowImpl->mpLastFocusWindow && !(mpWindowImpl->mnDlgCtrlFlags & DialogControlFlags::WantFocus) )
+ {
+ VclPtr<vcl::Window> xWindow(this);
+ mpWindowImpl->mpLastFocusWindow->GrabFocus();
+ if( xWindow->isDisposed() )
+ return;
+ }
+
+ NotifyEvent aNEvt( NotifyEventType::GETFOCUS, this );
+ CompatNotify( aNEvt );
+}
+
+void Window::LoseFocus()
+{
+ NotifyEvent aNEvt( NotifyEventType::LOSEFOCUS, this );
+ CompatNotify( aNEvt );
+}
+
+void Window::SetHelpHdl(const Link<vcl::Window&, bool>& rLink)
+{
+ if (mpWindowImpl) // may be called after dispose
+ {
+ mpWindowImpl->maHelpRequestHdl = rLink;
+ }
+}
+
+void Window::RequestHelp( const HelpEvent& rHEvt )
+{
+ // if Balloon-Help is requested, show the balloon
+ // with help text set
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ {
+ OUString rStr = GetHelpText();
+ if ( rStr.isEmpty() )
+ rStr = GetQuickHelpText();
+ if ( rStr.isEmpty() && ImplGetParent() && !ImplIsOverlapWindow() )
+ ImplGetParent()->RequestHelp( rHEvt );
+ else
+ {
+ Point aPos = GetPosPixel();
+ if ( ImplGetParent() && !ImplIsOverlapWindow() )
+ aPos = OutputToScreenPixel(Point(0, 0));
+ tools::Rectangle aRect( aPos, GetSizePixel() );
+
+ Help::ShowBalloon( this, rHEvt.GetMousePosPixel(), aRect, rStr );
+ }
+ }
+ else if ( rHEvt.GetMode() & HelpEventMode::QUICK )
+ {
+ const OUString& rStr = GetQuickHelpText();
+ if ( rStr.isEmpty() && ImplGetParent() && !ImplIsOverlapWindow() )
+ ImplGetParent()->RequestHelp( rHEvt );
+ else
+ {
+ Point aPos = GetPosPixel();
+ if ( ImplGetParent() && !ImplIsOverlapWindow() )
+ aPos = OutputToScreenPixel(Point(0, 0));
+ tools::Rectangle aRect( aPos, GetSizePixel() );
+ Help::ShowQuickHelp( this, aRect, rStr, QuickHelpFlags::CtrlText );
+ }
+ }
+ else if (!mpWindowImpl->maHelpRequestHdl.IsSet() || mpWindowImpl->maHelpRequestHdl.Call(*this))
+ {
+ OUString aStrHelpId( GetHelpId() );
+ if ( aStrHelpId.isEmpty() && ImplGetParent() )
+ ImplGetParent()->RequestHelp( rHEvt );
+ else
+ {
+ Help* pHelp = Application::GetHelp();
+ if ( pHelp )
+ {
+ if( !aStrHelpId.isEmpty() )
+ pHelp->Start( aStrHelpId, this );
+ else
+ pHelp->Start( OOO_HELP_INDEX, this );
+ }
+ }
+ }
+}
+
+void Window::Command( const CommandEvent& rCEvt )
+{
+ CallEventListeners( VclEventId::WindowCommand, const_cast<CommandEvent *>(&rCEvt) );
+
+ NotifyEvent aNEvt( NotifyEventType::COMMAND, this, &rCEvt );
+ if ( !CompatNotify( aNEvt ) )
+ mpWindowImpl->mbCommand = true;
+}
+
+void Window::Tracking( const TrackingEvent& rTEvt )
+{
+
+ ImplDockingWindowWrapper *pWrapper = ImplGetDockingManager()->GetDockingWindowWrapper( this );
+ if( pWrapper )
+ pWrapper->Tracking( rTEvt );
+}
+
+void Window::StateChanged(StateChangedType eType)
+{
+ switch (eType)
+ {
+ //stuff that doesn't invalidate the layout
+ case StateChangedType::ControlForeground:
+ case StateChangedType::ControlBackground:
+ case StateChangedType::UpdateMode:
+ case StateChangedType::ReadOnly:
+ case StateChangedType::Enable:
+ case StateChangedType::State:
+ case StateChangedType::Data:
+ case StateChangedType::InitShow:
+ case StateChangedType::ControlFocus:
+ break;
+ //stuff that does invalidate the layout
+ default:
+ queue_resize(eType);
+ break;
+ }
+}
+
+void Window::SetStyle( WinBits nStyle )
+{
+ if ( mpWindowImpl && mpWindowImpl->mnStyle != nStyle )
+ {
+ mpWindowImpl->mnPrevStyle = mpWindowImpl->mnStyle;
+ mpWindowImpl->mnStyle = nStyle;
+ CompatStateChanged( StateChangedType::Style );
+ }
+}
+
+void Window::SetExtendedStyle( WindowExtendedStyle nExtendedStyle )
+{
+
+ if ( mpWindowImpl->mnExtendedStyle == nExtendedStyle )
+ return;
+
+ vcl::Window* pWindow = ImplGetBorderWindow();
+ if( ! pWindow )
+ pWindow = this;
+ if( pWindow->mpWindowImpl->mbFrame )
+ {
+ SalExtStyle nExt = 0;
+ if( nExtendedStyle & WindowExtendedStyle::Document )
+ nExt |= SAL_FRAME_EXT_STYLE_DOCUMENT;
+ if( nExtendedStyle & WindowExtendedStyle::DocModified )
+ nExt |= SAL_FRAME_EXT_STYLE_DOCMODIFIED;
+
+ pWindow->ImplGetFrame()->SetExtendedFrameStyle( nExt );
+ }
+ mpWindowImpl->mnExtendedStyle = nExtendedStyle;
+}
+
+void Window::SetBorderStyle( WindowBorderStyle nBorderStyle )
+{
+
+ if ( !mpWindowImpl->mpBorderWindow )
+ return;
+
+ if( nBorderStyle == WindowBorderStyle::REMOVEBORDER &&
+ ! mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame &&
+ mpWindowImpl->mpBorderWindow->mpWindowImpl->mpParent
+ )
+ {
+ // this is a little awkward: some controls (e.g. svtools ProgressBar)
+ // cannot avoid getting constructed with WB_BORDER but want to disable
+ // borders in case of NWF drawing. So they need a method to remove their border window
+ VclPtr<vcl::Window> pBorderWin = mpWindowImpl->mpBorderWindow;
+ // remove us as border window's client
+ pBorderWin->mpWindowImpl->mpClientWindow = nullptr;
+ mpWindowImpl->mpBorderWindow = nullptr;
+ mpWindowImpl->mpRealParent = pBorderWin->mpWindowImpl->mpParent;
+ // reparent us above the border window
+ SetParent( pBorderWin->mpWindowImpl->mpParent );
+ // set us to the position and size of our previous border
+ Point aBorderPos( pBorderWin->GetPosPixel() );
+ Size aBorderSize( pBorderWin->GetSizePixel() );
+ setPosSizePixel( aBorderPos.X(), aBorderPos.Y(), aBorderSize.Width(), aBorderSize.Height() );
+ // release border window
+ pBorderWin.disposeAndClear();
+
+ // set new style bits
+ SetStyle( GetStyle() & (~WB_BORDER) );
+ }
+ else
+ {
+ if ( mpWindowImpl->mpBorderWindow->GetType() == WindowType::BORDERWINDOW )
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->SetBorderStyle( nBorderStyle );
+ else
+ mpWindowImpl->mpBorderWindow->SetBorderStyle( nBorderStyle );
+ }
+}
+
+WindowBorderStyle Window::GetBorderStyle() const
+{
+
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ if ( mpWindowImpl->mpBorderWindow->GetType() == WindowType::BORDERWINDOW )
+ return static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->GetBorderStyle();
+ else
+ return mpWindowImpl->mpBorderWindow->GetBorderStyle();
+ }
+
+ return WindowBorderStyle::NONE;
+}
+
+tools::Long Window::CalcTitleWidth() const
+{
+
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ if ( mpWindowImpl->mpBorderWindow->GetType() == WindowType::BORDERWINDOW )
+ return static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->CalcTitleWidth();
+ else
+ return mpWindowImpl->mpBorderWindow->CalcTitleWidth();
+ }
+ else if ( mpWindowImpl->mbFrame && (mpWindowImpl->mnStyle & WB_MOVEABLE) )
+ {
+ // we guess the width for frame windows as we do not know the
+ // border of external dialogs
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ vcl::Font aFont = GetFont();
+ const_cast<vcl::Window*>(this)->SetPointFont(const_cast<::OutputDevice&>(*GetOutDev()), rStyleSettings.GetTitleFont());
+ tools::Long nTitleWidth = GetTextWidth( GetText() );
+ const_cast<vcl::Window*>(this)->SetFont( aFont );
+ nTitleWidth += rStyleSettings.GetTitleHeight() * 3;
+ nTitleWidth += StyleSettings::GetBorderSize() * 2;
+ nTitleWidth += 10;
+ return nTitleWidth;
+ }
+
+ return 0;
+}
+
+void Window::SetInputContext( const InputContext& rInputContext )
+{
+
+ mpWindowImpl->maInputContext = rInputContext;
+ if ( !mpWindowImpl->mbInFocusHdl && HasFocus() )
+ ImplNewInputContext();
+}
+
+void Window::PostExtTextInputEvent(VclEventId nType, const OUString& rText)
+{
+ switch (nType)
+ {
+ case VclEventId::ExtTextInput:
+ {
+ std::unique_ptr<ExtTextInputAttr[]> pAttr(new ExtTextInputAttr[rText.getLength()]);
+ for (int i = 0; i < rText.getLength(); ++i) {
+ pAttr[i] = ExtTextInputAttr::Underline;
+ }
+ SalExtTextInputEvent aEvent { rText, pAttr.get(), rText.getLength(), EXTTEXTINPUT_CURSOR_OVERWRITE };
+ ImplWindowFrameProc(this, SalEvent::ExtTextInput, &aEvent);
+ }
+ break;
+ case VclEventId::EndExtTextInput:
+ ImplWindowFrameProc(this, SalEvent::EndExtTextInput, nullptr);
+ break;
+ default:
+ assert(false);
+ }
+}
+
+void Window::EndExtTextInput()
+{
+ if ( mpWindowImpl->mbExtTextInput )
+ ImplGetFrame()->EndExtTextInput( EndExtTextInputFlags::Complete );
+}
+
+void Window::SetCursorRect( const tools::Rectangle* pRect, tools::Long nExtTextInputWidth )
+{
+
+ ImplWinData* pWinData = ImplGetWinData();
+ if ( pWinData->mpCursorRect )
+ {
+ if ( pRect )
+ pWinData->mpCursorRect = *pRect;
+ else
+ pWinData->mpCursorRect.reset();
+ }
+ else
+ {
+ if ( pRect )
+ pWinData->mpCursorRect = *pRect;
+ }
+
+ pWinData->mnCursorExtWidth = nExtTextInputWidth;
+
+}
+
+const tools::Rectangle* Window::GetCursorRect() const
+{
+
+ ImplWinData* pWinData = ImplGetWinData();
+ return pWinData->mpCursorRect ? &*pWinData->mpCursorRect : nullptr;
+}
+
+tools::Long Window::GetCursorExtTextInputWidth() const
+{
+
+ ImplWinData* pWinData = ImplGetWinData();
+ return pWinData->mnCursorExtWidth;
+}
+
+void Window::SetCompositionCharRect( const tools::Rectangle* pRect, tools::Long nCompositionLength, bool bVertical ) {
+
+ ImplWinData* pWinData = ImplGetWinData();
+ pWinData->mpCompositionCharRects.reset();
+ pWinData->mbVertical = bVertical;
+ pWinData->mnCompositionCharRects = nCompositionLength;
+ if ( pRect && (nCompositionLength > 0) )
+ {
+ pWinData->mpCompositionCharRects.reset( new tools::Rectangle[nCompositionLength] );
+ for (tools::Long i = 0; i < nCompositionLength; ++i)
+ pWinData->mpCompositionCharRects[i] = pRect[i];
+ }
+}
+
+void Window::CollectChildren(::std::vector<vcl::Window *>& rAllChildren )
+{
+ rAllChildren.push_back( this );
+
+ VclPtr< vcl::Window > pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->CollectChildren( rAllChildren );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+}
+
+void Window::SetPointFont(vcl::RenderContext& rRenderContext, const vcl::Font& rFont,
+ bool bUseRenderContextDPI)
+{
+ vcl::Font aFont = rFont;
+ ImplPointToLogic(rRenderContext, aFont, bUseRenderContextDPI);
+ rRenderContext.SetFont(aFont);
+}
+
+vcl::Font Window::GetPointFont(vcl::RenderContext const & rRenderContext) const
+{
+ vcl::Font aFont = rRenderContext.GetFont();
+ ImplLogicToPoint(rRenderContext, aFont);
+ return aFont;
+}
+
+void Window::Show(bool bVisible, ShowFlags nFlags)
+{
+ if ( !mpWindowImpl || mpWindowImpl->mbVisible == bVisible )
+ return;
+
+ VclPtr<vcl::Window> xWindow(this);
+
+ bool bRealVisibilityChanged = false;
+ mpWindowImpl->mbVisible = bVisible;
+
+ if ( !bVisible )
+ {
+ ImplHideAllOverlaps();
+ if( !xWindow->mpWindowImpl )
+ return;
+
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ bool bOldUpdate = mpWindowImpl->mpBorderWindow->mpWindowImpl->mbNoParentUpdate;
+ if ( mpWindowImpl->mbNoParentUpdate )
+ mpWindowImpl->mpBorderWindow->mpWindowImpl->mbNoParentUpdate = true;
+ mpWindowImpl->mpBorderWindow->Show( false, nFlags );
+ mpWindowImpl->mpBorderWindow->mpWindowImpl->mbNoParentUpdate = bOldUpdate;
+ }
+ else if ( mpWindowImpl->mbFrame )
+ {
+ mpWindowImpl->mbSuppressAccessibilityEvents = true;
+ mpWindowImpl->mpFrame->Show( false );
+ }
+
+ CompatStateChanged( StateChangedType::Visible );
+
+ if ( mpWindowImpl->mbReallyVisible )
+ {
+ if ( mpWindowImpl->mbInitWinClipRegion )
+ ImplInitWinClipRegion();
+
+ vcl::Region aInvRegion = mpWindowImpl->maWinClipRegion;
+
+ if( !xWindow->mpWindowImpl )
+ return;
+
+ bRealVisibilityChanged = mpWindowImpl->mbReallyVisible;
+ ImplResetReallyVisible();
+ ImplSetClipFlag();
+
+ if ( ImplIsOverlapWindow() && !mpWindowImpl->mbFrame )
+ {
+ // convert focus
+ if ( !(nFlags & ShowFlags::NoFocusChange) && HasChildPathFocus() )
+ {
+ if ( mpWindowImpl->mpOverlapWindow->IsEnabled() &&
+ mpWindowImpl->mpOverlapWindow->IsInputEnabled() &&
+ ! mpWindowImpl->mpOverlapWindow->IsInModalMode()
+ )
+ mpWindowImpl->mpOverlapWindow->GrabFocus();
+ }
+ }
+
+ if ( !mpWindowImpl->mbFrame )
+ {
+ if (mpWindowImpl->mpWinData && mpWindowImpl->mpWinData->mbEnableNativeWidget)
+ {
+ /*
+ * #i48371# native theming: some themes draw outside the control
+ * area we tell them to (bad thing, but we cannot do much about it ).
+ * On hiding these controls they get invalidated with their window rectangle
+ * which leads to the parts outside the control area being left and not
+ * invalidated. Workaround: invalidate an area on the parent, too
+ */
+ const int workaround_border = 5;
+ tools::Rectangle aBounds( aInvRegion.GetBoundRect() );
+ aBounds.AdjustLeft( -workaround_border );
+ aBounds.AdjustTop( -workaround_border );
+ aBounds.AdjustRight(workaround_border );
+ aBounds.AdjustBottom(workaround_border );
+ aInvRegion = aBounds;
+ }
+ if ( !mpWindowImpl->mbNoParentUpdate )
+ {
+ if ( !aInvRegion.IsEmpty() )
+ ImplInvalidateParentFrameRegion( aInvRegion );
+ }
+ ImplGenerateMouseMove();
+ }
+ }
+ }
+ else
+ {
+ // inherit native widget flag for form controls
+ // required here, because frames never show up in the child hierarchy - which should be fixed...
+ // eg, the drop down of a combobox which is a system floating window
+ if( mpWindowImpl->mbFrame && GetParent() && !GetParent()->isDisposed() &&
+ GetParent()->IsCompoundControl() &&
+ GetParent()->IsNativeWidgetEnabled() != IsNativeWidgetEnabled() &&
+ !(GetStyle() & WB_TOOLTIPWIN) )
+ {
+ EnableNativeWidget( GetParent()->IsNativeWidgetEnabled() );
+ }
+
+ if ( mpWindowImpl->mbCallMove )
+ {
+ ImplCallMove();
+ }
+ if ( mpWindowImpl->mbCallResize )
+ {
+ ImplCallResize();
+ }
+
+ CompatStateChanged( StateChangedType::Visible );
+
+ vcl::Window* pTestParent;
+ if ( ImplIsOverlapWindow() )
+ pTestParent = mpWindowImpl->mpOverlapWindow;
+ else
+ pTestParent = ImplGetParent();
+ if ( mpWindowImpl->mbFrame || pTestParent->mpWindowImpl->mbReallyVisible )
+ {
+ // if a window becomes visible, send all child windows a StateChange,
+ // such that these can initialise themselves
+ ImplCallInitShow();
+
+ // If it is a SystemWindow it automatically pops up on top of
+ // all other windows if needed.
+ if ( ImplIsOverlapWindow() && !(nFlags & ShowFlags::NoActivate) )
+ {
+ ImplStartToTop(( nFlags & ShowFlags::ForegroundTask ) ? ToTopFlags::ForegroundTask : ToTopFlags::NONE );
+ ImplFocusToTop( ToTopFlags::NONE, false );
+ }
+
+ // adjust mpWindowImpl->mbReallyVisible
+ bRealVisibilityChanged = !mpWindowImpl->mbReallyVisible;
+ ImplSetReallyVisible();
+
+ // assure clip rectangles will be recalculated
+ ImplSetClipFlag();
+
+ if ( !mpWindowImpl->mbFrame )
+ {
+ InvalidateFlags nInvalidateFlags = InvalidateFlags::Children;
+ if( ! IsPaintTransparent() )
+ nInvalidateFlags |= InvalidateFlags::NoTransparent;
+ ImplInvalidate( nullptr, nInvalidateFlags );
+ ImplGenerateMouseMove();
+ }
+ }
+
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->Show( true, nFlags );
+ else if ( mpWindowImpl->mbFrame )
+ {
+ // #106431#, hide SplashScreen
+ ImplSVData* pSVData = ImplGetSVData();
+ if ( !pSVData->mpIntroWindow )
+ {
+ // The right way would be just to call this (not even in the 'if')
+ auto pApp = GetpApp();
+ if ( pApp )
+ pApp->InitFinished();
+ }
+ else if ( !ImplIsWindowOrChild( pSVData->mpIntroWindow ) )
+ {
+ // ... but the VCL splash is broken, and it needs this
+ // (for ./soffice .uno:NewDoc)
+ pSVData->mpIntroWindow->Hide();
+ }
+
+ //SAL_WARN_IF( mpWindowImpl->mbSuppressAccessibilityEvents, "vcl", "Window::Show() - Frame reactivated");
+ mpWindowImpl->mbSuppressAccessibilityEvents = false;
+
+ mpWindowImpl->mbPaintFrame = true;
+ if (!Application::IsHeadlessModeEnabled())
+ {
+ bool bNoActivate(nFlags & (ShowFlags::NoActivate|ShowFlags::NoFocusChange));
+ mpWindowImpl->mpFrame->Show( true, bNoActivate );
+ }
+ if( !xWindow->mpWindowImpl )
+ return;
+
+ // Query the correct size of the window, if we are waiting for
+ // a system resize
+ if ( mpWindowImpl->mbWaitSystemResize )
+ {
+ tools::Long nOutWidth;
+ tools::Long nOutHeight;
+ mpWindowImpl->mpFrame->GetClientSize( nOutWidth, nOutHeight );
+ ImplHandleResize( this, nOutWidth, nOutHeight );
+ }
+
+ if (mpWindowImpl->mpFrameData->mpBuffer && mpWindowImpl->mpFrameData->mpBuffer->GetOutputSizePixel() != GetOutputSizePixel())
+ // Make sure that the buffer size matches the window size, even if no resize was needed.
+ mpWindowImpl->mpFrameData->mpBuffer->SetOutputSizePixel(GetOutputSizePixel());
+ }
+
+ if( !xWindow->mpWindowImpl )
+ return;
+
+ ImplShowAllOverlaps();
+ }
+
+ if( !xWindow->mpWindowImpl )
+ return;
+
+ // the SHOW/HIDE events also serve as indicators to send child creation/destroy events to the access bridge
+ // However, the access bridge only uses this event if the data member is not NULL (it's kind of a hack that
+ // we re-use the SHOW/HIDE events this way, with this particular semantics).
+ // Since #104887#, the notifications for the access bridge are done in Impl(Set|Reset)ReallyVisible. Here, we
+ // now only notify with a NULL data pointer, for all other clients except the access bridge.
+ if ( !bRealVisibilityChanged )
+ CallEventListeners( mpWindowImpl->mbVisible ? VclEventId::WindowShow : VclEventId::WindowHide );
+}
+
+Size Window::GetSizePixel() const
+{
+ if (!mpWindowImpl)
+ {
+ SAL_WARN("vcl.layout", "WTF no windowimpl");
+ return Size(0,0);
+ }
+
+ // #i43257# trigger pending resize handler to assure correct window sizes
+ if( mpWindowImpl->mpFrameData->maResizeIdle.IsActive() )
+ {
+ VclPtr<vcl::Window> xWindow( const_cast<Window*>(this) );
+ mpWindowImpl->mpFrameData->maResizeIdle.Stop();
+ mpWindowImpl->mpFrameData->maResizeIdle.Invoke( nullptr );
+ if( xWindow->isDisposed() )
+ return Size(0,0);
+ }
+
+ return Size( GetOutDev()->mnOutWidth + mpWindowImpl->mnLeftBorder+mpWindowImpl->mnRightBorder,
+ GetOutDev()->mnOutHeight + mpWindowImpl->mnTopBorder+mpWindowImpl->mnBottomBorder );
+}
+
+void Window::GetBorder( sal_Int32& rLeftBorder, sal_Int32& rTopBorder,
+ sal_Int32& rRightBorder, sal_Int32& rBottomBorder ) const
+{
+ rLeftBorder = mpWindowImpl->mnLeftBorder;
+ rTopBorder = mpWindowImpl->mnTopBorder;
+ rRightBorder = mpWindowImpl->mnRightBorder;
+ rBottomBorder = mpWindowImpl->mnBottomBorder;
+}
+
+void Window::Enable( bool bEnable, bool bChild )
+{
+ if ( isDisposed() )
+ return;
+
+ if ( !bEnable )
+ {
+ // the tracking mode will be stopped or the capture will be stolen
+ // when a window is disabled,
+ if ( IsTracking() )
+ EndTracking( TrackingEventFlags::Cancel );
+ if ( IsMouseCaptured() )
+ ReleaseMouse();
+ // try to pass focus to the next control
+ // if the window has focus and is contained in the dialog control
+ // mpWindowImpl->mbDisabled should only be set after a call of ImplDlgCtrlNextWindow().
+ // Otherwise ImplDlgCtrlNextWindow() should be used
+ if ( HasFocus() )
+ ImplDlgCtrlNextWindow();
+ }
+
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ mpWindowImpl->mpBorderWindow->Enable( bEnable, false );
+ if ( (mpWindowImpl->mpBorderWindow->GetType() == WindowType::BORDERWINDOW) &&
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->mpMenuBarWindow )
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->mpMenuBarWindow->Enable( bEnable );
+ }
+
+ // #i56102# restore app focus win in case the
+ // window was disabled when the frame focus changed
+ ImplSVData* pSVData = ImplGetSVData();
+ if (bEnable && pSVData->mpWinData->mpFocusWin == nullptr
+ && mpWindowImpl->mpFrameData->mbHasFocus && mpWindowImpl->mpFrameData->mpFocusWin == this)
+ pSVData->mpWinData->mpFocusWin = this;
+
+ if ( mpWindowImpl->mbDisabled != !bEnable )
+ {
+ mpWindowImpl->mbDisabled = !bEnable;
+ if ( mpWindowImpl->mpSysObj )
+ mpWindowImpl->mpSysObj->Enable( bEnable && !mpWindowImpl->mbInputDisabled );
+ CompatStateChanged( StateChangedType::Enable );
+
+ CallEventListeners( bEnable ? VclEventId::WindowEnabled : VclEventId::WindowDisabled );
+ }
+
+ if ( bChild )
+ {
+ VclPtr< vcl::Window > pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->Enable( bEnable, bChild );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+ }
+
+ if ( IsReallyVisible() )
+ ImplGenerateMouseMove();
+}
+
+void Window::EnableInput( bool bEnable, bool bChild )
+{
+ if (!mpWindowImpl)
+ return;
+
+ if ( mpWindowImpl->mpBorderWindow )
+ {
+ mpWindowImpl->mpBorderWindow->EnableInput( bEnable, false );
+ if ( (mpWindowImpl->mpBorderWindow->GetType() == WindowType::BORDERWINDOW) &&
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->mpMenuBarWindow )
+ static_cast<ImplBorderWindow*>(mpWindowImpl->mpBorderWindow.get())->mpMenuBarWindow->EnableInput( bEnable );
+ }
+
+ if ( (!bEnable && mpWindowImpl->meAlwaysInputMode != AlwaysInputEnabled) || bEnable )
+ {
+ // automatically stop the tracking mode or steal capture
+ // if the window is disabled
+ if ( !bEnable )
+ {
+ if ( IsTracking() )
+ EndTracking( TrackingEventFlags::Cancel );
+ if ( IsMouseCaptured() )
+ ReleaseMouse();
+ }
+
+ if ( mpWindowImpl->mbInputDisabled != !bEnable )
+ {
+ mpWindowImpl->mbInputDisabled = !bEnable;
+ if ( mpWindowImpl->mpSysObj )
+ mpWindowImpl->mpSysObj->Enable( !mpWindowImpl->mbDisabled && bEnable );
+ }
+ }
+
+ // #i56102# restore app focus win in case the
+ // window was disabled when the frame focus changed
+ ImplSVData* pSVData = ImplGetSVData();
+ if (bEnable && pSVData->mpWinData->mpFocusWin == nullptr
+ && mpWindowImpl->mpFrameData->mbHasFocus && mpWindowImpl->mpFrameData->mpFocusWin == this)
+ pSVData->mpWinData->mpFocusWin = this;
+
+ if ( bChild )
+ {
+ VclPtr< vcl::Window > pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->EnableInput( bEnable, bChild );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+ }
+
+ if ( IsReallyVisible() )
+ ImplGenerateMouseMove();
+}
+
+void Window::EnableInput( bool bEnable, const vcl::Window* pExcludeWindow )
+{
+ if (!mpWindowImpl)
+ return;
+
+ EnableInput( bEnable );
+
+ // pExecuteWindow is the first Overlap-Frame --> if this
+ // shouldn't be the case, then this must be changed in dialog.cxx
+ if( pExcludeWindow )
+ pExcludeWindow = pExcludeWindow->ImplGetFirstOverlapWindow();
+ vcl::Window* pSysWin = mpWindowImpl->mpFrameWindow->mpWindowImpl->mpFrameData->mpFirstOverlap;
+ while ( pSysWin )
+ {
+ // Is Window in the path from this window
+ if ( ImplGetFirstOverlapWindow()->ImplIsWindowOrChild( pSysWin, true ) )
+ {
+ // Is Window not in the exclude window path or not the
+ // exclude window, then change the status
+ if ( !pExcludeWindow || !pExcludeWindow->ImplIsWindowOrChild( pSysWin, true ) )
+ pSysWin->EnableInput( bEnable );
+ }
+ pSysWin = pSysWin->mpWindowImpl->mpNextOverlap;
+ }
+
+ // enable/disable floating system windows as well
+ vcl::Window* pFrameWin = ImplGetSVData()->maFrameData.mpFirstFrame;
+ while ( pFrameWin )
+ {
+ if( pFrameWin->ImplIsFloatingWindow() )
+ {
+ // Is Window in the path from this window
+ if ( ImplGetFirstOverlapWindow()->ImplIsWindowOrChild( pFrameWin, true ) )
+ {
+ // Is Window not in the exclude window path or not the
+ // exclude window, then change the status
+ if ( !pExcludeWindow || !pExcludeWindow->ImplIsWindowOrChild( pFrameWin, true ) )
+ pFrameWin->EnableInput( bEnable );
+ }
+ }
+ pFrameWin = pFrameWin->mpWindowImpl->mpFrameData->mpNextFrame;
+ }
+
+ // the same for ownerdraw floating windows
+ if( !mpWindowImpl->mbFrame )
+ return;
+
+ ::std::vector< VclPtr<vcl::Window> >& rList = mpWindowImpl->mpFrameData->maOwnerDrawList;
+ for (auto const& elem : rList)
+ {
+ // Is Window in the path from this window
+ if ( ImplGetFirstOverlapWindow()->ImplIsWindowOrChild( elem, true ) )
+ {
+ // Is Window not in the exclude window path or not the
+ // exclude window, then change the status
+ if ( !pExcludeWindow || !pExcludeWindow->ImplIsWindowOrChild( elem, true ) )
+ elem->EnableInput( bEnable );
+ }
+ }
+}
+
+void Window::AlwaysEnableInput( bool bAlways, bool bChild )
+{
+
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->AlwaysEnableInput( bAlways, false );
+
+ if( bAlways && mpWindowImpl->meAlwaysInputMode != AlwaysInputEnabled )
+ {
+ mpWindowImpl->meAlwaysInputMode = AlwaysInputEnabled;
+ EnableInput(true, false);
+ }
+ else if( ! bAlways && mpWindowImpl->meAlwaysInputMode == AlwaysInputEnabled )
+ {
+ mpWindowImpl->meAlwaysInputMode = AlwaysInputNone;
+ }
+
+ if ( bChild )
+ {
+ VclPtr< vcl::Window > pChild = mpWindowImpl->mpFirstChild;
+ while ( pChild )
+ {
+ pChild->AlwaysEnableInput( bAlways, bChild );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+ }
+}
+
+void Window::SetActivateMode( ActivateModeFlags nMode )
+{
+
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->SetActivateMode( nMode );
+
+ if ( mpWindowImpl->mnActivateMode == nMode )
+ return;
+
+ mpWindowImpl->mnActivateMode = nMode;
+
+ // possibly trigger Deactivate/Activate
+ if ( mpWindowImpl->mnActivateMode != ActivateModeFlags::NONE )
+ {
+ if ( (mpWindowImpl->mbActive || (GetType() == WindowType::BORDERWINDOW)) &&
+ !HasChildPathFocus( true ) )
+ {
+ mpWindowImpl->mbActive = false;
+ Deactivate();
+ }
+ }
+ else
+ {
+ if ( !mpWindowImpl->mbActive || (GetType() == WindowType::BORDERWINDOW) )
+ {
+ mpWindowImpl->mbActive = true;
+ Activate();
+ }
+ }
+}
+
+void Window::setPosSizePixel( tools::Long nX, tools::Long nY,
+ tools::Long nWidth, tools::Long nHeight, PosSizeFlags nFlags )
+{
+ bool bHasValidSize = !mpWindowImpl->mbDefSize;
+
+ if ( nFlags & PosSizeFlags::Pos )
+ mpWindowImpl->mbDefPos = false;
+ if ( nFlags & PosSizeFlags::Size )
+ mpWindowImpl->mbDefSize = false;
+
+ // The top BorderWindow is the window which is to be positioned
+ VclPtr<vcl::Window> pWindow = this;
+ while ( pWindow->mpWindowImpl->mpBorderWindow )
+ pWindow = pWindow->mpWindowImpl->mpBorderWindow;
+
+ if ( pWindow->mpWindowImpl->mbFrame )
+ {
+ // Note: if we're positioning a frame, the coordinates are interpreted
+ // as being the top-left corner of the window's client area and NOT
+ // as the position of the border ! (due to limitations of several UNIX window managers)
+ tools::Long nOldWidth = pWindow->GetOutDev()->mnOutWidth;
+
+ if ( !(nFlags & PosSizeFlags::Width) )
+ nWidth = pWindow->GetOutDev()->mnOutWidth;
+ if ( !(nFlags & PosSizeFlags::Height) )
+ nHeight = pWindow->GetOutDev()->mnOutHeight;
+
+ sal_uInt16 nSysFlags=0;
+ VclPtr<vcl::Window> pParent = GetParent();
+ VclPtr<vcl::Window> pWinParent = pWindow->GetParent();
+
+ if( nFlags & PosSizeFlags::Width )
+ nSysFlags |= SAL_FRAME_POSSIZE_WIDTH;
+ if( nFlags & PosSizeFlags::Height )
+ nSysFlags |= SAL_FRAME_POSSIZE_HEIGHT;
+ if( nFlags & PosSizeFlags::X )
+ {
+ nSysFlags |= SAL_FRAME_POSSIZE_X;
+ if( pWinParent && (pWindow->GetStyle() & WB_SYSTEMCHILDWINDOW) )
+ {
+ nX += pWinParent->GetOutDev()->mnOutOffX;
+ }
+ if( pParent && pParent->GetOutDev()->ImplIsAntiparallel() )
+ {
+ tools::Rectangle aRect( Point ( nX, nY ), Size( nWidth, nHeight ) );
+ const OutputDevice *pParentOutDev = pParent->GetOutDev();
+ if (!comphelper::LibreOfficeKit::isActive())
+ pParentOutDev->ReMirror( aRect );
+ nX = aRect.Left();
+ }
+ }
+ if( !comphelper::LibreOfficeKit::isActive() &&
+ !(nFlags & PosSizeFlags::X) && bHasValidSize &&
+ pWindow->mpWindowImpl->mpFrame->maGeometry.width() )
+ {
+ // RTL: make sure the old right aligned position is not changed
+ // system windows will always grow to the right
+ if ( pWinParent )
+ {
+ OutputDevice *pParentOutDev = pWinParent->GetOutDev();
+ if( pParentOutDev->HasMirroredGraphics() )
+ {
+ const SalFrameGeometry& aSysGeometry = mpWindowImpl->mpFrame->GetUnmirroredGeometry();
+ const SalFrameGeometry& aParentSysGeometry =
+ pWinParent->mpWindowImpl->mpFrame->GetUnmirroredGeometry();
+ tools::Long myWidth = nOldWidth;
+ if( !myWidth )
+ myWidth = aSysGeometry.width();
+ if( !myWidth )
+ myWidth = nWidth;
+ nFlags |= PosSizeFlags::X;
+ nSysFlags |= SAL_FRAME_POSSIZE_X;
+ nX = aParentSysGeometry.x() - aSysGeometry.leftDecoration() + aParentSysGeometry.width()
+ - myWidth - 1 - aSysGeometry.x();
+ }
+ }
+ }
+ if( nFlags & PosSizeFlags::Y )
+ {
+ nSysFlags |= SAL_FRAME_POSSIZE_Y;
+ if( pWinParent && (pWindow->GetStyle() & WB_SYSTEMCHILDWINDOW) )
+ {
+ nY += pWinParent->GetOutDev()->mnOutOffY;
+ }
+ }
+
+ if( nSysFlags & (SAL_FRAME_POSSIZE_WIDTH|SAL_FRAME_POSSIZE_HEIGHT) )
+ {
+ // check for min/max client size and adjust size accordingly
+ // otherwise it may happen that the resize event is ignored, i.e. the old size remains
+ // unchanged but ImplHandleResize() is called with the wrong size
+ SystemWindow *pSystemWindow = dynamic_cast< SystemWindow* >( pWindow.get() );
+ if( pSystemWindow )
+ {
+ Size aMinSize = pSystemWindow->GetMinOutputSizePixel();
+ Size aMaxSize = pSystemWindow->GetMaxOutputSizePixel();
+ if( nWidth < aMinSize.Width() )
+ nWidth = aMinSize.Width();
+ if( nHeight < aMinSize.Height() )
+ nHeight = aMinSize.Height();
+
+ if( nWidth > aMaxSize.Width() )
+ nWidth = aMaxSize.Width();
+ if( nHeight > aMaxSize.Height() )
+ nHeight = aMaxSize.Height();
+ }
+ }
+
+ pWindow->mpWindowImpl->mpFrame->SetPosSize( nX, nY, nWidth, nHeight, nSysFlags );
+
+ // Adjust resize with the hack of different client size and frame geometries to fix
+ // native menu bars. Eventually this should be replaced by proper mnTopBorder usage.
+ pWindow->mpWindowImpl->mpFrame->GetClientSize(nWidth, nHeight);
+
+ // Resize should be called directly. If we haven't
+ // set the correct size, we get a second resize from
+ // the system with the correct size. This can be happened
+ // if the size is too small or too large.
+ ImplHandleResize( pWindow, nWidth, nHeight );
+ }
+ else
+ {
+ pWindow->ImplPosSizeWindow( nX, nY, nWidth, nHeight, nFlags );
+ if ( IsReallyVisible() )
+ ImplGenerateMouseMove();
+ }
+}
+
+Point Window::GetPosPixel() const
+{
+ return mpWindowImpl->maPos;
+}
+
+AbsoluteScreenPixelRectangle Window::GetDesktopRectPixel() const
+{
+ AbsoluteScreenPixelRectangle rRect;
+ mpWindowImpl->mpFrameWindow->mpWindowImpl->mpFrame->GetWorkArea( rRect );
+ return rRect;
+}
+
+Point Window::OutputToScreenPixel( const Point& rPos ) const
+{
+ // relative to top level parent
+ return Point( rPos.X() + GetOutDev()->mnOutOffX, rPos.Y() + GetOutDev()->mnOutOffY );
+}
+
+Point Window::ScreenToOutputPixel( const Point& rPos ) const
+{
+ // relative to top level parent
+ return Point( rPos.X() - GetOutDev()->mnOutOffX, rPos.Y() - GetOutDev()->mnOutOffY );
+}
+
+tools::Long Window::ImplGetUnmirroredOutOffX() const
+{
+ // revert mnOutOffX changes that were potentially made in ImplPosSizeWindow
+ tools::Long offx = GetOutDev()->mnOutOffX;
+ const OutputDevice *pOutDev = GetOutDev();
+ if( pOutDev->HasMirroredGraphics() )
+ {
+ if( mpWindowImpl->mpParent && !mpWindowImpl->mpParent->mpWindowImpl->mbFrame && mpWindowImpl->mpParent->GetOutDev()->ImplIsAntiparallel() )
+ {
+ if ( !ImplIsOverlapWindow() )
+ offx -= mpWindowImpl->mpParent->GetOutDev()->mnOutOffX;
+
+ offx = mpWindowImpl->mpParent->GetOutDev()->mnOutWidth - GetOutDev()->mnOutWidth - offx;
+
+ if ( !ImplIsOverlapWindow() )
+ offx += mpWindowImpl->mpParent->GetOutDev()->mnOutOffX;
+
+ }
+ }
+ return offx;
+}
+
+// normalized screen pixel are independent of mirroring
+Point Window::OutputToNormalizedScreenPixel( const Point& rPos ) const
+{
+ // relative to top level parent
+ tools::Long offx = ImplGetUnmirroredOutOffX();
+ return Point( rPos.X()+offx, rPos.Y() + GetOutDev()->mnOutOffY );
+}
+
+Point Window::NormalizedScreenToOutputPixel( const Point& rPos ) const
+{
+ // relative to top level parent
+ tools::Long offx = ImplGetUnmirroredOutOffX();
+ return Point( rPos.X()-offx, rPos.Y() - GetOutDev()->mnOutOffY );
+}
+
+AbsoluteScreenPixelPoint Window::OutputToAbsoluteScreenPixel( const Point& rPos ) const
+{
+ // relative to the screen
+ Point p = OutputToScreenPixel( rPos );
+ SalFrameGeometry g = mpWindowImpl->mpFrame->GetGeometry();
+ p.AdjustX(g.x() );
+ p.AdjustY(g.y() );
+ return AbsoluteScreenPixelPoint(p);
+}
+
+Point Window::AbsoluteScreenToOutputPixel( const AbsoluteScreenPixelPoint& rPos ) const
+{
+ // relative to the screen
+ Point p = ScreenToOutputPixel( Point(rPos) );
+ SalFrameGeometry g = mpWindowImpl->mpFrame->GetGeometry();
+ p.AdjustX( -(g.x()) );
+ p.AdjustY( -(g.y()) );
+ return p;
+}
+
+AbsoluteScreenPixelRectangle Window::ImplOutputToUnmirroredAbsoluteScreenPixel( const tools::Rectangle &rRect ) const
+{
+ // this method creates unmirrored screen coordinates to be compared with the desktop
+ // and is used for positioning of RTL popup windows correctly on the screen
+ SalFrameGeometry g = mpWindowImpl->mpFrame->GetUnmirroredGeometry();
+
+ Point p1 = rRect.TopRight();
+ p1 = OutputToScreenPixel(p1);
+ p1.setX( g.x()+g.width()-p1.X() );
+ p1.AdjustY(g.y() );
+
+ Point p2 = rRect.BottomLeft();
+ p2 = OutputToScreenPixel(p2);
+ p2.setX( g.x()+g.width()-p2.X() );
+ p2.AdjustY(g.y() );
+
+ return AbsoluteScreenPixelRectangle( AbsoluteScreenPixelPoint(p1), AbsoluteScreenPixelPoint(p2) );
+}
+
+tools::Rectangle Window::ImplUnmirroredAbsoluteScreenToOutputPixel( const AbsoluteScreenPixelRectangle &rRect ) const
+{
+ // undo ImplOutputToUnmirroredAbsoluteScreenPixel
+ SalFrameGeometry g = mpWindowImpl->mpFrame->GetUnmirroredGeometry();
+
+ Point p1( rRect.TopRight() );
+ p1.AdjustY(-g.y() );
+ p1.setX( g.x()+g.width()-p1.X() );
+ p1 = ScreenToOutputPixel(p1);
+
+ Point p2( rRect.BottomLeft() );
+ p2.AdjustY(-g.y());
+ p2.setX( g.x()+g.width()-p2.X() );
+ p2 = ScreenToOutputPixel(p2);
+
+ return tools::Rectangle( p1, p2 );
+}
+
+
+// with decoration
+tools::Rectangle Window::GetWindowExtentsRelative(const vcl::Window & rRelativeWindow) const
+{
+ AbsoluteScreenPixelRectangle aRect = GetWindowExtentsAbsolute();
+ // #106399# express coordinates relative to borderwindow
+ const vcl::Window *pRelWin = rRelativeWindow.mpWindowImpl->mpBorderWindow ? rRelativeWindow.mpWindowImpl->mpBorderWindow.get() : &rRelativeWindow;
+ return tools::Rectangle(
+ pRelWin->AbsoluteScreenToOutputPixel( aRect.GetPos() ),
+ aRect.GetSize() );
+}
+
+// with decoration
+AbsoluteScreenPixelRectangle Window::GetWindowExtentsAbsolute() const
+{
+ // make sure we use the extent of our border window,
+ // otherwise we miss a few pixels
+ const vcl::Window *pWin = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow : this;
+
+ AbsoluteScreenPixelPoint aPos( pWin->OutputToAbsoluteScreenPixel( Point(0,0) ) );
+ Size aSize ( pWin->GetSizePixel() );
+ // #104088# do not add decoration to the workwindow to be compatible to java accessibility api
+ if( mpWindowImpl->mbFrame || (mpWindowImpl->mpBorderWindow && mpWindowImpl->mpBorderWindow->mpWindowImpl->mbFrame && GetType() != WindowType::WORKWINDOW) )
+ {
+ SalFrameGeometry g = mpWindowImpl->mpFrame->GetGeometry();
+ aPos.AdjustX( -sal_Int32(g.leftDecoration()) );
+ aPos.AdjustY( -sal_Int32(g.topDecoration()) );
+ aSize.AdjustWidth(g.leftDecoration() + g.rightDecoration() );
+ aSize.AdjustHeight(g.topDecoration() + g.bottomDecoration() );
+ }
+ return AbsoluteScreenPixelRectangle( aPos, aSize );
+}
+
+void Window::Scroll( tools::Long nHorzScroll, tools::Long nVertScroll, ScrollFlags nFlags )
+{
+
+ ImplScroll( GetOutputRectPixel(),
+ nHorzScroll, nVertScroll, nFlags & ~ScrollFlags::Clip );
+}
+
+void Window::Scroll( tools::Long nHorzScroll, tools::Long nVertScroll,
+ const tools::Rectangle& rRect, ScrollFlags nFlags )
+{
+ OutputDevice *pOutDev = GetOutDev();
+ tools::Rectangle aRect = pOutDev->ImplLogicToDevicePixel( rRect );
+ aRect.Intersection( GetOutputRectPixel() );
+ if ( !aRect.IsEmpty() )
+ ImplScroll( aRect, nHorzScroll, nVertScroll, nFlags );
+}
+
+void WindowOutputDevice::Flush()
+{
+ if (mxOwnerWindow->mpWindowImpl)
+ mxOwnerWindow->mpWindowImpl->mpFrame->Flush( GetOutputRectPixel() );
+}
+
+void Window::SetUpdateMode( bool bUpdate )
+{
+ if (mpWindowImpl)
+ {
+ mpWindowImpl->mbNoUpdate = !bUpdate;
+ CompatStateChanged( StateChangedType::UpdateMode );
+ }
+}
+
+void Window::GrabFocus()
+{
+ ImplGrabFocus( GetFocusFlags::NONE );
+}
+
+bool Window::HasFocus() const
+{
+ return (this == ImplGetSVData()->mpWinData->mpFocusWin);
+}
+
+void Window::GrabFocusToDocument()
+{
+ ImplGrabFocusToDocument(GetFocusFlags::NONE);
+}
+
+VclPtr<vcl::Window> Window::GetFocusedWindow() const
+{
+ if (mpWindowImpl && mpWindowImpl->mpFrameData)
+ return mpWindowImpl->mpFrameData->mpFocusWin;
+ else
+ return VclPtr<vcl::Window>();
+}
+
+void Window::SetFakeFocus( bool bFocus )
+{
+ ImplGetWindowImpl()->mbFakeFocusSet = bFocus;
+}
+
+bool Window::HasChildPathFocus( bool bSystemWindow ) const
+{
+
+ vcl::Window* pFocusWin = ImplGetSVData()->mpWinData->mpFocusWin;
+ if ( pFocusWin )
+ return ImplIsWindowOrChild( pFocusWin, bSystemWindow );
+ return false;
+}
+
+void Window::SetCursor( vcl::Cursor* pCursor )
+{
+
+ if ( mpWindowImpl->mpCursor != pCursor )
+ {
+ if ( mpWindowImpl->mpCursor )
+ mpWindowImpl->mpCursor->ImplHide();
+ mpWindowImpl->mpCursor = pCursor;
+ if ( pCursor )
+ pCursor->ImplShow();
+ }
+}
+
+void Window::SetText( const OUString& rStr )
+{
+ if (!mpWindowImpl || rStr == mpWindowImpl->maText)
+ return;
+
+ OUString oldTitle( mpWindowImpl->maText );
+ mpWindowImpl->maText = rStr;
+
+ if ( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->SetText( rStr );
+ else if ( mpWindowImpl->mbFrame )
+ mpWindowImpl->mpFrame->SetTitle( rStr );
+
+ CallEventListeners( VclEventId::WindowFrameTitleChanged, &oldTitle );
+
+ // #107247# needed for accessibility
+ // The VclEventId::WindowFrameTitleChanged is (mis)used to notify accessible name changes.
+ // Therefore a window, which is labeled by this window, must also notify an accessible
+ // name change.
+ if ( IsReallyVisible() )
+ {
+ vcl::Window* pWindow = GetAccessibleRelationLabelFor();
+ if ( pWindow && pWindow != this )
+ pWindow->CallEventListeners( VclEventId::WindowFrameTitleChanged, &oldTitle );
+ }
+
+ CompatStateChanged( StateChangedType::Text );
+}
+
+OUString Window::GetText() const
+{
+
+ return mpWindowImpl->maText;
+}
+
+OUString Window::GetDisplayText() const
+{
+
+ return GetText();
+}
+
+const Wallpaper& Window::GetDisplayBackground() const
+{
+ // FIXME: fix issue 52349, need to fix this really in
+ // all NWF enabled controls
+ const ToolBox* pTB = dynamic_cast<const ToolBox*>(this);
+ if( pTB && IsNativeWidgetEnabled() )
+ return pTB->ImplGetToolBoxPrivateData()->maDisplayBackground;
+
+ if( !IsBackground() )
+ {
+ if( mpWindowImpl->mpParent )
+ return mpWindowImpl->mpParent->GetDisplayBackground();
+ }
+
+ const Wallpaper& rBack = GetBackground();
+ if( ! rBack.IsBitmap() &&
+ ! rBack.IsGradient() &&
+ rBack.GetColor()== COL_TRANSPARENT &&
+ mpWindowImpl->mpParent )
+ return mpWindowImpl->mpParent->GetDisplayBackground();
+ return rBack;
+}
+
+const OUString& Window::GetHelpText() const
+{
+ OUString aStrHelpId( GetHelpId() );
+ bool bStrHelpId = !aStrHelpId.isEmpty();
+
+ if ( !mpWindowImpl->maHelpText.getLength() && bStrHelpId )
+ {
+ if ( !IsDialog() && (mpWindowImpl->mnType != WindowType::TABPAGE) && (mpWindowImpl->mnType != WindowType::FLOATINGWINDOW) )
+ {
+ Help* pHelp = Application::GetHelp();
+ if ( pHelp )
+ {
+ mpWindowImpl->maHelpText = pHelp->GetHelpText(aStrHelpId, this);
+ mpWindowImpl->mbHelpTextDynamic = false;
+ }
+ }
+ }
+ else if( mpWindowImpl->mbHelpTextDynamic && bStrHelpId )
+ {
+ static const char* pEnv = getenv( "HELP_DEBUG" );
+ if( pEnv && *pEnv )
+ {
+ mpWindowImpl->maHelpText = mpWindowImpl->maHelpText + "\n------------------\n" + aStrHelpId;
+ }
+ mpWindowImpl->mbHelpTextDynamic = false;
+ }
+
+ //Fallback to Window::GetAccessibleDescription without reentry to GetHelpText()
+ if (mpWindowImpl->maHelpText.isEmpty() && mpWindowImpl->mpAccessibleInfos && mpWindowImpl->mpAccessibleInfos->pAccessibleDescription)
+ return *mpWindowImpl->mpAccessibleInfos->pAccessibleDescription;
+ return mpWindowImpl->maHelpText;
+}
+
+void Window::SetWindowPeer( Reference< css::awt::XVclWindowPeer > const & xPeer, VCLXWindow* pVCLXWindow )
+{
+ if (!mpWindowImpl || mpWindowImpl->mbInDispose)
+ return;
+
+ // be safe against re-entrance: first clear the old ref, then assign the new one
+ if (mpWindowImpl->mxWindowPeer)
+ {
+ // first, disconnect the peer from ourself, otherwise disposing it, will dispose us
+ UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
+ SAL_WARN_IF( !pWrapper, "vcl.window", "SetComponentInterface: No Wrapper!" );
+ if ( pWrapper )
+ pWrapper->SetWindowInterface( nullptr, mpWindowImpl->mxWindowPeer );
+ mpWindowImpl->mxWindowPeer->dispose();
+ mpWindowImpl->mxWindowPeer.clear();
+ }
+ mpWindowImpl->mxWindowPeer = xPeer;
+
+ mpWindowImpl->mpVCLXWindow = pVCLXWindow;
+}
+
+Reference< css::awt::XVclWindowPeer > Window::GetComponentInterface( bool bCreate )
+{
+ if ( !mpWindowImpl->mxWindowPeer.is() && bCreate )
+ {
+ UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
+ if ( pWrapper )
+ mpWindowImpl->mxWindowPeer = pWrapper->GetWindowInterface( this );
+ }
+ return mpWindowImpl->mxWindowPeer;
+}
+
+void Window::SetComponentInterface( Reference< css::awt::XVclWindowPeer > const & xIFace )
+{
+ UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper();
+ SAL_WARN_IF( !pWrapper, "vcl.window", "SetComponentInterface: No Wrapper!" );
+ if ( pWrapper )
+ pWrapper->SetWindowInterface( this, xIFace );
+}
+
+typedef std::map<vcl::LOKWindowId, VclPtr<vcl::Window>> LOKWindowsMap;
+
+namespace {
+
+LOKWindowsMap& GetLOKWindowsMap()
+{
+ // Map to remember the LOKWindowId <-> Window binding.
+ static LOKWindowsMap s_aLOKWindowsMap;
+
+ return s_aLOKWindowsMap;
+}
+
+}
+
+void Window::SetLOKNotifier(const vcl::ILibreOfficeKitNotifier* pNotifier, bool bParent)
+{
+ // don't allow setting this twice
+ assert(mpWindowImpl->mpLOKNotifier == nullptr);
+ assert(pNotifier);
+ // never use this in the desktop case
+ assert(comphelper::LibreOfficeKit::isActive());
+
+ if (!bParent)
+ {
+ // Counter to be able to have unique id's for each window.
+ static vcl::LOKWindowId sLastLOKWindowId = 1;
+
+ // assign the LOK window id
+ assert(mpWindowImpl->mnLOKWindowId == 0);
+ mpWindowImpl->mnLOKWindowId = sLastLOKWindowId++;
+ GetLOKWindowsMap().emplace(mpWindowImpl->mnLOKWindowId, this);
+ }
+
+ mpWindowImpl->mpLOKNotifier = pNotifier;
+}
+
+VclPtr<Window> Window::FindLOKWindow(vcl::LOKWindowId nWindowId)
+{
+ const auto it = GetLOKWindowsMap().find(nWindowId);
+ if (it != GetLOKWindowsMap().end())
+ return it->second;
+
+ return VclPtr<Window>();
+}
+
+bool Window::IsLOKWindowsEmpty()
+{
+ return GetLOKWindowsMap().empty();
+}
+
+void Window::ReleaseLOKNotifier()
+{
+ // unregister the LOK window binding
+ if (mpWindowImpl->mnLOKWindowId > 0)
+ GetLOKWindowsMap().erase(mpWindowImpl->mnLOKWindowId);
+
+ mpWindowImpl->mpLOKNotifier = nullptr;
+ mpWindowImpl->mnLOKWindowId = 0;
+}
+
+ILibreOfficeKitNotifier::~ILibreOfficeKitNotifier()
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ return;
+ }
+
+ for (auto it = GetLOKWindowsMap().begin(); it != GetLOKWindowsMap().end();)
+ {
+ WindowImpl* pWindowImpl = it->second->ImplGetWindowImpl();
+ if (pWindowImpl && pWindowImpl->mpLOKNotifier == this)
+ {
+ pWindowImpl->mpLOKNotifier = nullptr;
+ pWindowImpl->mnLOKWindowId = 0;
+ it = GetLOKWindowsMap().erase(it);
+ continue;
+ }
+
+ ++it;
+ }
+}
+
+const vcl::ILibreOfficeKitNotifier* Window::GetLOKNotifier() const
+{
+ return mpWindowImpl ? mpWindowImpl->mpLOKNotifier : nullptr;
+}
+
+vcl::LOKWindowId Window::GetLOKWindowId() const
+{
+ return mpWindowImpl ? mpWindowImpl->mnLOKWindowId : 0;
+}
+
+VclPtr<vcl::Window> Window::GetParentWithLOKNotifier()
+{
+ VclPtr<vcl::Window> pWindow(this);
+
+ while (pWindow && !pWindow->GetLOKNotifier())
+ pWindow = pWindow->GetParent();
+
+ return pWindow;
+}
+
+namespace
+{
+
+std::string_view windowTypeName(WindowType nWindowType)
+{
+ switch (nWindowType)
+ {
+ case WindowType::NONE: return "none";
+ case WindowType::MESSBOX: return "messagebox";
+ case WindowType::INFOBOX: return "infobox";
+ case WindowType::WARNINGBOX: return "warningbox";
+ case WindowType::ERRORBOX: return "errorbox";
+ case WindowType::QUERYBOX: return "querybox";
+ case WindowType::WINDOW: return "window";
+ case WindowType::WORKWINDOW: return "workwindow";
+ case WindowType::CONTAINER: return "container";
+ case WindowType::FLOATINGWINDOW: return "floatingwindow";
+ case WindowType::DIALOG: return "dialog";
+ case WindowType::MODELESSDIALOG: return "modelessdialog";
+ case WindowType::CONTROL: return "control";
+ case WindowType::PUSHBUTTON: return "pushbutton";
+ case WindowType::OKBUTTON: return "okbutton";
+ case WindowType::CANCELBUTTON: return "cancelbutton";
+ case WindowType::HELPBUTTON: return "helpbutton";
+ case WindowType::IMAGEBUTTON: return "imagebutton";
+ case WindowType::MENUBUTTON: return "menubutton";
+ case WindowType::MOREBUTTON: return "morebutton";
+ case WindowType::SPINBUTTON: return "spinbutton";
+ case WindowType::RADIOBUTTON: return "radiobutton";
+ case WindowType::CHECKBOX: return "checkbox";
+ case WindowType::TRISTATEBOX: return "tristatebox";
+ case WindowType::EDIT: return "edit";
+ case WindowType::MULTILINEEDIT: return "multilineedit";
+ case WindowType::COMBOBOX: return "combobox";
+ case WindowType::LISTBOX: return "listbox";
+ case WindowType::MULTILISTBOX: return "multilistbox";
+ case WindowType::FIXEDTEXT: return "fixedtext";
+ case WindowType::FIXEDLINE: return "fixedline";
+ case WindowType::FIXEDBITMAP: return "fixedbitmap";
+ case WindowType::FIXEDIMAGE: return "fixedimage";
+ case WindowType::GROUPBOX: return "groupbox";
+ case WindowType::SCROLLBAR: return "scrollbar";
+ case WindowType::SCROLLBARBOX: return "scrollbarbox";
+ case WindowType::SPLITTER: return "splitter";
+ case WindowType::SPLITWINDOW: return "splitwindow";
+ case WindowType::SPINFIELD: return "spinfield";
+ case WindowType::PATTERNFIELD: return "patternfield";
+ case WindowType::METRICFIELD: return "metricfield";
+ case WindowType::FORMATTEDFIELD: return "formattedfield";
+ case WindowType::CURRENCYFIELD: return "currencyfield";
+ case WindowType::DATEFIELD: return "datefield";
+ case WindowType::TIMEFIELD: return "timefield";
+ case WindowType::PATTERNBOX: return "patternbox";
+ case WindowType::NUMERICBOX: return "numericbox";
+ case WindowType::METRICBOX: return "metricbox";
+ case WindowType::CURRENCYBOX: return "currencybox";
+ case WindowType::DATEBOX: return "datebox";
+ case WindowType::TIMEBOX: return "timebox";
+ case WindowType::LONGCURRENCYBOX: return "longcurrencybox";
+ case WindowType::SCROLLWINDOW: return "scrollwindow";
+ case WindowType::TOOLBOX: return "toolbox";
+ case WindowType::DOCKINGWINDOW: return "dockingwindow";
+ case WindowType::STATUSBAR: return "statusbar";
+ case WindowType::TABPAGE: return "tabpage";
+ case WindowType::TABCONTROL: return "tabcontrol";
+ case WindowType::TABDIALOG: return "tabdialog";
+ case WindowType::BORDERWINDOW: return "borderwindow";
+ case WindowType::BUTTONDIALOG: return "buttondialog";
+ case WindowType::SYSTEMCHILDWINDOW: return "systemchildwindow";
+ case WindowType::SLIDER: return "slider";
+ case WindowType::MENUBARWINDOW: return "menubarwindow";
+ case WindowType::TREELISTBOX: return "treelistbox";
+ case WindowType::HELPTEXTWINDOW: return "helptextwindow";
+ case WindowType::INTROWINDOW: return "introwindow";
+ case WindowType::LISTBOXWINDOW: return "listboxwindow";
+ case WindowType::DOCKINGAREA: return "dockingarea";
+ case WindowType::RULER: return "ruler";
+ case WindowType::HEADERBAR: return "headerbar";
+ case WindowType::VERTICALTABCONTROL: return "verticaltabcontrol";
+
+ // nothing to do here, but for completeness
+ case WindowType::TOOLKIT_FRAMEWINDOW: return "toolkit_framewindow";
+ case WindowType::TOOLKIT_SYSTEMCHILDWINDOW: return "toolkit_systemchildwindow";
+ }
+
+ return "none";
+}
+
+}
+
+void Window::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ if (!mpWindowImpl)
+ return;
+
+ rJsonWriter.put("id", get_id()); // TODO could be missing - sort out
+ rJsonWriter.put("type", windowTypeName(GetType()));
+ rJsonWriter.put("text", GetText());
+ rJsonWriter.put("enabled", IsEnabled());
+ if (!IsVisible())
+ rJsonWriter.put("visible", false);
+
+ if (vcl::Window* pChild = mpWindowImpl->mpFirstChild)
+ {
+ auto childrenNode = rJsonWriter.startArray("children");
+ while (pChild)
+ {
+ {
+ auto childNode = rJsonWriter.startStruct();
+ pChild->DumpAsPropertyTree(rJsonWriter);
+ sal_Int32 nLeft = pChild->get_grid_left_attach();
+ sal_Int32 nTop = pChild->get_grid_top_attach();
+ if (nLeft != -1 && nTop != -1)
+ {
+ rJsonWriter.put("left", nLeft);
+ rJsonWriter.put("top", nTop);
+ }
+
+ sal_Int32 nWidth = pChild->get_grid_width();
+ if (nWidth > 1)
+ rJsonWriter.put("width", nWidth);
+ }
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+ }
+
+ vcl::Window* pAccLabelFor = getAccessibleRelationLabelFor();
+ if (pAccLabelFor)
+ rJsonWriter.put("labelFor", pAccLabelFor->get_id());
+
+ vcl::Window* pAccLabelledBy = GetAccessibleRelationLabeledBy();
+ if (pAccLabelledBy)
+ rJsonWriter.put("labelledBy", pAccLabelledBy->get_id());
+
+ mpWindowImpl->maDumpAsPropertyTreeHdl.Call(rJsonWriter);
+}
+
+void Window::ImplCallDeactivateListeners( vcl::Window *pNew )
+{
+ // no deactivation if the newly activated window is my child
+ if ( !pNew || !ImplIsChild( pNew ) )
+ {
+ VclPtr<vcl::Window> xWindow(this);
+ CallEventListeners( VclEventId::WindowDeactivate, pNew );
+ if( !xWindow->mpWindowImpl )
+ return;
+
+ // #100759#, avoid walking the wrong frame's hierarchy
+ // eg, undocked docking windows (ImplDockFloatWin)
+ if ( ImplGetParent() && ImplGetParent()->mpWindowImpl &&
+ mpWindowImpl->mpFrameWindow == ImplGetParent()->mpWindowImpl->mpFrameWindow )
+ ImplGetParent()->ImplCallDeactivateListeners( pNew );
+ }
+}
+
+void Window::ImplCallActivateListeners( vcl::Window *pOld )
+{
+ // no activation if the old active window is my child
+ if ( pOld && ImplIsChild( pOld ))
+ return;
+
+ VclPtr<vcl::Window> xWindow(this);
+ CallEventListeners( VclEventId::WindowActivate, pOld );
+ if( !xWindow->mpWindowImpl )
+ return;
+
+ if ( ImplGetParent() )
+ ImplGetParent()->ImplCallActivateListeners( pOld );
+ else if( (mpWindowImpl->mnStyle & WB_INTROWIN) == 0 )
+ {
+ // top level frame reached: store hint for DefModalDialogParent
+ ImplGetSVData()->maFrameData.mpActiveApplicationFrame = mpWindowImpl->mpFrameWindow;
+ }
+}
+
+void Window::SetClipboard(Reference<XClipboard> const & xClipboard)
+{
+ if (mpWindowImpl->mpFrameData)
+ mpWindowImpl->mpFrameData->mxClipboard = xClipboard;
+}
+
+Reference< XClipboard > Window::GetClipboard()
+{
+ if (!mpWindowImpl->mpFrameData)
+ return static_cast<XClipboard*>(nullptr);
+ if (!mpWindowImpl->mpFrameData->mxClipboard.is())
+ mpWindowImpl->mpFrameData->mxClipboard = GetSystemClipboard();
+ return mpWindowImpl->mpFrameData->mxClipboard;
+}
+
+void Window::RecordLayoutData( vcl::ControlLayoutData* pLayout, const tools::Rectangle& rRect )
+{
+ assert(GetOutDev()->mpOutDevData);
+ GetOutDev()->mpOutDevData->mpRecordLayout = pLayout;
+ GetOutDev()->mpOutDevData->maRecordRect = rRect;
+ Paint(*GetOutDev(), rRect);
+ GetOutDev()->mpOutDevData->mpRecordLayout = nullptr;
+}
+
+void Window::DrawSelectionBackground( const tools::Rectangle& rRect,
+ sal_uInt16 highlight,
+ bool bChecked,
+ bool bDrawBorder
+ )
+{
+ if( rRect.IsEmpty() )
+ return;
+
+ const StyleSettings& rStyles = GetSettings().GetStyleSettings();
+
+ // colors used for item highlighting
+ Color aSelectionBorderCol( rStyles.GetHighlightColor() );
+ Color aSelectionFillCol( aSelectionBorderCol );
+
+ bool bDark = rStyles.GetFaceColor().IsDark();
+ bool bBright = ( rStyles.GetFaceColor() == COL_WHITE );
+
+ int c1 = aSelectionBorderCol.GetLuminance();
+ int c2 = GetBackgroundColor().GetLuminance();
+
+ if( !bDark && !bBright && abs( c2-c1 ) < 75 )
+ {
+ // contrast too low
+ sal_uInt16 h,s,b;
+ aSelectionFillCol.RGBtoHSB( h, s, b );
+ if( b > 50 ) b -= 40;
+ else b += 40;
+ aSelectionFillCol = Color::HSBtoRGB( h, s, b );
+ aSelectionBorderCol = aSelectionFillCol;
+ }
+
+ tools::Rectangle aRect( rRect );
+ Color oldFillCol = GetOutDev()->GetFillColor();
+ Color oldLineCol = GetOutDev()->GetLineColor();
+
+ if( bDrawBorder )
+ GetOutDev()->SetLineColor( bDark ? COL_WHITE : ( bBright ? COL_BLACK : aSelectionBorderCol ) );
+ else
+ GetOutDev()->SetLineColor();
+
+ sal_uInt16 nPercent = 0;
+ if( !highlight )
+ {
+ if( bDark )
+ aSelectionFillCol = COL_BLACK;
+ else
+ nPercent = 80; // just checked (light)
+ }
+ else
+ {
+ if( bChecked && highlight == 2 )
+ {
+ if( bDark )
+ aSelectionFillCol = COL_LIGHTGRAY;
+ else if ( bBright )
+ {
+ aSelectionFillCol = COL_BLACK;
+ GetOutDev()->SetLineColor( COL_BLACK );
+ nPercent = 0;
+ }
+ else
+ nPercent = 20; // selected, pressed or checked ( very dark )
+ }
+ else if( bChecked || highlight == 1 )
+ {
+ if( bDark )
+ aSelectionFillCol = COL_GRAY;
+ else if ( bBright )
+ {
+ aSelectionFillCol = COL_BLACK;
+ GetOutDev()->SetLineColor( COL_BLACK );
+ nPercent = 0;
+ }
+ else
+ nPercent = 35; // selected, pressed or checked ( very dark )
+ }
+ else
+ {
+ if( bDark )
+ aSelectionFillCol = COL_LIGHTGRAY;
+ else if ( bBright )
+ {
+ aSelectionFillCol = COL_BLACK;
+ GetOutDev()->SetLineColor( COL_BLACK );
+ if( highlight == 3 )
+ nPercent = 80;
+ else
+ nPercent = 0;
+ }
+ else
+ nPercent = 70; // selected ( dark )
+ }
+ }
+
+ GetOutDev()->SetFillColor( aSelectionFillCol );
+
+ if( bDark )
+ {
+ GetOutDev()->DrawRect( aRect );
+ }
+ else
+ {
+ tools::Polygon aPoly( aRect );
+ tools::PolyPolygon aPolyPoly( aPoly );
+ GetOutDev()->DrawTransparent( aPolyPoly, nPercent );
+ }
+
+ GetOutDev()->SetFillColor( oldFillCol );
+ GetOutDev()->SetLineColor( oldLineCol );
+}
+
+bool Window::IsScrollable() const
+{
+ // check for scrollbars
+ VclPtr< vcl::Window > pChild = mpWindowImpl->mpFirstChild;
+ while( pChild )
+ {
+ if( pChild->GetType() == WindowType::SCROLLBAR )
+ return true;
+ else
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+ return false;
+}
+
+void Window::ImplMirrorFramePos( Point &pt ) const
+{
+ pt.setX( mpWindowImpl->mpFrame->maGeometry.width()-1-pt.X() );
+}
+
+// frame based modal counter (dialogs are not modal to the whole application anymore)
+bool Window::IsInModalMode() const
+{
+ return (mpWindowImpl->mpFrameWindow->mpWindowImpl->mpFrameData->mnModalMode != 0);
+}
+
+void Window::IncModalCount()
+{
+ vcl::Window* pFrameWindow = mpWindowImpl->mpFrameWindow;
+ vcl::Window* pParent = pFrameWindow;
+ while( pFrameWindow )
+ {
+ pFrameWindow->mpWindowImpl->mpFrameData->mnModalMode++;
+ while( pParent && pParent->mpWindowImpl->mpFrameWindow == pFrameWindow )
+ {
+ pParent = pParent->GetParent();
+ }
+ pFrameWindow = pParent ? pParent->mpWindowImpl->mpFrameWindow.get() : nullptr;
+ }
+}
+void Window::DecModalCount()
+{
+ vcl::Window* pFrameWindow = mpWindowImpl->mpFrameWindow;
+ vcl::Window* pParent = pFrameWindow;
+ while( pFrameWindow )
+ {
+ pFrameWindow->mpWindowImpl->mpFrameData->mnModalMode--;
+ while( pParent && pParent->mpWindowImpl->mpFrameWindow == pFrameWindow )
+ {
+ pParent = pParent->GetParent();
+ }
+ pFrameWindow = pParent ? pParent->mpWindowImpl->mpFrameWindow.get() : nullptr;
+ }
+}
+
+void Window::ImplIsInTaskPaneList( bool mbIsInTaskList )
+{
+ mpWindowImpl->mbIsInTaskPaneList = mbIsInTaskList;
+}
+
+void Window::ImplNotifyIconifiedState( bool bIconified )
+{
+ mpWindowImpl->mpFrameWindow->CallEventListeners( bIconified ? VclEventId::WindowMinimize : VclEventId::WindowNormalize );
+ // #109206# notify client window as well to have toolkit topwindow listeners notified
+ if( mpWindowImpl->mpFrameWindow->mpWindowImpl->mpClientWindow && mpWindowImpl->mpFrameWindow != mpWindowImpl->mpFrameWindow->mpWindowImpl->mpClientWindow )
+ mpWindowImpl->mpFrameWindow->mpWindowImpl->mpClientWindow->CallEventListeners( bIconified ? VclEventId::WindowMinimize : VclEventId::WindowNormalize );
+}
+
+bool Window::HasActiveChildFrame() const
+{
+ bool bRet = false;
+ vcl::Window *pFrameWin = ImplGetSVData()->maFrameData.mpFirstFrame;
+ while( pFrameWin )
+ {
+ if( pFrameWin != mpWindowImpl->mpFrameWindow )
+ {
+ bool bDecorated = false;
+ VclPtr< vcl::Window > pChildFrame = pFrameWin->ImplGetWindow();
+ // #i15285# unfortunately WB_MOVEABLE is the same as WB_TABSTOP which can
+ // be removed for ToolBoxes to influence the keyboard accessibility
+ // thus WB_MOVEABLE is no indicator for decoration anymore
+ // but FloatingWindows carry this information in their TitleType...
+ // TODO: avoid duplicate WinBits !!!
+ if( pChildFrame && pChildFrame->ImplIsFloatingWindow() )
+ bDecorated = static_cast<FloatingWindow*>(pChildFrame.get())->GetTitleType() != FloatWinTitleType::NONE;
+ if( bDecorated || (pFrameWin->mpWindowImpl->mnStyle & (WB_MOVEABLE | WB_SIZEABLE) ) )
+ if( pChildFrame && pChildFrame->IsVisible() && pChildFrame->IsActive() )
+ {
+ if( ImplIsChild( pChildFrame, true ) )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+ pFrameWin = pFrameWin->mpWindowImpl->mpFrameData->mpNextFrame;
+ }
+ return bRet;
+}
+
+LanguageType Window::GetInputLanguage() const
+{
+ return mpWindowImpl->mpFrame->GetInputLanguage();
+}
+
+void Window::EnableNativeWidget( bool bEnable )
+{
+ static const char* pNoNWF = getenv( "SAL_NO_NWF" );
+ if( pNoNWF && *pNoNWF )
+ bEnable = false;
+
+ if( bEnable != ImplGetWinData()->mbEnableNativeWidget )
+ {
+ ImplGetWinData()->mbEnableNativeWidget = bEnable;
+
+ // send datachanged event to allow for internal changes required for NWF
+ // like clipmode, transparency, etc.
+ DataChangedEvent aDCEvt( DataChangedEventType::SETTINGS, &*GetOutDev()->moSettings, AllSettingsFlags::STYLE );
+ CompatDataChanged( aDCEvt );
+
+ // sometimes the borderwindow is queried, so keep it in sync
+ if( mpWindowImpl->mpBorderWindow )
+ mpWindowImpl->mpBorderWindow->ImplGetWinData()->mbEnableNativeWidget = bEnable;
+ }
+
+ // push down, useful for compound controls
+ VclPtr< vcl::Window > pChild = mpWindowImpl->mpFirstChild;
+ while( pChild )
+ {
+ pChild->EnableNativeWidget( bEnable );
+ pChild = pChild->mpWindowImpl->mpNext;
+ }
+}
+
+bool Window::IsNativeWidgetEnabled() const
+{
+ return mpWindowImpl && ImplGetWinData()->mbEnableNativeWidget;
+}
+
+Reference< css::rendering::XCanvas > WindowOutputDevice::ImplGetCanvas( bool bSpriteCanvas ) const
+{
+ // Feed any with operating system's window handle
+
+ // common: first any is VCL pointer to window (for VCL canvas)
+ Sequence< Any > aArg{
+ Any(reinterpret_cast<sal_Int64>(this)),
+ Any(css::awt::Rectangle( mnOutOffX, mnOutOffY, mnOutWidth, mnOutHeight )),
+ Any(mxOwnerWindow->mpWindowImpl->mbAlwaysOnTop),
+ Any(Reference< css::awt::XWindow >(
+ mxOwnerWindow->GetComponentInterface(),
+ UNO_QUERY )),
+ GetSystemGfxDataAny()
+ };
+
+ Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
+
+ // Create canvas instance with window handle
+
+ static vcl::DeleteUnoReferenceOnDeinit<XMultiComponentFactory> xStaticCanvasFactory(
+ css::rendering::CanvasFactory::create( xContext ) );
+ Reference<XMultiComponentFactory> xCanvasFactory(xStaticCanvasFactory.get());
+ Reference< css::rendering::XCanvas > xCanvas;
+
+ if(xCanvasFactory.is())
+ {
+#ifdef _WIN32
+ // see #140456# - if we're running on a multiscreen setup,
+ // request special, multi-screen safe sprite canvas
+ // implementation (not DX5 canvas, as it cannot cope with
+ // surfaces spanning multiple displays). Note: canvas
+ // (without sprite) stays the same)
+ const sal_uInt32 nDisplay = static_cast< WinSalFrame* >( mxOwnerWindow->mpWindowImpl->mpFrame )->mnDisplay;
+ if( nDisplay >= Application::GetScreenCount() )
+ {
+ xCanvas.set( xCanvasFactory->createInstanceWithArgumentsAndContext(
+ bSpriteCanvas ?
+ OUString( "com.sun.star.rendering.SpriteCanvas.MultiScreen" ) :
+ OUString( "com.sun.star.rendering.Canvas.MultiScreen" ),
+ aArg,
+ xContext ),
+ UNO_QUERY );
+
+ }
+ else
+#endif
+ {
+ xCanvas.set( xCanvasFactory->createInstanceWithArgumentsAndContext(
+ bSpriteCanvas ?
+ OUString( "com.sun.star.rendering.SpriteCanvas" ) :
+ OUString( "com.sun.star.rendering.Canvas" ),
+ aArg,
+ xContext ),
+ UNO_QUERY );
+
+ }
+ }
+
+ // no factory??? Empty reference, then.
+ return xCanvas;
+}
+
+OUString Window::GetSurroundingText() const
+{
+ return OUString();
+}
+
+Selection Window::GetSurroundingTextSelection() const
+{
+ return Selection( 0, 0 );
+}
+
+namespace
+{
+ using namespace com::sun::star;
+
+ uno::Reference<accessibility::XAccessibleEditableText> lcl_GetxText(vcl::Window *pFocusWin)
+ {
+ uno::Reference<accessibility::XAccessibleEditableText> xText;
+ try
+ {
+ uno::Reference< accessibility::XAccessible > xAccessible( pFocusWin->GetAccessible() );
+ if (xAccessible.is())
+ xText = FindFocusedEditableText(xAccessible->getAccessibleContext());
+ }
+ catch(const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "vcl.gtk3", "Exception in getting input method surrounding text");
+ }
+ return xText;
+ }
+}
+
+// this is a rubbish implementation using a11y, ideally all subclasses implementing
+// GetSurroundingText/GetSurroundingTextSelection should implement this and then this
+// should be removed in favor of a stub that returns false
+bool Window::DeleteSurroundingText(const Selection& rSelection)
+{
+ uno::Reference<accessibility::XAccessibleEditableText> xText = lcl_GetxText(this);
+ if (xText.is())
+ {
+ sal_Int32 nPosition = xText->getCaretPosition();
+ // #i111768# range checking
+ sal_Int32 nDeletePos = rSelection.Min();
+ sal_Int32 nDeleteEnd = rSelection.Max();
+ if (nDeletePos < 0)
+ nDeletePos = 0;
+ if (nDeleteEnd < 0)
+ nDeleteEnd = 0;
+ if (nDeleteEnd > xText->getCharacterCount())
+ nDeleteEnd = xText->getCharacterCount();
+
+ xText->deleteText(nDeletePos, nDeleteEnd);
+ //tdf91641 adjust cursor if deleted chars shift it forward (normal case)
+ if (nDeletePos < nPosition)
+ {
+ if (nDeleteEnd <= nPosition)
+ nPosition = nPosition - (nDeleteEnd - nDeletePos);
+ else
+ nPosition = nDeletePos;
+
+ if (xText->getCharacterCount() >= nPosition)
+ xText->setCaretPosition( nPosition );
+ }
+ return true;
+ }
+
+ return false;
+}
+
+bool WindowOutputDevice::UsePolyPolygonForComplexGradient()
+{
+ return meRasterOp != RasterOp::OverPaint;
+}
+
+void Window::ApplySettings(vcl::RenderContext& /*rRenderContext*/)
+{
+}
+
+const SystemEnvData* Window::GetSystemData() const
+{
+
+ return mpWindowImpl->mpFrame ? mpWindowImpl->mpFrame->GetSystemData() : nullptr;
+}
+
+bool Window::SupportsDoubleBuffering() const
+{
+ return mpWindowImpl->mpFrameData->mpBuffer;
+}
+
+void Window::RequestDoubleBuffering(bool bRequest)
+{
+ if (bRequest)
+ {
+ mpWindowImpl->mpFrameData->mpBuffer = VclPtrInstance<VirtualDevice>();
+ // Make sure that the buffer size matches the frame size.
+ mpWindowImpl->mpFrameData->mpBuffer->SetOutputSizePixel(mpWindowImpl->mpFrameWindow->GetOutputSizePixel());
+ }
+ else
+ mpWindowImpl->mpFrameData->mpBuffer.reset();
+}
+
+/*
+ * The rationale here is that we moved destructors to
+ * dispose and this altered a lot of code paths, that
+ * are better left unchanged for now.
+ */
+void Window::CompatGetFocus()
+{
+ if (!mpWindowImpl || mpWindowImpl->mbInDispose)
+ Window::GetFocus();
+ else
+ GetFocus();
+}
+
+void Window::CompatLoseFocus()
+{
+ if (!mpWindowImpl || mpWindowImpl->mbInDispose)
+ Window::LoseFocus();
+ else
+ LoseFocus();
+}
+
+void Window::CompatStateChanged( StateChangedType nStateChange )
+{
+ if (!mpWindowImpl || mpWindowImpl->mbInDispose)
+ Window::StateChanged(nStateChange);
+ else
+ StateChanged(nStateChange);
+}
+
+void Window::CompatDataChanged( const DataChangedEvent& rDCEvt )
+{
+ if (!mpWindowImpl || mpWindowImpl->mbInDispose)
+ Window::DataChanged(rDCEvt);
+ else
+ DataChanged(rDCEvt);
+}
+
+bool Window::CompatPreNotify( NotifyEvent& rNEvt )
+{
+ if (!mpWindowImpl || mpWindowImpl->mbInDispose)
+ return Window::PreNotify( rNEvt );
+ else
+ return PreNotify( rNEvt );
+}
+
+bool Window::CompatNotify( NotifyEvent& rNEvt )
+{
+ if (!mpWindowImpl || mpWindowImpl->mbInDispose)
+ return Window::EventNotify( rNEvt );
+ else
+ return EventNotify( rNEvt );
+}
+
+void Window::set_id(const OUString& rID)
+{
+ mpWindowImpl->maID = rID;
+}
+
+const OUString& Window::get_id() const
+{
+ static OUString empty;
+ return mpWindowImpl ? mpWindowImpl->maID : empty;
+}
+
+FactoryFunction Window::GetUITestFactory() const
+{
+ return WindowUIObject::create;
+}
+
+WindowOutputDevice::WindowOutputDevice(vcl::Window& rOwnerWindow) :
+ ::OutputDevice(OUTDEV_WINDOW),
+ mxOwnerWindow(&rOwnerWindow)
+{
+ assert(mxOwnerWindow);
+}
+
+WindowOutputDevice::~WindowOutputDevice()
+{
+ disposeOnce();
+}
+
+void WindowOutputDevice::dispose()
+{
+ assert((!mxOwnerWindow || mxOwnerWindow->isDisposed()) && "This belongs to the associated window and must be disposed after it");
+ ::OutputDevice::dispose();
+ // need to do this after OutputDevice::dispose so that the call to WindowOutputDevice::ReleaseGraphics
+ // can release the graphics properly
+ mxOwnerWindow.clear();
+}
+
+css::awt::DeviceInfo WindowOutputDevice::GetDeviceInfo() const
+{
+ css::awt::DeviceInfo aInfo = GetCommonDeviceInfo(mxOwnerWindow->GetSizePixel());
+ mxOwnerWindow->GetBorder(aInfo.LeftInset, aInfo.TopInset, aInfo.RightInset, aInfo.BottomInset);
+ return aInfo;
+}
+
+
+} /* namespace vcl */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/window2.cxx b/vcl/source/window/window2.cxx
new file mode 100644
index 0000000000..ceaebf52be
--- /dev/null
+++ b/vcl/source/window/window2.cxx
@@ -0,0 +1,2059 @@
+/* -*- 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 <limits.h>
+
+#include <o3tl/float_int_conversion.hxx>
+#include <sal/log.hxx>
+
+#include <tools/helpers.hxx>
+
+#include <vcl/toolkit/dialog.hxx>
+#include <vcl/event.hxx>
+#include <vcl/toolkit/fixed.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/window.hxx>
+#include <vcl/scrollable.hxx>
+#include <vcl/toolkit/scrbar.hxx>
+#include <vcl/dockwin.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/builder.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <window.h>
+#include <svdata.hxx>
+#include <salgdi.hxx>
+#include <salframe.hxx>
+#include <scrwnd.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRelation.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+
+using namespace com::sun::star;
+
+namespace vcl {
+
+void Window::ShowFocus( const tools::Rectangle& rRect )
+{
+ if( mpWindowImpl->mbInShowFocus )
+ return;
+ mpWindowImpl->mbInShowFocus = true;
+
+ ImplWinData* pWinData = ImplGetWinData();
+
+ // native themeing suggest not to use focus rects
+ if( ! ( mpWindowImpl->mbUseNativeFocus &&
+ IsNativeWidgetEnabled() ) )
+ {
+ if ( !mpWindowImpl->mbInPaint )
+ {
+ if ( mpWindowImpl->mbFocusVisible )
+ {
+ if ( *pWinData->mpFocusRect == rRect )
+ {
+ mpWindowImpl->mbInShowFocus = false;
+ return;
+ }
+
+ ImplInvertFocus( *pWinData->mpFocusRect );
+ }
+
+ ImplInvertFocus( rRect );
+ }
+ pWinData->mpFocusRect = rRect;
+ mpWindowImpl->mbFocusVisible = true;
+ }
+ else
+ {
+ if( ! mpWindowImpl->mbNativeFocusVisible )
+ {
+ mpWindowImpl->mbNativeFocusVisible = true;
+ if ( !mpWindowImpl->mbInPaint )
+ Invalidate();
+ }
+ }
+ mpWindowImpl->mbInShowFocus = false;
+}
+
+void Window::HideFocus()
+{
+
+ if( mpWindowImpl->mbInHideFocus )
+ return;
+ mpWindowImpl->mbInHideFocus = true;
+
+ // native themeing can suggest not to use focus rects
+ if( ! ( mpWindowImpl->mbUseNativeFocus &&
+ IsNativeWidgetEnabled() ) )
+ {
+ if ( !mpWindowImpl->mbFocusVisible )
+ {
+ mpWindowImpl->mbInHideFocus = false;
+ return;
+ }
+
+ if ( !mpWindowImpl->mbInPaint )
+ ImplInvertFocus( *ImplGetWinData()->mpFocusRect );
+ mpWindowImpl->mbFocusVisible = false;
+ }
+ else
+ {
+ if( mpWindowImpl->mbNativeFocusVisible )
+ {
+ mpWindowImpl->mbNativeFocusVisible = false;
+ if ( !mpWindowImpl->mbInPaint )
+ Invalidate();
+ }
+ }
+ mpWindowImpl->mbInHideFocus = false;
+}
+
+void Window::ShowTracking( const tools::Rectangle& rRect, ShowTrackFlags nFlags )
+{
+ ImplWinData* pWinData = ImplGetWinData();
+
+ if ( !mpWindowImpl->mbInPaint || !(nFlags & ShowTrackFlags::TrackWindow) )
+ {
+ if ( mpWindowImpl->mbTrackVisible )
+ {
+ if ( (*pWinData->mpTrackRect == rRect) &&
+ (pWinData->mnTrackFlags == nFlags) )
+ return;
+
+ InvertTracking( *pWinData->mpTrackRect, pWinData->mnTrackFlags );
+ }
+
+ InvertTracking( rRect, nFlags );
+ }
+
+ pWinData->mpTrackRect = rRect;
+ pWinData->mnTrackFlags = nFlags;
+ mpWindowImpl->mbTrackVisible = true;
+}
+
+void Window::HideTracking()
+{
+ if ( mpWindowImpl->mbTrackVisible )
+ {
+ ImplWinData* pWinData = ImplGetWinData();
+ if ( !mpWindowImpl->mbInPaint || !(pWinData->mnTrackFlags & ShowTrackFlags::TrackWindow) )
+ InvertTracking( *pWinData->mpTrackRect, pWinData->mnTrackFlags );
+ mpWindowImpl->mbTrackVisible = false;
+ }
+}
+
+void Window::InvertTracking( const tools::Rectangle& rRect, ShowTrackFlags nFlags )
+{
+ OutputDevice *pOutDev = GetOutDev();
+ tools::Rectangle aRect( pOutDev->ImplLogicToDevicePixel( rRect ) );
+
+ if ( aRect.IsEmpty() )
+ return;
+ aRect.Normalize();
+
+ SalGraphics* pGraphics;
+
+ if ( nFlags & ShowTrackFlags::TrackWindow )
+ {
+ if ( !GetOutDev()->IsDeviceOutputNecessary() )
+ return;
+
+ // we need a graphics
+ if ( !GetOutDev()->mpGraphics )
+ {
+ if ( !pOutDev->AcquireGraphics() )
+ return;
+ }
+
+ if ( GetOutDev()->mbInitClipRegion )
+ GetOutDev()->InitClipRegion();
+
+ if ( GetOutDev()->mbOutputClipped )
+ return;
+
+ pGraphics = GetOutDev()->mpGraphics;
+ }
+ else
+ {
+ pGraphics = ImplGetFrameGraphics();
+
+ if ( nFlags & ShowTrackFlags::Clip )
+ {
+ vcl::Region aRegion( GetOutputRectPixel() );
+ ImplClipBoundaries( aRegion, false, false );
+ pOutDev->SelectClipRegion( aRegion, pGraphics );
+ }
+ }
+
+ ShowTrackFlags nStyle = nFlags & ShowTrackFlags::StyleMask;
+ if ( nStyle == ShowTrackFlags::Object )
+ pGraphics->Invert( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), SalInvert::TrackFrame, *GetOutDev() );
+ else if ( nStyle == ShowTrackFlags::Split )
+ pGraphics->Invert( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight(), SalInvert::N50, *GetOutDev() );
+ else
+ {
+ tools::Long nBorder = 1;
+ if ( nStyle == ShowTrackFlags::Big )
+ nBorder = 5;
+ pGraphics->Invert( aRect.Left(), aRect.Top(), aRect.GetWidth(), nBorder, SalInvert::N50, *GetOutDev() );
+ pGraphics->Invert( aRect.Left(), aRect.Bottom()-nBorder+1, aRect.GetWidth(), nBorder, SalInvert::N50, *GetOutDev() );
+ pGraphics->Invert( aRect.Left(), aRect.Top()+nBorder, nBorder, aRect.GetHeight()-(nBorder*2), SalInvert::N50, *GetOutDev() );
+ pGraphics->Invert( aRect.Right()-nBorder+1, aRect.Top()+nBorder, nBorder, aRect.GetHeight()-(nBorder*2), SalInvert::N50, *GetOutDev() );
+ }
+}
+
+IMPL_LINK( Window, ImplTrackTimerHdl, Timer*, pTimer, void )
+{
+ if (!mpWindowImpl)
+ {
+ SAL_WARN("vcl", "ImplTrackTimerHdl has outlived dispose");
+ return;
+ }
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // if Button-Repeat we have to change the timeout
+ if ( pSVData->mpWinData->mnTrackFlags & StartTrackingFlags::ButtonRepeat )
+ pTimer->SetTimeout( GetSettings().GetMouseSettings().GetButtonRepeat() );
+
+ // create Tracking-Event
+ Point aMousePos( mpWindowImpl->mpFrameData->mnLastMouseX, mpWindowImpl->mpFrameData->mnLastMouseY );
+ if( GetOutDev()->ImplIsAntiparallel() )
+ {
+ // re-mirror frame pos at pChild
+ const OutputDevice *pOutDev = GetOutDev();
+ pOutDev->ReMirror( aMousePos );
+ }
+ MouseEvent aMEvt( ScreenToOutputPixel( aMousePos ),
+ mpWindowImpl->mpFrameData->mnClickCount, MouseEventModifiers::NONE,
+ mpWindowImpl->mpFrameData->mnMouseCode,
+ mpWindowImpl->mpFrameData->mnMouseCode );
+ TrackingEvent aTEvt( aMEvt, TrackingEventFlags::Repeat );
+ Tracking( aTEvt );
+}
+
+void Window::SetUseFrameData(bool bUseFrameData)
+{
+ if (mpWindowImpl)
+ mpWindowImpl->mbUseFrameData = bUseFrameData;
+}
+
+void Window::StartTracking( StartTrackingFlags nFlags )
+{
+ if (!mpWindowImpl)
+ return;
+
+ ImplSVData* pSVData = ImplGetSVData();
+ VclPtr<vcl::Window> pTrackWin = mpWindowImpl->mbUseFrameData ?
+ mpWindowImpl->mpFrameData->mpTrackWin :
+ pSVData->mpWinData->mpTrackWin;
+
+ if ( pTrackWin.get() != this )
+ {
+ if ( pTrackWin )
+ pTrackWin->EndTracking( TrackingEventFlags::Cancel );
+ }
+
+ SAL_WARN_IF(pSVData->mpWinData->mpTrackTimer, "vcl", "StartTracking called while TrackerTimer still running");
+
+ if ( !mpWindowImpl->mbUseFrameData &&
+ (nFlags & (StartTrackingFlags::ScrollRepeat | StartTrackingFlags::ButtonRepeat)) )
+ {
+ pSVData->mpWinData->mpTrackTimer.reset(new AutoTimer("vcl::Window pSVData->mpWinData->mpTrackTimer"));
+
+ if ( nFlags & StartTrackingFlags::ScrollRepeat )
+ pSVData->mpWinData->mpTrackTimer->SetTimeout( MouseSettings::GetScrollRepeat() );
+ else
+ pSVData->mpWinData->mpTrackTimer->SetTimeout( MouseSettings::GetButtonStartRepeat() );
+ pSVData->mpWinData->mpTrackTimer->SetInvokeHandler( LINK( this, Window, ImplTrackTimerHdl ) );
+ pSVData->mpWinData->mpTrackTimer->Start();
+ }
+
+ if (mpWindowImpl->mbUseFrameData)
+ {
+ mpWindowImpl->mpFrameData->mpTrackWin = this;
+ }
+ else
+ {
+ pSVData->mpWinData->mpTrackWin = this;
+ pSVData->mpWinData->mnTrackFlags = nFlags;
+ CaptureMouse();
+ }
+}
+
+void Window::EndTracking( TrackingEventFlags nFlags )
+{
+ if (!mpWindowImpl)
+ return;
+
+ ImplSVData* pSVData = ImplGetSVData();
+ VclPtr<vcl::Window> pTrackWin = mpWindowImpl->mbUseFrameData ?
+ mpWindowImpl->mpFrameData->mpTrackWin :
+ pSVData->mpWinData->mpTrackWin;
+
+ if ( pTrackWin.get() != this )
+ return;
+
+ if ( !mpWindowImpl->mbUseFrameData && pSVData->mpWinData->mpTrackTimer )
+ pSVData->mpWinData->mpTrackTimer.reset();
+
+ mpWindowImpl->mpFrameData->mpTrackWin = pSVData->mpWinData->mpTrackWin = nullptr;
+ pSVData->mpWinData->mnTrackFlags = StartTrackingFlags::NONE;
+ ReleaseMouse();
+
+ // call EndTracking if required
+ if (mpWindowImpl->mpFrameData)
+ {
+ Point aMousePos( mpWindowImpl->mpFrameData->mnLastMouseX, mpWindowImpl->mpFrameData->mnLastMouseY );
+ if( GetOutDev()->ImplIsAntiparallel() )
+ {
+ // re-mirror frame pos at pChild
+ const OutputDevice *pOutDev = GetOutDev();
+ pOutDev->ReMirror( aMousePos );
+ }
+
+ MouseEvent aMEvt( ScreenToOutputPixel( aMousePos ),
+ mpWindowImpl->mpFrameData->mnClickCount, MouseEventModifiers::NONE,
+ mpWindowImpl->mpFrameData->mnMouseCode,
+ mpWindowImpl->mpFrameData->mnMouseCode );
+ TrackingEvent aTEvt( aMEvt, nFlags | TrackingEventFlags::End );
+ // CompatTracking effectively
+ if (!mpWindowImpl || mpWindowImpl->mbInDispose)
+ return Window::Tracking( aTEvt );
+ else
+ return Tracking( aTEvt );
+ }
+}
+
+bool Window::IsTracking() const
+{
+ if (!mpWindowImpl)
+ return false;
+ if (mpWindowImpl->mbUseFrameData && mpWindowImpl->mpFrameData)
+ {
+ return mpWindowImpl->mpFrameData->mpTrackWin == this;
+ }
+ if (!mpWindowImpl->mbUseFrameData && ImplGetSVData()->mpWinData)
+ {
+ return ImplGetSVData()->mpWinData->mpTrackWin == this;
+ }
+ return false;
+}
+
+void Window::StartAutoScroll( StartAutoScrollFlags nFlags )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ if ( pSVData->mpWinData->mpAutoScrollWin.get() != this )
+ {
+ if ( pSVData->mpWinData->mpAutoScrollWin )
+ pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll();
+ }
+
+ pSVData->mpWinData->mpAutoScrollWin = this;
+ pSVData->mpWinData->mnAutoScrollFlags = nFlags;
+ pSVData->maAppData.mpWheelWindow = VclPtr<ImplWheelWindow>::Create( this );
+}
+
+void Window::EndAutoScroll()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ if ( pSVData->mpWinData->mpAutoScrollWin.get() == this )
+ {
+ pSVData->mpWinData->mpAutoScrollWin = nullptr;
+ pSVData->mpWinData->mnAutoScrollFlags = StartAutoScrollFlags::NONE;
+ pSVData->maAppData.mpWheelWindow->ImplStop();
+ pSVData->maAppData.mpWheelWindow.disposeAndClear();
+ }
+}
+
+VclPtr<vcl::Window> Window::SaveFocus()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ if ( pSVData->mpWinData->mpFocusWin )
+ {
+ return pSVData->mpWinData->mpFocusWin;
+ }
+ else
+ return nullptr;
+}
+
+void Window::EndSaveFocus(const VclPtr<vcl::Window>& xFocusWin)
+{
+ if (xFocusWin && !xFocusWin->isDisposed())
+ {
+ xFocusWin->GrabFocus();
+ }
+}
+
+void Window::SetZoom( const Fraction& rZoom )
+{
+ if ( mpWindowImpl && mpWindowImpl->maZoom != rZoom )
+ {
+ mpWindowImpl->maZoom = rZoom;
+ CompatStateChanged( StateChangedType::Zoom );
+ }
+}
+
+void Window::SetZoomedPointFont(vcl::RenderContext& rRenderContext, const vcl::Font& rFont)
+{
+ const Fraction& rZoom = GetZoom();
+ if (rZoom.GetNumerator() != rZoom.GetDenominator())
+ {
+ vcl::Font aFont(rFont);
+ Size aSize = aFont.GetFontSize();
+ aSize.setWidth( FRound(double(aSize.Width() * rZoom)) );
+ aSize.setHeight( FRound(double(aSize.Height() * rZoom)) );
+ aFont.SetFontSize(aSize);
+ SetPointFont(rRenderContext, aFont);
+ }
+ else
+ {
+ SetPointFont(rRenderContext, rFont);
+ }
+}
+
+tools::Long Window::CalcZoom( tools::Long nCalc ) const
+{
+
+ const Fraction& rZoom = GetZoom();
+ if ( rZoom.GetNumerator() != rZoom.GetDenominator() )
+ {
+ double n = double(nCalc * rZoom);
+ nCalc = FRound( n );
+ }
+ return nCalc;
+}
+
+void Window::SetControlFont()
+{
+ if (mpWindowImpl && mpWindowImpl->mpControlFont)
+ {
+ mpWindowImpl->mpControlFont.reset();
+ CompatStateChanged(StateChangedType::ControlFont);
+ }
+}
+
+void Window::SetControlFont(const vcl::Font& rFont)
+{
+ if (rFont == vcl::Font())
+ {
+ SetControlFont();
+ return;
+ }
+
+ if (mpWindowImpl->mpControlFont)
+ {
+ if (*mpWindowImpl->mpControlFont == rFont)
+ return;
+ *mpWindowImpl->mpControlFont = rFont;
+ }
+ else
+ mpWindowImpl->mpControlFont = rFont;
+
+ CompatStateChanged(StateChangedType::ControlFont);
+}
+
+vcl::Font Window::GetControlFont() const
+{
+ if (mpWindowImpl->mpControlFont)
+ return *mpWindowImpl->mpControlFont;
+ else
+ {
+ vcl::Font aFont;
+ return aFont;
+ }
+}
+
+void Window::ApplyControlFont(vcl::RenderContext& rRenderContext, const vcl::Font& rFont)
+{
+ vcl::Font aFont(rFont);
+ if (IsControlFont())
+ aFont.Merge(GetControlFont());
+ SetZoomedPointFont(rRenderContext, aFont);
+}
+
+void Window::SetControlForeground()
+{
+ if (mpWindowImpl->mbControlForeground)
+ {
+ mpWindowImpl->maControlForeground = COL_TRANSPARENT;
+ mpWindowImpl->mbControlForeground = false;
+ CompatStateChanged(StateChangedType::ControlForeground);
+ }
+}
+
+void Window::SetControlForeground(const Color& rColor)
+{
+ if (rColor.IsTransparent())
+ {
+ if (mpWindowImpl->mbControlForeground)
+ {
+ mpWindowImpl->maControlForeground = COL_TRANSPARENT;
+ mpWindowImpl->mbControlForeground = false;
+ CompatStateChanged(StateChangedType::ControlForeground);
+ }
+ }
+ else
+ {
+ if (mpWindowImpl->maControlForeground != rColor)
+ {
+ mpWindowImpl->maControlForeground = rColor;
+ mpWindowImpl->mbControlForeground = true;
+ CompatStateChanged(StateChangedType::ControlForeground);
+ }
+ }
+}
+
+void Window::ApplyControlForeground(vcl::RenderContext& rRenderContext, const Color& rDefaultColor)
+{
+ Color aTextColor(rDefaultColor);
+ if (IsControlForeground())
+ aTextColor = GetControlForeground();
+ rRenderContext.SetTextColor(aTextColor);
+}
+
+void Window::SetControlBackground()
+{
+ if (mpWindowImpl->mbControlBackground)
+ {
+ mpWindowImpl->maControlBackground = COL_TRANSPARENT;
+ mpWindowImpl->mbControlBackground = false;
+ CompatStateChanged(StateChangedType::ControlBackground);
+ }
+}
+
+void Window::SetControlBackground(const Color& rColor)
+{
+ if (rColor.IsTransparent())
+ {
+ if (mpWindowImpl->mbControlBackground)
+ {
+ mpWindowImpl->maControlBackground = COL_TRANSPARENT;
+ mpWindowImpl->mbControlBackground = false;
+ CompatStateChanged(StateChangedType::ControlBackground);
+ }
+ }
+ else
+ {
+ if (mpWindowImpl->maControlBackground != rColor)
+ {
+ mpWindowImpl->maControlBackground = rColor;
+ mpWindowImpl->mbControlBackground = true;
+ CompatStateChanged(StateChangedType::ControlBackground);
+ }
+ }
+}
+
+void Window::ApplyControlBackground(vcl::RenderContext& rRenderContext, const Color& rDefaultColor)
+{
+ Color aColor(rDefaultColor);
+ if (IsControlBackground())
+ aColor = GetControlBackground();
+ rRenderContext.SetBackground(aColor);
+}
+
+Size Window::CalcWindowSize( const Size& rOutSz ) const
+{
+ Size aSz = rOutSz;
+ aSz.AdjustWidth(mpWindowImpl->mnLeftBorder+mpWindowImpl->mnRightBorder );
+ aSz.AdjustHeight(mpWindowImpl->mnTopBorder+mpWindowImpl->mnBottomBorder );
+ return aSz;
+}
+
+Size Window::CalcOutputSize( const Size& rWinSz ) const
+{
+ Size aSz = rWinSz;
+ aSz.AdjustWidth( -(mpWindowImpl->mnLeftBorder+mpWindowImpl->mnRightBorder) );
+ aSz.AdjustHeight( -(mpWindowImpl->mnTopBorder+mpWindowImpl->mnBottomBorder) );
+ return aSz;
+}
+
+vcl::Font Window::GetDrawPixelFont(OutputDevice const * pDev) const
+{
+ vcl::Font aFont = GetPointFont(*GetOutDev());
+ Size aFontSize = aFont.GetFontSize();
+ MapMode aPtMapMode(MapUnit::MapPoint);
+ aFontSize = pDev->LogicToPixel( aFontSize, aPtMapMode );
+ aFont.SetFontSize( aFontSize );
+ return aFont;
+}
+
+tools::Long Window::GetDrawPixel( OutputDevice const * pDev, tools::Long nPixels ) const
+{
+ tools::Long nP = nPixels;
+ if ( pDev->GetOutDevType() != OUTDEV_WINDOW )
+ {
+ MapMode aMap( MapUnit::Map100thMM );
+ Size aSz( nP, 0 );
+ aSz = PixelToLogic( aSz, aMap );
+ aSz = pDev->LogicToPixel( aSz, aMap );
+ nP = aSz.Width();
+ }
+ return nP;
+}
+
+// returns how much was actually scrolled (so that abs(retval) <= abs(nN))
+static double lcl_HandleScrollHelper( Scrollable* pScrl, double nN, bool isMultiplyByLineSize )
+{
+ if (!pScrl || !nN || pScrl->Inactive())
+ return 0.0;
+
+ tools::Long nNewPos = pScrl->GetThumbPos();
+ double scrolled = nN;
+
+ if ( nN == double(-LONG_MAX) )
+ nNewPos += pScrl->GetPageSize();
+ else if ( nN == double(LONG_MAX) )
+ nNewPos -= pScrl->GetPageSize();
+ else
+ {
+ // allowing both chunked and continuous scrolling
+ if(isMultiplyByLineSize){
+ nN*=pScrl->GetLineSize();
+ }
+
+ // compute how many quantized units to scroll
+ tools::Long magnitude = o3tl::saturating_cast<tools::Long>(abs(nN));
+ tools::Long change = copysign(magnitude, nN);
+
+ nNewPos = nNewPos - change;
+
+ scrolled = double(change);
+ // convert back to chunked/continuous
+ if(isMultiplyByLineSize){
+ scrolled /= pScrl->GetLineSize();
+ }
+ }
+
+ pScrl->DoScroll( nNewPos );
+
+ return scrolled;
+}
+
+bool Window::HandleScrollCommand( const CommandEvent& rCmd,
+ Scrollable* pHScrl, Scrollable* pVScrl )
+{
+ bool bRet = false;
+
+ if ( pHScrl || pVScrl )
+ {
+ switch( rCmd.GetCommand() )
+ {
+ case CommandEventId::StartAutoScroll:
+ {
+ StartAutoScrollFlags nFlags = StartAutoScrollFlags::NONE;
+ if ( pHScrl )
+ {
+ if ( (pHScrl->GetVisibleSize() < pHScrl->GetRangeMax()) &&
+ !pHScrl->Inactive() )
+ nFlags |= StartAutoScrollFlags::Horz;
+ }
+ if ( pVScrl )
+ {
+ if ( (pVScrl->GetVisibleSize() < pVScrl->GetRangeMax()) &&
+ !pVScrl->Inactive() )
+ nFlags |= StartAutoScrollFlags::Vert;
+ }
+
+ if ( nFlags != StartAutoScrollFlags::NONE )
+ {
+ StartAutoScroll( nFlags );
+ bRet = true;
+ }
+ }
+ break;
+
+ case CommandEventId::Wheel:
+ {
+ const CommandWheelData* pData = rCmd.GetWheelData();
+
+ if ( pData && (CommandWheelMode::SCROLL == pData->GetMode()) )
+ {
+ if (!pData->IsDeltaPixel())
+ {
+ double nScrollLines = pData->GetScrollLines();
+ double nLines;
+ double* partialScroll = pData->IsHorz()
+ ? &mpWindowImpl->mfPartialScrollX
+ : &mpWindowImpl->mfPartialScrollY;
+ if ( nScrollLines == COMMAND_WHEEL_PAGESCROLL )
+ {
+ if ( pData->GetDelta() < 0 )
+ nLines = double(-LONG_MAX);
+ else
+ nLines = double(LONG_MAX);
+ }
+ else
+ nLines = *partialScroll + pData->GetNotchDelta() * nScrollLines;
+ if ( nLines )
+ {
+ Scrollable* pScrl = pData->IsHorz() ? pHScrl : pVScrl;
+ double scrolled = lcl_HandleScrollHelper( pScrl, nLines, true );
+ *partialScroll = nLines - scrolled;
+ bRet = true;
+ }
+ }
+ else
+ {
+ // Mobile / touch scrolling section
+ const Point & deltaPoint = rCmd.GetMousePosPixel();
+
+ double deltaXInPixels = double(deltaPoint.X());
+ double deltaYInPixels = double(deltaPoint.Y());
+ Size winSize = GetOutputSizePixel();
+
+ if(pHScrl)
+ {
+ double visSizeX = double(pHScrl->GetVisibleSize());
+ double ratioX = deltaXInPixels / double(winSize.getWidth());
+ tools::Long deltaXInLogic = tools::Long(visSizeX * ratioX);
+ // Touch need to work by pixels. Did not apply this to
+ // Android, as android code may require adaptations
+ // to work with this scrolling code
+#ifndef IOS
+ tools::Long lineSizeX = pHScrl->GetLineSize();
+
+ if(lineSizeX)
+ {
+ deltaXInLogic /= lineSizeX;
+ }
+ else
+ {
+ deltaXInLogic = 0;
+ }
+#endif
+ if ( deltaXInLogic)
+ {
+#ifndef IOS
+ bool const isMultiplyByLineSize = true;
+#else
+ bool const isMultiplyByLineSize = false;
+#endif
+ lcl_HandleScrollHelper( pHScrl, deltaXInLogic, isMultiplyByLineSize );
+ bRet = true;
+ }
+ }
+ if(pVScrl)
+ {
+ double visSizeY = double(pVScrl->GetVisibleSize());
+ double ratioY = deltaYInPixels / double(winSize.getHeight());
+ tools::Long deltaYInLogic = tools::Long(visSizeY * ratioY);
+
+ // Touch need to work by pixels. Did not apply this to
+ // Android, as android code may require adaptations
+ // to work with this scrolling code
+#ifndef IOS
+ tools::Long lineSizeY = pVScrl->GetLineSize();
+ if(lineSizeY)
+ {
+ deltaYInLogic /= lineSizeY;
+ }
+ else
+ {
+ deltaYInLogic = 0;
+ }
+#endif
+ if ( deltaYInLogic )
+ {
+#ifndef IOS
+ bool const isMultiplyByLineSize = true;
+#else
+ bool const isMultiplyByLineSize = false;
+#endif
+ lcl_HandleScrollHelper( pVScrl, deltaYInLogic, isMultiplyByLineSize );
+
+ bRet = true;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case CommandEventId::GesturePan:
+ {
+ if (pVScrl)
+ {
+ const CommandGesturePanData* pData = rCmd.GetGesturePanData();
+ if (pData->meEventType == GestureEventPanType::Begin)
+ {
+ mpWindowImpl->mpFrameData->mnTouchPanPosition = pVScrl->GetThumbPos();
+ }
+ else if(pData->meEventType == GestureEventPanType::Update)
+ {
+ tools::Long nOriginalPosition = mpWindowImpl->mpFrameData->mnTouchPanPosition;
+ pVScrl->DoScroll(nOriginalPosition + (pData->mfOffset / pVScrl->GetVisibleSize()));
+ }
+ if (pData->meEventType == GestureEventPanType::End)
+ {
+ mpWindowImpl->mpFrameData->mnTouchPanPosition = -1;
+ }
+ bRet = true;
+ }
+ break;
+ }
+
+ case CommandEventId::AutoScroll:
+ {
+ const CommandScrollData* pData = rCmd.GetAutoScrollData();
+ if ( pData && (pData->GetDeltaX() || pData->GetDeltaY()) )
+ {
+ ImplHandleScroll( pHScrl, pData->GetDeltaX(),
+ pVScrl, pData->GetDeltaY() );
+ bRet = true;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+void Window::ImplHandleScroll( Scrollable* pHScrl, double nX,
+ Scrollable* pVScrl, double nY )
+{
+ lcl_HandleScrollHelper( pHScrl, nX, true );
+ lcl_HandleScrollHelper( pVScrl, nY, true );
+}
+
+DockingManager* Window::GetDockingManager()
+{
+ return ImplGetDockingManager();
+}
+
+void Window::EnableDocking( bool bEnable )
+{
+ // update list of dockable windows
+ if( bEnable )
+ ImplGetDockingManager()->AddWindow( this );
+ else
+ ImplGetDockingManager()->RemoveWindow( this );
+}
+
+// retrieves the list of owner draw decorated windows for this window hierarchy
+::std::vector<VclPtr<vcl::Window> >& Window::ImplGetOwnerDrawList()
+{
+ return ImplGetTopmostFrameWindow()->mpWindowImpl->mpFrameData->maOwnerDrawList;
+}
+
+void Window::SetHelpId( const OUString& rHelpId )
+{
+ mpWindowImpl->maHelpId = rHelpId;
+}
+
+const OUString& Window::GetHelpId() const
+{
+ return mpWindowImpl->maHelpId;
+}
+
+// --------- old inline methods ---------------
+
+vcl::Window* Window::ImplGetWindow() const
+{
+ if ( mpWindowImpl->mpClientWindow )
+ return mpWindowImpl->mpClientWindow;
+ else
+ return const_cast<vcl::Window*>(this);
+}
+
+ImplFrameData* Window::ImplGetFrameData()
+{
+ return mpWindowImpl ? mpWindowImpl->mpFrameData : nullptr;
+}
+
+SalFrame* Window::ImplGetFrame() const
+{
+ return mpWindowImpl ? mpWindowImpl->mpFrame : nullptr;
+}
+
+weld::Window* Window::GetFrameWeld() const
+{
+ SalFrame* pFrame = ImplGetFrame();
+ return pFrame ? pFrame->GetFrameWeld() : nullptr;
+}
+
+vcl::Window* Window::GetFrameWindow() const
+{
+ SalFrame* pFrame = ImplGetFrame();
+ return pFrame ? pFrame->GetWindow() : nullptr;
+}
+
+vcl::Window* Window::ImplGetParent() const
+{
+ return mpWindowImpl ? mpWindowImpl->mpParent.get() : nullptr;
+}
+
+vcl::Window* Window::ImplGetClientWindow() const
+{
+ return mpWindowImpl ? mpWindowImpl->mpClientWindow.get() : nullptr;
+}
+
+vcl::Window* Window::ImplGetBorderWindow() const
+{
+ return mpWindowImpl ? mpWindowImpl->mpBorderWindow.get() : nullptr;
+}
+
+vcl::Window* Window::ImplGetFirstOverlapWindow()
+{
+ if (!mpWindowImpl)
+ {
+ return nullptr;
+ }
+
+ if ( mpWindowImpl->mbOverlapWin )
+ return this;
+ else
+ return mpWindowImpl->mpOverlapWindow;
+}
+
+const vcl::Window* Window::ImplGetFirstOverlapWindow() const
+{
+ if (!mpWindowImpl)
+ {
+ return nullptr;
+ }
+
+ if ( mpWindowImpl->mbOverlapWin )
+ return this;
+ else
+ return mpWindowImpl->mpOverlapWindow;
+}
+
+vcl::Window* Window::ImplGetFrameWindow() const
+{
+ return mpWindowImpl ? mpWindowImpl->mpFrameWindow.get() : nullptr;
+}
+
+bool Window::IsDockingWindow() const
+{
+ return mpWindowImpl && mpWindowImpl->mbDockWin;
+}
+
+bool Window::ImplIsFloatingWindow() const
+{
+ return mpWindowImpl && mpWindowImpl->mbFloatWin;
+}
+
+bool Window::ImplIsSplitter() const
+{
+ return mpWindowImpl && mpWindowImpl->mbSplitter;
+}
+
+bool Window::ImplIsPushButton() const
+{
+ return mpWindowImpl && mpWindowImpl->mbPushButton;
+}
+
+bool Window::ImplIsOverlapWindow() const
+{
+ return mpWindowImpl && mpWindowImpl->mbOverlapWin;
+}
+
+void Window::ImplSetMouseTransparent( bool bTransparent )
+{
+ if (mpWindowImpl)
+ mpWindowImpl->mbMouseTransparent = bTransparent;
+}
+
+void Window::SetCompoundControl( bool bCompound )
+{
+ if (mpWindowImpl)
+ mpWindowImpl->mbCompoundControl = bCompound;
+}
+
+WinBits Window::GetStyle() const
+{
+ return mpWindowImpl ? mpWindowImpl->mnStyle : 0;
+}
+
+WinBits Window::GetPrevStyle() const
+{
+ return mpWindowImpl ? mpWindowImpl->mnPrevStyle : 0;
+}
+
+WindowExtendedStyle Window::GetExtendedStyle() const
+{
+ return mpWindowImpl ? mpWindowImpl->mnExtendedStyle : WindowExtendedStyle::NONE;
+}
+
+void Window::SetType( WindowType nType )
+{
+ if (mpWindowImpl)
+ mpWindowImpl->mnType = nType;
+}
+
+WindowType Window::GetType() const
+{
+ if (mpWindowImpl)
+ return mpWindowImpl->mnType;
+ else
+ return WindowType::NONE;
+}
+
+Dialog* Window::GetParentDialog() const
+{
+ const vcl::Window *pWindow = this;
+
+ while( pWindow )
+ {
+ if( pWindow->IsDialog() )
+ break;
+
+ pWindow = pWindow->GetParent();
+ }
+
+ return const_cast<Dialog *>(dynamic_cast<const Dialog*>(pWindow));
+}
+
+bool Window::IsSystemWindow() const
+{
+ return mpWindowImpl && mpWindowImpl->mbSysWin;
+}
+
+bool Window::IsDialog() const
+{
+ return mpWindowImpl && mpWindowImpl->mbDialog;
+}
+
+bool Window::IsMenuFloatingWindow() const
+{
+ return mpWindowImpl && mpWindowImpl->mbMenuFloatingWindow;
+}
+
+bool Window::IsToolbarFloatingWindow() const
+{
+ return mpWindowImpl && mpWindowImpl->mbToolbarFloatingWindow;
+}
+
+bool Window::IsNativeFrame() const
+{
+ if( mpWindowImpl->mbFrame )
+ // #101741 do not check for WB_CLOSEABLE because undecorated floaters (like menus!) are closeable
+ if( mpWindowImpl->mnStyle & (WB_MOVEABLE | WB_SIZEABLE) )
+ return true;
+ else
+ return false;
+ else
+ return false;
+}
+
+void Window::EnableAllResize()
+{
+ mpWindowImpl->mbAllResize = true;
+}
+
+void Window::EnableChildTransparentMode( bool bEnable )
+{
+ mpWindowImpl->mbChildTransparent = bEnable;
+}
+
+bool Window::IsChildTransparentModeEnabled() const
+{
+ return mpWindowImpl && mpWindowImpl->mbChildTransparent;
+}
+
+bool Window::IsMouseTransparent() const
+{
+ return mpWindowImpl && mpWindowImpl->mbMouseTransparent;
+}
+
+bool Window::IsPaintTransparent() const
+{
+ return mpWindowImpl && mpWindowImpl->mbPaintTransparent;
+}
+
+void Window::SetDialogControlStart( bool bStart )
+{
+ mpWindowImpl->mbDlgCtrlStart = bStart;
+}
+
+bool Window::IsDialogControlStart() const
+{
+ return mpWindowImpl && mpWindowImpl->mbDlgCtrlStart;
+}
+
+void Window::SetDialogControlFlags( DialogControlFlags nFlags )
+{
+ mpWindowImpl->mnDlgCtrlFlags = nFlags;
+}
+
+DialogControlFlags Window::GetDialogControlFlags() const
+{
+ return mpWindowImpl->mnDlgCtrlFlags;
+}
+
+const InputContext& Window::GetInputContext() const
+{
+ return mpWindowImpl->maInputContext;
+}
+
+bool Window::IsControlFont() const
+{
+ return bool(mpWindowImpl->mpControlFont);
+}
+
+const Color& Window::GetControlForeground() const
+{
+ return mpWindowImpl->maControlForeground;
+}
+
+bool Window::IsControlForeground() const
+{
+ return mpWindowImpl->mbControlForeground;
+}
+
+const Color& Window::GetControlBackground() const
+{
+ return mpWindowImpl->maControlBackground;
+}
+
+bool Window::IsControlBackground() const
+{
+ return mpWindowImpl->mbControlBackground;
+}
+
+bool Window::IsInPaint() const
+{
+ return mpWindowImpl && mpWindowImpl->mbInPaint;
+}
+
+vcl::Window* Window::GetParent() const
+{
+ return mpWindowImpl ? mpWindowImpl->mpRealParent.get() : nullptr;
+}
+
+bool Window::IsVisible() const
+{
+ return mpWindowImpl && mpWindowImpl->mbVisible;
+}
+
+bool Window::IsReallyVisible() const
+{
+ return mpWindowImpl && mpWindowImpl->mbReallyVisible;
+}
+
+bool Window::IsReallyShown() const
+{
+ return mpWindowImpl && mpWindowImpl->mbReallyShown;
+}
+
+bool Window::IsInInitShow() const
+{
+ return mpWindowImpl->mbInInitShow;
+}
+
+bool Window::IsEnabled() const
+{
+ return mpWindowImpl && !mpWindowImpl->mbDisabled;
+}
+
+bool Window::IsInputEnabled() const
+{
+ return mpWindowImpl && !mpWindowImpl->mbInputDisabled;
+}
+
+bool Window::IsAlwaysEnableInput() const
+{
+ return mpWindowImpl->meAlwaysInputMode == AlwaysInputEnabled;
+}
+
+ActivateModeFlags Window::GetActivateMode() const
+{
+ return mpWindowImpl->mnActivateMode;
+
+}
+
+bool Window::IsAlwaysOnTopEnabled() const
+{
+ return mpWindowImpl->mbAlwaysOnTop;
+}
+
+bool Window::IsDefaultPos() const
+{
+ return mpWindowImpl->mbDefPos;
+}
+
+bool Window::IsDefaultSize() const
+{
+ return mpWindowImpl->mbDefSize;
+}
+
+Point Window::GetOffsetPixelFrom(const vcl::Window& rWindow) const
+{
+ return Point(GetOutOffXPixel() - rWindow.GetOutOffXPixel(), GetOutOffYPixel() - rWindow.GetOutOffYPixel());
+}
+
+void Window::EnablePaint( bool bEnable )
+{
+ mpWindowImpl->mbPaintDisabled = !bEnable;
+}
+
+bool Window::IsPaintEnabled() const
+{
+ return !mpWindowImpl->mbPaintDisabled;
+}
+
+bool Window::IsUpdateMode() const
+{
+ return !mpWindowImpl->mbNoUpdate;
+}
+
+void Window::SetParentUpdateMode( bool bUpdate )
+{
+ mpWindowImpl->mbNoParentUpdate = !bUpdate;
+}
+
+bool Window::IsActive() const
+{
+ return mpWindowImpl->mbActive;
+}
+
+GetFocusFlags Window::GetGetFocusFlags() const
+{
+ return mpWindowImpl->mnGetFocusFlags;
+}
+
+bool Window::IsCompoundControl() const
+{
+ return mpWindowImpl && mpWindowImpl->mbCompoundControl;
+}
+
+bool Window::IsWait() const
+{
+ return (mpWindowImpl->mnWaitCount != 0);
+}
+
+vcl::Cursor* Window::GetCursor() const
+{
+ if (!mpWindowImpl)
+ return nullptr;
+ return mpWindowImpl->mpCursor;
+}
+
+const Fraction& Window::GetZoom() const
+{
+ return mpWindowImpl->maZoom;
+}
+
+bool Window::IsZoom() const
+{
+ return mpWindowImpl->maZoom.GetNumerator() != mpWindowImpl->maZoom.GetDenominator();
+}
+
+void Window::SetHelpText( const OUString& rHelpText )
+{
+ mpWindowImpl->maHelpText = rHelpText;
+ mpWindowImpl->mbHelpTextDynamic = true;
+}
+
+void Window::SetQuickHelpText( const OUString& rHelpText )
+{
+ if (mpWindowImpl)
+ mpWindowImpl->maQuickHelpText = rHelpText;
+}
+
+const OUString& Window::GetQuickHelpText() const
+{
+ return mpWindowImpl->maQuickHelpText;
+}
+
+bool Window::IsCreatedWithToolkit() const
+{
+ return mpWindowImpl->mbCreatedWithToolkit;
+}
+
+void Window::SetCreatedWithToolkit( bool b )
+{
+ mpWindowImpl->mbCreatedWithToolkit = b;
+}
+
+PointerStyle Window::GetPointer() const
+{
+ return mpWindowImpl->maPointer;
+}
+
+VCLXWindow* Window::GetWindowPeer() const
+{
+ return mpWindowImpl ? mpWindowImpl->mpVCLXWindow : nullptr;
+}
+
+void Window::SetPosPixel( const Point& rNewPos )
+{
+ setPosSizePixel( rNewPos.X(), rNewPos.Y(), 0, 0, PosSizeFlags::Pos );
+}
+
+void Window::SetSizePixel( const Size& rNewSize )
+{
+ setPosSizePixel( 0, 0, rNewSize.Width(), rNewSize.Height(),
+ PosSizeFlags::Size );
+}
+
+void Window::SetPosSizePixel( const Point& rNewPos, const Size& rNewSize )
+{
+ setPosSizePixel( rNewPos.X(), rNewPos.Y(),
+ rNewSize.Width(), rNewSize.Height());
+}
+
+void Window::SetOutputSizePixel( const Size& rNewSize )
+{
+ SetSizePixel( Size( rNewSize.Width()+mpWindowImpl->mnLeftBorder+mpWindowImpl->mnRightBorder,
+ rNewSize.Height()+mpWindowImpl->mnTopBorder+mpWindowImpl->mnBottomBorder ) );
+}
+
+//When a widget wants to renegotiate layout, get toplevel parent dialog and call
+//resize on it. Mark all intermediate containers (or container-alike) widgets
+//as dirty for the size remains unchanged, but layout changed circumstances
+namespace
+{
+ bool queue_ungrouped_resize(vcl::Window const *pOrigWindow)
+ {
+ bool bSomeoneCares = false;
+
+ vcl::Window *pWindow = pOrigWindow->GetParent();
+ if (pWindow)
+ {
+ if (isContainerWindow(*pWindow))
+ {
+ bSomeoneCares = true;
+ }
+ else if (pWindow->GetType() == WindowType::TABCONTROL)
+ {
+ bSomeoneCares = true;
+ }
+ pWindow->queue_resize();
+ }
+
+ return bSomeoneCares;
+ }
+}
+
+void Window::InvalidateSizeCache()
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mnOptimalWidthCache = -1;
+ pWindowImpl->mnOptimalHeightCache = -1;
+}
+
+static bool HasParentDockingWindow(const vcl::Window* pWindow)
+{
+ while( pWindow )
+ {
+ if( pWindow->IsDockingWindow() )
+ return true;
+
+ pWindow = pWindow->GetParent();
+ }
+
+ return false;
+}
+
+void Window::queue_resize(StateChangedType eReason)
+{
+ if (isDisposed())
+ return;
+
+ bool bSomeoneCares = queue_ungrouped_resize(this);
+
+ if (eReason != StateChangedType::Visible)
+ {
+ InvalidateSizeCache();
+ }
+
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ if (pWindowImpl->m_xSizeGroup && pWindowImpl->m_xSizeGroup->get_mode() != VclSizeGroupMode::NONE)
+ {
+ std::set<VclPtr<vcl::Window> > &rWindows = pWindowImpl->m_xSizeGroup->get_widgets();
+ for (VclPtr<vcl::Window> const & pOther : rWindows)
+ {
+ if (pOther == this)
+ continue;
+ queue_ungrouped_resize(pOther);
+ }
+ }
+
+ if (bSomeoneCares && !isDisposed())
+ {
+ //fdo#57090 force a resync of the borders of the borderwindow onto this
+ //window in case they have changed
+ vcl::Window* pBorderWindow = ImplGetBorderWindow();
+ if (pBorderWindow)
+ pBorderWindow->Resize();
+ }
+ if (VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier())
+ {
+ Size aSize = GetSizePixel();
+ if (!aSize.IsEmpty() && !pParent->IsInInitShow()
+ && (GetParentDialog() || HasParentDockingWindow(this)))
+ LogicInvalidate(nullptr);
+ }
+}
+
+namespace
+{
+ VclAlign toAlign(std::u16string_view rValue)
+ {
+ VclAlign eRet = VclAlign::Fill;
+
+ if (rValue == u"fill")
+ eRet = VclAlign::Fill;
+ else if (rValue == u"start")
+ eRet = VclAlign::Start;
+ else if (rValue == u"end")
+ eRet = VclAlign::End;
+ else if (rValue == u"center")
+ eRet = VclAlign::Center;
+ return eRet;
+ }
+}
+
+bool Window::set_font_attribute(const OUString &rKey, std::u16string_view rValue)
+{
+ if (rKey == "weight")
+ {
+ vcl::Font aFont(GetControlFont());
+ if (rValue == u"thin")
+ aFont.SetWeight(WEIGHT_THIN);
+ else if (rValue == u"ultralight")
+ aFont.SetWeight(WEIGHT_ULTRALIGHT);
+ else if (rValue == u"light")
+ aFont.SetWeight(WEIGHT_LIGHT);
+ else if (rValue == u"book")
+ aFont.SetWeight(WEIGHT_SEMILIGHT);
+ else if (rValue == u"normal")
+ aFont.SetWeight(WEIGHT_NORMAL);
+ else if (rValue == u"medium")
+ aFont.SetWeight(WEIGHT_MEDIUM);
+ else if (rValue == u"semibold")
+ aFont.SetWeight(WEIGHT_SEMIBOLD);
+ else if (rValue == u"bold")
+ aFont.SetWeight(WEIGHT_BOLD);
+ else if (rValue == u"ultrabold")
+ aFont.SetWeight(WEIGHT_ULTRABOLD);
+ else
+ aFont.SetWeight(WEIGHT_BLACK);
+ SetControlFont(aFont);
+ }
+ else if (rKey == "style")
+ {
+ vcl::Font aFont(GetControlFont());
+ if (rValue == u"normal")
+ aFont.SetItalic(ITALIC_NONE);
+ else if (rValue == u"oblique")
+ aFont.SetItalic(ITALIC_OBLIQUE);
+ else if (rValue == u"italic")
+ aFont.SetItalic(ITALIC_NORMAL);
+ SetControlFont(aFont);
+ }
+ else if (rKey == "underline")
+ {
+ vcl::Font aFont(GetControlFont());
+ aFont.SetUnderline(toBool(rValue) ? LINESTYLE_SINGLE : LINESTYLE_NONE);
+ SetControlFont(aFont);
+ }
+ else if (rKey == "scale")
+ {
+ // if no control font was set yet, take the underlying font from the device
+ vcl::Font aFont(IsControlFont() ? GetControlFont() : GetPointFont(*GetOutDev()));
+ aFont.SetFontHeight(aFont.GetFontHeight() * o3tl::toDouble(rValue));
+ SetControlFont(aFont);
+ }
+ else if (rKey == "size")
+ {
+ vcl::Font aFont(GetControlFont());
+ sal_Int32 nHeight = o3tl::toInt32(rValue) / 1000;
+ aFont.SetFontHeight(nHeight);
+ SetControlFont(aFont);
+ }
+ else
+ {
+ SAL_INFO("vcl.layout", "unhandled font attribute: " << rKey);
+ return false;
+ }
+ return true;
+}
+
+bool Window::set_property(const OUString &rKey, const OUString &rValue)
+{
+ if ((rKey == "label") || (rKey == "title") || (rKey == "text") )
+ {
+ SetText(BuilderUtils::convertMnemonicMarkup(rValue));
+ }
+ else if (rKey == "visible")
+ Show(toBool(rValue));
+ else if (rKey == "sensitive")
+ Enable(toBool(rValue));
+ else if (rKey == "resizable")
+ {
+ WinBits nBits = GetStyle();
+ nBits &= ~WB_SIZEABLE;
+ if (toBool(rValue))
+ nBits |= WB_SIZEABLE;
+ SetStyle(nBits);
+ }
+ else if (rKey == "xalign")
+ {
+ WinBits nBits = GetStyle();
+ nBits &= ~(WB_LEFT | WB_CENTER | WB_RIGHT);
+
+ float f = rValue.toFloat();
+ assert(f == 0.0 || f == 1.0 || f == 0.5);
+ if (f == 0.0)
+ nBits |= WB_LEFT;
+ else if (f == 1.0)
+ nBits |= WB_RIGHT;
+ else if (f == 0.5)
+ nBits |= WB_CENTER;
+
+ SetStyle(nBits);
+ }
+ else if (rKey == "justification")
+ {
+ WinBits nBits = GetStyle();
+ nBits &= ~(WB_LEFT | WB_CENTER | WB_RIGHT);
+
+ if (rValue == "left")
+ nBits |= WB_LEFT;
+ else if (rValue == "right")
+ nBits |= WB_RIGHT;
+ else if (rValue == "center")
+ nBits |= WB_CENTER;
+
+ SetStyle(nBits);
+ }
+ else if (rKey == "yalign")
+ {
+ WinBits nBits = GetStyle();
+ nBits &= ~(WB_TOP | WB_VCENTER | WB_BOTTOM);
+
+ float f = rValue.toFloat();
+ assert(f == 0.0 || f == 1.0 || f == 0.5);
+ if (f == 0.0)
+ nBits |= WB_TOP;
+ else if (f == 1.0)
+ nBits |= WB_BOTTOM;
+ else if (f == 0.5)
+ nBits |= WB_VCENTER;
+
+ SetStyle(nBits);
+ }
+ else if (rKey == "wrap")
+ {
+ WinBits nBits = GetStyle();
+ nBits &= ~WB_WORDBREAK;
+ if (toBool(rValue))
+ nBits |= WB_WORDBREAK;
+ SetStyle(nBits);
+ }
+ else if (rKey == "height-request")
+ set_height_request(rValue.toInt32());
+ else if (rKey == "width-request")
+ set_width_request(rValue.toInt32());
+ else if (rKey == "hexpand")
+ set_hexpand(toBool(rValue));
+ else if (rKey == "vexpand")
+ set_vexpand(toBool(rValue));
+ else if (rKey == "halign")
+ set_halign(toAlign(rValue));
+ else if (rKey == "valign")
+ set_valign(toAlign(rValue));
+ else if (rKey == "tooltip-markup")
+ SetQuickHelpText(rValue);
+ else if (rKey == "tooltip-text")
+ SetQuickHelpText(rValue);
+ else if (rKey == "border-width")
+ set_border_width(rValue.toInt32());
+ else if (rKey == "margin-start" || rKey == "margin-left")
+ {
+ assert(rKey == "margin-start" && "margin-left deprecated in favor of margin-start");
+ set_margin_start(rValue.toInt32());
+ }
+ else if (rKey == "margin-end" || rKey == "margin-right")
+ {
+ assert(rKey == "margin-end" && "margin-right deprecated in favor of margin-end");
+ set_margin_end(rValue.toInt32());
+ }
+ else if (rKey == "margin-top")
+ set_margin_top(rValue.toInt32());
+ else if (rKey == "margin-bottom")
+ set_margin_bottom(rValue.toInt32());
+ else if (rKey == "hscrollbar-policy")
+ {
+ WinBits nBits = GetStyle();
+ nBits &= ~(WB_AUTOHSCROLL|WB_HSCROLL);
+ if (rValue == "always")
+ nBits |= WB_HSCROLL;
+ else if (rValue == "automatic")
+ nBits |= WB_AUTOHSCROLL;
+ SetStyle(nBits);
+ }
+ else if (rKey == "vscrollbar-policy")
+ {
+ WinBits nBits = GetStyle();
+ nBits &= ~(WB_AUTOVSCROLL|WB_VSCROLL);
+ if (rValue == "always")
+ nBits |= WB_VSCROLL;
+ else if (rValue == "automatic")
+ nBits |= WB_AUTOVSCROLL;
+ SetStyle(nBits);
+ }
+ else if (rKey == "accessible-name")
+ {
+ SetAccessibleName(rValue);
+ }
+ else if (rKey == "accessible-description")
+ {
+ SetAccessibleDescription(rValue);
+ }
+ else if (rKey == "accessible-role")
+ {
+ sal_Int16 role = BuilderUtils::getRoleFromName(rValue);
+ if (role != com::sun::star::accessibility::AccessibleRole::UNKNOWN)
+ SetAccessibleRole(role);
+ }
+ else if (rKey == "use-markup")
+ {
+ //https://live.gnome.org/GnomeGoals/RemoveMarkupInMessages
+ SAL_WARN_IF(toBool(rValue), "vcl.layout", "Use pango attributes instead of mark-up");
+ }
+ else if (rKey == "has-focus")
+ {
+ if (toBool(rValue))
+ GrabFocus();
+ }
+ else if (rKey == "can-focus")
+ {
+ WinBits nBits = GetStyle();
+ nBits &= ~(WB_TABSTOP|WB_NOTABSTOP);
+ if (toBool(rValue))
+ nBits |= WB_TABSTOP;
+ else
+ nBits |= WB_NOTABSTOP;
+ SetStyle(nBits);
+ }
+ else
+ {
+ SAL_INFO("vcl.layout", "unhandled property: " << rKey);
+ return false;
+ }
+ return true;
+}
+
+void Window::set_height_request(sal_Int32 nHeightRequest)
+{
+ if (!mpWindowImpl)
+ return;
+
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+
+ if ( pWindowImpl->mnHeightRequest != nHeightRequest )
+ {
+ pWindowImpl->mnHeightRequest = nHeightRequest;
+ queue_resize();
+ }
+}
+
+void Window::set_width_request(sal_Int32 nWidthRequest)
+{
+ if (!mpWindowImpl)
+ return;
+
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+
+ if ( pWindowImpl->mnWidthRequest != nWidthRequest )
+ {
+ pWindowImpl->mnWidthRequest = nWidthRequest;
+ queue_resize();
+ }
+}
+
+Size Window::get_ungrouped_preferred_size() const
+{
+ Size aRet(get_width_request(), get_height_request());
+ if (aRet.Width() == -1 || aRet.Height() == -1)
+ {
+ //cache gets blown away by queue_resize
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ if (pWindowImpl->mnOptimalWidthCache == -1 || pWindowImpl->mnOptimalHeightCache == -1)
+ {
+ Size aOptimal(GetOptimalSize());
+ pWindowImpl->mnOptimalWidthCache = aOptimal.Width();
+ pWindowImpl->mnOptimalHeightCache = aOptimal.Height();
+ }
+
+ if (aRet.Width() == -1)
+ aRet.setWidth( pWindowImpl->mnOptimalWidthCache );
+ if (aRet.Height() == -1)
+ aRet.setHeight( pWindowImpl->mnOptimalHeightCache );
+ }
+ return aRet;
+}
+
+Size Window::get_preferred_size() const
+{
+ Size aRet(get_ungrouped_preferred_size());
+
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ if (pWindowImpl->m_xSizeGroup)
+ {
+ const VclSizeGroupMode eMode = pWindowImpl->m_xSizeGroup->get_mode();
+ if (eMode != VclSizeGroupMode::NONE)
+ {
+ const bool bIgnoreInHidden = pWindowImpl->m_xSizeGroup->get_ignore_hidden();
+ const std::set<VclPtr<vcl::Window> > &rWindows = pWindowImpl->m_xSizeGroup->get_widgets();
+ for (auto const& window : rWindows)
+ {
+ const vcl::Window *pOther = window;
+ if (pOther == this)
+ continue;
+ if (bIgnoreInHidden && !pOther->IsVisible())
+ continue;
+ Size aOtherSize = pOther->get_ungrouped_preferred_size();
+ if (eMode == VclSizeGroupMode::Both || eMode == VclSizeGroupMode::Horizontal)
+ aRet.setWidth( std::max(aRet.Width(), aOtherSize.Width()) );
+ if (eMode == VclSizeGroupMode::Both || eMode == VclSizeGroupMode::Vertical)
+ aRet.setHeight( std::max(aRet.Height(), aOtherSize.Height()) );
+ }
+ }
+ }
+
+ return aRet;
+}
+
+VclAlign Window::get_halign() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->meHalign;
+}
+
+void Window::set_halign(VclAlign eAlign)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->meHalign = eAlign;
+}
+
+VclAlign Window::get_valign() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->meValign;
+}
+
+void Window::set_valign(VclAlign eAlign)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->meValign = eAlign;
+}
+
+bool Window::get_hexpand() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mbHexpand;
+}
+
+void Window::set_hexpand(bool bExpand)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mbHexpand = bExpand;
+}
+
+bool Window::get_vexpand() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mbVexpand;
+}
+
+void Window::set_vexpand(bool bExpand)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mbVexpand = bExpand;
+}
+
+bool Window::get_expand() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mbExpand;
+}
+
+void Window::set_expand(bool bExpand)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mbExpand = bExpand;
+}
+
+VclPackType Window::get_pack_type() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mePackType;
+}
+
+void Window::set_pack_type(VclPackType ePackType)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mePackType = ePackType;
+}
+
+sal_Int32 Window::get_padding() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnPadding;
+}
+
+void Window::set_padding(sal_Int32 nPadding)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mnPadding = nPadding;
+}
+
+bool Window::get_fill() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mbFill;
+}
+
+void Window::set_fill(bool bFill)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mbFill = bFill;
+}
+
+sal_Int32 Window::get_grid_width() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnGridWidth;
+}
+
+void Window::set_grid_width(sal_Int32 nCols)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mnGridWidth = nCols;
+}
+
+sal_Int32 Window::get_grid_left_attach() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnGridLeftAttach;
+}
+
+void Window::set_grid_left_attach(sal_Int32 nAttach)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mnGridLeftAttach = nAttach;
+}
+
+sal_Int32 Window::get_grid_height() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnGridHeight;
+}
+
+void Window::set_grid_height(sal_Int32 nRows)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mnGridHeight = nRows;
+}
+
+sal_Int32 Window::get_grid_top_attach() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnGridTopAttach;
+}
+
+void Window::set_grid_top_attach(sal_Int32 nAttach)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mnGridTopAttach = nAttach;
+}
+
+void Window::set_border_width(sal_Int32 nBorderWidth)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mnBorderWidth = nBorderWidth;
+}
+
+sal_Int32 Window::get_border_width() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnBorderWidth;
+}
+
+void Window::set_margin_start(sal_Int32 nWidth)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ if (pWindowImpl->mnMarginLeft != nWidth)
+ {
+ pWindowImpl->mnMarginLeft = nWidth;
+ queue_resize();
+ }
+}
+
+sal_Int32 Window::get_margin_start() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnMarginLeft;
+}
+
+void Window::set_margin_end(sal_Int32 nWidth)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ if (pWindowImpl->mnMarginRight != nWidth)
+ {
+ pWindowImpl->mnMarginRight = nWidth;
+ queue_resize();
+ }
+}
+
+sal_Int32 Window::get_margin_end() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnMarginRight;
+}
+
+void Window::set_margin_top(sal_Int32 nWidth)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ if (pWindowImpl->mnMarginTop != nWidth)
+ {
+ pWindowImpl->mnMarginTop = nWidth;
+ queue_resize();
+ }
+}
+
+sal_Int32 Window::get_margin_top() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnMarginTop;
+}
+
+void Window::set_margin_bottom(sal_Int32 nWidth)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ if (pWindowImpl->mnMarginBottom != nWidth)
+ {
+ pWindowImpl->mnMarginBottom = nWidth;
+ queue_resize();
+ }
+}
+
+sal_Int32 Window::get_margin_bottom() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnMarginBottom;
+}
+
+sal_Int32 Window::get_height_request() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnHeightRequest;
+}
+
+sal_Int32 Window::get_width_request() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mnWidthRequest;
+}
+
+bool Window::get_secondary() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mbSecondary;
+}
+
+void Window::set_secondary(bool bSecondary)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mbSecondary = bSecondary;
+}
+
+bool Window::get_non_homogeneous() const
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ return pWindowImpl->mbNonHomogeneous;
+}
+
+void Window::set_non_homogeneous(bool bNonHomogeneous)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ pWindowImpl->mbNonHomogeneous = bNonHomogeneous;
+}
+
+void Window::add_to_size_group(const std::shared_ptr<VclSizeGroup>& xGroup)
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ //To-Do, multiple groups
+ pWindowImpl->m_xSizeGroup = xGroup;
+ pWindowImpl->m_xSizeGroup->insert(this);
+ if (VclSizeGroupMode::NONE != pWindowImpl->m_xSizeGroup->get_mode())
+ queue_resize();
+}
+
+void Window::remove_from_all_size_groups()
+{
+ WindowImpl *pWindowImpl = mpWindowImpl->mpBorderWindow ? mpWindowImpl->mpBorderWindow->mpWindowImpl.get() : mpWindowImpl.get();
+ //To-Do, multiple groups
+ if (pWindowImpl->m_xSizeGroup)
+ {
+ if (VclSizeGroupMode::NONE != pWindowImpl->m_xSizeGroup->get_mode())
+ queue_resize();
+ pWindowImpl->m_xSizeGroup->erase(this);
+ pWindowImpl->m_xSizeGroup.reset();
+ }
+}
+
+void Window::add_mnemonic_label(FixedText *pLabel)
+{
+ std::vector<VclPtr<FixedText> >& v = mpWindowImpl->m_aMnemonicLabels;
+ if (std::find(v.begin(), v.end(), VclPtr<FixedText>(pLabel)) != v.end())
+ return;
+ v.emplace_back(pLabel);
+ pLabel->set_mnemonic_widget(this);
+}
+
+void Window::remove_mnemonic_label(FixedText *pLabel)
+{
+ std::vector<VclPtr<FixedText> >& v = mpWindowImpl->m_aMnemonicLabels;
+ auto aFind = std::find(v.begin(), v.end(), VclPtr<FixedText>(pLabel));
+ if (aFind == v.end())
+ return;
+ v.erase(aFind);
+ pLabel->set_mnemonic_widget(nullptr);
+}
+
+const std::vector<VclPtr<FixedText> >& Window::list_mnemonic_labels() const
+{
+ return mpWindowImpl->m_aMnemonicLabels;
+}
+
+} /* namespace vcl */
+
+void InvertFocusRect(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ const int nBorder = 1;
+ rRenderContext.Invert(tools::Rectangle(Point(rRect.Left(), rRect.Top()), Size(rRect.GetWidth(), nBorder)), InvertFlags::N50);
+ rRenderContext.Invert(tools::Rectangle(Point(rRect.Left(), rRect.Bottom()-nBorder+1), Size(rRect.GetWidth(), nBorder)), InvertFlags::N50);
+ rRenderContext.Invert(tools::Rectangle(Point(rRect.Left(), rRect.Top()+nBorder), Size(nBorder, rRect.GetHeight()-(nBorder*2))), InvertFlags::N50);
+ rRenderContext.Invert(tools::Rectangle(Point(rRect.Right()-nBorder+1, rRect.Top()+nBorder), Size(nBorder, rRect.GetHeight()-(nBorder*2))), InvertFlags::N50);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/window3.cxx b/vcl/source/window/window3.cxx
new file mode 100644
index 0000000000..30c41f5e20
--- /dev/null
+++ b/vcl/source/window/window3.cxx
@@ -0,0 +1,220 @@
+/* -*- 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 <vcl/window.hxx>
+#include <window.h>
+#include <vcl/cursor.hxx>
+
+namespace vcl
+{
+Size Window::GetOptimalSize() const { return Size(); }
+
+void Window::ImplAdjustNWFSizes()
+{
+ for (Window* pWin = GetWindow(GetWindowType::FirstChild); pWin;
+ pWin = pWin->GetWindow(GetWindowType::Next))
+ pWin->ImplAdjustNWFSizes();
+}
+
+void WindowOutputDevice::ImplClearFontData(bool bNewFontLists)
+{
+ OutputDevice::ImplClearFontData(bNewFontLists);
+ for (Window* pChild = mxOwnerWindow->mpWindowImpl->mpFirstChild; pChild;
+ pChild = pChild->mpWindowImpl->mpNext)
+ pChild->GetOutDev()->ImplClearFontData(bNewFontLists);
+}
+
+void WindowOutputDevice::ImplRefreshFontData(bool bNewFontLists)
+{
+ OutputDevice::ImplRefreshFontData(bNewFontLists);
+ for (Window* pChild = mxOwnerWindow->mpWindowImpl->mpFirstChild; pChild;
+ pChild = pChild->mpWindowImpl->mpNext)
+ pChild->GetOutDev()->ImplRefreshFontData(bNewFontLists);
+}
+
+void WindowOutputDevice::ImplInitMapModeObjects()
+{
+ OutputDevice::ImplInitMapModeObjects();
+ if (mxOwnerWindow->mpWindowImpl->mpCursor)
+ mxOwnerWindow->mpWindowImpl->mpCursor->ImplNew();
+}
+
+const Font& Window::GetFont() const { return GetOutDev()->GetFont(); }
+void Window::SetFont(Font const& font) { return GetOutDev()->SetFont(font); }
+
+float Window::approximate_char_width() const { return GetOutDev()->approximate_char_width(); }
+
+const Wallpaper& Window::GetBackground() const { return GetOutDev()->GetBackground(); }
+bool Window::IsBackground() const { return GetOutDev()->IsBackground(); }
+tools::Long Window::GetTextHeight() const { return GetOutDev()->GetTextHeight(); }
+tools::Long Window::GetTextWidth(const OUString& rStr, sal_Int32 nIndex, sal_Int32 nLen,
+ vcl::text::TextLayoutCache const* pCache,
+ SalLayoutGlyphs const* const pLayoutCache) const
+{
+ return GetOutDev()->GetTextWidth(rStr, nIndex, nLen, pCache, pLayoutCache);
+}
+float Window::approximate_digit_width() const { return GetOutDev()->approximate_digit_width(); }
+
+bool Window::IsNativeControlSupported(ControlType nType, ControlPart nPart) const
+{
+ return GetOutDev()->IsNativeControlSupported(nType, nPart);
+}
+
+bool Window::GetNativeControlRegion(ControlType nType, ControlPart nPart,
+ const tools::Rectangle& rControlRegion, ControlState nState,
+ const ImplControlValue& aValue,
+ tools::Rectangle& rNativeBoundingRegion,
+ tools::Rectangle& rNativeContentRegion) const
+{
+ return GetOutDev()->GetNativeControlRegion(nType, nPart, rControlRegion, nState, aValue,
+ rNativeBoundingRegion, rNativeContentRegion);
+}
+
+Size Window::GetOutputSizePixel() const { return GetOutDev()->GetOutputSizePixel(); }
+
+tools::Rectangle Window::GetOutputRectPixel() const { return GetOutDev()->GetOutputRectPixel(); }
+
+void Window::SetTextLineColor() { GetOutDev()->SetTextLineColor(); }
+void Window::SetTextLineColor(const Color& rColor) { GetOutDev()->SetTextLineColor(rColor); }
+void Window::SetOverlineColor() { GetOutDev()->SetOverlineColor(); }
+void Window::SetOverlineColor(const Color& rColor) { GetOutDev()->SetOverlineColor(rColor); }
+void Window::SetTextFillColor() { GetOutDev()->SetTextFillColor(); }
+void Window::SetTextFillColor(const Color& rColor) { GetOutDev()->SetTextFillColor(rColor); }
+const MapMode& Window::GetMapMode() const { return GetOutDev()->GetMapMode(); }
+void Window::SetBackground() { GetOutDev()->SetBackground(); }
+void Window::SetBackground(const Wallpaper& rBackground)
+{
+ GetOutDev()->SetBackground(rBackground);
+}
+void Window::EnableMapMode(bool bEnable) { GetOutDev()->EnableMapMode(bEnable); }
+bool Window::IsMapModeEnabled() const { return GetOutDev()->IsMapModeEnabled(); }
+
+void Window::SetTextColor(const Color& rColor) { GetOutDev()->SetTextColor(rColor); }
+const Color& Window::GetTextColor() const { return GetOutDev()->GetTextColor(); }
+const Color& Window::GetTextLineColor() const { return GetOutDev()->GetTextLineColor(); }
+
+bool Window::IsTextLineColor() const { return GetOutDev()->IsTextLineColor(); }
+
+Color Window::GetTextFillColor() const { return GetOutDev()->GetTextFillColor(); }
+
+bool Window::IsTextFillColor() const { return GetOutDev()->IsTextFillColor(); }
+
+const Color& Window::GetOverlineColor() const { return GetOutDev()->GetOverlineColor(); }
+bool Window::IsOverlineColor() const { return GetOutDev()->IsOverlineColor(); }
+void Window::SetTextAlign(TextAlign eAlign) { GetOutDev()->SetTextAlign(eAlign); }
+
+float Window::GetDPIScaleFactor() const { return GetOutDev()->GetDPIScaleFactor(); }
+tools::Long Window::GetOutOffXPixel() const { return GetOutDev()->GetOutOffXPixel(); }
+tools::Long Window::GetOutOffYPixel() const { return GetOutDev()->GetOutOffYPixel(); }
+void Window::SetMapMode() { GetOutDev()->SetMapMode(); }
+void Window::SetMapMode(const MapMode& rNewMapMode) { GetOutDev()->SetMapMode(rNewMapMode); }
+bool Window::IsRTLEnabled() const { return GetOutDev()->IsRTLEnabled(); }
+TextAlign Window::GetTextAlign() const { return GetOutDev()->GetTextAlign(); }
+const AllSettings& Window::GetSettings() const { return GetOutDev()->GetSettings(); }
+
+Point Window::LogicToPixel(const Point& rLogicPt) const
+{
+ return GetOutDev()->LogicToPixel(rLogicPt);
+}
+Size Window::LogicToPixel(const Size& rLogicSize) const
+{
+ return GetOutDev()->LogicToPixel(rLogicSize);
+}
+tools::Rectangle Window::LogicToPixel(const tools::Rectangle& rLogicRect) const
+{
+ return GetOutDev()->LogicToPixel(rLogicRect);
+}
+vcl::Region Window::LogicToPixel(const vcl::Region& rLogicRegion) const
+{
+ return GetOutDev()->LogicToPixel(rLogicRegion);
+}
+Point Window::LogicToPixel(const Point& rLogicPt, const MapMode& rMapMode) const
+{
+ return GetOutDev()->LogicToPixel(rLogicPt, rMapMode);
+}
+Size Window::LogicToPixel(const Size& rLogicSize, const MapMode& rMapMode) const
+{
+ return GetOutDev()->LogicToPixel(rLogicSize, rMapMode);
+}
+tools::Rectangle Window::LogicToPixel(const tools::Rectangle& rLogicRect,
+ const MapMode& rMapMode) const
+{
+ return GetOutDev()->LogicToPixel(rLogicRect, rMapMode);
+}
+
+Point Window::PixelToLogic(const Point& rDevicePt) const
+{
+ return GetOutDev()->PixelToLogic(rDevicePt);
+}
+Size Window::PixelToLogic(const Size& rDeviceSize) const
+{
+ return GetOutDev()->PixelToLogic(rDeviceSize);
+}
+tools::Rectangle Window::PixelToLogic(const tools::Rectangle& rDeviceRect) const
+{
+ return GetOutDev()->PixelToLogic(rDeviceRect);
+}
+tools::PolyPolygon Window::PixelToLogic(const tools::PolyPolygon& rDevicePolyPoly) const
+{
+ return GetOutDev()->PixelToLogic(rDevicePolyPoly);
+}
+vcl::Region Window::PixelToLogic(const vcl::Region& rDeviceRegion) const
+{
+ return GetOutDev()->PixelToLogic(rDeviceRegion);
+}
+Point Window::PixelToLogic(const Point& rDevicePt, const MapMode& rMapMode) const
+{
+ return GetOutDev()->PixelToLogic(rDevicePt, rMapMode);
+}
+Size Window::PixelToLogic(const Size& rDeviceSize, const MapMode& rMapMode) const
+{
+ return GetOutDev()->PixelToLogic(rDeviceSize, rMapMode);
+}
+tools::Rectangle Window::PixelToLogic(const tools::Rectangle& rDeviceRect,
+ const MapMode& rMapMode) const
+{
+ return GetOutDev()->PixelToLogic(rDeviceRect, rMapMode);
+}
+
+Size Window::LogicToLogic(const Size& rSzSource, const MapMode* pMapModeSource,
+ const MapMode* pMapModeDest) const
+{
+ return GetOutDev()->LogicToLogic(rSzSource, pMapModeSource, pMapModeDest);
+}
+
+tools::Rectangle Window::GetTextRect(const tools::Rectangle& rRect, const OUString& rStr,
+ DrawTextFlags nStyle, TextRectInfo* pInfo,
+ const vcl::TextLayoutCommon* _pTextLayout) const
+{
+ return GetOutDev()->GetTextRect(rRect, rStr, nStyle, pInfo, _pTextLayout);
+}
+
+void Window::SetSettings(const AllSettings& rSettings) { GetOutDev()->SetSettings(rSettings); }
+void Window::SetSettings(const AllSettings& rSettings, bool bChild)
+{
+ static_cast<vcl::WindowOutputDevice*>(GetOutDev())->SetSettings(rSettings, bChild);
+}
+
+Color Window::GetBackgroundColor() const { return GetOutDev()->GetBackgroundColor(); }
+
+void Window::EnableRTL(bool bEnable) { GetOutDev()->EnableRTL(bEnable); }
+
+} /* namespace vcl */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/winproc.cxx b/vcl/source/window/winproc.cxx
new file mode 100644
index 0000000000..e2e47160e9
--- /dev/null
+++ b/vcl/source/window/winproc.cxx
@@ -0,0 +1,2948 @@
+/* -*- 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 <o3tl/safeint.hxx>
+#include <tools/debug.hxx>
+#include <tools/time.hxx>
+#include <sal/log.hxx>
+
+#include <unotools/localedatawrapper.hxx>
+
+#include <dndeventdispatcher.hxx>
+#include <comphelper/lok.hxx>
+#include <vcl/QueueInfo.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/event.hxx>
+#include <vcl/GestureEventPan.hxx>
+#include <vcl/GestureEventZoom.hxx>
+#include <vcl/GestureEventRotate.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/wrkwin.hxx>
+#include <vcl/toolkit/floatwin.hxx>
+#include <vcl/toolkit/dialog.hxx>
+#include <vcl/toolkit/edit.hxx>
+#include <vcl/help.hxx>
+#include <vcl/dockwin.hxx>
+#include <vcl/menu.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/ptrstyle.hxx>
+
+#include <svdata.hxx>
+#include <salwtype.hxx>
+#include <salframe.hxx>
+#include <accmgr.hxx>
+#include <print.h>
+#include <window.h>
+#include <helpwin.hxx>
+#include <brdwin.hxx>
+#include <dndlistenercontainer.hxx>
+
+#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
+#include <com/sun/star/datatransfer/dnd/XDragSource.hpp>
+#include <com/sun/star/awt/MouseEvent.hpp>
+
+#define IMPL_MIN_NEEDSYSWIN 49
+
+bool ImplCallPreNotify( NotifyEvent& rEvt )
+{
+ return rEvt.GetWindow()->CompatPreNotify( rEvt );
+}
+
+static bool ImplHandleMouseFloatMode( vcl::Window* pChild, const Point& rMousePos,
+ sal_uInt16 nCode, NotifyEventType nSVEvent,
+ bool bMouseLeave )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ if (pSVData->mpWinData->mpFirstFloat && !pSVData->mpWinData->mpCaptureWin
+ && !pSVData->mpWinData->mpFirstFloat->ImplIsFloatPopupModeWindow(pChild))
+ {
+ /*
+ * #93895# since floats are system windows, coordinates have
+ * to be converted to float relative for the hittest
+ */
+ bool bHitTestInsideRect = false;
+ FloatingWindow* pFloat = pSVData->mpWinData->mpFirstFloat->ImplFloatHitTest( pChild, rMousePos, bHitTestInsideRect );
+ if ( nSVEvent == NotifyEventType::MOUSEMOVE )
+ {
+ if ( bMouseLeave )
+ return true;
+
+ if ( !pFloat || bHitTestInsideRect )
+ {
+ if ( ImplGetSVHelpData().mpHelpWin && !ImplGetSVHelpData().mbKeyboardHelp )
+ ImplDestroyHelpWindow( true );
+ pChild->ImplGetFrame()->SetPointer( PointerStyle::Arrow );
+ return true;
+ }
+ }
+ else
+ {
+ if ( nCode & MOUSE_LEFT )
+ {
+ if ( nSVEvent == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ if ( !pFloat )
+ {
+ FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat();
+ pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
+ return true;
+ }
+ else if ( bHitTestInsideRect )
+ {
+ pFloat->ImplSetMouseDown();
+ return true;
+ }
+ }
+ else
+ {
+ if ( pFloat )
+ {
+ if ( bHitTestInsideRect )
+ {
+ if ( pFloat->ImplIsMouseDown() )
+ pFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel );
+ return true;
+ }
+ }
+ else
+ {
+ FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat();
+ FloatWinPopupFlags nPopupFlags = pLastLevelFloat->GetPopupModeFlags();
+ if ( !(nPopupFlags & FloatWinPopupFlags::NoMouseUpClose) )
+ {
+ pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
+ return true;
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( !pFloat )
+ {
+ FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat();
+ FloatWinPopupFlags nPopupFlags = pLastLevelFloat->GetPopupModeFlags();
+ if ( nPopupFlags & FloatWinPopupFlags::AllMouseButtonClose )
+ {
+ if ( (nPopupFlags & FloatWinPopupFlags::NoMouseUpClose) &&
+ (nSVEvent == NotifyEventType::MOUSEBUTTONUP) )
+ return true;
+ pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
+ return true;
+ }
+ else
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+static void ImplHandleMouseHelpRequest( vcl::Window* pChild, const Point& rMousePos )
+{
+ ImplSVHelpData& aHelpData = ImplGetSVHelpData();
+ if ( aHelpData.mpHelpWin &&
+ ( aHelpData.mpHelpWin->IsWindowOrChild( pChild ) ||
+ pChild->IsWindowOrChild( aHelpData.mpHelpWin ) ))
+ return;
+
+ HelpEventMode nHelpMode = HelpEventMode::NONE;
+ if ( aHelpData.mbQuickHelp )
+ nHelpMode = HelpEventMode::QUICK;
+ if ( aHelpData.mbBalloonHelp )
+ nHelpMode |= HelpEventMode::BALLOON;
+ if ( !(bool(nHelpMode)) )
+ return;
+
+ if ( pChild->IsInputEnabled() && !pChild->IsInModalMode() )
+ {
+ HelpEvent aHelpEvent( rMousePos, nHelpMode );
+ aHelpData.mbRequestingHelp = true;
+ pChild->RequestHelp( aHelpEvent );
+ aHelpData.mbRequestingHelp = false;
+ }
+ // #104172# do not kill keyboard activated tooltips
+ else if ( aHelpData.mpHelpWin && !aHelpData.mbKeyboardHelp)
+ {
+ ImplDestroyHelpWindow( true );
+ }
+}
+
+static void ImplSetMousePointer( vcl::Window const * pChild )
+{
+ if ( ImplGetSVHelpData().mbExtHelpMode )
+ pChild->ImplGetFrame()->SetPointer( PointerStyle::Help );
+ else
+ pChild->ImplGetFrame()->SetPointer( pChild->ImplGetMousePointer() );
+}
+
+static bool ImplCallCommand( const VclPtr<vcl::Window>& pChild, CommandEventId nEvt, void const * pData = nullptr,
+ bool bMouse = false, Point const * pPos = nullptr )
+{
+ Point aPos;
+ if ( pPos )
+ aPos = *pPos;
+ else
+ {
+ if( bMouse )
+ aPos = pChild->GetPointerPosPixel();
+ else
+ {
+ // simulate mouseposition at center of window
+ Size aSize( pChild->GetOutputSizePixel() );
+ aPos = Point( aSize.getWidth()/2, aSize.getHeight()/2 );
+ }
+ }
+
+ CommandEvent aCEvt( aPos, nEvt, bMouse, pData );
+ NotifyEvent aNCmdEvt( NotifyEventType::COMMAND, pChild, &aCEvt );
+ bool bPreNotify = ImplCallPreNotify( aNCmdEvt );
+ if ( pChild->isDisposed() )
+ return false;
+ if ( !bPreNotify )
+ {
+ pChild->ImplGetWindowImpl()->mbCommand = false;
+ pChild->Command( aCEvt );
+
+ if( pChild->isDisposed() )
+ return false;
+ pChild->ImplNotifyKeyMouseCommandEventListeners( aNCmdEvt );
+ if ( pChild->isDisposed() )
+ return false;
+ if ( pChild->ImplGetWindowImpl()->mbCommand )
+ return true;
+ }
+
+ return false;
+}
+
+/* #i34277# delayed context menu activation;
+* necessary if there already was a popup menu running.
+*/
+
+namespace {
+
+struct ContextMenuEvent
+{
+ VclPtr<vcl::Window> pWindow;
+ Point aChildPos;
+};
+
+}
+
+static void ContextMenuEventLink( void* pCEvent, void* )
+{
+ ContextMenuEvent* pEv = static_cast<ContextMenuEvent*>(pCEvent);
+
+ if( ! pEv->pWindow->isDisposed() )
+ {
+ ImplCallCommand( pEv->pWindow, CommandEventId::ContextMenu, nullptr, true, &pEv->aChildPos );
+ }
+ delete pEv;
+}
+
+bool ImplHandleMouseEvent( const VclPtr<vcl::Window>& xWindow, NotifyEventType nSVEvent, bool bMouseLeave,
+ tools::Long nX, tools::Long nY, sal_uInt64 nMsgTime,
+ sal_uInt16 nCode, MouseEventModifiers nMode )
+{
+ SAL_INFO( "vcl.debugevent",
+ "mouse event "
+ "(NotifyEventType " << static_cast<sal_uInt16>(nSVEvent) << ") "
+ "(MouseLeave " << bMouseLeave << ") "
+ "(X, Y " << nX << ", " << nY << ") "
+ "(Code " << nCode << ") "
+ "(Modifiers " << static_cast<sal_uInt16>(nMode) << ")");
+ ImplSVHelpData& aHelpData = ImplGetSVHelpData();
+ ImplSVData* pSVData = ImplGetSVData();
+ Point aMousePos( nX, nY );
+ VclPtr<vcl::Window> pChild;
+ bool bRet(false);
+ sal_uInt16 nClicks(0);
+ ImplFrameData* pWinFrameData = xWindow->ImplGetFrameData();
+ sal_uInt16 nOldCode = pWinFrameData->mnMouseCode;
+
+ if (comphelper::LibreOfficeKit::isActive() && AllSettings::GetLayoutRTL()
+ && xWindow->GetOutDev() && !xWindow->GetOutDev()->ImplIsAntiparallel())
+ {
+ xWindow->GetOutDev()->ReMirror(aMousePos);
+ nX = aMousePos.X();
+ nY = aMousePos.Y();
+ }
+
+ // we need a mousemove event, before we get a mousebuttondown or a
+ // mousebuttonup event
+ if ( (nSVEvent == NotifyEventType::MOUSEBUTTONDOWN) || (nSVEvent == NotifyEventType::MOUSEBUTTONUP) )
+ {
+ if ( (nSVEvent == NotifyEventType::MOUSEBUTTONUP) && aHelpData.mbExtHelpMode )
+ Help::EndExtHelp();
+ if ( aHelpData.mpHelpWin )
+ {
+ if( xWindow->ImplGetWindow() == aHelpData.mpHelpWin )
+ {
+ ImplDestroyHelpWindow( false );
+ return true; // xWindow is dead now - avoid crash!
+ }
+ else
+ ImplDestroyHelpWindow( true );
+ }
+
+ if ( (pWinFrameData->mnLastMouseX != nX) ||
+ (pWinFrameData->mnLastMouseY != nY) )
+ {
+ sal_uInt16 nMoveCode = nCode & ~(MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE);
+ ImplHandleMouseEvent(xWindow, NotifyEventType::MOUSEMOVE, false, nX, nY, nMsgTime, nMoveCode, nMode);
+ }
+ }
+
+ // update frame data
+ pWinFrameData->mnBeforeLastMouseX = pWinFrameData->mnLastMouseX;
+ pWinFrameData->mnBeforeLastMouseY = pWinFrameData->mnLastMouseY;
+ pWinFrameData->mnLastMouseX = nX;
+ pWinFrameData->mnLastMouseY = nY;
+ pWinFrameData->mnMouseCode = nCode;
+ MouseEventModifiers const nTmpMask = MouseEventModifiers::SYNTHETIC | MouseEventModifiers::MODIFIERCHANGED;
+ pWinFrameData->mnMouseMode = nMode & ~nTmpMask;
+ if ( bMouseLeave )
+ {
+ pWinFrameData->mbMouseIn = false;
+ if ( ImplGetSVHelpData().mpHelpWin && !ImplGetSVHelpData().mbKeyboardHelp )
+ {
+ ImplDestroyHelpWindow( true );
+
+ if ( xWindow->isDisposed() )
+ return true; // xWindow is dead now - avoid crash! (#122045#)
+ }
+ }
+ else
+ pWinFrameData->mbMouseIn = true;
+
+ DBG_ASSERT(!pSVData->mpWinData->mpTrackWin
+ || (pSVData->mpWinData->mpTrackWin == pSVData->mpWinData->mpCaptureWin),
+ "ImplHandleMouseEvent: TrackWin != CaptureWin");
+
+ // AutoScrollMode
+ if (pSVData->mpWinData->mpAutoScrollWin && (nSVEvent == NotifyEventType::MOUSEBUTTONDOWN))
+ {
+ pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll();
+ return true;
+ }
+
+ // find mouse window
+ if (pSVData->mpWinData->mpCaptureWin)
+ {
+ pChild = pSVData->mpWinData->mpCaptureWin;
+
+ SAL_WARN_IF( xWindow != pChild->ImplGetFrameWindow(), "vcl",
+ "ImplHandleMouseEvent: mouse event is not sent to capture window" );
+
+ // java client cannot capture mouse correctly
+ if ( xWindow != pChild->ImplGetFrameWindow() )
+ return false;
+
+ if ( bMouseLeave )
+ return false;
+ }
+ else
+ {
+ if ( bMouseLeave )
+ pChild = nullptr;
+ else
+ pChild = xWindow->ImplFindWindow( aMousePos );
+ }
+
+ // test this because mouse events are buffered in the remote version
+ // and size may not be in sync
+ if ( !pChild && !bMouseLeave )
+ return false;
+
+ // execute a few tests and catch the message or implement the status
+ if ( pChild )
+ {
+ if( pChild->GetOutDev()->ImplIsAntiparallel() )
+ {
+ // re-mirror frame pos at pChild
+ const OutputDevice *pChildWinOutDev = pChild->GetOutDev();
+ pChildWinOutDev->ReMirror( aMousePos );
+ }
+
+ // no mouse messages to disabled windows
+ // #106845# if the window was disabled during capturing we have to pass the mouse events to release capturing
+ if (pSVData->mpWinData->mpCaptureWin.get() != pChild
+ && (!pChild->IsEnabled() || !pChild->IsInputEnabled() || pChild->IsInModalMode()))
+ {
+ ImplHandleMouseFloatMode( pChild, aMousePos, nCode, nSVEvent, bMouseLeave );
+ if ( nSVEvent == NotifyEventType::MOUSEMOVE )
+ {
+ ImplHandleMouseHelpRequest( pChild, aMousePos );
+ if( pWinFrameData->mpMouseMoveWin.get() != pChild )
+ nMode |= MouseEventModifiers::ENTERWINDOW;
+ }
+
+ // Call the hook also, if Window is disabled
+
+ if ( nSVEvent == NotifyEventType::MOUSEBUTTONDOWN )
+ return true;
+ else
+ {
+ // Set normal MousePointer for disabled windows
+ if ( nSVEvent == NotifyEventType::MOUSEMOVE )
+ ImplSetMousePointer( pChild );
+
+ return false;
+ }
+ }
+
+ // End ExtTextInput-Mode, if the user click in the same TopLevel Window
+ if (pSVData->mpWinData->mpExtTextInputWin
+ && ((nSVEvent == NotifyEventType::MOUSEBUTTONDOWN)
+ || (nSVEvent == NotifyEventType::MOUSEBUTTONUP)))
+ pSVData->mpWinData->mpExtTextInputWin->EndExtTextInput();
+ }
+
+ // determine mouse event data
+ if ( nSVEvent == NotifyEventType::MOUSEMOVE )
+ {
+ // check if MouseMove belongs to same window and if the
+ // status did not change
+ if ( pChild )
+ {
+ Point aChildMousePos = pChild->ScreenToOutputPixel( aMousePos );
+ if ( !bMouseLeave &&
+ (pChild == pWinFrameData->mpMouseMoveWin) &&
+ (aChildMousePos.X() == pWinFrameData->mnLastMouseWinX) &&
+ (aChildMousePos.Y() == pWinFrameData->mnLastMouseWinY) &&
+ (nOldCode == pWinFrameData->mnMouseCode) )
+ {
+ // set mouse pointer anew, as it could have changed
+ // due to the mode switch
+ ImplSetMousePointer( pChild );
+ return false;
+ }
+
+ pWinFrameData->mnLastMouseWinX = aChildMousePos.X();
+ pWinFrameData->mnLastMouseWinY = aChildMousePos.Y();
+ }
+
+ // mouse click
+ nClicks = pWinFrameData->mnClickCount;
+
+ // call Start-Drag handler if required
+ // Warning: should be called before Move, as otherwise during
+ // fast mouse movements the applications move to the selection state
+ vcl::Window* pMouseDownWin = pWinFrameData->mpMouseDownWin;
+ if ( pMouseDownWin )
+ {
+ // check for matching StartDrag mode. We only compare
+ // the status of the mouse buttons, such that e. g. Mod1 can
+ // change immediately to the copy mode
+ const MouseSettings& rMSettings = pMouseDownWin->GetSettings().GetMouseSettings();
+ if ( (nCode & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)) ==
+ (MouseSettings::GetStartDragCode() & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)) )
+ {
+ if ( !pMouseDownWin->ImplGetFrameData()->mbStartDragCalled )
+ {
+ tools::Long nDragW = rMSettings.GetStartDragWidth();
+ tools::Long nDragH = rMSettings.GetStartDragHeight();
+ //long nMouseX = nX;
+ //long nMouseY = nY;
+ tools::Long nMouseX = aMousePos.X(); // #106074# use the possibly re-mirrored coordinates (RTL) ! nX,nY are unmodified !
+ tools::Long nMouseY = aMousePos.Y();
+ if ( (((nMouseX-nDragW) > pMouseDownWin->ImplGetFrameData()->mnFirstMouseX) ||
+ ((nMouseX+nDragW) < pMouseDownWin->ImplGetFrameData()->mnFirstMouseX)) ||
+ (((nMouseY-nDragH) > pMouseDownWin->ImplGetFrameData()->mnFirstMouseY) ||
+ ((nMouseY+nDragH) < pMouseDownWin->ImplGetFrameData()->mnFirstMouseY)) )
+ {
+ pMouseDownWin->ImplGetFrameData()->mbStartDragCalled = true;
+
+ // Check if drag source provides its own recognizer
+ if( pMouseDownWin->ImplGetFrameData()->mbInternalDragGestureRecognizer )
+ {
+ // query DropTarget from child window
+ css::uno::Reference< css::datatransfer::dnd::XDragGestureRecognizer > xDragGestureRecognizer(
+ pMouseDownWin->ImplGetWindowImpl()->mxDNDListenerContainer,
+ css::uno::UNO_QUERY );
+
+ if( xDragGestureRecognizer.is() )
+ {
+ // retrieve mouse position relative to mouse down window
+ Point relLoc = pMouseDownWin->ScreenToOutputPixel( Point(
+ pMouseDownWin->ImplGetFrameData()->mnFirstMouseX,
+ pMouseDownWin->ImplGetFrameData()->mnFirstMouseY ) );
+
+ // create a UNO mouse event out of the available data
+ css::awt::MouseEvent aMouseEvent( static_cast < css::uno::XInterface * > ( nullptr ),
+#ifdef MACOSX
+ nCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3),
+#else
+ nCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2),
+#endif
+ nCode & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE),
+ nMouseX,
+ nMouseY,
+ nClicks,
+ false );
+
+ SolarMutexReleaser aReleaser;
+
+ // FIXME: where do I get Action from ?
+ css::uno::Reference< css::datatransfer::dnd::XDragSource > xDragSource = pMouseDownWin->GetDragSource();
+
+ if( xDragSource.is() )
+ {
+ static_cast < DNDListenerContainer * > ( xDragGestureRecognizer.get() )->fireDragGestureEvent( 0,
+ relLoc.X(), relLoc.Y(), xDragSource, css::uno::Any( aMouseEvent ) );
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ pMouseDownWin->ImplGetFrameData()->mbStartDragCalled = true;
+ }
+
+ if (xWindow->isDisposed())
+ return true;
+ // test for mouseleave and mouseenter
+ VclPtr<vcl::Window> pMouseMoveWin = pWinFrameData->mpMouseMoveWin;
+ if ( pChild != pMouseMoveWin )
+ {
+ if ( pMouseMoveWin )
+ {
+ Point aLeaveMousePos = pMouseMoveWin->ScreenToOutputPixel( aMousePos );
+ MouseEvent aMLeaveEvt( aLeaveMousePos, nClicks, nMode | MouseEventModifiers::LEAVEWINDOW, nCode, nCode );
+ NotifyEvent aNLeaveEvt( NotifyEventType::MOUSEMOVE, pMouseMoveWin, &aMLeaveEvt );
+ pWinFrameData->mbInMouseMove = true;
+ pMouseMoveWin->ImplGetWinData()->mbMouseOver = false;
+
+ // A MouseLeave can destroy this window
+ if ( !ImplCallPreNotify( aNLeaveEvt ) )
+ {
+ pMouseMoveWin->MouseMove( aMLeaveEvt );
+ if( !pMouseMoveWin->isDisposed() )
+ aNLeaveEvt.GetWindow()->ImplNotifyKeyMouseCommandEventListeners( aNLeaveEvt );
+ }
+
+ pWinFrameData->mpMouseMoveWin = nullptr;
+ pWinFrameData->mbInMouseMove = false;
+
+ if ( pChild && pChild->isDisposed() )
+ pChild = nullptr;
+ if ( pMouseMoveWin->isDisposed() )
+ return true;
+ }
+
+ nMode |= MouseEventModifiers::ENTERWINDOW;
+ }
+ pWinFrameData->mpMouseMoveWin = pChild;
+ if( pChild )
+ pChild->ImplGetWinData()->mbMouseOver = true;
+
+ // MouseLeave
+ if ( !pChild )
+ return false;
+ }
+ else
+ {
+ if (pChild)
+ {
+ // mouse click
+ if ( nSVEvent == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ const MouseSettings& rMSettings = pChild->GetSettings().GetMouseSettings();
+ sal_uInt64 nDblClkTime = rMSettings.GetDoubleClickTime();
+ tools::Long nDblClkW = rMSettings.GetDoubleClickWidth();
+ tools::Long nDblClkH = rMSettings.GetDoubleClickHeight();
+ //long nMouseX = nX;
+ //long nMouseY = nY;
+ tools::Long nMouseX = aMousePos.X(); // #106074# use the possibly re-mirrored coordinates (RTL) ! nX,nY are unmodified !
+ tools::Long nMouseY = aMousePos.Y();
+
+ if ( (pChild == pChild->ImplGetFrameData()->mpMouseDownWin) &&
+ (nCode == pChild->ImplGetFrameData()->mnFirstMouseCode) &&
+ ((nMsgTime-pChild->ImplGetFrameData()->mnMouseDownTime) < nDblClkTime) &&
+ ((nMouseX-nDblClkW) <= pChild->ImplGetFrameData()->mnFirstMouseX) &&
+ ((nMouseX+nDblClkW) >= pChild->ImplGetFrameData()->mnFirstMouseX) &&
+ ((nMouseY-nDblClkH) <= pChild->ImplGetFrameData()->mnFirstMouseY) &&
+ ((nMouseY+nDblClkH) >= pChild->ImplGetFrameData()->mnFirstMouseY) )
+ {
+ pChild->ImplGetFrameData()->mnClickCount++;
+ pChild->ImplGetFrameData()->mbStartDragCalled = true;
+ }
+ else
+ {
+ pChild->ImplGetFrameData()->mpMouseDownWin = pChild;
+ pChild->ImplGetFrameData()->mnClickCount = 1;
+ pChild->ImplGetFrameData()->mnFirstMouseX = nMouseX;
+ pChild->ImplGetFrameData()->mnFirstMouseY = nMouseY;
+ pChild->ImplGetFrameData()->mnFirstMouseCode = nCode;
+ pChild->ImplGetFrameData()->mbStartDragCalled = (nCode & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)) !=
+ (MouseSettings::GetStartDragCode() & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE));
+ }
+ pChild->ImplGetFrameData()->mnMouseDownTime = nMsgTime;
+ }
+ nClicks = pChild->ImplGetFrameData()->mnClickCount;
+ }
+
+ pSVData->maAppData.mnLastInputTime = tools::Time::GetSystemTicks();
+ }
+
+ SAL_WARN_IF( !pChild, "vcl", "ImplHandleMouseEvent: pChild == NULL" );
+
+ if (!pChild)
+ return false;
+
+ // create mouse event
+ Point aChildPos = pChild->ScreenToOutputPixel( aMousePos );
+ MouseEvent aMEvt( aChildPos, nClicks, nMode, nCode, nCode );
+
+
+ // tracking window gets the mouse events
+ if (pSVData->mpWinData->mpTrackWin)
+ pChild = pSVData->mpWinData->mpTrackWin;
+
+ // handle FloatingMode
+ if (!pSVData->mpWinData->mpTrackWin && pSVData->mpWinData->mpFirstFloat)
+ {
+ if ( ImplHandleMouseFloatMode( pChild, aMousePos, nCode, nSVEvent, bMouseLeave ) )
+ {
+ if ( !pChild->isDisposed() )
+ {
+ pChild->ImplGetFrameData()->mbStartDragCalled = true;
+ }
+ return true;
+ }
+ }
+
+ // call handler
+ bool bCallHelpRequest = true;
+ SAL_WARN_IF( !pChild, "vcl", "ImplHandleMouseEvent: pChild is NULL" );
+
+ if (!pChild)
+ return false;
+
+ NotifyEvent aNEvt( nSVEvent, pChild, &aMEvt );
+ if ( nSVEvent == NotifyEventType::MOUSEMOVE )
+ pChild->ImplGetFrameData()->mbInMouseMove = true;
+
+ // bring window into foreground on mouseclick
+ if ( nSVEvent == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ if (!pSVData->mpWinData->mpFirstFloat
+ && // totop for floating windows in popup would change the focus and would close them immediately
+ !(pChild->ImplGetFrameWindow()->GetStyle()
+ & WB_OWNERDRAWDECORATION)) // ownerdrawdecorated windows must never grab focus
+ pChild->ToTop();
+ if ( pChild->isDisposed() )
+ return true;
+ }
+
+ if ( ImplCallPreNotify( aNEvt ) || pChild->isDisposed() )
+ bRet = true;
+ else
+ {
+ bRet = false;
+ if ( nSVEvent == NotifyEventType::MOUSEMOVE )
+ {
+ if (pSVData->mpWinData->mpTrackWin)
+ {
+ TrackingEvent aTEvt( aMEvt );
+ pChild->Tracking( aTEvt );
+ if ( !pChild->isDisposed() )
+ {
+ // When ScrollRepeat, we restart the timer
+ if (pSVData->mpWinData->mpTrackTimer
+ && (pSVData->mpWinData->mnTrackFlags & StartTrackingFlags::ScrollRepeat))
+ pSVData->mpWinData->mpTrackTimer->Start();
+ }
+ bCallHelpRequest = false;
+ bRet = true;
+ }
+ else
+ {
+ if( pChild->isDisposed() )
+ bCallHelpRequest = false;
+ else
+ {
+ // if the MouseMove handler changes the help window's visibility
+ // the HelpRequest handler should not be called anymore
+ vcl::Window* pOldHelpTextWin = ImplGetSVHelpData().mpHelpWin;
+ pChild->MouseMove( aMEvt );
+ if ( pOldHelpTextWin != ImplGetSVHelpData().mpHelpWin )
+ bCallHelpRequest = false;
+ }
+ }
+ }
+ else if ( nSVEvent == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ if ( pSVData->mpWinData->mpTrackWin )
+ bRet = true;
+ else
+ {
+ pChild->ImplGetWindowImpl()->mbMouseButtonDown = false;
+ pChild->MouseButtonDown( aMEvt );
+ }
+ }
+ else
+ {
+ if (pSVData->mpWinData->mpTrackWin)
+ {
+ pChild->EndTracking();
+ bRet = true;
+ }
+ else
+ {
+ pChild->ImplGetWindowImpl()->mbMouseButtonUp = false;
+ pChild->MouseButtonUp( aMEvt );
+ }
+ }
+
+ assert(aNEvt.GetWindow() == pChild);
+
+ if (!pChild->isDisposed())
+ pChild->ImplNotifyKeyMouseCommandEventListeners( aNEvt );
+ }
+
+ if (pChild->isDisposed())
+ return true;
+
+ if ( nSVEvent == NotifyEventType::MOUSEMOVE )
+ pChild->ImplGetWindowImpl()->mpFrameData->mbInMouseMove = false;
+
+ if ( nSVEvent == NotifyEventType::MOUSEMOVE )
+ {
+ if ( bCallHelpRequest && !ImplGetSVHelpData().mbKeyboardHelp )
+ ImplHandleMouseHelpRequest( pChild, pChild->OutputToScreenPixel( aMEvt.GetPosPixel() ) );
+ bRet = true;
+ }
+ else if ( !bRet )
+ {
+ if ( nSVEvent == NotifyEventType::MOUSEBUTTONDOWN )
+ {
+ if ( !pChild->ImplGetWindowImpl()->mbMouseButtonDown )
+ bRet = true;
+ }
+ else
+ {
+ if ( !pChild->ImplGetWindowImpl()->mbMouseButtonUp )
+ bRet = true;
+ }
+ }
+
+ if ( nSVEvent == NotifyEventType::MOUSEMOVE )
+ {
+ // set new mouse pointer
+ if ( !bMouseLeave )
+ ImplSetMousePointer( pChild );
+ }
+ else if ( (nSVEvent == NotifyEventType::MOUSEBUTTONDOWN) || (nSVEvent == NotifyEventType::MOUSEBUTTONUP) )
+ {
+ // Command-Events
+ if ( /*!bRet &&*/ (nClicks == 1) && (nSVEvent == NotifyEventType::MOUSEBUTTONDOWN) &&
+ (nCode == MOUSE_MIDDLE) )
+ {
+ MouseMiddleButtonAction nMiddleAction = pChild->GetSettings().GetMouseSettings().GetMiddleButtonAction();
+ if ( nMiddleAction == MouseMiddleButtonAction::AutoScroll )
+ bRet = !ImplCallCommand( pChild, CommandEventId::StartAutoScroll, nullptr, true, &aChildPos );
+ else if ( nMiddleAction == MouseMiddleButtonAction::PasteSelection )
+ bRet = !ImplCallCommand( pChild, CommandEventId::PasteSelection, nullptr, true, &aChildPos );
+ }
+ else
+ {
+ // ContextMenu
+ if ( (nCode == MouseSettings::GetContextMenuCode()) &&
+ (nClicks == MouseSettings::GetContextMenuClicks()) )
+ {
+ bool bContextMenu = (nSVEvent == NotifyEventType::MOUSEBUTTONDOWN);
+ if ( bContextMenu )
+ {
+ if( pSVData->maAppData.mpActivePopupMenu )
+ {
+ /* #i34277# there already is a context menu open
+ * that was probably just closed with EndPopupMode.
+ * We need to give the eventual corresponding
+ * PopupMenu::Execute a chance to end properly.
+ * Therefore delay context menu command and
+ * issue only after popping one frame of the
+ * Yield stack.
+ */
+ ContextMenuEvent* pEv = new ContextMenuEvent;
+ pEv->pWindow = pChild;
+ pEv->aChildPos = aChildPos;
+ Application::PostUserEvent( Link<void*,void>( pEv, ContextMenuEventLink ) );
+ }
+ else
+ bRet = ! ImplCallCommand( pChild, CommandEventId::ContextMenu, nullptr, true, &aChildPos );
+ }
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool ImplLOKHandleMouseEvent(const VclPtr<vcl::Window>& xWindow, NotifyEventType nEvent, bool /*bMouseLeave*/,
+ tools::Long nX, tools::Long nY, sal_uInt64 /*nMsgTime*/,
+ sal_uInt16 nCode, MouseEventModifiers nMode, sal_uInt16 nClicks)
+{
+ Point aMousePos(nX, nY);
+
+ if (!xWindow)
+ return false;
+
+ if (xWindow->isDisposed())
+ return false;
+
+ ImplFrameData* pFrameData = xWindow->ImplGetFrameData();
+ if (!pFrameData)
+ return false;
+
+ Point aWinPos = xWindow->ScreenToOutputPixel(aMousePos);
+
+ pFrameData->mnLastMouseX = nX;
+ pFrameData->mnLastMouseY = nY;
+ pFrameData->mnClickCount = nClicks;
+ pFrameData->mnMouseCode = nCode;
+ pFrameData->mbMouseIn = false;
+
+ vcl::Window* pDragWin = pFrameData->mpMouseDownWin;
+ if (pDragWin &&
+ nEvent == NotifyEventType::MOUSEMOVE &&
+ pFrameData->mbDragging)
+ {
+ css::uno::Reference<css::datatransfer::dnd::XDropTargetDragContext> xDropTargetDragContext =
+ new GenericDropTargetDragContext();
+ css::uno::Reference<css::datatransfer::dnd::XDropTarget> xDropTarget(
+ pDragWin->ImplGetWindowImpl()->mxDNDListenerContainer, css::uno::UNO_QUERY);
+
+ if (!xDropTarget.is() ||
+ !xDropTargetDragContext.is() ||
+ (nCode & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)) !=
+ (MouseSettings::GetStartDragCode() & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)))
+ {
+ pFrameData->mbStartDragCalled = pFrameData->mbDragging = false;
+ return false;
+ }
+
+ static_cast<DNDListenerContainer *>(xDropTarget.get())->fireDragOverEvent(
+ xDropTargetDragContext,
+ css::datatransfer::dnd::DNDConstants::ACTION_MOVE,
+ aWinPos.X(),
+ aWinPos.Y(),
+ (css::datatransfer::dnd::DNDConstants::ACTION_COPY |
+ css::datatransfer::dnd::DNDConstants::ACTION_MOVE |
+ css::datatransfer::dnd::DNDConstants::ACTION_LINK));
+
+ return true;
+ }
+
+ if (pDragWin &&
+ nEvent == NotifyEventType::MOUSEBUTTONUP &&
+ pFrameData->mbDragging)
+ {
+ css::uno::Reference<css::datatransfer::XTransferable> xTransfer;
+ css::uno::Reference<css::datatransfer::dnd::XDropTargetDropContext> xDropTargetDropContext =
+ new GenericDropTargetDropContext();
+ css::uno::Reference<css::datatransfer::dnd::XDropTarget> xDropTarget(
+ pDragWin->ImplGetWindowImpl()->mxDNDListenerContainer, css::uno::UNO_QUERY);
+
+ if (!xDropTarget.is() || !xDropTargetDropContext.is())
+ {
+ pFrameData->mbStartDragCalled = pFrameData->mbDragging = false;
+ return false;
+ }
+
+ Point dragOverPos = pDragWin->ScreenToOutputPixel(aMousePos);
+ static_cast<DNDListenerContainer *>(xDropTarget.get())->fireDropEvent(
+ xDropTargetDropContext,
+ css::datatransfer::dnd::DNDConstants::ACTION_MOVE,
+ dragOverPos.X(),
+ dragOverPos.Y(),
+ (css::datatransfer::dnd::DNDConstants::ACTION_COPY |
+ css::datatransfer::dnd::DNDConstants::ACTION_MOVE |
+ css::datatransfer::dnd::DNDConstants::ACTION_LINK),
+ xTransfer);
+
+ pFrameData->mbStartDragCalled = pFrameData->mbDragging = false;
+ return true;
+ }
+
+ if (pFrameData->mbDragging)
+ {
+ // wrong status, reset
+ pFrameData->mbStartDragCalled = pFrameData->mbDragging = false;
+ return false;
+ }
+
+ vcl::Window* pDownWin = pFrameData->mpMouseDownWin;
+ if (pDownWin && nEvent == NotifyEventType::MOUSEMOVE)
+ {
+ const MouseSettings& aSettings = pDownWin->GetSettings().GetMouseSettings();
+ if ((nCode & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)) ==
+ (MouseSettings::GetStartDragCode() & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)) )
+ {
+ if (!pFrameData->mbStartDragCalled)
+ {
+ tools::Long nDragWidth = aSettings.GetStartDragWidth();
+ tools::Long nDragHeight = aSettings.GetStartDragHeight();
+ tools::Long nMouseX = aMousePos.X();
+ tools::Long nMouseY = aMousePos.Y();
+
+ if ((((nMouseX - nDragWidth) > pFrameData->mnFirstMouseX) ||
+ ((nMouseX + nDragWidth) < pFrameData->mnFirstMouseX)) ||
+ (((nMouseY - nDragHeight) > pFrameData->mnFirstMouseY) ||
+ ((nMouseY + nDragHeight) < pFrameData->mnFirstMouseY)))
+ {
+ pFrameData->mbStartDragCalled = true;
+
+ if (pFrameData->mbInternalDragGestureRecognizer)
+ {
+ // query DropTarget from child window
+ css::uno::Reference< css::datatransfer::dnd::XDragGestureRecognizer > xDragGestureRecognizer(
+ pDownWin->ImplGetWindowImpl()->mxDNDListenerContainer,
+ css::uno::UNO_QUERY );
+
+ if (xDragGestureRecognizer.is())
+ {
+ // create a UNO mouse event out of the available data
+ css::awt::MouseEvent aEvent(
+ static_cast < css::uno::XInterface * > ( nullptr ),
+ #ifdef MACOSX
+ nCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3),
+ #else
+ nCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2),
+ #endif
+ nCode & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE),
+ nMouseX,
+ nMouseY,
+ nClicks,
+ false);
+ css::uno::Reference< css::datatransfer::dnd::XDragSource > xDragSource =
+ pDownWin->GetDragSource();
+
+ if (xDragSource.is())
+ {
+ static_cast<DNDListenerContainer *>(xDragGestureRecognizer.get())->
+ fireDragGestureEvent(
+ 0,
+ aWinPos.X(),
+ aWinPos.Y(),
+ xDragSource,
+ css::uno::Any(aEvent));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ MouseEvent aMouseEvent(aWinPos, nClicks, nMode, nCode, nCode);
+ if (nEvent == NotifyEventType::MOUSEMOVE)
+ {
+ if (pFrameData->mpTrackWin)
+ {
+ TrackingEvent aTrackingEvent(aMouseEvent);
+ pFrameData->mpTrackWin->Tracking(aTrackingEvent);
+ }
+ else
+ xWindow->MouseMove(aMouseEvent);
+ }
+ else if (nEvent == NotifyEventType::MOUSEBUTTONDOWN &&
+ !pFrameData->mpTrackWin)
+ {
+ pFrameData->mpMouseDownWin = xWindow;
+ pFrameData->mnFirstMouseX = aMousePos.X();
+ pFrameData->mnFirstMouseY = aMousePos.Y();
+
+ xWindow->MouseButtonDown(aMouseEvent);
+ }
+ else
+ {
+ if (pFrameData->mpTrackWin)
+ {
+ pFrameData->mpTrackWin->EndTracking();
+ }
+
+ pFrameData->mpMouseDownWin = nullptr;
+ pFrameData->mpMouseMoveWin = nullptr;
+ pFrameData->mbStartDragCalled = false;
+ xWindow->MouseButtonUp(aMouseEvent);
+ }
+
+ if (nEvent == NotifyEventType::MOUSEBUTTONDOWN)
+ {
+ // ContextMenu
+ if ( (nCode == MouseSettings::GetContextMenuCode()) &&
+ (nClicks == MouseSettings::GetContextMenuClicks()) )
+ {
+ ImplCallCommand(xWindow, CommandEventId::ContextMenu, nullptr, true, &aWinPos);
+ }
+ }
+
+ return true;
+}
+
+static vcl::Window* ImplGetKeyInputWindow( vcl::Window* pWindow )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // determine last input time
+ pSVData->maAppData.mnLastInputTime = tools::Time::GetSystemTicks();
+
+ // #127104# workaround for destroyed windows
+ if( pWindow->ImplGetWindowImpl() == nullptr )
+ return nullptr;
+
+ // find window - is every time the window which has currently the
+ // focus or the last time the focus.
+
+ // the first floating window always has the focus, try it, or any parent floating windows, first
+ vcl::Window* pChild = pSVData->mpWinData->mpFirstFloat;
+ while (pChild)
+ {
+ if (pChild->ImplGetWindowImpl())
+ {
+ if (pChild->ImplGetWindowImpl()->mbFloatWin)
+ {
+ if (static_cast<FloatingWindow *>(pChild)->GrabsFocus())
+ break;
+ }
+ else if (pChild->ImplGetWindowImpl()->mbDockWin)
+ {
+ vcl::Window* pParent = pChild->GetWindow(GetWindowType::RealParent);
+ if (pParent && pParent->ImplGetWindowImpl()->mbFloatWin &&
+ static_cast<FloatingWindow *>(pParent)->GrabsFocus())
+ break;
+ }
+ }
+ pChild = pChild->GetParent();
+ }
+
+ if (!pChild)
+ pChild = pWindow;
+
+ pChild = pChild->ImplGetWindowImpl() && pChild->ImplGetWindowImpl()->mpFrameData ? pChild->ImplGetWindowImpl()->mpFrameData->mpFocusWin.get() : nullptr;
+
+ // no child - then no input
+ if ( !pChild )
+ return nullptr;
+
+ // We call also KeyInput if we haven't the focus, because on Unix
+ // system this is often the case when a Lookup Choice Window has
+ // the focus - because this windows send the KeyInput directly to
+ // the window without resetting the focus
+
+ // no keyinput to disabled windows
+ if ( !pChild->IsEnabled() || !pChild->IsInputEnabled() || pChild->IsInModalMode() )
+ return nullptr;
+
+ return pChild;
+}
+
+static bool ImplHandleKey( vcl::Window* pWindow, NotifyEventType nSVEvent,
+ sal_uInt16 nKeyCode, sal_uInt16 nCharCode, sal_uInt16 nRepeat, bool bForward )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::KeyCode aKeyCode( nKeyCode, nKeyCode );
+ sal_uInt16 nEvCode = aKeyCode.GetCode();
+
+ // allow application key listeners to remove the key event
+ // but make sure we're not forwarding external KeyEvents, (ie where bForward is false)
+ // because those are coming back from the listener itself and MUST be processed
+ if( bForward )
+ {
+ VclEventId nVCLEvent;
+ switch( nSVEvent )
+ {
+ case NotifyEventType::KEYINPUT:
+ nVCLEvent = VclEventId::WindowKeyInput;
+ break;
+ case NotifyEventType::KEYUP:
+ nVCLEvent = VclEventId::WindowKeyUp;
+ break;
+ default:
+ nVCLEvent = VclEventId::NONE;
+ break;
+ }
+ KeyEvent aKeyEvent(static_cast<sal_Unicode>(nCharCode), aKeyCode, nRepeat);
+ if (nVCLEvent != VclEventId::NONE && Application::HandleKey(nVCLEvent, pWindow, &aKeyEvent))
+ return true;
+ }
+
+ bool bCtrlF6 = (aKeyCode.GetCode() == KEY_F6) && aKeyCode.IsMod1();
+
+ // determine last input time
+ pSVData->maAppData.mnLastInputTime = tools::Time::GetSystemTicks();
+
+ // handle tracking window
+ if ( nSVEvent == NotifyEventType::KEYINPUT )
+ {
+ if ( ImplGetSVHelpData().mbExtHelpMode )
+ {
+ Help::EndExtHelp();
+ if ( nEvCode == KEY_ESCAPE )
+ return true;
+ }
+ if ( ImplGetSVHelpData().mpHelpWin )
+ ImplDestroyHelpWindow( false );
+
+ // AutoScrollMode
+ if (pSVData->mpWinData->mpAutoScrollWin)
+ {
+ pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll();
+ if ( nEvCode == KEY_ESCAPE )
+ return true;
+ }
+
+ if (pSVData->mpWinData->mpTrackWin)
+ {
+ sal_uInt16 nOrigCode = aKeyCode.GetCode();
+
+ if ( nOrigCode == KEY_ESCAPE )
+ {
+ pSVData->mpWinData->mpTrackWin->EndTracking( TrackingEventFlags::Cancel | TrackingEventFlags::Key );
+ if (pSVData->mpWinData->mpFirstFloat)
+ {
+ FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat();
+ if ( !(pLastLevelFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoKeyClose) )
+ {
+ sal_uInt16 nEscCode = aKeyCode.GetCode();
+
+ if ( nEscCode == KEY_ESCAPE )
+ pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
+ }
+ }
+ return true;
+ }
+ else if ( nOrigCode == KEY_RETURN )
+ {
+ pSVData->mpWinData->mpTrackWin->EndTracking( TrackingEventFlags::Key );
+ return true;
+ }
+ else
+ return true;
+ }
+
+ // handle FloatingMode
+ if (pSVData->mpWinData->mpFirstFloat)
+ {
+ FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat();
+ if ( !(pLastLevelFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoKeyClose) )
+ {
+ sal_uInt16 nCode = aKeyCode.GetCode();
+
+ if ( (nCode == KEY_ESCAPE) || bCtrlF6)
+ {
+ pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
+ if( !bCtrlF6 )
+ return true;
+ }
+ }
+ }
+
+ // test for accel
+ if ( pSVData->maAppData.mpAccelMgr )
+ {
+ if ( pSVData->maAppData.mpAccelMgr->IsAccelKey( aKeyCode ) )
+ return true;
+ }
+ }
+
+ // find window
+ VclPtr<vcl::Window> pChild = ImplGetKeyInputWindow( pWindow );
+ if ( !pChild )
+ return false;
+
+ // #i1820# use locale specific decimal separator
+ if (nEvCode == KEY_DECIMAL)
+ {
+ // tdf#138932: don't modify the meaning of the key for password box
+ bool bPass = false;
+ if (auto pEdit = dynamic_cast<Edit*>(pChild.get()))
+ bPass = pEdit->IsPassword();
+ if (!bPass && Application::GetSettings().GetMiscSettings().GetEnableLocalizedDecimalSep())
+ {
+ OUString aSep(pWindow->GetSettings().GetLocaleDataWrapper().getNumDecimalSep());
+ nCharCode = static_cast<sal_uInt16>(aSep[0]);
+ }
+ }
+
+ // RTL: mirror cursor keys
+ if( (aKeyCode.GetCode() == KEY_LEFT || aKeyCode.GetCode() == KEY_RIGHT) &&
+ pChild->IsRTLEnabled() && pChild->GetOutDev()->HasMirroredGraphics() )
+ aKeyCode = vcl::KeyCode( aKeyCode.GetCode() == KEY_LEFT ? KEY_RIGHT : KEY_LEFT, aKeyCode.GetModifier() );
+
+ KeyEvent aKeyEvt( static_cast<sal_Unicode>(nCharCode), aKeyCode, nRepeat );
+ NotifyEvent aNotifyEvt( nSVEvent, pChild, &aKeyEvt );
+ bool bKeyPreNotify = ImplCallPreNotify( aNotifyEvt );
+ bool bRet = true;
+
+ if ( !bKeyPreNotify && !pChild->isDisposed() )
+ {
+ if ( nSVEvent == NotifyEventType::KEYINPUT )
+ {
+ UITestLogger::getInstance().logKeyInput(pChild, aKeyEvt);
+ pChild->ImplGetWindowImpl()->mbKeyInput = false;
+ pChild->KeyInput( aKeyEvt );
+ }
+ else
+ {
+ pChild->ImplGetWindowImpl()->mbKeyUp = false;
+ pChild->KeyUp( aKeyEvt );
+ }
+ if( !pChild->isDisposed() )
+ aNotifyEvt.GetWindow()->ImplNotifyKeyMouseCommandEventListeners( aNotifyEvt );
+ }
+
+ if ( pChild->isDisposed() )
+ return true;
+
+ if ( nSVEvent == NotifyEventType::KEYINPUT )
+ {
+ if ( !bKeyPreNotify && pChild->ImplGetWindowImpl()->mbKeyInput )
+ {
+ sal_uInt16 nCode = aKeyCode.GetCode();
+
+ // #101999# is focus in or below toolbox
+ bool bToolboxFocus=false;
+ if( (nCode == KEY_F1) && aKeyCode.IsShift() )
+ {
+ vcl::Window *pWin = pWindow->ImplGetWindowImpl()->mpFrameData->mpFocusWin;
+ while( pWin )
+ {
+ if( pWin->ImplGetWindowImpl()->mbToolBox )
+ {
+ bToolboxFocus = true;
+ break;
+ }
+ else
+ pWin = pWin->GetParent();
+ }
+ }
+
+ // ContextMenu
+ if ( (nCode == KEY_CONTEXTMENU) || ((nCode == KEY_F10) && aKeyCode.IsShift() && !aKeyCode.IsMod1() && !aKeyCode.IsMod2() ) )
+ bRet = !ImplCallCommand( pChild, CommandEventId::ContextMenu );
+ else if ( ( (nCode == KEY_F2) && aKeyCode.IsShift() ) || ( (nCode == KEY_F1) && aKeyCode.IsMod1() ) ||
+ // #101999# no active help when focus in toolbox, simulate BalloonHelp instead
+ ( (nCode == KEY_F1) && aKeyCode.IsShift() && bToolboxFocus ) )
+ {
+ // TipHelp via Keyboard (Shift-F2 or Ctrl-F1)
+ // simulate mouseposition at center of window
+
+ Size aSize = pChild->GetOutDev()->GetOutputSize();
+ Point aPos( aSize.getWidth()/2, aSize.getHeight()/2 );
+ aPos = pChild->OutputToScreenPixel( aPos );
+
+ HelpEvent aHelpEvent( aPos, HelpEventMode::BALLOON );
+ aHelpEvent.SetKeyboardActivated( true );
+ ImplGetSVHelpData().mbSetKeyboardHelp = true;
+ pChild->RequestHelp( aHelpEvent );
+ ImplGetSVHelpData().mbSetKeyboardHelp = false;
+ }
+ else if ( (nCode == KEY_F1) || (nCode == KEY_HELP) )
+ {
+ if ( !aKeyCode.GetModifier() )
+ {
+ if ( ImplGetSVHelpData().mbContextHelp )
+ {
+ Point aMousePos = pChild->OutputToScreenPixel( pChild->GetPointerPosPixel() );
+ HelpEvent aHelpEvent( aMousePos, HelpEventMode::CONTEXT );
+ pChild->RequestHelp( aHelpEvent );
+ }
+ else
+ bRet = false;
+ }
+ else if ( aKeyCode.IsShift() )
+ {
+ if ( ImplGetSVHelpData().mbExtHelp )
+ Help::StartExtHelp();
+ else
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ }
+ }
+ else
+ {
+ if ( !bKeyPreNotify && pChild->ImplGetWindowImpl()->mbKeyUp )
+ bRet = false;
+ }
+
+ // #105591# send keyinput to parent if we are a floating window and the key was not processed yet
+ if( !bRet && pWindow->ImplGetWindowImpl() && pWindow->ImplGetWindowImpl()->mbFloatWin && pWindow->GetParent() && (pWindow->ImplGetWindowImpl()->mpFrame != pWindow->GetParent()->ImplGetWindowImpl()->mpFrame) )
+ {
+ pChild = pWindow->GetParent();
+
+ // call handler
+ NotifyEvent aNEvt( nSVEvent, pChild, &aKeyEvt );
+ bool bPreNotify = ImplCallPreNotify( aNEvt );
+ if ( pChild->isDisposed() )
+ return true;
+
+ if ( !bPreNotify )
+ {
+ if ( nSVEvent == NotifyEventType::KEYINPUT )
+ {
+ pChild->ImplGetWindowImpl()->mbKeyInput = false;
+ pChild->KeyInput( aKeyEvt );
+ }
+ else
+ {
+ pChild->ImplGetWindowImpl()->mbKeyUp = false;
+ pChild->KeyUp( aKeyEvt );
+ }
+
+ if( !pChild->isDisposed() )
+ aNEvt.GetWindow()->ImplNotifyKeyMouseCommandEventListeners( aNEvt );
+ if ( pChild->isDisposed() )
+ return true;
+ }
+
+ if( bPreNotify || !pChild->ImplGetWindowImpl()->mbKeyInput )
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+static bool ImplHandleExtTextInput( vcl::Window* pWindow,
+ const OUString& rText,
+ const ExtTextInputAttr* pTextAttr,
+ sal_Int32 nCursorPos, sal_uInt16 nCursorFlags )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::Window* pChild = nullptr;
+
+ int nTries = 200;
+ while( nTries-- )
+ {
+ pChild = pSVData->mpWinData->mpExtTextInputWin;
+ if ( !pChild )
+ {
+ pChild = ImplGetKeyInputWindow( pWindow );
+ if ( !pChild )
+ return false;
+ }
+ if( !pChild->ImplGetWindowImpl()->mpFrameData->mnFocusId )
+ break;
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SAL_WARN("vcl", "Failed to get ext text input context");
+ break;
+ }
+ Application::Yield();
+ }
+
+ // If it is the first ExtTextInput call, we inform the information
+ // and allocate the data, which we must store in this mode
+ ImplWinData* pWinData = pChild->ImplGetWinData();
+ if ( !pChild->ImplGetWindowImpl()->mbExtTextInput )
+ {
+ pChild->ImplGetWindowImpl()->mbExtTextInput = true;
+ pWinData->mpExtOldText = OUString();
+ pWinData->mpExtOldAttrAry.reset();
+ pSVData->mpWinData->mpExtTextInputWin = pChild;
+ ImplCallCommand( pChild, CommandEventId::StartExtTextInput );
+ }
+
+ // be aware of being recursively called in StartExtTextInput
+ if ( !pChild->ImplGetWindowImpl()->mbExtTextInput )
+ return false;
+
+ // Test for changes
+ bool bOnlyCursor = false;
+ sal_Int32 nMinLen = std::min( pWinData->mpExtOldText->getLength(), rText.getLength() );
+ sal_Int32 nDeltaStart = 0;
+ while ( nDeltaStart < nMinLen )
+ {
+ if ( (*pWinData->mpExtOldText)[nDeltaStart] != rText[nDeltaStart] )
+ break;
+ nDeltaStart++;
+ }
+ if ( pWinData->mpExtOldAttrAry || pTextAttr )
+ {
+ if ( !pWinData->mpExtOldAttrAry || !pTextAttr )
+ nDeltaStart = 0;
+ else
+ {
+ sal_Int32 i = 0;
+ while ( i < nDeltaStart )
+ {
+ if ( pWinData->mpExtOldAttrAry[i] != pTextAttr[i] )
+ {
+ nDeltaStart = i;
+ break;
+ }
+ i++;
+ }
+ }
+ }
+ if ( (nDeltaStart >= nMinLen) &&
+ (pWinData->mpExtOldText->getLength() == rText.getLength()) )
+ bOnlyCursor = true;
+
+ // Call Event and store the information
+ CommandExtTextInputData aData( rText, pTextAttr,
+ nCursorPos, nCursorFlags,
+ bOnlyCursor );
+ *pWinData->mpExtOldText = rText;
+ pWinData->mpExtOldAttrAry.reset();
+ if ( pTextAttr )
+ {
+ pWinData->mpExtOldAttrAry.reset( new ExtTextInputAttr[rText.getLength()] );
+ memcpy( pWinData->mpExtOldAttrAry.get(), pTextAttr, rText.getLength()*sizeof( ExtTextInputAttr ) );
+ }
+ return !ImplCallCommand( pChild, CommandEventId::ExtTextInput, &aData );
+}
+
+static bool ImplHandleEndExtTextInput()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::Window* pChild = pSVData->mpWinData->mpExtTextInputWin;
+ bool bRet = false;
+
+ if ( pChild )
+ {
+ pChild->ImplGetWindowImpl()->mbExtTextInput = false;
+ pSVData->mpWinData->mpExtTextInputWin = nullptr;
+ ImplWinData* pWinData = pChild->ImplGetWinData();
+ pWinData->mpExtOldText.reset();
+ pWinData->mpExtOldAttrAry.reset();
+ bRet = !ImplCallCommand( pChild, CommandEventId::EndExtTextInput );
+ }
+
+ return bRet;
+}
+
+static void ImplHandleExtTextInputPos( vcl::Window* pWindow,
+ tools::Rectangle& rRect, tools::Long& rInputWidth,
+ bool * pVertical )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::Window* pChild = pSVData->mpWinData->mpExtTextInputWin;
+
+ if ( !pChild )
+ pChild = ImplGetKeyInputWindow( pWindow );
+ else
+ {
+ // Test, if the Window is related to the frame
+ if ( !pWindow->ImplIsWindowOrChild( pChild ) )
+ pChild = ImplGetKeyInputWindow( pWindow );
+ }
+
+ if ( pChild )
+ {
+ const OutputDevice *pChildOutDev = pChild->GetOutDev();
+ ImplCallCommand( pChild, CommandEventId::CursorPos );
+ const tools::Rectangle* pRect = pChild->GetCursorRect();
+ if ( pRect )
+ rRect = pChildOutDev->ImplLogicToDevicePixel( *pRect );
+ else
+ {
+ vcl::Cursor* pCursor = pChild->GetCursor();
+ if ( pCursor )
+ {
+ Point aPos = pChildOutDev->ImplLogicToDevicePixel( pCursor->GetPos() );
+ Size aSize = pChild->LogicToPixel( pCursor->GetSize() );
+ if ( !aSize.Width() )
+ aSize.setWidth( pChild->GetSettings().GetStyleSettings().GetCursorSize() );
+ rRect = tools::Rectangle( aPos, aSize );
+ }
+ else
+ rRect = tools::Rectangle( Point( pChild->GetOutOffXPixel(), pChild->GetOutOffYPixel() ), Size() );
+ }
+ rInputWidth = pChild->GetOutDev()->ImplLogicWidthToDevicePixel( pChild->GetCursorExtTextInputWidth() );
+ if ( !rInputWidth )
+ rInputWidth = rRect.GetWidth();
+ }
+ if (pVertical != nullptr)
+ *pVertical
+ = pChild != nullptr && pChild->GetInputContext().GetFont().IsVertical();
+}
+
+static bool ImplHandleInputContextChange( vcl::Window* pWindow )
+{
+ vcl::Window* pChild = ImplGetKeyInputWindow( pWindow );
+ CommandInputContextData aData;
+ return !ImplCallCommand( pChild, CommandEventId::InputContextChange, &aData );
+}
+
+static bool ImplCallWheelCommand( const VclPtr<vcl::Window>& pWindow, const Point& rPos,
+ const CommandWheelData* pWheelData )
+{
+ Point aCmdMousePos = pWindow->ScreenToOutputPixel( rPos );
+ CommandEvent aCEvt( aCmdMousePos, CommandEventId::Wheel, true, pWheelData );
+ NotifyEvent aNCmdEvt( NotifyEventType::COMMAND, pWindow, &aCEvt );
+ bool bPreNotify = ImplCallPreNotify( aNCmdEvt );
+ if ( pWindow->isDisposed() )
+ return false;
+ if ( !bPreNotify )
+ {
+ pWindow->ImplGetWindowImpl()->mbCommand = false;
+ pWindow->Command( aCEvt );
+ if ( pWindow->isDisposed() )
+ return false;
+ if ( pWindow->ImplGetWindowImpl()->mbCommand )
+ return true;
+ }
+ return false;
+}
+
+static bool acceptableWheelScrollTarget(const vcl::Window *pMouseWindow)
+{
+ return (pMouseWindow && !pMouseWindow->isDisposed() && pMouseWindow->IsInputEnabled() && !pMouseWindow->IsInModalMode());
+}
+
+//If the last event at the same absolute screen position was handled by a
+//different window then reuse that window if the event occurs within 1/2 a
+//second, i.e. so scrolling down something like the calc sidebar that contains
+//widgets that respond to wheel events will continue to send the event to the
+//scrolling widget in favour of the widget that happens to end up under the
+//mouse.
+static bool shouldReusePreviousMouseWindow(const SalWheelMouseEvent& rPrevEvt, const SalWheelMouseEvent& rEvt)
+{
+ return (rEvt.mnX == rPrevEvt.mnX && rEvt.mnY == rPrevEvt.mnY && rEvt.mnTime-rPrevEvt.mnTime < 500/*ms*/);
+}
+
+namespace {
+
+class HandleGestureEventBase
+{
+protected:
+ ImplSVData* m_pSVData;
+ VclPtr<vcl::Window> m_pWindow;
+ Point m_aMousePos;
+
+public:
+ HandleGestureEventBase(vcl::Window *pWindow, const Point &rMousePos)
+ : m_pSVData(ImplGetSVData())
+ , m_pWindow(pWindow)
+ , m_aMousePos(rMousePos)
+ {
+ }
+ bool Setup();
+ vcl::Window* FindTarget();
+ vcl::Window* Dispatch(vcl::Window* pTarget);
+ virtual bool CallCommand(vcl::Window *pWindow, const Point &rMousePos) = 0;
+ virtual ~HandleGestureEventBase() {}
+};
+
+}
+
+bool HandleGestureEventBase::Setup()
+{
+
+ if (m_pSVData->mpWinData->mpAutoScrollWin)
+ m_pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll();
+ if (ImplGetSVHelpData().mpHelpWin)
+ ImplDestroyHelpWindow( true );
+ return !m_pWindow->isDisposed();
+}
+
+vcl::Window* HandleGestureEventBase::FindTarget()
+{
+ // first check any floating window ( eg. drop down listboxes)
+ vcl::Window *pMouseWindow = nullptr;
+
+ if (m_pSVData->mpWinData->mpFirstFloat && !m_pSVData->mpWinData->mpCaptureWin &&
+ !m_pSVData->mpWinData->mpFirstFloat->ImplIsFloatPopupModeWindow( m_pWindow ) )
+ {
+ bool bHitTestInsideRect = false;
+ pMouseWindow = m_pSVData->mpWinData->mpFirstFloat->ImplFloatHitTest( m_pWindow, m_aMousePos, bHitTestInsideRect );
+ if (!pMouseWindow)
+ pMouseWindow = m_pSVData->mpWinData->mpFirstFloat;
+ }
+ // then try the window directly beneath the mouse
+ if( !pMouseWindow )
+ {
+ pMouseWindow = m_pWindow->ImplFindWindow( m_aMousePos );
+ }
+ else
+ {
+ // transform coordinates to float window frame coordinates
+ pMouseWindow = pMouseWindow->ImplFindWindow(
+ pMouseWindow->OutputToScreenPixel(
+ pMouseWindow->AbsoluteScreenToOutputPixel(
+ m_pWindow->OutputToAbsoluteScreenPixel(
+ m_pWindow->ScreenToOutputPixel( m_aMousePos ) ) ) ) );
+ }
+
+ while (acceptableWheelScrollTarget(pMouseWindow))
+ {
+ if (pMouseWindow->IsEnabled())
+ break;
+ //try the parent if this one is disabled
+ pMouseWindow = pMouseWindow->GetParent();
+ }
+
+ return pMouseWindow;
+}
+
+vcl::Window *HandleGestureEventBase::Dispatch(vcl::Window* pMouseWindow)
+{
+ vcl::Window *pDispatchedTo = nullptr;
+
+ if (acceptableWheelScrollTarget(pMouseWindow) && pMouseWindow->IsEnabled())
+ {
+ // transform coordinates to float window frame coordinates
+ Point aRelMousePos( pMouseWindow->OutputToScreenPixel(
+ pMouseWindow->AbsoluteScreenToOutputPixel(
+ m_pWindow->OutputToAbsoluteScreenPixel(
+ m_pWindow->ScreenToOutputPixel( m_aMousePos ) ) ) ) );
+ bool bPropagate = CallCommand(pMouseWindow, aRelMousePos);
+ if (!bPropagate)
+ pDispatchedTo = pMouseWindow;
+ }
+
+ // if the command was not handled try the focus window
+ if (!pDispatchedTo)
+ {
+ vcl::Window* pFocusWindow = m_pWindow->ImplGetWindowImpl()->mpFrameData->mpFocusWin;
+ if ( pFocusWindow && (pFocusWindow != pMouseWindow) &&
+ (pFocusWindow == m_pSVData->mpWinData->mpFocusWin) )
+ {
+ // no wheel-messages to disabled windows
+ if ( pFocusWindow->IsEnabled() && pFocusWindow->IsInputEnabled() && ! pFocusWindow->IsInModalMode() )
+ {
+ // transform coordinates to focus window frame coordinates
+ Point aRelMousePos( pFocusWindow->OutputToScreenPixel(
+ pFocusWindow->AbsoluteScreenToOutputPixel(
+ m_pWindow->OutputToAbsoluteScreenPixel(
+ m_pWindow->ScreenToOutputPixel( m_aMousePos ) ) ) ) );
+ bool bPropagate = CallCommand(pFocusWindow, aRelMousePos);
+ if (!bPropagate)
+ pDispatchedTo = pMouseWindow;
+ }
+ }
+ }
+ return pDispatchedTo;
+}
+
+namespace {
+
+class HandleWheelEvent : public HandleGestureEventBase
+{
+private:
+ CommandWheelData m_aWheelData;
+public:
+ HandleWheelEvent(vcl::Window *pWindow, const SalWheelMouseEvent& rEvt)
+ : HandleGestureEventBase(pWindow, Point(rEvt.mnX, rEvt.mnY))
+ {
+ CommandWheelMode nMode;
+ sal_uInt16 nCode = rEvt.mnCode;
+ bool bHorz = rEvt.mbHorz;
+ bool bPixel = rEvt.mbDeltaIsPixel;
+ if ( nCode & KEY_MOD1 )
+ nMode = CommandWheelMode::ZOOM;
+ else if ( nCode & KEY_MOD2 )
+ nMode = CommandWheelMode::DATAZOOM;
+ else
+ {
+ nMode = CommandWheelMode::SCROLL;
+ // #i85450# interpret shift-wheel as horizontal wheel action
+ if( (nCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3)) == KEY_SHIFT )
+ bHorz = true;
+ }
+
+ m_aWheelData = CommandWheelData(rEvt.mnDelta, rEvt.mnNotchDelta, rEvt.mnScrollLines, nMode, nCode, bHorz, bPixel);
+
+ }
+ virtual bool CallCommand(vcl::Window *pWindow, const Point &rMousePos) override
+ {
+ return ImplCallWheelCommand(pWindow, rMousePos, &m_aWheelData);
+ }
+ bool HandleEvent(const SalWheelMouseEvent& rEvt);
+};
+
+}
+
+bool HandleWheelEvent::HandleEvent(const SalWheelMouseEvent& rEvt)
+{
+ if (!Setup())
+ return false;
+
+ VclPtr<vcl::Window> xMouseWindow = FindTarget();
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // avoid the problem that scrolling via wheel to this point brings a widget
+ // under the mouse that also accepts wheel commands, so stick with the old
+ // widget if the time gap is very small
+ if (shouldReusePreviousMouseWindow(pSVData->mpWinData->maLastWheelEvent, rEvt) &&
+ acceptableWheelScrollTarget(pSVData->mpWinData->mpLastWheelWindow))
+ {
+ xMouseWindow = pSVData->mpWinData->mpLastWheelWindow;
+ }
+
+ pSVData->mpWinData->maLastWheelEvent = rEvt;
+
+ pSVData->mpWinData->mpLastWheelWindow = Dispatch(xMouseWindow);
+
+ return pSVData->mpWinData->mpLastWheelWindow;
+}
+
+namespace {
+
+class HandleGestureEvent : public HandleGestureEventBase
+{
+public:
+ HandleGestureEvent(vcl::Window *pWindow, const Point &rMousePos)
+ : HandleGestureEventBase(pWindow, rMousePos)
+ {
+ }
+ bool HandleEvent();
+};
+
+}
+
+bool HandleGestureEvent::HandleEvent()
+{
+ if (!Setup())
+ return false;
+
+ vcl::Window *pTarget = FindTarget();
+
+ bool bHandled = Dispatch(pTarget) != nullptr;
+ return bHandled;
+}
+
+static bool ImplHandleWheelEvent(vcl::Window* pWindow, const SalWheelMouseEvent& rEvt)
+{
+ HandleWheelEvent aHandler(pWindow, rEvt);
+ return aHandler.HandleEvent(rEvt);
+}
+
+namespace {
+
+class HandleGestureSwipeEvent : public HandleGestureEvent
+{
+private:
+ CommandGestureSwipeData m_aSwipeData;
+public:
+ HandleGestureSwipeEvent(vcl::Window *pWindow, const SalGestureSwipeEvent& rEvt)
+ : HandleGestureEvent(pWindow, Point(rEvt.mnX, rEvt.mnY)),
+ m_aSwipeData(rEvt.mnVelocityX)
+ {
+ }
+ virtual bool CallCommand(vcl::Window *pWindow, const Point &/*rMousePos*/) override
+ {
+ return ImplCallCommand(pWindow, CommandEventId::GestureSwipe, &m_aSwipeData);
+ }
+};
+
+}
+
+static bool ImplHandleSwipe(vcl::Window *pWindow, const SalGestureSwipeEvent& rEvt)
+{
+ HandleGestureSwipeEvent aHandler(pWindow, rEvt);
+ return aHandler.HandleEvent();
+}
+
+namespace {
+
+class HandleGestureLongPressEvent : public HandleGestureEvent
+{
+private:
+ CommandGestureLongPressData m_aLongPressData;
+public:
+ HandleGestureLongPressEvent(vcl::Window *pWindow, const SalGestureLongPressEvent& rEvt)
+ : HandleGestureEvent(pWindow, Point(rEvt.mnX, rEvt.mnY)),
+ m_aLongPressData(rEvt.mnX, rEvt.mnY)
+ {
+ }
+ virtual bool CallCommand(vcl::Window *pWindow, const Point &/*rMousePos*/) override
+ {
+ return ImplCallCommand(pWindow, CommandEventId::GestureLongPress, &m_aLongPressData);
+ }
+};
+
+}
+
+static bool ImplHandleLongPress(vcl::Window *pWindow, const SalGestureLongPressEvent& rEvt)
+{
+ HandleGestureLongPressEvent aHandler(pWindow, rEvt);
+ return aHandler.HandleEvent();
+}
+
+namespace {
+
+class HandleGesturePanEvent : public HandleGestureEvent
+{
+private:
+ CommandGesturePanData m_aGestureData;
+
+public:
+ HandleGesturePanEvent(vcl::Window* pWindow, const SalGestureEvent& rEvent)
+ : HandleGestureEvent(pWindow, Point(rEvent.mnX, rEvent.mnY))
+ , m_aGestureData(rEvent.mnX, rEvent.mnY, rEvent.meEventType, rEvent.mfOffset, rEvent.meOrientation)
+ {
+ }
+
+ virtual bool CallCommand(vcl::Window* pWindow, const Point& /*rMousePos*/) override
+ {
+ return ImplCallCommand(pWindow, CommandEventId::GesturePan, &m_aGestureData);
+ }
+};
+
+}
+
+static bool ImplHandleGestureEvent(vcl::Window* pWindow, const SalGestureEvent& rEvent)
+{
+ HandleGesturePanEvent aHandler(pWindow, rEvent);
+ return aHandler.HandleEvent();
+}
+
+namespace {
+
+class HandleGestureZoomEvent : public HandleGestureEvent
+{
+private:
+ CommandGestureZoomData m_aGestureData;
+
+public:
+ HandleGestureZoomEvent(vcl::Window* pWindow, const SalGestureZoomEvent& rEvent)
+ : HandleGestureEvent(pWindow, Point(rEvent.mnX, rEvent.mnY))
+ , m_aGestureData(rEvent.mnX, rEvent.mnY, rEvent.meEventType, rEvent.mfScaleDelta)
+ {
+ }
+
+ virtual bool CallCommand(vcl::Window* pWindow, const Point& /*rMousePos*/) override
+ {
+ return ImplCallCommand(pWindow, CommandEventId::GestureZoom, &m_aGestureData);
+ }
+};
+
+}
+
+static bool ImplHandleGestureZoomEvent(vcl::Window* pWindow, const SalGestureZoomEvent& rEvent)
+{
+ HandleGestureZoomEvent aHandler(pWindow, rEvent);
+ return aHandler.HandleEvent();
+}
+
+namespace {
+
+class HandleGestureRotateEvent : public HandleGestureEvent
+{
+private:
+ CommandGestureRotateData m_aGestureData;
+
+public:
+ HandleGestureRotateEvent(vcl::Window* pWindow, const SalGestureRotateEvent& rEvent)
+ : HandleGestureEvent(pWindow, Point(rEvent.mnX, rEvent.mnY))
+ , m_aGestureData(rEvent.mnX, rEvent.mnY, rEvent.meEventType, rEvent.mfAngleDelta)
+ {
+ }
+
+ virtual bool CallCommand(vcl::Window* pWindow, const Point& /*rMousePos*/) override
+ {
+ return ImplCallCommand(pWindow, CommandEventId::GestureRotate, &m_aGestureData);
+ }
+};
+
+}
+
+static bool ImplHandleGestureRotateEvent(vcl::Window* pWindow, const SalGestureRotateEvent& rEvent)
+{
+ HandleGestureRotateEvent aHandler(pWindow, rEvent);
+ return aHandler.HandleEvent();
+}
+
+static void ImplHandlePaint( vcl::Window* pWindow, const tools::Rectangle& rBoundRect, bool bImmediateUpdate )
+{
+ // system paint events must be checked for re-mirroring
+ pWindow->ImplGetWindowImpl()->mnPaintFlags |= ImplPaintFlags::CheckRtl;
+
+ // trigger paint for all windows that live in the new paint region
+ vcl::Region aRegion( rBoundRect );
+ pWindow->ImplInvalidateOverlapFrameRegion( aRegion );
+ if( bImmediateUpdate )
+ {
+ // #i87663# trigger possible pending resize notifications
+ // (GetSizePixel does that for us)
+ pWindow->GetSizePixel();
+ // force drawing immediately
+ pWindow->PaintImmediately();
+ }
+}
+
+static void KillOwnPopups( vcl::Window const * pWindow )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::Window *pParent = pWindow->ImplGetWindowImpl()->mpFrameWindow;
+ vcl::Window *pChild = pSVData->mpWinData->mpFirstFloat;
+ if ( pChild && pParent->ImplIsWindowOrChild( pChild, true ) )
+ {
+ if (!(pSVData->mpWinData->mpFirstFloat->GetPopupModeFlags()
+ & FloatWinPopupFlags::NoAppFocusClose))
+ pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel
+ | FloatWinPopupEndFlags::CloseAll);
+ }
+}
+
+void ImplHandleResize( vcl::Window* pWindow, tools::Long nNewWidth, tools::Long nNewHeight )
+{
+ const bool bChanged = (nNewWidth != pWindow->GetOutputSizePixel().Width()) || (nNewHeight != pWindow->GetOutDev()->GetOutputHeightPixel());
+ if (bChanged && pWindow->GetStyle() & (WB_MOVEABLE|WB_SIZEABLE))
+ {
+ KillOwnPopups( pWindow );
+ if( pWindow->ImplGetWindow() != ImplGetSVHelpData().mpHelpWin )
+ ImplDestroyHelpWindow( true );
+ }
+
+ if (
+ (nNewWidth > 0 && nNewHeight > 0) ||
+ pWindow->ImplGetWindow()->ImplGetWindowImpl()->mbAllResize
+ )
+ {
+ if (bChanged)
+ {
+ pWindow->GetOutDev()->mnOutWidth = nNewWidth;
+ pWindow->GetOutDev()->mnOutHeight = nNewHeight;
+ pWindow->ImplGetWindowImpl()->mbWaitSystemResize = false;
+ if ( pWindow->IsReallyVisible() )
+ pWindow->ImplSetClipFlag();
+ if ( pWindow->IsVisible() || pWindow->ImplGetWindow()->ImplGetWindowImpl()->mbAllResize ||
+ ( pWindow->ImplGetWindowImpl()->mbFrame && pWindow->ImplGetWindowImpl()->mpClientWindow ) ) // propagate resize for system border windows
+ {
+ bool bStartTimer = true;
+ // use resize buffering for user resizes
+ // ownerdraw decorated windows and floating windows can be resized immediately (i.e. synchronously)
+ if( pWindow->ImplGetWindowImpl()->mbFrame && (pWindow->GetStyle() & WB_SIZEABLE)
+ && !(pWindow->GetStyle() & WB_OWNERDRAWDECORATION) // synchronous resize for ownerdraw decorated windows (toolbars)
+ && !pWindow->ImplGetWindowImpl()->mbFloatWin ) // synchronous resize for floating windows, #i43799#
+ {
+ if( pWindow->ImplGetWindowImpl()->mpClientWindow )
+ {
+ // #i42750# presentation wants to be informed about resize
+ // as early as possible
+ WorkWindow* pWorkWindow = dynamic_cast<WorkWindow*>(pWindow->ImplGetWindowImpl()->mpClientWindow.get());
+ if( ! pWorkWindow || pWorkWindow->IsPresentationMode() )
+ bStartTimer = false;
+ }
+ else
+ {
+ WorkWindow* pWorkWindow = dynamic_cast<WorkWindow*>(pWindow);
+ if( ! pWorkWindow || pWorkWindow->IsPresentationMode() )
+ bStartTimer = false;
+ }
+ }
+ else
+ bStartTimer = false;
+
+ if( bStartTimer )
+ pWindow->ImplGetWindowImpl()->mpFrameData->maResizeIdle.Start();
+ else
+ pWindow->ImplCallResize(); // otherwise menus cannot be positioned
+ }
+ else
+ pWindow->ImplGetWindowImpl()->mbCallResize = true;
+
+ if (pWindow->SupportsDoubleBuffering() && pWindow->ImplGetWindowImpl()->mbFrame)
+ {
+ // Propagate resize for the frame's buffer.
+ pWindow->ImplGetWindowImpl()->mpFrameData->mpBuffer->SetOutputSizePixel(pWindow->GetOutputSizePixel());
+ }
+ }
+ }
+
+ pWindow->ImplGetWindowImpl()->mpFrameData->mbNeedSysWindow = (nNewWidth < IMPL_MIN_NEEDSYSWIN) ||
+ (nNewHeight < IMPL_MIN_NEEDSYSWIN);
+ bool bMinimized = (nNewWidth <= 0) || (nNewHeight <= 0);
+ if( bMinimized != pWindow->ImplGetWindowImpl()->mpFrameData->mbMinimized )
+ pWindow->ImplGetWindowImpl()->mpFrameWindow->ImplNotifyIconifiedState( bMinimized );
+ pWindow->ImplGetWindowImpl()->mpFrameData->mbMinimized = bMinimized;
+}
+
+static void ImplHandleMove( vcl::Window* pWindow )
+{
+ if( pWindow->ImplGetWindowImpl()->mbFrame && pWindow->ImplIsFloatingWindow() && pWindow->IsReallyVisible() )
+ {
+ static_cast<FloatingWindow*>(pWindow)->EndPopupMode( FloatWinPopupEndFlags::TearOff );
+ pWindow->ImplCallMove();
+ }
+
+ if( pWindow->GetStyle() & (WB_MOVEABLE|WB_SIZEABLE) )
+ {
+ KillOwnPopups( pWindow );
+ if( pWindow->ImplGetWindow() != ImplGetSVHelpData().mpHelpWin )
+ ImplDestroyHelpWindow( true );
+ }
+
+ if ( pWindow->IsVisible() )
+ pWindow->ImplCallMove();
+ else
+ pWindow->ImplGetWindowImpl()->mbCallMove = true; // make sure the framepos will be updated on the next Show()
+
+ if ( pWindow->ImplGetWindowImpl()->mbFrame && pWindow->ImplGetWindowImpl()->mpClientWindow )
+ pWindow->ImplGetWindowImpl()->mpClientWindow->ImplCallMove(); // notify client to update geometry
+
+}
+
+static void ImplHandleMoveResize( vcl::Window* pWindow, tools::Long nNewWidth, tools::Long nNewHeight )
+{
+ ImplHandleMove( pWindow );
+ ImplHandleResize( pWindow, nNewWidth, nNewHeight );
+}
+
+static void ImplActivateFloatingWindows( vcl::Window const * pWindow, bool bActive )
+{
+ // First check all overlapping windows
+ vcl::Window* pTempWindow = pWindow->ImplGetWindowImpl()->mpFirstOverlap;
+ while ( pTempWindow )
+ {
+ if ( pTempWindow->GetActivateMode() == ActivateModeFlags::NONE )
+ {
+ if ( (pTempWindow->GetType() == WindowType::BORDERWINDOW) &&
+ (pTempWindow->ImplGetWindow()->GetType() == WindowType::FLOATINGWINDOW) )
+ static_cast<ImplBorderWindow*>(pTempWindow)->SetDisplayActive( bActive );
+ }
+
+ ImplActivateFloatingWindows( pTempWindow, bActive );
+ pTempWindow = pTempWindow->ImplGetWindowImpl()->mpNext;
+ }
+}
+
+IMPL_LINK_NOARG(vcl::Window, ImplAsyncFocusHdl, void*, void)
+{
+ if (!ImplGetWindowImpl() || !ImplGetWindowImpl()->mpFrameData)
+ return;
+
+ ImplGetWindowImpl()->mpFrameData->mnFocusId = nullptr;
+
+ // If the status has been preserved, because we got back the focus
+ // in the meantime, we do nothing
+ bool bHasFocus = ImplGetWindowImpl()->mpFrameData->mbHasFocus || ImplGetWindowImpl()->mpFrameData->mbSysObjFocus;
+
+ // next execute the delayed functions
+ if ( bHasFocus )
+ {
+ // redraw all floating windows inactive
+ if ( ImplGetWindowImpl()->mpFrameData->mbStartFocusState != bHasFocus )
+ ImplActivateFloatingWindows( this, bHasFocus );
+
+ if ( ImplGetWindowImpl()->mpFrameData->mpFocusWin )
+ {
+ bool bHandled = false;
+ if ( ImplGetWindowImpl()->mpFrameData->mpFocusWin->IsInputEnabled() &&
+ ! ImplGetWindowImpl()->mpFrameData->mpFocusWin->IsInModalMode() )
+ {
+ if ( ImplGetWindowImpl()->mpFrameData->mpFocusWin->IsEnabled() )
+ {
+ ImplGetWindowImpl()->mpFrameData->mpFocusWin->GrabFocus();
+ bHandled = true;
+ }
+ else if( ImplGetWindowImpl()->mpFrameData->mpFocusWin->ImplHasDlgCtrl() )
+ {
+ // #109094# if the focus is restored to a disabled dialog control (was disabled meanwhile)
+ // try to move it to the next control
+ ImplGetWindowImpl()->mpFrameData->mpFocusWin->ImplDlgCtrlNextWindow();
+ bHandled = true;
+ }
+ }
+ if ( !bHandled )
+ {
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::Window* pTopLevelWindow = ImplGetWindowImpl()->mpFrameData->mpFocusWin->ImplGetFirstOverlapWindow();
+
+ if ((!pTopLevelWindow->IsInputEnabled() || pTopLevelWindow->IsInModalMode())
+ && !pSVData->mpWinData->mpExecuteDialogs.empty())
+ pSVData->mpWinData->mpExecuteDialogs.back()->ToTop(ToTopFlags::RestoreWhenMin | ToTopFlags::GrabFocusOnly);
+ else
+ pTopLevelWindow->GrabFocus();
+ }
+ }
+ else
+ GrabFocus();
+ }
+ else
+ {
+ vcl::Window* pFocusWin = ImplGetWindowImpl()->mpFrameData->mpFocusWin;
+ if ( pFocusWin )
+ {
+ ImplSVData* pSVData = ImplGetSVData();
+
+ if (pSVData->mpWinData->mpFocusWin == pFocusWin)
+ {
+ // transfer the FocusWindow
+ vcl::Window* pOverlapWindow = pFocusWin->ImplGetFirstOverlapWindow();
+ if ( pOverlapWindow && pOverlapWindow->ImplGetWindowImpl() )
+ pOverlapWindow->ImplGetWindowImpl()->mpLastFocusWindow = pFocusWin;
+ pSVData->mpWinData->mpFocusWin = nullptr;
+
+ if ( pFocusWin->ImplGetWindowImpl() && pFocusWin->ImplGetWindowImpl()->mpCursor )
+ pFocusWin->ImplGetWindowImpl()->mpCursor->ImplHide();
+
+ // call the Deactivate
+ vcl::Window* pOldOverlapWindow = pFocusWin->ImplGetFirstOverlapWindow();
+ vcl::Window* pOldRealWindow = pOldOverlapWindow->ImplGetWindow();
+
+ if (pOldOverlapWindow && pOldOverlapWindow->ImplGetWindowImpl() &&
+ pOldRealWindow && pOldRealWindow->ImplGetWindowImpl())
+ {
+ pOldOverlapWindow->ImplGetWindowImpl()->mbActive = false;
+ pOldOverlapWindow->Deactivate();
+ if ( pOldRealWindow != pOldOverlapWindow )
+ {
+ pOldRealWindow->ImplGetWindowImpl()->mbActive = false;
+ pOldRealWindow->Deactivate();
+ }
+ }
+
+ // TrackingMode is ended in ImplHandleLoseFocus
+#ifdef _WIN32
+ // To avoid problems with the Unix IME
+ pFocusWin->EndExtTextInput();
+#endif
+
+ NotifyEvent aNEvt(NotifyEventType::LOSEFOCUS, pFocusWin);
+ if (!ImplCallPreNotify(aNEvt))
+ pFocusWin->CompatLoseFocus();
+ pFocusWin->ImplCallDeactivateListeners(nullptr);
+ }
+ }
+
+ // Redraw all floating window inactive
+ if ( ImplGetWindowImpl()->mpFrameData->mbStartFocusState != bHasFocus )
+ ImplActivateFloatingWindows( this, bHasFocus );
+ }
+}
+
+static void ImplHandleGetFocus( vcl::Window* pWindow )
+{
+ if (!pWindow || !pWindow->ImplGetWindowImpl() || !pWindow->ImplGetWindowImpl()->mpFrameData)
+ return;
+
+ pWindow->ImplGetWindowImpl()->mpFrameData->mbHasFocus = true;
+
+ // execute Focus-Events after a delay, such that SystemChildWindows
+ // do not blink when they receive focus
+ if ( !pWindow->ImplGetWindowImpl()->mpFrameData->mnFocusId )
+ {
+ pWindow->ImplGetWindowImpl()->mpFrameData->mbStartFocusState = !pWindow->ImplGetWindowImpl()->mpFrameData->mbHasFocus;
+ pWindow->ImplGetWindowImpl()->mpFrameData->mnFocusId = Application::PostUserEvent( LINK( pWindow, vcl::Window, ImplAsyncFocusHdl ), nullptr, true);
+ vcl::Window* pFocusWin = pWindow->ImplGetWindowImpl()->mpFrameData->mpFocusWin;
+ if ( pFocusWin && pFocusWin->ImplGetWindowImpl()->mpCursor )
+ pFocusWin->ImplGetWindowImpl()->mpCursor->ImplShow();
+ }
+}
+
+static void ImplHandleLoseFocus( vcl::Window* pWindow )
+{
+ if (!pWindow)
+ return;
+
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // Abort the autoscroll if the frame loses focus
+ if (pSVData->mpWinData->mpAutoScrollWin)
+ pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll();
+
+ // Abort tracking if the frame loses focus
+ if (pSVData->mpWinData->mpTrackWin)
+ {
+ if (pSVData->mpWinData->mpTrackWin->ImplGetWindowImpl() &&
+ pSVData->mpWinData->mpTrackWin->ImplGetWindowImpl()->mpFrameWindow == pWindow)
+ pSVData->mpWinData->mpTrackWin->EndTracking(TrackingEventFlags::Cancel);
+ }
+
+ if (pWindow->ImplGetWindowImpl() && pWindow->ImplGetWindowImpl()->mpFrameData)
+ {
+ pWindow->ImplGetWindowImpl()->mpFrameData->mbHasFocus = false;
+
+ // execute Focus-Events after a delay, such that SystemChildWindows
+ // do not flicker when they receive focus
+ if ( !pWindow->ImplGetWindowImpl()->mpFrameData->mnFocusId )
+ {
+ pWindow->ImplGetWindowImpl()->mpFrameData->mbStartFocusState = !pWindow->ImplGetWindowImpl()->mpFrameData->mbHasFocus;
+ pWindow->ImplGetWindowImpl()->mpFrameData->mnFocusId = Application::PostUserEvent( LINK( pWindow, vcl::Window, ImplAsyncFocusHdl ), nullptr, true );
+ }
+
+ vcl::Window* pFocusWin = pWindow->ImplGetWindowImpl()->mpFrameData->mpFocusWin;
+ if ( pFocusWin && pFocusWin->ImplGetWindowImpl()->mpCursor )
+ pFocusWin->ImplGetWindowImpl()->mpCursor->ImplHide();
+ }
+
+ // Make sure that no menu is visible when a toplevel window loses focus.
+ VclPtr<FloatingWindow> pFirstFloat = pSVData->mpWinData->mpFirstFloat;
+ if (pFirstFloat && pFirstFloat->IsMenuFloatingWindow() && !pWindow->GetParent())
+ {
+ pFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll);
+ }
+}
+
+namespace {
+
+struct DelayedCloseEvent
+{
+ VclPtr<vcl::Window> pWindow;
+};
+
+}
+
+static void DelayedCloseEventLink( void* pCEvent, void* )
+{
+ DelayedCloseEvent* pEv = static_cast<DelayedCloseEvent*>(pCEvent);
+
+ if( ! pEv->pWindow->isDisposed() )
+ {
+ // dispatch to correct window type
+ if( pEv->pWindow->IsSystemWindow() )
+ static_cast<SystemWindow*>(pEv->pWindow.get())->Close();
+ else if( pEv->pWindow->IsDockingWindow() )
+ static_cast<DockingWindow*>(pEv->pWindow.get())->Close();
+ }
+ delete pEv;
+}
+
+static void ImplHandleClose( const vcl::Window* pWindow )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ bool bWasPopup = false;
+ if( pWindow->ImplIsFloatingWindow() &&
+ static_cast<const FloatingWindow*>(pWindow)->ImplIsInPrivatePopupMode() )
+ {
+ bWasPopup = true;
+ }
+
+ // on Close stop all floating modes and end popups
+ if (pSVData->mpWinData->mpFirstFloat)
+ {
+ FloatingWindow* pLastLevelFloat;
+ pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat();
+ pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll );
+ }
+ if ( ImplGetSVHelpData().mbExtHelpMode )
+ Help::EndExtHelp();
+ if ( ImplGetSVHelpData().mpHelpWin )
+ ImplDestroyHelpWindow( false );
+ // AutoScrollMode
+ if (pSVData->mpWinData->mpAutoScrollWin)
+ pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll();
+
+ if (pSVData->mpWinData->mpTrackWin)
+ pSVData->mpWinData->mpTrackWin->EndTracking( TrackingEventFlags::Cancel | TrackingEventFlags::Key );
+
+ if (bWasPopup)
+ return;
+
+ vcl::Window *pWin = pWindow->ImplGetWindow();
+ SystemWindow* pSysWin = dynamic_cast<SystemWindow*>(pWin);
+ if (pSysWin)
+ {
+ // See if the custom close handler is set.
+ const Link<SystemWindow&,void>& rLink = pSysWin->GetCloseHdl();
+ if (rLink.IsSet())
+ {
+ rLink.Call(*pSysWin);
+ return;
+ }
+ }
+
+ // check whether close is allowed
+ if ( pWin->IsEnabled() && pWin->IsInputEnabled() && !pWin->IsInModalMode() )
+ {
+ DelayedCloseEvent* pEv = new DelayedCloseEvent;
+ pEv->pWindow = pWin;
+ Application::PostUserEvent( Link<void*,void>( pEv, DelayedCloseEventLink ) );
+ }
+}
+
+static void ImplHandleUserEvent( ImplSVEvent* pSVEvent )
+{
+ if ( pSVEvent )
+ {
+ if ( pSVEvent->mbCall )
+ {
+ pSVEvent->maLink.Call( pSVEvent->mpData );
+ }
+
+ delete pSVEvent;
+ }
+}
+
+MouseEventModifiers ImplGetMouseMoveMode( SalMouseEvent const * pEvent )
+{
+ MouseEventModifiers nMode = MouseEventModifiers::NONE;
+ if ( !pEvent->mnCode )
+ nMode |= MouseEventModifiers::SIMPLEMOVE;
+ if ( (pEvent->mnCode & MOUSE_LEFT) && !(pEvent->mnCode & KEY_MOD1) )
+ nMode |= MouseEventModifiers::DRAGMOVE;
+ if ( (pEvent->mnCode & MOUSE_LEFT) && (pEvent->mnCode & KEY_MOD1) )
+ nMode |= MouseEventModifiers::DRAGCOPY;
+ return nMode;
+}
+
+MouseEventModifiers ImplGetMouseButtonMode( SalMouseEvent const * pEvent )
+{
+ MouseEventModifiers nMode = MouseEventModifiers::NONE;
+ if ( pEvent->mnButton == MOUSE_LEFT )
+ nMode |= MouseEventModifiers::SIMPLECLICK;
+ if ( (pEvent->mnButton == MOUSE_LEFT) && !(pEvent->mnCode & (MOUSE_MIDDLE | MOUSE_RIGHT)) )
+ nMode |= MouseEventModifiers::SELECT;
+ if ( (pEvent->mnButton == MOUSE_LEFT) && (pEvent->mnCode & KEY_MOD1) &&
+ !(pEvent->mnCode & (MOUSE_MIDDLE | MOUSE_RIGHT | KEY_SHIFT)) )
+ nMode |= MouseEventModifiers::MULTISELECT;
+ if ( (pEvent->mnButton == MOUSE_LEFT) && (pEvent->mnCode & KEY_SHIFT) &&
+ !(pEvent->mnCode & (MOUSE_MIDDLE | MOUSE_RIGHT | KEY_MOD1)) )
+ nMode |= MouseEventModifiers::RANGESELECT;
+ return nMode;
+}
+
+static bool ImplHandleSalMouseLeave( vcl::Window* pWindow, SalMouseEvent const * pEvent )
+{
+ return ImplHandleMouseEvent( pWindow, NotifyEventType::MOUSEMOVE, true,
+ pEvent->mnX, pEvent->mnY,
+ pEvent->mnTime, pEvent->mnCode,
+ ImplGetMouseMoveMode( pEvent ) );
+}
+
+static bool ImplHandleSalMouseMove( vcl::Window* pWindow, SalMouseEvent const * pEvent )
+{
+ return ImplHandleMouseEvent( pWindow, NotifyEventType::MOUSEMOVE, false,
+ pEvent->mnX, pEvent->mnY,
+ pEvent->mnTime, pEvent->mnCode,
+ ImplGetMouseMoveMode( pEvent ) );
+}
+
+static bool ImplHandleSalMouseButtonDown( vcl::Window* pWindow, SalMouseEvent const * pEvent )
+{
+ return ImplHandleMouseEvent( pWindow, NotifyEventType::MOUSEBUTTONDOWN, false,
+ pEvent->mnX, pEvent->mnY,
+ pEvent->mnTime,
+#ifdef MACOSX
+ pEvent->mnButton | (pEvent->mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3)),
+#else
+ pEvent->mnButton | (pEvent->mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2)),
+#endif
+ ImplGetMouseButtonMode( pEvent ) );
+}
+
+static bool ImplHandleSalMouseButtonUp( vcl::Window* pWindow, SalMouseEvent const * pEvent )
+{
+ return ImplHandleMouseEvent( pWindow, NotifyEventType::MOUSEBUTTONUP, false,
+ pEvent->mnX, pEvent->mnY,
+ pEvent->mnTime,
+#ifdef MACOSX
+ pEvent->mnButton | (pEvent->mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3)),
+#else
+ pEvent->mnButton | (pEvent->mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2)),
+#endif
+ ImplGetMouseButtonMode( pEvent ) );
+}
+
+static bool ImplHandleMenuEvent( vcl::Window const * pWindow, SalMenuEvent* pEvent, SalEvent nEvent )
+{
+ // Find SystemWindow and its Menubar and let it dispatch the command
+ bool bRet = false;
+ vcl::Window *pWin = pWindow->ImplGetWindowImpl()->mpFirstChild;
+ while ( pWin )
+ {
+ if ( pWin->ImplGetWindowImpl()->mbSysWin )
+ break;
+ pWin = pWin->ImplGetWindowImpl()->mpNext;
+ }
+ if( pWin )
+ {
+ MenuBar *pMenuBar = static_cast<SystemWindow*>(pWin)->GetMenuBar();
+ if( pMenuBar )
+ {
+ switch( nEvent )
+ {
+ case SalEvent::MenuActivate:
+ pMenuBar->HandleMenuActivateEvent( static_cast<Menu*>(pEvent->mpMenu) );
+ bRet = true;
+ break;
+ case SalEvent::MenuDeactivate:
+ pMenuBar->HandleMenuDeActivateEvent( static_cast<Menu*>(pEvent->mpMenu) );
+ bRet = true;
+ break;
+ case SalEvent::MenuHighlight:
+ bRet = pMenuBar->HandleMenuHighlightEvent( static_cast<Menu*>(pEvent->mpMenu), pEvent->mnId );
+ break;
+ case SalEvent::MenuButtonCommand:
+ bRet = pMenuBar->HandleMenuButtonEvent( pEvent->mnId );
+ break;
+ case SalEvent::MenuCommand:
+ bRet = pMenuBar->HandleMenuCommandEvent( static_cast<Menu*>(pEvent->mpMenu), pEvent->mnId );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return bRet;
+}
+
+static void ImplHandleSalKeyMod( vcl::Window* pWindow, SalKeyModEvent const * pEvent )
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::Window* pTrackWin = pSVData->mpWinData->mpTrackWin;
+ if ( pTrackWin )
+ pWindow = pTrackWin;
+#ifdef MACOSX
+ sal_uInt16 nOldCode = pWindow->ImplGetWindowImpl()->mpFrameData->mnMouseCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3);
+#else
+ sal_uInt16 nOldCode = pWindow->ImplGetWindowImpl()->mpFrameData->mnMouseCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2);
+#endif
+ sal_uInt16 nNewCode = pEvent->mnCode;
+ if ( nOldCode != nNewCode )
+ {
+#ifdef MACOSX
+ nNewCode |= pWindow->ImplGetWindowImpl()->mpFrameData->mnMouseCode & ~(KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3);
+#else
+ nNewCode |= pWindow->ImplGetWindowImpl()->mpFrameData->mnMouseCode & ~(KEY_SHIFT | KEY_MOD1 | KEY_MOD2);
+#endif
+ pWindow->ImplGetWindowImpl()->mpFrameWindow->ImplCallMouseMove( nNewCode, true );
+ }
+
+ // #105224# send commandevent to allow special treatment of Ctrl-LeftShift/Ctrl-RightShift etc.
+ // + auto-accelerator feature, tdf#92630
+
+ // try to find a key input window...
+ vcl::Window* pChild = ImplGetKeyInputWindow( pWindow );
+ //...otherwise fail safe...
+ if (!pChild)
+ pChild = pWindow;
+
+ CommandModKeyData data( pEvent->mnModKeyCode, pEvent->mbDown );
+ ImplCallCommand( pChild, CommandEventId::ModKeyChange, &data );
+}
+
+static void ImplHandleInputLanguageChange( vcl::Window* pWindow )
+{
+ // find window
+ vcl::Window* pChild = ImplGetKeyInputWindow( pWindow );
+ if ( !pChild )
+ return;
+
+ ImplCallCommand( pChild, CommandEventId::InputLanguageChange );
+}
+
+static void ImplHandleSalSettings( SalEvent nEvent )
+{
+ Application* pApp = GetpApp();
+ if ( !pApp )
+ return;
+
+ if ( nEvent == SalEvent::SettingsChanged )
+ {
+ AllSettings aSettings = Application::GetSettings();
+ Application::MergeSystemSettings( aSettings );
+ pApp->OverrideSystemSettings( aSettings );
+ Application::SetSettings( aSettings );
+ }
+ else
+ {
+ DataChangedEventType nType;
+ switch ( nEvent )
+ {
+ case SalEvent::PrinterChanged:
+ ImplDeletePrnQueueList();
+ nType = DataChangedEventType::PRINTER;
+ break;
+ case SalEvent::DisplayChanged:
+ nType = DataChangedEventType::DISPLAY;
+ break;
+ case SalEvent::FontChanged:
+ OutputDevice::ImplUpdateAllFontData( true );
+ nType = DataChangedEventType::FONTS;
+ break;
+ default:
+ nType = DataChangedEventType::NONE;
+ break;
+ }
+
+ if ( nType != DataChangedEventType::NONE )
+ {
+ DataChangedEvent aDCEvt( nType );
+ Application::ImplCallEventListenersApplicationDataChanged(&aDCEvt);
+ Application::NotifyAllWindows( aDCEvt );
+ }
+ }
+}
+
+static void ImplHandleSalExtTextInputPos( vcl::Window* pWindow, SalExtTextInputPosEvent* pEvt )
+{
+ tools::Rectangle aCursorRect;
+ ImplHandleExtTextInputPos( pWindow, aCursorRect, pEvt->mnExtWidth, &pEvt->mbVertical );
+ if ( aCursorRect.IsEmpty() )
+ {
+ pEvt->mnX = -1;
+ pEvt->mnY = -1;
+ pEvt->mnWidth = -1;
+ pEvt->mnHeight = -1;
+ }
+ else
+ {
+ pEvt->mnX = aCursorRect.Left();
+ pEvt->mnY = aCursorRect.Top();
+ pEvt->mnWidth = aCursorRect.GetWidth();
+ pEvt->mnHeight = aCursorRect.GetHeight();
+ }
+}
+
+static bool ImplHandleShowDialog( vcl::Window* pWindow, ShowDialogId nDialogId )
+{
+ if( ! pWindow )
+ return false;
+
+ if( pWindow->GetType() == WindowType::BORDERWINDOW )
+ {
+ vcl::Window* pWrkWin = pWindow->GetWindow( GetWindowType::Client );
+ if( pWrkWin )
+ pWindow = pWrkWin;
+ }
+ CommandDialogData aCmdData( nDialogId );
+ return ImplCallCommand( pWindow, CommandEventId::ShowDialog, &aCmdData );
+}
+
+static void ImplHandleSurroundingTextRequest( vcl::Window *pWindow,
+ OUString& rText,
+ Selection &rSelRange )
+{
+ vcl::Window* pChild = ImplGetKeyInputWindow( pWindow );
+
+ if ( !pChild )
+ {
+ rText.clear();
+ rSelRange.setMin( 0 );
+ rSelRange.setMax( 0 );
+ }
+ else
+ {
+ rText = pChild->GetSurroundingText();
+ Selection aSel = pChild->GetSurroundingTextSelection();
+ rSelRange.setMin( aSel.Min() );
+ rSelRange.setMax( aSel.Max() );
+ }
+}
+
+static void ImplHandleSalSurroundingTextRequest( vcl::Window *pWindow,
+ SalSurroundingTextRequestEvent *pEvt )
+{
+ Selection aSelRange;
+ ImplHandleSurroundingTextRequest( pWindow, pEvt->maText, aSelRange );
+
+ aSelRange.Normalize();
+
+ if( aSelRange.Min() < 0 )
+ pEvt->mnStart = 0;
+ else if( aSelRange.Min() > pEvt->maText.getLength() )
+ pEvt->mnStart = pEvt->maText.getLength();
+ else
+ pEvt->mnStart = aSelRange.Min();
+
+ if( aSelRange.Max() < 0 )
+ pEvt->mnStart = 0;
+ else if( aSelRange.Max() > pEvt->maText.getLength() )
+ pEvt->mnEnd = pEvt->maText.getLength();
+ else
+ pEvt->mnEnd = aSelRange.Max();
+}
+
+static void ImplHandleSalDeleteSurroundingTextRequest( vcl::Window *pWindow,
+ SalSurroundingTextSelectionChangeEvent *pEvt )
+{
+ vcl::Window* pChild = ImplGetKeyInputWindow( pWindow );
+
+ Selection aSelection(pEvt->mnStart, pEvt->mnEnd);
+ if (pChild && pChild->DeleteSurroundingText(aSelection))
+ {
+ pEvt->mnStart = aSelection.Min();
+ pEvt->mnEnd = aSelection.Max();
+ }
+ else
+ {
+ pEvt->mnStart = pEvt->mnEnd = SAL_MAX_UINT32;
+ }
+}
+
+static void ImplHandleSurroundingTextSelectionChange( vcl::Window *pWindow,
+ sal_uLong nStart,
+ sal_uLong nEnd )
+{
+ vcl::Window* pChild = ImplGetKeyInputWindow( pWindow );
+ if( pChild )
+ {
+ CommandSelectionChangeData data( nStart, nEnd );
+ ImplCallCommand( pChild, CommandEventId::SelectionChange, &data );
+ }
+}
+
+static void ImplHandleStartReconversion( vcl::Window *pWindow )
+{
+ vcl::Window* pChild = ImplGetKeyInputWindow( pWindow );
+ if( pChild )
+ ImplCallCommand( pChild, CommandEventId::PrepareReconversion );
+}
+
+static void ImplHandleSalQueryCharPosition( vcl::Window *pWindow,
+ SalQueryCharPositionEvent *pEvt )
+{
+ pEvt->mbValid = false;
+ pEvt->mbVertical = false;
+ pEvt->maCursorBound = AbsoluteScreenPixelRectangle();
+
+ ImplSVData* pSVData = ImplGetSVData();
+ vcl::Window* pChild = pSVData->mpWinData->mpExtTextInputWin;
+
+ if ( !pChild )
+ pChild = ImplGetKeyInputWindow( pWindow );
+ else
+ {
+ // Test, if the Window is related to the frame
+ if ( !pWindow->ImplIsWindowOrChild( pChild ) )
+ pChild = ImplGetKeyInputWindow( pWindow );
+ }
+
+ if( !pChild )
+ return;
+
+ ImplCallCommand( pChild, CommandEventId::QueryCharPosition );
+
+ ImplWinData* pWinData = pChild->ImplGetWinData();
+ if ( !(pWinData->mpCompositionCharRects && pEvt->mnCharPos < o3tl::make_unsigned( pWinData->mnCompositionCharRects )) )
+ return;
+
+ const OutputDevice *pChildOutDev = pChild->GetOutDev();
+ const tools::Rectangle& aRect = pWinData->mpCompositionCharRects[ pEvt->mnCharPos ];
+ tools::Rectangle aDeviceRect = pChildOutDev->ImplLogicToDevicePixel( aRect );
+ AbsoluteScreenPixelPoint aAbsScreenPos = pChild->OutputToAbsoluteScreenPixel( pChild->ScreenToOutputPixel(aDeviceRect.TopLeft()) );
+ pEvt->maCursorBound = AbsoluteScreenPixelRectangle(aAbsScreenPos, aDeviceRect.GetSize());
+ pEvt->mbVertical = pWinData->mbVertical;
+ pEvt->mbValid = true;
+}
+
+bool ImplWindowFrameProc( vcl::Window* _pWindow, SalEvent nEvent, const void* pEvent )
+{
+ DBG_TESTSOLARMUTEX();
+
+ // Ensure the window survives during this method.
+ VclPtr<vcl::Window> pWindow( _pWindow );
+
+ bool bRet = false;
+
+ // #119709# for some unknown reason it is possible to receive events (in this case key events)
+ // although the corresponding VCL window must have been destroyed already
+ // at least ImplGetWindowImpl() was NULL in these cases, so check this here
+ if( pWindow->ImplGetWindowImpl() == nullptr )
+ return false;
+
+ switch ( nEvent )
+ {
+ case SalEvent::MouseMove:
+ bRet = ImplHandleSalMouseMove( pWindow, static_cast<SalMouseEvent const *>(pEvent) );
+ break;
+ case SalEvent::ExternalMouseMove:
+ {
+ MouseEvent const * pMouseEvt = static_cast<MouseEvent const *>(pEvent);
+ SalMouseEvent aSalMouseEvent;
+
+ aSalMouseEvent.mnTime = tools::Time::GetSystemTicks();
+ aSalMouseEvent.mnX = pMouseEvt->GetPosPixel().X();
+ aSalMouseEvent.mnY = pMouseEvt->GetPosPixel().Y();
+ aSalMouseEvent.mnButton = 0;
+ aSalMouseEvent.mnCode = pMouseEvt->GetButtons() | pMouseEvt->GetModifier();
+
+ bRet = ImplHandleSalMouseMove( pWindow, &aSalMouseEvent );
+ }
+ break;
+ case SalEvent::MouseLeave:
+ bRet = ImplHandleSalMouseLeave( pWindow, static_cast<SalMouseEvent const *>(pEvent) );
+ break;
+ case SalEvent::MouseButtonDown:
+ bRet = ImplHandleSalMouseButtonDown( pWindow, static_cast<SalMouseEvent const *>(pEvent) );
+ break;
+ case SalEvent::ExternalMouseButtonDown:
+ {
+ MouseEvent const * pMouseEvt = static_cast<MouseEvent const *>(pEvent);
+ SalMouseEvent aSalMouseEvent;
+
+ aSalMouseEvent.mnTime = tools::Time::GetSystemTicks();
+ aSalMouseEvent.mnX = pMouseEvt->GetPosPixel().X();
+ aSalMouseEvent.mnY = pMouseEvt->GetPosPixel().Y();
+ aSalMouseEvent.mnButton = pMouseEvt->GetButtons();
+ aSalMouseEvent.mnCode = pMouseEvt->GetButtons() | pMouseEvt->GetModifier();
+
+ bRet = ImplHandleSalMouseButtonDown( pWindow, &aSalMouseEvent );
+ }
+ break;
+ case SalEvent::MouseButtonUp:
+ bRet = ImplHandleSalMouseButtonUp( pWindow, static_cast<SalMouseEvent const *>(pEvent) );
+ break;
+ case SalEvent::ExternalMouseButtonUp:
+ {
+ MouseEvent const * pMouseEvt = static_cast<MouseEvent const *>(pEvent);
+ SalMouseEvent aSalMouseEvent;
+
+ aSalMouseEvent.mnTime = tools::Time::GetSystemTicks();
+ aSalMouseEvent.mnX = pMouseEvt->GetPosPixel().X();
+ aSalMouseEvent.mnY = pMouseEvt->GetPosPixel().Y();
+ aSalMouseEvent.mnButton = pMouseEvt->GetButtons();
+ aSalMouseEvent.mnCode = pMouseEvt->GetButtons() | pMouseEvt->GetModifier();
+
+ bRet = ImplHandleSalMouseButtonUp( pWindow, &aSalMouseEvent );
+ }
+ break;
+ case SalEvent::MouseActivate:
+ bRet = false;
+ break;
+ case SalEvent::KeyInput:
+ {
+ SalKeyEvent const * pKeyEvt = static_cast<SalKeyEvent const *>(pEvent);
+ bRet = ImplHandleKey( pWindow, NotifyEventType::KEYINPUT,
+ pKeyEvt->mnCode, pKeyEvt->mnCharCode, pKeyEvt->mnRepeat, true );
+ }
+ break;
+ case SalEvent::ExternalKeyInput:
+ {
+ KeyEvent const * pKeyEvt = static_cast<KeyEvent const *>(pEvent);
+ bRet = ImplHandleKey( pWindow, NotifyEventType::KEYINPUT,
+ pKeyEvt->GetKeyCode().GetFullCode(), pKeyEvt->GetCharCode(), pKeyEvt->GetRepeat(), false );
+ }
+ break;
+ case SalEvent::KeyUp:
+ {
+ SalKeyEvent const * pKeyEvt = static_cast<SalKeyEvent const *>(pEvent);
+ bRet = ImplHandleKey( pWindow, NotifyEventType::KEYUP,
+ pKeyEvt->mnCode, pKeyEvt->mnCharCode, pKeyEvt->mnRepeat, true );
+ }
+ break;
+ case SalEvent::ExternalKeyUp:
+ {
+ KeyEvent const * pKeyEvt = static_cast<KeyEvent const *>(pEvent);
+ bRet = ImplHandleKey( pWindow, NotifyEventType::KEYUP,
+ pKeyEvt->GetKeyCode().GetFullCode(), pKeyEvt->GetCharCode(), pKeyEvt->GetRepeat(), false );
+ }
+ break;
+ case SalEvent::KeyModChange:
+ ImplHandleSalKeyMod( pWindow, static_cast<SalKeyModEvent const *>(pEvent) );
+ break;
+
+ case SalEvent::InputLanguageChange:
+ ImplHandleInputLanguageChange( pWindow );
+ break;
+
+ case SalEvent::MenuActivate:
+ case SalEvent::MenuDeactivate:
+ case SalEvent::MenuHighlight:
+ case SalEvent::MenuCommand:
+ case SalEvent::MenuButtonCommand:
+ bRet = ImplHandleMenuEvent( pWindow, const_cast<SalMenuEvent *>(static_cast<SalMenuEvent const *>(pEvent)), nEvent );
+ break;
+
+ case SalEvent::WheelMouse:
+ bRet = ImplHandleWheelEvent( pWindow, *static_cast<const SalWheelMouseEvent*>(pEvent));
+ break;
+
+ case SalEvent::Paint:
+ {
+ SalPaintEvent const * pPaintEvt = static_cast<SalPaintEvent const *>(pEvent);
+
+ if( AllSettings::GetLayoutRTL() )
+ {
+ SalFrame* pSalFrame = pWindow->ImplGetWindowImpl()->mpFrame;
+ const_cast<SalPaintEvent *>(pPaintEvt)->mnBoundX = pSalFrame->maGeometry.width() - pPaintEvt->mnBoundWidth - pPaintEvt->mnBoundX;
+ }
+
+ tools::Rectangle aBoundRect( Point( pPaintEvt->mnBoundX, pPaintEvt->mnBoundY ),
+ Size( pPaintEvt->mnBoundWidth, pPaintEvt->mnBoundHeight ) );
+ ImplHandlePaint( pWindow, aBoundRect, pPaintEvt->mbImmediateUpdate );
+ }
+ break;
+
+ case SalEvent::Move:
+ ImplHandleMove( pWindow );
+ break;
+
+ case SalEvent::Resize:
+ {
+ tools::Long nNewWidth;
+ tools::Long nNewHeight;
+ pWindow->ImplGetWindowImpl()->mpFrame->GetClientSize( nNewWidth, nNewHeight );
+ ImplHandleResize( pWindow, nNewWidth, nNewHeight );
+ }
+ break;
+
+ case SalEvent::MoveResize:
+ {
+ SalFrameGeometry g = pWindow->ImplGetWindowImpl()->mpFrame->GetGeometry();
+ ImplHandleMoveResize(pWindow, g.width(), g.height());
+ }
+ break;
+
+ case SalEvent::ClosePopups:
+ {
+ KillOwnPopups( pWindow );
+ }
+ break;
+
+ case SalEvent::GetFocus:
+ ImplHandleGetFocus( pWindow );
+ break;
+ case SalEvent::LoseFocus:
+ ImplHandleLoseFocus( pWindow );
+ break;
+
+ case SalEvent::Close:
+ ImplHandleClose( pWindow );
+ break;
+
+ case SalEvent::Shutdown:
+ {
+ static bool bInQueryExit = false;
+ if( !bInQueryExit )
+ {
+ bInQueryExit = true;
+ if ( GetpApp()->QueryExit() )
+ {
+ // end the message loop
+ Application::Quit();
+ return false;
+ }
+ else
+ {
+ bInQueryExit = false;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ case SalEvent::SettingsChanged:
+ case SalEvent::PrinterChanged:
+ case SalEvent::DisplayChanged:
+ case SalEvent::FontChanged:
+ ImplHandleSalSettings( nEvent );
+ break;
+
+ case SalEvent::UserEvent:
+ ImplHandleUserEvent( const_cast<ImplSVEvent *>(static_cast<ImplSVEvent const *>(pEvent)) );
+ break;
+
+ case SalEvent::ExtTextInput:
+ {
+ SalExtTextInputEvent const * pEvt = static_cast<SalExtTextInputEvent const *>(pEvent);
+ bRet = ImplHandleExtTextInput( pWindow,
+ pEvt->maText, pEvt->mpTextAttr,
+ pEvt->mnCursorPos, pEvt->mnCursorFlags );
+ }
+ break;
+ case SalEvent::EndExtTextInput:
+ bRet = ImplHandleEndExtTextInput();
+ break;
+ case SalEvent::ExtTextInputPos:
+ ImplHandleSalExtTextInputPos( pWindow, const_cast<SalExtTextInputPosEvent *>(static_cast<SalExtTextInputPosEvent const *>(pEvent)) );
+ break;
+ case SalEvent::InputContextChange:
+ bRet = ImplHandleInputContextChange( pWindow );
+ break;
+ case SalEvent::ShowDialog:
+ {
+ ShowDialogId nLOKWindowId = static_cast<ShowDialogId>(reinterpret_cast<sal_IntPtr>(pEvent));
+ bRet = ImplHandleShowDialog( pWindow, nLOKWindowId );
+ }
+ break;
+ case SalEvent::SurroundingTextRequest:
+ ImplHandleSalSurroundingTextRequest( pWindow, const_cast<SalSurroundingTextRequestEvent *>(static_cast<SalSurroundingTextRequestEvent const *>(pEvent)) );
+ break;
+ case SalEvent::DeleteSurroundingTextRequest:
+ ImplHandleSalDeleteSurroundingTextRequest( pWindow, const_cast<SalSurroundingTextSelectionChangeEvent *>(static_cast<SalSurroundingTextSelectionChangeEvent const *>(pEvent)) );
+ break;
+ case SalEvent::SurroundingTextSelectionChange:
+ {
+ SalSurroundingTextSelectionChangeEvent const * pEvt
+ = static_cast<SalSurroundingTextSelectionChangeEvent const *>(pEvent);
+ ImplHandleSurroundingTextSelectionChange( pWindow,
+ pEvt->mnStart,
+ pEvt->mnEnd );
+ [[fallthrough]]; // TODO: Fallthrough really intended?
+ }
+ case SalEvent::StartReconversion:
+ ImplHandleStartReconversion( pWindow );
+ break;
+
+ case SalEvent::QueryCharPosition:
+ ImplHandleSalQueryCharPosition( pWindow, const_cast<SalQueryCharPositionEvent *>(static_cast<SalQueryCharPositionEvent const *>(pEvent)) );
+ break;
+
+ case SalEvent::GestureSwipe:
+ bRet = ImplHandleSwipe(pWindow, *static_cast<const SalGestureSwipeEvent*>(pEvent));
+ break;
+
+ case SalEvent::GestureLongPress:
+ bRet = ImplHandleLongPress(pWindow, *static_cast<const SalGestureLongPressEvent*>(pEvent));
+ break;
+
+ case SalEvent::ExternalGesture:
+ {
+ auto const * pGestureEvent = static_cast<GestureEventPan const *>(pEvent);
+
+ SalGestureEvent aSalGestureEvent;
+ aSalGestureEvent.mfOffset = pGestureEvent->mnOffset;
+ aSalGestureEvent.mnX = pGestureEvent->mnX;
+ aSalGestureEvent.mnY = pGestureEvent->mnY;
+ aSalGestureEvent.meEventType = pGestureEvent->meEventType;
+ aSalGestureEvent.meOrientation = pGestureEvent->meOrientation;
+
+ bRet = ImplHandleGestureEvent(pWindow, aSalGestureEvent);
+ }
+ break;
+ case SalEvent::GesturePan:
+ {
+ auto const * aSalGestureEvent = static_cast<SalGestureEvent const *>(pEvent);
+ bRet = ImplHandleGestureEvent(pWindow, *aSalGestureEvent);
+ }
+ break;
+ case SalEvent::GestureZoom:
+ {
+ const auto * pGestureEvent = static_cast<SalGestureZoomEvent const *>(pEvent);
+ bRet = ImplHandleGestureZoomEvent(pWindow, *pGestureEvent);
+ }
+ break;
+ case SalEvent::GestureRotate:
+ {
+ const auto * pGestureEvent = static_cast<SalGestureRotateEvent const *>(pEvent);
+ bRet = ImplHandleGestureRotateEvent(pWindow, *pGestureEvent);
+ }
+ break;
+ default:
+ SAL_WARN( "vcl.layout", "ImplWindowFrameProc(): unknown event (" << static_cast<int>(nEvent) << ")" );
+ break;
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/window/wrkwin.cxx b/vcl/source/window/wrkwin.cxx
new file mode 100644
index 0000000000..a04c46ae29
--- /dev/null
+++ b/vcl/source/window/wrkwin.cxx
@@ -0,0 +1,271 @@
+/* -*- 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/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/wrkwin.hxx>
+// declare system types in sysdata.hxx
+#include <vcl/sysdata.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/rendering/XCanvas.hpp>
+
+#include <svdata.hxx>
+#include <salframe.hxx>
+#include <brdwin.hxx>
+#include <window.h>
+
+void WorkWindow::ImplInitWorkWindowData()
+{
+ mnIcon = 0; // Should be removed in the next top level update - now in SystemWindow
+
+ mnPresentationFlags = PresentationFlags::NONE;
+ mbPresentationMode = false;
+ mbPresentationVisible = false;
+ mbPresentationFull = false;
+ mbFullScreenMode = false;
+}
+
+void WorkWindow::ImplInit( vcl::Window* pParent, WinBits nStyle, SystemParentData* pSystemParentData )
+{
+ BorderWindowStyle nFrameStyle = BorderWindowStyle::Frame;
+ if ( nStyle & WB_APP )
+ nFrameStyle |= BorderWindowStyle::App;
+
+ VclPtrInstance<ImplBorderWindow> pBorderWin( pParent, pSystemParentData, nStyle, nFrameStyle );
+ Window::ImplInit( pBorderWin, nStyle & (WB_3DLOOK | WB_CLIPCHILDREN | WB_DIALOGCONTROL | WB_SYSTEMFLOATWIN), nullptr );
+ pBorderWin->mpWindowImpl->mpClientWindow = this;
+ pBorderWin->GetBorder( mpWindowImpl->mnLeftBorder, mpWindowImpl->mnTopBorder, mpWindowImpl->mnRightBorder, mpWindowImpl->mnBottomBorder );
+ mpWindowImpl->mpBorderWindow = pBorderWin;
+
+ // mpWindowImpl->mpRealParent = pParent; // should actually be set, but is not set due to errors with the menubar!!
+
+ if ( nStyle & WB_APP )
+ {
+ ImplSVData* pSVData = ImplGetSVData();
+ SAL_WARN_IF(pSVData->maFrameData.mpAppWin, "vcl",
+ "WorkWindow::WorkWindow(): More than one window with style WB_APP");
+ pSVData->maFrameData.mpAppWin = this;
+ }
+
+ SetActivateMode( ActivateModeFlags::GrabFocus );
+}
+
+void WorkWindow::ImplInit( vcl::Window* pParent, WinBits nStyle, const css::uno::Any& aSystemWorkWindowToken )
+{
+ if( aSystemWorkWindowToken.hasValue() )
+ {
+ css::uno::Sequence< sal_Int8 > aSeq;
+ aSystemWorkWindowToken >>= aSeq;
+ SystemParentData* pData = reinterpret_cast<SystemParentData*>(aSeq.getArray());
+ SAL_WARN_IF( aSeq.getLength() != sizeof( SystemParentData ) || pData->nSize != sizeof( SystemParentData ), "vcl", "WorkWindow::WorkWindow( vcl::Window*, const Any&, WinBits ) called with invalid Any" );
+ // init with style 0 as does WorkWindow::WorkWindow( SystemParentData* );
+ ImplInit( pParent, 0, pData );
+ }
+ else
+ ImplInit( pParent, nStyle );
+}
+
+WorkWindow::WorkWindow( WindowType nType ) :
+ SystemWindow( nType, "vcl::WorkWindow maLayoutIdle" )
+{
+ ImplInitWorkWindowData();
+}
+
+WorkWindow::WorkWindow( vcl::Window* pParent, WinBits nStyle ) :
+ SystemWindow( WindowType::WORKWINDOW, "vcl::WorkWindow maLayoutIdle" )
+{
+ ImplInitWorkWindowData();
+ ImplInit( pParent, nStyle );
+}
+
+WorkWindow::WorkWindow( vcl::Window* pParent, const css::uno::Any& aSystemWorkWindowToken, WinBits nStyle ) :
+ SystemWindow( WindowType::WORKWINDOW, "vcl::WorkWindow maLayoutIdle" )
+{
+ ImplInitWorkWindowData();
+ mbSysChild = true;
+ ImplInit( pParent, nStyle, aSystemWorkWindowToken );
+}
+
+WorkWindow::WorkWindow( SystemParentData* pParent ) :
+ SystemWindow( WindowType::WORKWINDOW, "vcl::WorkWindow maLayoutIdle" )
+{
+ ImplInitWorkWindowData();
+ mbSysChild = true;
+ ImplInit( nullptr, 0, pParent );
+}
+
+WorkWindow::~WorkWindow()
+{
+ disposeOnce();
+}
+
+void WorkWindow::dispose()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ if (pSVData->maFrameData.mpAppWin == this)
+ {
+ pSVData->maFrameData.mpAppWin = nullptr;
+ Application::Quit();
+ }
+ SystemWindow::dispose();
+}
+
+void WorkWindow::ShowFullScreenMode( bool bFullScreenMode )
+{
+ return ShowFullScreenMode( bFullScreenMode, GetScreenNumber());
+}
+
+void WorkWindow::ShowFullScreenMode( bool bFullScreenMode, sal_Int32 nDisplayScreen )
+{
+ if ( !mbFullScreenMode == !bFullScreenMode )
+ return;
+
+ mbFullScreenMode = bFullScreenMode;
+ if ( mbSysChild )
+ return;
+
+ // Dispose of the canvas implementation, which might rely on
+ // screen-specific system data.
+ GetOutDev()->ImplDisposeCanvas();
+
+ mpWindowImpl->mpFrameWindow->mpWindowImpl->mbWaitSystemResize = true;
+ ImplGetFrame()->ShowFullScreen( bFullScreenMode, nDisplayScreen );
+}
+
+void WorkWindow::StartPresentationMode( PresentationFlags nFlags )
+{
+ return StartPresentationMode( false/*bPresentation*/, nFlags, GetScreenNumber());
+}
+
+void WorkWindow::StartPresentationMode( bool bPresentation, PresentationFlags nFlags, sal_Int32 nDisplayScreen )
+{
+ if ( !bPresentation == !mbPresentationMode )
+ return;
+
+ if ( bPresentation )
+ {
+ mbPresentationMode = true;
+ mbPresentationVisible = IsVisible();
+ mbPresentationFull = mbFullScreenMode;
+ mnPresentationFlags = nFlags;
+
+ ShowFullScreenMode( true, nDisplayScreen );
+ if ( !mbSysChild )
+ {
+ if ( mnPresentationFlags & PresentationFlags::HideAllApps )
+ mpWindowImpl->mpFrame->SetAlwaysOnTop( true );
+ ToTop();
+ mpWindowImpl->mpFrame->StartPresentation( true );
+ }
+
+ Show();
+ }
+ else
+ {
+ Show( mbPresentationVisible );
+ if ( !mbSysChild )
+ {
+ mpWindowImpl->mpFrame->StartPresentation( false );
+ if ( mnPresentationFlags & PresentationFlags::HideAllApps )
+ mpWindowImpl->mpFrame->SetAlwaysOnTop( false );
+ }
+ ShowFullScreenMode( mbPresentationFull, nDisplayScreen );
+
+ mbPresentationMode = false;
+ mbPresentationVisible = false;
+ mbPresentationFull = false;
+ mnPresentationFlags = PresentationFlags::NONE;
+ }
+}
+
+bool WorkWindow::IsMinimized() const
+{
+ vcl::WindowData aData;
+ if (mpWindowImpl->mpFrame->GetWindowState(&aData))
+ return bool(aData.state() & vcl::WindowState::Minimized);
+ else
+ return false;
+}
+
+void WorkWindow::SetPluginParent( SystemParentData* pParent )
+{
+ SAL_WARN_IF( mbPresentationMode || mbFullScreenMode, "vcl", "SetPluginParent in fullscreen or presentation mode !" );
+
+ bool bWasDnd = Window::ImplStopDnd();
+
+ bool bShown = IsVisible();
+ Show( false );
+ mpWindowImpl->mpFrame->SetPluginParent( pParent );
+ Show( bShown );
+
+ if( bWasDnd )
+ Window::ImplStartDnd();
+}
+
+void WorkWindow::ImplSetFrameState(vcl::WindowState aFrameState )
+{
+ vcl::WindowData aState;
+ aState.setMask(vcl::WindowDataMask::State);
+ aState.setState(aFrameState);
+ mpWindowImpl->mpFrame->SetWindowState(&aState);
+}
+
+void WorkWindow::Minimize()
+{
+ ImplSetFrameState( vcl::WindowState::Minimized );
+}
+
+void WorkWindow::Restore()
+{
+ ImplSetFrameState( vcl::WindowState::Normal );
+}
+
+bool WorkWindow::Close()
+{
+ bool bCanClose = SystemWindow::Close();
+
+ // if it's the application window then close the application
+ if (bCanClose && (ImplGetSVData()->maFrameData.mpAppWin == this))
+ Application::Quit();
+
+ return bCanClose;
+}
+
+void WorkWindow::Maximize( bool bMaximize )
+{
+ ImplSetFrameState( bMaximize ? vcl::WindowState::Maximized : vcl::WindowState::Normal );
+}
+
+bool WorkWindow::IsMaximized() const
+{
+ bool bRet = false;
+
+ vcl::WindowData aState;
+ if( mpWindowImpl->mpFrame->GetWindowState( &aState ) )
+ {
+ if( aState.state() & (vcl::WindowState::Maximized |
+ vcl::WindowState::MaximizedHorz |
+ vcl::WindowState::MaximizedVert ) )
+ bRet = true;
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */