Enhancement : tdf#89908 Add Search field to the autofilter

Change-Id: Id44f61b5194f0a0898f8eed85ceadf78c3101aa1
Reviewed-on: https://gerrit.libreoffice.org/14958
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/sc/inc/AccessibleFilterTopWindow.hxx b/sc/inc/AccessibleFilterTopWindow.hxx
index 5983150..b389ef1 100644
--- a/sc/inc/AccessibleFilterTopWindow.hxx
+++ b/sc/inc/AccessibleFilterTopWindow.hxx
@@ -49,7 +49,7 @@ public:
    // Non-UNO Methods

    enum ChildControlType {
        LISTBOX, TOGGLE_ALL, SINGLE_ON_BTN, SINGLE_OFF_BTN, OK_BTN, CANCEL_BTN
        EDIT_SEARCH_BOX, LISTBOX, TOGGLE_ALL, SINGLE_ON_BTN, SINGLE_OFF_BTN, OK_BTN, CANCEL_BTN
    };
    void setAccessibleChild(
        const ::com::sun::star::uno::Reference<
@@ -61,6 +61,9 @@ private:
    ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >
        mxAccMenu;

    /** Edit search box for searching field members */
    ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >
        mxAccEditSearchBox;
    /** check list box for field member visibility */
    ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >
        mxAccListBox;
diff --git a/sc/source/ui/Accessibility/AccessibleFilterTopWindow.cxx b/sc/source/ui/Accessibility/AccessibleFilterTopWindow.cxx
index 9380967..8781ece 100644
--- a/sc/source/ui/Accessibility/AccessibleFilterTopWindow.cxx
+++ b/sc/source/ui/Accessibility/AccessibleFilterTopWindow.cxx
@@ -62,16 +62,18 @@ Reference<XAccessible> ScAccessibleFilterTopWindow::getAccessibleChild(
    switch (nIndex)
    {
        case 0:
            return mxAccListBox;
            return mxAccEditSearchBox;
        case 1:
            return mxAccToggleAll;
            return mxAccListBox;
        case 2:
            return mxAccSingleOnBtn;
            return mxAccToggleAll;
        case 3:
            return mxAccSingleOffBtn;
            return mxAccSingleOnBtn;
        case 4:
            return mxAccOkBtn;
            return mxAccSingleOffBtn;
        case 5:
            return mxAccOkBtn;
        case 6:
            return mxAccCancelBtn;
        default:
            ;
@@ -90,6 +92,8 @@ void ScAccessibleFilterTopWindow::setAccessibleChild(
{
    switch (eType)
    {
        case EDIT_SEARCH_BOX:
            mxAccEditSearchBox = rAccessible;
        case LISTBOX:
            mxAccListBox = rAccessible;
        break;
diff --git a/sc/source/ui/cctrl/checklistmenu.cxx b/sc/source/ui/cctrl/checklistmenu.cxx
index 1366bf5..4e73d9ef 100644
--- a/sc/source/ui/cctrl/checklistmenu.cxx
+++ b/sc/source/ui/cctrl/checklistmenu.cxx
@@ -32,6 +32,7 @@
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <com/sun/star/accessibility/XAccessibleContext.hpp>
#include <svtools/fmtfield.hxx>
#include <svtools/treelistentry.hxx>
#include "document.hxx"

using namespace com::sun::star;
@@ -855,6 +856,7 @@ void ScCheckListMenuWindow::CancelButton::Click()

ScCheckListMenuWindow::ScCheckListMenuWindow(vcl::Window* pParent, ScDocument* pDoc) :
    ScMenuFloatingWindow(pParent, pDoc),
    maEdSearch(this),
    maChecks(this,  WB_HASBUTTONS | WB_HASLINES | WB_HASLINESATROOT | WB_HASBUTTONSATROOT ),
    maChkToggleAll(this, 0),
    maBtnSelectSingle  (this, 0),
@@ -872,8 +874,9 @@ ScCheckListMenuWindow::ScCheckListMenuWindow(vcl::Window* pParent, ScDocument* p

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

    maTabStopCtrls.reserve(7);
    maTabStopCtrls.reserve(8);
    maTabStopCtrls.push_back(this);
    maTabStopCtrls.push_back(&maEdSearch);
    maTabStopCtrls.push_back(&maChecks);
    maTabStopCtrls.push_back(&maChkToggleAll);
    maTabStopCtrls.push_back(&maBtnSelectSingle);
@@ -895,6 +898,7 @@ void ScCheckListMenuWindow::getSectionPosSize(
    sal_Int32 nScaleFactor = GetDPIScaleFactor();

    // constant parameters.
    const long nSearchBoxMargin = 10 *nScaleFactor;
    const long nListBoxMargin = 5 * nScaleFactor;            // horizontal distance from the side of the dialog to the listbox border.
    const long nListBoxInnerPadding = 5 * nScaleFactor;
    const long nTopMargin = 5 * nScaleFactor;
@@ -906,13 +910,14 @@ void ScCheckListMenuWindow::getSectionPosSize(
    const long nBtnHeight = nLabelHeight * 2;
    const long nBottomMargin = 10 * nScaleFactor;
    const long nMenuListMargin = 5 * nScaleFactor;
    const long nSearchBoxHeight = nLabelHeight * 2;

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

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

    switch (eType)
    {
@@ -922,15 +927,21 @@ void ScCheckListMenuWindow::getSectionPosSize(
            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);
            rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin + nSearchBoxHeight + nSearchBoxMargin);
            rSize = Size(nListBoxWidth, nListBoxHeight);
        }
        break;
        case LISTBOX_AREA_INNER:
        {
            rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin);
            rPos = Point(nListBoxMargin, nTopMargin + nMenuHeight + nMenuListMargin + nSearchBoxHeight + nSearchBoxMargin);
            rPos.X() += nListBoxInnerPadding;
            rPos.Y() += nListBoxInnerPadding;

@@ -1028,6 +1039,14 @@ void ScCheckListMenuWindow::packWindow()
    maBtnCancel.SetFont(getLabelFont());
    maBtnCancel.Show();

    getSectionPosSize(aPos, aSize, EDIT_SEARCH);
    maEdSearch.SetPosSizePixel(aPos, aSize);
    maEdSearch.SetFont(getLabelFont());
    maEdSearch.SetControlBackground(rStyle.GetFieldColor());
    maEdSearch.SetPlaceholderText(SC_STRLOAD(RID_POPUP_FILTER, 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());
@@ -1084,7 +1103,7 @@ void ScCheckListMenuWindow::setAllMemberState(bool bSet)
        aLabel = maMembers[i].maName;
        if (aLabel.isEmpty())
            aLabel = ScGlobal::GetRscString(STR_EMPTYDATA);
        maChecks.CheckEntry( aLabel, maMembers[i].mpParent, bSet);
        maChecks.ShowCheckEntry( aLabel, maMembers[i].mpParent, true, bSet);
    }

    if (!maConfig.mbAllowEmptySet)
@@ -1165,6 +1184,49 @@ IMPL_LINK_NOARG(ScCheckListMenuWindow, TriStateHdl)
    return 0;
}

IMPL_LINK_NOARG(ScCheckListMenuWindow, EdModifyHdl)
{
    OUString aSearchText = maEdSearch.GetText();
    aSearchText = aSearchText.toAsciiLowerCase();
    bool bSearchTextEmpty = aSearchText.isEmpty();
    size_t n = maMembers.size();
    size_t nSelCount = 0;
    OUString aLabelDisp;

    for (size_t i = 0; i < n; ++i)
    {
        aLabelDisp = maMembers[i].maName;

        if ( aLabelDisp.isEmpty() )
            aLabelDisp = ScGlobal::GetRscString( STR_EMPTYDATA );

        if ( bSearchTextEmpty )
        {
            maChecks.ShowCheckEntry( aLabelDisp, maMembers[i].mpParent, true, maMembers[i].mbVisible );
            if ( maMembers[i].mbVisible )
                ++nSelCount;
            continue;
        }

        if ( aLabelDisp.toAsciiLowerCase().indexOf( aSearchText ) != -1 )
        {
            maChecks.ShowCheckEntry( aLabelDisp, maMembers[i].mpParent, true, true );
            ++nSelCount;
        }
        else
            maChecks.ShowCheckEntry( aLabelDisp, maMembers[i].mpParent, false, false );
    }

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

    return 0;
}

IMPL_LINK( ScCheckListMenuWindow, CheckHdl, SvTreeListBox*, pChecks )
{
    if (pChecks != &maChecks)
@@ -1253,6 +1315,8 @@ Reference<XAccessible> ScCheckListMenuWindow::CreateAccessible()
        fillMenuItemsToAccessible(pAccTop);

        pAccTop->setAccessibleChild(
            maEdSearch.CreateAccessible(), ScAccessibleFilterTopWindow::EDIT_SEARCH_BOX);
        pAccTop->setAccessibleChild(
            maChecks.CreateAccessible(), ScAccessibleFilterTopWindow::LISTBOX);
        pAccTop->setAccessibleChild(
            maChkToggleAll.CreateAccessible(), ScAccessibleFilterTopWindow::TOGGLE_ALL);
@@ -1448,6 +1512,27 @@ void ScCheckListBox::CheckEntry( SvTreeListEntry* pParent, bool bCheck )
    }
}

void ScCheckListBox::ShowCheckEntry( const OUString& sName, SvTreeListEntry* pParent, bool bShow, bool bCheck )
{
    SvTreeListEntry* pEntry = FindEntry( pParent, sName );
    if ( bShow )
    {
        if ( !pEntry )
        {
            pEntry = InsertEntry(
                sName, NULL, false, TREELIST_APPEND, NULL,
                SvLBoxButtonKind_enabledCheckbox);

            SetCheckButtonState(
                pEntry, bCheck ? SV_BUTTON_CHECKED : SV_BUTTON_UNCHECKED);
        }
        else
            CheckEntry( pEntry, bCheck );
    }
    else if ( pEntry )
        RemoveParentKeepChildren( pEntry );
}

SvTreeListEntry* ScCheckListBox::CountCheckedEntries( SvTreeListEntry* pParent, sal_uLong& nCount ) const
{
    if ( pParent && GetCheckButtonState( pParent ) == SV_BUTTON_CHECKED  )
diff --git a/sc/source/ui/cctrl/checklistmenu.src b/sc/source/ui/cctrl/checklistmenu.src
index e660b75..a6a0fe7 100644
--- a/sc/source/ui/cctrl/checklistmenu.src
+++ b/sc/source/ui/cctrl/checklistmenu.src
@@ -50,6 +50,11 @@ Resource RID_POPUP_FILTER
    {
        Text [ en-US ] = "Hide only the current item." ;
    };

    String STR_EDIT_SEARCH_ITEMS
    {
        Text [ en-US ] = "Search items..." ;
    };
};

Image RID_IMG_SELECT_CURRENT
diff --git a/sc/source/ui/inc/checklistmenu.hrc b/sc/source/ui/inc/checklistmenu.hrc
index bc1f47e..85dd506 100644
--- a/sc/source/ui/inc/checklistmenu.hrc
+++ b/sc/source/ui/inc/checklistmenu.hrc
@@ -28,6 +28,7 @@
#define STR_BTN_TOGGLE_ALL          4
#define STR_BTN_SELECT_CURRENT      5
#define STR_BTN_UNSELECT_CURRENT    6
#define STR_EDIT_SEARCH_ITEMS       7

#endif

diff --git a/sc/source/ui/inc/checklistmenu.hxx b/sc/source/ui/inc/checklistmenu.hxx
index 5e3cc18..aea8507 100644
--- a/sc/source/ui/inc/checklistmenu.hxx
+++ b/sc/source/ui/inc/checklistmenu.hxx
@@ -12,6 +12,7 @@

#include <vcl/popupmenuwindow.hxx>
#include <vcl/button.hxx>
#include <vcl/edit.hxx>
#include <vcl/scrbar.hxx>
#include <vcl/timer.hxx>
#include <svx/checklbx.hxx>
@@ -199,6 +200,7 @@ class ScCheckListBox : public SvTreeListBox
    void Init();
    void CheckEntry( const OUString& sName, SvTreeListEntry* pParent, bool bCheck = true );
    void CheckEntry( SvTreeListEntry* pEntry, bool bCheck = true );
    void ShowCheckEntry( const OUString& sName, SvTreeListEntry* pParent, bool bShow = true, bool bCheck = true );
    bool IsChecked( const OUString& sName, SvTreeListEntry* pParent );
    SvTreeListEntry* FindEntry( SvTreeListEntry* pParent, const OUString& sNode );
    sal_uInt16 GetCheckedEntryCount() const;
@@ -306,6 +308,7 @@ private:
        BTN_SINGLE_UNSELECT,
        BTN_OK,               // OK button
        BTN_CANCEL,           // Cancel button
        EDIT_SEARCH,          // Search box
    };
    void getSectionPosSize(Point& rPos, Size& rSize, SectionType eType) const;

@@ -321,10 +324,13 @@ private:
    DECL_LINK( ButtonHdl, Button* );
    DECL_LINK( TriStateHdl, void* );
    DECL_LINK( CheckHdl, SvTreeListBox* );
    DECL_LINK( EdModifyHdl, void* );

private:
    SvTreeListEntry* findEntry(  SvTreeListEntry* pParent, const OUString& rText );

    Edit maEdSearch;

    ScCheckListBox maChecks;

    TriStateBox     maChkToggleAll;