Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include "autofilterbuffer.hxx"
21 :
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 "oox/helper/attributelist.hxx"
30 : #include "oox/helper/containerhelper.hxx"
31 : #include "oox/helper/propertyset.hxx"
32 : #include "oox/token/properties.hxx"
33 : #include "addressconverter.hxx"
34 : #include "biffinputstream.hxx"
35 : #include "defnamesbuffer.hxx"
36 :
37 : namespace oox {
38 : namespace xls {
39 :
40 : using namespace ::com::sun::star::sheet;
41 : using namespace ::com::sun::star::table;
42 : using namespace ::com::sun::star::uno;
43 :
44 :
45 : // ============================================================================
46 :
47 : namespace {
48 :
49 : const sal_uInt8 BIFF12_TOP10FILTER_TOP = 0x01;
50 : const sal_uInt8 BIFF12_TOP10FILTER_PERCENT = 0x02;
51 :
52 : const sal_uInt16 BIFF12_FILTERCOLUMN_HIDDENBUTTON = 0x0001;
53 : const sal_uInt16 BIFF12_FILTERCOLUMN_SHOWBUTTON = 0x0002;
54 :
55 : const sal_uInt16 BIFF_FILTERCOLUMN_OR = 0x0001;
56 : const sal_uInt16 BIFF_FILTERCOLUMN_TOP10FILTER = 0x0010;
57 : const sal_uInt16 BIFF_FILTERCOLUMN_TOP = 0x0020;
58 : const sal_uInt16 BIFF_FILTERCOLUMN_PERCENT = 0x0040;
59 :
60 : const sal_uInt8 BIFF_FILTER_DATATYPE_NONE = 0;
61 : const sal_uInt8 BIFF_FILTER_DATATYPE_RK = 2;
62 : const sal_uInt8 BIFF_FILTER_DATATYPE_DOUBLE = 4;
63 : const sal_uInt8 BIFF_FILTER_DATATYPE_STRING = 6;
64 : const sal_uInt8 BIFF_FILTER_DATATYPE_BOOLEAN = 8;
65 : const sal_uInt8 BIFF_FILTER_DATATYPE_EMPTY = 12;
66 : const sal_uInt8 BIFF_FILTER_DATATYPE_NOTEMPTY = 14;
67 :
68 : // ----------------------------------------------------------------------------
69 :
70 1 : bool lclGetApiOperatorFromToken( sal_Int32& rnApiOperator, sal_Int32 nToken )
71 : {
72 1 : switch( nToken )
73 : {
74 0 : case XML_lessThan: rnApiOperator = FilterOperator2::NOT_EQUAL; return true;
75 1 : case XML_equal: rnApiOperator = FilterOperator2::EQUAL; return true;
76 0 : case XML_lessThanOrEqual: rnApiOperator = FilterOperator2::LESS_EQUAL; return true;
77 0 : case XML_greaterThan: rnApiOperator = FilterOperator2::GREATER; return true;
78 0 : case XML_notEqual: rnApiOperator = FilterOperator2::NOT_EQUAL; return true;
79 0 : case XML_greaterThanOrEqual: rnApiOperator = FilterOperator2::GREATER_EQUAL; return true;
80 : }
81 0 : return false;
82 : }
83 :
84 : /** Removes leading asterisk characters from the passed string.
85 : @return True = at least one asterisk character has been removed. */
86 1 : bool lclTrimLeadingAsterisks( OUString& rValue )
87 : {
88 1 : sal_Int32 nLength = rValue.getLength();
89 1 : sal_Int32 nPos = 0;
90 2 : while( (nPos < nLength) && (rValue[ nPos ] == '*') )
91 0 : ++nPos;
92 1 : if( nPos > 0 )
93 : {
94 0 : rValue = rValue.copy( nPos );
95 0 : return true;
96 : }
97 1 : return false;
98 : }
99 :
100 : /** Removes trailing asterisk characters from the passed string.
101 : @return True = at least one asterisk character has been removed. */
102 1 : bool lclTrimTrailingAsterisks( OUString& rValue )
103 : {
104 1 : sal_Int32 nLength = rValue.getLength();
105 1 : sal_Int32 nPos = nLength;
106 2 : while( (nPos > 0) && (rValue[ nPos - 1 ] == '*') )
107 0 : --nPos;
108 1 : if( nPos < nLength )
109 : {
110 0 : rValue = rValue.copy( 0, nPos );
111 0 : return true;
112 : }
113 1 : return false;
114 : }
115 :
116 : /** Converts wildcard characters '*' and '?' to regular expressions and quotes
117 : RE meta characters.
118 : @return True = passed string has been changed (RE needs to be enabled). */
119 1 : bool lclConvertWildcardsToRegExp( OUString& rValue )
120 : {
121 : // check existence of the wildcard characters '*' and '?'
122 1 : if( !rValue.isEmpty() && ((rValue.indexOf( '*' ) >= 0) || (rValue.indexOf( '?' ) >= 0)) )
123 : {
124 0 : OUStringBuffer aBuffer;
125 0 : aBuffer.ensureCapacity( rValue.getLength() + 5 );
126 0 : const sal_Unicode* pcChar = rValue.getStr();
127 0 : const sal_Unicode* pcEnd = pcChar + rValue.getLength();
128 0 : for( ; pcChar < pcEnd; ++pcChar )
129 : {
130 0 : switch( *pcChar )
131 : {
132 : case '?':
133 0 : aBuffer.append( sal_Unicode( '.' ) );
134 0 : break;
135 : case '*':
136 0 : aBuffer.append( sal_Unicode( '.' ) ).append( sal_Unicode( '*' ) );
137 0 : break;
138 : case '\\': case '.': case '|': case '(': case ')': case '^': case '$':
139 : // quote RE meta characters
140 0 : aBuffer.append( sal_Unicode( '\\' ) ).append( *pcChar );
141 0 : break;
142 : default:
143 0 : aBuffer.append( *pcChar );
144 : }
145 : }
146 0 : rValue = aBuffer.makeStringAndClear();
147 0 : return true;
148 : }
149 1 : return false;
150 : }
151 :
152 : } // namespace
153 :
154 : // ============================================================================
155 :
156 2 : ApiFilterSettings::ApiFilterSettings()
157 : {
158 2 : }
159 :
160 0 : void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, double fValue )
161 : {
162 0 : maFilterFields.resize( maFilterFields.size() + 1 );
163 0 : TableFilterField3& rFilterField = maFilterFields.back();
164 0 : rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
165 0 : rFilterField.Operator = nOperator;
166 0 : rFilterField.Values.realloc(1);
167 0 : rFilterField.Values[0].IsNumeric = true;
168 0 : rFilterField.Values[0].NumericValue = fValue;
169 0 : }
170 :
171 1 : void ApiFilterSettings::appendField( bool bAnd, sal_Int32 nOperator, const OUString& rValue )
172 : {
173 1 : maFilterFields.resize( maFilterFields.size() + 1 );
174 1 : TableFilterField3& rFilterField = maFilterFields.back();
175 1 : rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
176 1 : rFilterField.Operator = nOperator;
177 1 : rFilterField.Values.realloc(1);
178 1 : rFilterField.Values[0].IsNumeric = false;
179 1 : rFilterField.Values[0].StringValue = rValue;
180 1 : }
181 :
182 0 : void ApiFilterSettings::appendField( bool bAnd, const std::vector<OUString>& rValues )
183 : {
184 0 : maFilterFields.resize( maFilterFields.size() + 1 );
185 0 : TableFilterField3& rFilterField = maFilterFields.back();
186 0 : rFilterField.Connection = bAnd ? FilterConnection_AND : FilterConnection_OR;
187 0 : rFilterField.Operator = FilterOperator2::EQUAL;
188 0 : size_t n = rValues.size();
189 0 : rFilterField.Values.realloc(n);
190 0 : for (size_t i = 0; i < n; ++i)
191 : {
192 0 : rFilterField.Values[i].IsNumeric = false;
193 0 : rFilterField.Values[i].StringValue = rValues[i];
194 : }
195 0 : }
196 :
197 : // ============================================================================
198 :
199 1 : FilterSettingsBase::FilterSettingsBase( const WorkbookHelper& rHelper ) :
200 1 : WorkbookHelper( rHelper )
201 : {
202 1 : }
203 :
204 0 : void FilterSettingsBase::importAttribs( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
205 : {
206 0 : }
207 :
208 0 : void FilterSettingsBase::importRecord( sal_Int32 /*nRecId*/, SequenceInputStream& /*rStrm*/ )
209 : {
210 0 : }
211 :
212 0 : ApiFilterSettings FilterSettingsBase::finalizeImport( sal_Int32 /*nMaxCount*/ )
213 : {
214 0 : return ApiFilterSettings();
215 : }
216 :
217 : // ============================================================================
218 :
219 0 : DiscreteFilter::DiscreteFilter( const WorkbookHelper& rHelper ) :
220 : FilterSettingsBase( rHelper ),
221 : mnCalendarType( XML_none ),
222 0 : mbShowBlank( false )
223 : {
224 0 : }
225 :
226 0 : void DiscreteFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
227 : {
228 0 : switch( nElement )
229 : {
230 : case XLS_TOKEN( filters ):
231 0 : mnCalendarType = rAttribs.getToken( XML_calendarType, XML_none );
232 0 : mbShowBlank = rAttribs.getBool( XML_blank, false );
233 0 : break;
234 :
235 : case XLS_TOKEN( filter ):
236 : {
237 0 : OUString aValue = rAttribs.getXString( XML_val, OUString() );
238 0 : if( !aValue.isEmpty() )
239 0 : maValues.push_back( aValue );
240 : }
241 0 : break;
242 : }
243 0 : }
244 :
245 0 : void DiscreteFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
246 : {
247 0 : switch( nRecId )
248 : {
249 : case BIFF12_ID_DISCRETEFILTERS:
250 : {
251 : sal_Int32 nShowBlank, nCalendarType;
252 0 : rStrm >> nShowBlank >> nCalendarType;
253 :
254 : static const sal_Int32 spnCalendarTypes[] = {
255 : XML_none, XML_gregorian, XML_gregorianUs, XML_japan, XML_taiwan, XML_korea, XML_hijri, XML_thai, XML_hebrew,
256 : XML_gregorianMeFrench, XML_gregorianArabic, XML_gregorianXlitEnglish, XML_gregorianXlitFrench };
257 0 : mnCalendarType = STATIC_ARRAY_SELECT( spnCalendarTypes, nCalendarType, XML_none );
258 0 : mbShowBlank = nShowBlank != 0;
259 : }
260 0 : break;
261 :
262 : case BIFF12_ID_DISCRETEFILTER:
263 : {
264 0 : OUString aValue = BiffHelper::readString( rStrm );
265 0 : if( !aValue.isEmpty() )
266 0 : maValues.push_back( aValue );
267 : }
268 0 : break;
269 : }
270 0 : }
271 :
272 0 : ApiFilterSettings DiscreteFilter::finalizeImport( sal_Int32 nMaxCount )
273 : {
274 0 : ApiFilterSettings aSettings;
275 0 : if( static_cast< sal_Int32 >( maValues.size() ) <= nMaxCount )
276 : {
277 0 : aSettings.maFilterFields.reserve( maValues.size() );
278 :
279 : // insert all filter values
280 0 : aSettings.appendField( true, maValues );
281 :
282 : // extra field for 'show empty'
283 0 : if( mbShowBlank )
284 0 : aSettings.appendField( false, FilterOperator2::EMPTY, OUString() );
285 :
286 : /* Require disabled regular expressions, filter entries may contain
287 : any RE meta characters. */
288 0 : if( !maValues.empty() )
289 0 : aSettings.mobNeedsRegExp = false;
290 : }
291 0 : return aSettings;
292 : }
293 :
294 : // ============================================================================
295 :
296 0 : Top10Filter::Top10Filter( const WorkbookHelper& rHelper ) :
297 : FilterSettingsBase( rHelper ),
298 : mfValue( 0.0 ),
299 : mbTop( true ),
300 0 : mbPercent( false )
301 : {
302 0 : }
303 :
304 0 : void Top10Filter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
305 : {
306 0 : if( nElement == XLS_TOKEN( top10 ) )
307 : {
308 0 : mfValue = rAttribs.getDouble( XML_val, 0.0 );
309 0 : mbTop = rAttribs.getBool( XML_top, true );
310 0 : mbPercent = rAttribs.getBool( XML_percent, false );
311 : }
312 0 : }
313 :
314 0 : void Top10Filter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
315 : {
316 0 : if( nRecId == BIFF12_ID_TOP10FILTER )
317 : {
318 : sal_uInt8 nFlags;
319 0 : rStrm >> nFlags >> mfValue;
320 0 : mbTop = getFlag( nFlags, BIFF12_TOP10FILTER_TOP );
321 0 : mbPercent = getFlag( nFlags, BIFF12_TOP10FILTER_PERCENT );
322 : }
323 0 : }
324 :
325 0 : ApiFilterSettings Top10Filter::finalizeImport( sal_Int32 /*nMaxCount*/ )
326 : {
327 : sal_Int32 nOperator = mbTop ?
328 : (mbPercent ? FilterOperator2::TOP_PERCENT : FilterOperator2::TOP_VALUES) :
329 0 : (mbPercent ? FilterOperator2::BOTTOM_PERCENT : FilterOperator2::BOTTOM_VALUES);
330 0 : ApiFilterSettings aSettings;
331 0 : aSettings.appendField( true, nOperator, mfValue );
332 0 : return aSettings;
333 : }
334 :
335 : // ============================================================================
336 :
337 1 : FilterCriterionModel::FilterCriterionModel() :
338 : mnOperator( XML_equal ),
339 : mnDataType( BIFF_FILTER_DATATYPE_NONE ),
340 1 : mnStrLen( 0 )
341 : {
342 1 : }
343 :
344 0 : void FilterCriterionModel::setBiffOperator( sal_uInt8 nOperator )
345 : {
346 : static const sal_Int32 spnOperators[] = { XML_TOKEN_INVALID,
347 : XML_lessThan, XML_equal, XML_lessThanOrEqual, XML_greaterThan, XML_notEqual, XML_greaterThanOrEqual };
348 0 : mnOperator = STATIC_ARRAY_SELECT( spnOperators, nOperator, XML_TOKEN_INVALID );
349 0 : }
350 :
351 0 : void FilterCriterionModel::readBiffData( SequenceInputStream& rStrm )
352 : {
353 : sal_uInt8 nOperator;
354 0 : rStrm >> mnDataType >> nOperator;
355 0 : setBiffOperator( nOperator );
356 :
357 0 : switch( mnDataType )
358 : {
359 : case BIFF_FILTER_DATATYPE_DOUBLE:
360 0 : maValue <<= rStrm.readDouble();
361 0 : break;
362 : case BIFF_FILTER_DATATYPE_STRING:
363 : {
364 0 : rStrm.skip( 8 );
365 0 : OUString aValue = BiffHelper::readString( rStrm ).trim();
366 0 : if( !aValue.isEmpty() )
367 0 : maValue <<= aValue;
368 : }
369 0 : break;
370 : case BIFF_FILTER_DATATYPE_BOOLEAN:
371 0 : maValue <<= (rStrm.readuInt8() != 0);
372 0 : rStrm.skip( 7 );
373 0 : break;
374 : case BIFF_FILTER_DATATYPE_EMPTY:
375 0 : rStrm.skip( 8 );
376 0 : if( mnOperator == XML_equal )
377 0 : maValue <<= OUString();
378 0 : break;
379 : case BIFF_FILTER_DATATYPE_NOTEMPTY:
380 0 : rStrm.skip( 8 );
381 0 : if( mnOperator == XML_notEqual )
382 0 : maValue <<= OUString();
383 0 : break;
384 : default:
385 : OSL_ENSURE( false, "FilterCriterionModel::readBiffData - unexpected data type" );
386 0 : rStrm.skip( 8 );
387 : }
388 0 : }
389 :
390 : // ----------------------------------------------------------------------------
391 :
392 1 : CustomFilter::CustomFilter( const WorkbookHelper& rHelper ) :
393 : FilterSettingsBase( rHelper ),
394 1 : mbAnd( false )
395 : {
396 1 : }
397 :
398 2 : void CustomFilter::importAttribs( sal_Int32 nElement, const AttributeList& rAttribs )
399 : {
400 2 : switch( nElement )
401 : {
402 : case XLS_TOKEN( customFilters ):
403 1 : mbAnd = rAttribs.getBool( XML_and, false );
404 1 : break;
405 :
406 : case XLS_TOKEN( customFilter ):
407 : {
408 1 : FilterCriterionModel aCriterion;
409 1 : aCriterion.mnOperator = rAttribs.getToken( XML_operator, XML_equal );
410 2 : OUString aValue = rAttribs.getXString( XML_val, OUString() ).trim();
411 1 : if( (aCriterion.mnOperator == XML_equal) || (aCriterion.mnOperator == XML_notEqual) || (!aValue.isEmpty()) )
412 1 : aCriterion.maValue <<= aValue;
413 2 : appendCriterion( aCriterion );
414 : }
415 1 : break;
416 : }
417 2 : }
418 :
419 0 : void CustomFilter::importRecord( sal_Int32 nRecId, SequenceInputStream& rStrm )
420 : {
421 0 : switch( nRecId )
422 : {
423 : case BIFF12_ID_CUSTOMFILTERS:
424 0 : mbAnd = rStrm.readInt32() == 0;
425 0 : break;
426 :
427 : case BIFF12_ID_CUSTOMFILTER:
428 : {
429 0 : FilterCriterionModel aCriterion;
430 0 : aCriterion.readBiffData( rStrm );
431 0 : appendCriterion( aCriterion );
432 : }
433 0 : break;
434 : }
435 0 : }
436 :
437 1 : ApiFilterSettings CustomFilter::finalizeImport( sal_Int32 /*nMaxCount*/ )
438 : {
439 1 : ApiFilterSettings aSettings;
440 : OSL_ENSURE( maCriteria.size() <= 2, "CustomFilter::finalizeImport - too many filter criteria" );
441 2 : for( FilterCriterionVector::iterator aIt = maCriteria.begin(), aEnd = maCriteria.end(); aIt != aEnd; ++aIt )
442 : {
443 : // first extract the filter operator
444 1 : sal_Int32 nOperator = 0;
445 1 : bool bValidOperator = lclGetApiOperatorFromToken( nOperator, aIt->mnOperator );
446 1 : if( bValidOperator )
447 : {
448 1 : if( aIt->maValue.has< OUString >() )
449 : {
450 : // string argument
451 1 : OUString aValue;
452 1 : aIt->maValue >>= aValue;
453 : // check for 'empty', 'contains', 'begins with', or 'ends with' text filters
454 1 : bool bEqual = nOperator == FilterOperator2::EQUAL;
455 1 : bool bNotEqual = nOperator == FilterOperator2::NOT_EQUAL;
456 1 : if( bEqual || bNotEqual )
457 : {
458 1 : if( aValue.isEmpty() )
459 : {
460 : // empty comparison string: create empty/not empty filters
461 0 : nOperator = bNotEqual ? FilterOperator2::NOT_EMPTY : FilterOperator2::EMPTY;
462 : }
463 : else
464 : {
465 : // compare to something: try to find begins/ends/contains
466 1 : bool bHasLeadingAsterisk = lclTrimLeadingAsterisks( aValue );
467 1 : bool bHasTrailingAsterisk = lclTrimTrailingAsterisks( aValue );
468 : // just '***' matches everything, do not create a filter field
469 1 : bValidOperator = !aValue.isEmpty();
470 1 : if( bValidOperator )
471 : {
472 1 : if( bHasLeadingAsterisk && bHasTrailingAsterisk )
473 0 : nOperator = bNotEqual ? FilterOperator2::DOES_NOT_CONTAIN : FilterOperator2::CONTAINS;
474 1 : else if( bHasLeadingAsterisk )
475 0 : nOperator = bNotEqual ? FilterOperator2::DOES_NOT_END_WITH : FilterOperator2::ENDS_WITH;
476 1 : else if( bHasTrailingAsterisk )
477 0 : nOperator = bNotEqual ? FilterOperator2::DOES_NOT_BEGIN_WITH : FilterOperator2::BEGINS_WITH;
478 : // else: no asterisks, stick to equal/not equal
479 : }
480 : }
481 : }
482 :
483 1 : if( bValidOperator )
484 : {
485 : // if wildcards are present, require RE mode, otherwise keep don't care state
486 1 : if( lclConvertWildcardsToRegExp( aValue ) )
487 0 : aSettings.mobNeedsRegExp = true;
488 : // create a new UNO API filter field
489 1 : aSettings.appendField( mbAnd, nOperator, aValue );
490 1 : }
491 : }
492 0 : else if( aIt->maValue.has< double >() )
493 : {
494 : // floating-point argument
495 0 : double fValue = 0.0;
496 0 : aIt->maValue >>= fValue;
497 0 : aSettings.appendField( mbAnd, nOperator, fValue );
498 : }
499 : }
500 : }
501 1 : return aSettings;
502 : }
503 :
504 1 : void CustomFilter::appendCriterion( const FilterCriterionModel& rCriterion )
505 : {
506 1 : if( (rCriterion.mnOperator != XML_TOKEN_INVALID) && rCriterion.maValue.hasValue() )
507 1 : maCriteria.push_back( rCriterion );
508 1 : }
509 :
510 : // ============================================================================
511 :
512 1 : FilterColumn::FilterColumn( const WorkbookHelper& rHelper ) :
513 : WorkbookHelper( rHelper ),
514 : mnColId( -1 ),
515 : mbHiddenButton( false ),
516 1 : mbShowButton( true )
517 : {
518 1 : }
519 :
520 1 : void FilterColumn::importFilterColumn( const AttributeList& rAttribs )
521 : {
522 1 : mnColId = rAttribs.getInteger( XML_colId, -1 );
523 1 : mbHiddenButton = rAttribs.getBool( XML_hiddenButton, false );
524 1 : mbShowButton = rAttribs.getBool( XML_showButton, true );
525 1 : }
526 :
527 0 : void FilterColumn::importFilterColumn( SequenceInputStream& rStrm )
528 : {
529 : sal_uInt16 nFlags;
530 0 : rStrm >> mnColId >> nFlags;
531 0 : mbHiddenButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_HIDDENBUTTON );
532 0 : mbShowButton = getFlag( nFlags, BIFF12_FILTERCOLUMN_SHOWBUTTON );
533 0 : }
534 :
535 1 : ApiFilterSettings FilterColumn::finalizeImport( sal_Int32 nMaxCount )
536 : {
537 1 : ApiFilterSettings aSettings;
538 1 : if( (0 <= mnColId) && mxSettings.get() )
539 : {
540 : // filter settings object creates a sequence of filter fields
541 1 : aSettings = mxSettings->finalizeImport( nMaxCount );
542 : // add column index to all filter fields
543 2 : for( ApiFilterSettings::FilterFieldVector::iterator aIt = aSettings.maFilterFields.begin(), aEnd = aSettings.maFilterFields.end(); aIt != aEnd; ++aIt )
544 1 : aIt->Field = mnColId;
545 : }
546 1 : return aSettings;
547 : }
548 :
549 : // ============================================================================
550 :
551 1 : AutoFilter::AutoFilter( const WorkbookHelper& rHelper ) :
552 1 : WorkbookHelper( rHelper )
553 : {
554 1 : }
555 :
556 1 : void AutoFilter::importAutoFilter( const AttributeList& rAttribs, sal_Int16 nSheet )
557 : {
558 1 : OUString aRangeStr = rAttribs.getString( XML_ref, OUString() );
559 1 : getAddressConverter().convertToCellRangeUnchecked( maRange, aRangeStr, nSheet );
560 1 : }
561 :
562 0 : void AutoFilter::importAutoFilter( SequenceInputStream& rStrm, sal_Int16 nSheet )
563 : {
564 0 : BinRange aBinRange;
565 0 : rStrm >> aBinRange;
566 0 : getAddressConverter().convertToCellRangeUnchecked( maRange, aBinRange, nSheet );
567 0 : }
568 :
569 1 : FilterColumn& AutoFilter::createFilterColumn()
570 : {
571 1 : FilterColumnVector::value_type xFilterColumn( new FilterColumn( *this ) );
572 1 : maFilterColumns.push_back( xFilterColumn );
573 1 : return *xFilterColumn;
574 : }
575 :
576 1 : void AutoFilter::finalizeImport( const Reference<XSheetFilterDescriptor3>& rxFilterDesc )
577 : {
578 1 : if( rxFilterDesc.is() )
579 : {
580 : // set some common properties for the auto filter range
581 1 : PropertySet aDescProps( rxFilterDesc );
582 1 : aDescProps.setProperty( PROP_IsCaseSensitive, false );
583 1 : aDescProps.setProperty( PROP_SkipDuplicates, false );
584 1 : aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
585 1 : aDescProps.setProperty( PROP_ContainsHeader, true );
586 1 : aDescProps.setProperty( PROP_CopyOutputData, false );
587 :
588 : // maximum number of UNO API filter fields
589 1 : sal_Int32 nMaxCount = 0;
590 1 : aDescProps.getProperty( nMaxCount, PROP_MaxFieldCount );
591 : OSL_ENSURE( nMaxCount > 0, "AutoFilter::finalizeImport - invalid maximum filter field count" );
592 :
593 : // resulting list of all UNO API filter fields
594 2 : ::std::vector<TableFilterField3> aFilterFields;
595 :
596 : // track if columns require to enable or disable regular expressions
597 1 : OptValue< bool > obNeedsRegExp;
598 :
599 : /* Track whether the filter fields of the first filter column are
600 : connected with 'or'. In this case, other filter fields cannot be
601 : inserted without altering the result of the entire filter, due to
602 : Calc's precedence for the 'and' connection operator. Example:
603 : Excel's filter conditions 'A1 and (B1 or B2) and C1' where B1 and
604 : B2 belong to filter column B, will be evaluated by Calc as
605 : '(A1 and B1) or (B2 and C1)'. */
606 1 : bool bHasOrConnection = false;
607 :
608 : // process all filter column objects, exit when 'or' connection exists
609 2 : for( FilterColumnVector::iterator aIt = maFilterColumns.begin(), aEnd = maFilterColumns.end(); !bHasOrConnection && (aIt != aEnd); ++aIt )
610 : {
611 : // the filter settings object creates a list of filter fields
612 1 : ApiFilterSettings aSettings = (*aIt)->finalizeImport( nMaxCount );
613 1 : ApiFilterSettings::FilterFieldVector& rColumnFields = aSettings.maFilterFields;
614 :
615 : // new total number of filter fields
616 1 : sal_Int32 nNewCount = static_cast< sal_Int32 >( aFilterFields.size() + rColumnFields.size() );
617 :
618 : /* Check whether mode for regular expressions is compatible with
619 : the global mode in obNeedsRegExp. If either one is still in
620 : don't-care state, all is fine. If both are set, they must be
621 : equal. */
622 1 : bool bRegExpCompatible = !obNeedsRegExp || !aSettings.mobNeedsRegExp || (obNeedsRegExp.get() == aSettings.mobNeedsRegExp.get());
623 :
624 : // check whether fields are connected by 'or' (see comments above).
625 1 : if( rColumnFields.size() >= 2 )
626 0 : for( ApiFilterSettings::FilterFieldVector::iterator aSIt = rColumnFields.begin() + 1, aSEnd = rColumnFields.end(); !bHasOrConnection && (aSIt != aSEnd); ++aSIt )
627 0 : bHasOrConnection = aSIt->Connection == FilterConnection_OR;
628 :
629 : /* Skip the column filter, if no filter fields have been created,
630 : if the number of new filter fields would exceed the total limit
631 : of filter fields, or if the mode for regular expressions of the
632 : filter column does not fit. */
633 1 : if( !rColumnFields.empty() && (nNewCount <= nMaxCount) && bRegExpCompatible )
634 : {
635 : /* Add 'and' connection to the first filter field to connect
636 : it to the existing filter fields of other columns. */
637 1 : rColumnFields[ 0 ].Connection = FilterConnection_AND;
638 :
639 : // insert the new filter fields
640 1 : aFilterFields.insert( aFilterFields.end(), rColumnFields.begin(), rColumnFields.end() );
641 :
642 : // update the regular expressions mode
643 1 : obNeedsRegExp.assignIfUsed( aSettings.mobNeedsRegExp );
644 : }
645 1 : }
646 :
647 : // insert all filter fields to the filter descriptor
648 1 : if( !aFilterFields.empty() )
649 1 : rxFilterDesc->setFilterFields3( ContainerHelper::vectorToSequence( aFilterFields ) );
650 :
651 : // regular expressions
652 1 : bool bUseRegExp = obNeedsRegExp.get( false );
653 2 : aDescProps.setProperty( PROP_UseRegularExpressions, bUseRegExp );
654 : }
655 1 : }
656 :
657 : // ============================================================================
658 :
659 43 : AutoFilterBuffer::AutoFilterBuffer( const WorkbookHelper& rHelper ) :
660 43 : WorkbookHelper( rHelper )
661 : {
662 43 : }
663 :
664 1 : AutoFilter& AutoFilterBuffer::createAutoFilter()
665 : {
666 1 : AutoFilterVector::value_type xAutoFilter( new AutoFilter( *this ) );
667 1 : maAutoFilters.push_back( xAutoFilter );
668 1 : return *xAutoFilter;
669 : }
670 :
671 43 : void AutoFilterBuffer::finalizeImport( sal_Int16 nSheet )
672 : {
673 : // rely on existence of the defined name '_FilterDatabase' containing the range address of the filtered area
674 43 : if( const DefinedName* pFilterDBName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_FILTERDATABASE, nSheet ).get() )
675 : {
676 1 : CellRangeAddress aFilterRange;
677 1 : if( pFilterDBName->getAbsoluteRange( aFilterRange ) && (aFilterRange.Sheet == nSheet) )
678 : {
679 : // use the same name for the database range as used for the defined name '_FilterDatabase'
680 1 : Reference< XDatabaseRange > xDatabaseRange = createUnnamedDatabaseRangeObject( aFilterRange );
681 : // first, try to create an auto filter
682 1 : bool bHasAutoFilter = finalizeImport( xDatabaseRange );
683 : // no success: try to create an advanced filter
684 1 : if( !bHasAutoFilter && xDatabaseRange.is() )
685 : {
686 : // the built-in defined name 'Criteria' must exist
687 0 : if( const DefinedName* pCriteriaName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_CRITERIA, nSheet ).get() )
688 : {
689 0 : CellRangeAddress aCriteriaRange;
690 0 : if( pCriteriaName->getAbsoluteRange( aCriteriaRange ) )
691 : {
692 : // set some common properties for the filter descriptor
693 0 : PropertySet aDescProps( xDatabaseRange->getFilterDescriptor() );
694 0 : aDescProps.setProperty( PROP_IsCaseSensitive, false );
695 0 : aDescProps.setProperty( PROP_SkipDuplicates, false );
696 0 : aDescProps.setProperty( PROP_Orientation, TableOrientation_ROWS );
697 0 : aDescProps.setProperty( PROP_ContainsHeader, true );
698 : // criteria range may contain wildcards, but these are incompatible with REs
699 0 : aDescProps.setProperty( PROP_UseRegularExpressions, false );
700 :
701 : // position of output data (if built-in defined name 'Extract' exists)
702 0 : DefinedNameRef xExtractName = getDefinedNames().getByBuiltinId( BIFF_DEFNAME_EXTRACT, nSheet );
703 0 : CellRangeAddress aOutputRange;
704 0 : bool bHasOutputRange = xExtractName.get() && xExtractName->getAbsoluteRange( aOutputRange );
705 0 : aDescProps.setProperty( PROP_CopyOutputData, bHasOutputRange );
706 0 : if( bHasOutputRange )
707 : {
708 0 : aDescProps.setProperty( PROP_SaveOutputPosition, true );
709 0 : aDescProps.setProperty( PROP_OutputPosition, CellAddress( aOutputRange.Sheet, aOutputRange.StartColumn, aOutputRange.StartRow ) );
710 : }
711 :
712 : /* Properties of the database range (must be set after
713 : modifying properties of the filter descriptor,
714 : otherwise the 'FilterCriteriaSource' property gets
715 : deleted). */
716 0 : PropertySet aRangeProps( xDatabaseRange );
717 0 : aRangeProps.setProperty( PROP_AutoFilter, false );
718 0 : aRangeProps.setProperty( PROP_FilterCriteriaSource, aCriteriaRange );
719 : }
720 : }
721 1 : }
722 : }
723 : }
724 43 : }
725 :
726 1 : bool AutoFilterBuffer::finalizeImport( const Reference< XDatabaseRange >& rxDatabaseRange )
727 : {
728 1 : AutoFilter* pAutoFilter = getActiveAutoFilter();
729 1 : if( pAutoFilter && rxDatabaseRange.is() ) try
730 : {
731 : // the property 'AutoFilter' enables the drop-down buttons
732 1 : PropertySet aRangeProps( rxDatabaseRange );
733 1 : aRangeProps.setProperty( PROP_AutoFilter, true );
734 : // convert filter settings using the filter descriptor of the database range
735 2 : Reference< XSheetFilterDescriptor3 > xFilterDesc( rxDatabaseRange->getFilterDescriptor(), UNO_QUERY_THROW );
736 1 : pAutoFilter->finalizeImport( xFilterDesc );
737 : // return true to indicate enabled autofilter
738 2 : return true;
739 : }
740 0 : catch( Exception& )
741 : {
742 : }
743 0 : return false;
744 : }
745 :
746 1 : AutoFilter* AutoFilterBuffer::getActiveAutoFilter()
747 : {
748 : // Excel expects not more than one auto filter per sheet or table
749 : OSL_ENSURE( maAutoFilters.size() <= 1, "AutoFilterBuffer::getActiveAutoFilter - too many auto filters" );
750 : // stick to the last imported auto filter
751 1 : return maAutoFilters.empty() ? 0 : maAutoFilters.back().get();
752 : }
753 :
754 : // ============================================================================
755 :
756 : } // namespace xls
757 15 : } // namespace oox
758 :
759 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|