    /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
    /*
     * This file is part of the LibreOffice project.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     *
     * This file incorporates work covered by the following license notice:
     *
     *   Licensed to the Apache Software Foundation (ASF) under one or more
     *   contributor license agreements. See the NOTICE file distributed
     *   with this work for additional information regarding copyright
     *   ownership. The ASF licenses this file to you under the Apache
     *   License, Version 2.0 (the "License"); you may not use this file
     *   except in compliance with the License. You may obtain a copy of
     *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
     */

    #include <checklistmenu.hxx>
    #include <globstr.hrc>
    #include <scresid.hxx>
    #include <strings.hrc>
    #include <bitmaps.hlst>

    #include <vcl/decoview.hxx>
    #include <vcl/settings.hxx>
    #include <tools/wintypes.hxx>

    #include <AccessibleFilterMenu.hxx>
    #include <AccessibleFilterTopWindow.hxx>

    #include <com/sun/star/accessibility/XAccessible.hpp>
    #include <com/sun/star/accessibility/XAccessibleContext.hpp>
    #include <svtools/fmtfield.hxx>
    #include <svtools/svlbitm.hxx>
    #include <svtools/treelistentry.hxx>
    #include <document.hxx>

    using namespace com::sun::star;
    using ::com::sun::star::uno::Reference;
    using ::com::sun::star::accessibility::XAccessible;
    using ::com::sun::star::accessibility::XAccessibleContext;
    using ::std::vector;

    ScMenuFloatingWindow::MenuItemData::MenuItemData() :
        mbEnabled(true), mbSeparator(false),
        mpAction(static_cast<ScCheckListMenuWindow::Action*>(nullptr)),
        mpSubMenuWin(static_cast<ScMenuFloatingWindow*>(nullptr))
    {
    }

    ScMenuFloatingWindow::SubMenuItemData::SubMenuItemData(ScMenuFloatingWindow* pParent) :
        mpSubMenu(nullptr),
        mnMenuPos(MENU_NOT_SELECTED),
        mpParent(pParent)
    {
        maTimer.SetInvokeHandler( LINK(this, ScMenuFloatingWindow::SubMenuItemData, TimeoutHdl) );
        maTimer.SetTimeout(mpParent->GetSettings().GetMouseSettings().GetMenuDelay());
    }

    void ScMenuFloatingWindow::SubMenuItemData::reset()
    {
        mpSubMenu = nullptr;
        mnMenuPos = MENU_NOT_SELECTED;
        maTimer.Stop();
    }

    IMPL_LINK_NOARG(ScMenuFloatingWindow::SubMenuItemData, TimeoutHdl, Timer *, void)
    {
        mpParent->handleMenuTimeout(this);
    }

    ScMenuFloatingWindow::ScMenuFloatingWindow(vcl::Window* pParent, ScDocument* pDoc, sal_uInt16 nMenuStackLevel) :
        PopupMenuFloatingWindow(pParent),
        maOpenTimer(this),
        maCloseTimer(this),
        maName("ScMenuFloatingWindow"),
        mnSelectedMenu(MENU_NOT_SELECTED),
        mnClickedMenu(MENU_NOT_SELECTED),
        mpDoc(pDoc),
        mpParentMenu(dynamic_cast<ScMenuFloatingWindow*>(pParent))
    {
        SetMenuStackLevel(nMenuStackLevel);
        SetText("ScMenuFloatingWindow");

        const StyleSettings& rStyle = GetSettings().GetStyleSettings();

        const sal_uInt16 nPopupFontHeight = 12 * GetDPIScaleFactor();
        maLabelFont = rStyle.GetLabelFont();
        maLabelFont.SetFontHeight(nPopupFontHeight);
    }

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

    void ScMenuFloatingWindow::dispose()
    {
        EndPopupMode();
        for (auto i = maMenuItems.begin(); i != maMenuItems.end(); ++i)
            i->mpSubMenuWin.disposeAndClear();
        mpParentMenu.clear();
        PopupMenuFloatingWindow::dispose();
    }

    void ScMenuFloatingWindow::PopupModeEnd()
    {
        handlePopupEnd();
    }

    void ScMenuFloatingWindow::MouseMove(const MouseEvent& rMEvt)
    {
        const Point& rPos = rMEvt.GetPosPixel();
        size_t nSelectedMenu = getEnclosingMenuItem(rPos);
        setSelectedMenuItem(nSelectedMenu, true, false);

        Window::MouseMove(rMEvt);
    }

    void ScMenuFloatingWindow::MouseButtonDown(const MouseEvent& rMEvt)
    {
        const Point& rPos = rMEvt.GetPosPixel();
        mnClickedMenu = getEnclosingMenuItem(rPos);
        Window::MouseButtonDown(rMEvt);
    }

    void ScMenuFloatingWindow::MouseButtonUp(const MouseEvent& rMEvt)
    {
        executeMenuItem(mnClickedMenu);
        mnClickedMenu = MENU_NOT_SELECTED;
        Window::MouseButtonUp(rMEvt);
    }

    void ScMenuFloatingWindow::KeyInput(const KeyEvent& rKEvt)
    {
        if (maMenuItems.empty())
        {
            Window::KeyInput(rKEvt);
            return;
        }

        const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
        bool bHandled = true;
        size_t nSelectedMenu = mnSelectedMenu;
        size_t nLastMenuPos = maMenuItems.size() - 1;
        switch (rKeyCode.GetCode())
        {
            case KEY_UP:
            {
                if (nLastMenuPos == 0)
                    // There is only one menu item.  Do nothing.
                    break;

                size_t nOldPos = nSelectedMenu;

                if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == 0)
                    nSelectedMenu = nLastMenuPos;
                else
                    --nSelectedMenu;

                // Loop until a non-separator menu item is found.
                while (nSelectedMenu != nOldPos)
                {
                    if (maMenuItems[nSelectedMenu].mbSeparator)
                    {
                        if (nSelectedMenu)
                            --nSelectedMenu;
                        else
                            nSelectedMenu = nLastMenuPos;
                    }
                    else
                        break;
                }

                setSelectedMenuItem(nSelectedMenu, false, false);
            }
            break;
            case KEY_DOWN:
            {
                if (nLastMenuPos == 0)
                    // There is only one menu item.  Do nothing.
                    break;

                size_t nOldPos = nSelectedMenu;

                if (nSelectedMenu == MENU_NOT_SELECTED || nSelectedMenu == nLastMenuPos)
                    nSelectedMenu = 0;
                else
                    ++nSelectedMenu;

                // Loop until a non-separator menu item is found.
                while (nSelectedMenu != nOldPos)
                {
                    if (maMenuItems[nSelectedMenu].mbSeparator)
                    {
                        if (nSelectedMenu == nLastMenuPos)
                            nSelectedMenu = 0;
                        else
                            ++nSelectedMenu;
                    }
                    else
                        break;
                }

                setSelectedMenuItem(nSelectedMenu, false, false);
            }
            break;
            case KEY_LEFT:
                if (mpParentMenu)
                    mpParentMenu->endSubMenu(this);
            break;
            case KEY_RIGHT:
            {
                if (mnSelectedMenu >= maMenuItems.size() || mnSelectedMenu == MENU_NOT_SELECTED)
                    break;

                const MenuItemData& rMenu = maMenuItems[mnSelectedMenu];
                if (!rMenu.mbEnabled || !rMenu.mpSubMenuWin)
                    break;

                maOpenTimer.mnMenuPos = mnSelectedMenu;
                maOpenTimer.mpSubMenu = rMenu.mpSubMenuWin.get();
                launchSubMenu(true);
            }
            break;
            case KEY_RETURN:
                if (nSelectedMenu != MENU_NOT_SELECTED)
                    executeMenuItem(nSelectedMenu);
            break;
            default:
                bHandled = false;
        }

        if (!bHandled)
            Window::KeyInput(rKEvt);
    }

    void ScMenuFloatingWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
    {
        const StyleSettings& rStyle = GetSettings().GetStyleSettings();

        SetFont(maLabelFont);

        Color aBackColor = rStyle.GetMenuColor();
        Color aBorderColor = rStyle.GetShadowColor();

        tools::Rectangle aCtrlRect(Point(0, 0), GetOutputSizePixel());

        // Window background
        bool bNativeDrawn = true;
        if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
        {
            rRenderContext.SetClipRegion();
            bNativeDrawn = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Entire, aCtrlRect,
                                                            ControlState::ENABLED, ImplControlValue(), OUString());
        }
        else
            bNativeDrawn = false;

        if (!bNativeDrawn)
        {
            rRenderContext.SetFillColor(aBackColor);
            rRenderContext.SetLineColor(aBorderColor);
            rRenderContext.DrawRect(aCtrlRect);
        }

        // Menu items
        rRenderContext.SetTextColor(rStyle.GetMenuTextColor());
        drawAllMenuItems(rRenderContext);
    }

    Reference<XAccessible> ScMenuFloatingWindow::CreateAccessible()
    {
        if (!mxAccessible.is())
        {
            Reference<XAccessible> xAccParent = mpParentMenu ?
                mpParentMenu->GetAccessible() : GetAccessibleParentWindow()->GetAccessible();

            mxAccessible.set(new ScAccessibleFilterMenu(xAccParent, this, maName, 999));
            ScAccessibleFilterMenu* p = static_cast<ScAccessibleFilterMenu*>(
                mxAccessible.get());

            vector<MenuItemData>::const_iterator itr, itrBeg = maMenuItems.begin(), itrEnd = maMenuItems.end();
            for (itr = itrBeg; itr != itrEnd; ++itr)
            {
                size_t nPos = ::std::distance(itrBeg, itr);
                p->appendMenuItem(itr->maText, nPos);
            }
        }

        return mxAccessible;
    }

    void ScMenuFloatingWindow::addMenuItem(const OUString& rText, Action* pAction)
    {
        MenuItemData aItem;
        aItem.maText = rText;
        aItem.mbEnabled = true;
        aItem.mpAction.reset(pAction);
        maMenuItems.push_back(aItem);
    }

    void ScMenuFloatingWindow::addSeparator()
    {
        MenuItemData aItem;
        aItem.mbSeparator = true;
        maMenuItems.push_back(aItem);
    }

    ScMenuFloatingWindow* ScMenuFloatingWindow::addSubMenuItem(const OUString& rText, bool bEnabled)
    {
        MenuItemData aItem;
        aItem.maText = rText;
        aItem.mbEnabled = bEnabled;
        aItem.mpSubMenuWin.reset(VclPtr<ScMenuFloatingWindow>::Create(this, mpDoc, GetMenuStackLevel()+1));
        aItem.mpSubMenuWin->setName(rText);
        maMenuItems.push_back(aItem);
        return aItem.mpSubMenuWin.get();
    }

    void ScMenuFloatingWindow::handlePopupEnd()
    {
        clearSelectedMenuItem();
    }

    Size ScMenuFloatingWindow::getMenuSize() const
    {
        if (maMenuItems.empty())
            return Size();

        vector<MenuItemData>::const_iterator itr = maMenuItems.begin(), itrEnd = maMenuItems.end();
        long nTextWidth = 0;
        for (; itr != itrEnd; ++itr)
        {
            if (itr->mbSeparator)
                continue;

            nTextWidth = ::std::max(GetTextWidth(itr->maText), nTextWidth);
        }

        size_t nLastPos = maMenuItems.size()-1;
        Point aPos;
        Size aSize;
        getMenuItemPosSize(nLastPos, aPos, aSize);
        aPos.AdjustX(nTextWidth + 15 );
        aPos.AdjustY(aSize.Height() + 5 );
        return Size(aPos.X(), aPos.Y());
    }

    void ScMenuFloatingWindow::drawMenuItem(vcl::RenderContext& rRenderContext, size_t nPos)
    {
        if (nPos >= maMenuItems.size())
            return;

        Point aPos;
        Size aSize;
        getMenuItemPosSize(nPos, aPos, aSize);

        DecorationView aDecoView(&rRenderContext);
        long const nXOffset = 5;
        long nYOffset = (aSize.Height() - maLabelFont.GetFontHeight())/2;
        rRenderContext. DrawCtrlText(Point(aPos.X()+nXOffset, aPos.Y() + nYOffset), maMenuItems[nPos].maText, 0,
                                     maMenuItems[nPos].maText.getLength(),
                                     maMenuItems[nPos].mbEnabled ? DrawTextFlags::Mnemonic : DrawTextFlags::Disable);

        if (maMenuItems[nPos].mpSubMenuWin)
        {
            long nFontHeight = maLabelFont.GetFontHeight();
            Point aMarkerPos = aPos;
            aMarkerPos.AdjustY(aSize.Height() / 2 - nFontHeight / 4 + 1 );
            aMarkerPos.AdjustX(aSize.Width() - nFontHeight + nFontHeight / 4 );
            Size aMarkerSize(nFontHeight / 2, nFontHeight / 2);
            aDecoView.DrawSymbol(tools::Rectangle(aMarkerPos, aMarkerSize), SymbolType::SPIN_RIGHT, GetTextColor());
        }
    }

    void ScMenuFloatingWindow::drawSeparator(vcl::RenderContext& rRenderContext, size_t nPos)
    {
        Point aPos;
        Size aSize;
        getMenuItemPosSize(nPos, aPos, aSize);
        tools::Rectangle aRegion(aPos,aSize);

        if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
        {
            rRenderContext.Push(PushFlags::CLIPREGION);
            rRenderContext.IntersectClipRegion(aRegion);
            tools::Rectangle aCtrlRect(Point(0,0), GetOutputSizePixel());
            rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Entire, aCtrlRect,
                                             ControlState::ENABLED, ImplControlValue(), OUString());

            rRenderContext.Pop();
        }

        bool bNativeDrawn = false;
        if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Separator))
        {
            ControlState nState = ControlState::NONE;
            const MenuItemData& rData = maMenuItems[nPos];
            if (rData.mbEnabled)
                nState |= ControlState::ENABLED;

            bNativeDrawn = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Separator,
                                                            aRegion, nState, ImplControlValue(), OUString());
        }

        if (!bNativeDrawn)
        {
            const StyleSettings& rStyle = rRenderContext.GetSettings().GetStyleSettings();
            Point aTmpPos = aPos;
            aTmpPos.AdjustY(aSize.Height() / 2 );
            rRenderContext.SetLineColor(rStyle.GetShadowColor());
            rRenderContext.DrawLine(aTmpPos, Point(aSize.Width() + aTmpPos.X(), aTmpPos.Y()));
            aTmpPos.AdjustY( 1 );
            rRenderContext.SetLineColor(rStyle.GetLightColor());
            rRenderContext.DrawLine(aTmpPos, Point(aSize.Width() + aTmpPos.X(), aTmpPos.Y()));
            rRenderContext.SetLineColor();
        }
    }

    void ScMenuFloatingWindow::drawAllMenuItems(vcl::RenderContext& rRenderContext)
    {
        size_t n = maMenuItems.size();

        for (size_t i = 0; i < n; ++i)
        {
            if (maMenuItems[i].mbSeparator)
            {
                // Separator
                drawSeparator(rRenderContext, i);
            }
            else
            {
                // Normal menu item
                highlightMenuItem(rRenderContext, i, i == mnSelectedMenu);
            }
        }
    }

    void ScMenuFloatingWindow::executeMenuItem(size_t nPos)
    {
        if (nPos >= maMenuItems.size())
            return;

        if (!maMenuItems[nPos].mpAction)
            // no action is defined.
            return;

        maMenuItems[nPos].mpAction->execute();
        terminateAllPopupMenus();
    }

    void ScMenuFloatingWindow::setSelectedMenuItem(size_t nPos, bool bSubMenuTimer, bool bEnsureSubMenu)
    {
        if (mnSelectedMenu == nPos)
            // nothing to do.
            return;

        if (bEnsureSubMenu)
        {
            // Dismiss any child popup menu windows.
            if (mnSelectedMenu < maMenuItems.size() &&
                maMenuItems[mnSelectedMenu].mpSubMenuWin &&
                maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible())
            {
                maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible();
            }

            // The popup is not visible, yet a menu item is selected.  The request
            // most likely comes from the accessible object.  Make sure this
            // window, as well as all its parent windows are visible.
            if (!IsVisible() && mpParentMenu)
                mpParentMenu->ensureSubMenuVisible(this);
        }

        selectMenuItem(mnSelectedMenu, false, bSubMenuTimer);
        selectMenuItem(nPos, true, bSubMenuTimer);
        mnSelectedMenu = nPos;

        fireMenuHighlightedEvent();
    }

    void ScMenuFloatingWindow::handleMenuTimeout(const SubMenuItemData* pTimer)
    {
        if (pTimer == &maOpenTimer)
        {
            // Close any open submenu immediately.
            if (maCloseTimer.mpSubMenu)
            {
                maCloseTimer.mpSubMenu->EndPopupMode();
                maCloseTimer.mpSubMenu = nullptr;
                maCloseTimer.maTimer.Stop();
            }

            launchSubMenu(false);
        }
        else if (pTimer == &maCloseTimer)
        {
            // end submenu.
            if (maCloseTimer.mpSubMenu)
            {
                maOpenTimer.mpSubMenu = nullptr;

                maCloseTimer.mpSubMenu->EndPopupMode();
                maCloseTimer.mpSubMenu = nullptr;

                Invalidate();
                maOpenTimer.mnMenuPos = MENU_NOT_SELECTED;
            }
        }
    }

    void ScMenuFloatingWindow::queueLaunchSubMenu(size_t nPos, ScMenuFloatingWindow* pMenu)
    {
        if (!pMenu)
            return;

        // Set the submenu on launch queue.
        if (maOpenTimer.mpSubMenu)
        {
            if (maOpenTimer.mpSubMenu == pMenu)
            {
                if (pMenu == maCloseTimer.mpSubMenu)
                    maCloseTimer.reset();
                return;
            }

            // new submenu is being requested.
            queueCloseSubMenu();
        }

        maOpenTimer.mpSubMenu = pMenu;
        maOpenTimer.mnMenuPos = nPos;
        maOpenTimer.maTimer.Start();
    }

    void ScMenuFloatingWindow::queueCloseSubMenu()
    {
        if (!maOpenTimer.mpSubMenu)
            // There is no submenu to close.
            return;

        // Stop any submenu on queue for opening.
        maOpenTimer.maTimer.Stop();

        maCloseTimer.mpSubMenu = maOpenTimer.mpSubMenu;
        maCloseTimer.mnMenuPos = maOpenTimer.mnMenuPos;
        maCloseTimer.maTimer.Start();
    }

    void ScMenuFloatingWindow::launchSubMenu(bool bSetMenuPos)
    {
        Point aPos;
        Size aSize;
        getMenuItemPosSize(maOpenTimer.mnMenuPos, aPos, aSize);
        ScMenuFloatingWindow* pSubMenu = maOpenTimer.mpSubMenu;

        if (!pSubMenu)
            return;

        FloatWinPopupFlags nOldFlags = GetPopupModeFlags();
        SetPopupModeFlags(nOldFlags | FloatWinPopupFlags::NoAppFocusClose);
        pSubMenu->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
        pSubMenu->StartPopupMode(
            tools::Rectangle(aPos,aSize), (FloatWinPopupFlags::Right | FloatWinPopupFlags::GrabFocus));
        pSubMenu->AddPopupModeWindow(this);
        if (bSetMenuPos)
            pSubMenu->setSelectedMenuItem(0, false, false); // select menu item after the popup becomes fully visible.
        SetPopupModeFlags(nOldFlags);
    }

    void ScMenuFloatingWindow::endSubMenu(ScMenuFloatingWindow* pSubMenu)
    {
        if (!pSubMenu)
            return;

        pSubMenu->EndPopupMode();
        maOpenTimer.reset();

        size_t nMenuPos = getSubMenuPos(pSubMenu);
        if (nMenuPos != MENU_NOT_SELECTED)
        {
            mnSelectedMenu = nMenuPos;
            Invalidate();
            fireMenuHighlightedEvent();
        }
    }

    void ScMenuFloatingWindow::fillMenuItemsToAccessible(ScAccessibleFilterMenu* pAccMenu) const
    {
        vector<MenuItemData>::const_iterator itr, itrBeg = maMenuItems.begin(), itrEnd = maMenuItems.end();
        for (itr = itrBeg; itr != itrEnd; ++itr)
        {
            size_t nPos = ::std::distance(itrBeg, itr);
            pAccMenu->appendMenuItem(itr->maText, nPos);
        }
    }

    void ScMenuFloatingWindow::resizeToFitMenuItems()
    {
        SetOutputSizePixel(getMenuSize());
    }

    void ScMenuFloatingWindow::selectMenuItem(size_t nPos, bool bSelected, bool bSubMenuTimer)
    {
        if (nPos >= maMenuItems.size() || nPos == MENU_NOT_SELECTED)
        {
            queueCloseSubMenu();
            return;
        }

        if (!maMenuItems[nPos].mbEnabled)
        {
            queueCloseSubMenu();
            return;
        }

        Invalidate();

        if (bSelected)
        {
            if (mpParentMenu)
                mpParentMenu->setSubMenuFocused(this);

            if (bSubMenuTimer)
            {
                if (maMenuItems[nPos].mpSubMenuWin)
                {
                    ScMenuFloatingWindow* pSubMenu = maMenuItems[nPos].mpSubMenuWin.get();
                    queueLaunchSubMenu(nPos, pSubMenu);
                }
                else
                    queueCloseSubMenu();
            }
        }
    }

    void ScMenuFloatingWindow::clearSelectedMenuItem()
    {
        selectMenuItem(mnSelectedMenu, false, false);
        mnSelectedMenu = MENU_NOT_SELECTED;
    }

    ScMenuFloatingWindow* ScMenuFloatingWindow::getSubMenuWindow(size_t nPos) const
    {
        if (maMenuItems.size() <= nPos)
            return nullptr;

        return maMenuItems[nPos].mpSubMenuWin.get();
    }

    bool ScMenuFloatingWindow::isMenuItemSelected(size_t nPos) const
    {
        return nPos == mnSelectedMenu;
    }

    void ScMenuFloatingWindow::setName(const OUString& rName)
    {
        maName = rName;
    }

    void ScMenuFloatingWindow::highlightMenuItem(vcl::RenderContext& rRenderContext, size_t nPos, bool bSelected)
    {
        if (nPos == MENU_NOT_SELECTED)
            return;

        const StyleSettings& rStyle = rRenderContext.GetSettings().GetStyleSettings();
        Color aBackColor = rStyle.GetMenuColor();
        rRenderContext.SetFillColor(aBackColor);
        rRenderContext.SetLineColor(aBackColor);

        Point aPos;
        Size aSize;
        getMenuItemPosSize(nPos, aPos, aSize);
        tools::Rectangle aRegion(aPos,aSize);

        if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
        {
            rRenderContext.Push(PushFlags::CLIPREGION);
            rRenderContext.IntersectClipRegion(tools::Rectangle(aPos, aSize));
            tools::Rectangle aCtrlRect(Point(0,0), GetOutputSizePixel());
            rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Entire, aCtrlRect, ControlState::ENABLED,
                                             ImplControlValue(), OUString());
            rRenderContext.Pop();
        }

        bool bNativeDrawn = true;
        if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItem))
        {
            ControlState nState = bSelected ? ControlState::SELECTED : ControlState::NONE;
            if (maMenuItems[nPos].mbEnabled)
                nState |= ControlState::ENABLED;
            bNativeDrawn = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::MenuItem,
                                                            aRegion, nState, ImplControlValue(), OUString());
        }
        else
            bNativeDrawn = false;

        if (!bNativeDrawn)
        {
            if (bSelected)
            {
                aBackColor = rStyle.GetMenuHighlightColor();
                rRenderContext.SetFillColor(aBackColor);
                rRenderContext.SetLineColor(aBackColor);
            }
            rRenderContext.DrawRect(tools::Rectangle(aPos,aSize));
        }

        Color aTextColor = bSelected ? rStyle.GetMenuHighlightTextColor() : rStyle.GetMenuTextColor();
        rRenderContext.SetTextColor(aTextColor);
        drawMenuItem(rRenderContext, nPos);
    }

    void ScMenuFloatingWindow::getMenuItemPosSize(size_t nPos, Point& rPos, Size& rSize) const
    {
        size_t nCount = maMenuItems.size();
        if (nPos >= nCount)
            return;

        const sal_uInt16 nLeftMargin = 5;
        const sal_uInt16 nTopMargin = 5;
        const sal_uInt16 nMenuItemHeight = static_cast<sal_uInt16>(maLabelFont.GetFontHeight()*1.8);
        const sal_uInt16 nSepHeight = static_cast<sal_uInt16>(maLabelFont.GetFontHeight()*0.8);

        Point aPos1(nLeftMargin, nTopMargin);
        rPos = aPos1;
        for (size_t i = 0; i < nPos; ++i)
            rPos.AdjustY(maMenuItems[i].mbSeparator ? nSepHeight : nMenuItemHeight );

        Size aWndSize = GetSizePixel();
        sal_uInt16 nH = maMenuItems[nPos].mbSeparator ? nSepHeight : nMenuItemHeight;
        rSize = Size(aWndSize.Width() - nLeftMargin*2, nH);
    }

    size_t ScMenuFloatingWindow::getEnclosingMenuItem(const Point& rPos) const
    {
        size_t n = maMenuItems.size();
        for (size_t i = 0; i < n; ++i)
        {
            Point aPos;
            Size aSize;
            getMenuItemPosSize(i, aPos, aSize);
            tools::Rectangle aRect(aPos, aSize);
            if (aRect.IsInside(rPos))
                return maMenuItems[i].mbSeparator ? MENU_NOT_SELECTED : i;
        }
        return MENU_NOT_SELECTED;
    }

    size_t ScMenuFloatingWindow::getSubMenuPos(const ScMenuFloatingWindow* pSubMenu)
    {
        size_t n = maMenuItems.size();
        for (size_t i = 0; i < n; ++i)
        {
            if (maMenuItems[i].mpSubMenuWin.get() == pSubMenu)
                return i;
        }
        return MENU_NOT_SELECTED;
    }

    void ScMenuFloatingWindow::fireMenuHighlightedEvent()
    {
        if (mnSelectedMenu == MENU_NOT_SELECTED)
            return;

        if (!mxAccessible.is())
            return;

        Reference<XAccessibleContext> xAccCxt = mxAccessible->getAccessibleContext();
        if (!xAccCxt.is())
            return;

        Reference<XAccessible> xAccMenu = xAccCxt->getAccessibleChild(mnSelectedMenu);
        if (!xAccMenu.is())
            return;

        VclAccessibleEvent aEvent(VclEventId::MenuHighlight, xAccMenu);
        FireVclEvent(aEvent);
    }

    void ScMenuFloatingWindow::setSubMenuFocused(const ScMenuFloatingWindow* pSubMenu)
    {
        maCloseTimer.reset();
        size_t nMenuPos = getSubMenuPos(pSubMenu);
        if (mnSelectedMenu != nMenuPos)
        {
            mnSelectedMenu = nMenuPos;
            Invalidate();
        }
    }

    void ScMenuFloatingWindow::ensureSubMenuVisible(ScMenuFloatingWindow* pSubMenu)
    {
        if (mpParentMenu)
            mpParentMenu->ensureSubMenuVisible(this);

        if (pSubMenu->IsVisible())
            return;

        // Find the menu position of the submenu.
        size_t nMenuPos = getSubMenuPos(pSubMenu);
        if (nMenuPos != MENU_NOT_SELECTED)
        {
            setSelectedMenuItem(nMenuPos, false, false);

            Point aPos;
            Size aSize;
            getMenuItemPosSize(nMenuPos, aPos, aSize);

            FloatWinPopupFlags nOldFlags = GetPopupModeFlags();
            SetPopupModeFlags(nOldFlags | FloatWinPopupFlags::NoAppFocusClose);
            pSubMenu->resizeToFitMenuItems(); // set the size before launching the popup to get it positioned correctly.
            pSubMenu->StartPopupMode(
                tools::Rectangle(aPos,aSize), (FloatWinPopupFlags::Right | FloatWinPopupFlags::GrabFocus));
            pSubMenu->AddPopupModeWindow(this);
            SetPopupModeFlags(nOldFlags);
        }
    }

    void ScMenuFloatingWindow::ensureSubMenuNotVisible()
    {
        if (mnSelectedMenu <= maMenuItems.size() &&
            maMenuItems[mnSelectedMenu].mpSubMenuWin &&
            maMenuItems[mnSelectedMenu].mpSubMenuWin->IsVisible())
        {
            maMenuItems[mnSelectedMenu].mpSubMenuWin->ensureSubMenuNotVisible();
        }

        EndPopupMode();
    }

    void ScMenuFloatingWindow::terminateAllPopupMenus()
    {
        EndPopupMode();
        if (mpParentMenu)
            mpParentMenu->terminateAllPopupMenus();
    }

    ScCheckListMenuWindow::Config::Config() :
        mbAllowEmptySet(true), mbRTL(false)
    {
    }

    ScCheckListMember::ScCheckListMember()
        : mbVisible(true)
        , mbDate(false)
        , mbLeaf(false)
        , meDatePartType(YEAR)
        , mpParent(nullptr)
    {
    }

    ScCheckListMenuWindow::CancelButton::CancelButton(ScCheckListMenuWindow* pParent) :
        ::CancelButton(pParent), mpParent(pParent) {}

    ScCheckListMenuWindow::CancelButton::~CancelButton()
    {
        disposeOnce();
    }

    void ScCheckListMenuWindow::CancelButton::dispose()
    {
        mpParent.clear();
        ::CancelButton::dispose();
    }

    void ScCheckListMenuWindow::CancelButton::Click()
    {
        mpParent->EndPopupMode();
        ::CancelButton::Click();
    }

    ScCheckListMenuWindow::ScCheckListMenuWindow(vcl::Window* pParent, ScDocument* pDoc) :
        ScMenuFloatingWindow(pParent, pDoc),
        maEdSearch(VclPtr<ScSearchEdit>::Create(this)),
        maChecks(VclPtr<ScCheckListBox>::Create(this)),
        maChkToggleAll(VclPtr<TriStateBox>::Create(this, 0)),
        maBtnSelectSingle(VclPtr<ImageButton>::Create(this, 0)),
        maBtnUnselectSingle(VclPtr<ImageButton>::Create(this, 0)),
        maBtnOk(VclPtr<OKButton>::Create(this)),
        maBtnCancel(VclPtr<CancelButton>::Create(this)),
        maWndSize(),
        mePrevToggleAllState(TRISTATE_INDET),
        maTabStops(this)
    {
        float fScaleFactor = GetDPIScaleFactor();

        maWndSize = Size(200 * fScaleFactor, 330 * fScaleFactor);

        maTabStops.AddTabStop( this );
        maTabStops.AddTabStop( maEdSearch.get() );
        maTabStops.AddTabStop( maChecks.get() );
        maTabStops.AddTabStop( maChkToggleAll.get() );
        maTabStops.AddTabStop( maBtnSelectSingle.get() );
        maTabStops.AddTabStop( maBtnUnselectSingle.get() );
        maTabStops.AddTabStop( maBtnOk.get() );
        maTabStops.AddTabStop( maBtnCancel.get() );

        maEdSearch->SetTabStopsContainer( &maTabStops );
        maChecks->SetTabStopsContainer( &maTabStops );

        set_id("check_list_menu");
        maChkToggleAll->set_id("toggle_all");
        maBtnSelectSingle->set_id("select_current");
        maBtnUnselectSingle->set_id("unselect_current");
    }

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

    void ScCheckListMenuWindow::dispose()
    {
        maTabStops.clear();
        maEdSearch.disposeAndClear();
        maChecks.disposeAndClear();
        maChkToggleAll.disposeAndClear();
        maBtnSelectSingle.disposeAndClear();
        maBtnUnselectSingle.disposeAndClear();
        maBtnOk.disposeAndClear();
        maBtnCancel.disposeAndClear();
        ScMenuFloatingWindow::dispose();
    }

    void ScCheckListMenuWindow::getSectionPosSize(
        Point& rPos, Size& rSize, SectionType eType) const
    {
        float fScaleFactor = GetDPIScaleFactor();

        // constant parameters.
        const long nSearchBoxMargin = 10 *fScaleFactor;
        const long nListBoxMargin = 5 * fScaleFactor;            // horizontal distance from the side of the dialog to the listbox border.
        const long nListBoxInnerPadding = 5 * fScaleFactor;
        const long nTopMargin = 5 * fScaleFactor;
        const long nMenuHeight = maMenuSize.getHeight();
        const long nSingleItemBtnAreaHeight = 32 * fScaleFactor; // height of the middle area below the list box where the single-action buttons are.
        const long nBottomBtnAreaHeight = 50 * fScaleFactor;     // height of the bottom area where the OK and Cancel buttons are.
        const long nBtnWidth = 90 * fScaleFactor;
        const long nLabelHeight = getLabelFont().GetFontHeight();
        const long nBtnHeight = nLabelHeight * 2;
        const long nBottomMargin = 10 * fScaleFactor;
        const long nMenuListMargin = 5 * fScaleFactor;
        const long nSearchBoxHeight = nLabelHeight * 2;

        // parameters calculated from constants.
        const long nListBoxWidth = maWndSize.Width() - nListBoxMargin*2;
        const long nListBoxHeight = maWndSize.Height() - nTopMargin - nMenuHeight -
            nMenuListMargin - nSearchBoxHeight - nSearchBoxMargin - nSingleItemBtnAreaHeight - nBottomBtnAreaHeight;

        const long nSingleBtnAreaY = nTopMargin + nMenuHeight + nListBoxHeight + nMenuListMargin + nSearchBoxHeight + nSearchBoxMargin - 1;

        switch (eType)
        {
            case WHOLE:
            {
                rPos  = Point(0, 0);
                rSize = maWndSize;
            }
            break;
            case EDIT_SEARCH:
            {
                rPos = Point(nSearchBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin);
                rSize = Size(maWndSize.Width() - 2*nSearchBoxMargin, nSearchBoxHeight);
            }
            break;
            case LISTBOX_AREA_OUTER:
            {
                rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin + nSearchBoxHeight + nSearchBoxMargin);
                rSize = Size(nListBoxWidth, nListBoxHeight);
            }
            break;
            case LISTBOX_AREA_INNER:
            {
                rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin + nSearchBoxHeight + nSearchBoxMargin);
                rPos.AdjustX(nListBoxInnerPadding );
                rPos.AdjustY(nListBoxInnerPadding );

                rSize = Size(nListBoxWidth, nListBoxHeight);
                rSize.AdjustWidth( -(nListBoxInnerPadding*2) );
                rSize.AdjustHeight( -(nListBoxInnerPadding*2) );
            }
            break;
            case SINGLE_BTN_AREA:
            {
                rPos = Point(nListBoxMargin, nSingleBtnAreaY);
                rSize = Size(nListBoxWidth, nSingleItemBtnAreaHeight);
            }
            break;
            case CHECK_TOGGLE_ALL:
            {
                long h = std::min(maChkToggleAll->CalcMinimumSize().Height(), 26L);
                rPos = Point(nListBoxMargin, nSingleBtnAreaY);
                rPos.AdjustX(5 );
                rPos.AdjustY((nSingleItemBtnAreaHeight - h)/2 );
                rSize = Size(70, h);
            }
            break;
            case BTN_SINGLE_SELECT:
            {
                long h = 26 * fScaleFactor;
                rPos = Point(nListBoxMargin, nSingleBtnAreaY);
                rPos.AdjustX(nListBoxWidth - h - 10 - h - 10 );
                rPos.AdjustY((nSingleItemBtnAreaHeight - h)/2 );
                rSize = Size(h, h);
            }
            break;
            case BTN_SINGLE_UNSELECT:
            {
                long h = 26 * fScaleFactor;
                rPos = Point(nListBoxMargin, nSingleBtnAreaY);
                rPos.AdjustX(nListBoxWidth - h - 10 );
                rPos.AdjustY((nSingleItemBtnAreaHeight - h)/2 );
                rSize = Size(h, h);
            }
            break;
            case BTN_OK:
            {
                long x = (maWndSize.Width() - nBtnWidth*2)/3;
                long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
                rPos = Point(x, y);
                rSize = Size(nBtnWidth, nBtnHeight);
            }
            break;
            case BTN_CANCEL:
            {
                long x = (maWndSize.Width() - nBtnWidth*2)/3*2 + nBtnWidth;
                long y = maWndSize.Height() - nBottomMargin - nBtnHeight;
                rPos = Point(x, y);
                rSize = Size(nBtnWidth, nBtnHeight);
            }
            break;
            default:
                ;
        }
    }

    void ScCheckListMenuWindow::packWindow()
    {
        maMenuSize = getMenuSize();

        if (maWndSize.Width() < maMenuSize.Width())
            // Widen the window to fit the menu items.
            maWndSize.setWidth( maMenuSize.Width() );

        // Set proper window height based on the number of menu items.
        if (maWndSize.Height() < maMenuSize.Height()*2.8)
            maWndSize.setHeight( maMenuSize.Height()*2.8 );

        // TODO: Make sure the window height never exceeds the height of the
        // screen. Also do adjustment based on the number of check box items.

        SetOutputSizePixel(maWndSize);

        const StyleSettings& rStyle = GetSettings().GetStyleSettings();

        Point aPos;
        Size aSize;
        getSectionPosSize(aPos, aSize, WHOLE);
        SetOutputSizePixel(aSize);

        getSectionPosSize(aPos, aSize, BTN_OK);
        maBtnOk->SetPosSizePixel(aPos, aSize);
        maBtnOk->SetFont(getLabelFont());
        maBtnOk->SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
        maBtnOk->Show();

        getSectionPosSize(aPos, aSize, BTN_CANCEL);
        maBtnCancel->SetPosSizePixel(aPos, aSize);
        maBtnCancel->SetFont(getLabelFont());
        maBtnCancel->Show();

        getSectionPosSize(aPos, aSize, EDIT_SEARCH);
        maEdSearch->SetPosSizePixel(aPos, aSize);
        maEdSearch->SetFont(getLabelFont());
        maEdSearch->SetControlBackground(rStyle.GetFieldColor());
        maEdSearch->SetPlaceholderText(ScResId(STR_EDIT_SEARCH_ITEMS));
        maEdSearch->SetModifyHdl( LINK(this, ScCheckListMenuWindow, EdModifyHdl) );
        maEdSearch->Show();

        getSectionPosSize(aPos, aSize, LISTBOX_AREA_INNER);
        maChecks->SetPosSizePixel(aPos, aSize);
        maChecks->SetFont(getLabelFont());
        maChecks->SetCheckButtonHdl( LINK(this, ScCheckListMenuWindow, CheckHdl) );
        maChecks->Show();

        getSectionPosSize(aPos, aSize, CHECK_TOGGLE_ALL);
        maChkToggleAll->SetPosSizePixel(aPos, aSize);
        maChkToggleAll->SetFont(getLabelFont());
        maChkToggleAll->SetText(ScResId(STR_BTN_TOGGLE_ALL));
        maChkToggleAll->SetTextColor(rStyle.GetMenuTextColor());
        maChkToggleAll->SetControlBackground(rStyle.GetMenuColor());
        maChkToggleAll->SetClickHdl( LINK(this, ScCheckListMenuWindow, TriStateHdl) );
        maChkToggleAll->Show();

        float fScaleFactor = GetDPIScaleFactor();

        BitmapEx aSingleSelectBmp(RID_BMP_SELECT_CURRENT);
        if (fScaleFactor > 1)
            aSingleSelectBmp.Scale(fScaleFactor, fScaleFactor, BmpScaleFlag::Fast);
        Image aSingleSelect(aSingleSelectBmp);

        getSectionPosSize(aPos, aSize, BTN_SINGLE_SELECT);
        maBtnSelectSingle->SetPosSizePixel(aPos, aSize);
        maBtnSelectSingle->SetQuickHelpText(ScResId(STR_BTN_SELECT_CURRENT));
        maBtnSelectSingle->SetModeImage(aSingleSelect);
        maBtnSelectSingle->SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
        maBtnSelectSingle->Show();

        BitmapEx aSingleUnselectBmp(RID_BMP_UNSELECT_CURRENT);
        if (fScaleFactor > 1)
            aSingleUnselectBmp.Scale(fScaleFactor, fScaleFactor, BmpScaleFlag::Fast);
        Image aSingleUnselect(aSingleUnselectBmp);

        getSectionPosSize(aPos, aSize, BTN_SINGLE_UNSELECT);
        maBtnUnselectSingle->SetPosSizePixel(aPos, aSize);
        maBtnUnselectSingle->SetQuickHelpText(ScResId(STR_BTN_UNSELECT_CURRENT));
        maBtnUnselectSingle->SetModeImage(aSingleUnselect);
        maBtnUnselectSingle->SetClickHdl( LINK(this, ScCheckListMenuWindow, ButtonHdl) );
        maBtnUnselectSingle->Show();
    }

    void ScCheckListMenuWindow::setAllMemberState(bool bSet)
    {
        size_t n = maMembers.size();
        std::set<SvTreeListEntry*> aParents;
        for (size_t i = 0; i < n; ++i)
        {
            aParents.insert(maMembers[i].mpParent);
        }

        for (auto itr = aParents.begin(), itrEnd = aParents.end(); itr != itrEnd; ++itr)
        {
            if (!(*itr))
            {
                sal_uInt32 nCount = maChecks->GetEntryCount();
                for( sal_uInt32 i = 0; i < nCount; ++i)
                {
                    SvTreeListEntry* pEntry = maChecks->GetEntry(i);
                    if (!pEntry)
                        continue;

                    maChecks->CheckEntry(pEntry, bSet);
                }
            }
            else
            {
                SvTreeListEntries& rEntries = (*itr)->GetChildEntries();
                for (auto it = rEntries.begin(), itEnd = rEntries.end(); it != itEnd; ++ it)
                {
                    maChecks->CheckEntry(*itr, bSet);
                }
            }
        }

        if (!maConfig.mbAllowEmptySet)
            // We need to have at least one member selected.
            maBtnOk->Enable(maChecks->GetCheckedEntryCount() != 0);
    }

    void ScCheckListMenuWindow::selectCurrentMemberOnly(bool bSet)
    {
        setAllMemberState(!bSet);
        SvTreeListEntry* pEntry = maChecks->GetCurEntry();
        if (!pEntry)
            return;
        maChecks->CheckEntry(pEntry, bSet );
    }

    IMPL_LINK( ScCheckListMenuWindow, ButtonHdl, Button*, pBtn, void )
    {
        if (pBtn == maBtnOk.get())
            close(true);
        else if (pBtn == maBtnSelectSingle.get())
        {
            selectCurrentMemberOnly(true);
            CheckHdl(maChecks.get());
        }
        else if (pBtn == maBtnUnselectSingle.get())
        {
            selectCurrentMemberOnly(false);
            CheckHdl(maChecks.get());
        }
    }

    IMPL_LINK_NOARG(ScCheckListMenuWindow, TriStateHdl, Button*, void)
    {
        switch (mePrevToggleAllState)
        {
            case TRISTATE_FALSE:
                maChkToggleAll->SetState(TRISTATE_TRUE);
                setAllMemberState(true);
            break;
            case TRISTATE_TRUE:
                maChkToggleAll->SetState(TRISTATE_FALSE);
                setAllMemberState(false);
            break;
            case TRISTATE_INDET:
            default:
                maChkToggleAll->SetState(TRISTATE_TRUE);
                setAllMemberState(true);
            break;
        }

        mePrevToggleAllState = maChkToggleAll->GetState();
    }

    IMPL_LINK_NOARG(ScCheckListMenuWindow, EdModifyHdl, Edit&, void)
    {
        OUString aSearchText = maEdSearch->GetText();
        aSearchText = ScGlobal::pCharClass->lowercase( aSearchText );
        bool bSearchTextEmpty = aSearchText.isEmpty();
        size_t n = maMembers.size();
        size_t nSelCount = 0;
        OUString aLabelDisp;
        bool bSomeDateDeletes = false;

        for (size_t i = 0; i < n; ++i)
        {
            bool bIsDate = maMembers[i].mbDate;
            bool bPartialMatch = false;

            aLabelDisp = maMembers[i].maName;
            if ( aLabelDisp.isEmpty() )
                aLabelDisp = ScResId( STR_EMPTYDATA );

            if ( !bSearchTextEmpty )
            {
                if ( !bIsDate )
                    bPartialMatch = ( ScGlobal::pCharClass->lowercase( aLabelDisp ).indexOf( aSearchText ) != -1 );
                else if ( maMembers[i].meDatePartType == ScCheckListMember::DAY ) // Match with both numerical and text version of month
                    bPartialMatch = (ScGlobal::pCharClass->lowercase( OUString(
                                    maMembers[i].maRealName + maMembers[i].maDateParts[1] )).indexOf( aSearchText ) != -1);
                else
                    continue;
            }
            else if ( bIsDate && maMembers[i].meDatePartType != ScCheckListMember::DAY )
                continue;

            if ( bSearchTextEmpty )
            {
                SvTreeListEntry* pLeaf = maChecks->ShowCheckEntry( aLabelDisp, maMembers[i], true, maMembers[i].mbVisible );
                updateMemberParents( pLeaf, i );
                if ( maMembers[i].mbVisible )
                    ++nSelCount;
                continue;
            }

            if ( bPartialMatch )
            {
                SvTreeListEntry* pLeaf = maChecks->ShowCheckEntry( aLabelDisp, maMembers[i] );
                updateMemberParents( pLeaf, i );
                ++nSelCount;
            }
            else
            {
                maChecks->ShowCheckEntry( aLabelDisp, maMembers[i], false, false );
                if( bIsDate )
                    bSomeDateDeletes = true;
            }
        }

        if ( bSomeDateDeletes )
        {
            for (size_t i = 0; i < n; ++i)
            {
                if ( !maMembers[i].mbDate ) continue;
                if ( maMembers[i].meDatePartType != ScCheckListMember::DAY ) continue;
                updateMemberParents( nullptr, i );
            }
        }

        if ( nSelCount == n )
            maChkToggleAll->SetState( TRISTATE_TRUE );
        else if ( nSelCount == 0 )
            maChkToggleAll->SetState( TRISTATE_FALSE );
        else
            maChkToggleAll->SetState( TRISTATE_INDET );

        if ( !maConfig.mbAllowEmptySet )
            maBtnOk->Enable( nSelCount != 0);
    }

    IMPL_LINK( ScCheckListMenuWindow, CheckHdl, SvTreeListBox*, pChecks, void )
    {
        if (pChecks != maChecks.get())
            return;
        SvTreeListEntry* pEntry = pChecks->GetHdlEntry();
        if ( pEntry )
            maChecks->CheckEntry( pEntry,  ( pChecks->GetCheckButtonState( pEntry ) == SvButtonState::Checked ) );
        size_t nNumChecked = maChecks->GetCheckedEntryCount();
        if (nNumChecked == maMembers.size())
            // all members visible
            maChkToggleAll->SetState(TRISTATE_TRUE);
        else if (nNumChecked == 0)
            // no members visible
            maChkToggleAll->SetState(TRISTATE_FALSE);
        else
            maChkToggleAll->SetState(TRISTATE_INDET);

        if (!maConfig.mbAllowEmptySet)
            // We need to have at least one member selected.
            maBtnOk->Enable(nNumChecked != 0);

        mePrevToggleAllState = maChkToggleAll->GetState();
    }

    void ScCheckListMenuWindow::MouseMove(const MouseEvent& rMEvt)
    {
        ScMenuFloatingWindow::MouseMove(rMEvt);

        size_t nSelectedMenu = getSelectedMenuItem();
        if (nSelectedMenu == MENU_NOT_SELECTED)
            queueCloseSubMenu();
    }

    bool ScCheckListMenuWindow::EventNotify(NotifyEvent& rNEvt)
    {
        if (rNEvt.GetType() == MouseNotifyEvent::KEYUP)
        {
            const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
            const vcl::KeyCode& rCode = pKeyEvent->GetKeyCode();
            bool bShift = rCode.IsShift();
            if (rCode.GetCode() == KEY_TAB)
            {
                maTabStops.CycleFocus(bShift);
                return true;
            }
        }
        return ScMenuFloatingWindow::EventNotify(rNEvt);
    }

    void ScCheckListMenuWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
    {
        ScMenuFloatingWindow::Paint(rRenderContext, rRect);

        const StyleSettings& rStyle = GetSettings().GetStyleSettings();
        Color aMemberBackColor = rStyle.GetFieldColor();
        Color aBorderColor = rStyle.GetShadowColor();

        Point aPos;
        Size aSize;
        getSectionPosSize(aPos, aSize, LISTBOX_AREA_OUTER);

        // Member list box background
        rRenderContext.SetFillColor(aMemberBackColor);
        rRenderContext.SetLineColor(aBorderColor);
        rRenderContext.DrawRect(tools::Rectangle(aPos,aSize));

        // Single-action button box
        getSectionPosSize(aPos, aSize, SINGLE_BTN_AREA);
        rRenderContext.SetFillColor(rStyle.GetMenuColor());
        rRenderContext.DrawRect(tools::Rectangle(aPos,aSize));
    }

    void ScCheckListMenuWindow::updateMemberParents( const SvTreeListEntry* pLeaf, size_t nIdx )
    {

        if ( !maMembers[nIdx].mbDate || maMembers[nIdx].meDatePartType != ScCheckListMember::DAY )
            return;

        OUString aYearName  = maMembers[nIdx].maDateParts[0];
        OUString aMonthName = maMembers[nIdx].maDateParts[1];
        auto aItr = maYearMonthMap.find(aYearName + aMonthName);

        if ( pLeaf )
        {
            SvTreeListEntry* pMonthEntry = pLeaf->GetParent();
            SvTreeListEntry* pYearEntry = pMonthEntry ? pMonthEntry->GetParent() : nullptr;

            maMembers[nIdx].mpParent = pMonthEntry;
            if ( aItr != maYearMonthMap.end() )
            {
                size_t nMonthIdx = aItr->second;
                maMembers[nMonthIdx].mpParent = pYearEntry;
            }
        }
        else
        {
            SvTreeListEntry* pYearEntry = maChecks->FindEntry( nullptr, aYearName );
            if ( aItr != maYearMonthMap.end() && !pYearEntry )
            {
                size_t nMonthIdx = aItr->second;
                maMembers[nMonthIdx].mpParent = nullptr;
                maMembers[nIdx].mpParent = nullptr;
            }
            else if ( pYearEntry && !maChecks->FindEntry( pYearEntry, aMonthName ) )
                maMembers[nIdx].mpParent = nullptr;
        }
    }

    Reference<XAccessible> ScCheckListMenuWindow::CreateAccessible()
    {
        if (!mxAccessible.is() && maEdSearch)
        {
            mxAccessible.set(new ScAccessibleFilterTopWindow(
                GetAccessibleParentWindow()->GetAccessible(), this, getName()));
            ScAccessibleFilterTopWindow* pAccTop = static_cast<ScAccessibleFilterTopWindow*>(mxAccessible.get());
            fillMenuItemsToAccessible(pAccTop);

            pAccTop->setAccessibleChild(
                maEdSearch->CreateAccessible(), ScAccessibleFilterTopWindow::EDIT_SEARCH_BOX);
            pAccTop->setAccessibleChild(
                maChecks->CreateAccessible(), ScAccessibleFilterTopWindow::LISTBOX);
            pAccTop->setAccessibleChild(
                maChkToggleAll->CreateAccessible(), ScAccessibleFilterTopWindow::TOGGLE_ALL);
            pAccTop->setAccessibleChild(
                maBtnSelectSingle->CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_ON_BTN);
            pAccTop->setAccessibleChild(
                maBtnUnselectSingle->CreateAccessible(), ScAccessibleFilterTopWindow::SINGLE_OFF_BTN);
            pAccTop->setAccessibleChild(
                maBtnOk->CreateAccessible(), ScAccessibleFilterTopWindow::OK_BTN);
            pAccTop->setAccessibleChild(
                maBtnCancel->CreateAccessible(), ScAccessibleFilterTopWindow::CANCEL_BTN);
        }

        return mxAccessible;
    }

    void ScCheckListMenuWindow::setMemberSize(size_t n)
    {
        maMembers.reserve(n);
    }

    void ScCheckListMenuWindow::addDateMember(const OUString& rsName, double nVal, bool bVisible)
    {
        ScDocument* pDoc = getDoc();
        SvNumberFormatter* pFormatter = pDoc->GetFormatTable();

        // Convert the numeric date value to a date object.
        Date aDate = pFormatter->GetNullDate();
        aDate.AddDays(rtl::math::approxFloor(nVal));

        sal_Int16 nYear = aDate.GetYear();
        sal_uInt16 nMonth = aDate.GetMonth();
        sal_uInt16 nDay = aDate.GetDay();

        // Get the localized month name list.
        CalendarWrapper* pCalendar = ScGlobal::GetCalendar();
        uno::Sequence<i18n::CalendarItem2> aMonths = pCalendar->getMonths();
        if (aMonths.getLength() < nMonth)
            return;

        OUString aYearName = OUString::number(nYear);
        OUString aMonthName = aMonths[nMonth-1].FullName;
        OUString aDayName = OUString::number(nDay);

        if ( aDayName.getLength() == 1 )
            aDayName = "0" + aDayName;

        maChecks->SetUpdateMode(false);

        SvTreeListEntry* pYearEntry = maChecks->FindEntry(nullptr, aYearName);
        if (!pYearEntry)
        {
            pYearEntry = maChecks->InsertEntry(aYearName, nullptr, true);
            ScCheckListMember aMemYear;
            aMemYear.maName = aYearName;
            aMemYear.maRealName = rsName;
            aMemYear.mbDate = true;
            aMemYear.mbLeaf = false;
            aMemYear.mbVisible = bVisible;
            aMemYear.mpParent = nullptr;
            aMemYear.meDatePartType = ScCheckListMember::YEAR;
            maMembers.push_back(aMemYear);
        }

        SvTreeListEntry* pMonthEntry = maChecks->FindEntry(pYearEntry, aMonthName);
        if (!pMonthEntry)
        {
            pMonthEntry = maChecks->InsertEntry(aMonthName, pYearEntry, true);
            ScCheckListMember aMemMonth;
            aMemMonth.maName = aMonthName;
            aMemMonth.maRealName = rsName;
            aMemMonth.mbDate = true;
            aMemMonth.mbLeaf = false;
            aMemMonth.mbVisible = bVisible;
            aMemMonth.mpParent = pYearEntry;
            aMemMonth.meDatePartType = ScCheckListMember::MONTH;
            maMembers.push_back(aMemMonth);
            maYearMonthMap[aYearName + aMonthName] = maMembers.size() - 1;
        }

        SvTreeListEntry* pDayEntry = maChecks->FindEntry(pMonthEntry, aDayName);
        if (!pDayEntry)
        {
            maChecks->InsertEntry(aDayName, pMonthEntry);
            ScCheckListMember aMemDay;
            aMemDay.maName = aDayName;
            aMemDay.maRealName = rsName;
            aMemDay.maDateParts.resize(2);
            aMemDay.maDateParts[0] = aYearName;
            aMemDay.maDateParts[1] = aMonthName;
            aMemDay.mbDate = true;
            aMemDay.mbLeaf = true;
            aMemDay.mbVisible = bVisible;
            aMemDay.mpParent = pMonthEntry;
            aMemDay.meDatePartType = ScCheckListMember::DAY;
            maMembers.push_back(aMemDay);
        }

        maChecks->SetUpdateMode(true);
    }

    void ScCheckListMenuWindow::addMember(const OUString& rName, bool bVisible)
    {
        ScCheckListMember aMember;
        aMember.maName = rName;
        aMember.mbDate = false;
        aMember.mbLeaf = true;
        aMember.mbVisible = bVisible;
        aMember.mpParent = nullptr;
        maMembers.push_back(aMember);
    }

    ScTabStops::ScTabStops( ScCheckListMenuWindow* pMenuWin ) :
        mpMenuWindow( pMenuWin ),
        maControlToPos( ControlToPosMap() ),
        mnCurTabStop(0)
    {
        maControls.reserve( 8 );
    }

    ScTabStops::~ScTabStops()
    {}

    void ScTabStops::AddTabStop( vcl::Window* pWin )
    {
        maControls.emplace_back(pWin );
        maControlToPos[pWin] = maControls.size() - 1;
    }

    void ScTabStops::SetTabStop( vcl::Window* pWin )
    {
        if ( !maControls.size() )
            return;
        ControlToPosMap::const_iterator aIter = maControlToPos.find( pWin );
        if ( aIter == maControlToPos.end() )
            return;
        if ( aIter->second == mnCurTabStop )
            return;
        if ( mnCurTabStop < maControls.size() )
        {
            maControls[mnCurTabStop]->SetFakeFocus( false );
            maControls[mnCurTabStop]->LoseFocus();
        }
        mnCurTabStop = aIter->second;
        maControls[mnCurTabStop]->SetFakeFocus( true );
        maControls[mnCurTabStop]->GrabFocus();
    }

    void ScTabStops::CycleFocus( bool bReverse )
    {
        if (!maControls.size())
            return;
        if ( mnCurTabStop < maControls.size() )
        {
            maControls[mnCurTabStop]->SetFakeFocus( false );
            maControls[mnCurTabStop]->LoseFocus();
        }
        else
            mnCurTabStop = 0;

        if ( mpMenuWindow && mnCurTabStop == 0 )
            mpMenuWindow->clearSelectedMenuItem();

        size_t nIterCount = 0;

        if ( bReverse )
        {
            do
            {
                if ( mnCurTabStop > 0 )
                    --mnCurTabStop;
                else
                    mnCurTabStop = maControls.size() - 1;
                ++nIterCount;
            } while ( nIterCount <= maControls.size() && !maControls[mnCurTabStop]->IsEnabled() );
        }
        else
        {
            do
            {
                ++mnCurTabStop;
                if ( mnCurTabStop >= maControls.size() )
                    mnCurTabStop = 0;
                ++nIterCount;
            } while ( nIterCount <= maControls.size() && !maControls[mnCurTabStop]->IsEnabled() );
        }

        if ( nIterCount <= maControls.size() )
        {
            maControls[mnCurTabStop]->SetFakeFocus( true );
            maControls[mnCurTabStop]->GrabFocus();
        }
        // else : all controls are disabled, so can't do anything
    }

    void ScTabStops::clear()
    {
        mnCurTabStop = 0;
        maControlToPos.clear();
        maControls.clear();
    }

    ScCheckListBox::ScCheckListBox( vcl::Window* pParent )
        :  SvTreeListBox( pParent, 0 ), mbSeenMouseButtonDown( false )
    {
        Init();
        set_id("check_list_box");
    }

    SvTreeListEntry* ScCheckListBox::FindEntry( SvTreeListEntry* pParent, const OUString& sNode )
    {
        sal_uInt32 nRootPos = 0;
        SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : GetEntry( nRootPos );
        while ( pEntry )
        {
            if ( sNode == GetEntryText( pEntry ) )
                return pEntry;

            pEntry = pParent ? pEntry->NextSibling() : GetEntry( ++nRootPos );
        }
        return nullptr;
    }

    void ScCheckListBox::Init()
    {
        mpCheckButton.reset( new SvLBoxButtonData( this ) );
        EnableCheckButton( mpCheckButton.get() );
        SetNodeDefaultImages();
    }

    void ScCheckListBox::GetRecursiveChecked( SvTreeListEntry* pEntry, std::unordered_set<OUString>& vOut,
            OUString& rLabel )
    {
        if (GetCheckButtonState(pEntry) == SvButtonState::Checked)
        {
            // We have to hash parents and children together.
            // Per convention for easy access in getResult()
            // "child;parent;grandparent" while descending.
            if (rLabel.isEmpty())
                rLabel = GetEntryText(pEntry);
            else
                rLabel = GetEntryText(pEntry) + ";" + rLabel;

            // Prerequisite: the selection mechanism guarantees that if a child is
            // selected then also the parent is selected, so we only have to
            // inspect the children in case the parent is selected.
            if (pEntry->HasChildren())
            {
                const SvTreeListEntries& rChildren = pEntry->GetChildEntries();
                for (auto& rChild : rChildren)
                {
                    OUString aLabel = rLabel;
                    GetRecursiveChecked( rChild.get(), vOut, aLabel);
                    if (!aLabel.isEmpty() && aLabel != rLabel)
                        vOut.insert( aLabel);
                }
                // Let the caller not add the parent alone.
                rLabel.clear();
            }
        }
    }

    std::unordered_set<OUString> ScCheckListBox::GetAllChecked()
    {
        std::unordered_set<OUString> vResults(0);
        sal_uInt32 nRootPos = 0;
        SvTreeListEntry* pEntry = GetEntry(nRootPos);
        while (pEntry)
        {
            OUString aLabel;
            GetRecursiveChecked( pEntry, vResults, aLabel);
            if (!aLabel.isEmpty())
                vResults.insert( aLabel);
            pEntry = GetEntry(++nRootPos);
        }

        return vResults;
    }

    bool ScCheckListBox::IsChecked( const OUString& sName, SvTreeListEntry* pParent )
    {
        SvTreeListEntry* pEntry = FindEntry( pParent, sName );
        return pEntry && GetCheckButtonState( pEntry ) == SvButtonState::Checked;
    }

    void ScCheckListBox::CheckEntry( const OUString& sName, SvTreeListEntry* pParent, bool bCheck )
    {
        SvTreeListEntry* pEntry = FindEntry( pParent, sName );
        if ( pEntry )
            CheckEntry(  pEntry, bCheck );
    }

    // Recursively check all children of pParent
    void ScCheckListBox::CheckAllChildren( SvTreeListEntry* pParent, bool bCheck )
    {
        if ( pParent )
        {
            SetCheckButtonState(
                pParent, bCheck ? SvButtonState::Checked : SvButtonState::Unchecked );
        }
        SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
        while ( pEntry )
        {
            CheckAllChildren( pEntry, bCheck );
            pEntry = pEntry->NextSibling();
        }
    }

    void ScCheckListBox::CheckEntry( SvTreeListEntry* pParent, bool bCheck )
    {
        // recursively check all items below pParent
        CheckAllChildren( pParent, bCheck );
        // checking pParent can affect ancestors, e.g. if ancestor is unchecked and pParent is
        // now checked then the ancestor needs to be checked also
        SvTreeListEntry* pAncestor = GetParent(pParent);
        if ( pAncestor )
        {
            while ( pAncestor )
            {
                // if any first level children checked then ancestor
                // needs to be checked, similarly if no first level children
                // checked then ancestor needs to be unchecked
                SvTreeListEntry* pChild = FirstChild( pAncestor );
                bool bChildChecked = false;

                while ( pChild )
                {
                    if ( GetCheckButtonState( pChild ) == SvButtonState::Checked )
                    {
                        bChildChecked = true;
                        break;
                    }
                    pChild = pChild->NextSibling();
                }
                SetCheckButtonState( pAncestor, bChildChecked ? SvButtonState::Checked : SvButtonState::Unchecked );
                pAncestor = GetParent(pAncestor);
            }
        }
    }

    SvTreeListEntry* ScCheckListBox::ShowCheckEntry( const OUString& sName, ScCheckListMember& rMember, bool bShow, bool bCheck )
    {
        SvTreeListEntry* pEntry = nullptr;
        if (!rMember.mbDate || rMember.mpParent)
            pEntry = FindEntry( rMember.mpParent, sName );

        if ( bShow )
        {
            if ( !pEntry )
            {
                if (rMember.mbDate)
                {
                    if (rMember.maDateParts.empty())
                        return nullptr;

                    SvTreeListEntry* pYearEntry = FindEntry( nullptr, rMember.maDateParts[0] );
                    if ( !pYearEntry )
                        pYearEntry = InsertEntry( rMember.maDateParts[0], nullptr, true );
                    SvTreeListEntry* pMonthEntry = FindEntry( pYearEntry, rMember.maDateParts[1] );
                    if ( !pMonthEntry )
                        pMonthEntry = InsertEntry( rMember.maDateParts[1], pYearEntry, true );
                    SvTreeListEntry* pDayEntry = FindEntry( pMonthEntry, rMember.maName );
                    if ( !pDayEntry )
                        pDayEntry = InsertEntry( rMember.maName, pMonthEntry );

                    return pDayEntry; // Return leaf node
                }

                pEntry = InsertEntry(
                    sName);

                SetCheckButtonState(
                    pEntry, bCheck ? SvButtonState::Checked : SvButtonState::Unchecked);
            }
            else
                CheckEntry( pEntry, bCheck );
        }
        else if ( pEntry )
        {
            GetModel()->Remove( pEntry );
            SvTreeListEntry* pParent = rMember.mpParent;
            while ( pParent && !pParent->HasChildren() )
            {
                SvTreeListEntry* pTmp = pParent;
                pParent = pTmp->GetParent();
                GetModel()->Remove( pTmp );
            }
        }
        return nullptr;
    }

    void ScCheckListBox::CountCheckedEntries( SvTreeListEntry* pParent, sal_uLong& nCount ) const
    {
        if ( pParent && GetCheckButtonState( pParent ) == SvButtonState::Checked  )
            nCount++;
        // Iterate over the children
        SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
        while ( pEntry )
        {
            CountCheckedEntries( pEntry, nCount );
            pEntry = pEntry->NextSibling();
        }
    }

    sal_uInt16 ScCheckListBox::GetCheckedEntryCount() const
    {
        sal_uLong nCount = 0;
        CountCheckedEntries( nullptr,  nCount );
        return nCount;
    }

    void ScCheckListBox::KeyInput( const KeyEvent& rKEvt )
    {
        const vcl::KeyCode& rKey = rKEvt.GetKeyCode();

        if ( rKey.GetCode() == KEY_RETURN || rKey.GetCode() == KEY_SPACE )
        {
            SvTreeListEntry* pEntry = GetCurEntry();
            if ( pEntry )
            {
                bool bCheck = ( GetCheckButtonState( pEntry ) == SvButtonState::Checked );
                CheckEntry( pEntry, !bCheck );
                if ( bCheck != ( GetCheckButtonState( pEntry ) == SvButtonState::Checked ) )
                    CheckButtonHdl();
            }
        }
        else if ( GetEntryCount() )
            SvTreeListBox::KeyInput( rKEvt );
    }

    void ScCheckListBox::MouseButtonDown(const MouseEvent& rMEvt)
    {
        SvTreeListBox::MouseButtonDown( rMEvt );
        if ( rMEvt.IsLeft() )
            mbSeenMouseButtonDown = true;
    }

    void ScCheckListBox::MouseButtonUp(const MouseEvent& rMEvt)
    {
        SvTreeListBox::MouseButtonUp( rMEvt );
        if ( mpTabStops && mbSeenMouseButtonDown && rMEvt.IsLeft() )
        {
            mpTabStops->SetTabStop( this );
            mbSeenMouseButtonDown = false;
        }
    }

    void ScSearchEdit::MouseButtonDown(const MouseEvent& rMEvt)
    {
        Edit::MouseButtonDown( rMEvt );
        if ( mpTabStops && rMEvt.IsLeft() && rMEvt.GetClicks() >= 1 )
            mpTabStops->SetTabStop( this );
    }

    void ScCheckListMenuWindow::setHasDates(bool bHasDates)
    {
        // Enables type-ahead search in the check list box.
        maChecks->SetQuickSearch(true);
        if (bHasDates)
            maChecks->SetStyle(WB_HASBUTTONS | WB_HASLINES | WB_HASLINESATROOT | WB_HASBUTTONSATROOT);
        else
            maChecks->SetStyle(WB_HASBUTTONS);
    }

    void ScCheckListMenuWindow::initMembers()
    {
        size_t n = maMembers.size();
        size_t nVisMemCount = 0;

        maChecks->SetUpdateMode(false);
        maChecks->GetModel()->EnableInvalidate(false);

        for (size_t i = 0; i < n; ++i)
        {
            if (maMembers[i].mbDate)
            {
                maChecks->CheckEntry(maMembers[i].maName, maMembers[i].mpParent, maMembers[i].mbVisible);
                // Expand first node of checked dates
                if (!maMembers[i].mpParent && maChecks->IsChecked(maMembers[i].maName,  maMembers[i].mpParent))
                {
                    SvTreeListEntry* pEntry = maChecks->FindEntry(nullptr, maMembers[i].maName);
                    if (pEntry)
                        maChecks->Expand(pEntry);
                }
            }
            else
            {
                OUString aLabel = maMembers[i].maName;
                if (aLabel.isEmpty())
                    aLabel = ScResId(STR_EMPTYDATA);
                SvTreeListEntry* pEntry = maChecks->InsertEntry(
                    aLabel);

                maChecks->SetCheckButtonState(
                    pEntry, maMembers[i].mbVisible ? SvButtonState::Checked : SvButtonState::Unchecked);
            }

            if (maMembers[i].mbVisible)
                ++nVisMemCount;
        }
        if (nVisMemCount == n)
        {
            // all members visible
            maChkToggleAll->SetState(TRISTATE_TRUE);
            mePrevToggleAllState = TRISTATE_TRUE;
        }
        else if (nVisMemCount == 0)
        {
            // no members visible
            maChkToggleAll->SetState(TRISTATE_FALSE);
            mePrevToggleAllState = TRISTATE_FALSE;
        }
        else
        {
            maChkToggleAll->SetState(TRISTATE_INDET);
            mePrevToggleAllState = TRISTATE_INDET;
        }

        maChecks->GetModel()->EnableInvalidate(true);
        maChecks->SetUpdateMode(true);
    }

    void ScCheckListMenuWindow::setConfig(const Config& rConfig)
    {
        maConfig = rConfig;
    }

    bool ScCheckListMenuWindow::isAllSelected() const
    {
        return maChkToggleAll->IsChecked();
    }

    void ScCheckListMenuWindow::getResult(ResultType& rResult)
    {
        ResultType aResult;
        std::unordered_set<OUString> vCheckeds = maChecks->GetAllChecked();
        size_t n = maMembers.size();
        for (size_t i = 0; i < n; ++i)
        {
            if ( maMembers[i].mbLeaf )
            {
                OUStringBuffer aLabel = maMembers[i].maName;
                if (aLabel.isEmpty())
                    aLabel = ScResId(STR_EMPTYDATA);

                /* TODO: performance-wise this looks suspicious, concatenating to
                 * do the lookup for each leaf item seems wasteful. */
                // Checked labels are in the form "child;parent;grandparent".
                for (SvTreeListEntry* pParent = maMembers[i].mpParent;
                        pParent && pParent->GetFirstItem( SvLBoxItemType::String);
                        pParent = pParent->GetParent())
                {
                    aLabel.append(";").append(maChecks->GetEntryText( pParent));
                }
                bool bState = vCheckeds.find(aLabel.makeStringAndClear()) != vCheckeds.end();

                ResultEntry aResultEntry;
                aResultEntry.bValid = bState;
                if ( maMembers[i].mbDate )
                    aResultEntry.aName = maMembers[i].maRealName;
                else
                    aResultEntry.aName = maMembers[i].maName;
                aResultEntry.bDate = maMembers[i].mbDate;
                aResult.insert(aResultEntry);
            }
        }
        rResult.swap(aResult);
    }

    void ScCheckListMenuWindow::launch(const tools::Rectangle& rRect)
    {
        packWindow();
        if (!maConfig.mbAllowEmptySet)
            // We need to have at least one member selected.
            maBtnOk->Enable(maChecks->GetCheckedEntryCount() != 0);

        tools::Rectangle aRect(rRect);
        if (maConfig.mbRTL)
        {
            // In RTL mode, the logical "left" is visual "right".
            long nLeft = aRect.Left() - aRect.GetWidth();
            aRect.SetLeft( nLeft );
        }
        else if (maWndSize.Width() < aRect.GetWidth())
        {
            // Target rectangle (i.e. cell width) is wider than the window.
            // Simulate right-aligned launch by modifying the target rectangle
            // size.
            long nDiff = aRect.GetWidth() - maWndSize.Width();
            aRect.AdjustLeft(nDiff );
        }

        StartPopupMode(aRect, (FloatWinPopupFlags::Down | FloatWinPopupFlags::GrabFocus));
        maTabStops.CycleFocus(); // Set initial focus to the search box ( index = 1 )
    }

    void ScCheckListMenuWindow::close(bool bOK)
    {
        if (bOK && mpOKAction.get())
            mpOKAction->execute();

        EndPopupMode();
    }

    void ScCheckListMenuWindow::setExtendedData(ExtendedData* p)
    {
        mpExtendedData.reset(p);
    }

    ScCheckListMenuWindow::ExtendedData* ScCheckListMenuWindow::getExtendedData()
    {
        return mpExtendedData.get();
    }

    void ScCheckListMenuWindow::setOKAction(Action* p)
    {
        mpOKAction.reset(p);
    }

    void ScCheckListMenuWindow::setPopupEndAction(Action* p)
    {
        mpPopupEndAction.reset(p);
    }

    void ScCheckListMenuWindow::handlePopupEnd()
    {
        clearSelectedMenuItem();
        if (mpPopupEndAction)
            mpPopupEndAction->execute();
    }

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

↑ V557 Array overrun is possible. The 'mnSelectedMenu' index is pointing beyond array bound.

↑ V557 Array overrun is possible. The 'mnSelectedMenu' index is pointing beyond array bound.

↑ V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: mpTabStops.