Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/source/window/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 101 kB image not shown  

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)
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=98 H=88 G=93

¤ Dauer der Verarbeitung: 0.19 Sekunden  ¤

*© 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.