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 "xepivot.hxx"
21 : #include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp>
22 : #include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp>
23 : #include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp>
24 : #include <com/sun/star/sheet/DataPilotFieldReference.hpp>
25 : #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
26 :
27 : #include <algorithm>
28 : #include <math.h>
29 :
30 : #include <rtl/math.hxx>
31 : #include <tools/date.hxx>
32 : #include <svl/zformat.hxx>
33 : #include <sot/storage.hxx>
34 : #include "document.hxx"
35 : #include "dpobject.hxx"
36 : #include "dpsave.hxx"
37 : #include "dpdimsave.hxx"
38 : #include "dpshttab.hxx"
39 : #include "globstr.hrc"
40 : #include "fapihelper.hxx"
41 : #include "xestring.hxx"
42 : #include "xelink.hxx"
43 : #include "dputil.hxx"
44 :
45 : using namespace ::oox;
46 :
47 : using ::com::sun::star::sheet::DataPilotFieldOrientation;
48 : using ::com::sun::star::sheet::DataPilotFieldOrientation_HIDDEN;
49 : using ::com::sun::star::sheet::DataPilotFieldOrientation_ROW;
50 : using ::com::sun::star::sheet::DataPilotFieldOrientation_COLUMN;
51 : using ::com::sun::star::sheet::DataPilotFieldOrientation_PAGE;
52 : using ::com::sun::star::sheet::DataPilotFieldOrientation_DATA;
53 : using ::com::sun::star::sheet::GeneralFunction;
54 : using ::com::sun::star::sheet::DataPilotFieldSortInfo;
55 : using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
56 : using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
57 : using ::com::sun::star::sheet::DataPilotFieldReference;
58 :
59 : // Pivot cache
60 :
61 : namespace {
62 :
63 : // constants to track occurrence of specific data types
64 : const sal_uInt16 EXC_PCITEM_DATA_STRING = 0x0001; /// String, empty, boolean, error.
65 : const sal_uInt16 EXC_PCITEM_DATA_DOUBLE = 0x0002; /// Double with fraction.
66 : const sal_uInt16 EXC_PCITEM_DATA_INTEGER = 0x0004; /// Integer, double without fraction.
67 : const sal_uInt16 EXC_PCITEM_DATA_DATE = 0x0008; /// Date, time, date/time.
68 :
69 : /** Maps a bitfield consisting of EXC_PCITEM_DATA_* flags above to SXFIELD data type bitfield. */
70 : static const sal_uInt16 spnPCItemFlags[] =
71 : { // STR DBL INT DAT
72 : EXC_SXFIELD_DATA_NONE,
73 : EXC_SXFIELD_DATA_STR, // x
74 : EXC_SXFIELD_DATA_INT, // x
75 : EXC_SXFIELD_DATA_STR_INT, // x x
76 : EXC_SXFIELD_DATA_DBL, // x
77 : EXC_SXFIELD_DATA_STR_DBL, // x x
78 : EXC_SXFIELD_DATA_INT, // x x
79 : EXC_SXFIELD_DATA_STR_INT, // x x x
80 : EXC_SXFIELD_DATA_DATE, // x
81 : EXC_SXFIELD_DATA_DATE_STR, // x x
82 : EXC_SXFIELD_DATA_DATE_NUM, // x x
83 : EXC_SXFIELD_DATA_DATE_STR, // x x x
84 : EXC_SXFIELD_DATA_DATE_NUM, // x x
85 : EXC_SXFIELD_DATA_DATE_STR, // x x x
86 : EXC_SXFIELD_DATA_DATE_NUM, // x x x
87 : EXC_SXFIELD_DATA_DATE_STR // x x x x
88 : };
89 :
90 : } // namespace
91 :
92 0 : XclExpPCItem::XclExpPCItem( const OUString& rText ) :
93 0 : XclExpRecord( (!rText.isEmpty()) ? EXC_ID_SXSTRING : EXC_ID_SXEMPTY, 0 ),
94 0 : mnTypeFlag( EXC_PCITEM_DATA_STRING )
95 : {
96 0 : if( !rText.isEmpty() )
97 0 : SetText( rText );
98 : else
99 0 : SetEmpty();
100 0 : }
101 :
102 0 : XclExpPCItem::XclExpPCItem( double fValue ) :
103 0 : XclExpRecord( EXC_ID_SXDOUBLE, 8 )
104 : {
105 0 : SetDouble( fValue );
106 0 : mnTypeFlag = (fValue - floor( fValue ) == 0.0) ?
107 0 : EXC_PCITEM_DATA_INTEGER : EXC_PCITEM_DATA_DOUBLE;
108 0 : }
109 :
110 0 : XclExpPCItem::XclExpPCItem( const DateTime& rDateTime ) :
111 0 : XclExpRecord( EXC_ID_SXDATETIME, 8 )
112 : {
113 0 : SetDateTime( rDateTime );
114 0 : mnTypeFlag = EXC_PCITEM_DATA_DATE;
115 0 : }
116 :
117 0 : XclExpPCItem::XclExpPCItem( sal_Int16 nValue ) :
118 : XclExpRecord( EXC_ID_SXINTEGER, 2 ),
119 0 : mnTypeFlag( EXC_PCITEM_DATA_INTEGER )
120 : {
121 0 : SetInteger( nValue );
122 0 : }
123 :
124 0 : XclExpPCItem::XclExpPCItem( bool bValue ) :
125 : XclExpRecord( EXC_ID_SXBOOLEAN, 2 ),
126 0 : mnTypeFlag( EXC_PCITEM_DATA_STRING )
127 : {
128 0 : SetBool( bValue );
129 0 : }
130 :
131 0 : bool XclExpPCItem::EqualsText( const OUString& rText ) const
132 : {
133 0 : return rText.isEmpty() ? IsEmpty() : (GetText() && (*GetText() == rText));
134 : }
135 :
136 0 : bool XclExpPCItem::EqualsDouble( double fValue ) const
137 : {
138 0 : return GetDouble() && (*GetDouble() == fValue);
139 : }
140 :
141 0 : bool XclExpPCItem::EqualsDateTime( const DateTime& rDateTime ) const
142 : {
143 0 : return GetDateTime() && (*GetDateTime() == rDateTime);
144 : }
145 :
146 0 : bool XclExpPCItem::EqualsBool( bool bValue ) const
147 : {
148 0 : return GetBool() && (*GetBool() == bValue);
149 : }
150 :
151 0 : void XclExpPCItem::WriteBody( XclExpStream& rStrm )
152 : {
153 0 : if( const OUString* pText = GetText() )
154 : {
155 0 : rStrm << XclExpString( *pText );
156 : }
157 0 : else if( const double* pfValue = GetDouble() )
158 : {
159 0 : rStrm << *pfValue;
160 : }
161 0 : else if( const sal_Int16* pnValue = GetInteger() )
162 : {
163 0 : rStrm << *pnValue;
164 : }
165 0 : else if( const DateTime* pDateTime = GetDateTime() )
166 : {
167 0 : sal_uInt16 nYear = static_cast< sal_uInt16 >( pDateTime->GetYear() );
168 0 : sal_uInt16 nMonth = static_cast< sal_uInt16 >( pDateTime->GetMonth() );
169 0 : sal_uInt8 nDay = static_cast< sal_uInt8 >( pDateTime->GetDay() );
170 0 : sal_uInt8 nHour = static_cast< sal_uInt8 >( pDateTime->GetHour() );
171 0 : sal_uInt8 nMin = static_cast< sal_uInt8 >( pDateTime->GetMin() );
172 0 : sal_uInt8 nSec = static_cast< sal_uInt8 >( pDateTime->GetSec() );
173 0 : if( nYear < 1900 ) { nYear = 1900; nMonth = 1; nDay = 0; }
174 0 : rStrm << nYear << nMonth << nDay << nHour << nMin << nSec;
175 : }
176 0 : else if( const bool* pbValue = GetBool() )
177 : {
178 0 : rStrm << static_cast< sal_uInt16 >( *pbValue ? 1 : 0 );
179 : }
180 : else
181 : {
182 : // nothing to do for SXEMPTY
183 : OSL_ENSURE( IsEmpty(), "XclExpPCItem::WriteBody - no data found" );
184 : }
185 0 : }
186 :
187 0 : XclExpPCField::XclExpPCField(
188 : const XclExpRoot& rRoot, const XclExpPivotCache& rPCache, sal_uInt16 nFieldIdx,
189 : const ScDPObject& rDPObj, const ScRange& rRange ) :
190 : XclExpRecord( EXC_ID_SXFIELD ),
191 : XclPCField( EXC_PCFIELD_STANDARD, nFieldIdx ),
192 : XclExpRoot( rRoot ),
193 : mrPCache( rPCache ),
194 0 : mnTypeFlags( 0 )
195 : {
196 : // general settings for the standard field, insert all items from source range
197 0 : InitStandardField( rRange );
198 :
199 : // add special settings for inplace numeric grouping
200 0 : if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
201 : {
202 0 : if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
203 : {
204 0 : if( const ScDPSaveNumGroupDimension* pNumGroupDim = pSaveDimData->GetNumGroupDim( GetFieldName() ) )
205 : {
206 0 : const ScDPNumGroupInfo& rNumInfo = pNumGroupDim->GetInfo();
207 0 : const ScDPNumGroupInfo& rDateInfo = pNumGroupDim->GetDateInfo();
208 : OSL_ENSURE( !rNumInfo.mbEnable || !rDateInfo.mbEnable,
209 : "XclExpPCField::XclExpPCField - numeric and date grouping enabled" );
210 :
211 0 : if( rNumInfo.mbEnable )
212 0 : InitNumGroupField( rDPObj, rNumInfo );
213 0 : else if( rDateInfo.mbEnable )
214 0 : InitDateGroupField( rDPObj, rDateInfo, pNumGroupDim->GetDatePart() );
215 : }
216 : }
217 : }
218 :
219 : // final settings (flags, item numbers)
220 0 : Finalize();
221 0 : }
222 :
223 0 : XclExpPCField::XclExpPCField(
224 : const XclExpRoot& rRoot, const XclExpPivotCache& rPCache, sal_uInt16 nFieldIdx,
225 : const ScDPObject& rDPObj, const ScDPSaveGroupDimension& rGroupDim, const XclExpPCField& rBaseField ) :
226 : XclExpRecord( EXC_ID_SXFIELD ),
227 : XclPCField( EXC_PCFIELD_STDGROUP, nFieldIdx ),
228 : XclExpRoot( rRoot ),
229 : mrPCache( rPCache ),
230 0 : mnTypeFlags( 0 )
231 : {
232 : // add base field info (always using first base field, not predecessor of this field) ***
233 : OSL_ENSURE( rBaseField.GetFieldName() == rGroupDim.GetSourceDimName(),
234 : "XclExpPCField::FillFromGroup - wrong base cache field" );
235 0 : maFieldInfo.maName = rGroupDim.GetGroupDimName();
236 0 : maFieldInfo.mnGroupBase = rBaseField.GetFieldIndex();
237 :
238 : // add standard group info or date group info
239 0 : const ScDPNumGroupInfo& rDateInfo = rGroupDim.GetDateInfo();
240 0 : if( rDateInfo.mbEnable && (rGroupDim.GetDatePart() != 0) )
241 0 : InitDateGroupField( rDPObj, rDateInfo, rGroupDim.GetDatePart() );
242 : else
243 0 : InitStdGroupField( rBaseField, rGroupDim );
244 :
245 : // final settings (flags, item numbers)
246 0 : Finalize();
247 0 : }
248 :
249 0 : XclExpPCField::~XclExpPCField()
250 : {
251 0 : }
252 :
253 0 : void XclExpPCField::SetGroupChildField( const XclExpPCField& rChildField )
254 : {
255 : OSL_ENSURE( !::get_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD ),
256 : "XclExpPCField::SetGroupChildIndex - field already has a grouping child field" );
257 0 : ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASCHILD );
258 0 : maFieldInfo.mnGroupChild = rChildField.GetFieldIndex();
259 0 : }
260 :
261 0 : sal_uInt16 XclExpPCField::GetItemCount() const
262 : {
263 0 : return static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
264 : }
265 :
266 0 : const XclExpPCItem* XclExpPCField::GetItem( sal_uInt16 nItemIdx ) const
267 : {
268 0 : return GetVisItemList().GetRecord( nItemIdx ).get();
269 : }
270 :
271 0 : sal_uInt16 XclExpPCField::GetItemIndex( const OUString& rItemName ) const
272 : {
273 0 : const XclExpPCItemList& rItemList = GetVisItemList();
274 0 : for( size_t nPos = 0, nSize = rItemList.GetSize(); nPos < nSize; ++nPos )
275 0 : if( rItemList.GetRecord( nPos )->ConvertToText() == rItemName )
276 0 : return static_cast< sal_uInt16 >( nPos );
277 0 : return EXC_PC_NOITEM;
278 : }
279 :
280 0 : sal_Size XclExpPCField::GetIndexSize() const
281 : {
282 0 : return Has16BitIndexes() ? 2 : 1;
283 : }
284 :
285 0 : void XclExpPCField::WriteIndex( XclExpStream& rStrm, sal_uInt32 nSrcRow ) const
286 : {
287 : // only standard fields write item indexes
288 0 : if( nSrcRow < maIndexVec.size() )
289 : {
290 0 : sal_uInt16 nIndex = maIndexVec[ nSrcRow ];
291 0 : if( Has16BitIndexes() )
292 0 : rStrm << nIndex;
293 : else
294 0 : rStrm << static_cast< sal_uInt8 >( nIndex );
295 : }
296 0 : }
297 :
298 0 : void XclExpPCField::Save( XclExpStream& rStrm )
299 : {
300 : OSL_ENSURE( IsSupportedField(), "XclExpPCField::Save - unknown field type" );
301 : // SXFIELD
302 0 : XclExpRecord::Save( rStrm );
303 : // SXFDBTYPE
304 0 : XclExpUInt16Record( EXC_ID_SXFDBTYPE, EXC_SXFDBTYPE_DEFAULT ).Save( rStrm );
305 : // list of grouping items
306 0 : maGroupItemList.Save( rStrm );
307 : // SXGROUPINFO
308 0 : WriteSxgroupinfo( rStrm );
309 : // SXNUMGROUP and additional grouping items (grouping limit settings)
310 0 : WriteSxnumgroup( rStrm );
311 : // list of original items
312 0 : maOrigItemList.Save( rStrm );
313 0 : }
314 :
315 : // private --------------------------------------------------------------------
316 :
317 0 : const XclExpPCField::XclExpPCItemList& XclExpPCField::GetVisItemList() const
318 : {
319 : OSL_ENSURE( IsStandardField() == maGroupItemList.IsEmpty(),
320 : "XclExpPCField::GetVisItemList - unexpected additional items in standard field" );
321 0 : return IsStandardField() ? maOrigItemList : maGroupItemList;
322 : }
323 :
324 0 : void XclExpPCField::InitStandardField( const ScRange& rRange )
325 : {
326 : OSL_ENSURE( IsStandardField(), "XclExpPCField::InitStandardField - only for standard fields" );
327 : OSL_ENSURE( rRange.aStart.Col() == rRange.aEnd.Col(), "XclExpPCField::InitStandardField - cell range with multiple columns" );
328 :
329 0 : ScDocument& rDoc = GetDoc();
330 0 : SvNumberFormatter& rFormatter = GetFormatter();
331 :
332 : // field name is in top cell of the range
333 0 : ScAddress aPos( rRange.aStart );
334 0 : maFieldInfo.maName = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
335 : // #i76047# maximum field name length in pivot cache is 255
336 0 : if (maFieldInfo.maName.getLength() > EXC_PC_MAXSTRLEN)
337 0 : maFieldInfo.maName = maFieldInfo.maName.copy(0, EXC_PC_MAXSTRLEN);
338 :
339 : // loop over all cells, create pivot cache items
340 0 : for( aPos.IncRow(); (aPos.Row() <= rRange.aEnd.Row()) && (maOrigItemList.GetSize() < EXC_PC_MAXITEMCOUNT); aPos.IncRow() )
341 : {
342 0 : if( rDoc.HasValueData( aPos.Col(), aPos.Row(), aPos.Tab() ) )
343 : {
344 0 : double fValue = rDoc.GetValue( aPos );
345 0 : short nFmtType = rFormatter.GetType( rDoc.GetNumberFormat( aPos ) );
346 0 : if( nFmtType == css::util::NumberFormat::LOGICAL )
347 0 : InsertOrigBoolItem( fValue != 0 );
348 0 : else if( nFmtType & css::util::NumberFormat::DATETIME )
349 0 : InsertOrigDateTimeItem( GetDateTimeFromDouble( ::std::max( fValue, 0.0 ) ) );
350 : else
351 0 : InsertOrigDoubleItem( fValue );
352 : }
353 : else
354 : {
355 0 : OUString aText = rDoc.GetString(aPos.Col(), aPos.Row(), aPos.Tab());
356 0 : InsertOrigTextItem( aText );
357 : }
358 : }
359 0 : }
360 :
361 0 : void XclExpPCField::InitStdGroupField( const XclExpPCField& rBaseField, const ScDPSaveGroupDimension& rGroupDim )
362 : {
363 : OSL_ENSURE( IsGroupField(), "XclExpPCField::InitStdGroupField - only for standard grouping fields" );
364 :
365 0 : maFieldInfo.mnBaseItems = rBaseField.GetItemCount();
366 0 : maGroupOrder.resize( maFieldInfo.mnBaseItems, EXC_PC_NOITEM );
367 :
368 : // loop over all groups of this field
369 0 : for( long nGroupIdx = 0, nGroupCount = rGroupDim.GetGroupCount(); nGroupIdx < nGroupCount; ++nGroupIdx )
370 : {
371 0 : if( const ScDPSaveGroupItem* pGroupItem = rGroupDim.GetGroupByIndex( nGroupIdx ) )
372 : {
373 : // the index of the new item containing the grouping name
374 0 : sal_uInt16 nGroupItemIdx = EXC_PC_NOITEM;
375 : // loop over all elements of one group
376 0 : for( size_t nElemIdx = 0, nElemCount = pGroupItem->GetElementCount(); nElemIdx < nElemCount; ++nElemIdx )
377 : {
378 0 : if (const OUString* pElemName = pGroupItem->GetElementByIndex(nElemIdx))
379 : {
380 : // try to find the item that is part of the group in the base field
381 0 : sal_uInt16 nBaseItemIdx = rBaseField.GetItemIndex( *pElemName );
382 0 : if( nBaseItemIdx < maFieldInfo.mnBaseItems )
383 : {
384 : // add group name item only if there are any valid base items
385 0 : if( nGroupItemIdx == EXC_PC_NOITEM )
386 0 : nGroupItemIdx = InsertGroupItem( new XclExpPCItem( pGroupItem->GetGroupName() ) );
387 0 : maGroupOrder[ nBaseItemIdx ] = nGroupItemIdx;
388 : }
389 : }
390 : }
391 : }
392 : }
393 :
394 : // add items and base item indexes of all ungrouped elements
395 0 : for( sal_uInt16 nBaseItemIdx = 0; nBaseItemIdx < maFieldInfo.mnBaseItems; ++nBaseItemIdx )
396 : // items that are not part of a group still have the EXC_PC_NOITEM entry
397 0 : if( maGroupOrder[ nBaseItemIdx ] == EXC_PC_NOITEM )
398 : // try to find the base item
399 0 : if( const XclExpPCItem* pBaseItem = rBaseField.GetItem( nBaseItemIdx ) )
400 : // create a clone of the base item, insert its index into item order list
401 0 : maGroupOrder[ nBaseItemIdx ] = InsertGroupItem( new XclExpPCItem( *pBaseItem ) );
402 0 : }
403 :
404 0 : void XclExpPCField::InitNumGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo )
405 : {
406 : OSL_ENSURE( IsStandardField(), "XclExpPCField::InitNumGroupField - only for standard fields" );
407 : OSL_ENSURE( rNumInfo.mbEnable, "XclExpPCField::InitNumGroupField - numeric grouping not enabled" );
408 :
409 : // new field type, date type, limit settings (min/max/step/auto)
410 0 : if( rNumInfo.mbDateValues )
411 : {
412 : // special case: group by days with step count
413 0 : meFieldType = EXC_PCFIELD_DATEGROUP;
414 0 : maNumGroupInfo.SetScDateType( com::sun::star::sheet::DataPilotFieldGroupBy::DAYS );
415 0 : SetDateGroupLimit( rNumInfo, true );
416 : }
417 : else
418 : {
419 0 : meFieldType = EXC_PCFIELD_NUMGROUP;
420 0 : maNumGroupInfo.SetNumType();
421 0 : SetNumGroupLimit( rNumInfo );
422 : }
423 :
424 : // generate visible items
425 0 : InsertNumDateGroupItems( rDPObj, rNumInfo );
426 0 : }
427 :
428 0 : void XclExpPCField::InitDateGroupField( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nDatePart )
429 : {
430 : OSL_ENSURE( IsStandardField() || IsStdGroupField(), "XclExpPCField::InitDateGroupField - only for standard fields" );
431 : OSL_ENSURE( rDateInfo.mbEnable, "XclExpPCField::InitDateGroupField - date grouping not enabled" );
432 :
433 : // new field type
434 0 : meFieldType = IsStandardField() ? EXC_PCFIELD_DATEGROUP : EXC_PCFIELD_DATECHILD;
435 :
436 : // date type, limit settings (min/max/step/auto)
437 0 : maNumGroupInfo.SetScDateType( nDatePart );
438 0 : SetDateGroupLimit( rDateInfo, false );
439 :
440 : // generate visible items
441 0 : InsertNumDateGroupItems( rDPObj, rDateInfo, nDatePart );
442 0 : }
443 :
444 0 : void XclExpPCField::InsertItemArrayIndex( size_t nListPos )
445 : {
446 : OSL_ENSURE( IsStandardField(), "XclExpPCField::InsertItemArrayIndex - only for standard fields" );
447 0 : maIndexVec.push_back( static_cast< sal_uInt16 >( nListPos ) );
448 0 : }
449 :
450 0 : void XclExpPCField::InsertOrigItem( XclExpPCItem* pNewItem )
451 : {
452 0 : size_t nItemIdx = maOrigItemList.GetSize();
453 0 : maOrigItemList.AppendNewRecord( pNewItem );
454 0 : InsertItemArrayIndex( nItemIdx );
455 0 : mnTypeFlags |= pNewItem->GetTypeFlag();
456 0 : }
457 :
458 0 : void XclExpPCField::InsertOrigTextItem( const OUString& rText )
459 : {
460 0 : size_t nPos = 0;
461 0 : bool bFound = false;
462 : // #i76047# maximum item text length in pivot cache is 255
463 0 : OUString aShortText = rText.copy( 0, ::std::min(rText.getLength(), EXC_PC_MAXSTRLEN ) );
464 0 : for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
465 0 : if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsText( aShortText )) )
466 0 : InsertItemArrayIndex( nPos );
467 0 : if( !bFound )
468 0 : InsertOrigItem( new XclExpPCItem( aShortText ) );
469 0 : }
470 :
471 0 : void XclExpPCField::InsertOrigDoubleItem( double fValue )
472 : {
473 0 : size_t nPos = 0;
474 0 : bool bFound = false;
475 0 : for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
476 0 : if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDouble( fValue )) )
477 0 : InsertItemArrayIndex( nPos );
478 0 : if( !bFound )
479 0 : InsertOrigItem( new XclExpPCItem( fValue ) );
480 0 : }
481 :
482 0 : void XclExpPCField::InsertOrigDateTimeItem( const DateTime& rDateTime )
483 : {
484 0 : size_t nPos = 0;
485 0 : bool bFound = false;
486 0 : for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
487 0 : if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsDateTime( rDateTime )) )
488 0 : InsertItemArrayIndex( nPos );
489 0 : if( !bFound )
490 0 : InsertOrigItem( new XclExpPCItem( rDateTime ) );
491 0 : }
492 :
493 0 : void XclExpPCField::InsertOrigBoolItem( bool bValue )
494 : {
495 0 : size_t nPos = 0;
496 0 : bool bFound = false;
497 0 : for( size_t nSize = maOrigItemList.GetSize(); !bFound && (nPos < nSize); ++nPos )
498 0 : if( (bFound = maOrigItemList.GetRecord( nPos )->EqualsBool( bValue )) )
499 0 : InsertItemArrayIndex( nPos );
500 0 : if( !bFound )
501 0 : InsertOrigItem( new XclExpPCItem( bValue ) );
502 0 : }
503 :
504 0 : sal_uInt16 XclExpPCField::InsertGroupItem( XclExpPCItem* pNewItem )
505 : {
506 0 : maGroupItemList.AppendNewRecord( pNewItem );
507 0 : return static_cast< sal_uInt16 >( maGroupItemList.GetSize() - 1 );
508 : }
509 :
510 0 : void XclExpPCField::InsertNumDateGroupItems( const ScDPObject& rDPObj, const ScDPNumGroupInfo& rNumInfo, sal_Int32 nDatePart )
511 : {
512 : OSL_ENSURE( rDPObj.GetSheetDesc(), "XclExpPCField::InsertNumDateGroupItems - cannot generate element list" );
513 0 : if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
514 : {
515 : // get the string collection with original source elements
516 0 : const ScDPSaveData* pSaveData = rDPObj.GetSaveData();
517 0 : const ScDPDimensionSaveData* pDimData = NULL;
518 0 : if (pSaveData)
519 0 : pDimData = pSaveData->GetExistingDimensionData();
520 :
521 0 : const ScDPCache* pCache = pSrcDesc->CreateCache(pDimData);
522 0 : if (!pCache)
523 0 : return;
524 :
525 0 : ScSheetDPData aDPData(GetDocPtr(), *pSrcDesc, *pCache);
526 0 : long nDim = GetFieldIndex();
527 : // get the string collection with generated grouping elements
528 0 : ScDPNumGroupDimension aTmpDim( rNumInfo );
529 0 : if( nDatePart != 0 )
530 0 : aTmpDim.SetDateDimension();
531 : const std::vector<SCROW>& aMemberIds = aTmpDim.GetNumEntries(
532 0 : static_cast<SCCOL>(nDim), pCache);
533 0 : for ( size_t nIdx = 0 ; nIdx < aMemberIds.size(); nIdx++ )
534 : {
535 0 : const ScDPItemData* pData = aDPData.GetMemberById(nDim , aMemberIds[nIdx]);
536 0 : if ( pData )
537 : {
538 0 : OUString aStr = pCache->GetFormattedString(nDim, *pData);
539 0 : InsertGroupItem(new XclExpPCItem(aStr));
540 : }
541 0 : }
542 : }
543 : }
544 :
545 0 : void XclExpPCField::SetNumGroupLimit( const ScDPNumGroupInfo& rNumInfo )
546 : {
547 0 : ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rNumInfo.mbAutoStart );
548 0 : ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rNumInfo.mbAutoEnd );
549 0 : maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStart ) );
550 0 : maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfEnd ) );
551 0 : maNumGroupLimits.AppendNewRecord( new XclExpPCItem( rNumInfo.mfStep ) );
552 0 : }
553 :
554 0 : void XclExpPCField::SetDateGroupLimit( const ScDPNumGroupInfo& rDateInfo, bool bUseStep )
555 : {
556 0 : ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMIN, rDateInfo.mbAutoStart );
557 0 : ::set_flag( maNumGroupInfo.mnFlags, EXC_SXNUMGROUP_AUTOMAX, rDateInfo.mbAutoEnd );
558 0 : maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfStart ) ) );
559 0 : maNumGroupLimits.AppendNewRecord( new XclExpPCItem( GetDateTimeFromDouble( rDateInfo.mfEnd ) ) );
560 0 : sal_Int16 nStep = bUseStep ? limit_cast< sal_Int16 >( rDateInfo.mfStep, 1, SAL_MAX_INT16 ) : 1;
561 0 : maNumGroupLimits.AppendNewRecord( new XclExpPCItem( nStep ) );
562 0 : }
563 :
564 0 : void XclExpPCField::Finalize()
565 : {
566 : // flags
567 0 : ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_HASITEMS, !GetVisItemList().IsEmpty() );
568 : // Excel writes long indexes even for 0x0100 items (indexes from 0x00 to 0xFF)
569 0 : ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_16BIT, maOrigItemList.GetSize() >= 0x0100 );
570 0 : ::set_flag( maFieldInfo.mnFlags, EXC_SXFIELD_NUMGROUP, IsNumGroupField() || IsDateGroupField() );
571 : /* mnTypeFlags is updated in all Insert***Item() functions. Now the flags
572 : for the current combination of item types is added to the flags. */
573 0 : ::set_flag( maFieldInfo.mnFlags, spnPCItemFlags[ mnTypeFlags ] );
574 :
575 : // item count fields
576 0 : maFieldInfo.mnVisItems = static_cast< sal_uInt16 >( GetVisItemList().GetSize() );
577 0 : maFieldInfo.mnGroupItems = static_cast< sal_uInt16 >( maGroupItemList.GetSize() );
578 : // maFieldInfo.mnBaseItems set in InitStdGroupField()
579 0 : maFieldInfo.mnOrigItems = static_cast< sal_uInt16 >( maOrigItemList.GetSize() );
580 0 : }
581 :
582 0 : void XclExpPCField::WriteSxnumgroup( XclExpStream& rStrm )
583 : {
584 0 : if( IsNumGroupField() || IsDateGroupField() )
585 : {
586 : // SXNUMGROUP record
587 0 : rStrm.StartRecord( EXC_ID_SXNUMGROUP, 2 );
588 0 : rStrm << maNumGroupInfo;
589 0 : rStrm.EndRecord();
590 :
591 : // limits (min/max/step) for numeric grouping
592 : OSL_ENSURE( maNumGroupLimits.GetSize() == 3,
593 : "XclExpPCField::WriteSxnumgroup - missing numeric grouping limits" );
594 0 : maNumGroupLimits.Save( rStrm );
595 : }
596 0 : }
597 :
598 0 : void XclExpPCField::WriteSxgroupinfo( XclExpStream& rStrm )
599 : {
600 : OSL_ENSURE( IsStdGroupField() != maGroupOrder.empty(),
601 : "XclExpPCField::WriteSxgroupinfo - missing grouping info" );
602 0 : if( IsStdGroupField() && !maGroupOrder.empty() )
603 : {
604 0 : rStrm.StartRecord( EXC_ID_SXGROUPINFO, 2 * maGroupOrder.size() );
605 0 : for( ScfUInt16Vec::const_iterator aIt = maGroupOrder.begin(), aEnd = maGroupOrder.end(); aIt != aEnd; ++aIt )
606 0 : rStrm << *aIt;
607 0 : rStrm.EndRecord();
608 : }
609 0 : }
610 :
611 0 : void XclExpPCField::WriteBody( XclExpStream& rStrm )
612 : {
613 0 : rStrm << maFieldInfo;
614 0 : }
615 :
616 0 : XclExpPivotCache::XclExpPivotCache( const XclExpRoot& rRoot, const ScDPObject& rDPObj, sal_uInt16 nListIdx ) :
617 : XclExpRoot( rRoot ),
618 : mnListIdx( nListIdx ),
619 0 : mbValid( false )
620 : {
621 : // source from sheet only
622 0 : if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
623 : {
624 : /* maOrigSrcRange: Range received from the DataPilot object.
625 : maExpSrcRange: Range written to the DCONREF record.
626 : maDocSrcRange: Range used to get source data from Calc document.
627 : This range may be shorter than maExpSrcRange to improve export
628 : performance (#i22541#). */
629 0 : maOrigSrcRange = maExpSrcRange = maDocSrcRange = pSrcDesc->GetSourceRange();
630 0 : maSrcRangeName = pSrcDesc->GetRangeName();
631 :
632 : // internal sheet data only
633 0 : SCTAB nScTab = maExpSrcRange.aStart.Tab();
634 0 : if( (nScTab == maExpSrcRange.aEnd.Tab()) && GetTabInfo().IsExportTab( nScTab ) )
635 : {
636 : // ValidateRange() restricts source range to valid Excel limits
637 0 : if( GetAddressConverter().ValidateRange( maExpSrcRange, true ) )
638 : {
639 : // #i22541# skip empty cell areas (performance)
640 : SCCOL nDocCol1, nDocCol2;
641 : SCROW nDocRow1, nDocRow2;
642 0 : GetDoc().GetDataStart( nScTab, nDocCol1, nDocRow1 );
643 0 : GetDoc().GetPrintArea( nScTab, nDocCol2, nDocRow2, false );
644 0 : SCCOL nSrcCol1 = maExpSrcRange.aStart.Col();
645 0 : SCROW nSrcRow1 = maExpSrcRange.aStart.Row();
646 0 : SCCOL nSrcCol2 = maExpSrcRange.aEnd.Col();
647 0 : SCROW nSrcRow2 = maExpSrcRange.aEnd.Row();
648 :
649 : // #i22541# do not store index list for too big ranges
650 0 : if( 2 * (nDocRow2 - nDocRow1) < (nSrcRow2 - nSrcRow1) )
651 0 : ::set_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA, false );
652 :
653 : // adjust row indexes, keep one row of empty area to surely have the empty cache item
654 0 : if( nSrcRow1 < nDocRow1 )
655 0 : nSrcRow1 = nDocRow1 - 1;
656 0 : if( nSrcRow2 > nDocRow2 )
657 0 : nSrcRow2 = nDocRow2 + 1;
658 :
659 0 : maDocSrcRange.aStart.SetCol( ::std::max( nDocCol1, nSrcCol1 ) );
660 0 : maDocSrcRange.aStart.SetRow( nSrcRow1 );
661 0 : maDocSrcRange.aEnd.SetCol( ::std::min( nDocCol2, nSrcCol2 ) );
662 0 : maDocSrcRange.aEnd.SetRow( nSrcRow2 );
663 :
664 0 : GetDoc().GetName( nScTab, maTabName );
665 0 : maPCInfo.mnSrcRecs = static_cast< sal_uInt32 >( maExpSrcRange.aEnd.Row() - maExpSrcRange.aStart.Row() );
666 0 : maPCInfo.mnStrmId = nListIdx + 1;
667 0 : maPCInfo.mnSrcType = EXC_SXDB_SRC_SHEET;
668 :
669 0 : AddFields( rDPObj );
670 :
671 0 : mbValid = true;
672 : }
673 : }
674 : }
675 0 : }
676 :
677 0 : bool XclExpPivotCache::HasItemIndexList() const
678 : {
679 0 : return ::get_flag( maPCInfo.mnFlags, EXC_SXDB_SAVEDATA );
680 : }
681 :
682 0 : sal_uInt16 XclExpPivotCache::GetFieldCount() const
683 : {
684 0 : return static_cast< sal_uInt16 >( maFieldList.GetSize() );
685 : }
686 :
687 0 : const XclExpPCField* XclExpPivotCache::GetField( sal_uInt16 nFieldIdx ) const
688 : {
689 0 : return maFieldList.GetRecord( nFieldIdx ).get();
690 : }
691 :
692 0 : bool XclExpPivotCache::HasAddFields() const
693 : {
694 : // pivot cache can be shared, if there are no additional cache fields
695 0 : return maPCInfo.mnStdFields < maPCInfo.mnTotalFields;
696 : }
697 :
698 0 : bool XclExpPivotCache::HasEqualDataSource( const ScDPObject& rDPObj ) const
699 : {
700 : /* For now, only sheet sources are supported, therefore it is enough to
701 : compare the ScSheetSourceDesc. Later, there should be done more complicated
702 : comparisons regarding the source type of rDPObj and this cache. */
703 0 : if( const ScSheetSourceDesc* pSrcDesc = rDPObj.GetSheetDesc() )
704 0 : return pSrcDesc->GetSourceRange() == maOrigSrcRange;
705 0 : return false;
706 : }
707 :
708 0 : void XclExpPivotCache::Save( XclExpStream& rStrm )
709 : {
710 : OSL_ENSURE( mbValid, "XclExpPivotCache::Save - invalid pivot cache" );
711 : // SXIDSTM
712 0 : XclExpUInt16Record( EXC_ID_SXIDSTM, maPCInfo.mnStrmId ).Save( rStrm );
713 : // SXVS
714 0 : XclExpUInt16Record( EXC_ID_SXVS, EXC_SXVS_SHEET ).Save( rStrm );
715 :
716 0 : if (!maSrcRangeName.isEmpty())
717 : // DCONNAME
718 0 : WriteDConName(rStrm);
719 : else
720 : // DCONREF
721 0 : WriteDconref(rStrm);
722 :
723 : // create the pivot cache storage stream
724 0 : WriteCacheStream();
725 0 : }
726 :
727 0 : void XclExpPivotCache::SaveXml( XclExpXmlStream& /*rStrm*/ )
728 : {
729 0 : }
730 :
731 0 : XclExpPCField* XclExpPivotCache::GetFieldAcc( sal_uInt16 nFieldIdx )
732 : {
733 0 : return maFieldList.GetRecord( nFieldIdx ).get();
734 : }
735 :
736 0 : void XclExpPivotCache::AddFields( const ScDPObject& rDPObj )
737 : {
738 0 : AddStdFields( rDPObj );
739 0 : maPCInfo.mnStdFields = GetFieldCount();
740 0 : AddGroupFields( rDPObj );
741 0 : maPCInfo.mnTotalFields = GetFieldCount();
742 0 : };
743 :
744 0 : void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
745 : {
746 : // if item index list is not written, used shortened source range (maDocSrcRange) for performance
747 0 : const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
748 : // create a standard pivot cache field for each source column
749 0 : for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
750 : {
751 0 : ScRange aColRange( rRange );
752 0 : aColRange.aStart.SetCol( nScCol );
753 0 : aColRange.aEnd.SetCol( nScCol );
754 : maFieldList.AppendNewRecord( new XclExpPCField(
755 0 : GetRoot(), *this, GetFieldCount(), rDPObj, aColRange ) );
756 : }
757 0 : }
758 :
759 0 : void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
760 : {
761 0 : if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
762 : {
763 0 : if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
764 : {
765 : // loop over all existing standard fields to find their group fields
766 0 : for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
767 : {
768 0 : if( XclExpPCField* pCurrStdField = GetFieldAcc( nFieldIdx ) )
769 : {
770 0 : const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
771 0 : XclExpPCField* pLastGroupField = pCurrStdField;
772 0 : while( pGroupDim )
773 : {
774 : // insert the new grouping field
775 : XclExpPCFieldRef xNewGroupField( new XclExpPCField(
776 0 : GetRoot(), *this, GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField ) );
777 0 : maFieldList.AppendRecord( xNewGroupField );
778 :
779 : // register new grouping field at current grouping field, building a chain
780 0 : pLastGroupField->SetGroupChildField( *xNewGroupField );
781 :
782 : // next grouping dimension
783 0 : pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
784 0 : pLastGroupField = xNewGroupField.get();
785 0 : }
786 : }
787 : }
788 : }
789 : }
790 0 : }
791 :
792 0 : void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
793 : {
794 0 : XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), EMPTY_OUSTRING, &maTabName ) );
795 0 : rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
796 0 : rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
797 0 : << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
798 0 : << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
799 0 : << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
800 0 : << aRef
801 0 : << sal_uInt8( 0 );
802 0 : rStrm.EndRecord();
803 0 : }
804 :
805 0 : void XclExpPivotCache::WriteDConName( XclExpStream& rStrm ) const
806 : {
807 0 : XclExpString aName(maSrcRangeName);
808 0 : rStrm.StartRecord(EXC_ID_DCONNAME, aName.GetSize() + 2);
809 0 : rStrm << aName << sal_uInt16(0);
810 0 : rStrm.EndRecord();
811 0 : }
812 :
813 0 : void XclExpPivotCache::WriteCacheStream()
814 : {
815 0 : tools::SvRef<SotStorage> xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
816 0 : tools::SvRef<SotStorageStream> xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
817 0 : if( xSvStrm.Is() )
818 : {
819 0 : XclExpStream aStrm( *xSvStrm, GetRoot() );
820 : // SXDB
821 0 : WriteSxdb( aStrm );
822 : // SXDBEX
823 0 : WriteSxdbex( aStrm );
824 : // field list (SXFIELD and items)
825 0 : maFieldList.Save( aStrm );
826 : // index table (list of SXINDEXLIST)
827 0 : WriteSxindexlistList( aStrm );
828 : // EOF
829 0 : XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
830 0 : }
831 0 : }
832 :
833 0 : void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
834 : {
835 0 : rStrm.StartRecord( EXC_ID_SXDB, 21 );
836 0 : rStrm << maPCInfo;
837 0 : rStrm.EndRecord();
838 0 : }
839 :
840 0 : void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm )
841 : {
842 0 : rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
843 0 : rStrm << EXC_SXDBEX_CREATION_DATE
844 0 : << sal_uInt32( 0 ); // number of SXFORMULA records
845 0 : rStrm.EndRecord();
846 0 : }
847 :
848 0 : void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
849 : {
850 0 : if( HasItemIndexList() )
851 : {
852 0 : sal_Size nRecSize = 0;
853 0 : size_t nPos, nSize = maFieldList.GetSize();
854 0 : for( nPos = 0; nPos < nSize; ++nPos )
855 0 : nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
856 :
857 0 : for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
858 : {
859 0 : rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
860 0 : for( nPos = 0; nPos < nSize; ++nPos )
861 0 : maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
862 0 : rStrm.EndRecord();
863 : }
864 : }
865 0 : }
866 :
867 : // Pivot table
868 :
869 : namespace {
870 :
871 : /** Returns a display string for a data field containing the field name and aggregation function. */
872 0 : OUString lclGetDataFieldCaption( const OUString& rFieldName, GeneralFunction eFunc )
873 : {
874 0 : OUString aCaption;
875 :
876 0 : sal_uInt16 nResIdx = 0;
877 : using namespace ::com::sun::star::sheet;
878 0 : switch( eFunc )
879 : {
880 0 : case GeneralFunction_SUM: nResIdx = STR_FUN_TEXT_SUM; break;
881 0 : case GeneralFunction_COUNT: nResIdx = STR_FUN_TEXT_COUNT; break;
882 0 : case GeneralFunction_AVERAGE: nResIdx = STR_FUN_TEXT_AVG; break;
883 0 : case GeneralFunction_MAX: nResIdx = STR_FUN_TEXT_MAX; break;
884 0 : case GeneralFunction_MIN: nResIdx = STR_FUN_TEXT_MIN; break;
885 0 : case GeneralFunction_PRODUCT: nResIdx = STR_FUN_TEXT_PRODUCT; break;
886 0 : case GeneralFunction_COUNTNUMS: nResIdx = STR_FUN_TEXT_COUNT; break;
887 0 : case GeneralFunction_STDEV: nResIdx = STR_FUN_TEXT_STDDEV; break;
888 0 : case GeneralFunction_STDEVP: nResIdx = STR_FUN_TEXT_STDDEV; break;
889 0 : case GeneralFunction_VAR: nResIdx = STR_FUN_TEXT_VAR; break;
890 0 : case GeneralFunction_VARP: nResIdx = STR_FUN_TEXT_VAR; break;
891 : default:;
892 : }
893 0 : if( nResIdx )
894 0 : aCaption = ScGlobal::GetRscString( nResIdx ) + " - ";
895 0 : aCaption += rFieldName;
896 0 : return aCaption;
897 : }
898 :
899 : } // namespace
900 :
901 0 : XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
902 : XclExpRecord( EXC_ID_SXVI, 8 ),
903 0 : mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
904 : {
905 0 : maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
906 0 : maItemInfo.mnCacheIdx = nCacheIdx;
907 0 : maItemInfo.maVisName.mbUseCache = mpCacheItem != 0;
908 0 : }
909 :
910 0 : XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx, bool bUseCache ) :
911 : XclExpRecord( EXC_ID_SXVI, 8 ),
912 0 : mpCacheItem( 0 )
913 : {
914 0 : maItemInfo.mnType = nItemType;
915 0 : maItemInfo.mnCacheIdx = nCacheIdx;
916 0 : maItemInfo.maVisName.mbUseCache = bUseCache;
917 0 : }
918 :
919 0 : OUString XclExpPTItem::GetItemName() const
920 : {
921 0 : return mpCacheItem ? mpCacheItem->ConvertToText() : OUString();
922 : }
923 :
924 0 : void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
925 : {
926 : // #i115659# GetIsVisible() is not valid if HasIsVisible() returns false, default is 'visible' then
927 0 : ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, rSaveMem.HasIsVisible() && !rSaveMem.GetIsVisible() );
928 : // #i115659# GetShowDetails() is not valid if HasShowDetails() returns false, default is 'show detail' then
929 0 : ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, rSaveMem.HasShowDetails() && !rSaveMem.GetShowDetails() );
930 :
931 : // visible name
932 0 : const OUString* pVisName = rSaveMem.GetLayoutName();
933 0 : if (pVisName && !pVisName->equals(GetItemName()))
934 0 : maItemInfo.SetVisName(*pVisName);
935 0 : }
936 :
937 0 : void XclExpPTItem::WriteBody( XclExpStream& rStrm )
938 : {
939 0 : rStrm << maItemInfo;
940 0 : }
941 :
942 0 : XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
943 : mrPTable( rPTable ),
944 0 : mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
945 : {
946 0 : maFieldInfo.mnCacheIdx = nCacheIdx;
947 :
948 : // create field items
949 0 : if( mpCacheField )
950 0 : for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
951 0 : maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
952 0 : maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
953 0 : }
954 :
955 : // data access ----------------------------------------------------------------
956 :
957 0 : OUString XclExpPTField::GetFieldName() const
958 : {
959 0 : return mpCacheField ? mpCacheField->GetFieldName() : OUString();
960 : }
961 :
962 0 : sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
963 : {
964 : OSL_ENSURE( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
965 : // will return 0xFFFF for empty vector -> ok
966 0 : return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
967 : }
968 :
969 0 : sal_uInt16 XclExpPTField::GetItemIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
970 : {
971 0 : for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
972 0 : if( maItemList.GetRecord( nPos )->GetItemName() == rName )
973 0 : return static_cast< sal_uInt16 >( nPos );
974 0 : return nDefaultIdx;
975 : }
976 :
977 : // fill data --------------------------------------------------------------
978 :
979 : /**
980 : * Calc's subtotal names are escaped with backslashes ('\'), while Excel's
981 : * are not escaped at all.
982 : */
983 0 : static OUString lcl_convertCalcSubtotalName(const OUString& rName)
984 : {
985 0 : OUStringBuffer aBuf;
986 0 : const sal_Unicode* p = rName.getStr();
987 0 : sal_Int32 n = rName.getLength();
988 0 : bool bEscaped = false;
989 0 : for (sal_Int32 i = 0; i < n; ++i)
990 : {
991 0 : const sal_Unicode c = p[i];
992 0 : if (!bEscaped && c == '\\')
993 : {
994 0 : bEscaped = true;
995 0 : continue;
996 : }
997 :
998 0 : aBuf.append(c);
999 0 : bEscaped = false;
1000 : }
1001 0 : return aBuf.makeStringAndClear();
1002 : }
1003 :
1004 0 : void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1005 : {
1006 : // orientation
1007 0 : DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() );
1008 : OSL_ENSURE( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
1009 0 : maFieldInfo.AddApiOrient( eOrient );
1010 :
1011 : // show empty items (#i115659# GetShowEmpty() is not valid if HasShowEmpty() returns false, default is false then)
1012 0 : ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.HasShowEmpty() && rSaveDim.GetShowEmpty() );
1013 :
1014 : // visible name
1015 0 : const OUString* pLayoutName = rSaveDim.GetLayoutName();
1016 0 : if (pLayoutName && !pLayoutName->equals(GetFieldName()))
1017 0 : maFieldInfo.SetVisName(*pLayoutName);
1018 :
1019 0 : const OUString* pSubtotalName = rSaveDim.GetSubtotalName();
1020 0 : if (pSubtotalName)
1021 : {
1022 0 : OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
1023 0 : maFieldExtInfo.mpFieldTotalName.reset(new OUString(aSubName));
1024 : }
1025 :
1026 : // subtotals
1027 0 : XclPTSubtotalVec aSubtotals;
1028 0 : aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
1029 0 : for( long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
1030 0 : aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
1031 0 : maFieldInfo.SetSubtotals( aSubtotals );
1032 :
1033 : // sorting
1034 0 : if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
1035 : {
1036 0 : maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
1037 0 : if( pSortInfo->Mode == ::com::sun::star::sheet::DataPilotFieldSortMode::DATA )
1038 0 : maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
1039 0 : ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
1040 : }
1041 :
1042 : // auto show
1043 0 : if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
1044 : {
1045 0 : ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
1046 0 : maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
1047 0 : maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
1048 0 : maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
1049 : }
1050 :
1051 : // layout
1052 0 : if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
1053 : {
1054 0 : maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
1055 0 : ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
1056 : }
1057 :
1058 : // special page field properties
1059 0 : if( eOrient == DataPilotFieldOrientation_PAGE )
1060 : {
1061 0 : maPageInfo.mnField = GetFieldIndex();
1062 0 : maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
1063 : }
1064 :
1065 : // item properties
1066 0 : const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
1067 0 : for (ScDPSaveDimension::MemberList::const_iterator i=rMembers.begin(); i != rMembers.end() ; ++i)
1068 0 : if( XclExpPTItem* pItem = GetItemAcc( (*i)->GetName() ) )
1069 0 : pItem->SetPropertiesFromMember( **i );
1070 0 : }
1071 :
1072 0 : void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1073 : {
1074 0 : maDataInfoVec.push_back( XclPTDataFieldInfo() );
1075 0 : XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
1076 0 : rDataInfo.mnField = GetFieldIndex();
1077 :
1078 : // orientation
1079 0 : maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
1080 :
1081 : // aggregation function
1082 0 : GeneralFunction eFunc = static_cast< GeneralFunction >( rSaveDim.GetFunction() );
1083 0 : rDataInfo.SetApiAggFunc( eFunc );
1084 :
1085 : // visible name
1086 0 : const OUString* pVisName = rSaveDim.GetLayoutName();
1087 0 : if (pVisName)
1088 0 : rDataInfo.SetVisName(*pVisName);
1089 : else
1090 0 : rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
1091 :
1092 : // result field reference
1093 0 : if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
1094 : {
1095 0 : rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
1096 0 : rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
1097 0 : if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
1098 : {
1099 0 : rDataInfo.mnRefField = pRefField->GetFieldIndex();
1100 0 : if( pFieldRef->ReferenceItemType == ::com::sun::star::sheet::DataPilotFieldReferenceItemType::NAMED )
1101 0 : rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
1102 : }
1103 : }
1104 0 : }
1105 :
1106 0 : void XclExpPTField::AppendSubtotalItems()
1107 : {
1108 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
1109 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
1110 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
1111 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
1112 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
1113 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
1114 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
1115 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
1116 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
1117 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
1118 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
1119 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
1120 0 : }
1121 :
1122 : // records --------------------------------------------------------------------
1123 :
1124 0 : void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
1125 : {
1126 0 : rStrm << maPageInfo;
1127 0 : }
1128 :
1129 0 : void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
1130 : {
1131 : OSL_ENSURE( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
1132 0 : if( nDataInfoIdx < maDataInfoVec.size() )
1133 : {
1134 0 : rStrm.StartRecord( EXC_ID_SXDI, 12 );
1135 0 : rStrm << maDataInfoVec[ nDataInfoIdx ];
1136 0 : rStrm.EndRecord();
1137 : }
1138 0 : }
1139 :
1140 0 : void XclExpPTField::Save( XclExpStream& rStrm )
1141 : {
1142 : // SXVD
1143 0 : WriteSxvd( rStrm );
1144 : // list of SXVI records
1145 0 : maItemList.Save( rStrm );
1146 : // SXVDEX
1147 0 : WriteSxvdex( rStrm );
1148 0 : }
1149 :
1150 : // private --------------------------------------------------------------------
1151 :
1152 0 : XclExpPTItem* XclExpPTField::GetItemAcc( const OUString& rName )
1153 : {
1154 0 : XclExpPTItem* pItem = 0;
1155 0 : for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
1156 0 : if( maItemList.GetRecord( nPos )->GetItemName() == rName )
1157 0 : pItem = maItemList.GetRecord( nPos ).get();
1158 0 : return pItem;
1159 : }
1160 :
1161 0 : void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
1162 : {
1163 0 : maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE, true ) );
1164 0 : ++maFieldInfo.mnItemCount;
1165 0 : }
1166 :
1167 0 : void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
1168 : {
1169 0 : rStrm.StartRecord( EXC_ID_SXVD, 10 );
1170 0 : rStrm << maFieldInfo;
1171 0 : rStrm.EndRecord();
1172 0 : }
1173 :
1174 0 : void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
1175 : {
1176 0 : rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
1177 0 : rStrm << maFieldExtInfo;
1178 0 : rStrm.EndRecord();
1179 0 : }
1180 :
1181 0 : XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache, size_t ) :
1182 : XclExpRoot( rRoot ),
1183 : mrPCache( rPCache ),
1184 : maDataOrientField( *this, EXC_SXIVD_DATA ),
1185 : mnOutScTab( 0 ),
1186 : mbValid( false ),
1187 0 : mbFilterBtn( false )
1188 : {
1189 0 : const ScRange& rOutScRange = rDPObj.GetOutRange();
1190 0 : if( GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
1191 : {
1192 : // DataPilot properties -----------------------------------------------
1193 :
1194 : // pivot table properties from DP object
1195 0 : mnOutScTab = rOutScRange.aStart.Tab();
1196 0 : maPTInfo.maTableName = rDPObj.GetName();
1197 0 : maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
1198 :
1199 0 : maPTViewEx9Info.Init( rDPObj );
1200 :
1201 0 : if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1202 : {
1203 : // additional properties from ScDPSaveData
1204 0 : SetPropertiesFromDP( *pSaveData );
1205 :
1206 : // loop over all dimensions ---------------------------------------
1207 :
1208 : /* 1) Default-construct all pivot table fields for all pivot cache fields. */
1209 0 : for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
1210 0 : maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
1211 :
1212 0 : boost::ptr_vector<ScDPSaveDimension>::const_iterator iter;
1213 0 : const ScDPSaveData::DimsType& rDimList = pSaveData->GetDimensions();
1214 :
1215 : /* 2) First process all data dimensions, they are needed for extended
1216 : settings of row/column/page fields (sorting/auto show). */
1217 0 : for (iter = rDimList.begin(); iter != rDimList.end(); ++iter)
1218 0 : if (iter->GetOrientation() == DataPilotFieldOrientation_DATA)
1219 0 : SetDataFieldPropertiesFromDim(*iter);
1220 :
1221 : /* 3) Row/column/page/hidden fields. */
1222 0 : for (iter = rDimList.begin(); iter != rDimList.end(); ++iter)
1223 0 : if (iter->GetOrientation() != DataPilotFieldOrientation_DATA)
1224 0 : SetFieldPropertiesFromDim(*iter);
1225 :
1226 : // Finalize -------------------------------------------------------
1227 :
1228 0 : Finalize();
1229 0 : mbValid = true;
1230 : }
1231 : }
1232 0 : }
1233 :
1234 0 : const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
1235 : {
1236 0 : return mrPCache.GetField( nCacheIdx );
1237 : }
1238 :
1239 0 : const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
1240 : {
1241 0 : return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx ).get();
1242 : }
1243 :
1244 0 : const XclExpPTField* XclExpPivotTable::GetField( const OUString& rName ) const
1245 : {
1246 0 : return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
1247 : }
1248 :
1249 0 : sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
1250 : {
1251 0 : for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt )
1252 0 : if( const XclExpPTField* pField = GetField( aIt->first ) )
1253 0 : if( pField->GetFieldName() == rName )
1254 0 : return static_cast< sal_uInt16 >( aIt - maDataFields.begin() );
1255 0 : return nDefaultIdx;
1256 : }
1257 :
1258 0 : void XclExpPivotTable::Save( XclExpStream& rStrm )
1259 : {
1260 0 : if( mbValid )
1261 : {
1262 : // SXVIEW
1263 0 : WriteSxview( rStrm );
1264 : // pivot table fields (SXVD, SXVDEX, and item records)
1265 0 : maFieldList.Save( rStrm );
1266 : // SXIVD records for row and column fields
1267 0 : WriteSxivd( rStrm, maRowFields );
1268 0 : WriteSxivd( rStrm, maColFields );
1269 : // SXPI
1270 0 : WriteSxpi( rStrm );
1271 : // list of SXDI records containing data field info
1272 0 : WriteSxdiList( rStrm );
1273 : // SXLI records
1274 0 : WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
1275 0 : WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
1276 : // SXEX
1277 0 : WriteSxex( rStrm );
1278 : // QSISXTAG
1279 0 : WriteQsiSxTag( rStrm );
1280 : // SXVIEWEX9
1281 0 : WriteSxViewEx9( rStrm );
1282 : }
1283 0 : }
1284 :
1285 0 : XclExpPTField* XclExpPivotTable::GetFieldAcc( const OUString& rName )
1286 : {
1287 0 : XclExpPTField* pField = 0;
1288 0 : for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
1289 0 : if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
1290 0 : pField = maFieldList.GetRecord( nPos ).get();
1291 0 : return pField;
1292 : }
1293 :
1294 0 : XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
1295 : {
1296 : // data field orientation field?
1297 0 : if( rSaveDim.IsDataLayout() )
1298 0 : return &maDataOrientField;
1299 :
1300 : // a real dimension
1301 0 : OUString aFieldName = ScDPUtil::getSourceDimensionName(rSaveDim.GetName());
1302 0 : return aFieldName.isEmpty() ? NULL : GetFieldAcc(aFieldName);
1303 : }
1304 :
1305 : // fill data --------------------------------------------------------------
1306 :
1307 0 : void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
1308 : {
1309 0 : ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
1310 0 : ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
1311 0 : ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
1312 0 : mbFilterBtn = rSaveData.GetFilterButton();
1313 0 : const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
1314 0 : if (!pDim)
1315 0 : return;
1316 :
1317 0 : const OUString* pLayoutName = pDim->GetLayoutName();
1318 0 : if (pLayoutName)
1319 0 : maPTInfo.maDataName = *pLayoutName;
1320 : else
1321 0 : maPTInfo.maDataName = ScGlobal::GetRscString(STR_PIVOT_DATA);
1322 : }
1323 :
1324 0 : void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1325 : {
1326 0 : if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1327 : {
1328 : // field properties
1329 0 : pField->SetPropertiesFromDim( rSaveDim );
1330 :
1331 : // update the corresponding field position list
1332 0 : DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() );
1333 0 : sal_uInt16 nFieldIdx = pField->GetFieldIndex();
1334 0 : bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
1335 0 : bool bMultiData = maDataFields.size() > 1;
1336 :
1337 0 : if( !bDataLayout || bMultiData ) switch( eOrient )
1338 : {
1339 : case DataPilotFieldOrientation_ROW:
1340 0 : maRowFields.push_back( nFieldIdx );
1341 0 : if( bDataLayout )
1342 0 : maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1343 0 : break;
1344 : case DataPilotFieldOrientation_COLUMN:
1345 0 : maColFields.push_back( nFieldIdx );
1346 0 : if( bDataLayout )
1347 0 : maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
1348 0 : break;
1349 : case DataPilotFieldOrientation_PAGE:
1350 0 : maPageFields.push_back( nFieldIdx );
1351 : OSL_ENSURE( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
1352 0 : break;
1353 : case DataPilotFieldOrientation_DATA:
1354 : OSL_FAIL( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
1355 0 : break;
1356 : default:;
1357 : }
1358 : }
1359 0 : }
1360 :
1361 0 : void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1362 : {
1363 0 : if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1364 : {
1365 : // field properties
1366 0 : pField->SetDataPropertiesFromDim( rSaveDim );
1367 : // update the data field position list
1368 0 : maDataFields.push_back( XclPTDataFieldPos( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() ) );
1369 : }
1370 0 : }
1371 :
1372 0 : void XclExpPivotTable::Finalize()
1373 : {
1374 : // field numbers
1375 0 : maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
1376 0 : maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
1377 0 : maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
1378 0 : maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
1379 0 : maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
1380 :
1381 0 : maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
1382 0 : maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
1383 :
1384 : // subtotal items
1385 0 : for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
1386 0 : maFieldList.GetRecord( nPos )->AppendSubtotalItems();
1387 :
1388 : // find data field orientation field
1389 0 : maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
1390 0 : const ScfUInt16Vec* pFieldVec = 0;
1391 0 : switch( maPTInfo.mnDataAxis )
1392 : {
1393 0 : case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break;
1394 0 : case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break;
1395 : }
1396 :
1397 0 : if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
1398 : {
1399 0 : ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
1400 0 : if( aIt != pFieldVec->end() )
1401 0 : maPTInfo.mnDataPos = static_cast< sal_uInt16 >( aIt - pFieldVec->begin() );
1402 : }
1403 :
1404 : // single data field is always row oriented
1405 0 : if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
1406 0 : maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1407 :
1408 : // update output range (initialized in ctor)
1409 0 : sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
1410 0 : sal_uInt32& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
1411 0 : sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
1412 0 : sal_uInt32& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
1413 : // exclude page fields from output range
1414 0 : rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
1415 : // exclude filter button from output range
1416 0 : if( mbFilterBtn )
1417 0 : ++rnXclRow1;
1418 : // exclude empty row between (filter button and/or page fields) and table
1419 0 : if( mbFilterBtn || maPTInfo.mnPageFields )
1420 0 : ++rnXclRow1;
1421 :
1422 : // data area
1423 0 : sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
1424 0 : sal_uInt32& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
1425 0 : rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
1426 0 : rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
1427 0 : if( maDataFields.empty() )
1428 0 : ++rnDataXclRow;
1429 :
1430 0 : bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
1431 0 : if (bExtraHeaderRow)
1432 : // Insert an extra row only when there is no column field.
1433 0 : ++rnDataXclRow;
1434 :
1435 0 : rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
1436 0 : rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
1437 0 : maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
1438 0 : maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
1439 :
1440 : // first heading
1441 0 : maPTInfo.mnFirstHeadRow = rnXclRow1;
1442 0 : if (bExtraHeaderRow)
1443 0 : maPTInfo.mnFirstHeadRow += 2;
1444 0 : }
1445 :
1446 : // records ----------------------------------------------------------------
1447 :
1448 0 : void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
1449 : {
1450 0 : rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.getLength() + maPTInfo.maDataName.getLength() );
1451 0 : rStrm << maPTInfo;
1452 0 : rStrm.EndRecord();
1453 0 : }
1454 :
1455 0 : void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields )
1456 : {
1457 0 : if( !rFields.empty() )
1458 : {
1459 0 : rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
1460 0 : for( ScfUInt16Vec::const_iterator aIt = rFields.begin(), aEnd = rFields.end(); aIt != aEnd; ++aIt )
1461 0 : rStrm << *aIt;
1462 0 : rStrm.EndRecord();
1463 : }
1464 0 : }
1465 :
1466 0 : void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
1467 : {
1468 0 : if( !maPageFields.empty() )
1469 : {
1470 0 : rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
1471 0 : rStrm.SetSliceSize( 6 );
1472 0 : for( ScfUInt16Vec::const_iterator aIt = maPageFields.begin(), aEnd = maPageFields.end(); aIt != aEnd; ++aIt )
1473 : {
1474 0 : XclExpPTFieldRef xField = maFieldList.GetRecord( *aIt );
1475 0 : if( xField )
1476 0 : xField->WriteSxpiEntry( rStrm );
1477 0 : }
1478 0 : rStrm.EndRecord();
1479 : }
1480 0 : }
1481 :
1482 0 : void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
1483 : {
1484 0 : for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt )
1485 : {
1486 0 : XclExpPTFieldRef xField = maFieldList.GetRecord( aIt->first );
1487 0 : if( xField )
1488 0 : xField->WriteSxdi( rStrm, aIt->second );
1489 0 : }
1490 0 : }
1491 :
1492 0 : void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount )
1493 : {
1494 0 : if( nLineCount > 0 )
1495 : {
1496 0 : sal_Size nLineSize = 8 + 2 * nIndexCount;
1497 0 : rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
1498 :
1499 : /* Excel expects the records to be filled completely, do not
1500 : set a segment size... */
1501 : // rStrm.SetSliceSize( nLineSize );
1502 :
1503 0 : for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1504 : {
1505 : // Excel XP needs a partly initialized SXLI record
1506 0 : rStrm << sal_uInt16( 0 ) // number of equal index entries
1507 0 : << EXC_SXVI_TYPE_DATA
1508 0 : << nIndexCount
1509 0 : << EXC_SXLI_DEFAULTFLAGS;
1510 0 : rStrm.WriteZeroBytes( 2 * nIndexCount );
1511 : }
1512 0 : rStrm.EndRecord();
1513 : }
1514 0 : }
1515 :
1516 0 : void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
1517 : {
1518 0 : rStrm.StartRecord( EXC_ID_SXEX, 24 );
1519 0 : rStrm << maPTExtInfo;
1520 0 : rStrm.EndRecord();
1521 0 : }
1522 :
1523 0 : void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
1524 : {
1525 0 : rStrm.StartRecord( 0x0802, 32 );
1526 :
1527 0 : sal_uInt16 nRecordType = 0x0802;
1528 0 : sal_uInt16 nDummyFlags = 0x0000;
1529 0 : sal_uInt16 nTableType = 1; // 0 = query table : 1 = pivot table
1530 :
1531 0 : rStrm << nRecordType << nDummyFlags << nTableType;
1532 :
1533 : // General flags
1534 0 : sal_uInt16 nFlags = 0x0001;
1535 : #if 0
1536 : // for doc purpose
1537 : sal_uInt16 nFlags = 0x0000;
1538 : bool bEnableRefresh = true;
1539 : bool bPCacheInvalid = false;
1540 : bool bOlapPTReport = false;
1541 :
1542 : if (bEnableRefresh) nFlags |= 0x0001;
1543 : if (bPCacheInvalid) nFlags |= 0x0002;
1544 : if (bOlapPTReport) nFlags |= 0x0004;
1545 : #endif
1546 0 : rStrm << nFlags;
1547 :
1548 : // Feature-specific options. The value differs depending on the table
1549 : // type, but we assume the table type is always pivot table.
1550 0 : sal_uInt32 nOptions = 0x00000000;
1551 : #if 0
1552 : // documentation for which bit is for what
1553 : bool bNoStencil = false;
1554 : bool bHideTotal = false;
1555 : bool bEmptyRows = false;
1556 : bool bEmptyCols = false;
1557 : if (bNoStencil) nOptions |= 0x00000001;
1558 : if (bHideTotal) nOptions |= 0x00000002;
1559 : if (bEmptyRows) nOptions |= 0x00000008;
1560 : if (bEmptyCols) nOptions |= 0x00000010;
1561 : #endif
1562 0 : rStrm << nOptions;
1563 :
1564 : enum ExcelVersion
1565 : {
1566 : Excel2000 = 0,
1567 : ExcelXP = 1,
1568 : Excel2003 = 2,
1569 : Excel2007 = 3
1570 : };
1571 0 : ExcelVersion eXclVer = Excel2000;
1572 0 : sal_uInt8 nOffsetBytes = 16;
1573 0 : rStrm << static_cast<sal_uInt8>(eXclVer) // version table last refreshed
1574 0 : << static_cast<sal_uInt8>(eXclVer) // minimum version to refresh
1575 0 : << nOffsetBytes
1576 0 : << static_cast<sal_uInt8>(eXclVer); // first version created
1577 :
1578 0 : rStrm << XclExpString(maPTInfo.maTableName);
1579 0 : rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
1580 :
1581 0 : rStrm.EndRecord();
1582 0 : }
1583 :
1584 0 : void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
1585 : {
1586 : // Until we sync the autoformat ids only export if using grid header layout
1587 : // That could only have been set via xls import so far.
1588 0 : if ( 0 == maPTViewEx9Info.mnGridLayout )
1589 : {
1590 0 : rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
1591 0 : rStrm << maPTViewEx9Info;
1592 0 : rStrm.EndRecord();
1593 : }
1594 0 : }
1595 :
1596 : namespace {
1597 :
1598 : const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
1599 :
1600 : /** Record wrapper class to write the pivot caches or pivot tables. */
1601 92 : class XclExpPivotRecWrapper : public XclExpRecordBase
1602 : {
1603 : public:
1604 : explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
1605 : virtual void Save( XclExpStream& rStrm ) SAL_OVERRIDE;
1606 : private:
1607 : XclExpPivotTableManager& mrPTMgr;
1608 : SCTAB mnScTab;
1609 : };
1610 :
1611 46 : XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
1612 : mrPTMgr( rPTMgr ),
1613 46 : mnScTab( nScTab )
1614 : {
1615 46 : }
1616 :
1617 46 : void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
1618 : {
1619 46 : if( mnScTab == EXC_PTMGR_PIVOTCACHES )
1620 17 : mrPTMgr.WritePivotCaches( rStrm );
1621 : else
1622 29 : mrPTMgr.WritePivotTables( rStrm, mnScTab );
1623 46 : }
1624 :
1625 : } // namespace
1626 :
1627 72 : XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
1628 : XclExpRoot( rRoot ),
1629 72 : mbShareCaches( true )
1630 : {
1631 72 : }
1632 :
1633 17 : void XclExpPivotTableManager::CreatePivotTables()
1634 : {
1635 17 : if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
1636 17 : for( size_t nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
1637 0 : if( ScDPObject* pDPObj = (*pDPColl)[ nDPObj ] )
1638 0 : if( const XclExpPivotCache* pPCache = CreatePivotCache( *pDPObj ) )
1639 0 : maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), *pDPObj, *pPCache, nDPObj ) );
1640 17 : }
1641 :
1642 17 : XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
1643 : {
1644 17 : return XclExpRecordRef( new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES ) );
1645 : }
1646 :
1647 29 : XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
1648 : {
1649 29 : return XclExpRecordRef( new XclExpPivotRecWrapper( *this, nScTab ) );
1650 : }
1651 :
1652 17 : void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
1653 : {
1654 17 : maPCacheList.Save( rStrm );
1655 17 : }
1656 :
1657 29 : void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
1658 : {
1659 29 : for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
1660 : {
1661 0 : XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
1662 0 : if( xPTable->GetScTab() == nScTab )
1663 0 : xPTable->Save( rStrm );
1664 0 : }
1665 29 : }
1666 :
1667 0 : const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
1668 : {
1669 : // try to find a pivot cache with the same data source
1670 : /* #i25110# In Excel, the pivot cache contains additional fields
1671 : (i.e. grouping info, calculated fields). If the passed DataPilot object
1672 : or the found cache contains this data, do not share the cache with
1673 : multiple pivot tables. */
1674 0 : if( mbShareCaches )
1675 : {
1676 0 : if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1677 : {
1678 0 : const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
1679 : // no dimension save data at all or save data does not contain grouping info
1680 0 : if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
1681 : {
1682 : // check all existing pivot caches
1683 0 : for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
1684 : {
1685 0 : XclExpPivotCacheRef xPCache = maPCacheList.GetRecord( nPos );
1686 : // pivot cache does not have grouping info and source data is equal
1687 0 : if( !xPCache->HasAddFields() && xPCache->HasEqualDataSource( rDPObj ) )
1688 0 : return xPCache.get();
1689 0 : }
1690 : }
1691 : }
1692 : }
1693 :
1694 : // create a new pivot cache
1695 0 : sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
1696 0 : XclExpPivotCacheRef xNewPCache( new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx ) );
1697 0 : if( xNewPCache->IsValid() )
1698 : {
1699 0 : maPCacheList.AppendRecord( xNewPCache );
1700 0 : return xNewPCache.get();
1701 : }
1702 :
1703 0 : return 0;
1704 30 : }
1705 :
1706 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|