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/sheet/FilterConnection.hpp>
23 #include <com/sun/star/sheet/FilterOperator2.hpp>
24 #include <com/sun/star/sheet/TableFilterField3.hpp>
25 #include <com/sun/star/sheet/XDatabaseRange.hpp>
26 #include <com/sun/star/sheet/XSheetFilterDescriptor3.hpp>
27 #include <com/sun/star/table/TableOrientation.hpp>
28 #include <rtl/ustrbuf.hxx>
29 #include <osl/diagnose.h>
30 #include <oox/helper/attributelist.hxx>
31 #include <oox/helper/containerhelper.hxx>
32 #include <oox/helper/propertyset.hxx>
33 #include <oox/token/properties.hxx>
34 #include "addressconverter.hxx"
35 #include "biffinputstream.hxx"
36 #include "defnamesbuffer.hxx"
41 using namespace ::com::sun::star::sheet
;
42 using namespace ::com::sun::star::table
;
43 using namespace ::com::sun::star::uno
;
47 const sal_uInt8 BIFF12_TOP10FILTER_TOP
= 0x01;
48 const sal_uInt8 BIFF12_TOP10FILTER_PERCENT
= 0x02;
50 const sal_uInt16 BIFF12_FILTERCOLUMN_HIDDENBUTTON
= 0x0001;
51 const sal_uInt16 BIFF12_FILTERCOLUMN_SHOWBUTTON
= 0x0002;
53 const sal_uInt8 BIFF_FILTER_DATATYPE_NONE
= 0;
54 const sal_uInt8 BIFF_FILTER_DATATYPE_DOUBLE
= 4;
55 const sal_uInt8 BIFF_FILTER_DATATYPE_STRING
= 6;
56 const sal_uInt8 BIFF_FILTER_DATATYPE_BOOLEAN
= 8;
57 const sal_uInt8 BIFF_FILTER_DATATYPE_EMPTY
= 12;
58 const sal_uInt8 BIFF_FILTER_DATATYPE_NOTEMPTY
= 14;
60 bool lclGetApiOperatorFromToken( sal_Int32
& rnApiOperator
, sal_Int32 nToken
)
64 case XML_lessThan
: rnApiOperator
= FilterOperator2::NOT_EQUAL
; return true;
65 case XML_equal
: rnApiOperator
= FilterOperator2::EQUAL
; return true;
66 case XML_lessThanOrEqual
: rnApiOperator
= FilterOperator2::LESS_EQUAL
; return true;
67 case XML_greaterThan
: rnApiOperator
= FilterOperator2::GREATER
; return true;
68 case XML_notEqual
: rnApiOperator
= FilterOperator2::NOT_EQUAL
; return true;
69 case XML_greaterThanOrEqual
: rnApiOperator
= FilterOperator2::GREATER_EQUAL
; return true;
74 /** Removes leading asterisk characters from the passed string.
75 @return True = at least one asterisk character has been removed. */
76 bool lclTrimLeadingAsterisks( OUString
& rValue
)
78 sal_Int32 nLength
= rValue
.getLength();
80 while( (nPos
< nLength
) && (rValue
[ nPos
] == '*') )
84 rValue
= rValue
.copy( nPos
);
90 /** Removes trailing asterisk characters from the passed string.
91 @return True = at least one asterisk character has been removed. */
92 bool lclTrimTrailingAsterisks( OUString
& rValue
)
94 sal_Int32 nLength
= rValue
.getLength();
95 sal_Int32 nPos
= nLength
;
96 while( (nPos
> 0) && (rValue
[ nPos
- 1 ] == '*') )
100 rValue
= rValue
.copy( 0, nPos
);
106 /** Converts wildcard characters '*' and '?' to regular expressions and quotes
108 @return True = passed string has been changed (RE needs to be enabled). */
109 bool lclConvertWildcardsToRegExp( OUString
& rValue
)
111 // check existence of the wildcard characters '*' and '?'
112 if( !rValue
.isEmpty() && ((rValue
.indexOf( '*' ) >= 0) || (rValue
.indexOf( '?' ) >= 0)) )
114 OUStringBuffer aBuffer
;
115 aBuffer
.ensureCapacity( rValue
.getLength() + 5 );
116 const sal_Unicode
* pcChar
= rValue
.getStr();
117 const sal_Unicode
* pcEnd
= pcChar
+ rValue
.getLength();
118 for( ; pcChar
< pcEnd
; ++pcChar
)
123 aBuffer
.append( '.' );
126 aBuffer
.append( '.' ).append( '*' );
128 case '\\': case '.': case '|': case '(': case ')': case '^': case '$':
129 // quote RE meta characters
130 aBuffer
.append( '\\' ).append( *pcChar
);
133 aBuffer
.append( *pcChar
);
136 rValue
= aBuffer
.makeStringAndClear();
144 ApiFilterSettings::ApiFilterSettings()
148 void ApiFilterSettings::appendField( bool bAnd
, sal_Int32 nOperator
, double fValue
)
150 maFilterFields
.resize( maFilterFields
.size() + 1 );
151 TableFilterField3
& rFilterField
= maFilterFields
.back();
152 rFilterField
.Connection
= bAnd
? FilterConnection_AND
: FilterConnection_OR
;
153 rFilterField
.Operator
= nOperator
;
154 rFilterField
.Values
.realloc(1);
155 rFilterField
.Values
[0].IsNumeric
= true;
156 rFilterField
.Values
[0].NumericValue
= fValue
;
159 void ApiFilterSettings::appendField( bool bAnd
, sal_Int32 nOperator
, const OUString
& rValue
)
161 maFilterFields
.resize( maFilterFields
.size() + 1 );
162 TableFilterField3
& rFilterField
= maFilterFields
.back();
163 rFilterField
.Connection
= bAnd
? FilterConnection_AND
: FilterConnection_OR
;
164 rFilterField
.Operator
= nOperator
;
165 rFilterField
.Values
.realloc(1);
166 rFilterField
.Values
[0].IsNumeric
= false;
167 rFilterField
.Values
[0].StringValue
= rValue
;
170 void ApiFilterSettings::appendField( bool bAnd
, const std::vector
<OUString
>& rValues
)
172 maFilterFields
.resize( maFilterFields
.size() + 1 );
173 TableFilterField3
& rFilterField
= maFilterFields
.back();
174 rFilterField
.Connection
= bAnd
? FilterConnection_AND
: FilterConnection_OR
;
175 rFilterField
.Operator
= FilterOperator2::EQUAL
;
176 size_t n
= rValues
.size();
177 rFilterField
.Values
.realloc(n
);
178 for (size_t i
= 0; i
< n
; ++i
)
180 rFilterField
.Values
[i
].IsNumeric
= false;
181 rFilterField
.Values
[i
].StringValue
= rValues
[i
];
185 FilterSettingsBase::FilterSettingsBase( const WorkbookHelper
& rHelper
) :
186 WorkbookHelper( rHelper
)
190 void FilterSettingsBase::importAttribs( sal_Int32
/*nElement*/, const AttributeList
& /*rAttribs*/ )
194 void FilterSettingsBase::importRecord( sal_Int32
/*nRecId*/, SequenceInputStream
& /*rStrm*/ )
198 ApiFilterSettings
FilterSettingsBase::finalizeImport( sal_Int32
/*nMaxCount*/ )
200 return ApiFilterSettings();
203 DiscreteFilter::DiscreteFilter( const WorkbookHelper
& rHelper
) :
204 FilterSettingsBase( rHelper
),
205 mnCalendarType( XML_none
),
210 void DiscreteFilter::importAttribs( sal_Int32 nElement
, const AttributeList
& rAttribs
)
214 case XLS_TOKEN( filters
):
215 mnCalendarType
= rAttribs
.getToken( XML_calendarType
, XML_none
);
216 mbShowBlank
= rAttribs
.getBool( XML_blank
, false );
219 case XLS_TOKEN( filter
):
221 OUString aValue
= rAttribs
.getXString( XML_val
, OUString() );
222 if( !aValue
.isEmpty() )
223 maValues
.push_back( aValue
);
229 void DiscreteFilter::importRecord( sal_Int32 nRecId
, SequenceInputStream
& rStrm
)
233 case BIFF12_ID_DISCRETEFILTERS
:
235 sal_Int32 nShowBlank
, nCalendarType
;
236 nShowBlank
= rStrm
.readInt32();
237 nCalendarType
= rStrm
.readInt32();
239 static const sal_Int32 spnCalendarTypes
[] = {
240 XML_none
, XML_gregorian
, XML_gregorianUs
, XML_japan
, XML_taiwan
, XML_korea
, XML_hijri
, XML_thai
, XML_hebrew
,
241 XML_gregorianMeFrench
, XML_gregorianArabic
, XML_gregorianXlitEnglish
, XML_gregorianXlitFrench
};
242 mnCalendarType
= STATIC_ARRAY_SELECT( spnCalendarTypes
, nCalendarType
, XML_none
);
243 mbShowBlank
= nShowBlank
!= 0;
247 case BIFF12_ID_DISCRETEFILTER
:
249 OUString aValue
= BiffHelper::readString( rStrm
);
250 if( !aValue
.isEmpty() )
251 maValues
.push_back( aValue
);
257 ApiFilterSettings
DiscreteFilter::finalizeImport( sal_Int32 nMaxCount
)
259 ApiFilterSettings aSettings
;
260 if( static_cast< sal_Int32
>( maValues
.size() ) <= nMaxCount
)
262 aSettings
.maFilterFields
.reserve( maValues
.size() );
264 // insert all filter values
265 aSettings
.appendField( true, maValues
);
267 // extra field for 'show empty'
269 aSettings
.appendField( false, FilterOperator2::EMPTY
, OUString() );
271 /* Require disabled regular expressions, filter entries may contain
272 any RE meta characters. */
273 if( !maValues
.empty() )
274 aSettings
.mobNeedsRegExp
= false;
279 Top10Filter::Top10Filter( const WorkbookHelper
& rHelper
) :
280 FilterSettingsBase( rHelper
),
287 void Top10Filter::importAttribs( sal_Int32 nElement
, const AttributeList
& rAttribs
)
289 if( nElement
== XLS_TOKEN( top10
) )
291 mfValue
= rAttribs
.getDouble( XML_val
, 0.0 );
292 mbTop
= rAttribs
.getBool( XML_top
, true );
293 mbPercent
= rAttribs
.getBool( XML_percent
, false );
297 void Top10Filter::importRecord( sal_Int32 nRecId
, SequenceInputStream
& rStrm
)
299 if( nRecId
== BIFF12_ID_TOP10FILTER
)
302 nFlags
= rStrm
.readuChar();
303 mfValue
= rStrm
.readDouble();
304 mbTop
= getFlag( nFlags
, BIFF12_TOP10FILTER_TOP
);
305 mbPercent
= getFlag( nFlags
, BIFF12_TOP10FILTER_PERCENT
);
309 ApiFilterSettings
Top10Filter::finalizeImport( sal_Int32
/*nMaxCount*/ )
311 sal_Int32 nOperator
= mbTop
?
312 (mbPercent
? FilterOperator2::TOP_PERCENT
: FilterOperator2::TOP_VALUES
) :
313 (mbPercent
? FilterOperator2::BOTTOM_PERCENT
: FilterOperator2::BOTTOM_VALUES
);
314 ApiFilterSettings aSettings
;
315 aSettings
.appendField( true, nOperator
, mfValue
);
319 FilterCriterionModel::FilterCriterionModel() :
320 mnOperator( XML_equal
),
321 mnDataType( BIFF_FILTER_DATATYPE_NONE
),
326 void FilterCriterionModel::setBiffOperator( sal_uInt8 nOperator
)
328 static const sal_Int32 spnOperators
[] = { XML_TOKEN_INVALID
,
329 XML_lessThan
, XML_equal
, XML_lessThanOrEqual
, XML_greaterThan
, XML_notEqual
, XML_greaterThanOrEqual
};
330 mnOperator
= STATIC_ARRAY_SELECT( spnOperators
, nOperator
, XML_TOKEN_INVALID
);
333 void FilterCriterionModel::readBiffData( SequenceInputStream
& rStrm
)
336 mnDataType
= rStrm
.readuChar();
337 nOperator
= rStrm
.readuChar();
338 setBiffOperator( nOperator
);
342 case BIFF_FILTER_DATATYPE_DOUBLE
:
343 maValue
<<= rStrm
.readDouble();
345 case BIFF_FILTER_DATATYPE_STRING
:
348 OUString aValue
= BiffHelper::readString( rStrm
).trim();
349 if( !aValue
.isEmpty() )
353 case BIFF_FILTER_DATATYPE_BOOLEAN
:
354 maValue
<<= (rStrm
.readuInt8() != 0);
357 case BIFF_FILTER_DATATYPE_EMPTY
:
359 if( mnOperator
== XML_equal
)
360 maValue
<<= OUString();
362 case BIFF_FILTER_DATATYPE_NOTEMPTY
:
364 if( mnOperator
== XML_notEqual
)
365 maValue
<<= OUString();
368 OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unexpected data type" );
373 CustomFilter::CustomFilter( const WorkbookHelper
& rHelper
) :
374 FilterSettingsBase( rHelper
),
379 void CustomFilter::importAttribs( sal_Int32 nElement
, const AttributeList
& rAttribs
)
383 case XLS_TOKEN( customFilters
):
384 mbAnd
= rAttribs
.getBool( XML_and
, false );
387 case XLS_TOKEN( customFilter
):
389 FilterCriterionModel aCriterion
;
390 aCriterion
.mnOperator
= rAttribs
.getToken( XML_operator
, XML_equal
);
391 OUString aValue
= rAttribs
.getXString( XML_val
, OUString() ).trim();
392 if( (aCriterion
.mnOperator
== XML_equal
) || (aCriterion
.mnOperator
== XML_notEqual
) || (!aValue
.isEmpty()) )
393 aCriterion
.maValue
<<= aValue
;
394 appendCriterion( aCriterion
);
400 void CustomFilter::importRecord( sal_Int32 nRecId
, SequenceInputStream
& rStrm
)
404 case BIFF12_ID_CUSTOMFILTERS
:
405 mbAnd
= rStrm
.readInt32() == 0;
408 case BIFF12_ID_CUSTOMFILTER
:
410 FilterCriterionModel aCriterion
;
411 aCriterion
.readBiffData( rStrm
);
412 appendCriterion( aCriterion
);
418 ApiFilterSettings
CustomFilter::finalizeImport( sal_Int32
/*nMaxCount*/ )
420 ApiFilterSettings aSettings
;
421 OSL_ENSURE( maCriteria
.size() <= 2, "CustomFilter::finalizeImport - too many filter criteria" );
422 for( FilterCriterionVector::iterator aIt
= maCriteria
.begin(), aEnd
= maCriteria
.end(); aIt
!= aEnd
; ++aIt
)
424 // first extract the filter operator
425 sal_Int32 nOperator
= 0;
426 bool bValidOperator
= lclGetApiOperatorFromToken( nOperator
, aIt
->mnOperator
);
429 if( aIt
->maValue
.has
< OUString
>() )
433 aIt
->maValue
>>= aValue
;
434 // check for 'empty', 'contains', 'begins with', or 'ends with' text filters
435 bool bEqual
= nOperator
== FilterOperator2::EQUAL
;
436 bool bNotEqual
= nOperator
== FilterOperator2::NOT_EQUAL
;
437 if( bEqual
|| bNotEqual
)
439 if( aValue
.isEmpty() )
441 // empty comparison string: create empty/not empty filters
442 nOperator
= bNotEqual
? FilterOperator2::NOT_EMPTY
: FilterOperator2::EMPTY
;
446 // compare to something: try to find begins/ends/contains
447 bool bHasLeadingAsterisk
= lclTrimLeadingAsterisks( aValue
);
448 bool bHasTrailingAsterisk
= lclTrimTrailingAsterisks( aValue
);
449 // just '***' matches everything, do not create a filter field
450 bValidOperator
= !aValue
.isEmpty();
453 if( bHasLeadingAsterisk
&& bHasTrailingAsterisk
)
454 nOperator
= bNotEqual
? FilterOperator2::DOES_NOT_CONTAIN
: FilterOperator2::CONTAINS
;
455 else if( bHasLeadingAsterisk
)
456 nOperator
= bNotEqual
? FilterOperator2::DOES_NOT_END_WITH
: FilterOperator2::ENDS_WITH
;
457 else if( bHasTrailingAsterisk
)
458 nOperator
= bNotEqual
? FilterOperator2::DOES_NOT_BEGIN_WITH
: FilterOperator2::BEGINS_WITH
;
459 // else: no asterisks, stick to equal/not equal
466 // if wildcards are present, require RE mode, otherwise keep don't care state
467 if( lclConvertWildcardsToRegExp( aValue
) )
468 aSettings
.mobNeedsRegExp
= true;
469 // create a new UNO API filter field
470 aSettings
.appendField( mbAnd
, nOperator
, aValue
);
473 else if( aIt
->maValue
.has
< double >() )
475 // floating-point argument
477 aIt
->maValue
>>= fValue
;
478 aSettings
.appendField( mbAnd
, nOperator
, fValue
);
485 void CustomFilter::appendCriterion( const FilterCriterionModel
& rCriterion
)
487 if( (rCriterion
.mnOperator
!= XML_TOKEN_INVALID
) && rCriterion
.maValue
.hasValue() )
488 maCriteria
.push_back( rCriterion
);
491 FilterColumn::FilterColumn( const WorkbookHelper
& rHelper
) :
492 WorkbookHelper( rHelper
),
494 mbHiddenButton( false ),
499 void FilterColumn::importFilterColumn( const AttributeList
& rAttribs
)
501 mnColId
= rAttribs
.getInteger( XML_colId
, -1 );
502 mbHiddenButton
= rAttribs
.getBool( XML_hiddenButton
, false );
503 mbShowButton
= rAttribs
.getBool( XML_showButton
, true );
506 void FilterColumn::importFilterColumn( SequenceInputStream
& rStrm
)
509 mnColId
= rStrm
.readInt32();
510 nFlags
= rStrm
.readuInt16();
511 mbHiddenButton
= getFlag( nFlags
, BIFF12_FILTERCOLUMN_HIDDENBUTTON
);
512 mbShowButton
= getFlag( nFlags
, BIFF12_FILTERCOLUMN_SHOWBUTTON
);
515 ApiFilterSettings
FilterColumn::finalizeImport( sal_Int32 nMaxCount
)
517 ApiFilterSettings aSettings
;
518 if( (0 <= mnColId
) && mxSettings
.get() )
520 // filter settings object creates a sequence of filter fields
521 aSettings
= mxSettings
->finalizeImport( nMaxCount
);
522 // add column index to all filter fields
523 for( ApiFilterSettings::FilterFieldVector::iterator aIt
= aSettings
.maFilterFields
.begin(), aEnd
= aSettings
.maFilterFields
.end(); aIt
!= aEnd
; ++aIt
)
524 aIt
->Field
= mnColId
;
529 AutoFilter::AutoFilter( const WorkbookHelper
& rHelper
) :
530 WorkbookHelper( rHelper
)
534 void AutoFilter::importAutoFilter( const AttributeList
& rAttribs
, sal_Int16 nSheet
)
536 OUString aRangeStr
= rAttribs
.getString( XML_ref
, OUString() );
537 AddressConverter::convertToCellRangeUnchecked( maRange
, aRangeStr
, nSheet
);
540 void AutoFilter::importAutoFilter( SequenceInputStream
& rStrm
, sal_Int16 nSheet
)
544 AddressConverter::convertToCellRangeUnchecked( maRange
, aBinRange
, nSheet
);
547 FilterColumn
& AutoFilter::createFilterColumn()
549 FilterColumnVector::value_type
xFilterColumn( new FilterColumn( *this ) );
550 maFilterColumns
.push_back( xFilterColumn
);
551 return *xFilterColumn
;
554 void AutoFilter::finalizeImport( const Reference
<XSheetFilterDescriptor3
>& rxFilterDesc
)
556 if( rxFilterDesc
.is() )
558 // set some common properties for the auto filter range
559 PropertySet
aDescProps( rxFilterDesc
);
560 aDescProps
.setProperty( PROP_IsCaseSensitive
, false );
561 aDescProps
.setProperty( PROP_SkipDuplicates
, false );
562 aDescProps
.setProperty( PROP_Orientation
, TableOrientation_ROWS
);
563 aDescProps
.setProperty( PROP_ContainsHeader
, true );
564 aDescProps
.setProperty( PROP_CopyOutputData
, false );
566 // maximum number of UNO API filter fields
567 sal_Int32 nMaxCount
= 0;
568 aDescProps
.getProperty( nMaxCount
, PROP_MaxFieldCount
);
569 OSL_ENSURE( nMaxCount
> 0, "AutoFilter::finalizeImport - invalid maximum filter field count" );
571 // resulting list of all UNO API filter fields
572 ::std::vector
<TableFilterField3
> aFilterFields
;
574 // track if columns require to enable or disable regular expressions
575 OptValue
< bool > obNeedsRegExp
;
577 /* Track whether the filter fields of the first filter column are
578 connected with 'or'. In this case, other filter fields cannot be
579 inserted without altering the result of the entire filter, due to
580 Calc's precedence for the 'and' connection operator. Example:
581 Excel's filter conditions 'A1 and (B1 or B2) and C1' where B1 and
582 B2 belong to filter column B, will be evaluated by Calc as
583 '(A1 and B1) or (B2 and C1)'. */
584 bool bHasOrConnection
= false;
586 // process all filter column objects, exit when 'or' connection exists
587 for( FilterColumnVector::iterator aIt
= maFilterColumns
.begin(), aEnd
= maFilterColumns
.end(); !bHasOrConnection
&& (aIt
!= aEnd
); ++aIt
)
589 // the filter settings object creates a list of filter fields
590 ApiFilterSettings aSettings
= (*aIt
)->finalizeImport( nMaxCount
);
591 ApiFilterSettings::FilterFieldVector
& rColumnFields
= aSettings
.maFilterFields
;
593 // new total number of filter fields
594 sal_Int32 nNewCount
= static_cast< sal_Int32
>( aFilterFields
.size() + rColumnFields
.size() );
596 /* Check whether mode for regular expressions is compatible with
597 the global mode in obNeedsRegExp. If either one is still in
598 don't-care state, all is fine. If both are set, they must be
600 bool bRegExpCompatible
= !obNeedsRegExp
|| !aSettings
.mobNeedsRegExp
|| (obNeedsRegExp
.get() == aSettings
.mobNeedsRegExp
.get());
602 // check whether fields are connected by 'or' (see comments above).
603 if( rColumnFields
.size() >= 2 )
604 for( ApiFilterSettings::FilterFieldVector::iterator aSIt
= rColumnFields
.begin() + 1, aSEnd
= rColumnFields
.end(); !bHasOrConnection
&& (aSIt
!= aSEnd
); ++aSIt
)
605 bHasOrConnection
= aSIt
->Connection
== FilterConnection_OR
;
607 /* Skip the column filter, if no filter fields have been created,
608 if the number of new filter fields would exceed the total limit
609 of filter fields, or if the mode for regular expressions of the
610 filter column does not fit. */
611 if( !rColumnFields
.empty() && (nNewCount
<= nMaxCount
) && bRegExpCompatible
)
613 /* Add 'and' connection to the first filter field to connect
614 it to the existing filter fields of other columns. */
615 rColumnFields
[ 0 ].Connection
= FilterConnection_AND
;
617 // insert the new filter fields
618 aFilterFields
.insert( aFilterFields
.end(), rColumnFields
.begin(), rColumnFields
.end() );
620 // update the regular expressions mode
621 obNeedsRegExp
.assignIfUsed( aSettings
.mobNeedsRegExp
);
625 // insert all filter fields to the filter descriptor
626 if( !aFilterFields
.empty() )
627 rxFilterDesc
->setFilterFields3( ContainerHelper::vectorToSequence( aFilterFields
) );
629 // regular expressions
630 bool bUseRegExp
= obNeedsRegExp
.get( false );
631 aDescProps
.setProperty( PROP_UseRegularExpressions
, bUseRegExp
);
635 AutoFilterBuffer::AutoFilterBuffer( const WorkbookHelper
& rHelper
) :
636 WorkbookHelper( rHelper
)
640 AutoFilter
& AutoFilterBuffer::createAutoFilter()
642 AutoFilterVector::value_type
xAutoFilter( new AutoFilter( *this ) );
643 maAutoFilters
.push_back( xAutoFilter
);
647 void AutoFilterBuffer::finalizeImport( sal_Int16 nSheet
)
649 // rely on existence of the defined name '_FilterDatabase' containing the range address of the filtered area
650 if( const DefinedName
* pFilterDBName
= getDefinedNames().getByBuiltinId( BIFF_DEFNAME_FILTERDATABASE
, nSheet
).get() )
652 CellRangeAddress aFilterRange
;
653 if( pFilterDBName
->getAbsoluteRange( aFilterRange
) && (aFilterRange
.Sheet
== nSheet
) )
655 // use the same name for the database range as used for the defined name '_FilterDatabase'
656 Reference
< XDatabaseRange
> xDatabaseRange
= createUnnamedDatabaseRangeObject( aFilterRange
);
657 // first, try to create an auto filter
658 bool bHasAutoFilter
= finalizeImport( xDatabaseRange
);
659 // no success: try to create an advanced filter
660 if( !bHasAutoFilter
&& xDatabaseRange
.is() )
662 // the built-in defined name 'Criteria' must exist
663 if( const DefinedName
* pCriteriaName
= getDefinedNames().getByBuiltinId( BIFF_DEFNAME_CRITERIA
, nSheet
).get() )
665 CellRangeAddress aCriteriaRange
;
666 if( pCriteriaName
->getAbsoluteRange( aCriteriaRange
) )
668 // set some common properties for the filter descriptor
669 PropertySet
aDescProps( xDatabaseRange
->getFilterDescriptor() );
670 aDescProps
.setProperty( PROP_IsCaseSensitive
, false );
671 aDescProps
.setProperty( PROP_SkipDuplicates
, false );
672 aDescProps
.setProperty( PROP_Orientation
, TableOrientation_ROWS
);
673 aDescProps
.setProperty( PROP_ContainsHeader
, true );
674 // criteria range may contain wildcards, but these are incompatible with REs
675 aDescProps
.setProperty( PROP_UseRegularExpressions
, false );
677 // position of output data (if built-in defined name 'Extract' exists)
678 DefinedNameRef xExtractName
= getDefinedNames().getByBuiltinId( BIFF_DEFNAME_EXTRACT
, nSheet
);
679 CellRangeAddress aOutputRange
;
680 bool bHasOutputRange
= xExtractName
.get() && xExtractName
->getAbsoluteRange( aOutputRange
);
681 aDescProps
.setProperty( PROP_CopyOutputData
, bHasOutputRange
);
682 if( bHasOutputRange
)
684 aDescProps
.setProperty( PROP_SaveOutputPosition
, true );
685 aDescProps
.setProperty( PROP_OutputPosition
, CellAddress( aOutputRange
.Sheet
, aOutputRange
.StartColumn
, aOutputRange
.StartRow
) );
688 /* Properties of the database range (must be set after
689 modifying properties of the filter descriptor,
690 otherwise the 'FilterCriteriaSource' property gets
692 PropertySet
aRangeProps( xDatabaseRange
);
693 aRangeProps
.setProperty( PROP_AutoFilter
, false );
694 aRangeProps
.setProperty( PROP_FilterCriteriaSource
, aCriteriaRange
);
702 bool AutoFilterBuffer::finalizeImport( const Reference
< XDatabaseRange
>& rxDatabaseRange
)
704 AutoFilter
* pAutoFilter
= getActiveAutoFilter();
705 if( pAutoFilter
&& rxDatabaseRange
.is() ) try
707 // the property 'AutoFilter' enables the drop-down buttons
708 PropertySet
aRangeProps( rxDatabaseRange
);
709 aRangeProps
.setProperty( PROP_AutoFilter
, true );
710 // convert filter settings using the filter descriptor of the database range
711 Reference
< XSheetFilterDescriptor3
> xFilterDesc( rxDatabaseRange
->getFilterDescriptor(), UNO_QUERY_THROW
);
712 pAutoFilter
->finalizeImport( xFilterDesc
);
713 // return true to indicate enabled autofilter
722 AutoFilter
* AutoFilterBuffer::getActiveAutoFilter()
724 // Excel expects not more than one auto filter per sheet or table
725 OSL_ENSURE( maAutoFilters
.size() <= 1, "AutoFilterBuffer::getActiveAutoFilter - too many auto filters" );
726 // stick to the last imported auto filter
727 return maAutoFilters
.empty() ? 0 : maAutoFilters
.back().get();
733 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */