1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
46 using namespace ::com::sun::star::sheet
;
47 using namespace ::com::sun::star::table
;
48 using namespace ::com::sun::star::uno
;
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
)
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;
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();
85 while( (nPos
< nLength
) && (rValue
[ nPos
] == '*') )
89 rValue
= rValue
.copy( nPos
);
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 ] == '*') )
105 rValue
= rValue
.copy( 0, nPos
);
111 /** Converts wildcard characters '*' and '?' to regular expressions and quotes
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
)
128 aBuffer
.append( '.' );
131 aBuffer
.append( '.' ).append( '*' );
133 case '\\': case '.': case '|': case '(': case ')': case '^': case '$':
134 // quote RE meta characters
135 aBuffer
.append( '\\' ).append( *pcChar
);
138 aBuffer
.append( *pcChar
);
141 rValue
= aBuffer
.makeStringAndClear();
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
),
215 void DiscreteFilter::importAttribs( sal_Int32 nElement
, const AttributeList
& rAttribs
)
219 case XLS_TOKEN( filters
):
220 mnCalendarType
= rAttribs
.getToken( XML_calendarType
, XML_none
);
221 mbShowBlank
= rAttribs
.getBool( XML_blank
, false );
224 case XLS_TOKEN( filter
):
226 OUString aValue
= rAttribs
.getXString( XML_val
, OUString() );
227 if( !aValue
.isEmpty() )
228 maValues
.push_back( aValue
);
234 void DiscreteFilter::importRecord( sal_Int32 nRecId
, SequenceInputStream
& rStrm
)
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;
252 case BIFF12_ID_DISCRETEFILTER
:
254 OUString aValue
= BiffHelper::readString( rStrm
);
255 if( !aValue
.isEmpty() )
256 maValues
.push_back( aValue
);
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'
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;
284 Top10Filter::Top10Filter( const WorkbookHelper
& rHelper
) :
285 FilterSettingsBase( rHelper
),
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
)
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
);
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
)
340 mnDataType
= rStrm
.readuChar();
341 nOperator
= rStrm
.readuChar();
342 setBiffOperator( nOperator
);
346 case BIFF_FILTER_DATATYPE_DOUBLE
:
347 maValue
<<= rStrm
.readDouble();
349 case BIFF_FILTER_DATATYPE_STRING
:
352 OUString aValue
= BiffHelper::readString( rStrm
).trim();
353 if( !aValue
.isEmpty() )
357 case BIFF_FILTER_DATATYPE_BOOLEAN
:
358 maValue
<<= (rStrm
.readuInt8() != 0);
361 case BIFF_FILTER_DATATYPE_EMPTY
:
363 if( mnOperator
== XML_equal
)
364 maValue
<<= OUString();
366 case BIFF_FILTER_DATATYPE_NOTEMPTY
:
368 if( mnOperator
== XML_notEqual
)
369 maValue
<<= OUString();
372 OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unexpected data type" );
377 CustomFilter::CustomFilter( const WorkbookHelper
& rHelper
) :
378 FilterSettingsBase( rHelper
),
383 void CustomFilter::importAttribs( sal_Int32 nElement
, const AttributeList
& rAttribs
)
387 case XLS_TOKEN( customFilters
):
388 mbAnd
= rAttribs
.getBool( XML_and
, false );
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
);
404 void CustomFilter::importRecord( sal_Int32 nRecId
, SequenceInputStream
& rStrm
)
408 case BIFF12_ID_CUSTOMFILTERS
:
409 mbAnd
= rStrm
.readInt32() == 0;
412 case BIFF12_ID_CUSTOMFILTER
:
414 FilterCriterionModel aCriterion
;
415 aCriterion
.readBiffData( rStrm
);
416 appendCriterion( aCriterion
);
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
);
433 if( rCriterion
.maValue
.has
< OUString
>() )
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
;
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();
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
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
481 rCriterion
.maValue
>>= fValue
;
482 aSettings
.appendField( mbAnd
, nOperator
, fValue
);
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
),
498 mbHiddenButton( false ),
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
)
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
;
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
)
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
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
)
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
);
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
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
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();
743 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */