Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/sc/source/ui/view/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 270 kB image not shown  

Quelle  gridwin.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/.
 *
 * 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 <scitems.hxx>

#include <cstdlib>
#include <memory>
#include <editeng/adjustitem.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>
#include <sot/storage.hxx>
#include <editeng/eeitem.hxx>
#include <editeng/editobj.hxx>
#include <editeng/editstat.hxx>
#include <editeng/editview.hxx>
#include <editeng/flditem.hxx>
#include <editeng/justifyitem.hxx>
#include <editeng/outliner.hxx>
#include <editeng/misspellrange.hxx>
#include <o3tl/unit_conversion.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/viewfrm.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/ipclient.hxx>
#include <svl/stritem.hxx>
#include <svl/sharedstringpool.hxx>
#include <vcl/canvastools.hxx>
#include <vcl/commandevent.hxx>
#include <vcl/cursor.hxx>
#include <vcl/dialoghelper.hxx>
#include <vcl/inputctx.hxx>
#include <vcl/settings.hxx>
#include <vcl/virdev.hxx>
#include <vcl/weldutils.hxx>
#include <sot/formats.hxx>
#include <comphelper/classids.hxx>
#include <comphelper/scopeguard.hxx>

#include <svx/svdview.hxx>
#include <svx/svdocapt.hxx>
#include <svx/svdpagv.hxx>
#include <svtools/optionsdrawinglayer.hxx>

#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
#include <com/sun/star/sheet/MemberResultFlags.hpp>
#include <com/sun/star/sheet/TableValidationVisibility.hpp>
#include <com/sun/star/awt/KeyModifier.hpp>
#include <com/sun/star/awt/MouseButton.hpp>
#include <com/sun/star/script/vba/VBAEventId.hpp>
#include <com/sun/star/script/vba/XVBAEventProcessor.hpp>
#include <com/sun/star/text/textfield/Type.hpp>

#include <com/sun/star/awt/XVclWindowPeer.hdl>

#include <gridwin.hxx>
#include <tabvwsh.hxx>
#include <docsh.hxx>
#include <viewdata.hxx>
#include <tabview.hxx>
#include <select.hxx>
#include <scmod.hxx>
#include <document.hxx>
#include <attrib.hxx>
#include <dbdata.hxx>
#include <stlpool.hxx>
#include <printfun.hxx>
#include <cbutton.hxx>
#include <sc.hrc>
#include <helpids.h>
#include <globstr.hrc>
#include <strings.hrc>
#include <editutil.hxx>
#include <scresid.hxx>
#include <inputhdl.hxx>
#include <uiitems.hxx>
#include <formulacell.hxx>
#include <patattr.hxx>
#include <notemark.hxx>
#include <rfindlst.hxx>
#include <output.hxx>
#include <docfunc.hxx>
#include <dbdocfun.hxx>
#include <dpobject.hxx>
#include <transobj.hxx>
#include <drwtrans.hxx>
#include <seltrans.hxx>
#include <sizedev.hxx>
#include <AccessibilityHints.hxx>
#include <dpsave.hxx>
#include <viewuno.hxx>
#include <compiler.hxx>
#include <editable.hxx>
#include <fillinfo.hxx>
#include <filterentries.hxx>
#include <drwlayer.hxx>
#include <validat.hxx>
#include <tabprotection.hxx>
#include <postit.hxx>
#include <dpcontrol.hxx>
#include <checklistmenu.hxx>
#include <clipparam.hxx>
#include <overlayobject.hxx>
#include <cellsuno.hxx>
#include <drawview.hxx>
#include <dragdata.hxx>
#include <cliputil.hxx>
#include <queryentry.hxx>
#include <markdata.hxx>
#include <externalrefmgr.hxx>
#include <spellcheckcontext.hxx>
#include <uiobject.hxx>
#include <undoblk.hxx>
#include <datamapper.hxx>
#include <inputopt.hxx>
#include <queryparam.hxx>
#include <SparklineList.hxx>

#include <officecfg/Office/Common.hxx>

#include <svx/PaletteManager.hxx>
#include <svx/sdrpagewindow.hxx>
#include <svx/sdr/overlay/overlaymanager.hxx>
#include <vcl/svapp.hxx>
#include <vcl/uitest/logger.hxx>
#include <vcl/uitest/eventdescription.hxx>
#include <svx/sdr/overlay/overlayselection.hxx>
#include <comphelper/lok.hxx>
#include <sfx2/lokhelper.hxx>

#include <LibreOfficeKit/LibreOfficeKitEnums.h>

#include <vector>
#include <boost/property_tree/json_parser.hpp>

#include <FilterListBox.hxx>

using namespace css;
using namespace css::uno;

struct ScGridWindow::MouseEventState
{
    bool mbActivatePart;

    MouseEventState() :
        mbActivatePart(false)
    {}
};

#define SC_FILTERLISTBOX_LINES  12

ScGridWindow::VisibleRange::VisibleRange(const ScDocument& rDoc)
    : mnCol1(0)
    , mnCol2(rDoc.MaxCol())
    , mnRow1(0)
    , mnRow2(rDoc.MaxRow())
{
}

bool ScGridWindow::VisibleRange::isInside(SCCOL nCol, SCROW nRow) const
{
    return mnCol1 <= nCol && nCol <= mnCol2 && mnRow1 <= nRow && nRow <= mnRow2;
}

bool ScGridWindow::VisibleRange::set(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
{
    bool bChanged = mnCol1 != nCol1 || mnRow1 != nRow1 || mnCol2 != nCol2 || mnRow2 != nRow2;

    mnCol1 = nCol1;
    mnRow1 = nRow1;
    mnCol2 = nCol2;
    mnRow2 = nRow2;

    return bChanged;
}

//  ListBox in a FloatingWindow (pParent)
ScFilterListBox::ScFilterListBox(weld::Window* pParent, ScGridWindow* pGrid,
                                 SCCOL nNewCol, SCROW nNewRow, ScFilterBoxMode eNewMode)
    : xBuilder(Application::CreateBuilder(pParent, u"modules/scalc/ui/filterlist.ui"_ustr))
    , xPopover(xBuilder->weld_popover(u"FilterList"_ustr))
    , xTreeView(xBuilder->weld_tree_view(u"list"_ustr))
    , pGridWin(pGrid)
    , nCol(nNewCol)
    , nRow(nNewRow)
    , bInit(true)
    , bCancelled(false)
    , bGridHadMouseCaptured(pGrid->IsMouseCaptured())
    , nSel(0)
    , eMode(eNewMode)
    , nAsyncSelectHdl(nullptr)
{
    xTreeView->connect_row_activated(LINK(this, ScFilterListBox, SelectHdl));
    xTreeView->connect_key_press(LINK(this, ScFilterListBox, KeyInputHdl));
}

ScFilterListBox::~ScFilterListBox()
{
    if (nAsyncSelectHdl)
    {
        Application::RemoveUserEvent(nAsyncSelectHdl);
        nAsyncSelectHdl = nullptr;
    }
}

void ScFilterListBox::EndInit()
{
    sal_Int32 nPos = xTreeView->get_selected_index();
    if (nPos == -1)
        nSel = 0;
    else
        nSel = nPos;

    bInit = false;
}

IMPL_LINK(ScFilterListBox, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
{
    bool bDone = false;

    vcl::KeyCode aCode = rKeyEvent.GetKeyCode();
    // esc with no modifiers
    if (!aCode.GetModifier() && aCode.GetCode() == KEY_ESCAPE)
    {
        pGridWin->ClickExtern();  // clears the listbox
        bDone = true;
    }

    // nowhere to tab to
    if (aCode.GetCode() == KEY_TAB)
        bDone = true;

    return bDone;
}

IMPL_LINK_NOARG(ScFilterListBox, SelectHdl, weld::TreeView&, bool)
{
    if (!bInit && !bCancelled && !nAsyncSelectHdl)
    {
        int nPos = xTreeView->get_selected_index();
        if (nPos != -1)
        {
            nSel = nPos;
            // #i81298# launch async so the box isn't deleted from modifications within FilterSelect
            nAsyncSelectHdl = Application::PostUserEvent(LINK(this, ScFilterListBox, AsyncSelectHdl));
        }
    }
    return true;
}

IMPL_LINK_NOARG(ScFilterListBox, AsyncSelectHdl, void*, void)
{
    nAsyncSelectHdl = nullptr;

    //tdf#133971 hold self-ref until we return
    auto xThis(shared_from_this());
    pGridWin->FilterSelect(nSel);
    if (xThis.use_count() == 1)
    {
        // tdf#133855 we got disposed by FilterSelect
        return;
    }
    pGridWin->ClickExtern();
}

static bool lcl_IsEditableMatrix( ScDocument& rDoc, const ScRange& rRange )
{
    // If it is an editable range and if there is a Matrix cell at the bottom right with an
    // origin top left then the range will be set to contain the exact matrix.
    //! Extract the MatrixEdges functions directly from the column ???
    if ( !rDoc.IsBlockEditable( rRange.aStart.Tab(), rRange.aStart.Col(),rRange.aStart.Row(),
                                    rRange.aEnd.Col(),rRange.aEnd.Row() ) )
        return false;

    ScRefCellValue aCell(rDoc, rRange.aEnd);
    ScAddress aPos;
    return (aCell.getType() == CELLTYPE_FORMULA && aCell.getFormula()->GetMatrixOrigin(rDoc, aPos) && aPos == rRange.aStart);
}

static void lcl_UnLockComment( ScDrawView* pView, const Point& rPos, const ScViewData&&nbsp;rViewData )
{
    if (!pView)
        return;

    ScDocument& rDoc = rViewData.GetDocument();
    ScAddress aCellPos( rViewData.GetCurX(), rViewData.GetCurY(), rViewData.GetTabNo() );
    ScPostIt* pNote = rDoc.GetNote( aCellPos );
    SdrObject* pObj = pNote ? pNote->GetCaption() : nullptr;
    if( pObj && pObj->GetLogicRect().Contains( rPos ) && ScDrawLayer::IsNoteCaption( pObj ) )
    {
        const ScProtectionAttr* pProtAttr = rDoc.GetAttr( aCellPos, ATTR_PROTECTION );
        bool bProtectAttr = pProtAttr->GetProtection() || pProtAttr->GetHideCell() ;
        bool bProtectDoc =  rDoc.IsTabProtected( aCellPos.Tab() ) || rViewData.GetSfxDocShell().IsReadOnly() ;
        // unlock internal layer (if not protected), will be relocked in ScDrawView::MarkListHasChanged()
        pView->LockInternalLayer( bProtectDoc && bProtectAttr );
    }
}

static bool lcl_GetHyperlinkCell(
    ScDocument& rDoc, SCCOL& rPosX, SCROW nPosY, SCTAB nTab, ScRefCellValue& rCell, OUString& rURL )
{
    bool bFound = false;
    do
    {
        ScAddress aPos(rPosX, nPosY, nTab);
        rCell.assign(rDoc, aPos);
        if (rCell.isEmpty())
        {
            if ( rPosX <= 0 )
                return false;                           // everything empty to the links
            else
                --rPosX;                                // continue search
        }
        else
        {
            const ScPatternAttr* pPattern = rDoc.GetPattern(aPos);
            if ( !pPattern->GetItem(ATTR_HYPERLINK).GetValue().isEmpty() )
            {
                rURL = pPattern->GetItem(ATTR_HYPERLINK).GetValue();
                bFound = true;
            }
            else if (rCell.getType() == CELLTYPE_EDIT)
                bFound = true;
            else if (rCell.getType() == CELLTYPE_FORMULA && rCell.getFormula()->IsHyperLinkCell())
                bFound = true;
            else
                return false;                               // other cell
        }
    }
    while ( !bFound );

    return bFound;
}

static void lcl_GetMirror(Point& rPoint, tools::Rectangle& rRect, const tools::Long nWidth)
{
    tools::Long nLeft = rRect.Left();
    tools::Long nRight = rRect.Right();
    tools::Long nMirrorPX = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::px);
    tools::Long nMirrorMM = o3tl::convert(nWidth, o3tl::Length::twip, o3tl::Length::mm100);

    rPoint.setX(nMirrorPX - rPoint.X());
    rRect.SetLeft(nMirrorMM - nRight);
    rRect.SetRight(nMirrorMM - nLeft);
}

//  WB_DIALOGCONTROL needed for UNO-Controls
ScGridWindow::ScGridWindow( vcl::Window* pParent, ScViewData& rData, ScSplitPos eWhichPos )
:           DocWindow( pParent, WB_CLIPCHILDREN | WB_DIALOGCONTROL ),
            DropTargetHelper( this ),
            DragSourceHelper( this ),
            maVisibleRange(rData.GetDocument()),
            mrViewData( rData ),
            eWhich( eWhichPos ),
            nCursorHideCount( 0 ),
            nButtonDown( 0 ),
            nMouseStatus( SC_GM_NONE ),
            nNestedButtonState( ScNestedButtonState::NONE ),
            nDPField( 0 ),
            pDragDPObj( nullptr ),
            nRFIndex( 0 ),
            nRFAddX( 0 ),
            nRFAddY( 0 ),
            nPagebreakMouse( SC_PD_NONE ),
            nPagebreakBreak( 0 ),
            nPagebreakPrev( 0 ),
            nPageScript( SvtScriptType::NONE ),
            nDragStartX( -1 ),
            nDragStartY( -1 ),
            nDragEndX( -1 ),
            nDragEndY( -1 ),
            meDragInsertMode( INS_NONE ),
            aComboButton( GetOutDev() ),
            aCurMousePos( 0,0 ),
            nPaintCount( 0 ),
            aDrawSelectionPos( 0,0 ),
            aRFSelectedCorned( NONE ),
            maShowPageBreaksTimer("ScGridWindow maShowPageBreaksTimer"),
            bEEMouse( false ),
            bDPMouse( false ),
            bRFMouse( false ),
            bRFSize( false ),
            bPagebreakDrawn( false ),
            bDragRect( false ),
            bIsInPaint( false ),
            bNeedsRepaint( false ),
            bAutoMarkVisible( false ),
            bListValButton( false ),
            m_nDownPosX( -1 ),
            m_nDownPosY( -1 )
{
    set_id(u"grid_window"_ustr);
    switch(eWhich)
    {
        case SC_SPLIT_TOPLEFT:
            eHWhich = SC_SPLIT_LEFT;
            eVWhich = SC_SPLIT_TOP;
            break;
        case SC_SPLIT_TOPRIGHT:
            eHWhich = SC_SPLIT_RIGHT;
            eVWhich = SC_SPLIT_TOP;
            break;
        case SC_SPLIT_BOTTOMLEFT:
            eHWhich = SC_SPLIT_LEFT;
            eVWhich = SC_SPLIT_BOTTOM;
            break;
        case SC_SPLIT_BOTTOMRIGHT:
            eHWhich = SC_SPLIT_RIGHT;
            eVWhich = SC_SPLIT_BOTTOM;
            break;
        default:
            OSL_FAIL("GridWindow: wrong position");
    }

    SetUseFrameData(comphelper::LibreOfficeKit::isActive());
    SetBackground();

    SetMapMode(mrViewData.GetLogicMode(eWhich));
    EnableChildTransparentMode();
    SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus );

    SetHelpId( HID_SC_WIN_GRIDWIN );

    GetOutDev()->SetDigitLanguage( ScModule::GetOptDigitLanguage() );
    EnableRTL( false );

    bInitialPageBreaks = true;
    maShowPageBreaksTimer.SetInvokeHandler(LINK(this, ScGridWindow, InitiatePageBreaksTimer));
    maShowPageBreaksTimer.SetTimeout(1);
}

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

void ScGridWindow::dispose()
{
    maShowPageBreaksTimer.Stop();

    ImpDestroyOverlayObjects();

    mpFilterBox.reset();
    mpNoteOverlay.reset();
    mpAutoFilterPopup.reset();
    mpDPFieldPopup.reset();
    aComboButton.SetOutputDevice(nullptr);

    if (mpSpellCheckCxt)
        mpSpellCheckCxt->reset();
    mpSpellCheckCxt.reset();

    vcl::Window::dispose();
}

void ScGridWindow::ClickExtern()
{
    do
    {
        // #i84277# when initializing the filter box, a Basic error can deactivate the view
        if (mpFilterBox && mpFilterBox->IsInInit())
            break;
        mpFilterBox.reset();
    }
    while (false);

    if (mpDPFieldPopup)
    {
        mpDPFieldPopup->close(false);
        mpDPFieldPopup.reset();
    }
}

IMPL_LINK_NOARG(ScGridWindow, PopupModeEndHdl, weld::Popover&, void)
{
    if (mpFilterBox)
    {
        bool bMouseWasCaptured = mpFilterBox->MouseWasCaptured();
        mpFilterBox->SetCancelled();     // cancel select
        // restore the mouse capture state of the GridWindow to
        // what it was at initial popup time
        SAL_WARN_IF(bMouseWasCaptured, "sc.ui""Is there a scenario where the mouse was captured before mouse down?");
        if (bMouseWasCaptured)
            CaptureMouse();
    }
    GrabFocus();
}

IMPL_LINK( ScGridWindow, PopupSpellingHdl, SpellCallbackInfo&, rInfo, void )
{
    if( rInfo.nCommand == SpellCallbackCommand::STARTSPELLDLG )
        mrViewData.GetDispatcher().Execute( SID_SPELL_DIALOG, SfxCallMode::ASYNCHRON );
    else if (rInfo.nCommand == SpellCallbackCommand::AUTOCORRECT_OPTIONS)
        mrViewData.GetDispatcher().Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON );
    else //IGNOREWORD, ADDTODICTIONARY, WORDLANGUAGE, PARALANGUAGE
    {
        // The spelling status of the word has changed. Close the cell to reset the caches
        ScInputHandler* pHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
        if (pHdl)
            pHdl->EnterHandler();
    }
}

namespace {

struct AutoFilterData : public ScCheckListMenuControl::ExtendedData
{
    ScAddress maPos;
    ScDBData* mpData;
};

class AutoFilterAction : public ScCheckListMenuControl::Action
{
protected:
    VclPtr<ScGridWindow> mpWindow;
    ScGridWindow::AutoFilterMode meMode;
public:
    AutoFilterAction(ScGridWindow* p, ScGridWindow::AutoFilterMode eMode) :
        mpWindow(p), meMode(eMode) {}
    virtual bool execute() override
    {
        mpWindow->UpdateAutoFilterFromMenu(meMode);
        // UpdateAutoFilterFromMenu manually closes the popup so return
        // false to not attempt a second close
        return false;
    }
};

class AutoFilterPopupEndAction : public ScCheckListMenuControl::Action
{
    VclPtr<ScGridWindow> mpWindow;
    ScAddress maPos;
public:
    AutoFilterPopupEndAction(ScGridWindow* p, const ScAddress& rPos) :
        mpWindow(p), maPos(rPos) {}
    virtual bool execute() override
    {
        mpWindow->RefreshAutoFilterButton(maPos);
        mpWindow->GrabFocus();
        return false// this is called after the popup has been closed
    }
};

class AutoFilterSubMenuAction : public AutoFilterAction
{
protected:
    ScListSubMenuControl* m_pSubMenu;

public:
    AutoFilterSubMenuAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode)
        : AutoFilterAction(p, eMode)
        , m_pSubMenu(pSubMenu)
    {
    }
};

class AutoFilterColorAction : public AutoFilterSubMenuAction
{
private:
    Color m_aColor;

public:
    AutoFilterColorAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode, const Color& rColor)
        : AutoFilterSubMenuAction(p, pSubMenu, eMode)
        , m_aColor(rColor)
    {
    }

    virtual bool execute() override
    {
        const AutoFilterData* pData =
            static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());

        if (!pData)
            return false;

        ScDBData* pDBData = pData->mpData;
        if (!pDBData)
            return false;

        const ScAddress& rPos = pData->maPos;

        ScViewData& rViewData = m_pSubMenu->GetViewData();
        ScDocument& rDoc = rViewData.GetDocument();

        ScQueryParam aParam;
        pDBData->GetQueryParam(aParam);

        // Try to use the existing entry for the column (if one exists).
        ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);

        if (!pEntry)
        {
            // Something went terribly wrong!
            return false;
        }

        if (ScTabViewShell::isAnyEditViewInRange(rViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2))
            return false;

        pEntry->bDoQuery = true;
        pEntry->nField = rPos.Col();
        pEntry->eConnect = SC_AND;

        ScFilterEntries aFilterEntries;
        rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);

        bool bActive = false;
        ScQueryEntry::Item& rItem = pEntry->GetQueryItem();
        if (rItem.maColor == m_aColor
            && ((meMode == ScGridWindow::AutoFilterMode::TextColor
                 && rItem.meType == ScQueryEntry::ByTextColor)
                || (meMode == ScGridWindow::AutoFilterMode::BackgroundColor
                    && rItem.meType == ScQueryEntry::ByBackgroundColor)))
        {
            bActive = true;
        }

        // Disable color filter when active color was selected
        if (bActive)
        {
            aParam.RemoveAllEntriesByField(rPos.Col());
            pEntry = nullptr;   // invalidated by RemoveAllEntriesByField call

            // tdf#46184 reset filter options to default values
            aParam.eSearchType = utl::SearchParam::SearchType::Normal;
            aParam.bCaseSens = false;
            aParam.bDuplicate = true;
            aParam.bInplace = true;
        }
        else
        {
            if (meMode == ScGridWindow::AutoFilterMode::TextColor)
                pEntry->SetQueryByTextColor(m_aColor);
            else
                pEntry->SetQueryByBackgroundColor(m_aColor);
        }

        rViewData.GetView()->Query(aParam, nullptr, true);
        pDBData->SetQueryParam(aParam);

        return true;
    }
};

class AutoFilterSortColorAction : public AutoFilterSubMenuAction
{
private:
    Color m_aColor;
    ScViewData& m_rViewData;

public:
    AutoFilterSortColorAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, ScGridWindow::AutoFilterMode eMode, const Color& rColor, ScViewData& rViewData)
        : AutoFilterSubMenuAction(p, pSubMenu, eMode)
        , m_aColor(rColor)
        , m_rViewData(rViewData)
    {
    }

    virtual bool execute() override
    {
        const AutoFilterData* pData =
            static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());

        if (!pData)
            return false;

        ScDBData* pDBData = pData->mpData;
        if (!pDBData)
            return false;

        const ScAddress& rPos = pData->maPos;
        SCCOL nCol = rPos.Col();
        ScSortParam aSortParam;
        pDBData->GetSortParam(aSortParam);
        if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
            // out of bound
            return false;

        bool bHasHeader = pDBData->HasHeader();

        aSortParam.bHasHeader = bHasHeader;
        aSortParam.bByRow = true;
        aSortParam.bCaseSens = false;
        aSortParam.bNaturalSort = false;
        aSortParam.aDataAreaExtras.mbCellNotes = false;
        aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
        aSortParam.aDataAreaExtras.mbCellFormats = true;
        aSortParam.bInplace = true;
        aSortParam.maKeyState[0].bDoSort = true;
        aSortParam.maKeyState[0].nField = nCol;
        aSortParam.maKeyState[0].bAscending = true;
        aSortParam.maKeyState[0].aColorSortMode = meMode == ScGridWindow::AutoFilterMode::TextColor
                                                      ? ScColorSortMode::TextColor
                                                      : ScColorSortMode::BackgroundColor;
        aSortParam.maKeyState[0].aColorSortColor = m_aColor;

        for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
            aSortParam.maKeyState[i].bDoSort = false;

        m_rViewData.GetViewShell()->UISort(aSortParam);

        return true;
    }
};

class AutoFilterColorPopupStartAction : public AutoFilterSubMenuAction
{
private:
    bool mbIsFilter;
public:
    AutoFilterColorPopupStartAction(ScGridWindow* p, ScListSubMenuControl* pSubMenu, bool bIsFilter)
        : AutoFilterSubMenuAction(p, pSubMenu, ScGridWindow::AutoFilterMode::Normal),
        mbIsFilter(bIsFilter)
    {
    }

    virtual bool execute() override
    {
        const AutoFilterData* pData =
            static_cast<const AutoFilterData*>(m_pSubMenu->getExtendedData());

        if (!pData)
            return false;

        ScDBData* pDBData = pData->mpData;
        if (!pDBData)
            return false;

        ScViewData& rViewData = m_pSubMenu->GetViewData();
        ScDocument& rDoc = rViewData.GetDocument();
        const ScAddress& rPos = pData->maPos;

        ScFilterEntries aFilterEntries;
        rDoc.GetFilterEntries(rPos.Col(), rPos.Row(), rPos.Tab(), aFilterEntries);

        m_pSubMenu->clearMenuItems();

        XColorListRef xUserColorList;

        OUString aPaletteName(officecfg::Office::Common::UserColors::PaletteName::get());
        PaletteManager aPaletteManager;
        std::vector<OUString> aPaletteNames = aPaletteManager.GetPaletteList();
        for (size_t i = 0, nLen = aPaletteNames.size(); i < nLen; ++i)
        {
            if (aPaletteName == aPaletteNames[i])
            {
                aPaletteManager.SetPalette(i);
                xUserColorList = XPropertyList::AsColorList(
                                XPropertyList::CreatePropertyListFromURL(
                                XPropertyListType::Color, aPaletteManager.GetSelectedPalettePath()));
                if (!xUserColorList->Load())
                    xUserColorList = nullptr;
                break;
            }
        }

        ScQueryParam aParam;
        pDBData->GetQueryParam(aParam);
        ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);

        int nMenu = 0;
        for (auto eMode : {ScGridWindow::AutoFilterMode::BackgroundColor, ScGridWindow::AutoFilterMode::TextColor})
        {
            std::set<Color> aColors = eMode == ScGridWindow::AutoFilterMode::TextColor
                                          ? aFilterEntries.getTextColors()
                                          : aFilterEntries.getBackgroundColors();

            for (auto& rColor : aColors)
            {
                bool bActive = false;

                if (pEntry)
                {
                    ScQueryEntry::Item& rItem = pEntry->GetQueryItem();
                    if (rItem.maColor == rColor
                        && ((eMode == ScGridWindow::AutoFilterMode::TextColor
                             && rItem.meType == ScQueryEntry::ByTextColor)
                            || (eMode == ScGridWindow::AutoFilterMode::BackgroundColor
                                && rItem.meType == ScQueryEntry::ByBackgroundColor)))
                    {
                        bActive = true;
                    }
                }

                const bool bAutoColor = rColor == COL_AUTO;

                // ColorListBox::ShowPreview is similar
                ScopedVclPtr<VirtualDevice> xDev(m_pSubMenu->create_virtual_device());
                const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
                Size aImageSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());
                xDev->SetOutputSize(aImageSize);
                const tools::Rectangle aRect(Point(0, 0), aImageSize);

                if (bAutoColor)
                {
                    const Color aW(COL_WHITE);
                    const Color aG(0xef, 0xef, 0xef);
                    int nMinDim = std::min(aImageSize.Width(), aImageSize.Height()) + 1;
                    int nCheckSize = nMinDim / 3;
                    xDev->DrawCheckered(aRect.TopLeft(), aRect.GetSize(), std::min(nCheckSize, 8), aW, aG);
                    xDev->SetFillColor();
                }
                else
                    xDev->SetFillColor(rColor);

                xDev->SetLineColor(rStyleSettings.GetDisableColor());
                xDev->DrawRect(aRect);

                if (bAutoColor)
                {
                    OUString sText = eMode == ScGridWindow::AutoFilterMode::TextColor
                                         ? ScResId(SCSTR_FILTER_AUTOMATIC_COLOR)
                                         : ScResId(SCSTR_FILTER_NO_FILL);
                    if (mbIsFilter)
                    {
                        m_pSubMenu->addMenuColorItem(
                            sText, bActive, *xDev, nMenu,
                            new AutoFilterColorAction(mpWindow, m_pSubMenu, eMode, rColor));
                    }
                    else
                    {
                        m_pSubMenu->addMenuColorItem(
                            sText, bActive, *xDev, nMenu,
                            new AutoFilterSortColorAction(mpWindow, m_pSubMenu, eMode, rColor, rViewData));
                    }
                }
                else
                {
                    OUString sName;

                    bool bFoundColorName = false;
                    if (xUserColorList)
                    {
                        sal_Int32 nPos = xUserColorList->GetIndexOfColor(rColor);
                        if (nPos != -1)
                        {
                            XColorEntry* pColorEntry = xUserColorList->GetColor(nPos);
                            sName = pColorEntry->GetName();
                            bFoundColorName = true;
                        }
                    }
                    if (!bFoundColorName)
                        sName = "#" + rColor.AsRGBHexString().toAsciiUpperCase();

                    if (mbIsFilter)
                    {
                        m_pSubMenu->addMenuColorItem(
                            sName, bActive, *xDev, nMenu,
                            new AutoFilterColorAction(mpWindow, m_pSubMenu, eMode, rColor));
                    }
                    else
                    {
                        m_pSubMenu->addMenuColorItem(
                            sName, bActive, *xDev, nMenu,
                            new AutoFilterSortColorAction(mpWindow, m_pSubMenu, eMode, rColor,
                                                          rViewData));
                    }
                }
            }

            ++nMenu;
        }

        m_pSubMenu->resizeToFitMenuItems();

        return false;
    }
};

class AddItemToEntry
{
    ScQueryEntry::QueryItemsType& mrItems;
    svl::SharedStringPool& mrPool;
public:
    AddItemToEntry(ScQueryEntry::QueryItemsType& rItems, svl::SharedStringPool& ;rPool) :
        mrItems(rItems), mrPool(rPool) {}
    void operator() (const ScCheckListMenuControl::ResultEntry& rEntry)
    {
        if (rEntry.bValid)
        {
            ScQueryEntry::Item aNew;
            aNew.maString = mrPool.intern(rEntry.aName);
            // set the filter type to ByValue, if the filter condition is value
            aNew.meType = rEntry.bDate ? ScQueryEntry::ByDate : rEntry.bValue ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
            aNew.mfVal = rEntry.nValue;
            mrItems.push_back(aNew);
        }
    }
};

class AddSelectedItemString
{
    std::unordered_set<OUString>& mrSetString;
    std::unordered_set<double>& mrSetValue;
public:
    explicit AddSelectedItemString(std::unordered_set<OUString>& rString, std::unordered_set<double>& rValue) :
        mrSetString(rString), mrSetValue(rValue) {}

    void operator() (const ScQueryEntry::Item& rItem)
    {
        if( rItem.meType == ScQueryEntry::QueryType::ByValue )
            mrSetValue.insert(rItem.mfVal);
        else
            mrSetString.insert(rItem.maString.getString());
    }
};

void collectUIInformation(const OUString& aRow, const OUString& aCol , const OUString& aevent)
{
    EventDescription aDescription;
    aDescription.aAction = "LAUNCH";
    aDescription.aID = "grid_window";
    aDescription.aParameters = {{aevent, ""},
        {"ROW", aRow}, {"COL", aCol}};
    aDescription.aParent = "MainWindow";
    aDescription.aKeyWord = "ScGridWinUIObject";

    UITestLogger::getInstance().logEvent(aDescription);
}

}

void ScGridWindow::LaunchAutoFilterMenu(SCCOL nCol, SCROW nRow)
{
    SCTAB nTab = mrViewData.GetTabNo();
    ScDocument& rDoc = mrViewData.GetDocument();
    bool bLOKActive = comphelper::LibreOfficeKit::isActive();

    mpAutoFilterPopup.reset();

    // Estimate the width (in pixels) of the longest text in the list
    ScFilterEntries aFilterEntries;
    rDoc.GetFilterEntries(nCol, nRow, nTab, aFilterEntries);

    weld::Window* pPopupParent = GetFrameWeld();
    int nColWidth = ScViewData::ToPixel(rDoc.GetColWidth(nCol, nTab), mrViewData.GetPPTX());
    mpAutoFilterPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData,
                                                       aFilterEntries.mbHasDates, nColWidth));

    int nMaxTextWidth = 0;
    if (aFilterEntries.size() <= 10)
    {
        // do pixel calculation for all elements of short lists
        for (const auto& rEntry : aFilterEntries)
        {
            const OUString& aText = rEntry.GetString();
            nMaxTextWidth = std::max<int>(nMaxTextWidth, mpAutoFilterPopup->GetTextWidth(aText) + aText.getLength() * 2);
        }
    }
    else
    {
        // find the longest string, probably it will be the longest rendered text, too
        // (performance optimization for long lists)
        auto itMax = aFilterEntries.begin();
        for (auto it = itMax; it != aFilterEntries.end(); ++it)
        {
            int nTextWidth = it->GetString().getLength();
            if (nMaxTextWidth < nTextWidth)
            {
                nMaxTextWidth = nTextWidth;
                itMax = it;
            }
        }
        nMaxTextWidth = mpAutoFilterPopup->GetTextWidth(itMax->GetString()) + nMaxTextWidth * 2;
    }

    // window should be at least as wide as the column, or the longest text + checkbox, scrollbar ... (it is estimated with 70 pixel now)
    // window should be maximum 1024 pixel wide.
    int nWindowWidth = std::min<int>(1024, nMaxTextWidth + 70);
    nWindowWidth = mpAutoFilterPopup->IncreaseWindowWidthToFitText(nWindowWidth);
    nMaxTextWidth = std::max<int>(nMaxTextWidth, nWindowWidth - 70);

    mpAutoFilterPopup->setOKAction(new AutoFilterAction(this, AutoFilterMode::Normal));
    mpAutoFilterPopup->setPopupEndAction(
        new AutoFilterPopupEndAction(this, ScAddress(nCol, nRow, nTab)));
    std::unique_ptr<AutoFilterData> pData(new AutoFilterData);
    pData->maPos = ScAddress(nCol, nRow, nTab);

    Point aPos = mrViewData.GetScrPos(nCol, nRow, eWhich);
    tools::Long nSizeX  = 0;
    tools::Long nSizeY  = 0;
    mrViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
    if (bLOKActive)
    {
        // Reverse the zoom factor from aPos and nSize[X|Y]
        // before letting the autofilter window convert the to twips
        // with no zoom information.
        double fZoomX(mrViewData.GetZoomX());
        double fZoomY(mrViewData.GetZoomY());
        aPos.setX(aPos.getX() / fZoomX);
        aPos.setY(aPos.getY() / fZoomY);
        nSizeX = nSizeX / fZoomX;
        nSizeY = nSizeY / fZoomY;
    }
    tools::Rectangle aCellRect(bLOKActive ? aPos : OutputToScreenPixel(aPos), Size(nSizeX, nSizeY));

    ScDBData* pDBData = rDoc.GetDBAtCursor(nCol, nRow, nTab, ScDBDataPortion::AREA);
    if (!pDBData)
        return;

    pData->mpData = pDBData;
    mpAutoFilterPopup->setExtendedData(std::move(pData));

    ScQueryParam aParam;
    pDBData->GetQueryParam(aParam);
    std::vector<ScQueryEntry*> aEntries = aParam.FindAllEntriesByField(nCol);
    std::unordered_set<OUString> aSelectedString;
    std::unordered_set<double> aSelectedValue;
    bool bQueryByNonEmpty = aEntries.size() == 1 && aEntries[0]->IsQueryByNonEmpty();

    if (!bQueryByNonEmpty)
    {
        for (ScQueryEntry* pEntry : aEntries)
        {
            if (pEntry && pEntry->eOp == SC_EQUAL)
            {
                ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
                std::for_each(rItems.begin(), rItems.end(), AddSelectedItemString(aSelectedString, aSelectedValue));
            }
        }
    }

    // Populate the check box list.
    mpAutoFilterPopup->setMemberSize(aFilterEntries.size());
    for (auto it = aFilterEntries.begin(); it != aFilterEntries.end(); ++it)
    {
        // tdf#140745 show (empty) entry on top of the checkbox list if not hidden by filter
        if (it->GetString().isEmpty() && !it->IsHiddenByFilter())
        {
            const OUString& aStringVal = it->GetString();
            const double aDoubleVal = it->GetValue();
            bool bSelected = true;
            if (!aSelectedValue.empty() || !aSelectedString.empty())
                bSelected = aSelectedString.count(aStringVal) > 0;
            else if (bQueryByNonEmpty)
                bSelected = false;
            // it->IsHiddenByFilter() is always false here so no need to evaluate it
            mpAutoFilterPopup->addMember(aStringVal, aDoubleVal, bSelected, false);
            aFilterEntries.maStrData.erase(it);
            break;
        }
    }
    for (const auto& rEntry : aFilterEntries)
    {
        const OUString& aStringVal = rEntry.GetString();
        const double aDoubleVal = rEntry.GetValue();
        const double aRDoubleVal = rEntry.GetRoundedValue();
        bool bSelected = !rEntry.IsHiddenByFilter();

        if (!aSelectedValue.empty() || !aSelectedString.empty())
        {
            if (rEntry.GetStringType() == ScTypedStrData::Value)
            {
                if (aDoubleVal == aRDoubleVal)
                    bSelected = aSelectedValue.count(aDoubleVal) > 0
                                || aSelectedString.count(aStringVal) > 0;
                else
                    bSelected = aSelectedValue.count(aDoubleVal) > 0
                                || aSelectedValue.count(aRDoubleVal) > 0
                                || aSelectedString.count(aStringVal) > 0;
            }
            else
                bSelected = aSelectedString.count(aStringVal) > 0;
        }

        if ( rEntry.IsDate() )
            mpAutoFilterPopup->addDateMember( aStringVal, rEntry.GetValue(), bSelected, rEntry.IsHiddenByFilter());
        else
            mpAutoFilterPopup->addMember( aStringVal, aRDoubleVal, bSelected, rEntry.IsHiddenByFilter(),
                rEntry.GetStringType() == ScTypedStrData::Value );
    }

    // Populate the menu.
    mpAutoFilterPopup->addMenuItem(
        ScResId(STR_MENU_SORT_ASC),
        new AutoFilterAction(this, AutoFilterMode::SortAscending));
    mpAutoFilterPopup->addMenuItem(
        ScResId(STR_MENU_SORT_DESC),
        new AutoFilterAction(this, AutoFilterMode::SortDescending));
    if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_SORT_COLOR), truetrue))
        pSubMenu->setPopupStartAction(new AutoFilterColorPopupStartAction(this, pSubMenu, false));
    mpAutoFilterPopup->addSeparator();
    if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_FILTER_COLOR), truetrue))
        pSubMenu->setPopupStartAction(new AutoFilterColorPopupStartAction(this, pSubMenu, true));
    if (ScListSubMenuControl* pSubMenu = mpAutoFilterPopup->addSubMenuItem(ScResId(SCSTR_FILTER_CONDITION), truefalse))
    {
        pSubMenu->addMenuItem(
            ScResId(SCSTR_FILTER_EMPTY), new AutoFilterAction(this, AutoFilterMode::Empty));
        pSubMenu->addMenuItem(
            ScResId(SCSTR_FILTER_NOTEMPTY), new AutoFilterAction(this, AutoFilterMode::NonEmpty));
        pSubMenu->addMenuItem(
            ScResId(SCSTR_TOP10FILTER), new AutoFilterAction(this, AutoFilterMode::Top10));
        pSubMenu->addMenuItem(
            ScResId(SCSTR_BOTTOM10FILTER), new AutoFilterAction(this, AutoFilterMode::Bottom10));
        pSubMenu->addSeparator();
        pSubMenu->addMenuItem(
            ScResId(SCSTR_STDFILTER), new AutoFilterAction(this, AutoFilterMode::Custom));
        pSubMenu->resizeToFitMenuItems();
    }
    if (aEntries.size())
        mpAutoFilterPopup->addMenuItem(
            ScResId(SCSTR_CLEAR_FILTER), new AutoFilterAction(this, AutoFilterMode::Clear));

    mpAutoFilterPopup->initMembers(nMaxTextWidth + 20); // 20 pixel estimated for the checkbox

    ScCheckListMenuControl::Config aConfig;
    aConfig.mbAllowEmptySet = false;
    aConfig.mbRTL = mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo());
    mpAutoFilterPopup->setConfig(aConfig);
    if (IsMouseCaptured())
        ReleaseMouse();
    mpAutoFilterPopup->launch(pPopupParent, aCellRect);

    // remember filter rules before modification
    mpAutoFilterPopup->getResult(aSaveAutoFilterResult);

    collectUIInformation(OUString::number(nRow), OUString::number(nCol),u"AUTOFILTER"_ustr);
}

void ScGridWindow::RefreshAutoFilterButton(const ScAddress& rPos)
{
    if (mpFilterButton)
    {
        bool bFilterActive = IsAutoFilterActive(rPos.Col(), rPos.Row(), rPos.Tab());
        mpFilterButton->setHasHiddenMember(bFilterActive);
        mpFilterButton->setPopupPressed(false);
        mpFilterButton->draw();
    }
}

void ScGridWindow::UpdateAutoFilterFromMenu(AutoFilterMode eMode)
{
    // Terminate autofilter popup now when there is no further user input needed
    bool bColorMode = eMode == AutoFilterMode::TextColor || eMode == AutoFilterMode::BackgroundColor;
    if (!bColorMode)
        mpAutoFilterPopup->terminateAllPopupMenus();

    const AutoFilterData* pData =
        static_cast<const AutoFilterData*>(mpAutoFilterPopup->getExtendedData());

    if (!pData)
        return;

    const ScAddress& rPos = pData->maPos;
    ScDBData* pDBData = pData->mpData;
    if (!pDBData)
        return;

    ScDocument& rDoc = mrViewData.GetDocument();
    svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
    switch (eMode)
    {
        case AutoFilterMode::SortAscending:
        case AutoFilterMode::SortDescending:
        {
            SCCOL nCol = rPos.Col();
            ScSortParam aSortParam;
            pDBData->GetSortParam(aSortParam);
            if (nCol < aSortParam.nCol1 || nCol > aSortParam.nCol2)
                // out of bound
                return;

            bool bHasHeader = pDBData->HasHeader();

            aSortParam.bHasHeader = bHasHeader;
            aSortParam.bByRow = true;
            aSortParam.bCaseSens = false;
            aSortParam.bNaturalSort = false;
            aSortParam.aDataAreaExtras.mbCellNotes = false;
            aSortParam.aDataAreaExtras.mbCellDrawObjects = true;
            aSortParam.aDataAreaExtras.mbCellFormats = true;
            aSortParam.bInplace = true;
            aSortParam.maKeyState[0].bDoSort = true;
            aSortParam.maKeyState[0].nField = nCol;
            aSortParam.maKeyState[0].bAscending = (eMode == AutoFilterMode::SortAscending);
            aSortParam.maKeyState[0].aColorSortMode = ScColorSortMode::None;

            for (size_t i = 1; i < aSortParam.GetSortKeyCount(); ++i)
                aSortParam.maKeyState[i].bDoSort = false;

            mrViewData.GetViewShell()->UISort(aSortParam);
            return;
        }
        case AutoFilterMode::Custom:
        {
            ScRange aRange;
            pDBData->GetArea(aRange);
            mrViewData.GetView()->MarkRange(aRange);
            mrViewData.GetView()->SetCursor(rPos.Col(), rPos.Row());
            mrViewData.GetDispatcher().Execute(SID_FILTER, SfxCallMode::SLOT | SfxCallMode::RECORD);
            return;
        }
        default:
            ;
    }

    ScQueryParam aParam;
    pDBData->GetQueryParam(aParam);

    if (eMode == AutoFilterMode::Normal)
    {
        // Do not recreate autofilter rules if there are no changes from the user
        ScCheckListMenuControl::ResultType aResult;
        mpAutoFilterPopup->getResult(aResult);

        if (aResult == aSaveAutoFilterResult)
        {
            SAL_INFO("sc.ui""Apply autofilter to data when entries are the same");

            if (!mpAutoFilterPopup->isAllSelected())
            {
                // Apply autofilter to data
                ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);
                pEntry->bDoQuery = true;
                pEntry->nField = rPos.Col();
                pEntry->eConnect = SC_AND;
                pEntry->eOp = SC_EQUAL;
                mrViewData.GetView()->Query(aParam, nullptr, true);
            }

            return;
        }
    }

    // Remove old entries in auto-filter rules
    if (!bColorMode)
    {
        aParam.RemoveAllEntriesByField(rPos.Col());

        // tdf#46184 reset filter options to default values
        aParam.eSearchType = utl::SearchParam::SearchType::Normal;
        aParam.bCaseSens = false;
        aParam.bDuplicate = true;
        aParam.bInplace = true;
    }

    if (eMode != AutoFilterMode::Clear
        && !(eMode == AutoFilterMode::Normal && mpAutoFilterPopup->isAllSelected()))
    {
        // Try to use the existing entry for the column (if one exists).
        ScQueryEntry* pEntry = aParam.FindEntryByField(rPos.Col(), true);

        if (!pEntry)
            // Something went terribly wrong!
            return;

        if (ScTabViewShell::isAnyEditViewInRange(mrViewData.GetViewShell(), /*bColumns*/ false, aParam.nRow1, aParam.nRow2))
            return;

        pEntry->bDoQuery = true;
        pEntry->nField = rPos.Col();
        pEntry->eConnect = SC_AND;

        switch (eMode)
        {
            case AutoFilterMode::Normal:
            {
                pEntry->eOp = SC_EQUAL;

                ScCheckListMenuControl::ResultType aResult;
                mpAutoFilterPopup->getResult(aResult);

                ScQueryEntry::QueryItemsType& rItems = pEntry->GetQueryItems();
                rItems.clear();
                std::for_each(aResult.begin(), aResult.end(), AddItemToEntry(rItems, rPool));
            }
            break;
            case AutoFilterMode::Top10:
                pEntry->eOp = SC_TOPVAL;
                pEntry->GetQueryItem().meType = ScQueryEntry::ByString;
                pEntry->GetQueryItem().maString = rPool.intern(u"10"_ustr);
            break;
            case AutoFilterMode::Bottom10:
                pEntry->eOp = SC_BOTVAL;
                pEntry->GetQueryItem().meType = ScQueryEntry::ByString;
                pEntry->GetQueryItem().maString = rPool.intern(u"10"_ustr);
            break;
            case AutoFilterMode::Empty:
                pEntry->SetQueryByEmpty();
            break;
            case AutoFilterMode::NonEmpty:
                pEntry->SetQueryByNonEmpty();
            break;
            case AutoFilterMode::TextColor:
            case AutoFilterMode::BackgroundColor:
                assert(false && "should be handled by AutoFilterColorAction::execute");
            break;
            break;
            default:
                // We don't know how to handle this!
                return;
        }
    }

    mrViewData.GetView()->Query(aParam, nullptr, true);
    pDBData->SetQueryParam(aParam);
}

namespace {

void getCellGeometry(Point& rScrPos, Size& rScrSize, const ScViewData& rViewData, SCCOL nCol, SCROW nRow, ScSplitPos eWhich)
{
    // Get the screen position of the cell.
    rScrPos = rViewData.GetScrPos(nCol, nRow, eWhich);

    // Get the screen size of the cell.
    tools::Long nSizeX, nSizeY;
    rViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY);
    rScrSize = Size(nSizeX-1, nSizeY-1);
}

}

void ScGridWindow::LaunchPageFieldMenu( SCCOL nCol, SCROW nRow )
{
    if (nCol == 0)
        // We assume that the page field button is located in cell to the immediate left.
        return;

    SCTAB nTab = mrViewData.GetTabNo();
    ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
    if (!pDPObj)
        return;

    Point aScrPos;
    Size aScrSize;
    getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
    bool bLOK = comphelper::LibreOfficeKit::isActive();
    DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol-1, nRow, nTab), pDPObj);
}

void ScGridWindow::LaunchDPFieldMenu( SCCOL nCol, SCROW nRow )
{
    SCTAB nTab = mrViewData.GetTabNo();
    ScDPObject* pDPObj = mrViewData.GetDocument().GetDPAtCursor(nCol, nRow, nTab);
    if (!pDPObj)
        return;

    Point aScrPos;
    Size aScrSize;
    getCellGeometry(aScrPos, aScrSize, mrViewData, nCol, nRow, eWhich);
    bool bLOK = comphelper::LibreOfficeKit::isActive();
    DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, ScAddress(nCol, nRow, nTab), pDPObj);
}

void ScGridWindow::ShowFilterMenu(weld::Window* pParent, const tools::Rectangle& rCellRect, bool bLayoutRTL)
{
    auto nSizeX = rCellRect.GetWidth();

    // minimum width in pixel
    if (comphelper::LibreOfficeKit::isActive())
    {
        const tools::Long nMinLOKWinWidth = o3tl::convert(STD_COL_WIDTH * 13 / 10, o3tl::Length::twip, o3tl::Length::px);
        if (nSizeX < nMinLOKWinWidth)
            nSizeX = nMinLOKWinWidth;
    }

    weld::TreeView& rFilterBox = mpFilterBox->get_widget();
    int nEntryCount = rFilterBox.n_children();
    if (nEntryCount > SC_FILTERLISTBOX_LINES)
        nEntryCount = SC_FILTERLISTBOX_LINES;
    auto nHeight = rFilterBox.get_height_rows(nEntryCount);
    rFilterBox.set_size_request(-1, nHeight);
    Size aSize(rFilterBox.get_preferred_size());
    auto nMaxToExpandTo = std::min(nSizeX, static_cast<decltype(nSizeX)>(300));     // do not over do it (Pixel)
    if (aSize.Width() < nMaxToExpandTo)
        aSize.setWidth(nMaxToExpandTo);

    aSize.AdjustWidth(4); // add a little margin
    nSizeX += 4;
    aSize.AdjustHeight(4);

    tools::Rectangle aCellRect(rCellRect);
    aCellRect.AdjustLeft(-2); // offset the little margin above

    if (!bLayoutRTL && aSize.Width() > nSizeX)
    {
        //  move popup position
        tools::Long nDiff = aSize.Width() - nSizeX;
        tools::Long nNewX = aCellRect.Left() - nDiff;
        if ( nNewX < 0 )
            nNewX = 0;
        aCellRect.SetLeft( nNewX );
    }

    rFilterBox.set_size_request(aSize.Width(), aSize.Height());

    if (IsMouseCaptured())
        ReleaseMouse();
    mpFilterBox->popup_at_rect(pParent, aCellRect);
}

void ScGridWindow::DoScenarioMenu( const ScRange& rScenRange )
{
    bool bMenuAtTop = true;

    ScDocument& rDoc = mrViewData.GetDocument();
    mpFilterBox.reset();

    SCCOL nCol = rScenRange.aEnd.Col();     // Cell is below the Buttons
    SCROW nRow = rScenRange.aStart.Row();
    if (nRow == 0)
    {
        nRow = rScenRange.aEnd.Row() + 1;   // Range at very the top -> Button below
        if (nRow>rDoc.MaxRow()) nRow = rDoc.MaxRow();
        bMenuAtTop = false;
    }

    SCTAB nTab = mrViewData.GetTabNo();
    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );

    tools::Long nSizeX  = 0;
    tools::Long nSizeY  = 0;
    mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
    // The button height should not use the merged cell height, should still use single row height
    nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY());
    Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
    if ( bLayoutRTL )
        aPos.AdjustX( -nSizeX );
    tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));
    aCellRect.AdjustTop( -nSizeY );
    aCellRect.AdjustBottom( -(nSizeY - 1) );
    if (!bMenuAtTop)
    {
        Size aButSize = mrViewData.GetScenButSize();
        aCellRect.AdjustBottom(aButSize.Height());
    }

    //  Place the ListBox directly below the black line of the cell grid
    //  (It looks odd if the line gets hidden...)

    weld::Window* pParent = weld::GetPopupParent(*this, aCellRect);
    mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::Scenario);
    mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
    weld::TreeView& rFilterBox = mpFilterBox->get_widget();
    rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR

    //  Listbox fill
    rFilterBox.freeze();
    OUString aCurrent;
    OUString aTabName;
    SCTAB nTabCount = rDoc.GetTableCount();
    for (SCTAB i=nTab+1; i<nTabCount && rDoc.IsScenario(i); i++)
    {
        if (rDoc.HasScenarioRange( i, rScenRange ))
            if (rDoc.GetName( i, aTabName ))
            {
                rFilterBox.append_text(aTabName);
                if (rDoc.IsActiveScenario(i))
                    aCurrent = aTabName;
            }
    }
    rFilterBox.thaw();

    ShowFilterMenu(pParent, aCellRect, bLayoutRTL);

    rFilterBox.grab_focus();

    sal_Int32 nPos = -1;
    if (!aCurrent.isEmpty())
    {
        nPos = rFilterBox.find_text(aCurrent);
    }
    if (nPos == -1 && rFilterBox.n_children() > 0 )
    {
        nPos = 0;
    }
    if (nPos != -1)
    {
        rFilterBox.set_cursor(nPos);
        rFilterBox.select(nPos);
    }
    mpFilterBox->EndInit();
}

void ScGridWindow::LaunchDataSelectMenu(const SCCOL nCol, const SCROW nRow)
{
    mpFilterBox.reset();

    ScDocument& rDoc = mrViewData.GetDocument();
    const SCTAB nTab = mrViewData.GetTabNo();
    bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );

    tools::Long nSizeX  = 0;
    tools::Long nSizeY  = 0;
    mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY );
    Point aPos = mrViewData.GetScrPos( nCol, nRow, eWhich );
    bool bLOKActive = comphelper::LibreOfficeKit::isActive();

    if (bLOKActive)
    {
        // aPos is now view-zoom adjusted and in pixels an more importantly this is pixel aligned to the view-zoom,
        // but once we use this to set the position of the floating window, it has no information of view-zoom level
        // so if we don't reverse the zoom now, a simple PixelToLogic(aPos, MapMode(MapUnit::MapTwip)) employed in
        // FloatingWindow::ImplCalcPos will produce a 'scaled' twips position which will again get zoom scaled in the
        // client (effective double scaling) causing wrong positioning/size.
        double fZoomX(mrViewData.GetZoomX());
        double fZoomY(mrViewData.GetZoomY());
        aPos.setX(aPos.getX() / fZoomX);
        aPos.setY(aPos.getY() / fZoomY);
        nSizeX = nSizeX / fZoomX;
        nSizeY = nSizeY / fZoomY;
    }

    if ( bLayoutRTL )
        aPos.AdjustX( -nSizeX );
    tools::Rectangle aCellRect(aPos, Size(nSizeX, nSizeY));

    weld::Window* pParent = comphelper::LibreOfficeKit::isActive() ? GetFrameWeld() : weld::GetPopupParent(*this, aCellRect);
    mpFilterBox = std::make_shared<ScFilterListBox>(pParent, this, nCol, nRow, ScFilterBoxMode::DataSelect);
    mpFilterBox->connect_closed(LINK(this, ScGridWindow, PopupModeEndHdl));
    weld::TreeView& rFilterBox = mpFilterBox->get_widget();
    rFilterBox.set_direction(bLayoutRTL); // Fix for bug fdo#44925 use sheet direction for widget RTL/LTR

    // SetSize later

    const sal_uInt32 nIndex = rDoc.GetAttr(nCol, nRow, nTab, ATTR_VALIDDATA)->GetValue();
    const ScValidationData* pData = nIndex ? rDoc.GetValidationEntry(nIndex) : nullptr;

    bool bEmpty = false;
    std::vector<ScTypedStrData> aStrings; // case sensitive
    // Fill List
    rDoc.GetDataEntries(nCol, nRow, nTab, aStrings, true /* bValidation */);

    // IsIgnoreBlank allows blank values. Don't add empty string unless "Allow Empty Cells"
    if (pData && !pData->IsIgnoreBlank())
    {
        auto lambda = [](const ScTypedStrData& rStr) { return rStr.GetString().isEmpty(); };
        std::erase_if(aStrings, lambda);
    }

    if (aStrings.empty())
        bEmpty = true;

    if (!bEmpty)
    {
        rFilterBox.freeze();

        // Fill Listbox
        bool bWait = aStrings.size() > 100;

        if (bWait)
            EnterWait();

        for (const auto& rString : aStrings)
        {
            const OUString& rFilterString = rString.GetString();
            rFilterBox.append_text(rFilterString);
        }

        if (bWait)
            LeaveWait();

        rFilterBox.thaw();

        ShowFilterMenu(pParent, aCellRect, bLayoutRTL);
    }

    sal_Int32 nSelPos = -1;

    if ( nIndex )
    {
        if (pData)
        {
            std::unique_ptr<ScTypedStrData> pNew;
            OUString aDocStr = rDoc.GetString(nCol, nRow, nTab);
            if ( rDoc.HasValueData( nCol, nRow, nTab ) )
            {
                double fVal = rDoc.GetValue(ScAddress(nCol, nRow, nTab));
                pNew.reset(new ScTypedStrData(aDocStr, fVal, fVal, ScTypedStrData::Value));
            }
            else
                pNew.reset(new ScTypedStrData(aDocStr, 0.0, 0.0, ScTypedStrData::Standard));

            if (pData->GetListType() == css::sheet::TableValidationVisibility::SORTEDASCENDING)
            {
                auto it = std::lower_bound(aStrings.begin(), aStrings.end(), *pNew, ScTypedStrData::LessCaseSensitive());
                if (it != aStrings.end() && ScTypedStrData::EqualCaseSensitive()(*it, *pNew))
                    nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
            }
            else
            {
                auto it = std::find_if(aStrings.begin(), aStrings.end(), FindTypedStrData(*pNew, true));
                if (it != aStrings.end())
                    nSelPos = static_cast<sal_Int32>(std::distance(aStrings.begin(), it));
            }
        }
    }

    // Do not show an empty selection List:

    if ( bEmpty )
    {
        mpFilterBox.reset();
    }
    else
    {
        rFilterBox.grab_focus();

        if (rFilterBox.n_children())
        {
            if (nSelPos != -1)
                rFilterBox.set_cursor(nSelPos);
            else
                rFilterBox.set_cursor(0);
        }
        // Select only after GrabFocus, so that the focus rectangle gets correct
        if (nSelPos != -1)
            rFilterBox.select(nSelPos);
        else
            rFilterBox.unselect_all();

        mpFilterBox->EndInit();
    }
    collectUIInformation(OUString::number(nRow), OUString::number(nCol),u"SELECTMENU"_ustr);
}

void ScGridWindow::FilterSelect( sal_uLong nSel )
{
    weld::TreeView& rFilterBox = mpFilterBox->get_widget();
    OUString aString = rFilterBox.get_text(static_cast<sal_Int32>(nSel));

    SCCOL nCol = mpFilterBox->GetCol();
    SCROW nRow = mpFilterBox->GetRow();
    switch (mpFilterBox->GetMode())
    {
        case ScFilterBoxMode::DataSelect:
            ExecDataSelect(nCol, nRow, aString);
            break;
        case ScFilterBoxMode::Scenario:
            mrViewData.GetView()->UseScenario(aString);
            break;
    }

    // coverity[check_after_deref] - could be destroyed by ExecDataSelect
    if (mpFilterBox)
        mpFilterBox->popdown();

    GrabFocus();        // Otherwise the focus would be wrong on OS/2
}

void ScGridWindow::ExecDataSelect( SCCOL nCol, SCROW nRow, const OUString& rStr )
{
    ScInputHandler* pViewHdl = ScModule::get()->GetInputHdl(mrViewData.GetViewShell());
    if (pViewHdl && mrViewData.HasEditView(mrViewData.GetActivePart()))
        pViewHdl->CancelHandler();

    SCTAB nTab = mrViewData.GetTabNo();
    ScViewFunc* pView = mrViewData.GetView();
    pView->EnterData( nCol, nRow, nTab, rStr );

    // #i52307# CellContentChanged is not in EnterData so it isn't called twice
    // if the cursor is moved afterwards.
    pView->CellContentChanged();
}

void ScGridWindow::MoveMouseStatus( ScGridWindow& rDestWin )
{
    if (nButtonDown)
    {
        rDestWin.nButtonDown = nButtonDown;
        rDestWin.nMouseStatus = nMouseStatus;
    }

    if (bRFMouse)
    {
        rDestWin.bRFMouse = bRFMouse;
        rDestWin.bRFSize  = bRFSize;
        rDestWin.nRFIndex = nRFIndex;
        rDestWin.nRFAddX  = nRFAddX;
        rDestWin.nRFAddY  = nRFAddY;
        bRFMouse = false;
    }

    if (nPagebreakMouse)
    {
        rDestWin.nPagebreakMouse  = nPagebreakMouse;
        rDestWin.nPagebreakBreak  = nPagebreakBreak;
        rDestWin.nPagebreakPrev   = nPagebreakPrev;
        rDestWin.aPagebreakSource = aPagebreakSource;
        rDestWin.aPagebreakDrag   = aPagebreakDrag;
        nPagebreakMouse = SC_PD_NONE;
    }
}

bool ScGridWindow::TestMouse( const MouseEvent& rMEvt, bool bAction )
{
    //  MouseEvent buttons must only be checked if bAction==TRUE
    //  to allow changing the mouse pointer in MouseMove,
    //  but not start AutoFill with right button (#74229#).
    //  with bAction==sal_True, SetFillMode / SetDragMode is called

    if ( bAction && !rMEvt.IsLeft() )
        return false;

    bool bNewPointer = false;

    SfxInPlaceClient* pClient = mrViewData.GetViewShell()->GetIPClient();
    bool bOleActive = ( pClient && pClient->IsObjectInPlaceActive() );

    if ( mrViewData.IsActive() && !bOleActive && !mrViewData.GetViewShell()->IsLokReadOnlyView())
    {
        ScDocument& rDoc = mrViewData.GetDocument();
        SCTAB nTab = mrViewData.GetTabNo();
        bool bLayoutRTL = rDoc.IsLayoutRTL( nTab );

        //  Auto-Fill

        ScRange aMarkRange;
        if (mrViewData.GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE)
        {
            if (aMarkRange.aStart.Tab() == mrViewData.GetTabNo() && mpAutoFillRect)
            {
                Point aMousePos = rMEvt.GetPosPixel();
                if (mpAutoFillRect->Contains(aMousePos))
                {
                    SetPointer( PointerStyle::Cross );     //! bold cross ?
                    if (bAction)
                    {
                        SCCOL nX = aMarkRange.aEnd.Col();
                        SCROW nY = aMarkRange.aEnd.Row();

                        if ( lcl_IsEditableMatrix( mrViewData.GetDocument(), aMarkRange ) )
                            mrViewData.SetDragMode(
                                aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY, ScFillMode::MATRIX );
                        else
                            mrViewData.SetFillMode(
                                aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nX, nY );

                        //  The simple selection must also be recognized when dragging,
                        //  where the Marking flag is set and MarkToSimple won't work anymore.
                        mrViewData.GetMarkData().MarkToSimple();
                    }
                    bNewPointer = true;
                }
            }
        }

        //  Embedded rectangle

        if (rDoc.IsEmbedded())
        {
            ScRange aRange;
            rDoc.GetEmbedded( aRange );
            if ( mrViewData.GetTabNo() == aRange.aStart.Tab() )
            {
                Point aStartPos = mrViewData.GetScrPos( aRange.aStart.Col(), aRange.aStart.Row(), eWhich );
                Point aEndPos   = mrViewData.GetScrPos( aRange.aEnd.Col()+1, aRange.aEnd.Row()+1, eWhich );
                Point aMousePos = rMEvt.GetPosPixel();
                if ( bLayoutRTL )
                {
                    aStartPos.AdjustX(2 );
                    aEndPos.AdjustX(2 );
                }
                bool bTop = ( aMousePos.X() >= aStartPos.X()-3 && aMousePos.X() <= aStartPos.X()+1 &&
                              aMousePos.Y() >= aStartPos.Y()-3 && aMousePos.Y() <= aStartPos.Y()+1 );
                bool bBottom = ( aMousePos.X() >= aEndPos.X()-3 && aMousePos.X() <= aEndPos.X()+1 &&
                                 aMousePos.Y() >= aEndPos.Y()-3 && aMousePos.Y() <= aEndPos.Y()+1 );
                if ( bTop || bBottom )
                {
                    SetPointer( PointerStyle::Cross );
                    if (bAction)
                    {
                        ScFillMode nMode = bTop ? ScFillMode::EMBED_LT : ScFillMode::EMBED_RB;
                        mrViewData.SetDragMode(
                                    aRange.aStart.Col(), aRange.aStart.Row(),
                                    aRange.aEnd.Col(), aRange.aEnd.Row(), nMode );
                    }
                    bNewPointer = true;
                }
            }
        }
    }

    if (!bNewPointer && bAction)
    {
        mrViewData.ResetFillMode();
    }

    return bNewPointer;
}

void ScGridWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
    if (SfxLokHelper::getDeviceFormFactor() == LOKDeviceFormFactor::MOBILE)
    {
        ScViewFunc* pView = mrViewData.GetView();
        ScTabViewShell* pViewShell = mrViewData.GetViewShell();
        bool bRefMode = pViewShell && pViewShell->IsRefInputMode();

        Point aPos(rMEvt.GetPosPixel());
        SCCOL nPosX;
        SCROW nPosY;
        mrViewData.GetPosFromPixel(aPos.X(), aPos.Y(), eWhich, nPosX, nPosY);

        if (bRefMode && pView->GetFunctionSet().CheckRefBounds(nPosX, nPosY))
            return;
    }

    nNestedButtonState = ScNestedButtonState::Down;

    MouseEventState aState;
    HandleMouseButtonDown(rMEvt, aState);
    if (aState.mbActivatePart)
        mrViewData.GetView()->ActivatePart(eWhich);

    if ( nNestedButtonState == ScNestedButtonState::Up )
    {
        // #i41690# If an object is deactivated from MouseButtonDown, it might reschedule,
        // so MouseButtonUp comes before the MouseButtonDown call is finished. In this case,
        // simulate another MouseButtonUp call, so the selection state is consistent.

        nButtonDown = rMEvt.GetButtons();
        FakeButtonUp();

        if ( IsTracking() )
            EndTracking();      // normally done in VCL as part of MouseButtonUp handling
    }
    nNestedButtonState = ScNestedButtonState::NONE;
}

void ScGridWindow::HandleMouseButtonDown( const MouseEvent& rMEvt, MouseEventState& rState )
{
    // We have to check if a context menu is shown and we have an UI
    // active inplace client. In that case we have to ignore the event.
    // Otherwise we would crash (context menu has been
    // opened by inplace client and we would deactivate the inplace client,
    // the context menu is closed by VCL asynchronously which in the end
    // would work on deleted objects or the context menu has no parent anymore)
    SfxViewShell* pViewSh = mrViewData.GetViewShell();
    SfxInPlaceClient* pClient = pViewSh->GetIPClient();
    if ( pClient &&
         pClient->IsObjectInPlaceActive() &&
         vcl::IsInPopupMenuExecute() )
        return;

    aCurMousePos = rMEvt.GetPosPixel();

    // Filter popup is ended with its own mouse click, not when clicking into the Grid Window,
    // so the following query is no longer necessary:
    ClickExtern();  // deletes FilterBox when available

    HideNoteOverlay();

    bEEMouse = false;

    ScModule* pScMod = ScModule::get();
    if (pScMod->IsModalMode(&mrViewData.GetSfxDocShell()))
        return;

    const bool bWasMouseCaptured = IsMouseCaptured();
    SAL_WARN_IF(bWasMouseCaptured, "sc.ui""Is there a scenario where the mouse is captured before mouse down?");

    pScActiveViewShell = mrViewData.GetViewShell();         // if left is clicked
--> --------------------

--> maximum size reached

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

Messung V0.5
C=95 H=93 G=93

¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ¤

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