summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBalazs Varga <balazs.varga991@gmail.com>2021-02-18 18:04:53 +0100
committerGabor Kelemen <kelemen.gabor2@nisz.hu>2021-04-22 15:11:31 +0200
commitae067f7e46ef9606fca3a5a26857ef3169deb2dd (patch)
treeda1b48a61f9464bfade7406d10cc80b3774e8e67
parent9f4a3e9e161415d90661988f0215f0a467e115a4 (diff)
tdf#140469 XLSX import: apply more than 8 filters
in OOXML autofilter import by removing the artificial limit (which looked like the limit for conditions handled by the standard filter in LO, but not for the autofilter). Now the autofilter popup menu does not always select all items, if the document contained more than 8 selected items there. Change-Id: Iaa6ce15d4b1162ab78dd001734721ae859283d0d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111156 Tested-by: László Németh <nemeth@numbertext.org> Reviewed-by: László Németh <nemeth@numbertext.org> (cherry picked from commit 7ba76115b0e3baefae0ede66848f4340c7c7401b) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114494 Tested-by: Gabor Kelemen <kelemen.gabor2@nisz.hu> Reviewed-by: Gabor Kelemen <kelemen.gabor2@nisz.hu>
-rw-r--r--sc/qa/uitest/autofilter/autofilter.py15
-rw-r--r--sc/qa/uitest/data/autofilter/tdf140469.xlsxbin0 -> 9297 bytes
-rw-r--r--sc/source/filter/inc/autofilterbuffer.hxx10
-rw-r--r--sc/source/filter/oox/autofilterbuffer.cxx262
4 files changed, 135 insertions, 152 deletions
diff --git a/sc/qa/uitest/autofilter/autofilter.py b/sc/qa/uitest/autofilter/autofilter.py
index bb5260da504e..bf01911f62b4 100644
--- a/sc/qa/uitest/autofilter/autofilter.py
+++ b/sc/qa/uitest/autofilter/autofilter.py
@@ -128,4 +128,19 @@ class AutofilterTest(UITestCase):
xOkBtn.executeAction("CLICK", tuple())
self.ui_test.close_doc()
+
+ def test_tdf140469(self):
+ doc = self.ui_test.load_file(get_url_for_data_file("tdf140469.xlsx"))
+
+ xGridWin = self.xUITest.getTopFocusWindow().getChild("grid_window")
+
+ xGridWin.executeAction("LAUNCH", mkPropertyValues({"AUTOFILTER": "", "COL": "1", "ROW": "0"}))
+ xFloatWindow = self.xUITest.getFloatWindow()
+ xCheckListMenu = xFloatWindow.getChild("check_list_menu")
+ xTreeList = xCheckListMenu.getChild("check_list_box")
+ self.assertEqual(9, len(xTreeList.getChildren()))
+ xOkBtn = xFloatWindow.getChild("cancel")
+ xOkBtn.executeAction("CLICK", tuple())
+
+ self.ui_test.close_doc()
# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/sc/qa/uitest/data/autofilter/tdf140469.xlsx b/sc/qa/uitest/data/autofilter/tdf140469.xlsx
new file mode 100644
index 000000000000..3c90c7cfe1b1
--- /dev/null
+++ b/sc/qa/uitest/data/autofilter/tdf140469.xlsx
Binary files differ
diff --git a/sc/source/filter/inc/autofilterbuffer.hxx b/sc/source/filter/inc/autofilterbuffer.hxx
index e0270336a10e..3b512f4f98b9 100644
--- a/sc/source/filter/inc/autofilterbuffer.hxx
+++ b/sc/source/filter/inc/autofilterbuffer.hxx
@@ -64,7 +64,7 @@ public:
virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm );
/** Derived classes return converted UNO API filter settings representing all filter settings. */
- virtual ApiFilterSettings finalizeImport( sal_Int32 nMaxCount );
+ virtual ApiFilterSettings finalizeImport();
};
@@ -82,7 +82,7 @@ public:
virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
/** Returns converted UNO API filter settings representing all filter settings. */
- virtual ApiFilterSettings finalizeImport( sal_Int32 nMaxCount ) override;
+ virtual ApiFilterSettings finalizeImport() override;
private:
@@ -103,7 +103,7 @@ public:
virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
/** Returns converted UNO API filter settings representing all filter settings. */
- virtual ApiFilterSettings finalizeImport( sal_Int32 nMaxCount ) override;
+ virtual ApiFilterSettings finalizeImport() override;
private:
double mfValue; /// Number of items or percentage.
@@ -141,7 +141,7 @@ public:
virtual void importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm ) override;
/** Returns converted UNO API filter settings representing all filter settings. */
- virtual ApiFilterSettings finalizeImport( sal_Int32 nMaxCount ) override;
+ virtual ApiFilterSettings finalizeImport() override;
private:
/** Appends the passed filter criterion, if it contains valid settings. */
@@ -174,7 +174,7 @@ public:
/** Returns converted UNO API filter settings representing all filter
settings of this column. */
- ApiFilterSettings finalizeImport( sal_Int32 nMaxCount );
+ ApiFilterSettings finalizeImport();
private:
std::shared_ptr< FilterSettingsBase >
diff --git a/sc/source/filter/oox/autofilterbuffer.cxx b/sc/source/filter/oox/autofilterbuffer.cxx
index 3821c650dbcb..f8a860e27f80 100644
--- a/sc/source/filter/oox/autofilterbuffer.cxx
+++ b/sc/source/filter/oox/autofilterbuffer.cxx
@@ -205,7 +205,7 @@ void FilterSettingsBase::importRecord( sal_Int32 /*nRecId*/, SequenceInputStream
{
}
-ApiFilterSettings FilterSettingsBase::finalizeImport( sal_Int32 /*nMaxCount*/ )
+ApiFilterSettings FilterSettingsBase::finalizeImport()
{
return ApiFilterSettings();
}
@@ -295,25 +295,23 @@ void DiscreteFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm
}
}
-ApiFilterSettings DiscreteFilter::finalizeImport( sal_Int32 nMaxCount )
+ApiFilterSettings DiscreteFilter::finalizeImport()
{
ApiFilterSettings aSettings;
- if( static_cast< sal_Int32 >( maValues.size() ) <= nMaxCount )
- {
- aSettings.maFilterFields.reserve( maValues.size() );
+ aSettings.maFilterFields.reserve( maValues.size() );
- // insert all filter values
- aSettings.appendField( true, maValues );
+ // insert all filter values
+ aSettings.appendField( true, maValues );
- // extra field for 'show empty'
- if( mbShowBlank )
- aSettings.appendField( false, FilterOperator2::EMPTY, OUString() );
+ // extra field for 'show empty'
+ if( mbShowBlank )
+ aSettings.appendField( false, FilterOperator2::EMPTY, OUString() );
+
+ /* Require disabled regular expressions, filter entries may contain
+ any RE meta characters. */
+ if( !maValues.empty() )
+ aSettings.mobNeedsRegExp = false;
- /* Require disabled regular expressions, filter entries may contain
- any RE meta characters. */
- if( !maValues.empty() )
- aSettings.mobNeedsRegExp = false;
- }
return aSettings;
}
@@ -347,7 +345,7 @@ void Top10Filter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
}
}
-ApiFilterSettings Top10Filter::finalizeImport( sal_Int32 /*nMaxCount*/ )
+ApiFilterSettings Top10Filter::finalizeImport()
{
sal_Int32 nOperator = mbTop ?
(mbPercent ? FilterOperator2::TOP_PERCENT : FilterOperator2::TOP_VALUES) :
@@ -455,7 +453,7 @@ void CustomFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
}
}
-ApiFilterSettings CustomFilter::finalizeImport( sal_Int32 /*nMaxCount*/ )
+ApiFilterSettings CustomFilter::finalizeImport()
{
ApiFilterSettings aSettings;
OSL_ENSURE( maCriteria.size() <= 2, "CustomFilter::finalizeImport - too many filter criteria" );
@@ -552,13 +550,13 @@ void FilterColumn::importFilterColumn( SequenceInputStream& rStrm )
mbShowButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_SHOWBUTTON );
}
-ApiFilterSettings FilterColumn::finalizeImport( sal_Int32 nMaxCount )
+ApiFilterSettings FilterColumn::finalizeImport()
{
ApiFilterSettings aSettings;
if( (0 <= mnColId) && mxSettings )
{
// filter settings object creates a sequence of filter fields
- aSettings = mxSettings->finalizeImport( nMaxCount );
+ aSettings = mxSettings->finalizeImport();
// add column index to all filter fields
for( auto& rFilterField : aSettings.maFilterFields )
rFilterField.Field = mnColId;
@@ -627,144 +625,114 @@ void AutoFilter::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRa
{
// convert filter settings using the filter descriptor of the database range
const Reference<XSheetFilterDescriptor3> xFilterDesc( rxDatabaseRange->getFilterDescriptor(), UNO_QUERY_THROW );
- if( xFilterDesc.is() )
+ if( !xFilterDesc.is() )
+ return;
+ // set some common properties for the auto filter range
+ PropertySet aDescProps( xFilterDesc );
+ aDescProps.setProperty( PROP_IsCaseSensitive, false );
+ aDescProps.setProperty( PROP_SkipDuplicates, false );
+ aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
+ aDescProps.setProperty( PROP_ContainsHeader, true );
+ aDescProps.setProperty( PROP_CopyOutputData, false );
+ // resulting list of all UNO API filter fields
+ ::std::vector<TableFilterField3> aFilterFields;
+ // track if columns require to enable or disable regular expressions
+ OptValue< bool > obNeedsRegExp;
+ /* Track whether the filter fields of the first filter column are
+ connected with 'or'. In this case, other filter fields cannot be
+ inserted without altering the result of the entire filter, due to
+ Calc's precedence for the 'and' connection operator. Example:
+ Excel's filter conditions 'A1 and (B1 or B2) and C1' where B1 and
+ B2 belong to filter column B, will be evaluated by Calc as
+ '(A1 and B1) or (B2 and C1)'. */
+ bool bHasOrConnection = false;
+ // process all filter column objects, exit when 'or' connection exists
+ for( const auto& rxFilterColumn : maFilterColumns )
{
- // set some common properties for the auto filter range
- PropertySet aDescProps( xFilterDesc );
- aDescProps.setProperty( PROP_IsCaseSensitive, false );
- aDescProps.setProperty( PROP_SkipDuplicates, false );
- aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
- aDescProps.setProperty( PROP_ContainsHeader, true );
- aDescProps.setProperty( PROP_CopyOutputData, false );
-
- // maximum number of UNO API filter fields
- sal_Int32 nMaxCount = 0;
- aDescProps.getProperty( nMaxCount, PROP_MaxFieldCount );
- OSL_ENSURE( nMaxCount > 0, "AutoFilter::finalizeImport - invalid maximum filter field count" );
-
- // resulting list of all UNO API filter fields
- ::std::vector<TableFilterField3> aFilterFields;
-
- // track if columns require to enable or disable regular expressions
- OptValue< bool > obNeedsRegExp;
-
- /* Track whether the filter fields of the first filter column are
- connected with 'or'. In this case, other filter fields cannot be
- inserted without altering the result of the entire filter, due to
- Calc's precedence for the 'and' connection operator. Example:
- Excel's filter conditions 'A1 and (B1 or B2) and C1' where B1 and
- B2 belong to filter column B, will be evaluated by Calc as
- '(A1 and B1) or (B2 and C1)'. */
- bool bHasOrConnection = false;
-
- // process all filter column objects, exit when 'or' connection exists
- for( const auto& rxFilterColumn : maFilterColumns )
+ // the filter settings object creates a list of filter fields
+ ApiFilterSettings aSettings = rxFilterColumn->finalizeImport();
+ ApiFilterSettings::FilterFieldVector& rColumnFields = aSettings.maFilterFields;
+ /* 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
+ equal. */
+ bool bRegExpCompatible = !obNeedsRegExp || !aSettings.mobNeedsRegExp || (obNeedsRegExp.get() == aSettings.mobNeedsRegExp.get());
+ // check whether fields are connected by 'or' (see comments above).
+ if( rColumnFields.size() >= 2 )
+ bHasOrConnection = std::any_of(rColumnFields.begin() + 1, rColumnFields.end(),
+ [](const css::sheet::TableFilterField3& rColumnField) { return rColumnField.Connection == FilterConnection_OR; });
+ /* Skip the column filter, if no filter fields have been created,
+ and if the mode for regular expressions of the
+ filter column does not fit. */
+ if( !rColumnFields.empty() && bRegExpCompatible )
{
- // the filter settings object creates a list of filter fields
- ApiFilterSettings aSettings = rxFilterColumn->finalizeImport( nMaxCount );
- ApiFilterSettings::FilterFieldVector& rColumnFields = aSettings.maFilterFields;
-
- // new total number of filter fields
- sal_Int32 nNewCount = static_cast< sal_Int32 >( aFilterFields.size() + rColumnFields.size() );
-
- /* 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
- equal. */
- bool bRegExpCompatible = !obNeedsRegExp || !aSettings.mobNeedsRegExp || (obNeedsRegExp.get() == aSettings.mobNeedsRegExp.get());
-
- // check whether fields are connected by 'or' (see comments above).
- if( rColumnFields.size() >= 2 )
- bHasOrConnection = std::any_of(rColumnFields.begin() + 1, rColumnFields.end(),
- [](const css::sheet::TableFilterField3& rColumnField) { return rColumnField.Connection == FilterConnection_OR; });
-
- /* Skip the column filter, if no filter fields have been created,
- if the number of new filter fields would exceed the total limit
- of filter fields, or if the mode for regular expressions of the
- filter column does not fit. */
- if( !rColumnFields.empty() && (nNewCount <= nMaxCount) && bRegExpCompatible )
- {
- /* Add 'and' connection to the first filter field to connect
- it to the existing filter fields of other columns. */
- rColumnFields[ 0 ].Connection = FilterConnection_AND;
-
- // insert the new filter fields
- aFilterFields.insert( aFilterFields.end(), rColumnFields.begin(), rColumnFields.end() );
-
- // update the regular expressions mode
- obNeedsRegExp.assignIfUsed( aSettings.mobNeedsRegExp );
- }
-
- if( bHasOrConnection )
- break;
+ /* Add 'and' connection to the first filter field to connect
+ it to the existing filter fields of other columns. */
+ rColumnFields[ 0 ].Connection = FilterConnection_AND;
+ // insert the new filter fields
+ aFilterFields.insert( aFilterFields.end(), rColumnFields.begin(), rColumnFields.end() );
+ // update the regular expressions mode
+ obNeedsRegExp.assignIfUsed( aSettings.mobNeedsRegExp );
}
-
- // insert all filter fields to the filter descriptor
- if( !aFilterFields.empty() )
- xFilterDesc->setFilterFields3( ContainerHelper::vectorToSequence( aFilterFields ) );
-
- // regular expressions
- bool bUseRegExp = obNeedsRegExp.get( false );
- aDescProps.setProperty( PROP_UseRegularExpressions, bUseRegExp );
-
- // sort
- if (!maSortConditions.empty())
+ if( bHasOrConnection )
+ break;
+ }
+ // insert all filter fields to the filter descriptor
+ if( !aFilterFields.empty() )
+ xFilterDesc->setFilterFields3( ContainerHelper::vectorToSequence( aFilterFields ) );
+ // regular expressions
+ bool bUseRegExp = obNeedsRegExp.get( false );
+ aDescProps.setProperty( PROP_UseRegularExpressions, bUseRegExp );
+ // sort
+ if (!maSortConditions.empty())
+ {
+ const SortConditionVector::value_type& xSortConditionPointer = *maSortConditions.begin();
+ const SortCondition& rSorConditionLoaded = *xSortConditionPointer;
+ ScSortParam aParam;
+ aParam.bUserDef = false;
+ aParam.nUserIndex = 0;
+ aParam.bByRow = false;
+ ScUserList* pUserList = ScGlobal::GetUserList();
+ if (!rSorConditionLoaded.maSortCustomList.isEmpty())
{
- const SortConditionVector::value_type& xSortConditionPointer = *maSortConditions.begin();
- const SortCondition& rSorConditionLoaded = *xSortConditionPointer;
-
- ScSortParam aParam;
- aParam.bUserDef = false;
- aParam.nUserIndex = 0;
- aParam.bByRow = false;
-
- ScUserList* pUserList = ScGlobal::GetUserList();
- if (!rSorConditionLoaded.maSortCustomList.isEmpty())
+ for (size_t i=0; pUserList && i < pUserList->size(); i++)
{
- for (size_t i=0; pUserList && i < pUserList->size(); i++)
+ const OUString aEntry((*pUserList)[i].GetString());
+ if (aEntry.equalsIgnoreAsciiCase(rSorConditionLoaded.maSortCustomList))
{
- const OUString aEntry((*pUserList)[i].GetString());
- if (aEntry.equalsIgnoreAsciiCase(rSorConditionLoaded.maSortCustomList))
- {
- aParam.bUserDef = true;
- aParam.nUserIndex = i;
- break;
- }
+ aParam.bUserDef = true;
+ aParam.nUserIndex = i;
+ break;
}
}
-
- if (!aParam.bUserDef)
- {
- pUserList->push_back(new ScUserListData(rSorConditionLoaded.maSortCustomList));
- aParam.bUserDef = true;
- aParam.nUserIndex = pUserList->size()-1;
- }
-
- // set sort parameter if we have detected it
- if (aParam.bUserDef)
+ }
+ if (!aParam.bUserDef)
+ {
+ pUserList->push_back(new ScUserListData(rSorConditionLoaded.maSortCustomList));
+ aParam.bUserDef = true;
+ aParam.nUserIndex = pUserList->size()-1;
+ }
+ // set sort parameter if we have detected it
+ if (aParam.bUserDef)
+ {
+ SCCOLROW nStartPos = aParam.bByRow ? maRange.aStart.Col() : maRange.aStart.Row();
+ if (rSorConditionLoaded.mbDescending)
{
- SCCOLROW nStartPos = aParam.bByRow ? maRange.aStart.Col() : maRange.aStart.Row();
- if (rSorConditionLoaded.mbDescending)
- {
- // descending sort - need to enable 1st SortParam slot
- assert(aParam.GetSortKeyCount() == DEFSORT);
-
- aParam.maKeyState[0].bDoSort = true;
- aParam.maKeyState[0].bAscending = false;
- aParam.maKeyState[0].nField += nStartPos;
- }
-
- ScDocument& rDoc = getScDocument();
- ScDBData* pDBData = rDoc.GetDBAtArea(
- nSheet,
- maRange.aStart.Col(), maRange.aStart.Row(),
- maRange.aEnd.Col(), maRange.aEnd.Row());
-
- if (pDBData)
- pDBData->SetSortParam(aParam);
- else
- OSL_FAIL("AutoFilter::finalizeImport(): cannot find matching DBData");
+ // descending sort - need to enable 1st SortParam slot
+ assert(aParam.GetSortKeyCount() == DEFSORT);
+ aParam.maKeyState[0].bDoSort = true;
+ aParam.maKeyState[0].bAscending = false;
+ aParam.maKeyState[0].nField += nStartPos;
}
+ ScDocument& rDoc = getScDocument();
+ ScDBData* pDBData = rDoc.GetDBAtArea(
+ nSheet,
+ maRange.aStart.Col(), maRange.aStart.Row(),
+ maRange.aEnd.Col(), maRange.aEnd.Row());
+ if (pDBData)
+ pDBData->SetSortParam(aParam);
+ else
+ OSL_FAIL("AutoFilter::finalizeImport(): cannot find matching DBData");
}
}
}