Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / sc / source / filter / oox / autofilterbuffer.cxx
blob9e53200092ce492be65d3f569bd399198b71c148
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <autofilterbuffer.hxx>
22 #include <com/sun/star/beans/XPropertySet.hpp>
23 #include <com/sun/star/sheet/FilterConnection.hpp>
24 #include <com/sun/star/sheet/FilterOperator2.hpp>
25 #include <com/sun/star/sheet/TableFilterField3.hpp>
26 #include <com/sun/star/sheet/XDatabaseRange.hpp>
27 #include <com/sun/star/sheet/XSheetFilterDescriptor3.hpp>
28 #include <com/sun/star/table/TableOrientation.hpp>
29 #include <com/sun/star/table/CellAddress.hpp>
30 #include <rtl/ustrbuf.hxx>
31 #include <osl/diagnose.h>
32 #include <oox/helper/attributelist.hxx>
33 #include <oox/helper/containerhelper.hxx>
34 #include <oox/helper/propertyset.hxx>
35 #include <oox/helper/binaryinputstream.hxx>
36 #include <oox/token/namespaces.hxx>
37 #include <oox/token/properties.hxx>
38 #include <oox/token/tokens.hxx>
39 #include <addressconverter.hxx>
40 #include <defnamesbuffer.hxx>
41 #include <biffhelper.hxx>
43 namespace oox {
44 namespace xls {
46 using namespace ::com::sun::star::sheet;
47 using namespace ::com::sun::star::table;
48 using namespace ::com::sun::star::uno;
50 namespace {
52 const sal_uInt8 BIFF12_TOP10FILTER_TOP = 0x01;
53 const sal_uInt8 BIFF12_TOP10FILTER_PERCENT = 0x02;
55 const sal_uInt16 BIFF12_FILTERCOLUMN_HIDDENBUTTON = 0x0001;
56 const sal_uInt16 BIFF12_FILTERCOLUMN_SHOWBUTTON = 0x0002;
58 const sal_uInt8 BIFF_FILTER_DATATYPE_NONE = 0;
59 const sal_uInt8 BIFF_FILTER_DATATYPE_DOUBLE = 4;
60 const sal_uInt8 BIFF_FILTER_DATATYPE_STRING = 6;
61 const sal_uInt8 BIFF_FILTER_DATATYPE_BOOLEAN = 8;
62 const sal_uInt8 BIFF_FILTER_DATATYPE_EMPTY = 12;
63 const sal_uInt8 BIFF_FILTER_DATATYPE_NOTEMPTY = 14;
65 bool lclGetApiOperatorFromToken( sal_Int32& rnApiOperator, sal_Int32 nToken )
67 switch( nToken )
69 case XML_lessThan: rnApiOperator = FilterOperator2::NOT_EQUAL; return true;
70 case XML_equal: rnApiOperator = FilterOperator2::EQUAL; return true;
71 case XML_lessThanOrEqual: rnApiOperator = FilterOperator2::LESS_EQUAL; return true;
72 case XML_greaterThan: rnApiOperator = FilterOperator2::GREATER; return true;
73 case XML_notEqual: rnApiOperator = FilterOperator2::NOT_EQUAL; return true;
74 case XML_greaterThanOrEqual: rnApiOperator = FilterOperator2::GREATER_EQUAL; return true;
76 return false;
79 /** Removes leading asterisk characters from the passed string.
80 @return True = at least one asterisk character has been removed. */
81 bool lclTrimLeadingAsterisks( OUString& rValue )
83 sal_Int32 nLength = rValue.getLength();
84 sal_Int32 nPos = 0;
85 while( (nPos < nLength) && (rValue[ nPos ] == '*') )
86 ++nPos;
87 if( nPos > 0 )
89 rValue = rValue.copy( nPos );
90 return true;
92 return false;
95 /** Removes trailing asterisk characters from the passed string.
96 @return True = at least one asterisk character has been removed. */
97 bool lclTrimTrailingAsterisks( OUString& rValue )
99 sal_Int32 nLength = rValue.getLength();
100 sal_Int32 nPos = nLength;
101 while( (nPos > 0) && (rValue[ nPos - 1 ] == '*') )
102 --nPos;
103 if( nPos < nLength )
105 rValue = rValue.copy( 0, nPos );
106 return true;
108 return false;
111 /** Converts wildcard characters '*' and '?' to regular expressions and quotes
112 RE meta characters.
113 @return True = passed string has been changed (RE needs to be enabled). */
114 bool lclConvertWildcardsToRegExp( OUString& rValue )
116 // check existence of the wildcard characters '*' and '?'
117 if( !rValue.isEmpty() && ((rValue.indexOf( '*' ) >= 0) || (rValue.indexOf( '?' ) >= 0)) )
119 OUStringBuffer aBuffer;
120 aBuffer.ensureCapacity( rValue.getLength() + 5 );
121 const sal_Unicode* pcChar = rValue.getStr();
122 const sal_Unicode* pcEnd = pcChar + rValue.getLength();
123 for( ; pcChar < pcEnd; ++pcChar )
125 switch( *pcChar )
127 case '?':
128 aBuffer.append( '.' );
129 break;
130 case '*':
131 aBuffer.append( '.' ).append( '*' );
132 break;
133 case '\\': case '.': case '|': case '(': case ')': case '^': case '$':
134 // quote RE meta characters
135 aBuffer.append( '\\' ).append( *pcChar );
136 break;
137 default:
138 aBuffer.append( *pcChar );
141 rValue = aBuffer.makeStringAndClear();
142 return true;
144 return false;
147 } // namespace
149 ApiFilterSettings::ApiFilterSettings()
153 void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, double fValue )
155 maFilterFields.emplace_back();
156 TableFilterField3& rFilterField = maFilterFields.back();
157 rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
158 rFilterField.Operator = nOperator;
159 rFilterField.Values.realloc(1);
160 rFilterField.Values[0].IsNumeric = true;
161 rFilterField.Values[0].NumericValue = fValue;
164 void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, const OUString& rValue )
166 maFilterFields.emplace_back();
167 TableFilterField3& rFilterField = maFilterFields.back();
168 rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
169 rFilterField.Operator = nOperator;
170 rFilterField.Values.realloc(1);
171 rFilterField.Values[0].IsNumeric = false;
172 rFilterField.Values[0].StringValue = rValue;
175 void ApiFilterSettings::appendField( bool bAnd, const std::vector<OUString>& rValues )
177 maFilterFields.emplace_back();
178 TableFilterField3& rFilterField = maFilterFields.back();
179 rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
180 rFilterField.Operator = FilterOperator2::EQUAL;
181 size_t n = rValues.size();
182 rFilterField.Values.realloc(n);
183 for (size_t i = 0; i < n; ++i)
185 rFilterField.Values[i].IsNumeric = false;
186 rFilterField.Values[i].StringValue = rValues[i];
190 FilterSettingsBase::FilterSettingsBase( const WorkbookHelper& rHelper ) :
191 WorkbookHelper( rHelper )
195 void FilterSettingsBase::importAttribs( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
199 void FilterSettingsBase::importRecord( sal_Int32 /*nRecId*/, SequenceInputStream& /*rStrm*/ )
203 ApiFilterSettings FilterSettingsBase::finalizeImport( sal_Int32 /*nMaxCount*/ )
205 return ApiFilterSettings();
208 DiscreteFilter::DiscreteFilter( const WorkbookHelper& rHelper ) :
209 FilterSettingsBase( rHelper ),
210 mnCalendarType( XML_none ),
211 mbShowBlank( false )
215 void DiscreteFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
217 switch( nElement )
219 case XLS_TOKEN( filters ):
220 mnCalendarType = rAttribs.getToken( XML_calendarType, XML_none );
221 mbShowBlank = rAttribs.getBool( XML_blank, false );
222 break;
224 case XLS_TOKEN( filter ):
226 OUString aValue = rAttribs.getXString( XML_val, OUString() );
227 if( !aValue.isEmpty() )
228 maValues.push_back( aValue );
230 break;
234 void DiscreteFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
236 switch( nRecId )
238 case BIFF12_ID_DISCRETEFILTERS:
240 sal_Int32 nShowBlank, nCalendarType;
241 nShowBlank = rStrm.readInt32();
242 nCalendarType = rStrm.readInt32();
244 static const sal_Int32 spnCalendarTypes[] = {
245 XML_none, XML_gregorian, XML_gregorianUs, XML_japan, XML_taiwan, XML_korea, XML_hijri, XML_thai, XML_hebrew,
246 XML_gregorianMeFrench, XML_gregorianArabic, XML_gregorianXlitEnglish, XML_gregorianXlitFrench };
247 mnCalendarType = STATIC_ARRAY_SELECT( spnCalendarTypes, nCalendarType, XML_none );
248 mbShowBlank = nShowBlank != 0;
250 break;
252 case BIFF12_ID_DISCRETEFILTER:
254 OUString aValue = BiffHelper::readString( rStrm );
255 if( !aValue.isEmpty() )
256 maValues.push_back( aValue );
258 break;
262 ApiFilterSettings DiscreteFilter::finalizeImport( sal_Int32 nMaxCount )
264 ApiFilterSettings aSettings;
265 if( static_cast< sal_Int32 >( maValues.size() ) <= nMaxCount )
267 aSettings.maFilterFields.reserve( maValues.size() );
269 // insert all filter values
270 aSettings.appendField( true, maValues );
272 // extra field for 'show empty'
273 if( mbShowBlank )
274 aSettings.appendField( false, FilterOperator2::EMPTY, OUString() );
276 /* Require disabled regular expressions, filter entries may contain
277 any RE meta characters. */
278 if( !maValues.empty() )
279 aSettings.mobNeedsRegExp = false;
281 return aSettings;
284 Top10Filter::Top10Filter( const WorkbookHelper& rHelper ) :
285 FilterSettingsBase( rHelper ),
286 mfValue( 0.0 ),
287 mbTop( true ),
288 mbPercent( false )
292 void Top10Filter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
294 if( nElement == XLS_TOKEN( top10 ) )
296 mfValue = rAttribs.getDouble( XML_val, 0.0 );
297 mbTop = rAttribs.getBool( XML_top, true );
298 mbPercent = rAttribs.getBool( XML_percent, false );
302 void Top10Filter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
304 if( nRecId == BIFF12_ID_TOP10FILTER )
306 sal_uInt8 nFlags;
307 nFlags = rStrm.readuChar();
308 mfValue = rStrm.readDouble();
309 mbTop = getFlag( nFlags, BIFF12_TOP10FILTER_TOP );
310 mbPercent = getFlag( nFlags, BIFF12_TOP10FILTER_PERCENT );
314 ApiFilterSettings Top10Filter::finalizeImport( sal_Int32 /*nMaxCount*/ )
316 sal_Int32 nOperator = mbTop ?
317 (mbPercent ? FilterOperator2::TOP_PERCENT : FilterOperator2::TOP_VALUES) :
318 (mbPercent ? FilterOperator2::BOTTOM_PERCENT : FilterOperator2::BOTTOM_VALUES);
319 ApiFilterSettings aSettings;
320 aSettings.appendField( true, nOperator, mfValue );
321 return aSettings;
324 FilterCriterionModel::FilterCriterionModel() :
325 mnOperator( XML_equal ),
326 mnDataType( BIFF_FILTER_DATATYPE_NONE )
330 void FilterCriterionModel::setBiffOperator( sal_uInt8 nOperator )
332 static const sal_Int32 spnOperators[] = { XML_TOKEN_INVALID,
333 XML_lessThan, XML_equal, XML_lessThanOrEqual, XML_greaterThan, XML_notEqual, XML_greaterThanOrEqual };
334 mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
337 void FilterCriterionModel::readBiffData( SequenceInputStream& rStrm )
339 sal_uInt8 nOperator;
340 mnDataType = rStrm.readuChar();
341 nOperator = rStrm.readuChar();
342 setBiffOperator( nOperator );
344 switch( mnDataType )
346 case BIFF_FILTER_DATATYPE_DOUBLE:
347 maValue <<= rStrm.readDouble();
348 break;
349 case BIFF_FILTER_DATATYPE_STRING:
351 rStrm.skip( 8 );
352 OUString aValue = BiffHelper::readString( rStrm ).trim();
353 if( !aValue.isEmpty() )
354 maValue <<= aValue;
356 break;
357 case BIFF_FILTER_DATATYPE_BOOLEAN:
358 maValue <<= (rStrm.readuInt8() != 0);
359 rStrm.skip( 7 );
360 break;
361 case BIFF_FILTER_DATATYPE_EMPTY:
362 rStrm.skip( 8 );
363 if( mnOperator == XML_equal )
364 maValue <<= OUString();
365 break;
366 case BIFF_FILTER_DATATYPE_NOTEMPTY:
367 rStrm.skip( 8 );
368 if( mnOperator == XML_notEqual )
369 maValue <<= OUString();
370 break;
371 default:
372 OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unexpected data type" );
373 rStrm.skip( 8 );
377 CustomFilter::CustomFilter( const WorkbookHelper& rHelper ) :
378 FilterSettingsBase( rHelper ),
379 mbAnd( false )
383 void CustomFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
385 switch( nElement )
387 case XLS_TOKEN( customFilters ):
388 mbAnd = rAttribs.getBool( XML_and, false );
389 break;
391 case XLS_TOKEN( customFilter ):
393 FilterCriterionModel aCriterion;
394 aCriterion.mnOperator = rAttribs.getToken( XML_operator, XML_equal );
395 OUString aValue = rAttribs.getXString( XML_val, OUString() ).trim();
396 if( (aCriterion.mnOperator == XML_equal) || (aCriterion.mnOperator == XML_notEqual) || (!aValue.isEmpty()) )
397 aCriterion.maValue <<= aValue;
398 appendCriterion( aCriterion );
400 break;
404 void CustomFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
406 switch( nRecId )
408 case BIFF12_ID_CUSTOMFILTERS:
409 mbAnd = rStrm.readInt32() == 0;
410 break;
412 case BIFF12_ID_CUSTOMFILTER:
414 FilterCriterionModel aCriterion;
415 aCriterion.readBiffData( rStrm );
416 appendCriterion( aCriterion );
418 break;
422 ApiFilterSettings CustomFilter::finalizeImport( sal_Int32 /*nMaxCount*/ )
424 ApiFilterSettings aSettings;
425 OSL_ENSURE( maCriteria.size() <= 2, "CustomFilter::finalizeImport - too many filter criteria" );
426 for( const auto& rCriterion : maCriteria )
428 // first extract the filter operator
429 sal_Int32 nOperator = 0;
430 bool bValidOperator = lclGetApiOperatorFromToken( nOperator, rCriterion.mnOperator );
431 if( bValidOperator )
433 if( rCriterion.maValue.has< OUString >() )
435 // string argument
436 OUString aValue;
437 rCriterion.maValue >>= aValue;
438 // check for 'empty', 'contains', 'begins with', or 'ends with' text filters
439 bool bEqual = nOperator == FilterOperator2::EQUAL;
440 bool bNotEqual = nOperator == FilterOperator2::NOT_EQUAL;
441 if( bEqual || bNotEqual )
443 if( aValue.isEmpty() )
445 // empty comparison string: create empty/not empty filters
446 nOperator = bNotEqual ? FilterOperator2::NOT_EMPTY : FilterOperator2::EMPTY;
448 else
450 // compare to something: try to find begins/ends/contains
451 bool bHasLeadingAsterisk = lclTrimLeadingAsterisks( aValue );
452 bool bHasTrailingAsterisk = lclTrimTrailingAsterisks( aValue );
453 // just '***' matches everything, do not create a filter field
454 bValidOperator = !aValue.isEmpty();
455 if( bValidOperator )
457 if( bHasLeadingAsterisk && bHasTrailingAsterisk )
458 nOperator = bNotEqual ? FilterOperator2::DOES_NOT_CONTAIN : FilterOperator2::CONTAINS;
459 else if( bHasLeadingAsterisk )
460 nOperator = bNotEqual ? FilterOperator2::DOES_NOT_END_WITH : FilterOperator2::ENDS_WITH;
461 else if( bHasTrailingAsterisk )
462 nOperator = bNotEqual ? FilterOperator2::DOES_NOT_BEGIN_WITH : FilterOperator2::BEGINS_WITH;
463 // else: no asterisks, stick to equal/not equal
468 if( bValidOperator )
470 // if wildcards are present, require RE mode, otherwise keep don't care state
471 if( lclConvertWildcardsToRegExp( aValue ) )
472 aSettings.mobNeedsRegExp = true;
473 // create a new UNO API filter field
474 aSettings.appendField( mbAnd, nOperator, aValue );
477 else if( rCriterion.maValue.has< double >() )
479 // floating-point argument
480 double fValue = 0.0;
481 rCriterion.maValue >>= fValue;
482 aSettings.appendField( mbAnd, nOperator, fValue );
486 return aSettings;
489 void CustomFilter::appendCriterion( const FilterCriterionModel& rCriterion )
491 if( (rCriterion.mnOperator != XML_TOKEN_INVALID) && rCriterion.maValue.hasValue() )
492 maCriteria.push_back( rCriterion );
495 FilterColumn::FilterColumn( const WorkbookHelper& rHelper ) :
496 WorkbookHelper( rHelper ),
497 mnColId( -1 ),
498 mbHiddenButton( false ),
499 mbShowButton( true )
503 void FilterColumn::importFilterColumn( const AttributeList& rAttribs )
505 mnColId = rAttribs.getInteger( XML_colId, -1 );
506 mbHiddenButton = rAttribs.getBool( XML_hiddenButton, false );
507 mbShowButton = rAttribs.getBool( XML_showButton, true );
510 void FilterColumn::importFilterColumn( SequenceInputStream& rStrm )
512 sal_uInt16 nFlags;
513 mnColId = rStrm.readInt32();
514 nFlags = rStrm.readuInt16();
515 mbHiddenButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_HIDDENBUTTON );
516 mbShowButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_SHOWBUTTON );
519 ApiFilterSettings FilterColumn::finalizeImport( sal_Int32 nMaxCount )
521 ApiFilterSettings aSettings;
522 if( (0 <= mnColId) && mxSettings.get() )
524 // filter settings object creates a sequence of filter fields
525 aSettings = mxSettings->finalizeImport( nMaxCount );
526 // add column index to all filter fields
527 for( auto& rFilterField : aSettings.maFilterFields )
528 rFilterField.Field = mnColId;
530 return aSettings;
533 AutoFilter::AutoFilter( const WorkbookHelper& rHelper ) :
534 WorkbookHelper( rHelper )
538 void AutoFilter::importAutoFilter( const AttributeList& rAttribs, sal_Int16 nSheet )
540 OUString aRangeStr = rAttribs.getString( XML_ref, OUString() );
541 AddressConverter::convertToCellRangeUnchecked( maRange, aRangeStr, nSheet );
544 void AutoFilter::importAutoFilter( SequenceInputStream& rStrm, sal_Int16 nSheet )
546 BinRange aBinRange;
547 rStrm >> aBinRange;
548 AddressConverter::convertToCellRangeUnchecked( maRange, aBinRange, nSheet );
551 FilterColumn& AutoFilter::createFilterColumn()
553 FilterColumnVector::value_type xFilterColumn( new FilterColumn( *this ) );
554 maFilterColumns.push_back( xFilterColumn );
555 return *xFilterColumn;
558 void AutoFilter::finalizeImport( const Reference<XSheetFilterDescriptor3>& rxFilterDesc )
560 if( rxFilterDesc.is() )
562 // set some common properties for the auto filter range
563 PropertySet aDescProps( rxFilterDesc );
564 aDescProps.setProperty( PROP_IsCaseSensitive, false );
565 aDescProps.setProperty( PROP_SkipDuplicates, false );
566 aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
567 aDescProps.setProperty( PROP_ContainsHeader, true );
568 aDescProps.setProperty( PROP_CopyOutputData, false );
570 // maximum number of UNO API filter fields
571 sal_Int32 nMaxCount = 0;
572 aDescProps.getProperty( nMaxCount, PROP_MaxFieldCount );
573 OSL_ENSURE( nMaxCount > 0, "AutoFilter::finalizeImport - invalid maximum filter field count" );
575 // resulting list of all UNO API filter fields
576 ::std::vector<TableFilterField3> aFilterFields;
578 // track if columns require to enable or disable regular expressions
579 OptValue< bool > obNeedsRegExp;
581 /* Track whether the filter fields of the first filter column are
582 connected with 'or'. In this case, other filter fields cannot be
583 inserted without altering the result of the entire filter, due to
584 Calc's precedence for the 'and' connection operator. Example:
585 Excel's filter conditions 'A1 and (B1 or B2) and C1' where B1 and
586 B2 belong to filter column B, will be evaluated by Calc as
587 '(A1 and B1) or (B2 and C1)'. */
588 bool bHasOrConnection = false;
590 // process all filter column objects, exit when 'or' connection exists
591 for( const auto& rxFilterColumn : maFilterColumns )
593 // the filter settings object creates a list of filter fields
594 ApiFilterSettings aSettings = rxFilterColumn->finalizeImport( nMaxCount );
595 ApiFilterSettings::FilterFieldVector& rColumnFields = aSettings.maFilterFields;
597 // new total number of filter fields
598 sal_Int32 nNewCount = static_cast< sal_Int32 >( aFilterFields.size() + rColumnFields.size() );
600 /* Check whether mode for regular expressions is compatible with
601 the global mode in obNeedsRegExp. If either one is still in
602 don't-care state, all is fine. If both are set, they must be
603 equal. */
604 bool bRegExpCompatible = !obNeedsRegExp || !aSettings.mobNeedsRegExp || (obNeedsRegExp.get() == aSettings.mobNeedsRegExp.get());
606 // check whether fields are connected by 'or' (see comments above).
607 if( rColumnFields.size() >= 2 )
608 bHasOrConnection = std::any_of(rColumnFields.begin() + 1, rColumnFields.end(),
609 [](const css::sheet::TableFilterField3& rColumnField) { return rColumnField.Connection == FilterConnection_OR; });
611 /* Skip the column filter, if no filter fields have been created,
612 if the number of new filter fields would exceed the total limit
613 of filter fields, or if the mode for regular expressions of the
614 filter column does not fit. */
615 if( !rColumnFields.empty() && (nNewCount <= nMaxCount) && bRegExpCompatible )
617 /* Add 'and' connection to the first filter field to connect
618 it to the existing filter fields of other columns. */
619 rColumnFields[ 0 ].Connection = FilterConnection_AND;
621 // insert the new filter fields
622 aFilterFields.insert( aFilterFields.end(), rColumnFields.begin(), rColumnFields.end() );
624 // update the regular expressions mode
625 obNeedsRegExp.assignIfUsed( aSettings.mobNeedsRegExp );
628 if( bHasOrConnection )
629 break;
632 // insert all filter fields to the filter descriptor
633 if( !aFilterFields.empty() )
634 rxFilterDesc->setFilterFields3( ContainerHelper::vectorToSequence( aFilterFields ) );
636 // regular expressions
637 bool bUseRegExp = obNeedsRegExp.get( false );
638 aDescProps.setProperty( PROP_UseRegularExpressions, bUseRegExp );
642 AutoFilterBuffer::AutoFilterBuffer( const WorkbookHelper& rHelper ) :
643 WorkbookHelper( rHelper )
647 AutoFilter& AutoFilterBuffer::createAutoFilter()
649 AutoFilterVector::value_type xAutoFilter( new AutoFilter( *this ) );
650 maAutoFilters.push_back( xAutoFilter );
651 return *xAutoFilter;
654 void AutoFilterBuffer::finalizeImport( sal_Int16 nSheet )
656 // rely on existence of the defined name '_FilterDatabase' containing the range address of the filtered area
657 if( const DefinedName* pFilterDBName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_FILTERDATABASE, nSheet ).get() )
659 ScRange aFilterRange;
660 if( pFilterDBName->getAbsoluteRange( aFilterRange ) && (aFilterRange.aStart.Tab() == nSheet) )
662 // use the same name for the database range as used for the defined name '_FilterDatabase'
663 Reference< XDatabaseRange > xDatabaseRange = createUnnamedDatabaseRangeObject( aFilterRange );
664 // first, try to create an auto filter
665 bool bHasAutoFilter = finalizeImport( xDatabaseRange );
666 // no success: try to create an advanced filter
667 if( !bHasAutoFilter && xDatabaseRange.is() )
669 // the built-in defined name 'Criteria' must exist
670 if( const DefinedName* pCriteriaName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_CRITERIA, nSheet ).get() )
672 ScRange aCriteriaRange;
673 if( pCriteriaName->getAbsoluteRange( aCriteriaRange ) )
675 // set some common properties for the filter descriptor
676 PropertySet aDescProps( xDatabaseRange->getFilterDescriptor() );
677 aDescProps.setProperty( PROP_IsCaseSensitive, false );
678 aDescProps.setProperty( PROP_SkipDuplicates, false );
679 aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
680 aDescProps.setProperty( PROP_ContainsHeader, true );
681 // criteria range may contain wildcards, but these are incompatible with REs
682 aDescProps.setProperty( PROP_UseRegularExpressions, false );
684 // position of output data (if built-in defined name 'Extract' exists)
685 DefinedNameRef xExtractName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_EXTRACT, nSheet );
686 ScRange aOutputRange;
687 bool bHasOutputRange = xExtractName.get() && xExtractName->getAbsoluteRange( aOutputRange );
688 aDescProps.setProperty( PROP_CopyOutputData, bHasOutputRange );
689 if( bHasOutputRange )
691 aDescProps.setProperty( PROP_SaveOutputPosition, true );
692 aDescProps.setProperty( PROP_OutputPosition, CellAddress( aOutputRange.aStart.Tab(), aOutputRange.aStart.Col(), aOutputRange.aStart.Row() ) );
695 /* Properties of the database range (must be set after
696 modifying properties of the filter descriptor,
697 otherwise the 'FilterCriteriaSource' property gets
698 deleted). */
699 PropertySet aRangeProps( xDatabaseRange );
700 aRangeProps.setProperty( PROP_AutoFilter, false );
701 aRangeProps.setProperty( PROP_FilterCriteriaSource,
702 CellRangeAddress( aCriteriaRange.aStart.Tab(),
703 aCriteriaRange.aStart.Col(), aCriteriaRange.aStart.Row(),
704 aCriteriaRange.aEnd.Col(), aCriteriaRange.aEnd.Row() ));
712 bool AutoFilterBuffer::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRange )
714 AutoFilter* pAutoFilter = getActiveAutoFilter();
715 if( pAutoFilter && rxDatabaseRange.is() ) try
717 // the property 'AutoFilter' enables the drop-down buttons
718 PropertySet aRangeProps( rxDatabaseRange );
719 aRangeProps.setProperty( PROP_AutoFilter, true );
720 // convert filter settings using the filter descriptor of the database range
721 Reference< XSheetFilterDescriptor3 > xFilterDesc( rxDatabaseRange->getFilterDescriptor(), UNO_QUERY_THROW );
722 pAutoFilter->finalizeImport( xFilterDesc );
723 // return true to indicate enabled autofilter
724 return true;
726 catch( Exception& )
729 return false;
732 AutoFilter* AutoFilterBuffer::getActiveAutoFilter()
734 // Excel expects not more than one auto filter per sheet or table
735 OSL_ENSURE( maAutoFilters.size() <= 1, "AutoFilterBuffer::getActiveAutoFilter - too many auto filters" );
736 // stick to the last imported auto filter
737 return maAutoFilters.empty() ? nullptr : maAutoFilters.back().get();
740 } // namespace xls
741 } // namespace oox
743 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */