Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  layout.cxx

  Sprache: C
 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */


#include <sal/config.h>

// Needed since LLVM 15 libc++ (hence the ignored -Wunused-macros for older libc++) when
// #include <boost/multi_array.hpp> below includes Boost 1.79.0
// workdir/UnpackedTarball/boost/boost/functional.hpp using std::unary_function, but must
// come very early here in case <functional> is already (indirectly) included earlier:
#include <config_libcxx.h>
#if HAVE_LIBCPP
#if defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-macros"
#endif
// [-loplugin:reservedid]:
#define _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION
#if defined __clang__
#pragma clang diagnostic pop
#endif
#endif

#include <string_view>

#include <config_features.h>
#include <com/sun/star/accessibility/AccessibleRole.hpp>
#include <comphelper/base64.hxx>
#include <comphelper/lok.hxx>
#include <o3tl/enumarray.hxx>
#include <o3tl/enumrange.hxx>
#include <o3tl/string_view.hxx>
#include <tools/stream.hxx>
#include <utility>
#include <vcl/builder.hxx>
#include <vcl/toolkit/button.hxx>
#include <vcl/cvtgrf.hxx>
#include <vcl/decoview.hxx>
#include <vcl/help.hxx>
#include <vcl/toolkit/dialog.hxx>
#include <vcl/layout.hxx>
#include <vcl/toolkit/scrbar.hxx>
#include <vcl/stdtext.hxx>
#include <vcl/split.hxx>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/virdev.hxx>
#include <bitmaps.hlst>
#include <messagedialog.hxx>
#include <svdata.hxx>
#include <window.h>
#include <boost/multi_array.hpp>
#include <vcl/toolkit/vclmedit.hxx>
#include <vcl/uitest/uiobject.hxx>
#include <sal/log.hxx>
#include <tools/json_writer.hxx>

VclContainer::VclContainer(vcl::Window *pParent, WinBits nStyle)
    : Window(WindowType::CONTAINER)
    , m_bLayoutDirty(true)
{
    ImplInit(pParent, nStyle, nullptr);
    EnableChildTransparentMode();
    SetPaintTransparent(true);
    SetBackground();
}

sal_uInt16 VclContainer::getDefaultAccessibleRole() const
{
    return css::accessibility::AccessibleRole::PANEL;
}

Size VclContainer::GetOptimalSize() const
{
    return calculateRequisition();
}

void VclContainer::setLayoutPosSize(vcl::Window &rWindow, const Point &rPos, const Size &rSize)
{
    sal_Int32 nBorderWidth = rWindow.get_border_width();
    sal_Int32 nLeft = rWindow.get_margin_start() + nBorderWidth;
    sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth;
    sal_Int32 nRight = rWindow.get_margin_end() + nBorderWidth;
    sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth;
    Point aPos(rPos.X() + nLeft, rPos.Y() + nTop);
    Size aSize(rSize.Width() - nLeft - nRight, rSize.Height() - nTop - nBottom);
    rWindow.SetPosSizePixel(aPos, aSize);
}

void VclContainer::setLayoutAllocation(vcl::Window &rChild, const Point &rAllocPos, const Size &rChildAlloc)
{
    VclAlign eHalign = rChild.get_halign();
    VclAlign eValign = rChild.get_valign();

    //typical case
    if (eHalign == VclAlign::Fill && eValign == VclAlign::Fill)
    {
        setLayoutPosSize(rChild, rAllocPos, rChildAlloc);
        return;
    }

    Point aChildPos(rAllocPos);
    Size aChildSize(rChildAlloc);
    Size aChildPreferredSize(getLayoutRequisition(rChild));

    switch (eHalign)
    {
        case VclAlign::Fill:
            break;
        case VclAlign::Start:
            if (aChildPreferredSize.Width() < rChildAlloc.Width())
                aChildSize.setWidth( aChildPreferredSize.Width() );
            break;
        case VclAlign::End:
            if (aChildPreferredSize.Width() < rChildAlloc.Width())
                aChildSize.setWidth( aChildPreferredSize.Width() );
            aChildPos.AdjustX(rChildAlloc.Width() );
            aChildPos.AdjustX( -(aChildSize.Width()) );
            break;
        case VclAlign::Center:
            if (aChildPreferredSize.Width() < aChildSize.Width())
                aChildSize.setWidth( aChildPreferredSize.Width() );
            aChildPos.AdjustX((rChildAlloc.Width() - aChildSize.Width()) / 2 );
            break;
    }

    switch (eValign)
    {
        case VclAlign::Fill:
            break;
        case VclAlign::Start:
            if (aChildPreferredSize.Height() < rChildAlloc.Height())
                aChildSize.setHeight( aChildPreferredSize.Height() );
            break;
        case VclAlign::End:
            if (aChildPreferredSize.Height() < rChildAlloc.Height())
                aChildSize.setHeight( aChildPreferredSize.Height() );
            aChildPos.AdjustY(rChildAlloc.Height() );
            aChildPos.AdjustY( -(aChildSize.Height()) );
            break;
        case VclAlign::Center:
            if (aChildPreferredSize.Height() < aChildSize.Height())
                aChildSize.setHeight( aChildPreferredSize.Height() );
            aChildPos.AdjustY((rChildAlloc.Height() - aChildSize.Height()) / 2 );
            break;
    }

    setLayoutPosSize(rChild, aChildPos, aChildSize);
}

namespace
{
    Size subtractBorder(const vcl::Window &rWindow, const Size& rSize)
    {
        sal_Int32 nBorderWidth = rWindow.get_border_width();
        sal_Int32 nLeft = rWindow.get_margin_start() + nBorderWidth;
        sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth;
        sal_Int32 nRight = rWindow.get_margin_end() + nBorderWidth;
        sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth;
        Size aSize(rSize);
        return Size(aSize.Width() + nLeft + nRight, aSize.Height() + nTop + nBottom);
    }
}

Size VclContainer::getLayoutRequisition(const vcl::Window &rWindow)
{
    return subtractBorder(rWindow, rWindow.get_preferred_size());
}

void VclContainer::SetPosSizePixel(const Point& rAllocPos, const Size& rAllocation)
{
    bool bSizeChanged = rAllocation != GetOutputSizePixel();
    Window::SetPosSizePixel(rAllocPos, rAllocation);
    if (m_bLayoutDirty || bSizeChanged)
    {
        m_bLayoutDirty = false;
        setAllocation(rAllocation);
    }
}

void VclContainer::SetPosPixel(const Point& rAllocPos)
{
    Point aAllocPos = rAllocPos;
    sal_Int32 nBorderWidth = get_border_width();
    aAllocPos.AdjustX(nBorderWidth + get_margin_start() );
    aAllocPos.AdjustY(nBorderWidth + get_margin_top() );

    if (aAllocPos != GetPosPixel())
        Window::SetPosPixel(aAllocPos);
}

void VclContainer::SetSizePixel(const Size& rAllocation)
{
    Size aAllocation = rAllocation;
    sal_Int32 nBorderWidth = get_border_width();
    aAllocation.AdjustWidth( -(nBorderWidth*2 + get_margin_start() + get_margin_end()) );
    aAllocation.AdjustHeight( -(nBorderWidth*2 + get_margin_top() + get_margin_bottom()) );
    bool bSizeChanged = aAllocation != GetSizePixel();
    if (bSizeChanged)
        Window::SetSizePixel(aAllocation);
    if (m_bLayoutDirty || bSizeChanged)
    {
        m_bLayoutDirty = false;
        setAllocation(aAllocation);
    }
}

void VclContainer::queue_resize(StateChangedType eReason)
{
    m_bLayoutDirty = true;
    Window::queue_resize(eReason);
}

// support for screenshot context menu
void VclContainer::Command(const CommandEvent& rCEvt)
{
    if (CommandEventId::ContextMenu == rCEvt.GetCommand())
    {
        auto pParent = GetParent();
        if (pParent)
        {
            CommandEvent aCEvt(rCEvt.GetMousePosPixel() + GetPosPixel(), rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData());
            pParent->Command(aCEvt);
            return;
        }
    }

    // call parent (do not consume)
    Window::Command(rCEvt);
}

void VclBox::accumulateMaxes(const Size &rChildSize, Size &rSize) const
{
    tools::Long nSecondaryChildDimension = getSecondaryDimension(rChildSize);
    tools::Long nSecondaryBoxDimension = getSecondaryDimension(rSize);
    setSecondaryDimension(rSize, std::max(nSecondaryChildDimension, nSecondaryBoxDimension));

    tools::Long nPrimaryChildDimension = getPrimaryDimension(rChildSize);
    tools::Long nPrimaryBoxDimension = getPrimaryDimension(rSize);
    if (m_bHomogeneous)
        setPrimaryDimension(rSize, std::max(nPrimaryBoxDimension, nPrimaryChildDimension));
    else
        setPrimaryDimension(rSize, nPrimaryBoxDimension + nPrimaryChildDimension);
}

Size VclBox::calculateRequisition() const
{
    sal_uInt16 nVisibleChildren = 0;

    Size aSize;
    for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        ++nVisibleChildren;
        Size aChildSize = getLayoutRequisition(*pChild);

        tools::Long nPrimaryDimension = getPrimaryDimension(aChildSize);
        nPrimaryDimension += pChild->get_padding() * 2;
        setPrimaryDimension(aChildSize, nPrimaryDimension);

        accumulateMaxes(aChildSize, aSize);
    }

    return finalizeMaxes(aSize, nVisibleChildren);
}

void VclBox::setAllocation(const Size &rAllocation)
{
    sal_uInt16 nVisibleChildren = 0, nExpandChildren = 0;
    for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        ++nVisibleChildren;
        bool bExpand = getPrimaryDimensionChildExpand(*pChild);
        if (bExpand)
            ++nExpandChildren;
    }

    if (!nVisibleChildren)
        return;

    tools::Long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);

    tools::Long nHomogeneousDimension = 0, nExtraSpace = 0;
    if (m_bHomogeneous)
    {
        nHomogeneousDimension = (nAllocPrimaryDimension -
            (nVisibleChildren - 1) * m_nSpacing) / nVisibleChildren;
    }
    else if (nExpandChildren)
    {
        Size aRequisition = calculateRequisition();
        tools::Long nPrimaryDimension = getPrimaryDimension(rAllocation);
        nExtraSpace = (nPrimaryDimension - getPrimaryDimension(aRequisition)) / nExpandChildren;
    }

    //Split into those we pack from the start onwards, and those we pack from the end backwards
    o3tl::enumarray<VclPackType,std::vector<vcl::Window*>> aWindows;
    for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;

        VclPackType ePacking = pChild->get_pack_type();
        aWindows[ePacking].push_back(pChild);
    }

    //See VclBuilder::sortIntoBestTabTraversalOrder for why they are in visual
    //order under the parent which requires us to reverse them here to
    //pack from the end back
    std::reverse(aWindows[VclPackType::End].begin(),aWindows[VclPackType::End].end());

    for (VclPackType ePackType : o3tl::enumrange<VclPackType>())
    {
        Point aPos(0, 0);
        if (ePackType == VclPackType::End)
        {
            tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aPos);
            setPrimaryCoordinate(aPos, nPrimaryCoordinate + nAllocPrimaryDimension);
        }

        for (auto const& window : aWindows[ePackType])
        {
            vcl::Window *pChild = window;

            tools::Long nPadding = pChild->get_padding();

            Size aBoxSize;
            if (m_bHomogeneous)
                setPrimaryDimension(aBoxSize, nHomogeneousDimension);
            else
            {
                aBoxSize = getLayoutRequisition(*pChild);
                tools::Long nPrimaryDimension = getPrimaryDimension(aBoxSize);
                nPrimaryDimension += nPadding * 2;
                if (getPrimaryDimensionChildExpand(*pChild))
                    nPrimaryDimension += nExtraSpace;
                setPrimaryDimension(aBoxSize, nPrimaryDimension);
            }
            setSecondaryDimension(aBoxSize, getSecondaryDimension(rAllocation));

            Point aChildPos(aPos);
            Size aChildSize(aBoxSize);
            tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aPos);

            bool bFill = pChild->get_fill();
            if (bFill)
            {
                setPrimaryDimension(aChildSize, std::max(static_cast<tools::Long>(1),
                    std::min(getPrimaryDimension(rAllocation), getPrimaryDimension(aBoxSize) - nPadding * 2)));

                setPrimaryCoordinate(aChildPos, nPrimaryCoordinate + nPadding);
            }
            else
            {
                setPrimaryDimension(aChildSize,
                    getPrimaryDimension(getLayoutRequisition(*pChild)));

                setPrimaryCoordinate(aChildPos, nPrimaryCoordinate +
                    (getPrimaryDimension(aBoxSize) - getPrimaryDimension(aChildSize)) / 2);
            }

            tools::Long nDiff = getPrimaryDimension(aBoxSize) + m_nSpacing;
            if (ePackType == VclPackType::Start)
                setPrimaryCoordinate(aPos, nPrimaryCoordinate + nDiff);
            else
            {
                setPrimaryCoordinate(aPos, nPrimaryCoordinate - nDiff);
                setPrimaryCoordinate(aChildPos, getPrimaryCoordinate(aChildPos) -
                    getPrimaryDimension(aBoxSize));
            }

            setLayoutAllocation(*pChild, aChildPos, aChildSize);
        }
    }
}

bool VclBox::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "spacing")
        set_spacing(rValue.toInt32());
    else if (rKey == "homogeneous")
        set_homogeneous(toBool(rValue));
    else
        return VclContainer::set_property(rKey, rValue);
    return true;
}

void VclBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
    VclContainer::DumpAsPropertyTree(rJsonWriter);
    rJsonWriter.put("vertical", m_bVerticalContainer);
}

sal_uInt16 VclBox::getDefaultAccessibleRole() const
{
    // fdo#74284 call Boxes Panels, keep them as "Filler" under
    // at least Linux seeing as that's what Gtk3 did for GtkBoxes.
    // Though now with Gtk4 that uses GTK_ACCESSIBLE_ROLE_GROUP
    // which maps to ATSPI_ROLE_PANEL
#if defined(_WIN32)
    return css::accessibility::AccessibleRole::PANEL;
#else
    static sal_uInt16 eRole = Application::GetToolkitName() == "gtk4" ?
                              css::accessibility::AccessibleRole::PANEL :
                              css::accessibility::AccessibleRole::FILLER;
    return eRole;
#endif
}

#define DEFAULT_CHILD_MIN_WIDTH 85
#define DEFAULT_CHILD_MIN_HEIGHT 27

Size VclBox::finalizeMaxes(const Size &rSize, sal_uInt16 nVisibleChildren) const
{
    Size aRet;

    if (nVisibleChildren)
    {
        tools::Long nPrimaryDimension = getPrimaryDimension(rSize);
        if (m_bHomogeneous)
            nPrimaryDimension *= nVisibleChildren;
        setPrimaryDimension(aRet, nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
        setSecondaryDimension(aRet, getSecondaryDimension(rSize));
    }

    return aRet;
}

Size VclButtonBox::addReqGroups(const VclButtonBox::Requisition &rReq) const
{
    Size aRet;

    tools::Long nMainGroupDimension = getPrimaryDimension(rReq.m_aMainGroupSize);
    tools::Long nSubGroupDimension = getPrimaryDimension(rReq.m_aSubGroupSize);

    setPrimaryDimension(aRet, nMainGroupDimension + nSubGroupDimension);

    setSecondaryDimension(aRet,
        std::max(getSecondaryDimension(rReq.m_aMainGroupSize),
        getSecondaryDimension(rReq.m_aSubGroupSize)));

    return aRet;
}

static tools::Long getMaxNonOutlier(const std::vector<tools::Long> &rG, tools::Long nAvgDimension)
{
    tools::Long nMaxDimensionNonOutlier = 0;
    for (auto const& nPrimaryChildDimension : rG)
    {
        if (nPrimaryChildDimension < nAvgDimension * 1.5)
        {
            nMaxDimensionNonOutlier = std::max(nPrimaryChildDimension,
                nMaxDimensionNonOutlier);
        }
    }
    return nMaxDimensionNonOutlier;
}

static std::vector<tools::Long> setButtonSizes(const std::vector<tools::Long> &rG,
    const std::vector<bool> &rNonHomogeneous,
    tools::Long nAvgDimension, tools::Long nMaxNonOutlier, tools::Long nMinWidth)
{
    std::vector<tools::Long> aVec;
    //set everything < 1.5 times the average to the same width, leave the
    //outliers un-touched
    std::vector<bool>::const_iterator aJ = rNonHomogeneous.begin();
    auto nNonOutlierWidth = std::max(nMaxNonOutlier, nMinWidth);
    for (auto const& nPrimaryChildDimension : rG)
    {
        bool bNonHomogeneous = *aJ;
        if (!bNonHomogeneous && nPrimaryChildDimension < nAvgDimension * 1.5)
        {
            aVec.push_back(nNonOutlierWidth);
        }
        else
        {
            aVec.push_back(std::max(nPrimaryChildDimension, nMinWidth));
        }
        ++aJ;
    }
    return aVec;
}

VclButtonBox::Requisition VclButtonBox::calculatePrimarySecondaryRequisitions() const
{
    Requisition aReq;

    Size aMainGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
    Size aSubGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme

    tools::Long nMinMainGroupPrimary = getPrimaryDimension(aMainGroupSize);
    tools::Long nMinSubGroupPrimary = getPrimaryDimension(aSubGroupSize);
    tools::Long nMainGroupSecondary = getSecondaryDimension(aMainGroupSize);
    tools::Long nSubGroupSecondary = getSecondaryDimension(aSubGroupSize);

    bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center);

    std::vector<tools::Long> aMainGroupSizes;
    std::vector<bool> aMainGroupNonHomogeneous;
    std::vector<tools::Long> aSubGroupSizes;
    std::vector<bool> aSubGroupNonHomogeneous;

    for (const vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        Size aChildSize = getLayoutRequisition(*pChild);
        if (bIgnoreSecondaryPacking || !pChild->get_secondary())
        {
            //set the max secondary dimension
            nMainGroupSecondary = std::max(nMainGroupSecondary, getSecondaryDimension(aChildSize));
            //collect the primary dimensions
            aMainGroupSizes.push_back(getPrimaryDimension(aChildSize));
            aMainGroupNonHomogeneous.push_back(pChild->get_non_homogeneous());
        }
        else
        {
            nSubGroupSecondary = std::max(nSubGroupSecondary, getSecondaryDimension(aChildSize));
            aSubGroupSizes.push_back(getPrimaryDimension(aChildSize));
            aSubGroupNonHomogeneous.push_back(pChild->get_non_homogeneous());
        }
    }

    if (m_bHomogeneous)
    {
        tools::Long nMaxMainDimension = aMainGroupSizes.empty() ? 0 :
            *std::max_element(aMainGroupSizes.begin(), aMainGroupSizes.end());
        nMaxMainDimension = std::max(nMaxMainDimension, nMinMainGroupPrimary);
        tools::Long nMaxSubDimension = aSubGroupSizes.empty() ? 0 :
            *std::max_element(aSubGroupSizes.begin(), aSubGroupSizes.end());
        nMaxSubDimension = std::max(nMaxSubDimension, nMinSubGroupPrimary);
        tools::Long nMaxDimension = std::max(nMaxMainDimension, nMaxSubDimension);
        aReq.m_aMainGroupDimensions.resize(aMainGroupSizes.size(), nMaxDimension);
        aReq.m_aSubGroupDimensions.resize(aSubGroupSizes.size(), nMaxDimension);
    }
    else
    {
        //Ideally set everything to the same size, but find outlier widgets
        //that are way wider than the average and leave them
        //at their natural size and set the remainder to share the
        //max size of the remaining members of the buttonbox
        tools::Long nAccDimension = std::accumulate(aMainGroupSizes.begin(),
            aMainGroupSizes.end(), 0);
        nAccDimension = std::accumulate(aSubGroupSizes.begin(),
            aSubGroupSizes.end(), nAccDimension);

        size_t nTotalSize = aMainGroupSizes.size() + aSubGroupSizes.size();

        tools::Long nAvgDimension = nTotalSize ? nAccDimension / nTotalSize : 0;

        tools::Long nMaxMainNonOutlier = getMaxNonOutlier(aMainGroupSizes,
            nAvgDimension);
        tools::Long nMaxSubNonOutlier = getMaxNonOutlier(aSubGroupSizes,
            nAvgDimension);
        tools::Long nMaxNonOutlier = std::max(nMaxMainNonOutlier, nMaxSubNonOutlier);

        aReq.m_aMainGroupDimensions = setButtonSizes(aMainGroupSizes,
            aMainGroupNonHomogeneous,
            nAvgDimension, nMaxNonOutlier, nMinMainGroupPrimary);
        aReq.m_aSubGroupDimensions = setButtonSizes(aSubGroupSizes,
            aSubGroupNonHomogeneous,
            nAvgDimension, nMaxNonOutlier, nMinSubGroupPrimary);
    }

    if (!aReq.m_aMainGroupDimensions.empty())
    {
        setSecondaryDimension(aReq.m_aMainGroupSize, nMainGroupSecondary);
        setPrimaryDimension(aReq.m_aMainGroupSize,
            std::accumulate(aReq.m_aMainGroupDimensions.begin(),
                aReq.m_aMainGroupDimensions.end(), 0));
    }
    if (!aReq.m_aSubGroupDimensions.empty())
    {
        setSecondaryDimension(aReq.m_aSubGroupSize, nSubGroupSecondary);
        setPrimaryDimension(aReq.m_aSubGroupSize,
            std::accumulate(aReq.m_aSubGroupDimensions.begin(),
                aReq.m_aSubGroupDimensions.end(), 0));
    }

    return aReq;
}

Size VclButtonBox::addSpacing(const Size &rSize, sal_uInt16 nVisibleChildren) const
{
    Size aRet;

    if (nVisibleChildren)
    {
        tools::Long nPrimaryDimension = getPrimaryDimension(rSize);
        setPrimaryDimension(aRet,
            nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
        setSecondaryDimension(aRet, getSecondaryDimension(rSize));
    }

    return aRet;
}

Size VclButtonBox::calculateRequisition() const
{
    Requisition aReq(calculatePrimarySecondaryRequisitions());
    sal_uInt16 nVisibleChildren = aReq.m_aMainGroupDimensions.size() +
        aReq.m_aSubGroupDimensions.size();
    return addSpacing(addReqGroups(aReq), nVisibleChildren);
}

bool VclButtonBox::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "layout-style")
    {
        VclButtonBoxStyle eStyle = VclButtonBoxStyle::Default;
        if (rValue == "spread")
            eStyle = VclButtonBoxStyle::Spread;
        else if (rValue == "edge")
            eStyle = VclButtonBoxStyle::Edge;
        else if (rValue == "start")
            eStyle = VclButtonBoxStyle::Start;
        else if (rValue == "end")
            eStyle = VclButtonBoxStyle::End;
        else if (rValue == "center")
            eStyle = VclButtonBoxStyle::Center;
        else
        {
            SAL_WARN("vcl.layout""unknown layout style " << rValue);
        }
        m_eLayoutStyle = eStyle;
    }
    else
        return VclBox::set_property(rKey, rValue);
    return true;
}

void VclButtonBox::setAllocation(const Size &rAllocation)
{
    Requisition aReq(calculatePrimarySecondaryRequisitions());

    if (aReq.m_aMainGroupDimensions.empty() && aReq.m_aSubGroupDimensions.empty())
        return;

    tools::Long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);

    Point aMainGroupPos, aOtherGroupPos;
    int nSpacing = m_nSpacing;

    //To-Do, other layout styles
    switch (m_eLayoutStyle)
    {
        case VclButtonBoxStyle::Start:
            if (!aReq.m_aSubGroupDimensions.empty())
            {
                tools::Long nOtherPrimaryDimension = getPrimaryDimension(
                    addSpacing(aReq.m_aSubGroupSize, aReq.m_aSubGroupDimensions.size()));
                setPrimaryCoordinate(aOtherGroupPos,
                    nAllocPrimaryDimension - nOtherPrimaryDimension);
            }
            break;
        case VclButtonBoxStyle::Spread:
            if (!aReq.m_aMainGroupDimensions.empty())
            {
                tools::Long nMainPrimaryDimension = getPrimaryDimension(
                    addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
                tools::Long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
                nExtraSpace += (aReq.m_aMainGroupDimensions.size()-1) * nSpacing;
                nSpacing = nExtraSpace/(aReq.m_aMainGroupDimensions.size()+1);
                setPrimaryCoordinate(aMainGroupPos, nSpacing);
            }
            break;
        case VclButtonBoxStyle::Center:
            if (!aReq.m_aMainGroupDimensions.empty())
            {
                tools::Long nMainPrimaryDimension = getPrimaryDimension(
                    addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
                tools::Long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
                setPrimaryCoordinate(aMainGroupPos, nExtraSpace/2);
            }
            break;
        default:
            SAL_WARN("vcl.layout""todo unimplemented layout style");
            [[fallthrough]];
        case VclButtonBoxStyle::Default:
        case VclButtonBoxStyle::End:
            if (!aReq.m_aMainGroupDimensions.empty())
            {
                tools::Long nMainPrimaryDimension = getPrimaryDimension(
                    addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
                setPrimaryCoordinate(aMainGroupPos,
                    nAllocPrimaryDimension - nMainPrimaryDimension);
            }
            break;
    }

    Size aChildSize;
    setSecondaryDimension(aChildSize, getSecondaryDimension(rAllocation));

    std::vector<tools::Long>::const_iterator aPrimaryI = aReq.m_aMainGroupDimensions.begin();
    std::vector<tools::Long>::const_iterator aSecondaryI = aReq.m_aSubGroupDimensions.begin();
    bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center);
    for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;

        if (bIgnoreSecondaryPacking || !pChild->get_secondary())
        {
            tools::Long nMainGroupPrimaryDimension = *aPrimaryI++;
            setPrimaryDimension(aChildSize, nMainGroupPrimaryDimension);
            setLayoutAllocation(*pChild, aMainGroupPos, aChildSize);
            tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aMainGroupPos);
            setPrimaryCoordinate(aMainGroupPos, nPrimaryCoordinate + nMainGroupPrimaryDimension + nSpacing);
        }
        else
        {
            tools::Long nSubGroupPrimaryDimension = *aSecondaryI++;
            setPrimaryDimension(aChildSize, nSubGroupPrimaryDimension);
            setLayoutAllocation(*pChild, aOtherGroupPos, aChildSize);
            tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aOtherGroupPos);
            setPrimaryCoordinate(aOtherGroupPos, nPrimaryCoordinate + nSubGroupPrimaryDimension + nSpacing);
        }
    }
}

void VclButtonBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
    VclBox::DumpAsPropertyTree(rJsonWriter);
    rJsonWriter.put("type""buttonbox");

    switch(m_eLayoutStyle)
    {
        case VclButtonBoxStyle::Default:
            rJsonWriter.put("layoutstyle""default");
            break;

        case VclButtonBoxStyle::Spread:
            rJsonWriter.put("layoutstyle""spread");
            break;

        case VclButtonBoxStyle::Edge:
            rJsonWriter.put("layoutstyle""edge");
            break;

        case VclButtonBoxStyle::Center:
            rJsonWriter.put("layoutstyle""center");
            break;

        case VclButtonBoxStyle::Start:
            rJsonWriter.put("layoutstyle""start");
            break;

        case VclButtonBoxStyle::End:
            rJsonWriter.put("layoutstyle""end");
            break;
    }
}

namespace {

struct ButtonOrder
{
    std::u16string_view m_aType;
    int m_nPriority;
};

}

static int getButtonPriority(std::u16string_view rType)
{
    static const size_t N_TYPES = 6;
    static const ButtonOrder aDiscardCancelSave[N_TYPES] =
    {
        { u"discard", 0 },
        { u"cancel", 1 },
        { u"no", 2 },
        { u"save", 3 },
        { u"yes", 3 },
        { u"ok", 3 }
    };

    static const ButtonOrder aSaveDiscardCancel[N_TYPES] =
    {
        { u"save", 0 },
        { u"yes", 0 },
        { u"ok", 0 },
        { u"discard", 1 },
        { u"no", 1 },
        { u"cancel", 2 }
    };

    const ButtonOrder* pOrder = &aDiscardCancelSave[0];

    const OUString &rEnv = Application::GetDesktopEnvironment();

    if (rEnv.equalsIgnoreAsciiCase("windows") ||
        rEnv.equalsIgnoreAsciiCase("lxqt") ||
        rEnv.startsWithIgnoreAsciiCase("plasma"))
    {
        pOrder = &aSaveDiscardCancel[0];
    }

    for (size_t i = 0; i < N_TYPES; ++i, ++pOrder)
    {
        if (rType == pOrder->m_aType)
            return pOrder->m_nPriority;
    }

    return -1;
}

namespace {

class sortButtons
{
    bool m_bVerticalContainer;
public:
    explicit sortButtons(bool bVerticalContainer)
        : m_bVerticalContainer(bVerticalContainer)
    {
    }
    bool operator()(const vcl::Window *pA, const vcl::Window *pB) const;
};

}

bool sortButtons::operator()(const vcl::Window *pA, const vcl::Window *pB) const
{
    //sort into two groups of pack start and pack end
    VclPackType ePackA = pA->get_pack_type();
    VclPackType ePackB = pB->get_pack_type();
    if (ePackA < ePackB)
        return true;
    if (ePackA > ePackB)
        return false;
    bool bPackA = pA->get_secondary();
    bool bPackB = pB->get_secondary();
    if (!m_bVerticalContainer)
    {
        //for horizontal boxes group secondaries before primaries
        if (bPackA > bPackB)
            return true;
        if (bPackA < bPackB)
            return false;
    }
    else
    {
        //for vertical boxes group secondaries after primaries
        if (bPackA < bPackB)
            return true;
        if (bPackA > bPackB)
            return false;
    }

    //now order within groups according to platform rules
    return getButtonPriority(pA->get_id()) < getButtonPriority(pB->get_id());
}

void sort_native_button_order(const VclBox& rContainer)
{
    std::vector<vcl::Window*> aChilds;
    for (vcl::Window* pChild = rContainer.GetWindow(GetWindowType::FirstChild); pChild;
        pChild = pChild->GetWindow(GetWindowType::Next))
    {
        aChilds.push_back(pChild);
    }

    //sort child order within parent so that we match the platform
    //button order
    std::stable_sort(aChilds.begin(), aChilds.end(), sortButtons(rContainer.get_orientation()));
    BuilderUtils::reorderWithinParent(aChilds, true);
}

namespace {

struct GridEntry
{
    VclPtr<vcl::Window> pChild;
    sal_Int32 nSpanWidth;
    sal_Int32 nSpanHeight;
    int x;
    int y;
    GridEntry()
        : pChild(nullptr)
        , nSpanWidth(0)
        , nSpanHeight(0)
        , x(-1)
        , y(-1)
    {
    }
};

}

typedef boost::multi_array<GridEntry, 2> array_type;

#if defined _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4459)
#pragma warning(disable : 4996)
#endif
static array_type assembleGrid(const VclGrid &rGrid)
{
#if defined _MSC_VER
#pragma warning(pop)
#endif
    array_type A;

    for (vcl::Window* pChild = rGrid.GetWindow(GetWindowType::FirstChild); pChild;
        pChild = pChild->GetWindow(GetWindowType::Next))
    {
        sal_Int32 nLeftAttach = std::max<sal_Int32>(pChild->get_grid_left_attach(), 0);
        sal_Int32 nWidth = pChild->get_grid_width();
        sal_Int32 nMaxXPos = nLeftAttach+nWidth-1;

        sal_Int32 nTopAttach = std::max<sal_Int32>(pChild->get_grid_top_attach(), 0);
        sal_Int32 nHeight = pChild->get_grid_height();
        sal_Int32 nMaxYPos = nTopAttach+nHeight-1;

        sal_Int32 nCurrentMaxXPos = A.shape()[0]-1;
        sal_Int32 nCurrentMaxYPos = A.shape()[1]-1;
        if (nMaxXPos > nCurrentMaxXPos || nMaxYPos > nCurrentMaxYPos)
        {
            nCurrentMaxXPos = std::max(nMaxXPos, nCurrentMaxXPos);
            nCurrentMaxYPos = std::max(nMaxYPos, nCurrentMaxYPos);
            A.resize(boost::extents[nCurrentMaxXPos+1][nCurrentMaxYPos+1]);
        }

#if defined _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4459)
#pragma warning(disable : 4996)
#endif
        GridEntry &rEntry = A[nLeftAttach][nTopAttach];
#if defined _MSC_VER
#pragma warning(pop)
#endif
        rEntry.pChild = pChild;
        rEntry.nSpanWidth = nWidth;
        rEntry.nSpanHeight = nHeight;
        rEntry.x = nLeftAttach;
        rEntry.y = nTopAttach;

        for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
        {
            for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
            {
                GridEntry &rSpan = A[nLeftAttach+nSpanX][nTopAttach+nSpanY];
                rSpan.x = nLeftAttach;
                rSpan.y = nTopAttach;
            }
        }
    }

    //see if we have any empty rows/cols
    sal_Int32 nMaxX = A.shape()[0];
    sal_Int32 nMaxY = A.shape()[1];

    std::vector<bool> aNonEmptyCols(nMaxX);
    std::vector<bool> aNonEmptyRows(nMaxY);

    for (sal_Int32 x = 0; x < nMaxX; ++x)
    {
        for (sal_Int32 y = 0; y < nMaxY; ++y)
        {
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
#endif
            const GridEntry &rEntry = A[x][y];
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
#pragma GCC diagnostic pop
#endif
            const vcl::Window *pChild = rEntry.pChild;
            if (pChild && pChild->IsVisible())
            {
                aNonEmptyCols[x] = true;
                if (rGrid.get_column_homogeneous())
                {
                    for (sal_Int32 nSpanX = 1; nSpanX < rEntry.nSpanWidth; ++nSpanX)
                        aNonEmptyCols[x+nSpanX] = true;
                }
                aNonEmptyRows[y] = true;
                if (rGrid.get_row_homogeneous())
                {
                    for (sal_Int32 nSpanY = 1; nSpanY < rEntry.nSpanHeight; ++nSpanY)
                        aNonEmptyRows[y+nSpanY] = true;
                }
            }
        }
    }

    if (!rGrid.get_column_homogeneous())
    {
        //reduce the spans of elements that span empty columns
        for (sal_Int32 x = 0; x < nMaxX; ++x)
        {
            std::set<GridEntry*> candidates;
            for (sal_Int32 y = 0; y < nMaxY; ++y)
            {
                if (aNonEmptyCols[x])
                    continue;
                GridEntry &rSpan = A[x][y];
                //cell x/y is spanned by the widget at cell rSpan.x/rSpan.y,
                //just points back to itself if there's no cell spanning
                if ((rSpan.x == -1) || (rSpan.y == -1))
                {
                    //there is no entry for this cell, i.e. this is a cell
                    //with no widget in it, or spanned by any other widget
                    continue;
                }
                GridEntry &rEntry = A[rSpan.x][rSpan.y];
                candidates.insert(&rEntry);
            }
            for (auto const& candidate : candidates)
            {
                GridEntry *pEntry = candidate;
                --pEntry->nSpanWidth;
            }
        }
    }

    if (!rGrid.get_row_homogeneous())
    {
        //reduce the spans of elements that span empty rows
        for (sal_Int32 y = 0; y < nMaxY; ++y)
        {
            std::set<GridEntry*> candidates;
            for (sal_Int32 x = 0; x < nMaxX; ++x)
            {
                if (aNonEmptyRows[y])
                    continue;
                GridEntry &rSpan = A[x][y];
                //cell x/y is spanned by the widget at cell rSpan.x/rSpan.y,
                //just points back to itself if there's no cell spanning
                if ((rSpan.x == -1) || (rSpan.y == -1))
                {
                    //there is no entry for this cell, i.e. this is a cell
                    //with no widget in it, or spanned by any other widget
                    continue;
                }
                GridEntry &rEntry = A[rSpan.x][rSpan.y];
                candidates.insert(&rEntry);
            }
            for (auto const& candidate : candidates)
            {
                GridEntry *pEntry = candidate;
                --pEntry->nSpanHeight;
            }
        }
    }

    sal_Int32 nNonEmptyCols = std::count(aNonEmptyCols.begin(), aNonEmptyCols.end(), true);
    sal_Int32 nNonEmptyRows = std::count(aNonEmptyRows.begin(), aNonEmptyRows.end(), true);

    //make new grid without empty rows and columns
#if defined _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4459)
#pragma warning(disable : 4996)
#endif
    array_type B(boost::extents[nNonEmptyCols][nNonEmptyRows]);
#if defined _MSC_VER
#pragma warning(pop)
#endif
    for (sal_Int32 x = 0, x2 = 0; x < nMaxX; ++x)
    {
        if (!aNonEmptyCols[x])
            continue;
        for (sal_Int32 y = 0, y2 = 0; y < nMaxY; ++y)
        {
            if (!aNonEmptyRows[y])
                continue;
            GridEntry &rEntry = A[x][y];
            B[x2][y2++] = rEntry;
        }
        ++x2;
    }

    return B;
}

static bool isNullGrid(const array_type &A)
{
    sal_Int32 nMaxX = A.shape()[0];
    sal_Int32 nMaxY = A.shape()[1];

    return !nMaxX || !nMaxY;
}

static void calcMaxs(const array_type &A, std::vector<VclGrid::Value> &rWidths, std::vector<VclGrid::Value> &rHeights)
{
    sal_Int32 nMaxX = A.shape()[0];
    sal_Int32 nMaxY = A.shape()[1];

    rWidths.resize(nMaxX);
    rHeights.resize(nMaxY);

    //first use the non spanning entries to set default width/heights
    for (sal_Int32 x = 0; x < nMaxX; ++x)
    {
        for (sal_Int32 y = 0; y < nMaxY; ++y)
        {
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
#elif defined _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4459)
#pragma warning(disable : 4996)
#endif
            const GridEntry &rEntry = A[x][y];
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
#pragma GCC diagnostic pop
#elif defined _MSC_VER
#pragma warning(pop)
#endif
            const vcl::Window *pChild = rEntry.pChild;
            if (!pChild || !pChild->IsVisible())
                continue;

            sal_Int32 nWidth = rEntry.nSpanWidth;
            sal_Int32 nHeight = rEntry.nSpanHeight;

            for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
                rWidths[x+nSpanX].m_bExpand |= pChild->get_hexpand();

            for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
                rHeights[y+nSpanY].m_bExpand |= pChild->get_vexpand();

            if (nWidth == 1 || nHeight == 1)
            {
                Size aChildSize = VclContainer::getLayoutRequisition(*pChild);
                if (nWidth == 1)
                    rWidths[x].m_nValue = std::max(rWidths[x].m_nValue, aChildSize.Width());
                if (nHeight == 1)
                    rHeights[y].m_nValue = std::max(rHeights[y].m_nValue, aChildSize.Height());
            }
        }
    }

    //now use the spanning entries and split any extra sizes across expanding rows/cols
    //where possible
    for (sal_Int32 x = 0; x < nMaxX; ++x)
    {
        for (sal_Int32 y = 0; y < nMaxY; ++y)
        {
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
#endif
            const GridEntry &rEntry = A[x][y];
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
#pragma GCC diagnostic pop
#endif
            const vcl::Window *pChild = rEntry.pChild;
            if (!pChild || !pChild->IsVisible())
                continue;

            sal_Int32 nWidth = rEntry.nSpanWidth;
            sal_Int32 nHeight = rEntry.nSpanHeight;

            if (nWidth == 1 && nHeight == 1)
                continue;

            Size aChildSize = VclContainer::getLayoutRequisition(*pChild);

            if (nWidth > 1)
            {
                sal_Int32 nExistingWidth = 0;
                for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
                    nExistingWidth += rWidths[x+nSpanX].m_nValue;

                sal_Int32 nExtraWidth = aChildSize.Width() - nExistingWidth;

                if (nExtraWidth > 0)
                {
                    bool bForceExpandAll = false;
                    sal_Int32 nExpandables = 0;
                    for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
                        if (rWidths[x+nSpanX].m_bExpand)
                            ++nExpandables;
                    if (nExpandables == 0)
                    {
                        nExpandables = nWidth;
                        bForceExpandAll = true;
                    }

                    for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
                    {
                        if (rWidths[x+nSpanX].m_bExpand || bForceExpandAll)
                            rWidths[x+nSpanX].m_nValue += nExtraWidth/nExpandables;
                    }
                }
            }

            if (nHeight > 1)
            {
                sal_Int32 nExistingHeight = 0;
                for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
                    nExistingHeight += rHeights[y+nSpanY].m_nValue;

                sal_Int32 nExtraHeight = aChildSize.Height() - nExistingHeight;

                if (nExtraHeight > 0)
                {
                    bool bForceExpandAll = false;
                    sal_Int32 nExpandables = 0;
                    for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
                        if (rHeights[y+nSpanY].m_bExpand)
                            ++nExpandables;
                    if (nExpandables == 0)
                    {
                        nExpandables = nHeight;
                        bForceExpandAll = true;
                    }

                    for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
                    {
                        if (rHeights[y+nSpanY].m_bExpand || bForceExpandAll)
                            rHeights[y+nSpanY].m_nValue += nExtraHeight/nExpandables;
                    }
                }
            }
        }
    }
}

static bool compareValues(const VclGrid::Value &i, const VclGrid::Value &j)
{
    return i.m_nValue < j.m_nValue;
}

static VclGrid::Value accumulateValues(const VclGrid::Value &i, const VclGrid::Value &j)
{
    VclGrid::Value aRet;
    aRet.m_nValue = i.m_nValue + j.m_nValue;
    aRet.m_bExpand = i.m_bExpand || j.m_bExpand;
    return aRet;
}

Size VclGrid::calculateRequisition() const
{
    return calculateRequisitionForSpacings(get_row_spacing(), get_column_spacing());
}

Size VclGrid::calculateRequisitionForSpacings(sal_Int32 nRowSpacing, sal_Int32 nColSpacing) const
{
    array_type A = assembleGrid(*this);

    if (isNullGrid(A))
        return Size();

    std::vector<Value> aWidths;
    std::vector<Value> aHeights;
    calcMaxs(A, aWidths, aHeights);

    tools::Long nTotalWidth = 0;
    if (get_column_homogeneous())
    {
        nTotalWidth = std::max_element(aWidths.begin(), aWidths.end(), compareValues)->m_nValue;
        nTotalWidth *= aWidths.size();
    }
    else
    {
        nTotalWidth = std::accumulate(aWidths.begin(), aWidths.end(), Value(), accumulateValues).m_nValue;
    }

    nTotalWidth += nColSpacing * (aWidths.size()-1);

    tools::Long nTotalHeight = 0;
    if (get_row_homogeneous())
    {
        nTotalHeight = std::max_element(aHeights.begin(), aHeights.end(), compareValues)->m_nValue;
        nTotalHeight *= aHeights.size();
    }
    else
    {
        nTotalHeight = std::accumulate(aHeights.begin(), aHeights.end(), Value(), accumulateValues).m_nValue;
    }

    nTotalHeight += nRowSpacing * (aHeights.size()-1);

    return Size(nTotalWidth, nTotalHeight);
}

void VclGrid::setAllocation(const Size& rAllocation)
{
    array_type A = assembleGrid(*this);

    if (isNullGrid(A))
        return;

    sal_Int32 nMaxX = A.shape()[0];
    sal_Int32 nMaxY = A.shape()[1];

    Size aRequisition;
    std::vector<Value> aWidths(nMaxX);
    std::vector<Value> aHeights(nMaxY);
    if (!get_column_homogeneous() || !get_row_homogeneous())
    {
        aRequisition = calculateRequisition();
        calcMaxs(A, aWidths, aHeights);
    }

    sal_Int32 nColSpacing(get_column_spacing());
    sal_Int32 nRowSpacing(get_row_spacing());

    tools::Long nAvailableWidth = rAllocation.Width();
    if (nMaxX)
        nAvailableWidth -= nColSpacing * (nMaxX - 1);
    if (get_column_homogeneous())
    {
        for (sal_Int32 x = 0; x < nMaxX; ++x)
            aWidths[x].m_nValue = nAvailableWidth/nMaxX;
    }
    else if (rAllocation.Width() != aRequisition.Width())
    {
        sal_Int32 nExpandables = 0;
        for (sal_Int32 x = 0; x < nMaxX; ++x)
            if (aWidths[x].m_bExpand)
                ++nExpandables;
        tools::Long nExtraWidthForExpanders = nExpandables ? (rAllocation.Width() - aRequisition.Width()) / nExpandables : 0;

        //We don't fit and there is no volunteer to be shrunk
        if (!nExpandables && rAllocation.Width() < aRequisition.Width())
        {
            //first reduce spacing
            while (nColSpacing)
            {
                nColSpacing /= 2;
                aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing);
                if (aRequisition.Width() <= rAllocation.Width())
                    break;
            }

            //share out the remaining pain to everyone
            tools::Long nExtraWidth = (rAllocation.Width() - aRequisition.Width()) / nMaxX;

            for (sal_Int32 x = 0; x < nMaxX; ++x)
                aWidths[x].m_nValue += nExtraWidth;
        }

        if (nExtraWidthForExpanders)
        {
            for (sal_Int32 x = 0; x < nMaxX; ++x)
                if (aWidths[x].m_bExpand)
                    aWidths[x].m_nValue += nExtraWidthForExpanders;
        }
    }

    tools::Long nAvailableHeight = rAllocation.Height();
    if (nMaxY)
        nAvailableHeight -= nRowSpacing * (nMaxY - 1);
    if (get_row_homogeneous())
    {
        for (sal_Int32 y = 0; y < nMaxY; ++y)
            aHeights[y].m_nValue = nAvailableHeight/nMaxY;
    }
    else if (rAllocation.Height() != aRequisition.Height())
    {
        sal_Int32 nExpandables = 0;
        for (sal_Int32 y = 0; y < nMaxY; ++y)
            if (aHeights[y].m_bExpand)
                ++nExpandables;
        tools::Long nExtraHeightForExpanders = nExpandables ? (rAllocation.Height() - aRequisition.Height()) / nExpandables : 0;

        //We don't fit and there is no volunteer to be shrunk
        if (!nExpandables && rAllocation.Height() < aRequisition.Height())
        {
            //first reduce spacing
            while (nRowSpacing)
            {
                nRowSpacing /= 2;
                aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing);
                if (aRequisition.Height() <= rAllocation.Height())
                    break;
            }

            //share out the remaining pain to everyone
            tools::Long nExtraHeight = (rAllocation.Height() - aRequisition.Height()) / nMaxY;

            for (sal_Int32 y = 0; y < nMaxY; ++y)
                aHeights[y].m_nValue += nExtraHeight;
        }

        if (nExtraHeightForExpanders)
        {
            for (sal_Int32 y = 0; y < nMaxY; ++y)
                if (aHeights[y].m_bExpand)
                    aHeights[y].m_nValue += nExtraHeightForExpanders;
        }
    }

    Point aAllocPos(0, 0);
    for (sal_Int32 x = 0; x < nMaxX; ++x)
    {
        for (sal_Int32 y = 0; y < nMaxY; ++y)
        {
#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdangling-reference"
#endif
            GridEntry &rEntry = A[x][y];
#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
#pragma GCC diagnostic pop
#endif
            vcl::Window *pChild = rEntry.pChild;
            if (pChild)
            {
                Size aChildAlloc(0, 0);

                sal_Int32 nWidth = rEntry.nSpanWidth;
                for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
                    aChildAlloc.AdjustWidth(aWidths[x+nSpanX].m_nValue );
                aChildAlloc.AdjustWidth(nColSpacing*(nWidth-1) );

                sal_Int32 nHeight = rEntry.nSpanHeight;
                for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
                    aChildAlloc.AdjustHeight(aHeights[y+nSpanY].m_nValue );
                aChildAlloc.AdjustHeight(nRowSpacing*(nHeight-1) );

                setLayoutAllocation(*pChild, aAllocPos, aChildAlloc);
            }
            aAllocPos.AdjustY(aHeights[y].m_nValue + nRowSpacing );
        }
        aAllocPos.AdjustX(aWidths[x].m_nValue + nColSpacing );
        aAllocPos.setY( 0 );
    }
}

void VclGrid::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
    VclContainer::DumpAsPropertyTree(rJsonWriter);
    rJsonWriter.put("type""grid");
}

bool VclGrid::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "row-spacing")
        set_row_spacing(rValue.toInt32());
    else if (rKey == "column-spacing")
        set_column_spacing(rValue.toInt32());
    else if (rKey == "row-homogeneous")
        m_bRowHomogeneous = toBool(rValue);
    else if (rKey == "column-homogeneous")
        m_bColumnHomogeneous = toBool(rValue);
    else if (rKey == "n-rows")
        /*nothing to do*/;
    else
        return VclContainer::set_property(rKey, rValue);
    return true;
}

const vcl::Window *VclBin::get_child() const
{
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();

    return pWindowImpl->mpFirstChild;
}

vcl::Window *VclBin::get_child()
{
    return const_cast<vcl::Window*>(const_cast<const VclBin*>(this)->get_child());
}

Size VclBin::calculateRequisition() const
{
    const vcl::Window *pChild = get_child();
    if (pChild && pChild->IsVisible())
        return getLayoutRequisition(*pChild);
    return Size(0, 0);
}

void VclBin::setAllocation(const Size &rAllocation)
{
    vcl::Window *pChild = get_child();
    if (pChild && pChild->IsVisible())
        setLayoutAllocation(*pChild, Point(0, 0), rAllocation);
}

VclFrame::~VclFrame()
{
    disposeOnce();
}

void VclFrame::dispose()
{
    m_pLabel.reset();
    VclBin::dispose();
}

//To-Do, hook a DecorationView into VclFrame ?

Size VclFrame::calculateRequisition() const
{
    Size aRet(0, 0);

    const vcl::Window *pChild = get_child();
    const vcl::Window *pLabel = get_label_widget();

    if (pChild && pChild->IsVisible())
        aRet = getLayoutRequisition(*pChild);

    if (pLabel && pLabel->IsVisible())
    {
        Size aLabelSize = getLayoutRequisition(*pLabel);
        aRet.AdjustHeight(aLabelSize.Height() );
        aRet.setWidth( std::max(aLabelSize.Width(), aRet.Width()) );
    }

    return aRet;
}

void VclFrame::setAllocation(const Size &rAllocation)
{
    //SetBackground( Color(0xFF, 0x00, 0xFF) );

    Size aAllocation(rAllocation);
    Point aChildPos;

    vcl::Window *pChild = get_child();
    vcl::Window *pLabel = get_label_widget();

    if (pLabel && pLabel->IsVisible())
    {
        Size aLabelSize = getLayoutRequisition(*pLabel);
        aLabelSize.setHeight( std::min(aLabelSize.Height(), aAllocation.Height()) );
        aLabelSize.setWidth( std::min(aLabelSize.Width(), aAllocation.Width()) );
        setLayoutAllocation(*pLabel, aChildPos, aLabelSize);
        aAllocation.AdjustHeight( -(aLabelSize.Height()) );
        aChildPos.AdjustY(aLabelSize.Height() );
    }

    if (pChild && pChild->IsVisible())
        setLayoutAllocation(*pChild, aChildPos, aAllocation);
}

IMPL_LINK(VclFrame, WindowEventListener, VclWindowEvent&, rEvent, void)
{
    if (rEvent.GetId() == VclEventId::ObjectDying)
        designate_label(nullptr);
}

void VclFrame::designate_label(vcl::Window *pWindow)
{
    assert(!pWindow || pWindow->GetParent() == this);
    if (m_pLabel)
        m_pLabel->RemoveEventListener(LINK(this, VclFrame, WindowEventListener));
    m_pLabel = pWindow;
    if (m_pLabel)
        m_pLabel->AddEventListener(LINK(this, VclFrame, WindowEventListener));
}

const vcl::Window *VclFrame::get_label_widget() const
{
    if (m_pLabel)
        return m_pLabel;
    assert(GetChildCount() <= 2);
    //The label widget is normally the first (of two) children
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();
    if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //no label exists
        return nullptr;
    return pWindowImpl->mpFirstChild;
}

vcl::Window *VclFrame::get_label_widget()
{
    return const_cast<vcl::Window*>(const_cast<const VclFrame*>(this)->get_label_widget());
}

const vcl::Window *VclFrame::get_child() const
{
    //The child widget is the normally the last (of two) children
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();
    assert(GetChildCount() == 2 || pWindowImpl->mbInDispose);
    if (!m_pLabel)
        return pWindowImpl->mpLastChild;
    if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //only label exists
        return nullptr;
    return pWindowImpl->mpLastChild;
}

vcl::Window *VclFrame::get_child()
{
    return const_cast<vcl::Window*>(const_cast<const VclFrame*>(this)->get_child());
}

void VclFrame::set_label(const OUString &rLabel)
{
    vcl::Window *pLabel = get_label_widget();
    assert(pLabel);
    pLabel->SetText(rLabel);
}

OUString VclFrame::get_label() const
{
    const vcl::Window *pLabel = get_label_widget();
    assert(pLabel);
    return pLabel->GetText();
}

OUString VclFrame::getDefaultAccessibleName() const
{
    const vcl::Window *pLabel = get_label_widget();
    if (pLabel)
        return pLabel->GetAccessibleName();
    return VclBin::getDefaultAccessibleName();
}

void VclFrame::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
    VclBin::DumpAsPropertyTree(rJsonWriter);
    rJsonWriter.put("type""frame");
}

class DisclosureButton final : public CheckBox
{
    virtual void ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext) override
    {
        /* HACK: DisclosureButton is currently assuming, that the disclosure sign
           will fit into the rectangle occupied by a normal checkbox on all themes.
           If this does not hold true for some theme, ImplGetCheckImageSize
           would have to be overridden for DisclosureButton; also GetNativeControlRegion
           for ControlType::ListNode would have to be implemented and taken into account
        */


        tools::Rectangle aStateRect(GetStateRect());

        ImplControlValue aControlValue(GetState() == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off);
        tools::Rectangle aCtrlRegion(aStateRect);
        ControlState nState = ControlState::NONE;

        if (HasFocus())
            nState |= ControlState::FOCUSED;
        if (GetButtonState() & DrawButtonFlags::Default)
            nState |= ControlState::DEFAULT;
        if (Window::IsEnabled())
            nState |= ControlState::ENABLED;
        if (IsMouseOver() && GetMouseRect().Contains(GetPointerPosPixel()))
            nState |= ControlState::ROLLOVER;

        if (rRenderContext.DrawNativeControl(ControlType::ListNode, ControlPart::Entire, aCtrlRegion,
                                              nState, aControlValue, OUString()))
            return;

        ImplSVCtrlData& rCtrlData(ImplGetSVData()->maCtrlData);
        if (!rCtrlData.moDisclosurePlus)
            rCtrlData.moDisclosurePlus.emplace(StockImage::Yes, SV_DISCLOSURE_PLUS);
        if (!rCtrlData.moDisclosureMinus)
            rCtrlData.moDisclosureMinus.emplace(StockImage::Yes, SV_DISCLOSURE_MINUS);

        Image* pImg
            = IsChecked() ? &*rCtrlData.moDisclosureMinus : &*rCtrlData.moDisclosurePlus;

        DrawImageFlags nStyle = DrawImageFlags::NONE;
        if (!IsEnabled())
            nStyle |= DrawImageFlags::Disable;

        Size aSize(aStateRect.GetSize());
        Size aImgSize(pImg->GetSizePixel());
        Point aOff((aSize.Width() - aImgSize.Width()) / 2,
                   (aSize.Height() - aImgSize.Height()) / 2);
        aOff += aStateRect.TopLeft();
        rRenderContext.DrawImage(aOff, *pImg, nStyle);
    }

public:
    explicit DisclosureButton(vcl::Window* pParent)
        : CheckBox(pParent, 0)
    {
    }

    virtual void KeyInput( const KeyEvent& rKEvt ) override
    {
        vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();

        if( !aKeyCode.GetModifier()  &&
            ( ( aKeyCode.GetCode() == KEY_ADD ) ||
              ( aKeyCode.GetCode() == KEY_SUBTRACT ) )
            )
        {
            Check( aKeyCode.GetCode() == KEY_ADD );
        }
        else
            CheckBox::KeyInput( rKEvt );
    }
};

VclExpander::VclExpander(vcl::Window *pParent)
    : VclBin(pParent)
    , m_bResizeTopLevel(false)
    , m_pDisclosureButton(VclPtr<DisclosureButton>::Create(this))
{
    m_pDisclosureButton->SetToggleHdl(LINK(this, VclExpander, ClickHdl));
    m_pDisclosureButton->Show();
}

VclExpander::~VclExpander()
{
    disposeOnce();
}

bool VclExpander::get_expanded() const
{
    return m_pDisclosureButton->IsChecked();
}

void VclExpander::set_expanded(bool bExpanded)
{
    m_pDisclosureButton->Check(bExpanded);
}

void VclExpander::set_label(const OUString& rLabel)
{
    m_pDisclosureButton->SetText(rLabel);
}

OUString VclExpander::get_label() const
{
    return m_pDisclosureButton->GetText();
}

void VclExpander::dispose()
{
    m_pDisclosureButton.disposeAndClear();
    VclBin::dispose();
}

const vcl::Window *VclExpander::get_child() const
{
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();

    assert(pWindowImpl->mpFirstChild == m_pDisclosureButton);

    return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next);
}

vcl::Window *VclExpander::get_child()
{
    return const_cast<vcl::Window*>(const_cast<const VclExpander*>(this)->get_child());
}

Size VclExpander::calculateRequisition() const
{
    Size aRet(0, 0);

    WindowImpl* pWindowImpl = ImplGetWindowImpl();

    const vcl::Window *pChild = get_child();
    const vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild ? pWindowImpl->mpLastChild.get() : nullptr;

    if (pChild && pChild->IsVisible() && m_pDisclosureButton->IsChecked())
        aRet = getLayoutRequisition(*pChild);

    Size aExpanderSize = getLayoutRequisition(*m_pDisclosureButton);

    if (pLabel && pLabel->IsVisible())
    {
        Size aLabelSize = getLayoutRequisition(*pLabel);
        aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) );
        aExpanderSize.AdjustWidth(aLabelSize.Width() );
    }

    aRet.AdjustHeight(aExpanderSize.Height() );
    aRet.setWidth( std::max(aExpanderSize.Width(), aRet.Width()) );

    return aRet;
}

void VclExpander::setAllocation(const Size &rAllocation)
{
    Size aAllocation(rAllocation);
    Point aChildPos;

    WindowImpl* pWindowImpl = ImplGetWindowImpl();

    //The label widget is the last (of two) children
    vcl::Window *pChild = get_child();
    vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild.get() ? pWindowImpl->mpLastChild.get() : nullptr;

    Size aButtonSize = getLayoutRequisition(*m_pDisclosureButton);
    Size aLabelSize;
    Size aExpanderSize = aButtonSize;
    if (pLabel && pLabel->IsVisible())
    {
        aLabelSize = getLayoutRequisition(*pLabel);
        aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) );
        aExpanderSize.AdjustWidth(aLabelSize.Width() );
    }

    aExpanderSize.setHeight( std::min(aExpanderSize.Height(), aAllocation.Height()) );
    aExpanderSize.setWidth( std::min(aExpanderSize.Width(), aAllocation.Width()) );

    aButtonSize.setHeight( std::min(aButtonSize.Height(), aExpanderSize.Height()) );
    aButtonSize.setWidth( std::min(aButtonSize.Width(), aExpanderSize.Width()) );

    tools::Long nExtraExpanderHeight = aExpanderSize.Height() - aButtonSize.Height();
    Point aButtonPos(aChildPos.X(), aChildPos.Y() + nExtraExpanderHeight/2);
    setLayoutAllocation(*m_pDisclosureButton, aButtonPos, aButtonSize);

    if (pLabel && pLabel->IsVisible())
    {
        aLabelSize.setHeight( std::min(aLabelSize.Height(), aExpanderSize.Height()) );
        aLabelSize.setWidth( std::min(aLabelSize.Width(),
            aExpanderSize.Width() - aButtonSize.Width()) );

        tools::Long nExtraLabelHeight = aExpanderSize.Height() - aLabelSize.Height();
        Point aLabelPos(aChildPos.X() + aButtonSize.Width(), aChildPos.Y() + nExtraLabelHeight/2);
        setLayoutAllocation(*pLabel, aLabelPos, aLabelSize);
    }

    aAllocation.AdjustHeight( -(aExpanderSize.Height()) );
    aChildPos.AdjustY(aExpanderSize.Height() );

    if (pChild && pChild->IsVisible())
    {
        if (!m_pDisclosureButton->IsChecked())
            aAllocation = Size();
        setLayoutAllocation(*pChild, aChildPos, aAllocation);
    }
}

bool VclExpander::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "expanded")
        set_expanded(toBool(rValue));
    else if (rKey == "resize-toplevel")
        m_bResizeTopLevel = toBool(rValue);
    else
        return VclBin::set_property(rKey, rValue);
    return true;
}

void VclExpander::StateChanged(StateChangedType nType)
{
    VclBin::StateChanged( nType );

    if (nType == StateChangedType::InitShow)
    {
        vcl::Window *pChild = get_child();
        if (pChild)
            pChild->Show(m_pDisclosureButton->IsChecked());
    }
}

const vcl::Window *VclExpander::get_label_widget() const
{
    return m_pDisclosureButton;
}

vcl::Window *VclExpander::get_label_widget()
{
    return const_cast<vcl::Window*>(const_cast<const VclExpander*>(this)->get_label_widget());
}

void VclExpander::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
    VclContainer::DumpAsPropertyTree(rJsonWriter);
    rJsonWriter.put("type""expander");
}

FactoryFunction VclExpander::GetUITestFactory() const
{
    return ExpanderUIObject::create;
}

IMPL_LINK( VclExpander, ClickHdl, CheckBox&, rBtn, void )
{
    vcl::Window *pChild = get_child();
    if (pChild)
    {
        pChild->Show(rBtn.IsChecked());
        queue_resize();
        Dialog* pResizeDialog = m_bResizeTopLevel ? GetParentDialog() : nullptr;
        if (pResizeDialog)
            pResizeDialog->setOptimalLayoutSize(true);
    }
    maExpandedHdl.Call(*this);
}

VclScrolledWindow::VclScrolledWindow(vcl::Window *pParent)
    : VclBin(pParent, WB_HIDE | WB_CLIPCHILDREN | WB_AUTOHSCROLL | WB_AUTOVSCROLL | WB_TABSTOP)
    , m_bUserManagedScrolling(false)
    , m_eDrawFrameStyle(DrawFrameStyle::NONE)
    , m_eDrawFrameFlags(DrawFrameFlags::WindowBorder)
    , m_pVScroll(VclPtr<ScrollBar>::Create(this, WB_HIDE | WB_VERT))
    , m_pHScroll(VclPtr<ScrollBar>::Create(this, WB_HIDE | WB_HORZ))
    , m_aScrollBarBox(VclPtr<ScrollBarBox>::Create(this, WB_HIDE))
{
    SetType(WindowType::SCROLLWINDOW);

    AllSettings aAllSettings = GetSettings();
    StyleSettings aStyle = aAllSettings.GetStyleSettings();
    aStyle.SetMonoColor(aStyle.GetShadowColor());
    aAllSettings.SetStyleSettings(aStyle);
    GetOutDev()->SetSettings(aAllSettings);

    Link<ScrollBar*,void> aLink( LINK( this, VclScrolledWindow, ScrollBarHdl ) );
    m_pVScroll->SetScrollHdl(aLink);
    m_pHScroll->SetScrollHdl(aLink);

    m_nBorderWidth = CalcBorderWidth();
}

int VclScrolledWindow::CalcBorderWidth() const
{
    if (m_eDrawFrameStyle == DrawFrameStyle::NONE)
        return 0;
    const tools::Rectangle aRect(tools::Rectangle(Point(0, 0), Size(100, 100)));
    DecorationView aDecoView(const_cast<OutputDevice*>(GetOutDev()));
    // don't actually draw anything, just measure what size it would be and the diff is the desired border size to reserve
    const tools::Rectangle aContentRect = aDecoView.DrawFrame(aRect, m_eDrawFrameStyle, m_eDrawFrameFlags | DrawFrameFlags::NoDraw);
    const auto nBorderWidth = (aRect.GetWidth() - aContentRect.GetWidth()) / 2;
    return std::max<int>(nBorderWidth, 1);
}

void VclScrolledWindow::dispose()
{
    m_pVScroll.disposeAndClear();
    m_pHScroll.disposeAndClear();
    m_aScrollBarBox.disposeAndClear();
    VclBin::dispose();
}

IMPL_LINK_NOARG(VclScrolledWindow, ScrollBarHdl, ScrollBar*, void)
{
    vcl::Window *pChild = get_child();
    if (!pChild)
        return;

    assert(dynamic_cast<VclViewport*>(pChild) && "scrolledwindow child should be a Viewport");

    pChild = pChild->GetWindow(GetWindowType::FirstChild);

    if (!pChild)
        return;

    Point aWinPos(-m_pHScroll->GetThumbPos(), -m_pVScroll->GetThumbPos());
    pChild->SetPosPixel(aWinPos);
}

const vcl::Window *VclScrolledWindow::get_child() const
{
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();
    assert(GetChildCount() == 4 || pWindowImpl->mbInDispose);
    return pWindowImpl->mpLastChild;
}

vcl::Window *VclScrolledWindow::get_child()
{
    return const_cast<vcl::Window*>(const_cast<const VclScrolledWindow*>(this)->get_child());
}

Size VclScrolledWindow::calculateRequisition() const
{
    Size aRet(0, 0);

    const vcl::Window *pChild = get_child();
    if (pChild && pChild->IsVisible())
        aRet = getLayoutRequisition(*pChild);

    if (GetStyle() & WB_VSCROLL)
        aRet.AdjustWidth(getLayoutRequisition(*m_pVScroll).Width() );

    if (GetStyle() & WB_HSCROLL)
        aRet.AdjustHeight(getLayoutRequisition(*m_pHScroll).Height() );

    aRet.AdjustHeight(2 * m_nBorderWidth);
    aRet.AdjustWidth(2 * m_nBorderWidth);

    return aRet;
}

void VclScrolledWindow::InitScrollBars(const Size &rRequest)
{
    const vcl::Window *pChild = get_child();
    if (!pChild || !pChild->IsVisible())
        return;

    Size aOutSize(getVisibleChildSize());

    m_pVScroll->SetRangeMax(rRequest.Height());
    m_pVScroll->SetVisibleSize(aOutSize.Height());
    m_pVScroll->SetPageSize(16);

    m_pHScroll->SetRangeMax(rRequest.Width());
    m_pHScroll->SetVisibleSize(aOutSize.Width());
    m_pHScroll->SetPageSize(16);

    m_pVScroll->Scroll();
    m_pHScroll->Scroll();
}

void VclScrolledWindow::doSetAllocation(const Size &rAllocation, bool bRetryOnFailure)
{
    Size aChildReq;

    vcl::Window *pChild = get_child();
    if (pChild && pChild->IsVisible())
        aChildReq = getLayoutRequisition(*pChild);

    tools::Long nAvailHeight = rAllocation.Height() - 2 * m_nBorderWidth;
    tools::Long nAvailWidth = rAllocation.Width() - 2 * m_nBorderWidth;

    // vert. ScrollBar
    bool bShowVScroll;
    if (GetStyle() & WB_AUTOVSCROLL)
        bShowVScroll = nAvailHeight < aChildReq.Height();
    else
        bShowVScroll = (GetStyle() & WB_VSCROLL) != 0;

    if (bShowVScroll)
        nAvailWidth -= getLayoutRequisition(*m_pVScroll).Width();

    // horz. ScrollBar
    bool bShowHScroll;
    if (GetStyle() & WB_AUTOHSCROLL)
    {
        bShowHScroll = nAvailWidth < aChildReq.Width();

        if (bShowHScroll)
            nAvailHeight -= getLayoutRequisition(*m_pHScroll).Height();

        if (GetStyle() & WB_AUTOVSCROLL)
            bShowVScroll = nAvailHeight < aChildReq.Height();
    }
    else
        bShowHScroll = (GetStyle() & WB_HSCROLL) != 0;

    if (m_pHScroll->IsVisible() != bShowHScroll)
        m_pHScroll->Show(bShowHScroll);
    if (m_pVScroll->IsVisible() != bShowVScroll)
        m_pVScroll->Show(bShowVScroll);

    Size aInnerSize(rAllocation);
    aInnerSize.AdjustWidth(-2 * m_nBorderWidth);
    aInnerSize.AdjustHeight(-2 * m_nBorderWidth);

    bool bBothVisible = m_pVScroll->IsVisible() && m_pHScroll->IsVisible();
    auto nScrollBarWidth = getLayoutRequisition(*m_pVScroll).Width();
    auto nScrollBarHeight = getLayoutRequisition(*m_pHScroll).Height();

    if (m_pVScroll->IsVisible())
    {
        Point aScrollPos(rAllocation.Width() - nScrollBarWidth - m_nBorderWidth, m_nBorderWidth);
        Size aScrollSize(nScrollBarWidth, rAllocation.Height() - 2 * m_nBorderWidth);
        if (bBothVisible)
            aScrollSize.AdjustHeight(-nScrollBarHeight);
        setLayoutAllocation(*m_pVScroll, aScrollPos, aScrollSize);
        aInnerSize.AdjustWidth( -nScrollBarWidth );
    }

    if (m_pHScroll->IsVisible())
    {
        Point aScrollPos(m_nBorderWidth, rAllocation.Height() - nScrollBarHeight);
        Size aScrollSize(rAllocation.Width() - 2 * m_nBorderWidth, nScrollBarHeight);
        if (bBothVisible)
            aScrollSize.AdjustWidth(-nScrollBarWidth);
        setLayoutAllocation(*m_pHScroll, aScrollPos, aScrollSize);
        aInnerSize.AdjustHeight( -nScrollBarHeight );
    }

    if (bBothVisible)
    {
        Point aBoxPos(aInnerSize.Width() + m_nBorderWidth, aInnerSize.Height() + m_nBorderWidth);
        m_aScrollBarBox->SetPosSizePixel(aBoxPos, Size(nScrollBarWidth, nScrollBarHeight));
        m_aScrollBarBox->Show();
    }
    else
    {
        m_aScrollBarBox->Hide();
    }

    if (pChild && pChild->IsVisible())
    {
        assert(dynamic_cast<VclViewport*>(pChild) && "scrolledwindow child should be a Viewport");

        WinBits nOldBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL));

        setLayoutAllocation(*pChild, Point(m_nBorderWidth, m_nBorderWidth), aInnerSize);

        // tdf#128758 if the layout allocation triggered some callback that
        // immediately invalidates the layout by adding scrollbars then
        // normally this would simply retrigger layout and another toplevel
        // attempt is made later. But the initial layout attempt blocks
        // relayouts, so just make another single effort here.
        WinBits nNewBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL));
        if (nOldBits != nNewBits && bRetryOnFailure)
        {
            doSetAllocation(rAllocation, false);
            return;
        }
    }

    if (!m_bUserManagedScrolling)
        InitScrollBars(aChildReq);
}

void VclScrolledWindow::setAllocation(const Size &rAllocation)
{
    doSetAllocation(rAllocation, true);
}

Size VclScrolledWindow::getVisibleChildSize() const
{
    Size aRet(GetSizePixel());
    if (m_pVScroll->IsVisible())
        aRet.AdjustWidth( -(m_pVScroll->GetSizePixel().Width()) );
    if (m_pHScroll->IsVisible())
        aRet.AdjustHeight( -(m_pHScroll->GetSizePixel().Height()) );
    aRet.AdjustHeight(-2 * m_nBorderWidth);
    aRet.AdjustWidth(-2 * m_nBorderWidth);
    return aRet;
}

bool VclScrolledWindow::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "shadow-type" || rKey == "name")
    {
        if (rKey == "shadow-type")
        {
            // despite the style names, this looks like the best mapping
            if (rValue == "in")
                m_eDrawFrameStyle = DrawFrameStyle::Out;
            else if (rValue == "out")
                m_eDrawFrameStyle = DrawFrameStyle::In;
            else if (rValue == "etched-in")
                m_eDrawFrameStyle = DrawFrameStyle::DoubleOut;
            else if (rValue == "etched-out")
                m_eDrawFrameStyle = DrawFrameStyle::DoubleIn;
            else if (rValue == "none")
                m_eDrawFrameStyle = DrawFrameStyle::NONE;
        }
        else if (rKey == "name")
        {
            m_eDrawFrameFlags = DrawFrameFlags::WindowBorder;
            if (rValue == "monoborder")
                m_eDrawFrameFlags |= DrawFrameFlags::Mono;
        }

        auto nBorderWidth = CalcBorderWidth();
        if (m_nBorderWidth != nBorderWidth)
        {
            m_nBorderWidth = nBorderWidth;
            queue_resize();
        }

        return true;
    }

    bool bRet = VclBin::set_property(rKey, rValue);
    m_pVScroll->Show((GetStyle() & WB_VSCROLL) != 0);
    m_pHScroll->Show((GetStyle() & WB_HSCROLL) != 0);
    return bRet;
}

bool VclScrolledWindow::EventNotify(NotifyEvent& rNEvt)
{
    bool bDone = false;
    if ( rNEvt.GetType() == NotifyEventType::COMMAND )
    {
        const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
        if ( rCEvt.GetCommand() == CommandEventId::Wheel )
        {
            const CommandWheelData* pData = rCEvt.GetWheelData();
            if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
            {
                // tdf#140537 only handle scroll commands in the valid shown scrollbars
                bDone = HandleScrollCommand(rCEvt,
                                            m_pHScroll->IsVisible() ? m_pHScroll : nullptr,
                                            m_pVScroll->IsVisible() ? m_pVScroll : nullptr);
            }
        }
        else if (rCEvt.GetCommand() == CommandEventId::GesturePan)
        {
            bDone = HandleScrollCommand(rCEvt, m_pHScroll->IsVisible() ? m_pHScroll : nullptr,
                                        m_pVScroll->IsVisible() ? m_pVScroll : nullptr);
        }
    }

    return bDone || VclBin::EventNotify( rNEvt );
}

void VclScrolledWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
    VclBin::Paint(rRenderContext, rRect);
    if (m_eDrawFrameStyle == DrawFrameStyle::NONE)
        return;
    const tools::Rectangle aRect(tools::Rectangle(Point(0,0), GetSizePixel()));
    DecorationView aDecoView(&rRenderContext);
    const tools::Rectangle aContentRect = aDecoView.DrawFrame(aRect, m_eDrawFrameStyle, m_eDrawFrameFlags);
    const auto nBorderWidth = (aRect.GetWidth() - aContentRect.GetWidth()) / 2;
    SAL_WARN_IF(nBorderWidth > m_nBorderWidth, "vcl.layout""desired border at paint " <<
                nBorderWidth << " is larger than expected " << m_nBorderWidth);
}

namespace {
void lcl_dumpScrollbar(::tools::JsonWriter& rJsonWriter, const ScrollBar& rScrollBar)
{
    rJsonWriter.put("lower", rScrollBar.GetRangeMin());
    rJsonWriter.put("upper", rScrollBar.GetRangeMax());
    rJsonWriter.put("step_increment", rScrollBar.GetLineSize());
    rJsonWriter.put("page_increment", rScrollBar.GetPageSize());
    rJsonWriter.put("value", rScrollBar.GetThumbPos());
    rJsonWriter.put("page_size", rScrollBar.GetVisibleSize());
}
};

void VclScrolledWindow::DumpAsPropertyTree(::tools::JsonWriter& rJsonWriter)
{
    VclBin::DumpAsPropertyTree(rJsonWriter);

    rJsonWriter.put("user_managed_scrolling", m_bUserManagedScrolling);

    {
        auto aVertical = rJsonWriter.startNode("vertical");

        ScrollBar& rScrollBar = getVertScrollBar();
        lcl_dumpScrollbar(rJsonWriter, rScrollBar);

        WinBits nWinBits = GetStyle();
        if (nWinBits & WB_VSCROLL)
            rJsonWriter.put("policy""always");
        else if (nWinBits & WB_AUTOVSCROLL)
            rJsonWriter.put("policy""auto");
        else
            rJsonWriter.put("policy""never");
    }

    {
        auto aHorizontal = rJsonWriter.startNode("horizontal");

        ScrollBar& rScrollBar = getHorzScrollBar();
        lcl_dumpScrollbar(rJsonWriter, rScrollBar);

        WinBits nWinBits = GetStyle();
        if (nWinBits & WB_HSCROLL)
            rJsonWriter.put("policy""always");
        else if (nWinBits & WB_AUTOHSCROLL)
            rJsonWriter.put("policy""auto");
        else
            rJsonWriter.put("policy""never");
    }
}

void VclViewport::setAllocation(const Size &rAllocation)
{
    vcl::Window *pChild = get_child();
    if (!(pChild && pChild->IsVisible()))
        return;

    Size aReq(getLayoutRequisition(*pChild));
    aReq.setWidth( std::max(aReq.Width(), rAllocation.Width()) );
    aReq.setHeight( std::max(aReq.Height(), rAllocation.Height()) );
    Point aKeepPos(pChild->GetPosPixel());
    if (m_bInitialAllocation)
    {
        aKeepPos = Point(0, 0);
        m_bInitialAllocation = false;
    }
    setLayoutAllocation(*pChild, aKeepPos, aReq);
}

const vcl::Window *VclEventBox::get_child() const
{
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();

    assert(pWindowImpl->mpFirstChild.get() == m_aEventBoxHelper.get());

    return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next);
}

vcl::Window *VclEventBox::get_child()
{
    return const_cast<vcl::Window*>(const_cast<const VclEventBox*>(this)->get_child());
}

void VclEventBox::setAllocation(const Size& rAllocation)
{
    Point aChildPos(0, 0);
    for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        setLayoutAllocation(*pChild, aChildPos, rAllocation);
    }
}

Size VclEventBox::calculateRequisition() const
{
    Size aRet(0, 0);

    for (const vcl::Window* pChild = get_child(); pChild;
        pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        Size aChildSize = getLayoutRequisition(*pChild);
        aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) );
        aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) );
    }

    return aRet;
}

void VclEventBox::Command(const CommandEvent&)
{
    //discard events by default to block them reaching children
}

VclEventBox::~VclEventBox()
{
    disposeOnce();
}

void VclEventBox::dispose()
{
    m_aEventBoxHelper.disposeAndClear();
    VclBin::dispose();
}

void VclSizeGroup::trigger_queue_resize()
{
    //sufficient to trigger one widget to trigger all of them
    if (!m_aWindows.empty())
    {
        (*m_aWindows.begin())->queue_resize();
    }
}

void VclSizeGroup::set_ignore_hidden(bool bIgnoreHidden)
{
    if (bIgnoreHidden != m_bIgnoreHidden)
    {
        m_bIgnoreHidden = bIgnoreHidden;
        trigger_queue_resize();
    }
}

void VclSizeGroup::set_mode(VclSizeGroupMode eMode)
{
    if (eMode != m_eMode)
    {
        m_eMode = eMode;
        trigger_queue_resize();
    }

}

void VclSizeGroup::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "ignore-hidden")
        set_ignore_hidden(toBool(rValue));
    else if (rKey == "mode")
    {
        VclSizeGroupMode eMode = VclSizeGroupMode::Horizontal;
        if (rValue == "none")
            eMode = VclSizeGroupMode::NONE;
        else if (rValue == "horizontal")
            eMode = VclSizeGroupMode::Horizontal;
        else if (rValue == "vertical")
            eMode = VclSizeGroupMode::Vertical;
        else if (rValue == "both")
            eMode = VclSizeGroupMode::Both;
        else
        {
            SAL_WARN("vcl.layout""unknown size group mode" << rValue);
        }
        set_mode(eMode);
    }
    else
    {
        SAL_INFO("vcl.layout""unhandled property: " << rKey);
    }
}

void MessageDialog::create_message_area()
{
    setDeferredProperties();

    if (m_pGrid)
        return;

    VclContainer *pContainer = get_content_area();
    assert(pContainer);

    m_pGrid.reset( VclPtr<VclGrid>::Create(pContainer) );
    m_pGrid->reorderWithinParent(0);
    m_pGrid->set_column_spacing(12);
    m_pMessageBox.reset(VclPtr<VclVBox>::Create(m_pGrid));
    m_pMessageBox->set_grid_left_attach(1);
    m_pMessageBox->set_grid_top_attach(0);
    m_pMessageBox->set_spacing(GetTextHeight());

    m_pImage = VclPtr<FixedImage>::Create(m_pGrid, WB_CENTER | WB_VCENTER | WB_3DLOOK);
    switch (m_eMessageType)
    {
        case VclMessageType::Info:
            m_pImage->SetImage(GetStandardInfoBoxImage());
            break;
        case VclMessageType::Warning:
            m_pImage->SetImage(GetStandardWarningBoxImage());
            break;
        case VclMessageType::Question:
            m_pImage->SetImage(GetStandardQueryBoxImage());
            break;
        case VclMessageType::Error:
            m_pImage->SetImage(GetStandardErrorBoxImage());
            break;
        case VclMessageType::Other:
            break;
    }
    m_pImage->set_grid_left_attach(0);
    m_pImage->set_grid_top_attach(0);
    m_pImage->set_valign(VclAlign::Start);
    m_pImage->Show(m_eMessageType != VclMessageType::Other);

    WinBits nWinStyle = WB_CLIPCHILDREN | WB_LEFT | WB_VCENTER | WB_NOLABEL | WB_NOTABSTOP;

    bool bHasSecondaryText = !m_sSecondaryString.isEmpty();

    m_pPrimaryMessage = VclPtr<VclMultiLineEdit>::Create(m_pMessageBox, nWinStyle);
    m_pPrimaryMessage->SetPaintTransparent(true);
    m_pPrimaryMessage->EnableCursor(false);

    m_pPrimaryMessage->set_hexpand(true);
    m_pPrimaryMessage->SetText(m_sPrimaryString);
    m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty());

    m_pSecondaryMessage = VclPtr<VclMultiLineEdit>::Create(m_pMessageBox, nWinStyle);
    m_pSecondaryMessage->SetPaintTransparent(true);
    m_pSecondaryMessage->EnableCursor(false);
    m_pSecondaryMessage->set_hexpand(true);
    m_pSecondaryMessage->SetText(m_sSecondaryString);
    m_pSecondaryMessage->Show(bHasSecondaryText);

    MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, bHasSecondaryText ? m_pSecondaryMessage.get() : nullptr);

    VclButtonBox *pButtonBox = get_action_area();
    assert(pButtonBox);

    VclPtr<PushButton> pBtn;
    short nDefaultResponse = get_default_response();
    switch (m_eButtonsType)
    {
        case VclButtonsType::NONE:
            break;
        case VclButtonsType::Ok:
            pBtn.reset( VclPtr<OKButton>::Create(pButtonBox) );
            pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
            pBtn->Show();
            pBtn->set_id(u"ok"_ustr);
            add_button(pBtn, RET_OK, true);
            nDefaultResponse = RET_OK;
            break;
        case VclButtonsType::Close:
            pBtn.reset( VclPtr<CloseButton>::Create(pButtonBox) );
            pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
            pBtn->Show();
            pBtn->set_id(u"close"_ustr);
            add_button(pBtn, RET_CLOSE, true);
            nDefaultResponse = RET_CLOSE;
            break;
        case VclButtonsType::Cancel:
            pBtn.reset( VclPtr<CancelButton>::Create(pButtonBox) );
            pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
            pBtn->Show();
            pBtn->set_id(u"cancel"_ustr);
            add_button(pBtn, RET_CANCEL, true);
            nDefaultResponse = RET_CANCEL;
            break;
        case VclButtonsType::YesNo:
            pBtn = VclPtr<PushButton>::Create(pButtonBox);
            pBtn->SetText(GetStandardText(StandardButtonType::Yes));
            pBtn->Show();
            pBtn->set_id(u"yes"_ustr);
            add_button(pBtn, RET_YES, true);

            pBtn.reset( VclPtr<PushButton>::Create(pButtonBox) );
            pBtn->SetText(GetStandardText(StandardButtonType::No));
            pBtn->Show();
            pBtn->set_id(u"no"_ustr);
            add_button(pBtn, RET_NO, true);
            nDefaultResponse = RET_NO;
            break;
        case VclButtonsType::OkCancel:
            pBtn.reset( VclPtr<OKButton>::Create(pButtonBox) );
            pBtn->Show();
            pBtn->set_id(u"ok"_ustr);
            add_button(pBtn, RET_OK, true);

            pBtn.reset( VclPtr<CancelButton>::Create(pButtonBox) );
            pBtn->Show();
            pBtn->set_id(u"cancel"_ustr);
            add_button(pBtn, RET_CANCEL, true);
            nDefaultResponse = RET_CANCEL;
            break;
    }
    set_default_response(nDefaultResponse);
    sort_native_button_order(*pButtonBox);
    m_pMessageBox->Show();
    m_pGrid->Show();
}

void MessageDialog::create_owned_areas()
{
#if defined _WIN32
    set_border_width(3);
#else
    set_border_width(12);
#endif
    m_pOwnedContentArea.reset(VclPtr<VclVBox>::Create(thisfalse, 24));
    set_content_area(m_pOwnedContentArea);
    m_pOwnedContentArea->Show();
    m_pOwnedActionArea.reset( VclPtr<VclHButtonBox>::Create(m_pOwnedContentArea) );
    set_action_area(m_pOwnedActionArea);
    m_pOwnedActionArea->Show();
}

MessageDialog::MessageDialog(vcl::Window* pParent, WinBits nStyle)
    : Dialog(pParent, nStyle)
    , m_eButtonsType(VclButtonsType::NONE)
    , m_eMessageType(VclMessageType::Info)
    , m_pOwnedContentArea(nullptr)
    , m_pOwnedActionArea(nullptr)
    , m_pGrid(nullptr)
    , m_pMessageBox(nullptr)
    , m_pImage(nullptr)
    , m_pPrimaryMessage(nullptr)
    , m_pSecondaryMessage(nullptr)
{
    SetType(WindowType::MESSBOX);
}

MessageDialog::MessageDialog(vcl::Window* pParent,
    OUString aMessage,
    VclMessageType eMessageType,
    VclButtonsType eButtonsType)
    : Dialog(pParent, WB_MOVEABLE | WB_3DLOOK | WB_CLOSEABLE)
    , m_eButtonsType(eButtonsType)
    , m_eMessageType(eMessageType)
    , m_pGrid(nullptr)
    , m_pMessageBox(nullptr)
    , m_pImage(nullptr)
    , m_pPrimaryMessage(nullptr)
    , m_pSecondaryMessage(nullptr)
    , m_sPrimaryString(std::move(aMessage))
{
    SetType(WindowType::MESSBOX);
    create_owned_areas();
    create_message_area();

    switch (m_eMessageType)
    {
        case VclMessageType::Info:
            SetText(GetStandardInfoBoxText());
            break;
        case VclMessageType::Warning:
            SetText(GetStandardWarningBoxText());
            break;
        case VclMessageType::Question:
            SetText(GetStandardQueryBoxText());
            break;
        case VclMessageType::Error:
            SetText(GetStandardErrorBoxText());
            SetTaskBarState(VclTaskBarStates::Error);
            break;
        case VclMessageType::Other:
            SetText(Application::GetDisplayName());
            break;
    }
}

void MessageDialog::dispose()
{
    SetTaskBarState(VclTaskBarStates::Normal);

    disposeOwnedButtons();
    m_pPrimaryMessage.disposeAndClear();
    m_pSecondaryMessage.disposeAndClear();
    m_pImage.disposeAndClear();
    m_pMessageBox.disposeAndClear();
    m_pGrid.disposeAndClear();
    m_pOwnedActionArea.disposeAndClear();
    m_pOwnedContentArea.disposeAndClear();
    Dialog::dispose();
}

MessageDialog::~MessageDialog()
{
    disposeOnce();
}

void MessageDialog::SetMessagesWidths(vcl::Window const *pParent,
    VclMultiLineEdit *pPrimaryMessage, VclMultiLineEdit *pSecondaryMessage)
{
    if (pSecondaryMessage)
    {
        assert(pPrimaryMessage);
        vcl::Font aFont = pParent->GetSettings().GetStyleSettings().GetLabelFont();
        aFont.SetFontSize(Size(0, aFont.GetFontSize().Height() * 1.2));
        aFont.SetWeight(WEIGHT_BOLD);
        pPrimaryMessage->SetControlFont(aFont);
        pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 44);
        pSecondaryMessage->SetMaxTextWidth(pSecondaryMessage->approximate_char_width() * 60);
    }
    else
        pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 60);
}

OUString const & MessageDialog::get_primary_text() const
{
    const_cast<MessageDialog*>(this)->setDeferredProperties();

    return m_sPrimaryString;
}

OUString const & MessageDialog::get_secondary_text() const
{
    const_cast<MessageDialog*>(this)->setDeferredProperties();

    return m_sSecondaryString;
}

bool MessageDialog::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "text")
        set_primary_text(rValue);
    else if (rKey == "secondary-text")
        set_secondary_text(rValue);
    else if (rKey == "message-type")
    {
        VclMessageType eMode = VclMessageType::Info;
        if (rValue == "info")
            eMode = VclMessageType::Info;
        else if (rValue == "warning")
            eMode = VclMessageType::Warning;
        else if (rValue == "question")
            eMode = VclMessageType::Question;
        else if (rValue == "error")
            eMode = VclMessageType::Error;
        else if (rValue == "other")
            eMode = VclMessageType::Other;
        else
        {
            SAL_WARN("vcl.layout""unknown message type mode" << rValue);
        }
        m_eMessageType = eMode;
    }
    else if (rKey == "buttons")
    {
        m_eButtonsType = BuilderBase::mapGtkToVclButtonsType(rValue);
    }
    else
        return Dialog::set_property(rKey, rValue);
    return true;
}

void MessageDialog::set_primary_text(const OUString &rPrimaryString)
{
    m_sPrimaryString = rPrimaryString;
    if (m_pPrimaryMessage)
    {
        m_pPrimaryMessage->SetText(m_sPrimaryString);
        m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty());
        MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr);
    }
}

void MessageDialog::set_secondary_text(const OUString &rSecondaryString)
{
    m_sSecondaryString = rSecondaryString;
    if (m_pSecondaryMessage)
    {
        m_pSecondaryMessage->SetText("\n" + m_sSecondaryString);
        m_pSecondaryMessage->Show(!m_sSecondaryString.isEmpty());
        MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr);
    }
}

void MessageDialog::StateChanged(StateChangedType nType)
{
    Dialog::StateChanged(nType);
    if (nType == StateChangedType::InitShow)
    {
        // MessageBox should be at least as wide as to see the title
        auto nTitleWidth = CalcTitleWidth();
        // Extra-Width for Close button
        nTitleWidth += mpWindowImpl->mnTopBorder;
        if (get_preferred_size().Width() < nTitleWidth)
        {
            set_width_request(nTitleWidth);
            DoInitialLayout();
        }
    }
}

VclPaned::VclPaned(vcl::Window *pParent, bool bVertical)
    : VclContainer(pParent, WB_HIDE | WB_CLIPCHILDREN)
    , m_pSplitter(VclPtr<Splitter>::Create(this, bVertical ? WB_VSCROLL : WB_HSCROLL))
    , m_nPosition(-1)
{
    m_pSplitter->SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetFaceColor()));
    m_pSplitter->Show();
}

void VclPaned::dispose()
{
    m_pSplitter.disposeAndClear();
    VclContainer::dispose();
}

VclVPaned::VclVPaned(vcl::Window *pParent)
    : VclPaned(pParent, true)
{
    m_pSplitter->SetSplitHdl(LINK(this, VclVPaned, SplitHdl));
}

IMPL_LINK(VclVPaned, SplitHdl, Splitter*, pSplitter, void)
{
    tools::Long nSize = pSplitter->GetSplitPosPixel();
    Size aSplitterSize(m_pSplitter->GetSizePixel());
    Size aAllocation(GetSizePixel());
    arrange(aAllocation, nSize, aAllocation.Height() - nSize - aSplitterSize.Height());
}

void VclVPaned::arrange(const Size& rAllocation, tools::Long nFirstHeight, tools::Long nSecondHeight)
{
    Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height());
    Size aFirstChildSize(rAllocation.Width(), nFirstHeight);
    Size aSecondChildSize(rAllocation.Width(), nSecondHeight);
    int nElement = 0;
    for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
        pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        if (nElement == 0)
        {
            Point aSplitterPos(0, aFirstChildSize.Height());
            setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize);
            m_nPosition = aSplitterPos.Y() + aSplitterSize.Height() / 2;
        }
        else if (nElement == 1)
        {
            Point aChildPos(0, 0);
            setLayoutAllocation(*pChild, aChildPos, aFirstChildSize);
        }
        else if (nElement == 2)
        {
            Point aChildPos(0, aFirstChildSize.Height() + aSplitterSize.Height());
            setLayoutAllocation(*pChild, aChildPos, aSecondChildSize);
        }
        ++nElement;
    }
}

void VclVPaned::set_position(tools::Long nPosition)
{
    VclPaned::set_position(nPosition);

    Size aAllocation(GetSizePixel());
    Size aSplitterSize(m_pSplitter->GetSizePixel());

    nPosition -= aSplitterSize.Height() / 2;

    arrange(aAllocation, nPosition, aAllocation.Height() - nPosition - aSplitterSize.Height());
}

void VclVPaned::setAllocation(const Size& rAllocation)
{
    //supporting "shrink" could be done by adjusting the allowed drag rectangle
    m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation));
    Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height());
    const tools::Long nHeight = rAllocation.Height() - aSplitterSize.Height();

    tools::Long nFirstHeight = 0;
    tools::Long nSecondHeight = 0;
    bool bFirstCanResize = true;
    bool bSecondCanResize = true;
    const bool bInitialAllocation = get_position() < 0;
    int nElement = 0;
    for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
        pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        if (nElement == 1)
        {
            if (bInitialAllocation)
                nFirstHeight = getLayoutRequisition(*pChild).Height();
            else
                nFirstHeight = pChild->GetSizePixel().Height() + pChild->get_margin_top() + pChild->get_margin_bottom();
            bFirstCanResize = pChild->get_expand();
        }
        else if (nElement == 2)
        {
            if (bInitialAllocation)
                nSecondHeight = getLayoutRequisition(*pChild).Height();
            else
                nSecondHeight = pChild->GetSizePixel().Height() + pChild->get_margin_top() + pChild->get_margin_bottom();
            bSecondCanResize = pChild->get_expand();
        }
        ++nElement;
    }
    tools::Long nHeightRequest = nFirstHeight + nSecondHeight;
    tools::Long nHeightDiff = nHeight - nHeightRequest;
    if (bFirstCanResize == bSecondCanResize)
        nFirstHeight += nHeightDiff/2;
    else if (bFirstCanResize)
        nFirstHeight += nHeightDiff;
    arrange(rAllocation, nFirstHeight, rAllocation.Height() - nFirstHeight - aSplitterSize.Height());
}

Size VclVPaned::calculateRequisition() const
{
    Size aRet(0, 0);

    for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
        pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        Size aChildSize = getLayoutRequisition(*pChild);
        aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) );
        aRet.AdjustHeight(aChildSize.Height() );
    }

    return aRet;
}

VclHPaned::VclHPaned(vcl::Window *pParent)
    : VclPaned(pParent, false)
{
    m_pSplitter->SetSplitHdl(LINK(this, VclHPaned, SplitHdl));
}

IMPL_LINK(VclHPaned, SplitHdl, Splitter*, pSplitter, void)
{
    tools::Long nSize = pSplitter->GetSplitPosPixel();
    Size aSplitterSize(m_pSplitter->GetSizePixel());
    Size aAllocation(GetSizePixel());
    arrange(aAllocation, nSize, aAllocation.Width() - nSize - aSplitterSize.Width());
}

void VclHPaned::arrange(const Size& rAllocation, tools::Long nFirstWidth, tools::Long nSecondWidth)
{
    Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height());
    Size aFirstChildSize(nFirstWidth, rAllocation.Height());
    Size aSecondChildSize(nSecondWidth, rAllocation.Height());
    int nElement = 0;
    for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
        pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        if (nElement == 0)
        {
            Point aSplitterPos(aFirstChildSize.Width(), 0);
            setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize);
            m_nPosition = aSplitterPos.X() + aSplitterSize.Width() / 2;
        }
        else if (nElement == 1)
        {
            Point aChildPos(0, 0);
            setLayoutAllocation(*pChild, aChildPos, aFirstChildSize);
        }
        else if (nElement == 2)
        {
            Point aChildPos(aFirstChildSize.Width() + aSplitterSize.Width(), 0);
            setLayoutAllocation(*pChild, aChildPos, aSecondChildSize);
        }
        ++nElement;
    }
}

void VclHPaned::set_position(tools::Long nPosition)
{
    VclPaned::set_position(nPosition);

    Size aAllocation(GetSizePixel());
    Size aSplitterSize(m_pSplitter->GetSizePixel());

    nPosition -= aSplitterSize.Width() / 2;

    arrange(aAllocation, nPosition, aAllocation.Width() - nPosition - aSplitterSize.Width());
}

void VclHPaned::setAllocation(const Size& rAllocation)
{
    //supporting "shrink" could be done by adjusting the allowed drag rectangle
    m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation));
    Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height());
    const tools::Long nWidth = rAllocation.Width() - aSplitterSize.Width();

    tools::Long nFirstWidth = 0;
    tools::Long nSecondWidth = 0;
    bool bFirstCanResize = true;
    bool bSecondCanResize = true;
    const bool bInitialAllocation = get_position() < 0;
    int nElement = 0;
    for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
        pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        if (nElement == 1)
        {
            if (bInitialAllocation)
                nFirstWidth = getLayoutRequisition(*pChild).Width();
            else
                nFirstWidth = pChild->GetSizePixel().Width() + pChild->get_margin_start() + pChild->get_margin_end();
            bFirstCanResize = pChild->get_expand();
        }
        else if (nElement == 2)
        {
            if (bInitialAllocation)
                nSecondWidth = getLayoutRequisition(*pChild).Width();
            else
                nSecondWidth = pChild->GetSizePixel().Width() + pChild->get_margin_start() + pChild->get_margin_end();
            bSecondCanResize = pChild->get_expand();
        }
        ++nElement;
    }
    tools::Long nWidthRequest = nFirstWidth + nSecondWidth;
    tools::Long nWidthDiff = nWidth - nWidthRequest;
    if (bFirstCanResize == bSecondCanResize)
        nFirstWidth += nWidthDiff/2;
    else if (bFirstCanResize)
        nFirstWidth += nWidthDiff;
    arrange(rAllocation, nFirstWidth, rAllocation.Width() - nFirstWidth - aSplitterSize.Width());
}

Size VclHPaned::calculateRequisition() const
{
    Size aRet(0, 0);

    for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
        pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;
        Size aChildSize = getLayoutRequisition(*pChild);
        aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) );
        aRet.AdjustWidth(aChildSize.Width() );
    }

    return aRet;
}

Size getLegacyBestSizeForChildren(const vcl::Window &rWindow)
{
    tools::Rectangle aBounds;

    for (const vcl::Window* pChild = rWindow.GetWindow(GetWindowType::FirstChild); pChild;
        pChild = pChild->GetWindow(GetWindowType::Next))
    {
        if (!pChild->IsVisible())
            continue;

        tools::Rectangle aChildBounds(pChild->GetPosPixel(), pChild->GetSizePixel());
        aBounds.Union(aChildBounds);
    }

    if (aBounds.IsEmpty())
        return rWindow.GetSizePixel();

    Size aRet(aBounds.GetSize());
    Point aTopLeft(aBounds.TopLeft());
    aRet.AdjustWidth(aTopLeft.X()*2 );
    aRet.AdjustHeight(aTopLeft.Y()*2 );

    return aRet;
}

vcl::Window* getNonLayoutParent(vcl::Window *pWindow)
{
    while (pWindow)
    {
        pWindow = pWindow->GetParent();
        if (!pWindow || !isContainerWindow(*pWindow))
            break;
    }
    return pWindow;
}

bool isVisibleInLayout(const vcl::Window *pWindow)
{
    bool bVisible = true;
    while (bVisible)
    {
        bVisible = pWindow->IsVisible();
        pWindow = pWindow->GetParent();
        if (!pWindow || !isContainerWindow(*pWindow))
            break;
    }
    return bVisible;
}

bool isEnabledInLayout(const vcl::Window *pWindow)
{
    bool bEnabled = true;
    while (bEnabled)
    {
        bEnabled = pWindow->IsEnabled();
        pWindow = pWindow->GetParent();
        if (!pWindow || !isContainerWindow(*pWindow))
            break;
    }
    return bEnabled;
}

bool isLayoutEnabled(const vcl::Window *pWindow)
{
    //Child is a container => we're layout enabled
    const vcl::Window *pChild = pWindow ? pWindow->GetWindow(GetWindowType::FirstChild) : nullptr;
    return pChild && isContainerWindow(*pChild) && !pChild->GetWindow(GetWindowType::Next);
}

void VclDrawingArea::RequestHelp(const HelpEvent& rHelpEvent)
{
    if (!(rHelpEvent.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON)))
        return;

    Point aPos(ScreenToOutputPixel(rHelpEvent.GetMousePosPixel()));
    tools::Rectangle aHelpArea(aPos.X(), aPos.Y());
    OUString sHelpTip = m_aQueryTooltipHdl.Call(aHelpArea);
    if (sHelpTip.isEmpty())
    {
        Control::RequestHelp(rHelpEvent);
        return;
    }
    Point aPt = OutputToScreenPixel(aHelpArea.TopLeft());
    aHelpArea.SetLeft(aPt.X());
    aHelpArea.SetTop(aPt.Y());
    aPt = OutputToScreenPixel(aHelpArea.BottomRight());
    aHelpArea.SetRight(aPt.X());
    aHelpArea.SetBottom(aPt.Y());
    // tdf#125369 recover newline support of tdf#101779
    QuickHelpFlags eHelpWinStyle = sHelpTip.indexOf('\n') != -1 ? QuickHelpFlags::TipStyleBalloon : QuickHelpFlags::NONE;
    Help::ShowQuickHelp(this, aHelpArea, sHelpTip, eHelpWinStyle);
}

void VclDrawingArea::StartDrag(sal_Int8, const Point&)
{
    if (m_aStartDragHdl.Call(this))
        return;

    rtl::Reference<TransferDataContainer> xContainer = m_xTransferHelper;
    if (!m_xTransferHelper.is())
        return;

    xContainer->StartDrag(this, m_nDragAction);
}

OUString VclDrawingArea::GetSurroundingText() const
{
    if (!m_aGetSurroundingHdl.IsSet())
        return Control::GetSurroundingText();
    OUString sSurroundingText;
    m_aGetSurroundingHdl.Call(sSurroundingText);
    return sSurroundingText;
}

Selection VclDrawingArea::GetSurroundingTextSelection() const
{
    if (!m_aGetSurroundingHdl.IsSet())
        return Control::GetSurroundingTextSelection();
    OUString sSurroundingText;
    int nCursor = m_aGetSurroundingHdl.Call(sSurroundingText);
    return Selection(nCursor, nCursor);
}

bool VclDrawingArea::DeleteSurroundingText(const Selection& rSelection)
{
    if (!m_aDeleteSurroundingHdl.IsSet())
        return Control::DeleteSurroundingText(rSelection);
    return m_aDeleteSurroundingHdl.Call(rSelection);
}

VclHPaned::~VclHPaned()
{
}

VclVPaned::~VclVPaned()
{
}

VclPaned::~VclPaned()
{
    disposeOnce();
}

VclScrolledWindow::~VclScrolledWindow()
{
    disposeOnce();
}

void VclDrawingArea::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
    Control::DumpAsPropertyTree(rJsonWriter);
    rJsonWriter.put("type""drawingarea");

    ScopedVclPtrInstance<VirtualDevice> pDevice;
    OutputDevice* pRefDevice = GetOutDev();
    Size aRenderSize(pRefDevice->PixelToLogic(GetOutputSizePixel()));
    Size aOutputSize = GetSizePixel();
    pDevice->SetOutputSize(aRenderSize);
    tools::Rectangle aRect(Point(0,0), aRenderSize);

    // Dark mode support
    pDevice->DrawWallpaper(aRect, pRefDevice->GetBackground());

    Paint(*pDevice, aRect);

    BitmapEx aImage = pDevice->GetBitmapEx(Point(0,0), aRenderSize);
    aImage.Scale(aOutputSize);
    rJsonWriter.put("imagewidth", aRenderSize.Width());
    rJsonWriter.put("imageheight", aRenderSize.Height());

    SvMemoryStream aOStm(65535, 65535);
    if(GraphicConverter::Export(aOStm, aImage, ConvertDataFormat::PNG) == ERRCODE_NONE)
    {
        css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
        OStringBuffer aBuffer("data:image/png;base64,");
        ::comphelper::Base64::encode(aBuffer, aSeq);
        rJsonWriter.put("image", aBuffer);
    }
    rJsonWriter.put("text", GetQuickHelpText());
}

FactoryFunction VclDrawingArea::GetUITestFactory() const
{
    if (m_pFactoryFunction)
        return m_pFactoryFunction;
    return DrawingAreaUIObject::create;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5 in Prozent
C=98 H=90 G=94

¤ Dauer der Verarbeitung: 0.55 Sekunden  (vorverarbeitet am  2026-05-05) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge