From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- vcl/source/window/EnumContext.cxx | 215 ++ vcl/source/window/NotebookBarAddonsMerger.cxx | 184 + vcl/source/window/OptionalBox.cxx | 62 + vcl/source/window/abstdlg.cxx | 85 + vcl/source/window/accel.cxx | 279 ++ vcl/source/window/accessibility.cxx | 612 ++++ vcl/source/window/accmgr.cxx | 228 ++ vcl/source/window/brdwin.cxx | 2003 ++++++++++ vcl/source/window/bubblewindow.cxx | 557 +++ vcl/source/window/bufferdevice.cxx | 45 + vcl/source/window/bufferdevice.hxx | 35 + vcl/source/window/builder.cxx | 4402 ++++++++++++++++++++++ vcl/source/window/clipping.cxx | 709 ++++ vcl/source/window/commandevent.cxx | 198 + vcl/source/window/cursor.cxx | 485 +++ vcl/source/window/debug.cxx | 48 + vcl/source/window/debugevent.cxx | 271 ++ vcl/source/window/decoview.cxx | 1046 ++++++ vcl/source/window/dialog.cxx | 1711 +++++++++ vcl/source/window/dlgctrl.cxx | 1159 ++++++ vcl/source/window/dlgctrl.hxx | 34 + vcl/source/window/dndeventdispatcher.cxx | 412 +++ vcl/source/window/dndlistenercontainer.cxx | 487 +++ vcl/source/window/dockingarea.cxx | 257 ++ vcl/source/window/dockmgr.cxx | 1075 ++++++ vcl/source/window/dockwin.cxx | 1147 ++++++ vcl/source/window/errinf.cxx | 329 ++ vcl/source/window/event.cxx | 673 ++++ vcl/source/window/floatwin.cxx | 973 +++++ vcl/source/window/globalization.cxx | 44 + vcl/source/window/impldockingwrapper.hxx | 131 + vcl/source/window/introwin.cxx | 49 + vcl/source/window/keycod.cxx | 95 + vcl/source/window/keyevent.cxx | 67 + vcl/source/window/layout.cxx | 3028 +++++++++++++++ vcl/source/window/legacyaccessibility.cxx | 241 ++ vcl/source/window/menu.cxx | 3077 ++++++++++++++++ vcl/source/window/menubarwindow.cxx | 1222 +++++++ vcl/source/window/menubarwindow.hxx | 142 + vcl/source/window/menufloatingwindow.cxx | 1318 +++++++ vcl/source/window/menufloatingwindow.hxx | 128 + vcl/source/window/menuitemlist.cxx | 314 ++ vcl/source/window/menuitemlist.hxx | 150 + vcl/source/window/menuwindow.cxx | 111 + vcl/source/window/menuwindow.hxx | 56 + vcl/source/window/mnemonic.cxx | 348 ++ vcl/source/window/mouse.cxx | 768 ++++ vcl/source/window/paint.cxx | 1809 +++++++++ vcl/source/window/printdlg.cxx | 2267 ++++++++++++ vcl/source/window/scrwnd.cxx | 379 ++ vcl/source/window/seleng.cxx | 421 +++ vcl/source/window/settings.cxx | 266 ++ vcl/source/window/split.cxx | 698 ++++ vcl/source/window/splitwin.cxx | 2719 ++++++++++++++ vcl/source/window/stacking.cxx | 1151 ++++++ vcl/source/window/status.cxx | 1464 ++++++++ vcl/source/window/syschild.cxx | 200 + vcl/source/window/syswin.cxx | 1183 ++++++ vcl/source/window/tabdlg.cxx | 184 + vcl/source/window/tabpage.cxx | 310 ++ vcl/source/window/taskpanelist.cxx | 287 ++ vcl/source/window/toolbox.cxx | 4846 +++++++++++++++++++++++++ vcl/source/window/toolbox2.cxx | 1781 +++++++++ vcl/source/window/window.cxx | 3978 ++++++++++++++++++++ vcl/source/window/window2.cxx | 2043 +++++++++++ vcl/source/window/window3.cxx | 220 ++ vcl/source/window/winproc.cxx | 2884 +++++++++++++++ vcl/source/window/wrkwin.cxx | 272 ++ 68 files changed, 60372 insertions(+) create mode 100644 vcl/source/window/EnumContext.cxx create mode 100644 vcl/source/window/NotebookBarAddonsMerger.cxx create mode 100644 vcl/source/window/OptionalBox.cxx create mode 100644 vcl/source/window/abstdlg.cxx create mode 100644 vcl/source/window/accel.cxx create mode 100644 vcl/source/window/accessibility.cxx create mode 100644 vcl/source/window/accmgr.cxx create mode 100644 vcl/source/window/brdwin.cxx create mode 100644 vcl/source/window/bubblewindow.cxx create mode 100644 vcl/source/window/bufferdevice.cxx create mode 100644 vcl/source/window/bufferdevice.hxx create mode 100644 vcl/source/window/builder.cxx create mode 100644 vcl/source/window/clipping.cxx create mode 100644 vcl/source/window/commandevent.cxx create mode 100644 vcl/source/window/cursor.cxx create mode 100644 vcl/source/window/debug.cxx create mode 100644 vcl/source/window/debugevent.cxx create mode 100644 vcl/source/window/decoview.cxx create mode 100644 vcl/source/window/dialog.cxx create mode 100644 vcl/source/window/dlgctrl.cxx create mode 100644 vcl/source/window/dlgctrl.hxx create mode 100644 vcl/source/window/dndeventdispatcher.cxx create mode 100644 vcl/source/window/dndlistenercontainer.cxx create mode 100644 vcl/source/window/dockingarea.cxx create mode 100644 vcl/source/window/dockmgr.cxx create mode 100644 vcl/source/window/dockwin.cxx create mode 100644 vcl/source/window/errinf.cxx create mode 100644 vcl/source/window/event.cxx create mode 100644 vcl/source/window/floatwin.cxx create mode 100644 vcl/source/window/globalization.cxx create mode 100644 vcl/source/window/impldockingwrapper.hxx create mode 100644 vcl/source/window/introwin.cxx create mode 100644 vcl/source/window/keycod.cxx create mode 100644 vcl/source/window/keyevent.cxx create mode 100644 vcl/source/window/layout.cxx create mode 100644 vcl/source/window/legacyaccessibility.cxx create mode 100644 vcl/source/window/menu.cxx create mode 100644 vcl/source/window/menubarwindow.cxx create mode 100644 vcl/source/window/menubarwindow.hxx create mode 100644 vcl/source/window/menufloatingwindow.cxx create mode 100644 vcl/source/window/menufloatingwindow.hxx create mode 100644 vcl/source/window/menuitemlist.cxx create mode 100644 vcl/source/window/menuitemlist.hxx create mode 100644 vcl/source/window/menuwindow.cxx create mode 100644 vcl/source/window/menuwindow.hxx create mode 100644 vcl/source/window/mnemonic.cxx create mode 100644 vcl/source/window/mouse.cxx create mode 100644 vcl/source/window/paint.cxx create mode 100644 vcl/source/window/printdlg.cxx create mode 100644 vcl/source/window/scrwnd.cxx create mode 100644 vcl/source/window/seleng.cxx create mode 100644 vcl/source/window/settings.cxx create mode 100644 vcl/source/window/split.cxx create mode 100644 vcl/source/window/splitwin.cxx create mode 100644 vcl/source/window/stacking.cxx create mode 100644 vcl/source/window/status.cxx create mode 100644 vcl/source/window/syschild.cxx create mode 100644 vcl/source/window/syswin.cxx create mode 100644 vcl/source/window/tabdlg.cxx create mode 100644 vcl/source/window/tabpage.cxx create mode 100644 vcl/source/window/taskpanelist.cxx create mode 100644 vcl/source/window/toolbox.cxx create mode 100644 vcl/source/window/toolbox2.cxx create mode 100644 vcl/source/window/window.cxx create mode 100644 vcl/source/window/window2.cxx create mode 100644 vcl/source/window/window3.cxx create mode 100644 vcl/source/window/winproc.cxx create mode 100644 vcl/source/window/wrkwin.cxx (limited to 'vcl/source/window') diff --git a/vcl/source/window/EnumContext.cxx b/vcl/source/window/EnumContext.cxx new file mode 100644 index 000000000..6ca075eb6 --- /dev/null +++ b/vcl/source/window/EnumContext.cxx @@ -0,0 +1,215 @@ +/* -*- 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 + +#include +#include + +#include + +namespace vcl { + +namespace { + +typedef ::std::map ApplicationMap; + +ApplicationMap maApplicationMap; +o3tl::enumarray maApplicationVector; + +typedef ::std::map ContextMap; + +ContextMap maContextMap; +o3tl::enumarray 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_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 000000000..274b455bd --- /dev/null +++ b/vcl/source/window/NotebookBarAddonsMerger.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 + +#include + +#include +#include +#include +#include +#include +#include +#include + +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& 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& m_xFrame, + const AddonNotebookBarItem& aAddonNotebookBarItem, + const std::vector& aImageVec, const tools::ULong nIter) +{ + ToolBoxItemId nItemId(0); + ToolBox* pToolbox = dynamic_cast(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->SetItemCommand(nItemId, 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& m_xFrame, + const NotebookBarAddonsItem& aNotebookBarAddonsItem, + VclBuilder::stringmap& rMap) +{ + std::vector aImageVec = aNotebookBarAddonsItem.aImageValues; + tools::ULong nIter = 0; + sal_uInt16 nPriorityIdx = aImageVec.size(); + css::uno::Sequence> aExtension; + for (std::size_t nIdx = 0; nIdx < aNotebookBarAddonsItem.aAddonValues.size(); nIdx++) + { + aExtension = aNotebookBarAddonsItem.aAddonValues[nIdx]; + + for (const css::uno::Sequence& pExtension : + std::as_const(aExtension)) + { + VclPtr pOptionalParent; + pOptionalParent = VclPtr::Create(pParent); + pOptionalParent->Show(); + + vcl::IPrioritable* pPrioritable + = dynamic_cast(pOptionalParent.get()); + if (pPrioritable) + pPrioritable->SetPriority(nPriorityIdx - nIter); + + VclPtr 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 OString& sItemIdName, + NotebookBarAddonsItem& aNotebookBarAddonsItem) +{ + std::vector aImageVec = aNotebookBarAddonsItem.aImageValues; + tools::ULong nIter = 0; + css::uno::Sequence> 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 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 000000000..28055f7e2 --- /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 +#include + +/* + * 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 000000000..29d79b0cd --- /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 +#include +#include + +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( + 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 VclAbstractDialog::getAllPageUIXMLDescriptions() const +{ + // default has no pages + return std::vector(); +} + +bool VclAbstractDialog::selectPageByUIXMLDescription(const OString& /*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 000000000..a20c289f2 --- /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 +#include +#include + +#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(nMid); + } + } + while ( nLow <= nHigh ); + + if ( pIndex ) + { + if ( nId > nCompareId ) + *pIndex = static_cast(nMid+1); + else + *pIndex = static_cast(nMid); + } + + return ACCELENTRY_NOTFOUND; +} + +static void ImplAccelEntryInsert( ImplAccelList* pList, std::unique_ptr pEntry ) +{ + sal_uInt16 nInsIndex(0); + std::vector::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& i : rAccelData.maIdList) + { + std::unique_ptr 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& 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 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(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 000000000..d332da62a --- /dev/null +++ b/vcl/source/window/accessibility.cxx @@ -0,0 +1,612 @@ +/* -*- 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 +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +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; + else + // #101741 do not check for WB_CLOSEABLE because undecorated floaters (like menus!) are closeable + if( mpWindowImpl->mbFrame && mpWindowImpl->mnStyle & (WB_MOVEABLE | WB_SIZEABLE) ) + return true; + else + return false; +} + +bool Window::ImplIsAccessibleNativeFrame() 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; +} + +vcl::Window* Window::GetAccessibleParentWindow() const +{ + if (!mpWindowImpl || ImplIsAccessibleNativeFrame()) + 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(this); + if( pBorderWindow->mpMenuBarWindow && + pBorderWindow->mpMenuBarWindow->IsVisible() + ) + --nChildren; + } + else if( GetType() == WindowType::WORKWINDOW ) + { + WorkWindow *pWorkWindow = static_cast(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(this)->GetMenuBar() ) + { + if( n == 0 ) + { + MenuBar *pMenuBar = static_cast(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(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 (ImplIsAccessibleNativeFrame() ) + 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 OutputDevice::GetNonMnemonicString( aAccessibleName ); +} + +void Window::SetAccessibleDescription( const OUString& rDescription ) +{ + if ( ! mpWindowImpl->mpAccessibleInfos ) + mpWindowImpl->mpAccessibleInfos.reset( new ImplAccessibleInfos ); + + std::optional& 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 > 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 +FindFocusedEditableText(uno::Reference const& xContext) +{ + if (!xContext.is()) + return uno::Reference(); + + uno::Reference xState = xContext->getAccessibleStateSet(); + if (xState.is()) + { + if (xState->contains(accessibility::AccessibleStateType::FOCUSED)) + { + uno::Reference xText(xContext, uno::UNO_QUERY); + if (xText.is()) + return xText; + if (xState->contains(accessibility::AccessibleStateType::MANAGES_DESCENDANTS)) + return uno::Reference(); + } + } + + bool bSafeToIterate = true; + sal_Int32 nCount = xContext->getAccessibleChildCount(); + if (nCount < 0 || nCount > SAL_MAX_UINT16 /* slow enough for anyone */) + bSafeToIterate = false; + if (!bSafeToIterate) + return uno::Reference(); + + for (sal_Int32 i = 0; i < xContext->getAccessibleChildCount(); ++i) + { + uno::Reference xChild = xContext->getAccessibleChild(i); + if (!xChild.is()) + continue; + uno::Reference xChildContext + = xChild->getAccessibleContext(); + if (!xChildContext.is()) + continue; + uno::Reference xText + = FindFocusedEditableText(xChildContext); + if (xText.is()) + return xText; + } + return uno::Reference(); +} + +/* 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 000000000..26ea9846e --- /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 +#include + +#include + +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 000000000..c003ed17b --- /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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + 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; + } + 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; + } + 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; + 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); + + 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()) + nState |= ControlState::FOCUSED; + else if(mbNWFBorder) + { + // FIXME: this is currently only on macOS, see if other platforms can profit + + // FIXME: for macOS focus rings all controls need to support GetNativeControlRegion + // for the dropdown style + if (pCtrl->HasFocus() || pCtrl->HasChildPathFocus()) + nState |= ControlState::FOCUSED; + } + + bool bMouseOver = false; + 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(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(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(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 pWin = pBorderWindow->ImplGetClientWindow()->ImplGetWindow(); + SystemWindow *pSysWin = dynamic_cast(pWin.get()); + DockingWindow *pDockWin = dynamic_cast(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(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(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(pBorderWindow->ImplGetClientWindow())->IsInPopupMode() ) + static_cast(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(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 + if (!(pData->mnTitleType & (BorderWindowTitleType::Normal | BorderWindowTitleType::Small))) + { + FloatingWindow* pWin = dynamic_cast(pData->mpBorderWindow->GetWindow(GetWindowType::Client)); + if (pWin) + { + vcl::Region aClipRgn(aInRect); + tools::Rectangle aItemClipRect(pWin->ImplGetItemEdgeClipRect()); + if (!aItemClipRect.IsEmpty()) + { + aItemClipRect.SetPos(pData->mpBorderWindow->AbsoluteScreenToOutputPixel(aItemClipRect.TopLeft())); + aClipRgn.Exclude(aItemClipRect); + 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 ) + { + if( nStyle & WB_SYSTEMCHILDWINDOW ) + { + mpWindowImpl->mbOverlapWin = true; + mpWindowImpl->mbFrame = true; + mbFrameBorder = false; + } + else if( nStyle & (WB_OWNERDRAWDECORATION | WB_POPUP) ) + { + mpWindowImpl->mbOverlapWin = true; + mpWindowImpl->mbFrame = true; + mbFrameBorder = (nOrgStyle & WB_NOBORDER) == 0; + } + else + { + mpWindowImpl->mbOverlapWin = true; + mpWindowImpl->mbFrame = true; + 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& rFrame, + const NotebookBarAddonsItem& aNotebookBarAddonsItem) +{ + if (mpNotebookBar) + mpNotebookBar.disposeAndClear(); + mpNotebookBar = VclPtr::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 000000000..1a4a46a3f --- /dev/null +++ b/vcl/source/window/bubblewindow.cxx @@ -0,0 +1,557 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include + +#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, const OUString& rTitle, + const OUString& rText, const Image& rImage ) + : FloatingWindow( pParent, WB_SYSTEMWINDOW + | WB_OWNERDRAWDECORATION + | WB_NOBORDER + ) + , maBubbleTitle( rTitle ) + , maBubbleText( rText ) + , maBubbleImage( rImage ) + , 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() ); + Point 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") + , mnIconID (0) + , 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); +} + +VclPtr MenuBarUpdateIconManager::GetBubbleWindow() +{ + if ( !mpIconSysWin ) + return nullptr; + + tools::Rectangle aIconRect = mpIconMBar->GetMenuBarButtonRectPixel( mnIconID ); + if( aIconRect.IsEmpty() ) + return nullptr; + + auto pBubbleWin = mpBubbleWin; + + if ( !pBubbleWin ) { + pBubbleWin = VclPtr::Create( mpIconSysWin, 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( false ); +} + +IMPL_LINK(MenuBarUpdateIconManager, WindowEventHdl, VclWindowEvent&, rEvent, void) +{ + VclEventId nEventID = rEvent.GetId(); + + if ( VclEventId::ObjectDying == nEventID ) + { + if ( mpIconSysWin == rEvent.GetWindow() ) + { + mpIconSysWin->RemoveEventListener( maWindowEventHdl ); + RemoveBubbleWindow( true ); + } + } + 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(rEvent.GetData()); + if ( pMBar && ( pMBar == mpIconMBar ) ) + RemoveBubbleWindow( true ); + } + else if ( ( nEventID == VclEventId::WindowMove ) || + ( nEventID == VclEventId::WindowResize ) ) + { + if ( ( mpIconSysWin == rEvent.GetWindow() ) && + mpBubbleWin && ( mpIconMBar != nullptr ) ) + { + tools::Rectangle aIconRect = mpIconMBar->GetMenuBarButtonRectPixel( mnIconID ); + 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(false); + + return false; +} + +IMPL_LINK_NOARG(MenuBarUpdateIconManager, WaitTimeOutHdl, Timer *, void) +{ + mpBubbleWin = GetBubbleWindow(); + + if ( mpBubbleWin ) + { + mpBubbleWin->Show(); + } +} + +MenuBarUpdateIconManager::~MenuBarUpdateIconManager() +{ + Application::RemoveEventListener( maApplicationEventHdl ); + + RemoveBubbleWindow(true); +} + +void MenuBarUpdateIconManager::SetShowMenuIcon(bool bShowMenuIcon) +{ + if ( bShowMenuIcon != mbShowMenuIcon ) + { + mbShowMenuIcon = bShowMenuIcon; + if ( bShowMenuIcon ) + Application::PostUserEvent(LINK(this, MenuBarUpdateIconManager, UserEventHdl)); + else + RemoveBubbleWindow( true ); + } +} + +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 *pSysWin, bool bAddEventHdl) +{ + if ( ! mbShowMenuIcon ) + return; + + MenuBar *pActiveMBar = pSysWin->GetMenuBar(); + if ( ( pSysWin != mpIconSysWin ) || ( pActiveMBar != mpIconMBar ) ) + { + if ( bAddEventHdl && mpIconSysWin ) + mpIconSysWin->RemoveEventListener( maWindowEventHdl ); + + RemoveBubbleWindow( true ); + + 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 ); + mnIconID = pActiveMBar->AddMenuBarButton( aImage, + LINK( this, MenuBarUpdateIconManager, ClickHdl ), + aBuf.makeStringAndClear() + ); + pActiveMBar->SetMenuBarButtonHighlightHdl( mnIconID, + LINK( this, MenuBarUpdateIconManager, HighlightHdl ) ); + } + mpIconMBar = pActiveMBar; + mpIconSysWin = pSysWin; + if ( bAddEventHdl && mpIconSysWin ) + mpIconSysWin->AddEventListener( maWindowEventHdl ); + } + + if ( mbShowBubble && pActiveMBar ) + { + mpBubbleWin = GetBubbleWindow(); + if ( mpBubbleWin ) + { + mpBubbleWin->Show(); + maTimeoutTimer.Start(); + } + mbShowBubble = false; + } +} + +void MenuBarUpdateIconManager::RemoveBubbleWindow( bool bRemoveIcon ) +{ + maWaitIdle.Stop(); + maTimeoutTimer.Stop(); + + if ( mpBubbleWin ) + { + mpBubbleWin.disposeAndClear(); + } + + if ( !bRemoveIcon ) + return; + + try { + if ( mpIconMBar && ( mnIconID != 0 ) ) + { + mpIconMBar->RemoveMenuBarButton( mnIconID ); + mpIconMBar = nullptr; + mnIconID = 0; + } + } + catch ( ... ) { + mpIconMBar = nullptr; + mnIconID = 0; + } + + mpIconSysWin = nullptr; +} + +/* 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 000000000..188fbb1ac --- /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& pWindow, vcl::RenderContext& rRenderContext) + : m_pBuffer(VclPtr::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 000000000..eafc829e5 --- /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 +#include + +namespace vcl +{ +/// Buffers drawing on a vcl::RenderContext using a VirtualDevice. +class VCL_DLLPUBLIC BufferDevice +{ + ScopedVclPtr m_pBuffer; + VclPtr m_pWindow; + vcl::RenderContext& m_rRenderContext; + bool m_bDisposed = false; + +public: + BufferDevice(const VclPtr& 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 000000000..d25426ced --- /dev/null +++ b/vcl/source/window/builder.cxx @@ -0,0 +1,4402 @@ +/* -*- 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 +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(DISABLE_DYNLOADING) || defined(LINUX) +#include +#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& 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 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 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(), 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, + bool /*bMobile*/) +{ + if (comphelper::LibreOfficeKit::isActive()) + return JSInstanceBuilder::CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage); + else + return ImplGetSVData()->mpDefInst->CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage); +} + +weld::Window* Application::GetFrameWeld(const css::uno::Reference& 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& 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::min() + nFactor)) || + (nValue > (std::numeric_limits::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"; + if (aSuffix != "\"" && aSuffix != sDoublePrime) + aStr += " "; + else + aSuffix = sDoublePrime; + } + else if (m_eSrcUnit == FieldUnit::FOOT) + { + OUString sPrime = u"\u2032"; + 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 xEntry, std::unique_ptr 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 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 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, + const OString& sID, const css::uno::Reference& rFrame, + bool bLegacy, const NotebookBarAddonsItem* pNotebookBarAddonsItem) + : m_pNotebookBarAddonsItem(pNotebookBarAddonsItem + ? new NotebookBarAddonsItem(*pNotebookBarAddonsItem) + : new NotebookBarAddonsItem{}) + , m_sID(sID) + , m_sHelpRoot(OUStringToOString(sUIFile, RTL_TEXTENCODING_UTF8)) + , m_pStringReplace(Translate::GetReadStringHook()) + , m_pParent(pParent) + , m_bToplevelParentFound(false) + , m_bLegacy(bLegacy) + , m_pParserState(new ParserState) + , m_xFrame(rFrame) +{ + m_bToplevelHasDeferredInit = pParent && + ((pParent->IsSystemWindow() && static_cast(pParent)->isDeferredInit()) || + (pParent->IsDockingWindow() && static_cast(pParent)->isDeferredInit())); + m_bToplevelHasDeferredProperties = m_bToplevelHasDeferredInit; + + sal_Int32 nIdx = m_sHelpRoot.lastIndexOf('.'); + if (nIdx != -1) + m_sHelpRoot = m_sHelpRoot.copy(0, nIdx); + m_sHelpRoot += "/"; + + OUString sUri = sUIDir + sUIFile; + + try + { + xmlreader::XmlReader reader(sUri); + + 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(mnemonicWidget.m_sID); + vcl::Window *pOther = get(mnemonicWidget.m_sValue.toUtf8()); + 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& elemMap : rMap) + { + const OString &rType = elemMap.first; + const OUString &rParam = elemMap.second; + if (rType == "role") + { + sal_Int16 role = BuilderUtils::getRoleFromName(rParam.toUtf8()); + if (role != com::sun::star::accessibility::AccessibleRole::UNKNOWN) + pSource->SetAccessibleRole(role); + } + else + { + vcl::Window *pTarget = get(rParam.toUtf8()); + 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(elem.m_sID); + RadioButton *pOther = get(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 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(pTarget); + ComboBox *pComboBoxTarget = dynamic_cast(pTarget); + SvTabListBox *pTreeBoxTarget = dynamic_cast(pTarget); + // pStore may be empty + const ListStore *pStore = get_model_by_name(elem.m_sValue.toUtf8()); + SAL_WARN_IF(!pListBoxTarget && !pComboBoxTarget && !pTreeBoxTarget && !dynamic_cast(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(elem.m_sID); + const TextBuffer *pBuffer = get_buffer_by_name(elem.m_sValue.toUtf8()); + 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(get(elem.m_sID)); + const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8()); + 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(get(elem.m_sID)); + const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8()); + 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(elem.m_sID); + const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8()); + 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(get(elem.m_sID)); + const Adjustment* pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8()); + 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 xGroup(std::make_shared()); + + for (auto const& elem : sizeGroup.m_aProperties) + { + const OString &rKey = elem.first; + const OUString &rValue = elem.second; + xGroup->set_property(rKey, rValue); + } + + for (auto const& elem : sizeGroup.m_aWidgets) + { + vcl::Window* pWindow = get(elem.getStr()); + pWindow->add_to_size_group(xGroup); + } + } + + //Set button images when everything has been imported + std::set aImagesToBeRemoved; + for (auto const& elem : m_pParserState->m_aButtonImageWidgetMaps) + { + PushButton *pTargetButton = nullptr; + RadioButton *pTargetRadio = nullptr; + Button *pTarget = nullptr; + + if (!elem.m_bRadio) + { + pTargetButton = get(elem.m_sID); + pTarget = pTargetButton; + } + else + { + pTargetRadio = get(elem.m_sID); + pTarget = pTargetRadio; + } + + FixedImage *pImage = get(elem.m_sValue.toUtf8()); + 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.toUtf8()); + 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.toUtf8()); + } + + //Set button menus when everything has been imported + for (auto const& elem : m_pParserState->m_aButtonMenuMaps) + { + MenuButton *pTarget = get(elem.m_sID); + PopupMenu *pMenu = get_menu(elem.m_sValue.toUtf8()); + 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(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::reverse_iterator aI = m_aChildren.rbegin(), + aEnd = m_aChildren.rend(); aI != aEnd; ++aI) + { + aI->m_pWindow.disposeAndClear(); + } + m_aChildren.clear(); + + for (std::vector::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(OString("pixbuf")); + if (aFind != rMap.end()) + { + sIconName = aFind->second; + rMap.erase(aFind); + } + } + { + VclBuilder::stringmap::iterator aFind = rMap.find(OString("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(OString("relief")); + if (aFind != rMap.end()) + { + if (aFind->second == "half") + nBits = WB_FLATBUTTON | WB_BEVELBUTTON; + else 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(OString("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(OString("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(OString("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(OString("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(OString("tooltip-text")); + if (aFind == rMap.end()) + aFind = rMap.find(OString("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(OString("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(OString("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(OString("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(OString("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(OString("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& 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); + } + + VclPtr