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 == NUMBERFORMAT_LOGICAL )
347 0 : InsertOrigBoolItem( fValue != 0 );
348 0 : else if( nFmtType & 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 )) == true )
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 )) == true )
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 )) == true )
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 )) == true )
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 : AddCalcFields( rDPObj );
742 0 : maPCInfo.mnTotalFields = GetFieldCount();
743 0 : };
744 :
745 0 : void XclExpPivotCache::AddStdFields( const ScDPObject& rDPObj )
746 : {
747 : // if item index list is not written, used shortened source range (maDocSrcRange) for performance
748 0 : const ScRange& rRange = HasItemIndexList() ? maExpSrcRange : maDocSrcRange;
749 : // create a standard pivot cache field for each source column
750 0 : for( SCCOL nScCol = rRange.aStart.Col(), nEndScCol = rRange.aEnd.Col(); nScCol <= nEndScCol; ++nScCol )
751 : {
752 0 : ScRange aColRange( rRange );
753 0 : aColRange.aStart.SetCol( nScCol );
754 0 : aColRange.aEnd.SetCol( nScCol );
755 : maFieldList.AppendNewRecord( new XclExpPCField(
756 0 : GetRoot(), *this, GetFieldCount(), rDPObj, aColRange ) );
757 : }
758 0 : }
759 :
760 0 : void XclExpPivotCache::AddGroupFields( const ScDPObject& rDPObj )
761 : {
762 0 : if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
763 : {
764 0 : if( const ScDPDimensionSaveData* pSaveDimData = pSaveData->GetExistingDimensionData() )
765 : {
766 : // loop over all existing standard fields to find their group fields
767 0 : for( sal_uInt16 nFieldIdx = 0; nFieldIdx < maPCInfo.mnStdFields; ++nFieldIdx )
768 : {
769 0 : if( XclExpPCField* pCurrStdField = GetFieldAcc( nFieldIdx ) )
770 : {
771 0 : const ScDPSaveGroupDimension* pGroupDim = pSaveDimData->GetGroupDimForBase( pCurrStdField->GetFieldName() );
772 0 : XclExpPCField* pLastGroupField = pCurrStdField;
773 0 : while( pGroupDim )
774 : {
775 : // insert the new grouping field
776 : XclExpPCFieldRef xNewGroupField( new XclExpPCField(
777 0 : GetRoot(), *this, GetFieldCount(), rDPObj, *pGroupDim, *pCurrStdField ) );
778 0 : maFieldList.AppendRecord( xNewGroupField );
779 :
780 : // register new grouping field at current grouping field, building a chain
781 0 : pLastGroupField->SetGroupChildField( *xNewGroupField );
782 :
783 : // next grouping dimension
784 0 : pGroupDim = pSaveDimData->GetGroupDimForBase( pGroupDim->GetGroupDimName() );
785 0 : pLastGroupField = xNewGroupField.get();
786 0 : }
787 : }
788 : }
789 : }
790 : }
791 0 : }
792 :
793 0 : void XclExpPivotCache::AddCalcFields( const ScDPObject& /*rDPObj*/ )
794 : {
795 : // not supported
796 0 : }
797 :
798 0 : void XclExpPivotCache::WriteDconref( XclExpStream& rStrm ) const
799 : {
800 0 : XclExpString aRef( XclExpUrlHelper::EncodeUrl( GetRoot(), EMPTY_OUSTRING, &maTabName ) );
801 0 : rStrm.StartRecord( EXC_ID_DCONREF, 7 + aRef.GetSize() );
802 0 : rStrm << static_cast< sal_uInt16 >( maExpSrcRange.aStart.Row() )
803 0 : << static_cast< sal_uInt16 >( maExpSrcRange.aEnd.Row() )
804 0 : << static_cast< sal_uInt8 >( maExpSrcRange.aStart.Col() )
805 0 : << static_cast< sal_uInt8 >( maExpSrcRange.aEnd.Col() )
806 0 : << aRef
807 0 : << sal_uInt8( 0 );
808 0 : rStrm.EndRecord();
809 0 : }
810 :
811 0 : void XclExpPivotCache::WriteDConName( XclExpStream& rStrm ) const
812 : {
813 0 : XclExpString aName(maSrcRangeName);
814 0 : rStrm.StartRecord(EXC_ID_DCONNAME, aName.GetSize() + 2);
815 0 : rStrm << aName << sal_uInt16(0);
816 0 : rStrm.EndRecord();
817 0 : }
818 :
819 0 : void XclExpPivotCache::WriteCacheStream()
820 : {
821 0 : SotStorageRef xSvStrg = OpenStorage( EXC_STORAGE_PTCACHE );
822 0 : SotStorageStreamRef xSvStrm = OpenStream( xSvStrg, ScfTools::GetHexStr( maPCInfo.mnStrmId ) );
823 0 : if( xSvStrm.Is() )
824 : {
825 0 : XclExpStream aStrm( *xSvStrm, GetRoot() );
826 : // SXDB
827 0 : WriteSxdb( aStrm );
828 : // SXDBEX
829 0 : WriteSxdbex( aStrm );
830 : // field list (SXFIELD and items)
831 0 : maFieldList.Save( aStrm );
832 : // index table (list of SXINDEXLIST)
833 0 : WriteSxindexlistList( aStrm );
834 : // EOF
835 0 : XclExpEmptyRecord( EXC_ID_EOF ).Save( aStrm );
836 0 : }
837 0 : }
838 :
839 0 : void XclExpPivotCache::WriteSxdb( XclExpStream& rStrm ) const
840 : {
841 0 : rStrm.StartRecord( EXC_ID_SXDB, 21 );
842 0 : rStrm << maPCInfo;
843 0 : rStrm.EndRecord();
844 0 : }
845 :
846 0 : void XclExpPivotCache::WriteSxdbex( XclExpStream& rStrm ) const
847 : {
848 0 : rStrm.StartRecord( EXC_ID_SXDBEX, 12 );
849 0 : rStrm << EXC_SXDBEX_CREATION_DATE
850 0 : << sal_uInt32( 0 ); // number of SXFORMULA records
851 0 : rStrm.EndRecord();
852 0 : }
853 :
854 0 : void XclExpPivotCache::WriteSxindexlistList( XclExpStream& rStrm ) const
855 : {
856 0 : if( HasItemIndexList() )
857 : {
858 0 : sal_Size nRecSize = 0;
859 0 : size_t nPos, nSize = maFieldList.GetSize();
860 0 : for( nPos = 0; nPos < nSize; ++nPos )
861 0 : nRecSize += maFieldList.GetRecord( nPos )->GetIndexSize();
862 :
863 0 : for( sal_uInt32 nSrcRow = 0; nSrcRow < maPCInfo.mnSrcRecs; ++nSrcRow )
864 : {
865 0 : rStrm.StartRecord( EXC_ID_SXINDEXLIST, nRecSize );
866 0 : for( nPos = 0; nPos < nSize; ++nPos )
867 0 : maFieldList.GetRecord( nPos )->WriteIndex( rStrm, nSrcRow );
868 0 : rStrm.EndRecord();
869 : }
870 : }
871 0 : }
872 :
873 : // Pivot table
874 :
875 : namespace {
876 :
877 : /** Returns a display string for a data field containing the field name and aggregation function. */
878 0 : OUString lclGetDataFieldCaption( const OUString& rFieldName, GeneralFunction eFunc )
879 : {
880 0 : OUString aCaption;
881 :
882 0 : sal_uInt16 nResIdx = 0;
883 : using namespace ::com::sun::star::sheet;
884 0 : switch( eFunc )
885 : {
886 0 : case GeneralFunction_SUM: nResIdx = STR_FUN_TEXT_SUM; break;
887 0 : case GeneralFunction_COUNT: nResIdx = STR_FUN_TEXT_COUNT; break;
888 0 : case GeneralFunction_AVERAGE: nResIdx = STR_FUN_TEXT_AVG; break;
889 0 : case GeneralFunction_MAX: nResIdx = STR_FUN_TEXT_MAX; break;
890 0 : case GeneralFunction_MIN: nResIdx = STR_FUN_TEXT_MIN; break;
891 0 : case GeneralFunction_PRODUCT: nResIdx = STR_FUN_TEXT_PRODUCT; break;
892 0 : case GeneralFunction_COUNTNUMS: nResIdx = STR_FUN_TEXT_COUNT; break;
893 0 : case GeneralFunction_STDEV: nResIdx = STR_FUN_TEXT_STDDEV; break;
894 0 : case GeneralFunction_STDEVP: nResIdx = STR_FUN_TEXT_STDDEV; break;
895 0 : case GeneralFunction_VAR: nResIdx = STR_FUN_TEXT_VAR; break;
896 0 : case GeneralFunction_VARP: nResIdx = STR_FUN_TEXT_VAR; break;
897 : default:;
898 : }
899 0 : if( nResIdx )
900 0 : aCaption = ScGlobal::GetRscString( nResIdx ) + " - ";
901 0 : aCaption += rFieldName;
902 0 : return( aCaption );
903 : }
904 :
905 : } // namespace
906 :
907 0 : XclExpPTItem::XclExpPTItem( const XclExpPCField& rCacheField, sal_uInt16 nCacheIdx ) :
908 : XclExpRecord( EXC_ID_SXVI, 8 ),
909 0 : mpCacheItem( rCacheField.GetItem( nCacheIdx ) )
910 : {
911 0 : maItemInfo.mnType = EXC_SXVI_TYPE_DATA;
912 0 : maItemInfo.mnCacheIdx = nCacheIdx;
913 0 : maItemInfo.maVisName.mbUseCache = mpCacheItem != 0;
914 0 : }
915 :
916 0 : XclExpPTItem::XclExpPTItem( sal_uInt16 nItemType, sal_uInt16 nCacheIdx, bool bUseCache ) :
917 : XclExpRecord( EXC_ID_SXVI, 8 ),
918 0 : mpCacheItem( 0 )
919 : {
920 0 : maItemInfo.mnType = nItemType;
921 0 : maItemInfo.mnCacheIdx = nCacheIdx;
922 0 : maItemInfo.maVisName.mbUseCache = bUseCache;
923 0 : }
924 :
925 0 : OUString XclExpPTItem::GetItemName() const
926 : {
927 0 : return mpCacheItem ? mpCacheItem->ConvertToText() : OUString();
928 : }
929 :
930 0 : void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
931 : {
932 : // #i115659# GetIsVisible() is not valid if HasIsVisible() returns false, default is 'visible' then
933 0 : ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, rSaveMem.HasIsVisible() && !rSaveMem.GetIsVisible() );
934 : // #i115659# GetShowDetails() is not valid if HasShowDetails() returns false, default is 'show detail' then
935 0 : ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, rSaveMem.HasShowDetails() && !rSaveMem.GetShowDetails() );
936 :
937 : // visible name
938 0 : const OUString* pVisName = rSaveMem.GetLayoutName();
939 0 : if (pVisName && !pVisName->equals(GetItemName()))
940 0 : maItemInfo.SetVisName(*pVisName);
941 0 : }
942 :
943 0 : void XclExpPTItem::WriteBody( XclExpStream& rStrm )
944 : {
945 0 : rStrm << maItemInfo;
946 0 : }
947 :
948 0 : XclExpPTField::XclExpPTField( const XclExpPivotTable& rPTable, sal_uInt16 nCacheIdx ) :
949 : mrPTable( rPTable ),
950 0 : mpCacheField( rPTable.GetCacheField( nCacheIdx ) )
951 : {
952 0 : maFieldInfo.mnCacheIdx = nCacheIdx;
953 :
954 : // create field items
955 0 : if( mpCacheField )
956 0 : for( sal_uInt16 nItemIdx = 0, nItemCount = mpCacheField->GetItemCount(); nItemIdx < nItemCount; ++nItemIdx )
957 0 : maItemList.AppendNewRecord( new XclExpPTItem( *mpCacheField, nItemIdx ) );
958 0 : maFieldInfo.mnItemCount = static_cast< sal_uInt16 >( maItemList.GetSize() );
959 0 : }
960 :
961 : // data access ----------------------------------------------------------------
962 :
963 0 : OUString XclExpPTField::GetFieldName() const
964 : {
965 0 : return mpCacheField ? mpCacheField->GetFieldName() : OUString();
966 : }
967 :
968 0 : sal_uInt16 XclExpPTField::GetLastDataInfoIndex() const
969 : {
970 : OSL_ENSURE( !maDataInfoVec.empty(), "XclExpPTField::GetLastDataInfoIndex - no data info found" );
971 : // will return 0xFFFF for empty vector -> ok
972 0 : return static_cast< sal_uInt16 >( maDataInfoVec.size() - 1 );
973 : }
974 :
975 0 : sal_uInt16 XclExpPTField::GetItemIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
976 : {
977 0 : for( size_t nPos = 0, nSize = maItemList.GetSize(); nPos < nSize; ++nPos )
978 0 : if( maItemList.GetRecord( nPos )->GetItemName() == rName )
979 0 : return static_cast< sal_uInt16 >( nPos );
980 0 : return nDefaultIdx;
981 : }
982 :
983 : // fill data --------------------------------------------------------------
984 :
985 : /**
986 : * Calc's subtotal names are escaped with backslashes ('\'), while Excel's
987 : * are not escaped at all.
988 : */
989 0 : static OUString lcl_convertCalcSubtotalName(const OUString& rName)
990 : {
991 0 : OUStringBuffer aBuf;
992 0 : const sal_Unicode* p = rName.getStr();
993 0 : sal_Int32 n = rName.getLength();
994 0 : bool bEscaped = false;
995 0 : for (sal_Int32 i = 0; i < n; ++i)
996 : {
997 0 : const sal_Unicode c = p[i];
998 0 : if (!bEscaped && c == '\\')
999 : {
1000 0 : bEscaped = true;
1001 0 : continue;
1002 : }
1003 :
1004 0 : aBuf.append(c);
1005 0 : bEscaped = false;
1006 : }
1007 0 : return aBuf.makeStringAndClear();
1008 : }
1009 :
1010 0 : void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1011 : {
1012 : // orientation
1013 0 : DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() );
1014 : OSL_ENSURE( eOrient != DataPilotFieldOrientation_DATA, "XclExpPTField::SetPropertiesFromDim - called for data field" );
1015 0 : maFieldInfo.AddApiOrient( eOrient );
1016 :
1017 : // show empty items (#i115659# GetShowEmpty() is not valid if HasShowEmpty() returns false, default is false then)
1018 0 : ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.HasShowEmpty() && rSaveDim.GetShowEmpty() );
1019 :
1020 : // visible name
1021 0 : const OUString* pLayoutName = rSaveDim.GetLayoutName();
1022 0 : if (pLayoutName && !pLayoutName->equals(GetFieldName()))
1023 0 : maFieldInfo.SetVisName(*pLayoutName);
1024 :
1025 0 : const OUString* pSubtotalName = rSaveDim.GetSubtotalName();
1026 0 : if (pSubtotalName)
1027 : {
1028 0 : OUString aSubName = lcl_convertCalcSubtotalName(*pSubtotalName);
1029 0 : maFieldExtInfo.mpFieldTotalName.reset(new OUString(aSubName));
1030 : }
1031 :
1032 : // subtotals
1033 0 : XclPTSubtotalVec aSubtotals;
1034 0 : aSubtotals.reserve( static_cast< size_t >( rSaveDim.GetSubTotalsCount() ) );
1035 0 : for( long nSubtIdx = 0, nSubtCount = rSaveDim.GetSubTotalsCount(); nSubtIdx < nSubtCount; ++nSubtIdx )
1036 0 : aSubtotals.push_back( rSaveDim.GetSubTotalFunc( nSubtIdx ) );
1037 0 : maFieldInfo.SetSubtotals( aSubtotals );
1038 :
1039 : // sorting
1040 0 : if( const DataPilotFieldSortInfo* pSortInfo = rSaveDim.GetSortInfo() )
1041 : {
1042 0 : maFieldExtInfo.SetApiSortMode( pSortInfo->Mode );
1043 0 : if( pSortInfo->Mode == ::com::sun::star::sheet::DataPilotFieldSortMode::DATA )
1044 0 : maFieldExtInfo.mnSortField = mrPTable.GetDataFieldIndex( pSortInfo->Field, EXC_SXVDEX_SORT_OWN );
1045 0 : ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SORT_ASC, pSortInfo->IsAscending );
1046 : }
1047 :
1048 : // auto show
1049 0 : if( const DataPilotFieldAutoShowInfo* pShowInfo = rSaveDim.GetAutoShowInfo() )
1050 : {
1051 0 : ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_AUTOSHOW, pShowInfo->IsEnabled );
1052 0 : maFieldExtInfo.SetApiAutoShowMode( pShowInfo->ShowItemsMode );
1053 0 : maFieldExtInfo.SetApiAutoShowCount( pShowInfo->ItemCount );
1054 0 : maFieldExtInfo.mnShowField = mrPTable.GetDataFieldIndex( pShowInfo->DataField, EXC_SXVDEX_SHOW_NONE );
1055 : }
1056 :
1057 : // layout
1058 0 : if( const DataPilotFieldLayoutInfo* pLayoutInfo = rSaveDim.GetLayoutInfo() )
1059 : {
1060 0 : maFieldExtInfo.SetApiLayoutMode( pLayoutInfo->LayoutMode );
1061 0 : ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_LAYOUT_BLANK, pLayoutInfo->AddEmptyLines );
1062 : }
1063 :
1064 : // special page field properties
1065 0 : if( eOrient == DataPilotFieldOrientation_PAGE )
1066 : {
1067 0 : maPageInfo.mnField = GetFieldIndex();
1068 0 : maPageInfo.mnSelItem = EXC_SXPI_ALLITEMS;
1069 : }
1070 :
1071 : // item properties
1072 0 : const ScDPSaveDimension::MemberList &rMembers = rSaveDim.GetMembers();
1073 0 : for (ScDPSaveDimension::MemberList::const_iterator i=rMembers.begin(); i != rMembers.end() ; ++i)
1074 0 : if( XclExpPTItem* pItem = GetItemAcc( (*i)->GetName() ) )
1075 0 : pItem->SetPropertiesFromMember( **i );
1076 0 : }
1077 :
1078 0 : void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1079 : {
1080 0 : maDataInfoVec.push_back( XclPTDataFieldInfo() );
1081 0 : XclPTDataFieldInfo& rDataInfo = maDataInfoVec.back();
1082 0 : rDataInfo.mnField = GetFieldIndex();
1083 :
1084 : // orientation
1085 0 : maFieldInfo.AddApiOrient( DataPilotFieldOrientation_DATA );
1086 :
1087 : // aggregation function
1088 0 : GeneralFunction eFunc = static_cast< GeneralFunction >( rSaveDim.GetFunction() );
1089 0 : rDataInfo.SetApiAggFunc( eFunc );
1090 :
1091 : // visible name
1092 0 : const OUString* pVisName = rSaveDim.GetLayoutName();
1093 0 : if (pVisName)
1094 0 : rDataInfo.SetVisName(*pVisName);
1095 : else
1096 0 : rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
1097 :
1098 : // result field reference
1099 0 : if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
1100 : {
1101 0 : rDataInfo.SetApiRefType( pFieldRef->ReferenceType );
1102 0 : rDataInfo.SetApiRefItemType( pFieldRef->ReferenceItemType );
1103 0 : if( const XclExpPTField* pRefField = mrPTable.GetField( pFieldRef->ReferenceField ) )
1104 : {
1105 0 : rDataInfo.mnRefField = pRefField->GetFieldIndex();
1106 0 : if( pFieldRef->ReferenceItemType == ::com::sun::star::sheet::DataPilotFieldReferenceItemType::NAMED )
1107 0 : rDataInfo.mnRefItem = pRefField->GetItemIndex( pFieldRef->ReferenceItemName, 0 );
1108 : }
1109 : }
1110 0 : }
1111 :
1112 0 : void XclExpPTField::AppendSubtotalItems()
1113 : {
1114 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_DEFAULT ) AppendSubtotalItem( EXC_SXVI_TYPE_DEFAULT );
1115 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_SUM ) AppendSubtotalItem( EXC_SXVI_TYPE_SUM );
1116 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNT ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNT );
1117 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_AVERAGE ) AppendSubtotalItem( EXC_SXVI_TYPE_AVERAGE );
1118 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MAX ) AppendSubtotalItem( EXC_SXVI_TYPE_MAX );
1119 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_MIN ) AppendSubtotalItem( EXC_SXVI_TYPE_MIN );
1120 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_PROD ) AppendSubtotalItem( EXC_SXVI_TYPE_PROD );
1121 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_COUNTNUM ) AppendSubtotalItem( EXC_SXVI_TYPE_COUNTNUM );
1122 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEV ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEV );
1123 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_STDDEVP ) AppendSubtotalItem( EXC_SXVI_TYPE_STDDEVP );
1124 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VAR ) AppendSubtotalItem( EXC_SXVI_TYPE_VAR );
1125 0 : if( maFieldInfo.mnSubtotals & EXC_SXVD_SUBT_VARP ) AppendSubtotalItem( EXC_SXVI_TYPE_VARP );
1126 0 : }
1127 :
1128 : // records --------------------------------------------------------------------
1129 :
1130 0 : void XclExpPTField::WriteSxpiEntry( XclExpStream& rStrm ) const
1131 : {
1132 0 : rStrm << maPageInfo;
1133 0 : }
1134 :
1135 0 : void XclExpPTField::WriteSxdi( XclExpStream& rStrm, sal_uInt16 nDataInfoIdx ) const
1136 : {
1137 : OSL_ENSURE( nDataInfoIdx < maDataInfoVec.size(), "XclExpPTField::WriteSxdi - data field not found" );
1138 0 : if( nDataInfoIdx < maDataInfoVec.size() )
1139 : {
1140 0 : rStrm.StartRecord( EXC_ID_SXDI, 12 );
1141 0 : rStrm << maDataInfoVec[ nDataInfoIdx ];
1142 0 : rStrm.EndRecord();
1143 : }
1144 0 : }
1145 :
1146 0 : void XclExpPTField::Save( XclExpStream& rStrm )
1147 : {
1148 : // SXVD
1149 0 : WriteSxvd( rStrm );
1150 : // list of SXVI records
1151 0 : maItemList.Save( rStrm );
1152 : // SXVDEX
1153 0 : WriteSxvdex( rStrm );
1154 0 : }
1155 :
1156 : // private --------------------------------------------------------------------
1157 :
1158 0 : XclExpPTItem* XclExpPTField::GetItemAcc( const OUString& rName )
1159 : {
1160 0 : XclExpPTItem* pItem = 0;
1161 0 : for( size_t nPos = 0, nSize = maItemList.GetSize(); !pItem && (nPos < nSize); ++nPos )
1162 0 : if( maItemList.GetRecord( nPos )->GetItemName() == rName )
1163 0 : pItem = maItemList.GetRecord( nPos ).get();
1164 0 : return pItem;
1165 : }
1166 :
1167 0 : void XclExpPTField::AppendSubtotalItem( sal_uInt16 nItemType )
1168 : {
1169 0 : maItemList.AppendNewRecord( new XclExpPTItem( nItemType, EXC_SXVI_DEFAULT_CACHE, true ) );
1170 0 : ++maFieldInfo.mnItemCount;
1171 0 : }
1172 :
1173 0 : void XclExpPTField::WriteSxvd( XclExpStream& rStrm ) const
1174 : {
1175 0 : rStrm.StartRecord( EXC_ID_SXVD, 10 );
1176 0 : rStrm << maFieldInfo;
1177 0 : rStrm.EndRecord();
1178 0 : }
1179 :
1180 0 : void XclExpPTField::WriteSxvdex( XclExpStream& rStrm ) const
1181 : {
1182 0 : rStrm.StartRecord( EXC_ID_SXVDEX, 20 );
1183 0 : rStrm << maFieldExtInfo;
1184 0 : rStrm.EndRecord();
1185 0 : }
1186 :
1187 0 : XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& rDPObj, const XclExpPivotCache& rPCache, size_t ) :
1188 : XclExpRoot( rRoot ),
1189 : mrPCache( rPCache ),
1190 : maDataOrientField( *this, EXC_SXIVD_DATA ),
1191 : mnOutScTab( 0 ),
1192 : mbValid( false ),
1193 0 : mbFilterBtn( false )
1194 : {
1195 0 : const ScRange& rOutScRange = rDPObj.GetOutRange();
1196 0 : if( GetAddressConverter().ConvertRange( maPTInfo.maOutXclRange, rOutScRange, true ) )
1197 : {
1198 : // DataPilot properties -----------------------------------------------
1199 :
1200 : // pivot table properties from DP object
1201 0 : mnOutScTab = rOutScRange.aStart.Tab();
1202 0 : maPTInfo.maTableName = rDPObj.GetName();
1203 0 : maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
1204 :
1205 0 : maPTViewEx9Info.Init( rDPObj );
1206 :
1207 0 : if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1208 : {
1209 : // additional properties from ScDPSaveData
1210 0 : SetPropertiesFromDP( *pSaveData );
1211 :
1212 : // loop over all dimensions ---------------------------------------
1213 :
1214 : /* 1) Default-construct all pivot table fields for all pivot cache fields. */
1215 0 : for( sal_uInt16 nFieldIdx = 0, nFieldCount = mrPCache.GetFieldCount(); nFieldIdx < nFieldCount; ++nFieldIdx )
1216 0 : maFieldList.AppendNewRecord( new XclExpPTField( *this, nFieldIdx ) );
1217 :
1218 0 : boost::ptr_vector<ScDPSaveDimension>::const_iterator iter;
1219 0 : const ScDPSaveData::DimsType& rDimList = pSaveData->GetDimensions();
1220 :
1221 : /* 2) First process all data dimensions, they are needed for extended
1222 : settings of row/column/page fields (sorting/auto show). */
1223 0 : for (iter = rDimList.begin(); iter != rDimList.end(); ++iter)
1224 0 : if (iter->GetOrientation() == DataPilotFieldOrientation_DATA)
1225 0 : SetDataFieldPropertiesFromDim(*iter);
1226 :
1227 : /* 3) Row/column/page/hidden fields. */
1228 0 : for (iter = rDimList.begin(); iter != rDimList.end(); ++iter)
1229 0 : if (iter->GetOrientation() != DataPilotFieldOrientation_DATA)
1230 0 : SetFieldPropertiesFromDim(*iter);
1231 :
1232 : // Finalize -------------------------------------------------------
1233 :
1234 0 : Finalize();
1235 0 : mbValid = true;
1236 : }
1237 : }
1238 0 : }
1239 :
1240 0 : const XclExpPCField* XclExpPivotTable::GetCacheField( sal_uInt16 nCacheIdx ) const
1241 : {
1242 0 : return mrPCache.GetField( nCacheIdx );
1243 : }
1244 :
1245 0 : const XclExpPTField* XclExpPivotTable::GetField( sal_uInt16 nFieldIdx ) const
1246 : {
1247 0 : return (nFieldIdx == EXC_SXIVD_DATA) ? &maDataOrientField : maFieldList.GetRecord( nFieldIdx ).get();
1248 : }
1249 :
1250 0 : const XclExpPTField* XclExpPivotTable::GetField( const OUString& rName ) const
1251 : {
1252 0 : return const_cast< XclExpPivotTable* >( this )->GetFieldAcc( rName );
1253 : }
1254 :
1255 0 : sal_uInt16 XclExpPivotTable::GetDataFieldIndex( const OUString& rName, sal_uInt16 nDefaultIdx ) const
1256 : {
1257 0 : for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt )
1258 0 : if( const XclExpPTField* pField = GetField( aIt->first ) )
1259 0 : if( pField->GetFieldName() == rName )
1260 0 : return static_cast< sal_uInt16 >( aIt - maDataFields.begin() );
1261 0 : return nDefaultIdx;
1262 : }
1263 :
1264 0 : void XclExpPivotTable::Save( XclExpStream& rStrm )
1265 : {
1266 0 : if( mbValid )
1267 : {
1268 : // SXVIEW
1269 0 : WriteSxview( rStrm );
1270 : // pivot table fields (SXVD, SXVDEX, and item records)
1271 0 : maFieldList.Save( rStrm );
1272 : // SXIVD records for row and column fields
1273 0 : WriteSxivd( rStrm, maRowFields );
1274 0 : WriteSxivd( rStrm, maColFields );
1275 : // SXPI
1276 0 : WriteSxpi( rStrm );
1277 : // list of SXDI records containing data field info
1278 0 : WriteSxdiList( rStrm );
1279 : // SXLI records
1280 0 : WriteSxli( rStrm, maPTInfo.mnDataRows, maPTInfo.mnRowFields );
1281 0 : WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
1282 : // SXEX
1283 0 : WriteSxex( rStrm );
1284 : // QSISXTAG
1285 0 : WriteQsiSxTag( rStrm );
1286 : // SXVIEWEX9
1287 0 : WriteSxViewEx9( rStrm );
1288 : }
1289 0 : }
1290 :
1291 0 : XclExpPTField* XclExpPivotTable::GetFieldAcc( const OUString& rName )
1292 : {
1293 0 : XclExpPTField* pField = 0;
1294 0 : for( size_t nPos = 0, nSize = maFieldList.GetSize(); !pField && (nPos < nSize); ++nPos )
1295 0 : if( maFieldList.GetRecord( nPos )->GetFieldName() == rName )
1296 0 : pField = maFieldList.GetRecord( nPos ).get();
1297 0 : return pField;
1298 : }
1299 :
1300 0 : XclExpPTField* XclExpPivotTable::GetFieldAcc( const ScDPSaveDimension& rSaveDim )
1301 : {
1302 : // data field orientation field?
1303 0 : if( rSaveDim.IsDataLayout() )
1304 0 : return &maDataOrientField;
1305 :
1306 : // a real dimension
1307 0 : OUString aFieldName = ScDPUtil::getSourceDimensionName(rSaveDim.GetName());
1308 0 : return aFieldName.isEmpty() ? NULL : GetFieldAcc(aFieldName);
1309 : }
1310 :
1311 : // fill data --------------------------------------------------------------
1312 :
1313 0 : void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
1314 : {
1315 0 : ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_ROWGRAND, rSaveData.GetRowGrand() );
1316 0 : ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
1317 0 : ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
1318 0 : mbFilterBtn = rSaveData.GetFilterButton();
1319 0 : const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
1320 0 : if (!pDim)
1321 0 : return;
1322 :
1323 0 : const OUString* pLayoutName = pDim->GetLayoutName();
1324 0 : if (pLayoutName)
1325 0 : maPTInfo.maDataName = *pLayoutName;
1326 : else
1327 0 : maPTInfo.maDataName = ScGlobal::GetRscString(STR_PIVOT_DATA);
1328 : }
1329 :
1330 0 : void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1331 : {
1332 0 : if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1333 : {
1334 : // field properties
1335 0 : pField->SetPropertiesFromDim( rSaveDim );
1336 :
1337 : // update the corresponding field position list
1338 0 : DataPilotFieldOrientation eOrient = static_cast< DataPilotFieldOrientation >( rSaveDim.GetOrientation() );
1339 0 : sal_uInt16 nFieldIdx = pField->GetFieldIndex();
1340 0 : bool bDataLayout = nFieldIdx == EXC_SXIVD_DATA;
1341 0 : bool bMultiData = maDataFields.size() > 1;
1342 :
1343 0 : if( !bDataLayout || bMultiData ) switch( eOrient )
1344 : {
1345 : case DataPilotFieldOrientation_ROW:
1346 0 : maRowFields.push_back( nFieldIdx );
1347 0 : if( bDataLayout )
1348 0 : maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1349 0 : break;
1350 : case DataPilotFieldOrientation_COLUMN:
1351 0 : maColFields.push_back( nFieldIdx );
1352 0 : if( bDataLayout )
1353 0 : maPTInfo.mnDataAxis = EXC_SXVD_AXIS_COL;
1354 0 : break;
1355 : case DataPilotFieldOrientation_PAGE:
1356 0 : maPageFields.push_back( nFieldIdx );
1357 : OSL_ENSURE( !bDataLayout, "XclExpPivotTable::SetFieldPropertiesFromDim - wrong orientation for data fields" );
1358 0 : break;
1359 : case DataPilotFieldOrientation_DATA:
1360 : OSL_FAIL( "XclExpPivotTable::SetFieldPropertiesFromDim - called for data field" );
1361 0 : break;
1362 : default:;
1363 : }
1364 : }
1365 0 : }
1366 :
1367 0 : void XclExpPivotTable::SetDataFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
1368 : {
1369 0 : if( XclExpPTField* pField = GetFieldAcc( rSaveDim ) )
1370 : {
1371 : // field properties
1372 0 : pField->SetDataPropertiesFromDim( rSaveDim );
1373 : // update the data field position list
1374 0 : maDataFields.push_back( XclPTDataFieldPos( pField->GetFieldIndex(), pField->GetLastDataInfoIndex() ) );
1375 : }
1376 0 : }
1377 :
1378 0 : void XclExpPivotTable::Finalize()
1379 : {
1380 : // field numbers
1381 0 : maPTInfo.mnFields = static_cast< sal_uInt16 >( maFieldList.GetSize() );
1382 0 : maPTInfo.mnRowFields = static_cast< sal_uInt16 >( maRowFields.size() );
1383 0 : maPTInfo.mnColFields = static_cast< sal_uInt16 >( maColFields.size() );
1384 0 : maPTInfo.mnPageFields = static_cast< sal_uInt16 >( maPageFields.size() );
1385 0 : maPTInfo.mnDataFields = static_cast< sal_uInt16 >( maDataFields.size() );
1386 :
1387 0 : maPTExtInfo.mnPagePerRow = maPTInfo.mnPageFields;
1388 0 : maPTExtInfo.mnPagePerCol = (maPTInfo.mnPageFields > 0) ? 1 : 0;
1389 :
1390 : // subtotal items
1391 0 : for( size_t nPos = 0, nSize = maFieldList.GetSize(); nPos < nSize; ++nPos )
1392 0 : maFieldList.GetRecord( nPos )->AppendSubtotalItems();
1393 :
1394 : // find data field orientation field
1395 0 : maPTInfo.mnDataPos = EXC_SXVIEW_DATALAST;
1396 0 : const ScfUInt16Vec* pFieldVec = 0;
1397 0 : switch( maPTInfo.mnDataAxis )
1398 : {
1399 0 : case EXC_SXVD_AXIS_ROW: pFieldVec = &maRowFields; break;
1400 0 : case EXC_SXVD_AXIS_COL: pFieldVec = &maColFields; break;
1401 : }
1402 :
1403 0 : if( pFieldVec && !pFieldVec->empty() && (pFieldVec->back() != EXC_SXIVD_DATA) )
1404 : {
1405 0 : ScfUInt16Vec::const_iterator aIt = ::std::find( pFieldVec->begin(), pFieldVec->end(), EXC_SXIVD_DATA );
1406 0 : if( aIt != pFieldVec->end() )
1407 0 : maPTInfo.mnDataPos = static_cast< sal_uInt16 >( aIt - pFieldVec->begin() );
1408 : }
1409 :
1410 : // single data field is always row oriented
1411 0 : if( maPTInfo.mnDataAxis == EXC_SXVD_AXIS_NONE )
1412 0 : maPTInfo.mnDataAxis = EXC_SXVD_AXIS_ROW;
1413 :
1414 : // update output range (initialized in ctor)
1415 0 : sal_uInt16& rnXclCol1 = maPTInfo.maOutXclRange.maFirst.mnCol;
1416 0 : sal_uInt32& rnXclRow1 = maPTInfo.maOutXclRange.maFirst.mnRow;
1417 0 : sal_uInt16& rnXclCol2 = maPTInfo.maOutXclRange.maLast.mnCol;
1418 0 : sal_uInt32& rnXclRow2 = maPTInfo.maOutXclRange.maLast.mnRow;
1419 : // exclude page fields from output range
1420 0 : rnXclRow1 = rnXclRow1 + maPTInfo.mnPageFields;
1421 : // exclude filter button from output range
1422 0 : if( mbFilterBtn )
1423 0 : ++rnXclRow1;
1424 : // exclude empty row between (filter button and/or page fields) and table
1425 0 : if( mbFilterBtn || maPTInfo.mnPageFields )
1426 0 : ++rnXclRow1;
1427 :
1428 : // data area
1429 0 : sal_uInt16& rnDataXclCol = maPTInfo.maDataXclPos.mnCol;
1430 0 : sal_uInt32& rnDataXclRow = maPTInfo.maDataXclPos.mnRow;
1431 0 : rnDataXclCol = rnXclCol1 + maPTInfo.mnRowFields;
1432 0 : rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
1433 0 : if( maDataFields.empty() )
1434 0 : ++rnDataXclRow;
1435 :
1436 0 : bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
1437 0 : if (bExtraHeaderRow)
1438 : // Insert an extra row only when there is no column field.
1439 0 : ++rnDataXclRow;
1440 :
1441 0 : rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
1442 0 : rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
1443 0 : maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
1444 0 : maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
1445 :
1446 : // first heading
1447 0 : maPTInfo.mnFirstHeadRow = rnXclRow1;
1448 0 : if (bExtraHeaderRow)
1449 0 : maPTInfo.mnFirstHeadRow += 2;
1450 0 : }
1451 :
1452 : // records ----------------------------------------------------------------
1453 :
1454 0 : void XclExpPivotTable::WriteSxview( XclExpStream& rStrm ) const
1455 : {
1456 0 : rStrm.StartRecord( EXC_ID_SXVIEW, 46 + maPTInfo.maTableName.getLength() + maPTInfo.maDataName.getLength() );
1457 0 : rStrm << maPTInfo;
1458 0 : rStrm.EndRecord();
1459 0 : }
1460 :
1461 0 : void XclExpPivotTable::WriteSxivd( XclExpStream& rStrm, const ScfUInt16Vec& rFields ) const
1462 : {
1463 0 : if( !rFields.empty() )
1464 : {
1465 0 : rStrm.StartRecord( EXC_ID_SXIVD, rFields.size() * 2 );
1466 0 : for( ScfUInt16Vec::const_iterator aIt = rFields.begin(), aEnd = rFields.end(); aIt != aEnd; ++aIt )
1467 0 : rStrm << *aIt;
1468 0 : rStrm.EndRecord();
1469 : }
1470 0 : }
1471 :
1472 0 : void XclExpPivotTable::WriteSxpi( XclExpStream& rStrm ) const
1473 : {
1474 0 : if( !maPageFields.empty() )
1475 : {
1476 0 : rStrm.StartRecord( EXC_ID_SXPI, maPageFields.size() * 6 );
1477 0 : rStrm.SetSliceSize( 6 );
1478 0 : for( ScfUInt16Vec::const_iterator aIt = maPageFields.begin(), aEnd = maPageFields.end(); aIt != aEnd; ++aIt )
1479 : {
1480 0 : XclExpPTFieldRef xField = maFieldList.GetRecord( *aIt );
1481 0 : if( xField )
1482 0 : xField->WriteSxpiEntry( rStrm );
1483 0 : }
1484 0 : rStrm.EndRecord();
1485 : }
1486 0 : }
1487 :
1488 0 : void XclExpPivotTable::WriteSxdiList( XclExpStream& rStrm ) const
1489 : {
1490 0 : for( XclPTDataFieldPosVec::const_iterator aIt = maDataFields.begin(), aEnd = maDataFields.end(); aIt != aEnd; ++aIt )
1491 : {
1492 0 : XclExpPTFieldRef xField = maFieldList.GetRecord( aIt->first );
1493 0 : if( xField )
1494 0 : xField->WriteSxdi( rStrm, aIt->second );
1495 0 : }
1496 0 : }
1497 :
1498 0 : void XclExpPivotTable::WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount ) const
1499 : {
1500 0 : if( nLineCount > 0 )
1501 : {
1502 0 : sal_Size nLineSize = 8 + 2 * nIndexCount;
1503 0 : rStrm.StartRecord( EXC_ID_SXLI, nLineSize * nLineCount );
1504 :
1505 : /* Excel expects the records to be filled completely, do not
1506 : set a segment size... */
1507 : // rStrm.SetSliceSize( nLineSize );
1508 :
1509 0 : for( sal_uInt16 nLine = 0; nLine < nLineCount; ++nLine )
1510 : {
1511 : // Excel XP needs a partly initialized SXLI record
1512 0 : rStrm << sal_uInt16( 0 ) // number of equal index entries
1513 0 : << EXC_SXVI_TYPE_DATA
1514 0 : << nIndexCount
1515 0 : << EXC_SXLI_DEFAULTFLAGS;
1516 0 : rStrm.WriteZeroBytes( 2 * nIndexCount );
1517 : }
1518 0 : rStrm.EndRecord();
1519 : }
1520 0 : }
1521 :
1522 0 : void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
1523 : {
1524 0 : rStrm.StartRecord( EXC_ID_SXEX, 24 );
1525 0 : rStrm << maPTExtInfo;
1526 0 : rStrm.EndRecord();
1527 0 : }
1528 :
1529 0 : void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
1530 : {
1531 0 : rStrm.StartRecord( 0x0802, 32 );
1532 :
1533 0 : sal_uInt16 nRecordType = 0x0802;
1534 0 : sal_uInt16 nDummyFlags = 0x0000;
1535 0 : sal_uInt16 nTableType = 1; // 0 = query table : 1 = pivot table
1536 :
1537 0 : rStrm << nRecordType << nDummyFlags << nTableType;
1538 :
1539 : // General flags
1540 0 : sal_uInt16 nFlags = 0x0001;
1541 : #if 0
1542 : // for doc purpose
1543 : sal_uInt16 nFlags = 0x0000;
1544 : bool bEnableRefresh = true;
1545 : bool bPCacheInvalid = false;
1546 : bool bOlapPTReport = false;
1547 :
1548 : if (bEnableRefresh) nFlags |= 0x0001;
1549 : if (bPCacheInvalid) nFlags |= 0x0002;
1550 : if (bOlapPTReport) nFlags |= 0x0004;
1551 : #endif
1552 0 : rStrm << nFlags;
1553 :
1554 : // Feature-specific options. The value differs depending on the table
1555 : // type, but we assume the table type is always pivot table.
1556 0 : sal_uInt32 nOptions = 0x00000000;
1557 : #if 0
1558 : // documentation for which bit is for what
1559 : bool bNoStencil = false;
1560 : bool bHideTotal = false;
1561 : bool bEmptyRows = false;
1562 : bool bEmptyCols = false;
1563 : if (bNoStencil) nOptions |= 0x00000001;
1564 : if (bHideTotal) nOptions |= 0x00000002;
1565 : if (bEmptyRows) nOptions |= 0x00000008;
1566 : if (bEmptyCols) nOptions |= 0x00000010;
1567 : #endif
1568 0 : rStrm << nOptions;
1569 :
1570 : enum ExcelVersion
1571 : {
1572 : Excel2000 = 0,
1573 : ExcelXP = 1,
1574 : Excel2003 = 2,
1575 : Excel2007 = 3
1576 : };
1577 0 : ExcelVersion eXclVer = Excel2000;
1578 0 : sal_uInt8 nOffsetBytes = 16;
1579 0 : rStrm << static_cast<sal_uInt8>(eXclVer) // version table last refreshed
1580 0 : << static_cast<sal_uInt8>(eXclVer) // minimum version to refresh
1581 0 : << nOffsetBytes
1582 0 : << static_cast<sal_uInt8>(eXclVer); // first version created
1583 :
1584 0 : rStrm << XclExpString(maPTInfo.maTableName);
1585 0 : rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
1586 :
1587 0 : rStrm.EndRecord();
1588 0 : }
1589 :
1590 0 : void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
1591 : {
1592 : // Until we sync the autoformat ids only export if using grid header layout
1593 : // That could only have been set via xls import so far.
1594 0 : if ( 0 == maPTViewEx9Info.mnGridLayout )
1595 : {
1596 0 : rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
1597 0 : rStrm << maPTViewEx9Info;
1598 0 : rStrm.EndRecord();
1599 : }
1600 0 : }
1601 :
1602 : namespace {
1603 :
1604 : const SCTAB EXC_PTMGR_PIVOTCACHES = SCTAB_MAX;
1605 :
1606 : /** Record wrapper class to write the pivot caches or pivot tables. */
1607 152 : class XclExpPivotRecWrapper : public XclExpRecordBase
1608 : {
1609 : public:
1610 : explicit XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab );
1611 : virtual void Save( XclExpStream& rStrm ) SAL_OVERRIDE;
1612 : private:
1613 : XclExpPivotTableManager& mrPTMgr;
1614 : SCTAB mnScTab;
1615 : };
1616 :
1617 76 : XclExpPivotRecWrapper::XclExpPivotRecWrapper( XclExpPivotTableManager& rPTMgr, SCTAB nScTab ) :
1618 : mrPTMgr( rPTMgr ),
1619 76 : mnScTab( nScTab )
1620 : {
1621 76 : }
1622 :
1623 76 : void XclExpPivotRecWrapper::Save( XclExpStream& rStrm )
1624 : {
1625 76 : if( mnScTab == EXC_PTMGR_PIVOTCACHES )
1626 26 : mrPTMgr.WritePivotCaches( rStrm );
1627 : else
1628 50 : mrPTMgr.WritePivotTables( rStrm, mnScTab );
1629 76 : }
1630 :
1631 : } // namespace
1632 :
1633 64 : XclExpPivotTableManager::XclExpPivotTableManager( const XclExpRoot& rRoot ) :
1634 : XclExpRoot( rRoot ),
1635 64 : mbShareCaches( true )
1636 : {
1637 64 : }
1638 :
1639 26 : void XclExpPivotTableManager::CreatePivotTables()
1640 : {
1641 26 : if( ScDPCollection* pDPColl = GetDoc().GetDPCollection() )
1642 26 : for( size_t nDPObj = 0, nCount = pDPColl->GetCount(); nDPObj < nCount; ++nDPObj )
1643 0 : if( ScDPObject* pDPObj = (*pDPColl)[ nDPObj ] )
1644 0 : if( const XclExpPivotCache* pPCache = CreatePivotCache( *pDPObj ) )
1645 0 : maPTableList.AppendNewRecord( new XclExpPivotTable( GetRoot(), *pDPObj, *pPCache, nDPObj ) );
1646 26 : }
1647 :
1648 26 : XclExpRecordRef XclExpPivotTableManager::CreatePivotCachesRecord()
1649 : {
1650 26 : return XclExpRecordRef( new XclExpPivotRecWrapper( *this, EXC_PTMGR_PIVOTCACHES ) );
1651 : }
1652 :
1653 50 : XclExpRecordRef XclExpPivotTableManager::CreatePivotTablesRecord( SCTAB nScTab )
1654 : {
1655 50 : return XclExpRecordRef( new XclExpPivotRecWrapper( *this, nScTab ) );
1656 : }
1657 :
1658 26 : void XclExpPivotTableManager::WritePivotCaches( XclExpStream& rStrm )
1659 : {
1660 26 : maPCacheList.Save( rStrm );
1661 26 : }
1662 :
1663 50 : void XclExpPivotTableManager::WritePivotTables( XclExpStream& rStrm, SCTAB nScTab )
1664 : {
1665 50 : for( size_t nPos = 0, nSize = maPTableList.GetSize(); nPos < nSize; ++nPos )
1666 : {
1667 0 : XclExpPivotTableRef xPTable = maPTableList.GetRecord( nPos );
1668 0 : if( xPTable->GetScTab() == nScTab )
1669 0 : xPTable->Save( rStrm );
1670 0 : }
1671 50 : }
1672 :
1673 0 : const XclExpPivotCache* XclExpPivotTableManager::CreatePivotCache( const ScDPObject& rDPObj )
1674 : {
1675 : // try to find a pivot cache with the same data source
1676 : /* #i25110# In Excel, the pivot cache contains additional fields
1677 : (i.e. grouping info, calculated fields). If the passed DataPilot object
1678 : or the found cache contains this data, do not share the cache with
1679 : multiple pivot tables. */
1680 0 : if( mbShareCaches )
1681 : {
1682 0 : if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
1683 : {
1684 0 : const ScDPDimensionSaveData* pDimSaveData = pSaveData->GetExistingDimensionData();
1685 : // no dimension save data at all or save data does not contain grouping info
1686 0 : if( !pDimSaveData || !pDimSaveData->HasGroupDimensions() )
1687 : {
1688 : // check all existing pivot caches
1689 0 : for( size_t nPos = 0, nSize = maPCacheList.GetSize(); nPos < nSize; ++nPos )
1690 : {
1691 0 : XclExpPivotCacheRef xPCache = maPCacheList.GetRecord( nPos );
1692 : // pivot cache does not have grouping info and source data is equal
1693 0 : if( !xPCache->HasAddFields() && xPCache->HasEqualDataSource( rDPObj ) )
1694 0 : return xPCache.get();
1695 0 : }
1696 : }
1697 : }
1698 : }
1699 :
1700 : // create a new pivot cache
1701 0 : sal_uInt16 nNewCacheIdx = static_cast< sal_uInt16 >( maPCacheList.GetSize() );
1702 0 : XclExpPivotCacheRef xNewPCache( new XclExpPivotCache( GetRoot(), rDPObj, nNewCacheIdx ) );
1703 0 : if( xNewPCache->IsValid() )
1704 : {
1705 0 : maPCacheList.AppendRecord( xNewPCache );
1706 0 : return xNewPCache.get();
1707 : }
1708 :
1709 0 : return 0;
1710 48 : }
1711 :
1712 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|