treelist for autofilter ( nested nodes for dates ) ( using SvxTreeListBox )

Attempt at trying to get more interopable behavior, imho the way dates are
presented in the autofilter of the "other" well known spreadsheet application
is very nice and intuitive. This attempt here is lacking in that

a) for some reason I am not getting the node icons ( and subsequently can't sing
le click anything to expand the nodes ) Only double clicking works to expand
b) the 'check' behaviour is not quite right, e.g. as simple example is if all
nodes of a particular date are unset then clicking the leaf node ( e.g. day )
wont set the ancestor nodes.

Change-Id: Iaa89cac21fb18074ff64a984f099a35851c2fe3c
diff --git a/sc/inc/typedstrdata.hxx b/sc/inc/typedstrdata.hxx
index 346978b..7d8cf44 100644
--- a/sc/inc/typedstrdata.hxx
+++ b/sc/inc/typedstrdata.hxx
@@ -27,13 +27,15 @@
    };

    ScTypedStrData( const OUString& rStr, double nVal = 0.0,
                    StringType eType = Standard );
                    StringType eType = Standard, bool bDate = false );

    ScTypedStrData( const ScTypedStrData& rCpy );

    bool IsStrData() const;
    bool IsDate() const;
    SC_DLLPUBLIC const OUString& GetString() const;
    StringType GetStringType() const;
    double GetValue() const { return mfValue; }

    struct LessCaseSensitive : std::binary_function<ScTypedStrData, ScTypedStrData, bool>
    {
@@ -62,6 +64,7 @@
    OUString maStrValue;
    double mfValue;
    StringType meStrType;
    bool   mbIsDate;
};

class FindTypedStrData : std::unary_function<ScTypedStrData, bool>
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index b452b68..ce2d81f 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1795,15 +1795,17 @@
        }

        short nType = pFormatter->GetType(nFormat);
        bool bDate = false;
        if ((nType & NUMBERFORMAT_DATE) && !(nType & NUMBERFORMAT_TIME))
        {
            // special case for date values.  Disregard the time
            // element if the number format is of date type.
            fVal = rtl::math::approxFloor(fVal);
            mbHasDates = true;
            bDate = true;
        }

        mrStrings.push_back(ScTypedStrData(aStr, fVal, ScTypedStrData::Value));
        // maybe extend ScTypedStrData enum is also an option here
        mrStrings.push_back(ScTypedStrData(aStr, fVal, ScTypedStrData::Value,bDate));
    }

public:
diff --git a/sc/source/core/tool/typedstrdata.cxx b/sc/source/core/tool/typedstrdata.cxx
index 57d7358..2ffd773b 100644
--- a/sc/source/core/tool/typedstrdata.cxx
+++ b/sc/source/core/tool/typedstrdata.cxx
@@ -20,6 +20,9 @@
    if (left.meStrType == Value)
        return left.mfValue < right.mfValue;

    if (left.mbIsDate != right.mbIsDate)
        return left.mbIsDate < right.mbIsDate;

    return ScGlobal::GetCaseCollator()->compareString(
        left.maStrValue, right.maStrValue) < 0;
}
@@ -32,6 +35,9 @@
    if (left.meStrType == Value)
        return left.mfValue < right.mfValue;

    if (left.mbIsDate != right.mbIsDate)
        return left.mbIsDate < right.mbIsDate;

    return ScGlobal::GetCollator()->compareString(
        left.maStrValue, right.maStrValue) < 0;
}
@@ -44,6 +50,9 @@
    if (left.meStrType == Value && left.mfValue != right.mfValue)
        return false;

    if (left.mbIsDate != right.mbIsDate )
        return false;

    return ScGlobal::GetCaseCollator()->compareString(
        left.maStrValue, right.maStrValue) == 0;
}
@@ -56,6 +65,9 @@
    if (left.meStrType == Value && left.mfValue != right.mfValue)
        return false;

    if (left.mbIsDate != right.mbIsDate )
        return false;

    return ScGlobal::GetCollator()->compareString(
        left.maStrValue, right.maStrValue) == 0;
}
@@ -75,21 +87,28 @@
}

ScTypedStrData::ScTypedStrData(
    const OUString& rStr, double nVal, StringType nType ) :
    const OUString& rStr, double nVal, StringType nType, bool bDate ) :
    maStrValue(rStr),
    mfValue(nVal),
    meStrType(nType) {}
    meStrType(nType),
    mbIsDate( bDate ) {}

ScTypedStrData::ScTypedStrData( const ScTypedStrData& rCpy ) :
    maStrValue(rCpy.maStrValue),
    mfValue(rCpy.mfValue),
    meStrType(rCpy.meStrType) {}
    meStrType(rCpy.meStrType),
    mbIsDate( rCpy.mbIsDate ) {}

bool ScTypedStrData::IsStrData() const
{
    return meStrType != Value;
}

bool ScTypedStrData::IsDate() const
{
    return mbIsDate;
}

const OUString& ScTypedStrData::GetString() const
{
    return maStrValue;
diff --git a/sc/source/ui/cctrl/checklistmenu.cxx b/sc/source/ui/cctrl/checklistmenu.cxx
index e32c03d..08aa1b4 100644
--- a/sc/source/ui/cctrl/checklistmenu.cxx
+++ b/sc/source/ui/cctrl/checklistmenu.cxx
@@ -29,6 +29,8 @@

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

using ::com::sun::star::uno::Reference;
using ::com::sun::star::accessibility::XAccessible;
@@ -850,6 +852,11 @@
        mpParentMenu->terminateAllPopupMenus();
}

ScDocument* ScMenuFloatingWindow::getDoc()
{
    return mpDoc;
}

// ============================================================================

ScCheckListMenuWindow::Config::Config() :
@@ -1078,7 +1085,7 @@
{
    size_t n = maMembers.size();
    for (size_t i = 0; i < n; ++i)
        maChecks.CheckEntryPos(static_cast<sal_uInt16>(i), bSet);
        maChecks.CheckEntryPos( maMembers[i].maName, maMembers[i].mpParent, bSet);

    if (!maConfig.mbAllowEmptySet)
        // We need to have at least one member selected.
@@ -1088,8 +1095,9 @@
void ScCheckListMenuWindow::selectCurrentMemberOnly(bool bSet)
{
    setAllMemberState(!bSet);
    sal_uInt16 nSelected = maChecks.GetSelectEntryPos();
    maChecks.CheckEntryPos(nSelected, bSet);
//    sal_uInt16 nSelected = maChecks.GetSelectEntryPos();
    SvTreeListEntry* pEntry = maChecks.GetCurEntry();
    maChecks.CheckEntryPos(pEntry, bSet );
}

void ScCheckListMenuWindow::cycleFocus(bool bReverse)
@@ -1160,7 +1168,9 @@
{
    if (pChecks != &maChecks)
        return 0;

    SvTreeListEntry* pEntry = pChecks->GetHdlEntry();
    if ( pEntry )
        maChecks.CheckEntryPos( pEntry,  ( pChecks->GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED ) );
    size_t nNumChecked = maChecks.GetCheckedEntryCount();
    if (nNumChecked == maMembers.size())
        // all members visible
@@ -1267,14 +1277,177 @@
    maMembers.reserve(n);
}

void ScCheckListMenuWindow::addDateMember(const OUString& rsName, double nVal, bool bVisible)
{
    ScDocument* pDoc = getDoc();
    if ( pDoc )
    {
        SvNumberFormatter* pFormatter = pDoc->GetFormatTable();
        OUString rsDate;
        if ( pFormatter )
        {
            OUString sFormat("YYYY/MMMM/DD");
            Color* pColor = NULL;
            pFormatter->GetPreviewString(sFormat,
                nVal,
                rsDate,
                &pColor,
                ScGlobal::eLnge );
        }
        maChecks.SetUpdateMode(false);
        sal_Int32 nIndex = 0;
        std::vector< OUString > vParts;
        OUString sParent;
        SvTreeListEntry* pParent = NULL;
        int count = 0;
        do
        {
            OUString sPart = rsDate.getToken( 0, '/', nIndex );
            bool bLeaf = ( ++count == 3 );
            SvTreeListEntry* pChild = maChecks.FindEntry( pParent, sPart );
            if ( !pChild )
            {
                if ( bLeaf )
                    pChild = maChecks.SvTreeListBox::InsertEntry( sPart, pParent, sal_False, LISTBOX_APPEND, NULL, SvLBoxButtonKind_enabledCheckbox );
                else
                    pChild = maChecks.SvTreeListBox::InsertEntry( sPart, pParent, sal_True, LISTBOX_APPEND, NULL, SvLBoxButtonKind_enabledCheckbox );
                Member aMember;
                aMember.maName = sPart;
                aMember.maRealName = rsName;
                aMember.mbDate = true;
                aMember.mbLeaf = bLeaf;
                aMember.mbVisible = bVisible;
                aMember.mpParent = pParent;
                maMembers.push_back(aMember);
            }
            sParent = sPart;
            pParent = pChild;
        } while ( nIndex >= 0 );
        maChecks.SetUpdateMode(true);
    }
}

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

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

        pEntry = pParent ? NextSibling( pEntry ) : GetEntry( ++nRootPos );
    }
    return NULL;
}

void ScCheckListBox::Init()
{
    mpCheckButton = new SvLBoxButtonData( this );
    EnableCheckButton( mpCheckButton );
    SetNodeDefaultImages();
}

sal_Bool ScCheckListBox::IsChecked( OUString& sName, SvTreeListEntry* pParent )
{
    SvTreeListEntry* pEntry = FindEntry( pParent, sName );
    if ( pEntry && GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED)
        return sal_True;
    return sal_False;
}

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

void ScCheckListBox::CheckEntryPos( SvTreeListEntry* pParent, sal_Bool bCheck )
{
    // currently pParent ( and *all* children ) are checked with state of bCheck
    // *BUT* if this is not a Root node then bCheck here should also influence the
    // ancestor hierarchy ( e.g. a child node checked or uncheck MAY need to check/uncheck
    // the parent/grandparent node )
    if ( pParent )
    {
        SetCheckButtonState(
            pParent, bCheck ? SvButtonState( SV_BUTTON_CHECKED ) :
                                           SvButtonState( SV_BUTTON_UNCHECKED ) );
    }
    SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
    while ( pEntry )
    {
        CheckEntryPos( pEntry, bCheck );
        pEntry = NextSibling( pEntry );
    }
}

SvTreeListEntry* ScCheckListBox::CountCheckedEntries( SvTreeListEntry* pParent, sal_uLong& nCount ) const
{
    if ( pParent && GetCheckButtonState( pParent ) == SV_BUTTON_CHECKED  )
        nCount++;
    // Iterate over the children
    SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
    while ( pEntry )
    {
        CountCheckedEntries( pEntry, nCount );
        pEntry = NextSibling( pEntry );
    }
    return NULL;
}

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

void ScCheckListBox::ExpandChildren( SvTreeListEntry* pParent )
{
    if ( pParent )
        Expand( pParent );
    // Iterate over the children
    SvTreeListEntry* pEntry = pParent ? FirstChild( pParent ) : First();
    while ( pEntry )
    {
        ExpandChildren( pEntry );
        pEntry = NextSibling( pEntry );
    }
}

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

    if ( rKey.GetCode() == KEY_RETURN || rKey.GetCode() == KEY_SPACE )
    {
        SvTreeListEntry* pEntry = GetCurEntry();

        if ( pEntry )
        {
            sal_Bool bCheck = ( GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED );
            CheckEntryPos( pEntry, !bCheck );
            if ( bCheck != ( GetCheckButtonState( pEntry ) == SV_BUTTON_CHECKED ) )
                CheckButtonHdl();
        }
    }
    else if ( GetEntryCount() )
        SvTreeListBox::KeyInput( rKEvt );
}

void ScCheckListMenuWindow::initMembers()
{
    size_t n = maMembers.size();
@@ -1282,8 +1455,19 @@
    maChecks.SetUpdateMode(false);
    for (size_t i = 0; i < n; ++i)
    {
        maChecks.InsertEntry(maMembers[i].maName);
        maChecks.CheckEntryPos(static_cast< sal_uInt16 >( i ), maMembers[i].mbVisible);
        if ( !maMembers[ i ].mbDate )
        {
            maChecks.InsertEntry(maMembers[i].maName, NULL, sal_False, LISTBOX_APPEND, NULL,
                SvLBoxButtonKind_enabledCheckbox );
        }
        // Expand all nodes of dates
        // Needs better behaviour, what gets expanded how much etc. ( depending
        // on the tree contents )
        else if ( maMembers[ i ].mpParent == NULL )
        {
            maChecks.ExpandChildren( maChecks.FindEntry( NULL, maMembers[ i ].maName ) );
        }
        maChecks.CheckEntryPos( maMembers[i].maName, maMembers[i].mpParent, maMembers[i].mbVisible);
        if (maMembers[i].mbVisible)
            ++nVisMemCount;
    }
@@ -1323,8 +1507,16 @@
    size_t n = maMembers.size();
    for (size_t i = 0; i < n; ++i)
    {
        bool bState = maChecks.IsChecked(static_cast< sal_uInt16 >( i ));
        aResult.insert(ResultType::value_type(maMembers[i].maName, bState));
        if ( maMembers[i].mbLeaf )
        {
            bool bState =  maChecks.IsChecked( maMembers[i].maName,  maMembers[i].mpParent );
            OUString sName;
            if ( maMembers[i].mbDate )
                sName = maMembers[i].maRealName;
            else
                sName = maMembers[i].maName;
            aResult.insert(ResultType::value_type(sName, bState));
        }
    }
    rResult.swap(aResult);
}
diff --git a/sc/source/ui/inc/checklistmenu.hxx b/sc/source/ui/inc/checklistmenu.hxx
index 305026d..ba8166b 100644
--- a/sc/source/ui/inc/checklistmenu.hxx
+++ b/sc/source/ui/inc/checklistmenu.hxx
@@ -186,6 +186,24 @@
    ScMenuFloatingWindow* mpParentMenu;
};


class ScCheckListBox : public SvTreeListBox
{
    SvLBoxButtonData*   mpCheckButton;
    SvTreeListEntry* CountCheckedEntries( SvTreeListEntry* pParent, sal_uLong& nCount ) const;
    public:
    ScCheckListBox( Window* pParent, WinBits nWinStyle = 0 ) : SvTreeListBox( pParent, nWinStyle ) { Init(); }
    ScCheckListBox( Window* pParent, const ResId& rResId ) : SvTreeListBox( pParent, rResId ) { Init(); }
    ~ScCheckListBox() { delete mpCheckButton; }
    void Init();
    void            CheckEntryPos       ( OUString& sName, SvTreeListEntry* pParent, sal_Bool bCheck = sal_True );
    void            CheckEntryPos       ( SvTreeListEntry* pEntry, sal_Bool bCheck = sal_True );
    sal_Bool        IsChecked( OUString& sName, SvTreeListEntry* pParent );
    SvTreeListEntry* FindEntry( SvTreeListEntry* pParent, const OUString& sNode );
    sal_uInt16 GetCheckedEntryCount() const;
    void         ExpandChildren( SvTreeListEntry* pParent );
    virtual void KeyInput( const KeyEvent& rKEvt );
};
/**
 * This class implements a popup window for field button, for quick access
 * of hide-item list, and possibly more stuff related to field options.
@@ -225,6 +243,7 @@
    virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > CreateAccessible();

    void setMemberSize(size_t n);
    void addDateMember(const OUString& rName, double nVal, bool bVisible);
    void addMember(const OUString& rName, bool bVisible);
    void initMembers();
    void setConfig(const Config& rConfig);
@@ -255,10 +274,14 @@
private:
    struct Member
    {
        OUString maName;
        OUString maName; // node name
        OUString maRealName;
        bool            mbVisible;
        bool            mbDate;
        bool            mbLeaf;

        Member();
        SvTreeListEntry* mpParent;
    };

    class CancelButton : public ::CancelButton
@@ -299,7 +322,9 @@
    DECL_LINK( CheckHdl, SvTreeListBox* );

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

    ScCheckListBox maChecks;

    TriStateBox     maChkToggleAll;
    ImageButton     maBtnSelectSingle;
diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx
index 2b4e657..dc13e38 100644
--- a/sc/source/ui/view/gridwin.cxx
+++ b/sc/source/ui/view/gridwin.cxx
@@ -706,7 +706,10 @@
        bool bSelected = true;
        if (!aSelected.empty())
            bSelected = aSelected.count(aVal) > 0;
        mpAutoFilterPopup->addMember(aVal, bSelected);
        if ( it->IsDate() )
            mpAutoFilterPopup->addDateMember( aVal, it->GetValue(), bSelected );
        else
            mpAutoFilterPopup->addMember(aVal, bSelected);
    }
    mpAutoFilterPopup->initMembers();