tdf#144786 Implement XML_hiddenButton functionality

Now hides autofilter button when there's an XML_hiddenButton=true
or a XML_showButton=false attribute

Change-Id: I911ef23fb5e4feff8c7de0ec154bff871a29f2e8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/143300
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
diff --git a/sc/qa/unit/data/xlsx/hiddenButton.xlsx b/sc/qa/unit/data/xlsx/hiddenButton.xlsx
new file mode 100644
index 0000000..20019f4
--- /dev/null
+++ b/sc/qa/unit/data/xlsx/hiddenButton.xlsx
Binary files differ
diff --git a/sc/qa/unit/subsequent_export_test2.cxx b/sc/qa/unit/subsequent_export_test2.cxx
index 6284011..77b033d 100644
--- a/sc/qa/unit/subsequent_export_test2.cxx
+++ b/sc/qa/unit/subsequent_export_test2.cxx
@@ -189,6 +189,7 @@ public:
    void testTdf148820();
    void testEmbeddedTextInDecimal();
    void testTotalsRowFunction();
    void testAutofilterHiddenButton();

    CPPUNIT_TEST_SUITE(ScExportTest2);

@@ -315,6 +316,7 @@ public:
    CPPUNIT_TEST(testTdf148820);
    CPPUNIT_TEST(testEmbeddedTextInDecimal);
    CPPUNIT_TEST(testTotalsRowFunction);
    CPPUNIT_TEST(testAutofilterHiddenButton);

    CPPUNIT_TEST_SUITE_END();
};
@@ -2849,6 +2851,19 @@ void ScExportTest2::testTotalsRowFunction()
    }
}

void ScExportTest2::testAutofilterHiddenButton()
{
    createScDoc("xlsx/hiddenButton.xlsx");
    saveAndReload("Calc Office Open XML");
    xmlDocUniquePtr pDocXml = parseExport("xl/tables/table1.xml");
    CPPUNIT_ASSERT(pDocXml);
    for (int i = 1; i <= 5; i++)
    {
        auto sPath = "/x:table/x:autoFilter/x:filterColumn[" + std::to_string(i) + "]";
        assertXPath(pDocXml, sPath.c_str(), "hiddenButton", "1");
    }
}

CPPUNIT_TEST_SUITE_REGISTRATION(ScExportTest2);

CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/source/filter/excel/excrecds.cxx b/sc/source/filter/excel/excrecds.cxx
index b175445..3feac86 100644
--- a/sc/source/filter/excel/excrecds.cxx
+++ b/sc/source/filter/excel/excrecds.cxx
@@ -47,6 +47,8 @@

#include <xcl97rec.hxx>
#include <tabprotection.hxx>
#include <scitems.hxx>
#include <attrib.hxx>

using namespace ::oox;

@@ -603,11 +605,12 @@ void ExcFilterCondition::SaveText( XclExpStream& rStrm )
    }
}

XclExpAutofilter::XclExpAutofilter( const XclExpRoot& rRoot, sal_uInt16 nC ) :
XclExpAutofilter::XclExpAutofilter( const XclExpRoot& rRoot, sal_uInt16 nC, bool bIsEmpty ) :
    XclExpRecord( EXC_ID_AUTOFILTER, 24 ),
    XclExpRoot( rRoot ),
    meType(FilterCondition),
    meType(bIsEmpty ? Empty : FilterCondition),
    nCol( nC ),
    bIsButtonHidden( false ),
    nFlags( 0 ),
    bHasBlankValue( false )
{
@@ -818,10 +821,13 @@ void XclExpAutofilter::SaveXml( XclExpXmlStream& rStrm )

    sax_fastparser::FSHelperPtr& rWorksheet = rStrm.GetCurrentStream();

    std::optional<OString> sHiddenButtonValue;
    if (bIsButtonHidden)
        sHiddenButtonValue = "1";

    rWorksheet->startElement( XML_filterColumn,
            XML_colId, OString::number(nCol)
            // OOXTODO: XML_hiddenButton,   AutoFilter12 fHideArrow?
            // OOXTODO: XML_showButton
            XML_colId, OString::number(nCol),
            XML_hiddenButton, sHiddenButtonValue
    );

    switch (meType)
@@ -911,6 +917,8 @@ void XclExpAutofilter::SaveXml( XclExpXmlStream& rStrm )
            rWorksheet->endElement(XML_filters);
        }
        break;
        // Used for constructing an empty filterColumn element for exporting the XML_hiddenButton attribute
        case Empty: break;
    }
    rWorksheet->endElement( XML_filterColumn );
}
@@ -971,6 +979,8 @@ ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const
        bool    bContLoop   = true;
        bool        bHasOr      = false;
        SCCOLROW nFirstField = aParam.GetEntry( 0 ).nField;
        ScDocument& rDoc = rRoot.GetDoc();
        SCROW nRow = aRange.aStart.Row();

        // create AUTOFILTER records for filtered columns
        for( SCSIZE nEntry = 0; !bConflict && bContLoop && (nEntry < aParam.GetEntryCount()); nEntry++ )
@@ -980,7 +990,11 @@ ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const
            bContLoop = rEntry.bDoQuery;
            if( bContLoop )
            {
                XclExpAutofilter* pFilter = GetByCol( static_cast<SCCOL>(rEntry.nField) - aRange.aStart.Col() );
                SCCOL nCol = static_cast<SCCOL>( rEntry.nField ) - aRange.aStart.Col();
                auto nFlag = rDoc.GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->GetValue();
                bool bIsButtonHidden = !( nFlag & ScMF::Auto );
                XclExpAutofilter* pFilter = GetByCol( nCol );
                pFilter->SetButtonHidden( bIsButtonHidden );

                if( nEntry > 0 )
                    bHasOr |= (rEntry.eConnect == SC_OR);
@@ -994,6 +1008,34 @@ ExcAutoFilterRecs::ExcAutoFilterRecs( const XclExpRoot& rRoot, SCTAB nTab, const
            }
        }

        sal_uInt16 nColId = 0;
        for ( auto nCol = aRange.aStart.Col(); nCol <= aRange.aEnd.Col(); nCol++, nColId++ )
        {
            auto nFlag = rDoc.GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->GetValue();
            bool bIsButtonHidden = !( nFlag & ScMF::Auto );
            if ( bIsButtonHidden )
            {
                // Create filter column with hiddenButton=1 attribute if it doesn't exist
                XclExpAutofilterRef xFilter;
                bool bFilterFound = false;
                for( size_t nPos = 0, nSize = maFilterList.GetSize(); nPos < nSize; ++nPos )
                {
                    xFilter = maFilterList.GetRecord( nPos );
                    if( xFilter->GetCol() == static_cast<sal_uInt16>(nCol) )
                    {
                        bFilterFound = true;
                        break;
                    }
                }
                if ( !bFilterFound )
                {
                    xFilter = new XclExpAutofilter( GetRoot(), nColId, /*bIsEmpty*/true );
                    xFilter->SetButtonHidden( true );
                    maFilterList.AppendRecord( xFilter );
                }
            }
        }

        // additional tests for conflicts
        for( size_t nPos = 0, nSize = maFilterList.GetSize(); !bConflict && (nPos < nSize); ++nPos )
        {
diff --git a/sc/source/filter/inc/autofilterbuffer.hxx b/sc/source/filter/inc/autofilterbuffer.hxx
index 6721c18..fad4de5 100644
--- a/sc/source/filter/inc/autofilterbuffer.hxx
+++ b/sc/source/filter/inc/autofilterbuffer.hxx
@@ -197,6 +197,7 @@ public:
    /** Returns converted UNO API filter settings representing all filter
        settings of this column. */
    ApiFilterSettings   finalizeImport();
    bool                isButtonHidden();

private:
    std::shared_ptr< FilterSettingsBase >
diff --git a/sc/source/filter/inc/excrecds.hxx b/sc/source/filter/inc/excrecds.hxx
index 2e4885a..c7ab0aa9 100644
--- a/sc/source/filter/inc/excrecds.hxx
+++ b/sc/source/filter/inc/excrecds.hxx
@@ -361,6 +361,7 @@ class XclExpAutofilter : public XclExpRecord, protected XclExpRoot
private:
    enum FilterType
    {
        Empty,
        FilterCondition,
        MultiValue,
        BlankValue,
@@ -368,6 +369,7 @@ private:
    };
    FilterType              meType;
    sal_uInt16              nCol;
    bool                    bIsButtonHidden;
    sal_uInt16              nFlags;
    bool                    bHasBlankValue;
    ExcFilterCondition      aCond[ 2 ];
@@ -380,7 +382,7 @@ private:
    virtual void            WriteBody( XclExpStream& rStrm ) override;

public:
                            XclExpAutofilter( const XclExpRoot& rRoot, sal_uInt16 nC );
                            XclExpAutofilter( const XclExpRoot& rRoot, sal_uInt16 nC, bool bIsEmpty = false );

    sal_uInt16       GetCol() const          { return nCol; }
    bool             HasTop10() const        { return ::get_flag( nFlags, EXC_AFFLAG_TOP10 ); }
@@ -388,6 +390,7 @@ public:
    bool                    HasCondition() const;
    bool                    AddEntry( const ScQueryEntry& rEntry );
    void                    AddMultiValueEntry( const ScQueryEntry& rEntry );
    void                    SetButtonHidden(bool bValue) { bIsButtonHidden = bValue; }
    void AddColorEntry( const ScQueryEntry& rEntry );

    virtual void            SaveXml( XclExpXmlStream& rStrm ) override;
diff --git a/sc/source/filter/oox/autofilterbuffer.cxx b/sc/source/filter/oox/autofilterbuffer.cxx
index 3295087..b7f14d8 100644
--- a/sc/source/filter/oox/autofilterbuffer.cxx
+++ b/sc/source/filter/oox/autofilterbuffer.cxx
@@ -656,6 +656,11 @@ ApiFilterSettings FilterColumn::finalizeImport()
    return aSettings;
}

bool FilterColumn::isButtonHidden()
{
    return (mbShowButton == false) || (mbHiddenButton == true);
}

// SortCondition

SortCondition::SortCondition( const WorkbookHelper& rHelper ) :
@@ -743,6 +748,11 @@ void AutoFilter::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRa
        '(A1 and B1) or (B2 and C1)'. */
    bool bHasOrConnection = false;

    ScDocument& rDoc = getScDocument();
    SCCOL nCol = maRange.aStart.Col();
    SCROW nRow = maRange.aStart.Row();
    SCTAB nTab = maRange.aStart.Tab();

    // process all filter column objects, exit when 'or' connection exists
    for( const auto& rxFilterColumn : maFilterColumns )
    {
@@ -750,6 +760,13 @@ void AutoFilter::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRa
        ApiFilterSettings aSettings = rxFilterColumn->finalizeImport();
        ApiFilterSettings::FilterFieldVector& rColumnFields = aSettings.maFilterFields;

        if (rxFilterColumn->isButtonHidden())
        {
            auto nFlag = rDoc.GetAttr(nCol, nRow, nTab, ATTR_MERGE_FLAG)->GetValue();
            rDoc.ApplyAttr(nCol, nRow, nTab, ScMergeFlagAttr(nFlag & ~ScMF::Auto));
        }
        nCol++;

        /*  Check whether mode for regular expressions is compatible with
            the global mode in obNeedsRegExp. If either one is still in
            don't-care state, all is fine. If both are set, they must be
@@ -838,7 +855,6 @@ void AutoFilter::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRa
        aParam.maKeyState[0].nField += nStartPos;
    }

    ScDocument& rDoc = getScDocument();
    ScDBData* pDBData = rDoc.GetDBAtArea(
        nSheet,
        maRange.aStart.Col(), maRange.aStart.Row(),