sc/source/ui/cctrl/checklistmenu.cxx   |  150 +++++++++++++++++++++++++--------
 sc/source/ui/inc/checklistmenu.hxx     |    6 +
 sc/uiconfig/scalc/ui/filterdropdown.ui |   24 ++++-
 3 files changed, 139 insertions(+), 41 deletions(-)

New commits:
commit d4c34206bcf64b94eac4f0761aeacc285e08af55
Author:     Sahil <me.sahilgau...@gmail.com>
AuthorDate: Wed Dec 27 16:01:07 2023 +0530
Commit:     Heiko Tietze <heiko.tie...@documentfoundation.org>
CommitDate: Mon Feb 12 12:53:39 2024 +0100

    tdf#133836 Autofilter allow adding up members to the current selection
    
    Added a new checkbox [x] Lock, which if checked locks the current
    selection, and further items then can be added to that locked
    selection
    
    Change-Id: If8a5da308443458abfed59b303ee25feffa0aa6d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/159737
    Tested-by: Heiko Tietze <heiko.tie...@documentfoundation.org>
    Tested-by: Jenkins
    Reviewed-by: Heiko Tietze <heiko.tie...@documentfoundation.org>

diff --git a/sc/source/ui/cctrl/checklistmenu.cxx 
b/sc/source/ui/cctrl/checklistmenu.cxx
index d11d5405347e..60078c335437 100644
--- a/sc/source/ui/cctrl/checklistmenu.cxx
+++ b/sc/source/ui/cctrl/checklistmenu.cxx
@@ -474,6 +474,8 @@ ScCheckListMenuControl::Config::Config() :
 ScCheckListMember::ScCheckListMember()
     : mnValue(0.0)
     , mbVisible(true)
+    , mbMarked(false)
+    , mbCheck(true)
     , mbHiddenByOtherFilter(false)
     , mbDate(false)
     , mbLeaf(false)
@@ -504,6 +506,7 @@ 
ScCheckListMenuControl::ScCheckListMenuControl(weld::Widget* pParent, ScViewData
     , mxListChecks(mxBuilder->weld_tree_view("check_list_box"))
     , mxTreeChecks(mxBuilder->weld_tree_view("check_tree_box"))
     , mxChkToggleAll(mxBuilder->weld_check_button("toggle_all"))
+    , mxChkLockChecked(mxBuilder->weld_check_button("lock_checked"))
     , mxBtnSelectSingle(mxBuilder->weld_button("select_current"))
     , mxBtnUnselectSingle(mxBuilder->weld_button("unselect_current"))
     , mxButtonBox(mxBuilder->weld_box("buttonbox"))
@@ -536,6 +539,7 @@ 
ScCheckListMenuControl::ScCheckListMenuControl(weld::Widget* pParent, ScViewData
     mxListChecks->connect_popup_menu(LINK(this, ScCheckListMenuControl, 
CommandHdl));
     mxTreeChecks->connect_popup_menu(LINK(this, ScCheckListMenuControl, 
CommandHdl));
     mxChkToggleAll->connect_mouse_move(LINK(this, ScCheckListMenuControl, 
MouseEnterHdl));
+    mxChkLockChecked->connect_mouse_move(LINK(this, ScCheckListMenuControl, 
MouseEnterHdl));
     mxBtnSelectSingle->connect_mouse_move(LINK(this, ScCheckListMenuControl, 
MouseEnterHdl));
     mxBtnUnselectSingle->connect_mouse_move(LINK(this, ScCheckListMenuControl, 
MouseEnterHdl));
     mxBtnOk->connect_mouse_move(LINK(this, ScCheckListMenuControl, 
MouseEnterHdl));
@@ -601,6 +605,7 @@ 
ScCheckListMenuControl::ScCheckListMenuControl(weld::Widget* pParent, ScViewData
     mxListChecks->connect_toggled(LINK(this, ScCheckListMenuControl, 
CheckHdl));
     mxListChecks->connect_key_press(LINK(this, ScCheckListMenuControl, 
KeyInputHdl));
     mxChkToggleAll->connect_toggled(LINK(this, ScCheckListMenuControl, 
TriStateHdl));
+    mxChkLockChecked->connect_toggled(LINK(this, ScCheckListMenuControl, 
LockCheckedHdl));
     mxBtnSelectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, 
ButtonHdl));
     mxBtnUnselectSingle->connect_clicked(LINK(this, ScCheckListMenuControl, 
ButtonHdl));
 
@@ -745,6 +750,104 @@ IMPL_LINK(ScCheckListMenuControl, ButtonHdl, 
weld::Button&, rBtn, void)
     }
 }
 
+namespace
+{
+    void insertMember(weld::TreeView& rView, const weld::TreeIter& rIter, 
const ScCheckListMember& rMember, bool bChecked, bool bLock=false)
+    {
+        OUString aLabel = rMember.maName;
+        if (aLabel.isEmpty())
+            aLabel = ScResId(STR_EMPTYDATA);
+        rView.set_toggle(rIter, bChecked ? TRISTATE_TRUE : TRISTATE_FALSE);
+        rView.set_text(rIter, aLabel, 0);
+
+        if (bLock)
+            rView.set_sensitive(rIter, !rMember.mbHiddenByOtherFilter && 
!rMember.mbMarked);
+        else
+            rView.set_sensitive(rIter, !rMember.mbHiddenByOtherFilter);
+    }
+
+    void loadSearchedMembers(std::vector<int>& rSearchedMembers, 
std::vector<ScCheckListMember>& rMembers,
+                             const OUString& rSearchText, bool bLock=false)
+    {
+        const OUString aSearchText = ScGlobal::getCharClass().lowercase( 
rSearchText );
+
+        for (size_t i = 0; i < rMembers.size(); ++i)
+        {
+            assert(!rMembers[i].mbDate);
+
+            OUString aLabelDisp = rMembers[i].maName;
+            if ( aLabelDisp.isEmpty() )
+                aLabelDisp = ScResId( STR_EMPTYDATA );
+
+            bool bPartialMatch = ScGlobal::getCharClass().lowercase( 
aLabelDisp ).indexOf( aSearchText ) != -1;
+
+            if (!bPartialMatch)
+                continue;
+            if (!bLock || (!rMembers[i].mbMarked && 
!rMembers[i].mbHiddenByOtherFilter))
+                rSearchedMembers.push_back(i);
+        }
+
+        if (bLock)
+            for (size_t i = 0; i < rMembers.size(); ++i)
+                if (rMembers[i].mbMarked && !rMembers[i].mbHiddenByOtherFilter)
+                    rSearchedMembers.push_back(i);
+
+    }
+}
+
+IMPL_LINK_NOARG(ScCheckListMenuControl, LockCheckedHdl, weld::Toggleable&, 
void)
+{
+    // assume all members are checked
+    for (auto& aMember : maMembers)
+        aMember.mbCheck = true;
+
+    // go over the members visible in the popup, and remember which one is
+    // checked, and which one is not
+    mpChecks->all_foreach([this](weld::TreeIter& rEntry){
+        if (mpChecks->get_toggle(rEntry) == TRISTATE_TRUE)
+        {
+            for (auto& aMember : maMembers)
+                if (aMember.maName == mpChecks->get_text(rEntry))
+                    aMember.mbMarked = true;
+        }
+        else
+        {
+            for (auto& aMember : maMembers)
+                if (aMember.maName == mpChecks->get_text(rEntry))
+                    aMember.mbCheck = false;
+        }
+
+        return false;
+    });
+
+    mpChecks->freeze();
+    mpChecks->clear();
+    mpChecks->thaw();
+
+    OUString aSearchText = mxEdSearch->get_text();
+    if (aSearchText.isEmpty())
+    {
+        initMembers(-1, !mxChkLockChecked->get_active());
+    }
+    else
+    {
+        std::vector<int> aShownIndexes;
+        loadSearchedMembers(aShownIndexes, maMembers, aSearchText, true);
+        std::vector<int> aFixedWidths { mnCheckWidthReq };
+
+        // insert the members, remember whether checked or unchecked.
+        mpChecks->bulk_insert_for_each(aShownIndexes.size(), [this, 
&aShownIndexes](weld::TreeIter& rIter, int i) {
+            size_t nIndex = aShownIndexes[i];
+            insertMember(*mpChecks, rIter, maMembers[nIndex], 
maMembers[nIndex].mbCheck, mxChkLockChecked->get_active());
+        }, nullptr, &aFixedWidths);
+    }
+
+    // unmarking should happen after the members are inserted
+    if (!mxChkLockChecked->get_active())
+        for (auto& aMember : maMembers)
+            aMember.mbMarked = false;
+}
+
 IMPL_LINK_NOARG(ScCheckListMenuControl, TriStateHdl, weld::Toggleable&, void)
 {
     switch (mePrevToggleAllState)
@@ -767,19 +870,6 @@ IMPL_LINK_NOARG(ScCheckListMenuControl, TriStateHdl, 
weld::Toggleable&, void)
     mePrevToggleAllState = mxChkToggleAll->get_state();
 }
 
-namespace
-{
-    void insertMember(weld::TreeView& rView, const weld::TreeIter& rIter, 
const ScCheckListMember& rMember, bool bChecked)
-    {
-        OUString aLabel = rMember.maName;
-        if (aLabel.isEmpty())
-            aLabel = ScResId(STR_EMPTYDATA);
-        rView.set_toggle(rIter, bChecked ? TRISTATE_TRUE : TRISTATE_FALSE);
-        rView.set_text(rIter, aLabel, 0);
-        rView.set_sensitive(rIter, !rMember.mbHiddenByOtherFilter);
-    }
-}
-
 IMPL_LINK_NOARG(ScCheckListMenuControl, ComboChangedHdl, weld::ComboBox&, void)
 {
     if (mbIsMultiField && mxFieldChangedAction)
@@ -876,29 +966,13 @@ IMPL_LINK_NOARG(ScCheckListMenuControl, 
SearchEditTimeoutHdl, Timer*, void)
             nSelCount = initMembers();
         else
         {
-            std::vector<size_t> aShownIndexes;
-
-            for (size_t i = 0; i < nEnableMember; ++i)
-            {
-                assert(!maMembers[i].mbDate);
-
-                OUString aLabelDisp = maMembers[i].maName;
-                if ( aLabelDisp.isEmpty() )
-                    aLabelDisp = ScResId( STR_EMPTYDATA );
-
-                bool bPartialMatch = ScGlobal::getCharClass().lowercase( 
aLabelDisp ).indexOf( aSearchText ) != -1;
-
-                if (!bPartialMatch)
-                    continue;
-
-                aShownIndexes.push_back(i);
-            }
-
+            std::vector<int> aShownIndexes;
+            loadSearchedMembers(aShownIndexes, maMembers, aSearchText, 
mxChkLockChecked->get_active());
             std::vector<int> aFixedWidths { mnCheckWidthReq };
             // tdf#122419 insert in the fastest order, this might be backwards.
             mpChecks->bulk_insert_for_each(aShownIndexes.size(), [this, 
&aShownIndexes, &nSelCount](weld::TreeIter& rIter, int i) {
                 size_t nIndex = aShownIndexes[i];
-                insertMember(*mpChecks, rIter, maMembers[nIndex], true);
+                insertMember(*mpChecks, rIter, maMembers[nIndex], true, 
mxChkLockChecked->get_active());
                 ++nSelCount;
             }, nullptr, &aFixedWidths);
         }
@@ -1115,6 +1189,8 @@ void ScCheckListMenuControl::addMember(const OUString& 
rName, const double nVal,
     aMember.mbLeaf = true;
     aMember.mbValue = bValue;
     aMember.mbVisible = bVisible;
+    aMember.mbMarked = false;
+    aMember.mbCheck = true;
     aMember.mbHiddenByOtherFilter = bHiddenByOtherFilter;
     aMember.mxParent.reset();
     maMembers.emplace_back(std::move(aMember));
@@ -1357,7 +1433,7 @@ IMPL_LINK(ScCheckListMenuControl, KeyInputHdl, const 
KeyEvent&, rKEvt, bool)
     return false;
 }
 
-size_t ScCheckListMenuControl::initMembers(int nMaxMemberWidth)
+size_t ScCheckListMenuControl::initMembers(int nMaxMemberWidth, bool bUnlock)
 {
     size_t n = maMembers.size();
     size_t nEnableMember = std::count_if(maMembers.begin(), maMembers.end(),
@@ -1373,10 +1449,12 @@ size_t ScCheckListMenuControl::initMembers(int 
nMaxMemberWidth)
         // tdf#134038 insert in the fastest order, this might be backwards so 
only do it for
         // the !mbHasDates case where no entry depends on another to exist 
before getting
         // inserted. We cannot retain pre-existing treeview content, only 
clear and fill it.
-        mpChecks->bulk_insert_for_each(n, [this, 
&nVisMemCount](weld::TreeIter& rIter, int i) {
+        mpChecks->bulk_insert_for_each(n, [this, &nVisMemCount, 
&bUnlock](weld::TreeIter& rIter, int i) {
             assert(!maMembers[i].mbDate);
-            insertMember(*mpChecks, rIter, maMembers[i], 
maMembers[i].mbVisible);
-            if (maMembers[i].mbVisible)
+            bool bCheck = ((mxChkLockChecked->get_active() || bUnlock) ? 
maMembers[i].mbMarked : maMembers[i].mbVisible);
+            insertMember(*mpChecks, rIter, maMembers[i], bCheck, 
mxChkLockChecked->get_active());
+
+            if (bCheck)
                 ++nVisMemCount;
         }, nullptr, &aFixedWidths);
     }
diff --git a/sc/source/ui/inc/checklistmenu.hxx 
b/sc/source/ui/inc/checklistmenu.hxx
index 1e85b17c48f2..8e24baef433b 100644
--- a/sc/source/ui/inc/checklistmenu.hxx
+++ b/sc/source/ui/inc/checklistmenu.hxx
@@ -36,6 +36,8 @@ struct ScCheckListMember
     OUString                 maRealName;
     double                   mnValue; // number value of filter condition
     bool                     mbVisible;
+    bool                     mbMarked;
+    bool                     mbCheck;
     bool                     mbHiddenByOtherFilter;
     bool                     mbDate;
     bool                     mbLeaf;
@@ -139,7 +141,7 @@ public:
     void addMember(const OUString& rName, const double nVal, bool bVisible, 
bool bHiddenByOtherFilter,
                    bool bValue = false);
     void clearMembers();
-    size_t initMembers(int nMaxMemberWidth = -1);
+    size_t initMembers(int nMaxMemberWidth = -1, bool bUnlock=false);
     void setConfig(const Config& rConfig);
 
     bool isAllSelected() const;
@@ -231,6 +233,7 @@ private:
 
     DECL_LINK(ButtonHdl, weld::Button&, void);
     DECL_LINK(TriStateHdl, weld::Toggleable&, void);
+    DECL_LINK(LockCheckedHdl, weld::Toggleable&, void);
 
     void Check(const weld::TreeIter* pIter);
 
@@ -279,6 +282,7 @@ private:
     weld::TreeView* mpChecks;
 
     std::unique_ptr<weld::CheckButton> mxChkToggleAll;
+    std::unique_ptr<weld::CheckButton> mxChkLockChecked;
     std::unique_ptr<weld::Button> mxBtnSelectSingle;
     std::unique_ptr<weld::Button> mxBtnUnselectSingle;
 
diff --git a/sc/uiconfig/scalc/ui/filterdropdown.ui 
b/sc/uiconfig/scalc/ui/filterdropdown.ui
index d7f081749ef5..8fa9e8f98395 100644
--- a/sc/uiconfig/scalc/ui/filterdropdown.ui
+++ b/sc/uiconfig/scalc/ui/filterdropdown.ui
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.38.2 -->
+<!-- Generated with glade 3.40.0 -->
 <interface domain="sc">
   <requires lib="gtk+" version="3.20"/>
   <object class="GtkMenu" id="contextmenu">
@@ -153,7 +153,7 @@
               <object class="GtkLabel" id="select_field_label">
                 <property name="can-focus">False</property>
                 <property name="label" translatable="yes" 
context="filterdropdown|select_field_label">Select Field</property>
-                <property name="mnemonic_widget">multi_field_combo</property>
+                <property name="mnemonic-widget">multi_field_combo</property>
               </object>
               <packing>
                 <property name="expand">False</property>
@@ -227,6 +227,22 @@
                         <property name="position">0</property>
                       </packing>
                     </child>
+                    <child>
+                      <object class="GtkCheckButton" id="lock_checked">
+                        <property name="label" translatable="yes" 
context="filterdropdown|STR_BTN_TOGGLE_ALL">Lock</property>
+                        <property name="visible">True</property>
+                        <property name="can-focus">True</property>
+                        <property name="receives-default">False</property>
+                        <property name="hexpand">True</property>
+                        <property name="use-underline">True</property>
+                        <property name="draw-indicator">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">True</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
                     <child>
                       <object class="GtkButton" id="select_current">
                         <property name="visible">True</property>
@@ -239,7 +255,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">True</property>
-                        <property name="position">1</property>
+                        <property name="position">2</property>
                       </packing>
                     </child>
                     <child>
@@ -254,7 +270,7 @@
                       <packing>
                         <property name="expand">False</property>
                         <property name="fill">True</property>
-                        <property name="position">2</property>
+                        <property name="position">3</property>
                       </packing>
                     </child>
                   </object>

Reply via email to