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