ooo-build r15086 - in branches/ooo-build-3-0-1: . patches/dev300
- From: kyoshida svn gnome org
- To: svn-commits-list gnome org
- Subject: ooo-build r15086 - in branches/ooo-build-3-0-1: . patches/dev300
- Date: Thu, 15 Jan 2009 22:24:33 +0000 (UTC)
Author: kyoshida
Date: Thu Jan 15 22:24:33 2009
New Revision: 15086
URL: http://svn.gnome.org/viewvc/ooo-build?rev=15086&view=rev
Log:
2009-01-15 Kohei Yoshida <kyoshida novell com>
* patches/dev300/calc-dp-custom-names-offapi.diff:
* patches/dev300/calc-dp-custom-names-sc.diff:
* patches/dev300/apply: ported from trunk, but are in CalcExperimental
section which is disabled by default.
Added:
branches/ooo-build-3-0-1/patches/dev300/calc-dp-custom-names-offapi.diff
branches/ooo-build-3-0-1/patches/dev300/calc-dp-custom-names-sc.diff
Modified:
branches/ooo-build-3-0-1/ChangeLog
branches/ooo-build-3-0-1/patches/dev300/apply
Modified: branches/ooo-build-3-0-1/patches/dev300/apply
==============================================================================
--- branches/ooo-build-3-0-1/patches/dev300/apply (original)
+++ branches/ooo-build-3-0-1/patches/dev300/apply Thu Jan 15 22:24:33 2009
@@ -1825,6 +1825,9 @@
calc-external-defined-names-svtools.diff, i#3740, i#4385, n#355685, kohei
calc-external-defined-names-officecfg.diff, i#3740, i#4385, n#355685, kohei
+# support custom names in data pilot tables.
+calc-dp-custom-names-sc.diff, n#338014, i#22029, kohei
+calc-dp-custom-names-offapi.diff, n#338014, i#22029, kohei
[ CalcSolver ]
SectionOwner => kohei
Added: branches/ooo-build-3-0-1/patches/dev300/calc-dp-custom-names-offapi.diff
==============================================================================
--- (empty file)
+++ branches/ooo-build-3-0-1/patches/dev300/calc-dp-custom-names-offapi.diff Thu Jan 15 22:24:33 2009
@@ -0,0 +1,22 @@
+diff --git offapi/com/sun/star/sheet/MemberResultFlags.idl offapi/com/sun/star/sheet/MemberResultFlags.idl
+index 788673c..943e9ac 100644
+--- offapi/com/sun/star/sheet/MemberResultFlags.idl
++++ offapi/com/sun/star/sheet/MemberResultFlags.idl
+@@ -59,6 +59,17 @@ published constants MemberResultFlags
+ */
+ const long CONTINUE = 4;
+
++ //-------------------------------------------------------------------------
++
++ /** The element contains a grand total.
++ */
++ const long GRANDTOTAL = 8;
++
++ //-------------------------------------------------------------------------
++
++ /** The element is a numeric value.
++ */
++ const long NUMERIC = 16;
+ };
+
+ //=============================================================================
Added: branches/ooo-build-3-0-1/patches/dev300/calc-dp-custom-names-sc.diff
==============================================================================
--- (empty file)
+++ branches/ooo-build-3-0-1/patches/dev300/calc-dp-custom-names-sc.diff Thu Jan 15 22:24:33 2009
@@ -0,0 +1,2993 @@
+diff --git sc/inc/dpobject.hxx sc/inc/dpobject.hxx
+index 6a2978b..d195d13 100644
+--- sc/inc/dpobject.hxx
++++ sc/inc/dpobject.hxx
+@@ -38,6 +38,8 @@
+ #include "dpoutput.hxx"
+ #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
+
++#include <memory>
++
+ //------------------------------------------------------------------
+
+ namespace com { namespace sun { namespace star { namespace sheet {
+@@ -167,7 +169,16 @@ public:
+ void SetTag(const String& rNew);
+ const String& GetTag() const { return aTableTag; }
+
+- BOOL IsDimNameInUse( const String& rName ) const;
++ /**
++ * Data description cell displays the description of a data dimension if
++ * and only if there is only one data dimension. It's usually located at
++ * the upper-left corner of the table output.
++ */
++ bool IsDataDescriptionCell(const ScAddress& rPos);
++
++ bool IsGrandTotalCell(const ScAddress& rPos);
++
++ bool IsDimNameInUse(const ::rtl::OUString& rName) const;
+ String GetDimName( long nDim, BOOL& rIsDataLayout );
+ BOOL IsDuplicated( long nDim );
+ long GetDimCount();
+@@ -293,12 +304,9 @@ public:
+
+ ScDPObject* operator[](USHORT nIndex) const {return (ScDPObject*)At(nIndex);}
+
+- BOOL StoreOld( SvStream& rStream ) const;
+- BOOL StoreNew( SvStream& rStream ) const;
+- BOOL LoadNew( SvStream& rStream );
+-
++#if OLD_PIVOT_IMPLEMENTATION
+ void ConvertOldTables( ScPivotCollection& rOldColl );
+-
++#endif
+ void DeleteOnTab( SCTAB nTab );
+ void UpdateReference( UpdateRefMode eUpdateRefMode,
+ const ScRange& r, SCsCOL nDx, SCsROW nDy, SCsTAB nDz );
+diff --git sc/inc/dpsave.hxx sc/inc/dpsave.hxx
+index 5d794ac..f047bc7 100644
+--- sc/inc/dpsave.hxx
++++ sc/inc/dpsave.hxx
+@@ -34,9 +34,11 @@
+ #include <tools/string.hxx>
+ #include <tools/list.hxx>
+ #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
++#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
+ #include "scdllapi.h"
+ #include <hash_map>
+ #include <list>
++#include <memory>
+
+ namespace com { namespace sun { namespace star { namespace sheet {
+ struct DataPilotFieldReference;
+@@ -58,13 +60,13 @@ class ScDPSaveMember
+ {
+ private:
+ String aName;
++ ::std::auto_ptr<rtl::OUString> mpLayoutName; // custom name to be displayed in the table.
+ USHORT nVisibleMode;
+ USHORT nShowDetailsMode;
+
+ public:
+ ScDPSaveMember(const String& rName);
+ ScDPSaveMember(const ScDPSaveMember& r);
+- ScDPSaveMember(SvStream& rStream);
+ ~ScDPSaveMember();
+
+ BOOL operator== ( const ScDPSaveMember& r ) const;
+@@ -79,20 +81,23 @@ public:
+
+ void SetName( const String& rNew ); // used if the source member was renamed (groups)
+
++ void SetLayoutName( const ::rtl::OUString& rName );
++ const ::rtl::OUString* GetLayoutName() const;
++ void RemoveLayoutName();
++
+ void WriteToSource( const com::sun::star::uno::Reference<
+ com::sun::star::uno::XInterface>& xMember,
+ sal_Int32 nPosition );
+-
+- void Store( SvStream& rStream ) const;
+ };
+
+
+-class ScDPSaveDimension
++class SC_DLLPUBLIC ScDPSaveDimension
+ {
+ private:
+ String aName;
+- String* pLayoutName; // alternative name for layout, not used (yet)
+ String* pSelectedPage;
++ ::std::auto_ptr<rtl::OUString> mpLayoutName;
++ ::std::auto_ptr<rtl::OUString> mpSubtotalName;
+ BOOL bIsDataLayout;
+ BOOL bDupFlag;
+ USHORT nOrientation;
+@@ -116,7 +121,6 @@ private:
+ public:
+ ScDPSaveDimension(const String& rName, BOOL bDataLayout);
+ ScDPSaveDimension(const ScDPSaveDimension& r);
+- ScDPSaveDimension(SvStream& rStream);
+ ~ScDPSaveDimension();
+
+ BOOL operator== ( const ScDPSaveDimension& r ) const;
+@@ -143,10 +147,14 @@ public:
+ USHORT GetFunction() const { return nFunction; }
+ void SetUsedHierarchy(long nNew);
+ long GetUsedHierarchy() const { return nUsedHierarchy; }
+- void SetLayoutName(const String* pName);
+- const String& GetLayoutName() const;
+- BOOL HasLayoutName() const;
+- void ResetLayoutName();
++
++ void SetLayoutName(const ::rtl::OUString& rName);
++ const ::rtl::OUString* GetLayoutName() const;
++ void RemoveLayoutName();
++ void SetSubtotalName(const ::rtl::OUString& rName);
++ const ::rtl::OUString* GetSubtotalName() const;
++
++ bool IsMemberNameInUse(const ::rtl::OUString& rName) const;
+
+ const ::com::sun::star::sheet::DataPilotFieldReference* GetReferenceValue() const { return pReferenceValue; }
+ void SetReferenceValue(const ::com::sun::star::sheet::DataPilotFieldReference* pNew);
+@@ -171,8 +179,6 @@ public:
+
+ void WriteToSource( const com::sun::star::uno::Reference<
+ com::sun::star::uno::XInterface>& xDim );
+-
+- void Store( SvStream& rStream ) const;
+ };
+
+
+@@ -188,6 +194,8 @@ private:
+ BOOL bFilterButton; // not passed to DataPilotSource
+ BOOL bDrillDown; // not passed to DataPilotSource
+
++ ::std::auto_ptr<rtl::OUString> mpGrandTotalName;
++
+ public:
+ ScDPSaveData();
+ ScDPSaveData(const ScDPSaveData& r);
+@@ -197,11 +205,15 @@ public:
+
+ BOOL operator== ( const ScDPSaveData& r ) const;
+
++ void SetGrandTotalName(const ::rtl::OUString& rName);
++ const ::rtl::OUString* GetGrandTotalName() const;
++
+ const List& GetDimensions() const { return aDimList; }
+- void AddDimension(ScDPSaveDimension* pDim) { aDimList.Insert(pDim, LIST_APPEND); }
++ void AddDimension(ScDPSaveDimension* pDim) { aDimList.Insert(pDim, LIST_APPEND); }
+
+ ScDPSaveDimension* GetDimensionByName(const String& rName);
+ ScDPSaveDimension* GetDataLayoutDimension();
++ ScDPSaveDimension* GetExistingDataLayoutDimension() const;
+
+ ScDPSaveDimension* DuplicateDimension(const String& rName);
+ ScDPSaveDimension& DuplicateDimension(const ScDPSaveDimension& rDim);
+@@ -212,6 +224,7 @@ public:
+ void RemoveDimensionByName(const String& rName);
+
+ ScDPSaveDimension* GetInnermostDimension(USHORT nOrientation);
++ ScDPSaveDimension* GetFirstDimension(::com::sun::star::sheet::DataPilotFieldOrientation eOrientation);
+ long GetDataDimensionCount() const;
+
+
+@@ -233,9 +246,6 @@ public:
+ void WriteToSource( const com::sun::star::uno::Reference<
+ com::sun::star::sheet::XDimensionsSupplier>& xSource );
+
+- void Store( SvStream& rStream ) const;
+- void Load( SvStream& rStream );
+-
+ BOOL IsEmpty() const;
+
+ const ScDPDimensionSaveData* GetExistingDimensionData() const { return pDimensionData; }
+diff --git sc/inc/dptabres.hxx sc/inc/dptabres.hxx
+index 3fb5ef8..60ad93e 100644
+--- sc/inc/dptabres.hxx
++++ sc/inc/dptabres.hxx
+@@ -41,6 +41,7 @@
+ #include <hash_map>
+ #include <hash_set>
+ #include <vector>
++#include <memory>
+
+ namespace com { namespace sun { namespace star { namespace sheet {
+ struct DataPilotFieldReference;
+@@ -262,7 +263,7 @@ public:
+
+ long GetMeasureCount() const { return nMeasCount; }
+ ScSubTotalFunc GetMeasureFunction(long nMeasure) const;
+- String GetMeasureString(long nMeasure, BOOL bForce, ScSubTotalFunc eForceFunc) const;
++ String GetMeasureString(long nMeasure, BOOL bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const;
+ String GetMeasureDimensionName(long nMeasure) const;
+ const ::com::sun::star::sheet::DataPilotFieldReference& GetMeasureRefVal(long nMeasure) const;
+ USHORT GetMeasureRefOrient(long nMeasure) const;
+@@ -284,6 +285,8 @@ public:
+ const ScDPItemData& rBaseData, long nBaseIndex ) const;
+ BOOL HasCommonElement( const ScDPItemData& rFirstData, long nFirstIndex,
+ const ScDPItemData& rSecondData, long nSecondIndex ) const;
++
++ const ScDPSource* GetSource() const;
+ };
+
+
+diff --git sc/inc/dptabsrc.hxx sc/inc/dptabsrc.hxx
+index 1afed45..9f696da 100644
+--- sc/inc/dptabsrc.hxx
++++ sc/inc/dptabsrc.hxx
+@@ -33,6 +33,7 @@
+
+ #include <vector>
+ #include <hash_map>
++#include <memory>
+ #include <tools/string.hxx>
+ #include <tools/list.hxx>
+ #include "global.hxx" // enum ScSubTotalFunc
+@@ -108,11 +109,8 @@ class ScDPSource : public cppu::WeakImplHelper6<
+ com::sun::star::lang::XServiceInfo >
+ {
+ private:
+- void FillCalcInfo(bool bIsRow, ScDPTableData::CalcInfo& rInfo, bool &bHasAutoShow);
+-
+-private:
+ ScDPTableData* pData; // data source
+- ScDPDimensions* pDimensions; // api objects
++ ScDPDimensions* pDimensions; // api objects
+ // settings:
+ long nColDims[SC_DAPI_MAXFIELDS];
+ long nRowDims[SC_DAPI_MAXFIELDS];
+@@ -139,9 +137,18 @@ private:
+ List aRowLevelList;
+ BOOL bResultOverflow;
+
++ ::std::auto_ptr<rtl::OUString> mpGrandTotalName;
++
+ void CreateRes_Impl();
+ void FillMemberResults();
+ void FillLevelList( USHORT nOrientation, List& rList );
++ void FillCalcInfo(bool bIsRow, ScDPTableData::CalcInfo& rInfo, bool &bHasAutoShow);
++
++ /**
++ * Set visibilities of individual rows in the cache table based on the
++ * page field data.
++ */
++ void FilterCacheTableByPageDimensions();
+
+ void SetDupCount( long nNew );
+
+@@ -152,11 +159,15 @@ public:
+ ScDPTableData* GetData() { return pData; }
+ const ScDPTableData* GetData() const { return pData; }
+
++ void SetGrandTotalName(const ::rtl::OUString& rName);
++ const ::rtl::OUString* GetGrandTotalName() const;
++
+ USHORT GetOrientation(long nColumn);
+ void SetOrientation(long nColumn, USHORT nNew);
+ long GetPosition(long nColumn);
+
+ long GetDataDimensionCount();
++ ScDPDimension* GetDataDimension(long nIndex);
+ String GetDataDimName(long nIndex);
+ BOOL IsDataLayoutDimension(long nDim);
+ USHORT GetDataLayoutOrientation();
+@@ -323,6 +334,8 @@ private:
+ long nUsedHier;
+ USHORT nFunction; // enum GeneralFunction
+ String aName; // if empty, take from source
++ ::std::auto_ptr<rtl::OUString> mpLayoutName;
++ ::std::auto_ptr<rtl::OUString> mpSubtotalName;
+ long nSourceDim; // >=0 if dup'ed
+ ::com::sun::star::sheet::DataPilotFieldReference
+ aReferenceValue; // settings for "show data as" / "displayed value"
+@@ -340,6 +353,9 @@ public:
+ ScDPDimension* CreateCloneObject();
+ ScDPHierarchies* GetHierarchiesObject();
+
++ const ::rtl::OUString* GetLayoutName() const;
++ const ::rtl::OUString* GetSubtotalName() const;
++
+ // XNamed
+ virtual ::rtl::OUString SAL_CALL getName() throw(::com::sun::star::uno::RuntimeException);
+ virtual void SAL_CALL setName( const ::rtl::OUString& aName )
+@@ -726,7 +742,7 @@ private:
+ long nLev;
+
+ ScDPItemData maData;
+-// String aCaption; // visible name (changeable by user)
++ ::std::auto_ptr<rtl::OUString> mpLayoutName;
+
+ sal_Int32 nPosition; // manual sorting
+ BOOL bVisible;
+@@ -740,6 +756,7 @@ public:
+ BOOL IsNamedItem( const ScDPItemData& r ) const;
+ String GetNameStr() const;
+ void FillItemData( ScDPItemData& rData ) const;
++ const ::rtl::OUString* GetLayoutName() const;
+
+ sal_Int32 Compare( const ScDPMember& rOther ) const; // visible order
+
+diff --git sc/inc/unonames.hxx sc/inc/unonames.hxx
+index 3573c7d..b72890b 100644
+--- sc/inc/unonames.hxx
++++ sc/inc/unonames.hxx
+@@ -557,6 +557,9 @@
+ #define SC_UNO_ROWFIELDCOUNT "RowFieldCount"
+ #define SC_UNO_COLUMNFIELDCOUNT "ColumnFieldCount"
+ #define SC_UNO_DATAFIELDCOUNT "DataFieldCount"
++#define SC_UNO_LAYOUTNAME "LayoutName"
++#define SC_UNO_FIELD_SUBTOTALNAME "FieldSubtotalName"
++#define SC_UNO_GRANDTOTAL_NAME "GrandTotalName"
+
+ // (preliminary:)
+ #define SC_UNO_REFVALUE "ReferenceValue"
+diff --git sc/source/core/data/dpobject.cxx sc/source/core/data/dpobject.cxx
+index 8e1c9b8..b8ed88f 100644
+--- sc/source/core/data/dpobject.cxx
++++ sc/source/core/data/dpobject.cxx
+@@ -83,6 +83,7 @@ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::sheet::DataPilotTableHeaderData;
+ using ::com::sun::star::sheet::DataPilotTablePositionData;
++using ::rtl::OUString;
+
+ // -----------------------------------------------------------------------
+
+@@ -339,6 +340,31 @@ void ScDPObject::SetTag(const String& rNew)
+ aTableTag = rNew;
+ }
+
++bool ScDPObject::IsDataDescriptionCell(const ScAddress& rPos)
++{
++ if (!pSaveData)
++ return false;
++
++ long nDataDimCount = pSaveData->GetDataDimensionCount();
++ if (nDataDimCount != 1)
++ // There has to be exactly one data dimension for the description to
++ // appear at top-left corner.
++ return false;
++
++ CreateOutput();
++ ScRange aTabRange = pOutput->GetOutputRange(sheet::DataPilotOutputRangeType::TABLE);
++ return (rPos == aTabRange.aStart);
++}
++
++bool ScDPObject::IsGrandTotalCell(const ScAddress& rPos)
++{
++ if (!pSaveData)
++ return false;
++
++ long nDataDimCount = pSaveData->GetDataDimensionCount();
++ return false;
++}
++
+ uno::Reference<sheet::XDimensionsSupplier> ScDPObject::GetSource()
+ {
+ CreateObjects();
+@@ -429,7 +455,8 @@ void ScDPObject::CreateObjects()
+ pData = pGroupData;
+ }
+
+- xSource = new ScDPSource( pData );
++ ScDPSource* pSource = new ScDPSource( pData );
++ xSource = pSource;
+ }
+
+ if (pSaveData)
+@@ -673,23 +700,33 @@ void ScDPObject::GetDrillDownData(const ScAddress& rPos, Sequence< Sequence<Any>
+ rTableData = xDrillDownData->getDrillDownData(filters);
+ }
+
+-BOOL ScDPObject::IsDimNameInUse( const String& rName ) const
++bool ScDPObject::IsDimNameInUse(const OUString& rName) const
+ {
+- if ( xSource.is() )
+- {
+- uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions();
+- if ( xDimsName.is() )
+- {
+- rtl::OUString aCompare( rName );
+- uno::Sequence<rtl::OUString> aNames = xDimsName->getElementNames();
+- long nCount = aNames.getLength();
+- const rtl::OUString* pArr = aNames.getConstArray();
+- for (long nPos=0; nPos<nCount; nPos++)
+- if ( pArr[nPos] == aCompare ) //! ignore case
+- return TRUE;
+- }
+- }
+- return FALSE; // not found
++ if (!xSource.is())
++ return false;
++
++ Reference<container::XNameAccess> xDims = xSource->getDimensions();
++ Sequence<OUString> aDimNames = xDims->getElementNames();
++ sal_Int32 n = aDimNames.getLength();
++ for (sal_Int32 i = 0; i < n; ++i)
++ {
++ const OUString& rDimName = aDimNames[i];
++ if (rDimName.equalsIgnoreAsciiCase(rName))
++ return true;
++
++ Reference<beans::XPropertySet> xPropSet(xDims->getByName(rDimName), UNO_QUERY);
++ if (!xPropSet.is())
++ continue;
++
++ Any any = xPropSet->getPropertyValue(OUString::createFromAscii(SC_UNO_LAYOUTNAME));
++ OUString aLayoutName;
++ if (any >>= aLayoutName)
++ {
++ if (aLayoutName.equalsIgnoreAsciiCase(rName))
++ return true;
++ }
++ }
++ return false;
+ }
+
+ String ScDPObject::GetDimName( long nDim, BOOL& rIsDataLayout )
+@@ -2581,82 +2618,6 @@ DataObject* ScDPCollection::Clone() const
+ return new ScDPCollection(*this);
+ }
+
+-BOOL ScDPCollection::StoreOld( SvStream& rStream ) const
+-{
+- BOOL bSuccess = TRUE;
+-
+- USHORT nSheetCount = 0;
+- USHORT i;
+- for (i=0; i<nCount; i++)
+- if ( ((const ScDPObject*)At(i))->IsSheetData() )
+- ++nSheetCount;
+-
+- ScMultipleWriteHeader aHdr( rStream );
+-
+- rStream << nSheetCount; // only tables from sheet data
+-
+- for (i=0; i<nCount && bSuccess; i++)
+- {
+- const ScDPObject* pObj = (const ScDPObject*)At(i);
+- if ( pObj->IsSheetData() )
+- bSuccess = pObj->StoreOld( rStream, aHdr );
+- }
+-
+- return bSuccess;
+-}
+-
+-BOOL ScDPCollection::StoreNew( SvStream& rStream ) const
+-{
+- BOOL bSuccess = TRUE;
+-
+- ScMultipleWriteHeader aHdr( rStream );
+-
+- rStream << (long)SC_DP_VERSION_CURRENT;
+- rStream << (long)nCount;
+-
+- for (USHORT i=0; i<nCount && bSuccess; i++)
+- bSuccess = ((const ScDPObject*)At(i))->StoreNew( rStream, aHdr );
+-
+- return bSuccess;
+-}
+-
+-BOOL ScDPCollection::LoadNew( SvStream& rStream )
+-{
+- BOOL bSuccess = TRUE;
+-
+- FreeAll();
+- ScMultipleReadHeader aHdr( rStream );
+-
+- long nVer;
+- rStream >> nVer;
+-
+- // check for all supported versions here..
+-
+- if ( nVer != SC_DP_VERSION_CURRENT )
+- {
+- DBG_ERROR("skipping unknown version of data pilot obejct");
+- if ( rStream.GetError() == SVSTREAM_OK )
+- rStream.SetError( SCWARN_IMPORT_INFOLOST );
+- return FALSE;
+- }
+-
+- long nNewCount;
+- rStream >> nNewCount;
+- for (long i=0; i<nNewCount; i++)
+- {
+- ScDPObject* pObj = new ScDPObject( pDoc );
+- if ( pObj->LoadNew(rStream, aHdr) )
+- {
+- pObj->SetAlive( TRUE );
+- Insert( pObj );
+- }
+- else
+- delete pObj;
+- }
+-
+- return bSuccess;
+-}
+-
+ void ScDPCollection::DeleteOnTab( SCTAB nTab )
+ {
+ USHORT nPos = 0;
+@@ -2835,7 +2796,7 @@ void ScDPCollection::ConvertOldTables( ScPivotCollection& rOldColl )
+ }
+ rOldColl.FreeAll();
+ }
+-
++#endif
+
+
+
+diff --git sc/source/core/data/dpoutput.cxx sc/source/core/data/dpoutput.cxx
+index 36904d4..1d61683 100644
+--- sc/source/core/data/dpoutput.cxx
++++ sc/source/core/data/dpoutput.cxx
+@@ -81,6 +81,7 @@
+
+ using namespace com::sun::star;
+ using ::std::vector;
++using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Reference;
+@@ -98,7 +99,6 @@ using ::rtl::OUString;
+ #define DP_PROP_ORIENTATION "Orientation"
+ #define DP_PROP_POSITION "Position"
+ #define DP_PROP_USEDHIERARCHY "UsedHierarchy"
+-#define DP_PROP_DATADESCR "DataDescription"
+ #define DP_PROP_ISDATALAYOUT "IsDataLayoutDimension"
+ #define DP_PROP_NUMBERFORMAT "NumberFormat"
+ #define DP_PROP_FILTER "Filter"
+@@ -119,7 +119,8 @@ struct ScDPOutLevelData
+ long nLevel;
+ long nDimPos;
+ uno::Sequence<sheet::MemberResult> aResult;
+- String aCaption;
++ String maName; /// Name is the internal field name.
++ String aCaption; /// Caption is the name visible in the output table.
+
+ ScDPOutLevelData() { nDim = nHier = nLevel = nDimPos = -1; }
+
+@@ -453,7 +454,15 @@ ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsS
+ xLevel, uno::UNO_QUERY );
+ if ( xLevNam.is() && xLevRes.is() )
+ {
+- String aCaption = String(xLevNam->getName()); //! Caption...
++ String aName = xLevNam->getName();
++ OUString aCaption = aName; // Caption equals the field name by default.
++ Reference<XPropertySet> xPropSet(xLevel, UNO_QUERY);
++ if (xPropSet.is())
++ {
++ Any any = xPropSet->getPropertyValue(
++ OUString::createFromAscii(SC_UNO_LAYOUTNAME));
++ any >>= aCaption;
++ }
+ switch ( eDimOrient )
+ {
+ case sheet::DataPilotFieldOrientation_COLUMN:
+@@ -462,6 +471,7 @@ ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsS
+ pColFields[nColFieldCount].nLevel = nLev;
+ pColFields[nColFieldCount].nDimPos = nDimPos;
+ pColFields[nColFieldCount].aResult = xLevRes->getResults();
++ pColFields[nColFieldCount].maName = aName;
+ pColFields[nColFieldCount].aCaption= aCaption;
+ if (!lcl_MemberEmpty(pColFields[nColFieldCount].aResult))
+ ++nColFieldCount;
+@@ -472,6 +482,7 @@ ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsS
+ pRowFields[nRowFieldCount].nLevel = nLev;
+ pRowFields[nRowFieldCount].nDimPos = nDimPos;
+ pRowFields[nRowFieldCount].aResult = xLevRes->getResults();
++ pRowFields[nRowFieldCount].maName = aName;
+ pRowFields[nRowFieldCount].aCaption= aCaption;
+ if (!lcl_MemberEmpty(pRowFields[nRowFieldCount].aResult))
+ ++nRowFieldCount;
+@@ -482,6 +493,7 @@ ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsS
+ pPageFields[nPageFieldCount].nLevel = nLev;
+ pPageFields[nPageFieldCount].nDimPos = nDimPos;
+ pPageFields[nPageFieldCount].aResult = lcl_GetSelectedPageAsResult(xDimProp);
++ pPageFields[nPageFieldCount].maName = aName;
+ pPageFields[nPageFieldCount].aCaption= aCaption;
+ // no check on results for page fields
+ ++nPageFieldCount;
+@@ -538,7 +550,7 @@ ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsS
+ try
+ {
+ uno::Any aAny = xSrcProp->getPropertyValue(
+- rtl::OUString::createFromAscii(DP_PROP_DATADESCR) );
++ rtl::OUString::createFromAscii(SC_UNO_DATADESC) );
+ rtl::OUString aUStr;
+ aAny >>= aUStr;
+ aDataDescription = String( aUStr );
+@@ -615,9 +627,16 @@ void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const sheet::MemberResult& rData, BOOL bColHeader, long nLevel )
+ {
+ long nFlags = rData.Flags;
++
++ rtl::OUStringBuffer aCaptionBuf;
++ if (!(nFlags & sheet::MemberResultFlags::NUMERIC))
++ // This caption is not a number. Make sure it won't get parsed as one.
++ aCaptionBuf.append(sal_Unicode('\''));
++ aCaptionBuf.append(rData.Caption);
++
+ if ( nFlags & sheet::MemberResultFlags::HASMEMBER )
+ {
+- pDoc->SetString( nCol, nRow, nTab, rData.Caption );
++ pDoc->SetString( nCol, nRow, nTab, aCaptionBuf.makeStringAndClear() );
+ }
+ else
+ {
+@@ -1162,7 +1181,7 @@ bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>&
+ for (SCCOL nColField = 0; nColField < nColFieldCount && bFilterByCol; ++nColField)
+ {
+ sheet::DataPilotFieldFilter filter;
+- filter.FieldName = pColFields[nColField].aCaption;
++ filter.FieldName = pColFields[nColField].maName;
+
+ const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].aResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+@@ -1179,10 +1198,15 @@ bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>&
+ }
+
+ // row fields
++ bool bDataLayoutExists = (nDataFieldCount > 1);
+ for (SCROW nRowField = 0; nRowField < nRowFieldCount && bFilterByRow; ++nRowField)
+ {
++ if (bDataLayoutExists && nRowField == nRowFieldCount - 1)
++ // There is no sense including the data layout field for filtering.
++ continue;
++
+ sheet::DataPilotFieldFilter filter;
+- filter.FieldName = pRowFields[nRowField].aCaption;
++ filter.FieldName = pRowFields[nRowField].maName;
+
+ const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].aResult;
+ const sheet::MemberResult* pArray = rSequence.getConstArray();
+@@ -1220,8 +1244,7 @@ bool lcl_IsNamedDataField( const ScDPGetPivotDataField& rTarget, const String& r
+
+ bool lcl_IsNamedCategoryField( const ScDPGetPivotDataField& rFilter, const ScDPOutLevelData& rField )
+ {
+- //! name from source instead of caption?
+- return ScGlobal::pTransliteration->isEqual( rFilter.maFieldName, rField.aCaption );
++ return ScGlobal::pTransliteration->isEqual( rFilter.maFieldName, rField.maName );
+ }
+
+ bool lcl_IsCondition( const sheet::MemberResult& rResultEntry, const ScDPGetPivotDataField& rFilter )
+@@ -1772,7 +1795,7 @@ long ScDPOutput::GetHeaderDim( const ScAddress& rPos, USHORT& rOrient )
+
+ // test for row header
+
+- if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol == nCol < nTabStartCol + nRowFieldCount )
++ if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount )
+ {
+ rOrient = sheet::DataPilotFieldOrientation_ROW;
+ long nField = nCol - nTabStartCol;
+diff --git sc/source/core/data/dpsave.cxx sc/source/core/data/dpsave.cxx
+index 197253c..4caf541 100644
+--- sc/source/core/data/dpsave.cxx
++++ sc/source/core/data/dpsave.cxx
+@@ -60,6 +60,7 @@
+ #include <com/sun/star/util/XCloneable.hpp>
+
+ using namespace com::sun::star;
++using ::rtl::OUString;
+
+ // -----------------------------------------------------------------------
+
+@@ -114,6 +115,7 @@ void lcl_SkipExtra( SvStream& rStream )
+
+ ScDPSaveMember::ScDPSaveMember(const String& rName) :
+ aName( rName ),
++ mpLayoutName(NULL),
+ nVisibleMode( SC_DPSAVEMODE_DONTKNOW ),
+ nShowDetailsMode( SC_DPSAVEMODE_DONTKNOW )
+ {
+@@ -121,27 +123,12 @@ ScDPSaveMember::ScDPSaveMember(const String& rName) :
+
+ ScDPSaveMember::ScDPSaveMember(const ScDPSaveMember& r) :
+ aName( r.aName ),
++ mpLayoutName(NULL),
+ nVisibleMode( r.nVisibleMode ),
+ nShowDetailsMode( r.nShowDetailsMode )
+ {
+-}
+-
+-ScDPSaveMember::ScDPSaveMember(SvStream& rStream)
+-{
+- rStream.ReadByteString( aName, rStream.GetStreamCharSet() );
+- rStream >> nVisibleMode;
+- rStream >> nShowDetailsMode;
+-
+- lcl_SkipExtra( rStream ); // reads at least 1 USHORT
+-}
+-
+-void ScDPSaveMember::Store( SvStream& rStream ) const
+-{
+- rStream.WriteByteString( aName, rStream.GetStreamCharSet() );
+- rStream << nVisibleMode;
+- rStream << nShowDetailsMode;
+-
+- rStream << (USHORT) 0; // nExtra
++ if (r.mpLayoutName.get())
++ mpLayoutName.reset(new OUString(*r.mpLayoutName));
+ }
+
+ ScDPSaveMember::~ScDPSaveMember()
+@@ -186,6 +173,21 @@ void ScDPSaveMember::SetName( const String& rNew )
+ aName = rNew;
+ }
+
++void ScDPSaveMember::SetLayoutName( const OUString& rName )
++{
++ mpLayoutName.reset(new OUString(rName));
++}
++
++const OUString* ScDPSaveMember::GetLayoutName() const
++{
++ return mpLayoutName.get();
++}
++
++void ScDPSaveMember::RemoveLayoutName()
++{
++ mpLayoutName.reset(NULL);
++}
++
+ void ScDPSaveMember::WriteToSource( const uno::Reference<uno::XInterface>& xMember, sal_Int32 nPosition )
+ {
+ // nothing to do?
+@@ -206,6 +208,18 @@ void ScDPSaveMember::WriteToSource( const uno::Reference<uno::XInterface>& xMemb
+ lcl_SetBoolProperty( xMembProp,
+ rtl::OUString::createFromAscii(DP_PROP_SHOWDETAILS), (BOOL)nShowDetailsMode );
+
++ if (mpLayoutName.get())
++ {
++ try
++ {
++ uno::Any any;
++ any <<= rtl::OUString(*mpLayoutName);
++ xMembProp->setPropertyValue(rtl::OUString::createFromAscii(SC_UNO_LAYOUTNAME), any);
++ }
++ catch (uno::Exception&)
++ {
++ }
++ }
+ if ( nPosition >= 0 )
+ {
+ try
+@@ -224,8 +238,9 @@ void ScDPSaveMember::WriteToSource( const uno::Reference<uno::XInterface>& xMemb
+
+ ScDPSaveDimension::ScDPSaveDimension(const String& rName, BOOL bDataLayout) :
+ aName( rName ),
+- pLayoutName( NULL ),
+ pSelectedPage( NULL ),
++ mpLayoutName(NULL),
++ mpSubtotalName(NULL),
+ bIsDataLayout( bDataLayout ),
+ bDupFlag( FALSE ),
+ nOrientation( sheet::DataPilotFieldOrientation_HIDDEN ),
+@@ -244,6 +259,8 @@ ScDPSaveDimension::ScDPSaveDimension(const String& rName, BOOL bDataLayout) :
+
+ ScDPSaveDimension::ScDPSaveDimension(const ScDPSaveDimension& r) :
+ aName( r.aName ),
++ mpLayoutName(NULL),
++ mpSubtotalName(NULL),
+ bIsDataLayout( r.bIsDataLayout ),
+ bDupFlag( r.bDupFlag ),
+ nOrientation( r.nOrientation ),
+@@ -284,88 +301,14 @@ ScDPSaveDimension::ScDPSaveDimension(const ScDPSaveDimension& r) :
+ pLayoutInfo = new sheet::DataPilotFieldLayoutInfo( *(r.pLayoutInfo) );
+ else
+ pLayoutInfo = NULL;
+- if (r.pLayoutName)
+- pLayoutName = new String( *(r.pLayoutName) );
+- else
+- pLayoutName = NULL;
+ if (r.pSelectedPage)
+ pSelectedPage = new String( *(r.pSelectedPage) );
+ else
+ pSelectedPage = NULL;
+-}
+-
+-ScDPSaveDimension::ScDPSaveDimension(SvStream& rStream)
+-{
+- long i;
+-
+- rStream.ReadByteString( aName, rStream.GetStreamCharSet() );
+- rStream >> bIsDataLayout;
+-
+- rStream >> bDupFlag;
+-
+- rStream >> nOrientation;
+- rStream >> nFunction; // enum GeneralFunction
+- rStream >> nUsedHierarchy;
+-
+- rStream >> nShowEmptyMode; //! at level
+-
+- rStream >> bSubTotalDefault; //! at level
+- rStream >> nSubTotalCount;
+- if (nSubTotalCount)
+- {
+- pSubTotalFuncs = new USHORT[nSubTotalCount];
+- for (i=0; i<nSubTotalCount; i++)
+- rStream >> pSubTotalFuncs[i];
+- }
+- else
+- pSubTotalFuncs = NULL;
+-
+- lcl_SkipExtra( rStream ); // reads at least 1 USHORT
+-
+- long nNewCount;
+- rStream >> nNewCount;
+- for (i=0; i<nNewCount; i++)
+- {
+- ScDPSaveMember* pNew = new ScDPSaveMember( rStream );
+- maMemberHash[pNew->GetName()] = pNew;
+- maMemberList.push_back( pNew );
+- }
+- pReferenceValue = NULL;
+- pSortInfo = NULL;
+- pAutoShowInfo = NULL;
+- pLayoutInfo = NULL;
+- pLayoutName = NULL;
+- pSelectedPage = NULL;
+-}
+-
+-void ScDPSaveDimension::Store( SvStream& rStream ) const
+-{
+- long i;
+-
+- rStream.WriteByteString( aName, rStream.GetStreamCharSet() );
+- rStream << bIsDataLayout;
+-
+- rStream << bDupFlag;
+-
+- rStream << nOrientation;
+- rStream << nFunction; // enum GeneralFunction
+- rStream << nUsedHierarchy;
+-
+- rStream << nShowEmptyMode; //! at level
+-
+- //! subtotals at level
+- rStream << bSubTotalDefault;
+- long nSubCnt = pSubTotalFuncs ? nSubTotalCount : 0;
+- rStream << nSubCnt;
+- for (i=0; i<nSubCnt; i++)
+- rStream << pSubTotalFuncs[i];
+-
+- rStream << (USHORT) 0; // nExtra
+-
+- long nCount = maMemberHash.size();
+- rStream << nCount;
+- for (MemberList::const_iterator iter=maMemberList.begin(); iter != maMemberList.end() ; iter++)
+- (*iter)->Store( rStream );
++ if (r.mpLayoutName.get())
++ mpLayoutName.reset(new OUString(*r.mpLayoutName));
++ if (r.mpSubtotalName.get())
++ mpSubtotalName.reset(new OUString(*r.mpSubtotalName));
+ }
+
+ ScDPSaveDimension::~ScDPSaveDimension()
+@@ -376,7 +319,6 @@ ScDPSaveDimension::~ScDPSaveDimension()
+ delete pSortInfo;
+ delete pAutoShowInfo;
+ delete pLayoutInfo;
+- delete pLayoutName;
+ delete pSelectedPage;
+ delete [] pSubTotalFuncs;
+ }
+@@ -488,31 +430,45 @@ void ScDPSaveDimension::SetUsedHierarchy(long nNew)
+ nUsedHierarchy = nNew;
+ }
+
+-BOOL ScDPSaveDimension::HasLayoutName() const
++void ScDPSaveDimension::SetSubtotalName(const OUString& rName)
+ {
+- return ( pLayoutName != NULL );
++ mpSubtotalName.reset(new OUString(rName));
+ }
+
+-void ScDPSaveDimension::ResetLayoutName()
++const OUString* ScDPSaveDimension::GetSubtotalName() const
+ {
+- delete pLayoutName;
+- pLayoutName = NULL;
++ return mpSubtotalName.get();
+ }
+
+-void ScDPSaveDimension::SetLayoutName(const String* pName)
++bool ScDPSaveDimension::IsMemberNameInUse(const OUString& rName) const
+ {
+- delete pLayoutName;
+- if (pName)
+- pLayoutName = new String( *pName );
+- else
+- pLayoutName = NULL;
++ MemberList::const_iterator itr = maMemberList.begin(), itrEnd = maMemberList.end();
++ for (; itr != itrEnd; ++itr)
++ {
++ const ScDPSaveMember* pMem = *itr;
++ if (rName.equalsIgnoreAsciiCase(pMem->GetName()))
++ return true;
++
++ const OUString* pLayoutName = pMem->GetLayoutName();
++ if (pLayoutName && rName.equalsIgnoreAsciiCase(*pLayoutName))
++ return true;
++ }
++ return false;
++}
++
++void ScDPSaveDimension::SetLayoutName(const OUString& rName)
++{
++ mpLayoutName.reset(new OUString(rName));
++}
++
++const OUString* ScDPSaveDimension::GetLayoutName() const
++{
++ return mpLayoutName.get();
+ }
+
+-const String& ScDPSaveDimension::GetLayoutName() const
++void ScDPSaveDimension::RemoveLayoutName()
+ {
+- if (pLayoutName)
+- return *pLayoutName;
+- return aName;
++ mpLayoutName.reset(NULL);
+ }
+
+ void ScDPSaveDimension::SetReferenceValue(const sheet::DataPilotFieldReference* pNew)
+@@ -648,6 +604,19 @@ void ScDPSaveDimension::WriteToSource( const uno::Reference<uno::XInterface>& xD
+ {
+ aAny <<= aFilter;
+ xDimProp->setPropertyValue( rtl::OUString::createFromAscii(DP_PROP_FILTER), aAny );
++ if (mpLayoutName.get())
++ {
++ aAny <<= *mpLayoutName;
++ xDimProp->setPropertyValue(rtl::OUString::createFromAscii(SC_UNO_LAYOUTNAME), aAny);
++ }
++
++ const OUString* pSubTotalName = GetSubtotalName();
++ if (pSubTotalName)
++ {
++ // Custom subtotal name, with '?' being replaced by the visible field name later.
++ aAny <<= *pSubTotalName;
++ xDimProp->setPropertyValue(OUString::createFromAscii(SC_UNO_FIELD_SUBTOTALNAME), aAny);
++ }
+ }
+ catch ( beans::UnknownPropertyException& )
+ {
+@@ -790,7 +759,8 @@ ScDPSaveData::ScDPSaveData() :
+ nIgnoreEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
+ nRepeatEmptyMode( SC_DPSAVEMODE_DONTKNOW ),
+ bFilterButton( TRUE ),
+- bDrillDown( TRUE )
++ bDrillDown( TRUE ),
++ mpGrandTotalName(NULL)
+ {
+ }
+
+@@ -800,7 +770,8 @@ ScDPSaveData::ScDPSaveData(const ScDPSaveData& r) :
+ nIgnoreEmptyMode( r.nIgnoreEmptyMode ),
+ nRepeatEmptyMode( r.nRepeatEmptyMode ),
+ bFilterButton( r.bFilterButton ),
+- bDrillDown( r.bDrillDown )
++ bDrillDown( r.bDrillDown ),
++ mpGrandTotalName(NULL)
+ {
+ if ( r.pDimensionData )
+ pDimensionData = new ScDPDimensionSaveData( *r.pDimensionData );
+@@ -813,6 +784,9 @@ ScDPSaveData::ScDPSaveData(const ScDPSaveData& r) :
+ ScDPSaveDimension* pNew = new ScDPSaveDimension( *(ScDPSaveDimension*)r.aDimList.GetObject(i) );
+ aDimList.Insert( pNew, LIST_APPEND );
+ }
++
++ if (r.mpGrandTotalName.get())
++ mpGrandTotalName.reset(new OUString(*r.mpGrandTotalName));
+ }
+
+ ScDPSaveData& ScDPSaveData::operator= ( const ScDPSaveData& r )
+@@ -849,6 +823,9 @@ ScDPSaveData& ScDPSaveData::operator= ( const ScDPSaveData& r )
+ new ScDPSaveDimension( *(ScDPSaveDimension*)r.aDimList.GetObject(i) );
+ aDimList.Insert( pNew, LIST_APPEND );
+ }
++
++ if (r.mpGrandTotalName.get())
++ mpGrandTotalName.reset(new OUString(*r.mpGrandTotalName));
+ }
+ return *this;
+ }
+@@ -876,6 +853,16 @@ BOOL ScDPSaveData::operator== ( const ScDPSaveData& r ) const
+ *(ScDPSaveDimension*)r.aDimList.GetObject(i) ) )
+ return FALSE;
+
++ if (mpGrandTotalName.get())
++ {
++ if (!r.mpGrandTotalName.get())
++ return false;
++ if (!mpGrandTotalName->equals(*r.mpGrandTotalName))
++ return false;
++ }
++ else if (r.mpGrandTotalName.get())
++ return false;
++
+ return TRUE;
+ }
+
+@@ -889,6 +876,16 @@ ScDPSaveData::~ScDPSaveData()
+ delete pDimensionData;
+ }
+
++void ScDPSaveData::SetGrandTotalName(const OUString& rName)
++{
++ mpGrandTotalName.reset(new OUString(rName));
++}
++
++const OUString* ScDPSaveData::GetGrandTotalName() const
++{
++ return mpGrandTotalName.get();
++}
++
+ ScDPSaveDimension* ScDPSaveData::GetDimensionByName(const String& rName)
+ {
+ long nCount = aDimList.Count();
+@@ -931,6 +928,17 @@ ScDPSaveDimension* ScDPSaveData::GetNewDimensionByName(const String& rName)
+
+ ScDPSaveDimension* ScDPSaveData::GetDataLayoutDimension()
+ {
++ ScDPSaveDimension* pDim = GetExistingDataLayoutDimension();
++ if (pDim)
++ return pDim;
++
++ ScDPSaveDimension* pNew = new ScDPSaveDimension( String(), TRUE );
++ aDimList.Insert( pNew, LIST_APPEND );
++ return pNew;
++}
++
++ScDPSaveDimension* ScDPSaveData::GetExistingDataLayoutDimension() const
++{
+ long nCount = aDimList.Count();
+ for (long i=0; i<nCount; i++)
+ {
+@@ -938,9 +946,7 @@ ScDPSaveDimension* ScDPSaveData::GetDataLayoutDimension()
+ if ( pDim->IsDataLayout() )
+ return pDim;
+ }
+- ScDPSaveDimension* pNew = new ScDPSaveDimension( String(), TRUE );
+- aDimList.Insert( pNew, LIST_APPEND );
+- return pNew;
++ return NULL;
+ }
+
+ ScDPSaveDimension* ScDPSaveData::DuplicateDimension(const String& rName)
+@@ -994,6 +1000,18 @@ ScDPSaveDimension* ScDPSaveData::GetInnermostDimension(USHORT nOrientation)
+ return pInner; // the last matching one
+ }
+
++ScDPSaveDimension* ScDPSaveData::GetFirstDimension(sheet::DataPilotFieldOrientation eOrientation)
++{
++ long nCount = aDimList.Count();
++ for (long i = 0; i < nCount; ++i)
++ {
++ ScDPSaveDimension* pDim = static_cast<ScDPSaveDimension*>(aDimList.GetObject(i));
++ if (pDim->GetOrientation() == eOrientation && !pDim->IsDataLayout())
++ return pDim;
++ }
++ return NULL;
++}
++
+ long ScDPSaveData::GetDataDimensionCount() const
+ {
+ long nDataCount = 0;
+@@ -1101,6 +1119,14 @@ void ScDPSaveData::WriteToSource( const uno::Reference<sheet::XDimensionsSupplie
+ if ( nRepeatEmptyMode != SC_DPSAVEMODE_DONTKNOW )
+ lcl_SetBoolProperty( xSourceProp,
+ rtl::OUString::createFromAscii(DP_PROP_REPEATIFEMPTY), (BOOL)nRepeatEmptyMode );
++
++ const OUString* pGrandTotalName = GetGrandTotalName();
++ if (pGrandTotalName)
++ {
++ uno::Any any;
++ any <<= *pGrandTotalName;
++ xSourceProp->setPropertyValue(OUString::createFromAscii(SC_UNO_GRANDTOTAL_NAME), any);
++ }
+ }
+ catch(uno::Exception&)
+ {
+@@ -1196,48 +1222,6 @@ void ScDPSaveData::WriteToSource( const uno::Reference<sheet::XDimensionsSupplie
+ }
+ }
+
+-void ScDPSaveData::Store( SvStream& rStream ) const
+-{
+- //! multi-header for individual entries
+-
+- long nCount = aDimList.Count();
+- rStream << nCount;
+- for (long i=0; i<nCount; i++)
+- {
+- const ScDPSaveDimension* pDim = (const ScDPSaveDimension*)aDimList.GetObject(i);
+- pDim->Store( rStream );
+- }
+-
+- rStream << nColumnGrandMode;
+- rStream << nRowGrandMode;
+- rStream << nIgnoreEmptyMode;
+- rStream << nRepeatEmptyMode;
+-
+- rStream << (USHORT) 0; // nExtra
+-}
+-
+-void ScDPSaveData::Load( SvStream& rStream )
+-{
+- //! multi-header for individual entries
+-
+- DBG_ASSERT( aDimList.Count()==0, "ScDPSaveData::Load not empty" );
+-
+- long nNewCount;
+- rStream >> nNewCount;
+- for (long i=0; i<nNewCount; i++)
+- {
+- ScDPSaveDimension* pNew = new ScDPSaveDimension( rStream );
+- aDimList.Insert( pNew, LIST_APPEND );
+- }
+-
+- rStream >> nColumnGrandMode;
+- rStream >> nRowGrandMode;
+- rStream >> nIgnoreEmptyMode;
+- rStream >> nRepeatEmptyMode;
+-
+- lcl_SkipExtra( rStream ); // reads at least 1 USHORT
+-}
+-
+ BOOL ScDPSaveData::IsEmpty() const
+ {
+ long nCount = aDimList.Count();
+diff --git sc/source/core/data/dptabres.cxx sc/source/core/data/dptabres.cxx
+index c52139c..87d0698 100644
+--- sc/source/core/data/dptabres.cxx
++++ sc/source/core/data/dptabres.cxx
+@@ -67,6 +67,7 @@ using ::std::vector;
+ using ::std::pair;
+ using ::std::hash_map;
+ using ::com::sun::star::uno::Sequence;
++using ::rtl::OUString;
+
+ // -----------------------------------------------------------------------
+
+@@ -825,11 +826,11 @@ USHORT ScDPResultData::GetMeasureRefOrient(long nMeasure) const
+ return pMeasRefOrient[nMeasure];
+ }
+
+-String ScDPResultData::GetMeasureString(long nMeasure, BOOL bForce, ScSubTotalFunc eForceFunc) const
++String ScDPResultData::GetMeasureString(long nMeasure, BOOL bForce, ScSubTotalFunc eForceFunc, bool& rbTotalResult) const
+ {
+ // with bForce==TRUE, return function instead of "result" for single measure
+ // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc
+-
++ rbTotalResult = false;
+ if ( nMeasure < 0 || ( nMeasCount == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE ) )
+ {
+ // for user-specified subtotal function with all measures,
+@@ -837,12 +838,19 @@ String ScDPResultData::GetMeasureString(long nMeasure, BOOL bForce, ScSubTotalFu
+ if ( eForceFunc != SUBTOTAL_FUNC_NONE )
+ return ScGlobal::GetRscString(nFuncStrIds[eForceFunc]);
+
++ rbTotalResult = true;
+ return ScGlobal::GetRscString(STR_TABLE_ERGEBNIS);
+ }
+ else
+ {
+ DBG_ASSERT( pMeasNames && nMeasure < nMeasCount, "bumm" );
+-
++ ScDPDimension* pDataDim = pSource->GetDataDimension(nMeasure);
++ if (pDataDim)
++ {
++ const OUString* pLayoutName = pDataDim->GetLayoutName();
++ if (pLayoutName)
++ return *pLayoutName;
++ }
+ String aRet;
+ ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ?
+ GetMeasureFunction(nMeasure) : eForceFunc;
+@@ -896,6 +904,11 @@ BOOL ScDPResultData::HasCommonElement( const ScDPItemData& rFirstData, long nFir
+ return pSource->GetData()->HasCommonElement( rFirstData, nFirstIndex, rSecondData, nSecondIndex );
+ }
+
++const ScDPSource* ScDPResultData::GetSource() const
++{
++ return pSource;
++}
++
+ // -----------------------------------------------------------------------
+
+
+@@ -1172,6 +1185,25 @@ void ScDPResultMember::ProcessData( const vector<ScDPItemData>& aChildMembers, c
+ }
+ }
+
++/**
++ * Parse subtotal string and replace all occurrences of '?' with the
++ * caption string.
++ */
++static String lcl_parseSubtotalName(const String& rSubStr, const String& rCaption)
++{
++ String aNewStr;
++ xub_StrLen n = rSubStr.Len();
++ for (xub_StrLen i = 0; i < n; ++i)
++ {
++ sal_Unicode c = rSubStr.GetChar(i);
++ if (c == sal_Unicode('?'))
++ aNewStr.Append(rCaption);
++ else
++ aNewStr.Append(c);
++ }
++ return aNewStr;
++}
++
+ void ScDPResultMember::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences,
+ long& rPos, long nMeasure, BOOL bRoot,
+ const String* pMemberName,
+@@ -1204,17 +1236,25 @@ void ScDPResultMember::FillMemberResults( uno::Sequence<sheet::MemberResult>* pS
+ }
+
+ String aCaption = aName;
++ if (pMemberDesc)
++ {
++ const OUString* pLayoutName = pMemberDesc->GetLayoutName();
++ if (pLayoutName)
++ {
++ aCaption = *pLayoutName;
++ bIsNumeric = false; // layout name is always non-numeric.
++ }
++ }
++
+ if ( pMemberCaption ) // use pMemberCaption if != NULL
+ aCaption = *pMemberCaption;
+ if (!aCaption.Len())
+ aCaption = ScGlobal::GetRscString(STR_EMPTYDATA);
+
+- if ( !bIsNumeric )
+- {
+- // add a "'" character so a string isn't parsed as value in the output cell
+- //! have a separate bit in Flags (MemberResultFlags) instead?
+- aCaption.Insert( (sal_Unicode) '\'', 0 );
+- }
++ if (bIsNumeric)
++ pArray[rPos].Flags |= sheet::MemberResultFlags::NUMERIC;
++ else
++ pArray[rPos].Flags &= ~sheet::MemberResultFlags::NUMERIC;
+
+ if ( nSize && !bRoot ) // root is overwritten by first dimension
+ {
+@@ -1277,9 +1317,30 @@ void ScDPResultMember::FillMemberResults( uno::Sequence<sheet::MemberResult>* pS
+ if (bHasChild)
+ eForce = lcl_GetForceFunc( pParentLevel, nUserPos );
+
+- String aSubStr = aName; //! caption?
++ bool bTotalResult = false;
++ String aSubStr = aCaption;
+ aSubStr += ' ';
+- aSubStr += pResultData->GetMeasureString(nMemberMeasure, FALSE, eForce);
++ aSubStr += pResultData->GetMeasureString(nMemberMeasure, FALSE, eForce, bTotalResult);
++
++ if (bTotalResult)
++ {
++ if (pMemberDesc)
++ {
++ // single data field layout.
++ const OUString* pSubtotalName = pParentDim->GetSubtotalName();
++ if (pSubtotalName)
++ aSubStr = lcl_parseSubtotalName(*pSubtotalName, aCaption);
++ pArray[rPos].Flags &= ~sheet::MemberResultFlags::GRANDTOTAL;
++ }
++ else
++ {
++ // root member - subtotal (grand total?) for multi-data field layout.
++ const rtl::OUString* pGrandTotalName = pResultData->GetSource()->GetGrandTotalName();
++ if (pGrandTotalName)
++ aSubStr = *pGrandTotalName;
++ pArray[rPos].Flags |= sheet::MemberResultFlags::GRANDTOTAL;
++ }
++ }
+
+ pArray[rPos].Name = rtl::OUString(aName);
+ pArray[rPos].Caption = rtl::OUString(aSubStr);
+@@ -2796,8 +2857,9 @@ void ScDPResultDimension::FillMemberResults( uno::Sequence<sheet::MemberResult>*
+ // in data layout dimension, use first member with different measures/names
+ if ( bIsDataLayout )
+ {
++ bool bTotalResult = false;
+ String aMbrName = pResultData->GetMeasureDimensionName( nSorted );
+- String aMbrCapt = pResultData->GetMeasureString( nSorted, FALSE, SUBTOTAL_FUNC_NONE );
++ String aMbrCapt = pResultData->GetMeasureString( nSorted, FALSE, SUBTOTAL_FUNC_NONE, bTotalResult );
+ maMemberArray[0]->FillMemberResults( pSequences, nPos, nSorted, FALSE, &aMbrName, &aMbrCapt );
+ }
+ else if ( pMember->IsVisible() )
+diff --git sc/source/core/data/dptabsrc.cxx sc/source/core/data/dptabsrc.cxx
+index 600eb7b..0f9add4 100644
+--- sc/source/core/data/dptabsrc.cxx
++++ sc/source/core/data/dptabsrc.cxx
+@@ -83,6 +83,7 @@ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
++using ::rtl::OUString;
+
+ // -----------------------------------------------------------------------
+
+@@ -139,7 +140,8 @@ ScDPSource::ScDPSource( ScDPTableData* pD ) :
+ pRowResRoot( NULL ),
+ pColResults( NULL ),
+ pRowResults( NULL ),
+- bResultOverflow( FALSE )
++ bResultOverflow( FALSE ),
++ mpGrandTotalName(NULL)
+ {
+ pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty );
+ }
+@@ -161,6 +163,16 @@ ScDPSource::~ScDPSource()
+ delete pResData;
+ }
+
++void ScDPSource::SetGrandTotalName(const ::rtl::OUString& rName)
++{
++ mpGrandTotalName.reset(new ::rtl::OUString(rName));
++}
++
++const ::rtl::OUString* ScDPSource::GetGrandTotalName() const
++{
++ return mpGrandTotalName.get();
++}
++
+ USHORT ScDPSource::GetOrientation(long nColumn)
+ {
+ long i;
+@@ -184,16 +196,21 @@ long ScDPSource::GetDataDimensionCount()
+ return nDataDimCount;
+ }
+
++ScDPDimension* ScDPSource::GetDataDimension(long nIndex)
++{
++ if (nIndex < 0 || nIndex >= nDataDimCount)
++ return NULL;
++
++ long nDimIndex = nDataDims[nIndex];
++ return GetDimensionsObject()->getByIndex(nDimIndex);
++}
++
+ String ScDPSource::GetDataDimName( long nIndex )
+ {
+ String aRet;
+- if ( nIndex >= 0 && nIndex < nDataDimCount )
+- {
+- long nDimIndex = nDataDims[nIndex];
+- ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nDimIndex);
+- if (pDim)
+- aRet = String( pDim->getName() );
+- }
++ ScDPDimension* pDim = GetDataDimension(nIndex);
++ if (pDim)
++ aRet = String(pDim->getName());
+ return aRet;
+ }
+
+@@ -480,7 +497,10 @@ String ScDPSource::getDataDescription()
+
+ String aRet;
+ if ( pResData->GetMeasureCount() == 1 )
+- aRet = pResData->GetMeasureString( 0, TRUE, SUBTOTAL_FUNC_NONE );
++ {
++ bool bTotalResult = false;
++ aRet = pResData->GetMeasureString( 0, TRUE, SUBTOTAL_FUNC_NONE, bTotalResult );
++ }
+
+ // empty for more than one measure
+
+@@ -685,6 +705,55 @@ void ScDPSource::FillCalcInfo(bool bIsRow, ScDPTableData::CalcInfo& rInfo, bool
+ }
+ }
+
++void ScDPSource::FilterCacheTableByPageDimensions()
++{
++ ScSimpleSharedString& rSharedString = GetData()->GetSharedString();
++
++ // filter table by page dimensions.
++ vector<ScDPCacheTable::Criterion> aCriteria;
++ for (long i = 0; i < nPageDimCount; ++i)
++ {
++ ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nPageDims[i]);
++ long nField = pDim->GetDimension();
++
++ ScDPMembers* pMems = pDim->GetHierarchiesObject()->getByIndex(0)->
++ GetLevelsObject()->getByIndex(0)->GetMembersObject();
++
++ long nMemCount = pMems->getCount();
++ ScDPCacheTable::Criterion aFilter;
++ aFilter.mnFieldIndex = static_cast<sal_Int32>(nField);
++ aFilter.mpFilter.reset(new ScDPCacheTable::GroupFilter(rSharedString));
++ ScDPCacheTable::GroupFilter* pGrpFilter =
++ static_cast<ScDPCacheTable::GroupFilter*>(aFilter.mpFilter.get());
++ for (long j = 0; j < nMemCount; ++j)
++ {
++ ScDPMember* pMem = pMems->getByIndex(j);
++ if (pMem->getIsVisible())
++ {
++ ScDPItemData aData;
++ pMem->FillItemData(aData);
++ pGrpFilter->addMatchItem(aData.aString, aData.fValue, aData.bHasValue);
++ }
++ }
++ if (pGrpFilter->getMatchItemCount() < static_cast<size_t>(nMemCount))
++ // there is at least one invisible item. Add this filter criterion to the mix.
++ aCriteria.push_back(aFilter);
++
++ if (!pDim || !pDim->HasSelectedPage())
++ continue;
++
++ const ScDPItemData& rData = pDim->GetSelectedData();
++ aCriteria.push_back(ScDPCacheTable::Criterion());
++ ScDPCacheTable::Criterion& r = aCriteria.back();
++ r.mnFieldIndex = static_cast<sal_Int32>(nField);
++ sal_Int32 nStrId = rSharedString.getStringId(rData.aString);
++ r.mpFilter.reset(
++ new ScDPCacheTable::SingleFilter(rSharedString, nStrId, rData.fValue, rData.bHasValue));
++ }
++ if (!aCriteria.empty())
++ pData->FilterCacheTable(aCriteria);
++}
++
+ void ScDPSource::CreateRes_Impl()
+ {
+ if ( !pResData )
+@@ -850,29 +919,8 @@ void ScDPSource::CreateRes_Impl()
+ }
+ else
+ {
+- {
+- ScSimpleSharedString& rSharedString = GetData()->GetSharedString();
++ FilterCacheTableByPageDimensions();
+
+- // filter table by page dimensions.
+- vector<ScDPCacheTable::Criterion> aCriteria;
+- for (i = 0; i < nPageDimCount; ++i)
+- {
+- ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nPageDims[i]);
+- if (!pDim || !pDim->HasSelectedPage())
+- continue;
+-
+- long nField = pDim->GetDimension();
+- const ScDPItemData& rData = pDim->GetSelectedData();
+- aCriteria.push_back(ScDPCacheTable::Criterion());
+- ScDPCacheTable::Criterion& r = aCriteria.back();
+- r.mnFieldIndex = static_cast<sal_Int32>(nField);
+- sal_Int32 nStrId = rSharedString.getStringId(rData.aString);
+- r.mpFilter.reset(
+- new ScDPCacheTable::SingleFilter(rSharedString, nStrId, rData.fValue, rData.bHasValue));
+- }
+- if (!aCriteria.empty())
+- pData->FilterCacheTable(aCriteria);
+- }
+ aInfo.aPageDims.reserve(nPageDimCount);
+ for (i = 0; i < nPageDimCount; ++i)
+ aInfo.aPageDims.push_back(nPageDims[i]);
+@@ -1065,6 +1113,7 @@ uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPSource::getPropertySetInfo(
+ throw(uno::RuntimeException)
+ {
+ ScUnoGuard aGuard;
++ using beans::PropertyAttribute::READONLY;
+
+ static SfxItemPropertyMap aDPSourceMap_Impl[] =
+ {
+@@ -1073,6 +1122,10 @@ uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPSource::getPropertySetInfo(
+ {MAP_CHAR_LEN(SC_UNO_IGNOREEM), 0, &getBooleanCppuType(), 0, 0 }, // for sheet data only
+ {MAP_CHAR_LEN(SC_UNO_REPEATIF), 0, &getBooleanCppuType(), 0, 0 }, // for sheet data only
+ {MAP_CHAR_LEN(SC_UNO_ROWGRAND), 0, &getBooleanCppuType(), 0, 0 },
++ {MAP_CHAR_LEN(SC_UNO_ROWFIELDCOUNT), 0, &getCppuType(static_cast<sal_Int32*>(0)), READONLY, 0 },
++ {MAP_CHAR_LEN(SC_UNO_COLUMNFIELDCOUNT), 0, &getCppuType(static_cast<sal_Int32*>(0)), READONLY, 0 },
++ {MAP_CHAR_LEN(SC_UNO_DATAFIELDCOUNT), 0, &getCppuType(static_cast<sal_Int32*>(0)), READONLY, 0 },
++ {MAP_CHAR_LEN(SC_UNO_GRANDTOTAL_NAME), 0, &getCppuType(static_cast<OUString*>(0)), 0, 0 },
+ {0,0,0,0,0,0}
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+@@ -1094,6 +1147,12 @@ void SAL_CALL ScDPSource::setPropertyValue( const rtl::OUString& aPropertyName,
+ setIgnoreEmptyRows( lcl_GetBoolFromAny( aValue ) );
+ else if ( aNameStr.EqualsAscii( SC_UNO_REPEATIF ) )
+ setRepeatIfEmpty( lcl_GetBoolFromAny( aValue ) );
++ else if (aNameStr.EqualsAscii(SC_UNO_GRANDTOTAL_NAME))
++ {
++ OUString aName;
++ if (aValue >>= aName)
++ mpGrandTotalName.reset(new OUString(aName));
++ }
+ else
+ {
+ DBG_ERROR("unknown property");
+@@ -1123,6 +1182,11 @@ uno::Any SAL_CALL ScDPSource::getPropertyValue( const rtl::OUString& aPropertyNa
+ aRet <<= static_cast<sal_Int32>(nColDimCount);
+ else if ( aNameStr.EqualsAscii( SC_UNO_DATAFIELDCOUNT ) ) // read-only
+ aRet <<= static_cast<sal_Int32>(nDataDimCount);
++ else if (aNameStr.EqualsAscii(SC_UNO_GRANDTOTAL_NAME))
++ {
++ if (mpGrandTotalName.get())
++ aRet <<= *mpGrandTotalName;
++ }
+ else
+ {
+ DBG_ERROR("unknown property");
+@@ -1270,6 +1334,8 @@ ScDPDimension::ScDPDimension( ScDPSource* pSrc, long nD ) :
+ pHierarchies( NULL ),
+ nUsedHier( 0 ),
+ nFunction( SUBTOTAL_FUNC_SUM ), // sum is default
++ mpLayoutName(NULL),
++ mpSubtotalName(NULL),
+ nSourceDim( -1 ),
+ bHasSelectedPage( FALSE ),
+ pSelectedData( NULL )
+@@ -1297,6 +1363,16 @@ ScDPHierarchies* ScDPDimension::GetHierarchiesObject()
+ return pHierarchies;
+ }
+
++const rtl::OUString* ScDPDimension::GetLayoutName() const
++{
++ return mpLayoutName.get();
++}
++
++const rtl::OUString* ScDPDimension::GetSubtotalName() const
++{
++ return mpSubtotalName.get();
++}
++
+ uno::Reference<container::XNameAccess> SAL_CALL ScDPDimension::getHierarchies()
+ throw(uno::RuntimeException)
+ {
+@@ -1456,6 +1532,8 @@ uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPDimension::getPropertySetIn
+ {MAP_CHAR_LEN(SC_UNO_POSITION), 0, &getCppuType((sal_Int32*)0), 0, 0 },
+ {MAP_CHAR_LEN(SC_UNO_REFVALUE), 0, &getCppuType((sheet::DataPilotFieldReference*)0), 0, 0 },
+ {MAP_CHAR_LEN(SC_UNO_USEDHIER), 0, &getCppuType((sal_Int32*)0), 0, 0 },
++ {MAP_CHAR_LEN(SC_UNO_LAYOUTNAME), 0, &getCppuType(static_cast<rtl::OUString*>(0)), 0, 0 },
++ {MAP_CHAR_LEN(SC_UNO_FIELD_SUBTOTALNAME), 0, &getCppuType(static_cast<rtl::OUString*>(0)), 0, 0 },
+ {0,0,0,0,0,0}
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+@@ -1526,6 +1604,18 @@ void SAL_CALL ScDPDimension::setPropertyValue( const rtl::OUString& aPropertyNam
+ }
+ DELETEZ( pSelectedData ); // invalid after changing aSelectedPage
+ }
++ else if (aNameStr.EqualsAscii(SC_UNO_LAYOUTNAME))
++ {
++ OUString aName;
++ if (aValue >>= aName)
++ mpLayoutName.reset(new OUString(aName));
++ }
++ else if (aNameStr.EqualsAscii(SC_UNO_FIELD_SUBTOTALNAME))
++ {
++ OUString aName;
++ if (aValue >>= aName)
++ mpSubtotalName.reset(new OUString(aName));
++ }
+ else
+ {
+ DBG_ERROR("unknown property");
+@@ -1585,6 +1675,10 @@ uno::Any SAL_CALL ScDPDimension::getPropertyValue( const rtl::OUString& aPropert
+ else
+ aRet <<= uno::Sequence<sheet::TableFilterField>(0);
+ }
++ else if (aNameStr.EqualsAscii(SC_UNO_LAYOUTNAME))
++ aRet <<= mpLayoutName.get() ? *mpLayoutName : OUString::createFromAscii("");
++ else if (aNameStr.EqualsAscii(SC_UNO_FIELD_SUBTOTALNAME))
++ aRet <<= mpSubtotalName.get() ? *mpSubtotalName : OUString::createFromAscii("");
+ else
+ {
+ DBG_ERROR("unknown property");
+@@ -2085,7 +2179,11 @@ uno::Sequence<sheet::MemberResult> SAL_CALL ScDPLevel::getResults() throw(uno::R
+ return aRet;
+ }
+
+- return pSource->GetData()->getDimensionName( nSrcDim ); // (original) dimension name
++ ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
++ if (!pDim)
++ return rtl::OUString();
++
++ return pDim->getName();
+ }
+
+ void SAL_CALL ScDPLevel::setName( const ::rtl::OUString& /* rNewName */ ) throw(uno::RuntimeException)
+@@ -2188,6 +2286,20 @@ uno::Any SAL_CALL ScDPLevel::getPropertyValue( const rtl::OUString& aPropertyNam
+ aRet <<= aAutoShowInfo;
+ else if ( aNameStr.EqualsAscii( SC_UNO_LAYOUT ) )
+ aRet <<= aLayoutInfo;
++ else if (aNameStr.EqualsAscii(SC_UNO_LAYOUTNAME))
++ {
++ // read only property
++ long nSrcDim = pSource->GetSourceDim(nDim);
++ ScDPDimension* pDim = pSource->GetDimensionsObject()->getByIndex(nSrcDim);
++ if (!pDim)
++ return aRet;
++
++ const OUString* pLayoutName = pDim->GetLayoutName();
++ if (!pLayoutName)
++ return aRet;
++
++ aRet <<= *pLayoutName;
++ }
+ else
+ {
+ DBG_ERROR("unknown property");
+@@ -2480,6 +2592,7 @@ ScDPMember::ScDPMember( ScDPSource* pSrc, long nD, long nH, long nL,
+ nHier( nH ),
+ nLev( nL ),
+ maData( rN, fV, bHV ),
++ mpLayoutName(NULL),
+ nPosition( -1 ),
+ bVisible( TRUE ),
+ bShowDet( TRUE )
+@@ -2540,6 +2653,11 @@ void ScDPMember::FillItemData( ScDPItemData& rData ) const
+ rData = maData;
+ }
+
++const OUString* ScDPMember::GetLayoutName() const
++{
++ return mpLayoutName.get();
++}
++
+ String ScDPMember::GetNameStr() const
+ {
+ return maData.aString;
+@@ -2599,6 +2717,7 @@ uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPMember::getPropertySetInfo(
+ {MAP_CHAR_LEN(SC_UNO_ISVISIBL), 0, &getBooleanCppuType(), 0, 0 },
+ {MAP_CHAR_LEN(SC_UNO_POSITION), 0, &getCppuType((sal_Int32*)0), 0, 0 },
+ {MAP_CHAR_LEN(SC_UNO_SHOWDETA), 0, &getBooleanCppuType(), 0, 0 },
++ {MAP_CHAR_LEN(SC_UNO_LAYOUTNAME), 0, &getCppuType(static_cast<rtl::OUString*>(0)), 0, 0 },
+ {0,0,0,0,0,0}
+ };
+ static uno::Reference<beans::XPropertySetInfo> aRef =
+@@ -2622,6 +2741,12 @@ void SAL_CALL ScDPMember::setPropertyValue( const rtl::OUString& aPropertyName,
+ if (aValue >>= nInt)
+ setPosition( nInt );
+ }
++ else if (aNameStr.EqualsAscii(SC_UNO_LAYOUTNAME))
++ {
++ rtl::OUString aName;
++ if (aValue >>= aName)
++ mpLayoutName.reset(new rtl::OUString(aName));
++ }
+ else
+ {
+ DBG_ERROR("unknown property");
+@@ -2641,6 +2766,8 @@ uno::Any SAL_CALL ScDPMember::getPropertyValue( const rtl::OUString& aPropertyNa
+ lcl_SetBoolInAny( aRet, getShowDetails() );
+ else if ( aNameStr.EqualsAscii( SC_UNO_POSITION ) )
+ aRet <<= (sal_Int32) getPosition();
++ else if (aNameStr.EqualsAscii(SC_UNO_LAYOUTNAME))
++ aRet <<= mpLayoutName.get() ? *mpLayoutName : rtl::OUString();
+ else
+ {
+ DBG_ERROR("unknown property");
+diff --git sc/source/filter/excel/read.cxx sc/source/filter/excel/read.cxx
+index a868e17..27f0446 100644
+--- sc/source/filter/excel/read.cxx
++++ sc/source/filter/excel/read.cxx
+@@ -1187,6 +1187,9 @@ FltError ImportExcel8::Read( void )
+ eLastErr = SCWARN_IMPORT_ROW_OVERFLOW;
+ else if( rAddrConv.IsColTruncated() )
+ eLastErr = SCWARN_IMPORT_COLUMN_OVERFLOW;
++
++ if( GetBiff() == EXC_BIFF8 )
++ GetPivotTableManager().MaybeRefreshPivotTables();
+ }
+
+ return eLastErr;
+diff --git sc/source/filter/excel/xepivot.cxx sc/source/filter/excel/xepivot.cxx
+index 2a4994a..824ea9b 100644
+--- sc/source/filter/excel/xepivot.cxx
++++ sc/source/filter/excel/xepivot.cxx
+@@ -64,6 +64,7 @@ using ::com::sun::star::sheet::DataPilotFieldSortInfo;
+ using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo;
+ using ::com::sun::star::sheet::DataPilotFieldLayoutInfo;
+ using ::com::sun::star::sheet::DataPilotFieldReference;
++using ::rtl::OUString;
+
+ // ============================================================================
+ // Pivot cache
+@@ -942,6 +943,11 @@ void XclExpPTItem::SetPropertiesFromMember( const ScDPSaveMember& rSaveMem )
+ {
+ ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN, !rSaveMem.GetIsVisible() );
+ ::set_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL, !rSaveMem.GetShowDetails() );
++
++ // visible name
++ const OUString* pVisName = rSaveMem.GetLayoutName();
++ if (pVisName && !pVisName->equals(GetItemName()))
++ maItemInfo.SetVisName(*pVisName);
+ }
+
+ void XclExpPTItem::WriteBody( XclExpStream& rStrm )
+@@ -1010,8 +1016,13 @@ void XclExpPTField::SetPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+ ::set_flag( maFieldExtInfo.mnFlags, EXC_SXVDEX_SHOWALL, rSaveDim.GetShowEmpty() );
+
+ // visible name
+- if( rSaveDim.HasLayoutName() && (rSaveDim.GetLayoutName() != GetFieldName()) )
+- maFieldInfo.SetVisName( rSaveDim.GetLayoutName() );
++ const OUString* pLayoutName = rSaveDim.GetLayoutName();
++ if (pLayoutName && !pLayoutName->equals(GetFieldName()))
++ maFieldInfo.SetVisName(*pLayoutName);
++
++ const rtl::OUString* pSubtotalName = rSaveDim.GetSubtotalName();
++ if (pSubtotalName)
++ maFieldExtInfo.mpFieldTotalName.reset(new rtl::OUString(*pSubtotalName));
+
+ // subtotals
+ XclPTSubtotalVec aSubtotals;
+@@ -1078,7 +1089,11 @@ void XclExpPTField::SetDataPropertiesFromDim( const ScDPSaveDimension& rSaveDim
+ rDataInfo.SetApiAggFunc( eFunc );
+
+ // visible name
+- rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
++ const rtl::OUString* pVisName = rSaveDim.GetLayoutName();
++ if (pVisName)
++ rDataInfo.SetVisName(*pVisName);
++ else
++ rDataInfo.SetVisName( lclGetDataFieldCaption( GetFieldName(), eFunc ) );
+
+ // result field reference
+ if( const DataPilotFieldReference* pFieldRef = rSaveDim.GetReferenceValue() )
+@@ -1187,10 +1202,9 @@ XclExpPivotTable::XclExpPivotTable( const XclExpRoot& rRoot, const ScDPObject& r
+ // pivot table properties from DP object
+ mnOutScTab = rOutScRange.aStart.Tab();
+ maPTInfo.maTableName = rDPObj.GetName();
+- maPTInfo.maDataName = ScGlobal::GetRscString( STR_PIVOT_DATA );
+ maPTInfo.mnCacheIdx = mrPCache.GetCacheIndex();
+
+- maPTAutoFormat.Init( rDPObj );
++ maPTViewEx9Info.Init( rDPObj );
+
+ if( const ScDPSaveData* pSaveData = rDPObj.GetSaveData() )
+ {
+@@ -1271,8 +1285,10 @@ void XclExpPivotTable::Save( XclExpStream& rStrm )
+ WriteSxli( rStrm, maPTInfo.mnDataCols, maPTInfo.mnColFields );
+ // SXEX
+ WriteSxex( rStrm );
+- // SX_AUTOFORMAT
+- WriteSxAutoformat( rStrm );
++ // QSISXTAG
++ WriteQsiSxTag( rStrm );
++ // SXVIEWEX9
++ WriteSxViewEx9( rStrm );
+ }
+ }
+
+@@ -1306,6 +1322,15 @@ void XclExpPivotTable::SetPropertiesFromDP( const ScDPSaveData& rSaveData )
+ ::set_flag( maPTInfo.mnFlags, EXC_SXVIEW_COLGRAND, rSaveData.GetColumnGrand() );
+ ::set_flag( maPTExtInfo.mnFlags, EXC_SXEX_DRILLDOWN, rSaveData.GetDrillDown() );
+ mbFilterBtn = rSaveData.GetFilterButton();
++ const ScDPSaveDimension* pDim = rSaveData.GetExistingDataLayoutDimension();
++ if (!pDim)
++ return;
++
++ const rtl::OUString* pLayoutName = pDim->GetLayoutName();
++ if (pLayoutName)
++ maPTInfo.maDataName = *pLayoutName;
++ else
++ maPTInfo.maDataName = ScGlobal::GetRscString(STR_PIVOT_DATA);
+ }
+
+ void XclExpPivotTable::SetFieldPropertiesFromDim( const ScDPSaveDimension& rSaveDim )
+@@ -1413,17 +1438,21 @@ void XclExpPivotTable::Finalize()
+ rnDataXclRow = rnXclRow1 + maPTInfo.mnColFields + 1;
+ if( maDataFields.empty() )
+ ++rnDataXclRow;
+- if( 0 == maPTAutoFormat.mnGridLayout )
++
++ bool bExtraHeaderRow = (0 == maPTViewEx9Info.mnGridLayout && maPTInfo.mnColFields == 0);
++ if (bExtraHeaderRow)
++ // Insert an extra row only when there is no column field.
+ ++rnDataXclRow;
++
+ rnXclCol2 = ::std::max( rnXclCol2, rnDataXclCol );
+ rnXclRow2 = ::std::max( rnXclRow2, rnDataXclRow );
+ maPTInfo.mnDataCols = rnXclCol2 - rnDataXclCol + 1;
+ maPTInfo.mnDataRows = rnXclRow2 - rnDataXclRow + 1;
+
+ // first heading
+- maPTInfo.mnFirstHeadRow = rnXclRow1 + 1;
+- if( 0 == maPTAutoFormat.mnGridLayout )
+- maPTInfo.mnFirstHeadRow++;
++ maPTInfo.mnFirstHeadRow = rnXclRow1;
++ if (bExtraHeaderRow)
++ maPTInfo.mnFirstHeadRow += 2;
+ }
+
+ // records ----------------------------------------------------------------
+@@ -1499,14 +1528,68 @@ void XclExpPivotTable::WriteSxex( XclExpStream& rStrm ) const
+ rStrm.EndRecord();
+ }
+
+-void XclExpPivotTable::WriteSxAutoformat( XclExpStream& rStrm ) const
++void XclExpPivotTable::WriteQsiSxTag( XclExpStream& rStrm ) const
++{
++ rStrm.StartRecord( 0x0802, 32 );
++
++ sal_uInt16 nRecordType = 0x0802;
++ sal_uInt16 nDummyFlags = 0x0000;
++ sal_uInt16 nTableType = 1; // 0 = query table : 1 = pivot table
++
++ rStrm << nRecordType << nDummyFlags << nTableType;
++
++ // General flags
++ bool bEnableRefresh = true;
++ bool bPCacheInvalid = false;
++ bool bOlapPTReport = false;
++
++ sal_uInt16 nFlags = 0x0000;
++ if (bEnableRefresh) nFlags |= 0x0001;
++ if (bPCacheInvalid) nFlags |= 0x0002;
++ if (bOlapPTReport) nFlags |= 0x0004;
++ rStrm << nFlags;
++
++ // Feature-specific options. The value differs depending on the table
++ // type, but we assume the table type is always pivot table.
++ sal_uInt32 nOptions = 0x00000000;
++ bool bNoStencil = false;
++ bool bHideTotal = false;
++ bool bEmptyRows = false;
++ bool bEmptyCols = false;
++ if (bNoStencil) nOptions |= 0x00000001;
++ if (bHideTotal) nOptions |= 0x00000002;
++ if (bEmptyRows) nOptions |= 0x00000008;
++ if (bEmptyCols) nOptions |= 0x00000010;
++ rStrm << nOptions;
++
++ enum ExcelVersion
++ {
++ Excel2000 = 0,
++ ExcelXP = 1,
++ Excel2003 = 2,
++ Excel2007 = 3
++ };
++ ExcelVersion eXclVer = Excel2000;
++ sal_uInt8 nOffsetBytes = 16;
++ rStrm << static_cast<sal_uInt8>(eXclVer) // version table last refreshed
++ << static_cast<sal_uInt8>(eXclVer) // minimum version to refresh
++ << nOffsetBytes
++ << static_cast<sal_uInt8>(eXclVer); // first version created
++
++ rStrm << XclExpString(maPTInfo.maTableName);
++ rStrm << static_cast<sal_uInt16>(0x0001); // no idea what this is for.
++
++ rStrm.EndRecord();
++}
++
++void XclExpPivotTable::WriteSxViewEx9( XclExpStream& rStrm ) const
+ {
+ // Until we sync the autoformat ids only export if using grid header layout
+ // That could only have been set via xls import so far.
+- if ( 0 == maPTAutoFormat.mnGridLayout )
++ if ( 0 == maPTViewEx9Info.mnGridLayout )
+ {
+ rStrm.StartRecord( EXC_ID_SXVIEWEX9, 17 );
+- rStrm << maPTAutoFormat;
++ rStrm << maPTViewEx9Info;
+ rStrm.EndRecord();
+ }
+ }
+diff --git sc/source/filter/excel/xestring.cxx sc/source/filter/excel/xestring.cxx
+index c029ad5..e16f9aa 100644
+--- sc/source/filter/excel/xestring.cxx
++++ sc/source/filter/excel/xestring.cxx
+@@ -415,7 +415,8 @@ void XclExpString::WriteFormats( XclExpStream& rStrm, bool bWriteSize ) const
+
+ void XclExpString::Write( XclExpStream& rStrm ) const
+ {
+- WriteHeader( rStrm );
++ if (!mbSkipHeader)
++ WriteHeader( rStrm );
+ WriteBuffer( rStrm );
+ if( IsWriteFormats() ) // only in BIFF8 included in string
+ WriteFormats( rStrm );
+@@ -529,6 +530,7 @@ void XclExpString::Init( sal_Int32 nCurrLen, XclStrFlags nFlags, sal_uInt16 nMax
+ mbSmartFlags = bBiff8 && ::get_flag( nFlags, EXC_STR_SMARTFLAGS );
+ mbSkipFormats = ::get_flag( nFlags, EXC_STR_SEPARATEFORMATS );
+ mbWrapped = false;
++ mbSkipHeader = ::get_flag( nFlags, EXC_STR_NOHEADER );
+ mnMaxLen = nMaxLen;
+ SetStrLen( nCurrLen );
+
+diff --git sc/source/filter/excel/xipivot.cxx sc/source/filter/excel/xipivot.cxx
+index 22379de..74b42b1 100644
+--- sc/source/filter/excel/xipivot.cxx
++++ sc/source/filter/excel/xipivot.cxx
+@@ -847,6 +847,11 @@ void XclImpPivotCache::ReadPivotCacheStream( XclImpStream& rStrm )
+ }
+ }
+
++bool XclImpPivotCache::IsRefreshOnLoad() const
++{
++ return static_cast<bool>(maPCInfo.mnFlags & 0x0004);
++}
++
+ // ============================================================================
+ // Pivot table
+ // ============================================================================
+@@ -882,6 +887,8 @@ void XclImpPTItem::ConvertItem( ScDPSaveDimension& rSaveDim ) const
+ ScDPSaveMember& rMember = *rSaveDim.GetMemberByName( *pItemName );
+ rMember.SetIsVisible( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDDEN ) );
+ rMember.SetShowDetails( !::get_flag( maItemInfo.mnFlags, EXC_SXVI_HIDEDETAIL ) );
++ if (maItemInfo.HasVisName())
++ rMember.SetLayoutName(*maItemInfo.GetVisName());
+ }
+ }
+
+@@ -1043,7 +1050,7 @@ ScDPSaveDimension* XclImpPTField::ConvertRCPField( ScDPSaveData& rSaveData ) con
+ // visible name
+ if( const String* pVisName = maFieldInfo.GetVisName() )
+ if( pVisName->Len() > 0 )
+- rSaveDim.SetLayoutName( pVisName );
++ rSaveDim.SetLayoutName( *pVisName );
+
+ // subtotal function(s)
+ XclPTSubtotalVec aSubtotalVec;
+@@ -1075,6 +1082,10 @@ ScDPSaveDimension* XclImpPTField::ConvertRCPField( ScDPSaveData& rSaveData ) con
+ // grouping info
+ pCacheField->ConvertGroupField( rSaveData, mrPTable.GetVisFieldNames() );
+
++ // custom subtotal name
++ if (maFieldExtInfo.mpFieldTotalName.get())
++ rSaveDim.SetSubtotalName(*maFieldExtInfo.mpFieldTotalName);
++
+ return &rSaveDim;
+ }
+
+@@ -1099,7 +1110,7 @@ void XclImpPTField::ConvertDataFieldInfo( ScDPSaveDimension& rSaveDim, const Xcl
+ // visible name
+ if( const String* pVisName = rDataInfo.GetVisName() )
+ if( pVisName->Len() > 0 )
+- rSaveDim.SetLayoutName( pVisName );
++ rSaveDim.SetLayoutName( *pVisName );
+
+ // aggregation function
+ rSaveDim.SetFunction( static_cast< USHORT >( rDataInfo.GetApiAggFunc() ) );
+@@ -1134,7 +1145,8 @@ void XclImpPTField::ConvertItems( ScDPSaveDimension& rSaveDim ) const
+
+ XclImpPivotTable::XclImpPivotTable( const XclImpRoot& rRoot ) :
+ XclImpRoot( rRoot ),
+- maDataOrientField( *this, EXC_SXIVD_DATA )
++ maDataOrientField( *this, EXC_SXIVD_DATA ),
++ mpDPObj(NULL)
+ {
+ }
+
+@@ -1298,7 +1310,7 @@ void XclImpPivotTable::ReadSxex( XclImpStream& rStrm )
+
+ void XclImpPivotTable::ReadSxViewEx9( XclImpStream& rStrm )
+ {
+- rStrm >> maPTAutoFormat;
++ rStrm >> maPTViewEx9Info;
+ }
+
+ // ----------------------------------------------------------------------------
+@@ -1336,11 +1348,14 @@ void XclImpPivotTable::Convert()
+ if( const XclImpPTField* pField = GetField( *aIt ) )
+ pField->ConvertPageField( aSaveData );
+
++#if 0 // Why do we do this ???
++
+ // hidden fields
+ for( sal_uInt16 nField = 0, nCount = GetFieldCount(); nField < nCount; ++nField )
+ if( const XclImpPTField* pField = GetField( nField ) )
+ if( (pField->GetAxes() & EXC_SXVD_AXIS_ROWCOLPAGE) == 0 )
+ pField->ConvertHiddenField( aSaveData );
++#endif
+
+ // data fields
+ for( aIt = maFiltDataFields.begin(), aEnd = maFiltDataFields.end(); aIt != aEnd; ++aIt )
+@@ -1364,12 +1379,30 @@ void XclImpPivotTable::Convert()
+ // create the DataPilot
+ ScDPObject* pDPObj = new ScDPObject( GetDocPtr() );
+ pDPObj->SetName( maPTInfo.maTableName );
++ if (maPTInfo.maDataName.Len() > 0)
++ aSaveData.GetDataLayoutDimension()->SetLayoutName(maPTInfo.maDataName);
++
++ if (maPTViewEx9Info.maGrandTotalName.Len() > 0)
++ aSaveData.SetGrandTotalName(maPTViewEx9Info.maGrandTotalName);
++
+ pDPObj->SetSaveData( aSaveData );
+ pDPObj->SetSheetDesc( aDesc );
+ pDPObj->SetOutRange( aOutRange );
+ pDPObj->SetAlive( TRUE );
+- pDPObj->SetHeaderLayout( maPTAutoFormat.mnGridLayout == 0 );
++ pDPObj->SetHeaderLayout( maPTViewEx9Info.mnGridLayout == 0 );
++
+ GetDoc().GetDPCollection()->Insert( pDPObj );
++ mpDPObj = pDPObj;
++}
++
++void XclImpPivotTable::MaybeRefresh()
++{
++ if (mpDPObj && mxPCache->IsRefreshOnLoad())
++ {
++ // 'refresh table on load' flag is set. Refresh the table now. Some
++ // Excel files contain partial table output when this flag is set.
++ mpDPObj->Output();
++ }
+ }
+
+ // ============================================================================
+@@ -1484,85 +1517,10 @@ void XclImpPivotTableManager::ConvertPivotTables()
+ (*aIt)->Convert();
+ }
+
+-// ============================================================================
+-
+-// Pivot table autoformat settings ============================================
+-
+-/**
+-classic : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00
+-default : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00
+-report01 : 10 08 02 00 00 00 00 00 20 00 00 00 00 10 00 00 00
+-report02 : 10 08 02 00 00 00 00 00 20 00 00 00 01 10 00 00 00
+-report03 : 10 08 02 00 00 00 00 00 20 00 00 00 02 10 00 00 00
+-report04 : 10 08 02 00 00 00 00 00 20 00 00 00 03 10 00 00 00
+-report05 : 10 08 02 00 00 00 00 00 20 00 00 00 04 10 00 00 00
+-report06 : 10 08 02 00 00 00 00 00 20 00 00 00 05 10 00 00 00
+-report07 : 10 08 02 00 00 00 00 00 20 00 00 00 06 10 00 00 00
+-report08 : 10 08 02 00 00 00 00 00 20 00 00 00 07 10 00 00 00
+-report09 : 10 08 02 00 00 00 00 00 20 00 00 00 08 10 00 00 00
+-report10 : 10 08 02 00 00 00 00 00 20 00 00 00 09 10 00 00 00
+-table01 : 10 08 00 00 00 00 00 00 20 00 00 00 0a 10 00 00 00
+-table02 : 10 08 00 00 00 00 00 00 20 00 00 00 0b 10 00 00 00
+-table03 : 10 08 00 00 00 00 00 00 20 00 00 00 0c 10 00 00 00
+-table04 : 10 08 00 00 00 00 00 00 20 00 00 00 0d 10 00 00 00
+-table05 : 10 08 00 00 00 00 00 00 20 00 00 00 0e 10 00 00 00
+-table06 : 10 08 00 00 00 00 00 00 20 00 00 00 0f 10 00 00 00
+-table07 : 10 08 00 00 00 00 00 00 20 00 00 00 10 10 00 00 00
+-table08 : 10 08 00 00 00 00 00 00 20 00 00 00 11 10 00 00 00
+-table09 : 10 08 00 00 00 00 00 00 20 00 00 00 12 10 00 00 00
+-table10 : 10 08 00 00 00 00 00 00 20 00 00 00 13 10 00 00 00
+-none : 10 08 00 00 00 00 00 00 20 00 00 00 15 10 00 00 00
+-**/
+-
+-XclPTAutoFormat::XclPTAutoFormat() :
+- mbReport( 0 ),
+- mnAutoFormat( 0 ),
+- mnGridLayout( 0x10 )
+-{
+-}
+-
+-void XclPTAutoFormat::Init( const ScDPObject& rDPObj )
+-{
+- if( rDPObj.GetHeaderLayout() )
+- {
+- mbReport = 0;
+- mnAutoFormat = 1;
+- mnGridLayout = 0;
+- }
+- else
+- {
+- // Report1 for now
+- // TODO : sync with autoformat indicies
+- mbReport = 2;
+- mnAutoFormat = 1;
+- mnGridLayout = 0x10;
+- }
+-}
+-
+-XclImpStream& operator>>( XclImpStream& rStrm, XclPTAutoFormat& rInfo )
+-{
+- rStrm.Ignore( 2 );
+- rStrm >> rInfo.mbReport; /// 2 for report* fmts ?
+- rStrm.Ignore( 6 );
+- sal_uInt8 nDummy;
+- return rStrm
+- >> rInfo.mnAutoFormat
+- >> rInfo.mnGridLayout
+- >> nDummy >> nDummy >> nDummy;
+-}
+-
+-XclExpStream& operator<<( XclExpStream& rStrm, const XclPTAutoFormat& rInfo )
++void XclImpPivotTableManager::MaybeRefreshPivotTables()
+ {
+- return rStrm
+- << EXC_PT_AUTOFMT_HEADER
+- << rInfo.mbReport
+- << EXC_PT_AUTOFMT_ZERO
+- << EXC_PT_AUTOFMT_FLAGS
+- << rInfo.mnAutoFormat
+- << rInfo.mnGridLayout
+- << static_cast<sal_uInt8>(0x00)
+- << static_cast<sal_uInt8>(0x00)
+- << static_cast<sal_uInt8>(0x00);
++ for( XclImpPivotTableVec::iterator aIt = maPTables.begin(), aEnd = maPTables.end(); aIt != aEnd; ++aIt )
++ (*aIt)->MaybeRefresh();
+ }
+
+ // ============================================================================
+diff --git sc/source/filter/excel/xlpivot.cxx sc/source/filter/excel/xlpivot.cxx
+index 7840f05..6267a66 100644
+--- sc/source/filter/excel/xlpivot.cxx
++++ sc/source/filter/excel/xlpivot.cxx
+@@ -32,6 +32,7 @@
+ #include "precompiled_sc.hxx"
+ #include "xlpivot.hxx"
+ #include "dpgroup.hxx"
++#include "dpsave.hxx"
+ #include "xistream.hxx"
+ #include "xestream.hxx"
+ #include "xestring.hxx"
+@@ -588,7 +589,9 @@ XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldInfo& rInfo )
+ XclPTFieldExtInfo::XclPTFieldExtInfo() :
+ mnFlags( EXC_SXVDEX_DEFAULTFLAGS ),
+ mnSortField( EXC_SXVDEX_SORT_OWN ),
+- mnShowField( EXC_SXVDEX_SHOW_NONE )
++ mnShowField( EXC_SXVDEX_SHOW_NONE ),
++ mnNumFmt(0),
++ mpFieldTotalName(NULL)
+ {
+ }
+
+@@ -648,10 +651,19 @@ void XclPTFieldExtInfo::SetApiLayoutMode( sal_Int32 nLayoutMode )
+
+ XclImpStream& operator>>( XclImpStream& rStrm, XclPTFieldExtInfo& rInfo )
+ {
+- return rStrm
+- >> rInfo.mnFlags
+- >> rInfo.mnSortField
+- >> rInfo.mnShowField;
++ sal_uInt8 nNameLen = 0;
++ rStrm >> rInfo.mnFlags
++ >> rInfo.mnSortField
++ >> rInfo.mnShowField
++ >> rInfo.mnNumFmt
++ >> nNameLen;
++
++ rStrm.Ignore(10);
++ if (nNameLen != 0xFF)
++ // Custom field total name is used. Pick it up.
++ rInfo.mpFieldTotalName.reset(new rtl::OUString(rStrm.ReadUniString(nNameLen, 0)));
++
++ return rStrm;
+ }
+
+ XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldExtInfo& rInfo )
+@@ -659,9 +671,23 @@ XclExpStream& operator<<( XclExpStream& rStrm, const XclPTFieldExtInfo& rInfo )
+ rStrm << rInfo.mnFlags
+ << rInfo.mnSortField
+ << rInfo.mnShowField
+- << EXC_SXVDEX_FORMAT_NONE
+- << sal_uInt16( 0xFFFF ); // unknown
+- rStrm.WriteZeroBytes( 8 ); // unknown
++ << EXC_SXVDEX_FORMAT_NONE;
++
++ if (rInfo.mpFieldTotalName.get() && rInfo.mpFieldTotalName->getLength() > 0)
++ {
++ rtl::OUString aFinalName = *rInfo.mpFieldTotalName;
++ if (aFinalName.getLength() >= 254)
++ aFinalName = aFinalName.copy(0, 254);
++ sal_uInt8 nNameLen = static_cast<sal_uInt8>(aFinalName.getLength());
++ rStrm << nNameLen;
++ rStrm.WriteZeroBytes(10);
++ rStrm << XclExpString(aFinalName, EXC_STR_NOHEADER);
++ }
++ else
++ {
++ rStrm << sal_uInt16(0xFFFF);
++ rStrm.WriteZeroBytes(8);
++ }
+ return rStrm;
+ }
+
+@@ -932,3 +958,86 @@ XclExpStream& operator<<( XclExpStream& rStrm, const XclPTExtInfo& rInfo )
+
+ // ============================================================================
+
++// Pivot table autoformat settings ============================================
++
++/**
++classic : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00
++default : 10 08 00 00 00 00 00 00 20 00 00 00 01 00 00 00 00
++report01 : 10 08 02 00 00 00 00 00 20 00 00 00 00 10 00 00 00
++report02 : 10 08 02 00 00 00 00 00 20 00 00 00 01 10 00 00 00
++report03 : 10 08 02 00 00 00 00 00 20 00 00 00 02 10 00 00 00
++report04 : 10 08 02 00 00 00 00 00 20 00 00 00 03 10 00 00 00
++report05 : 10 08 02 00 00 00 00 00 20 00 00 00 04 10 00 00 00
++report06 : 10 08 02 00 00 00 00 00 20 00 00 00 05 10 00 00 00
++report07 : 10 08 02 00 00 00 00 00 20 00 00 00 06 10 00 00 00
++report08 : 10 08 02 00 00 00 00 00 20 00 00 00 07 10 00 00 00
++report09 : 10 08 02 00 00 00 00 00 20 00 00 00 08 10 00 00 00
++report10 : 10 08 02 00 00 00 00 00 20 00 00 00 09 10 00 00 00
++table01 : 10 08 00 00 00 00 00 00 20 00 00 00 0a 10 00 00 00
++table02 : 10 08 00 00 00 00 00 00 20 00 00 00 0b 10 00 00 00
++table03 : 10 08 00 00 00 00 00 00 20 00 00 00 0c 10 00 00 00
++table04 : 10 08 00 00 00 00 00 00 20 00 00 00 0d 10 00 00 00
++table05 : 10 08 00 00 00 00 00 00 20 00 00 00 0e 10 00 00 00
++table06 : 10 08 00 00 00 00 00 00 20 00 00 00 0f 10 00 00 00
++table07 : 10 08 00 00 00 00 00 00 20 00 00 00 10 10 00 00 00
++table08 : 10 08 00 00 00 00 00 00 20 00 00 00 11 10 00 00 00
++table09 : 10 08 00 00 00 00 00 00 20 00 00 00 12 10 00 00 00
++table10 : 10 08 00 00 00 00 00 00 20 00 00 00 13 10 00 00 00
++none : 10 08 00 00 00 00 00 00 20 00 00 00 15 10 00 00 00
++**/
++
++XclPTViewEx9Info::XclPTViewEx9Info() :
++ mbReport( 0 ),
++ mnAutoFormat( 0 ),
++ mnGridLayout( 0x10 )
++{
++}
++
++void XclPTViewEx9Info::Init( const ScDPObject& rDPObj )
++{
++ if( rDPObj.GetHeaderLayout() )
++ {
++ mbReport = 0;
++ mnAutoFormat = 1;
++ mnGridLayout = 0;
++ }
++ else
++ {
++ // Report1 for now
++ // TODO : sync with autoformat indicies
++ mbReport = 2;
++ mnAutoFormat = 1;
++ mnGridLayout = 0x10;
++ }
++
++ const ScDPSaveData* pData = rDPObj.GetSaveData();
++ if (pData)
++ {
++ const rtl::OUString* pGrandTotal = pData->GetGrandTotalName();
++ if (pGrandTotal)
++ maGrandTotalName = *pGrandTotal;
++ }
++}
++
++XclImpStream& operator>>( XclImpStream& rStrm, XclPTViewEx9Info& rInfo )
++{
++ rStrm.Ignore( 2 );
++ rStrm >> rInfo.mbReport; /// 2 for report* fmts ?
++ rStrm.Ignore( 6 );
++ rStrm >> rInfo.mnAutoFormat >> rInfo.mnGridLayout;
++ rInfo.maGrandTotalName = rStrm.ReadUniString();
++ return rStrm;
++}
++
++XclExpStream& operator<<( XclExpStream& rStrm, const XclPTViewEx9Info& rInfo )
++{
++ return rStrm
++ << EXC_PT_AUTOFMT_HEADER
++ << rInfo.mbReport
++ << EXC_PT_AUTOFMT_ZERO
++ << EXC_PT_AUTOFMT_FLAGS
++ << rInfo.mnAutoFormat
++ << rInfo.mnGridLayout
++ << XclExpString(rInfo.maGrandTotalName, EXC_STR_DEFAULT, EXC_PT_MAXSTRLEN);
++}
++
+diff --git sc/source/filter/inc/xepivot.hxx sc/source/filter/inc/xepivot.hxx
+index 62a19b5..41e0907 100644
+--- sc/source/filter/inc/xepivot.hxx
++++ sc/source/filter/inc/xepivot.hxx
+@@ -411,8 +411,10 @@ private:
+ void WriteSxli( XclExpStream& rStrm, sal_uInt16 nLineCount, sal_uInt16 nIndexCount ) const;
+ /** Writes the SXEX records containing additional pivot table info. */
+ void WriteSxex( XclExpStream& rStrm ) const;
++
++ void WriteQsiSxTag( XclExpStream& rStrm ) const;
+ /** Writes the SX_AUTOFORMAT records with the autoformat id and header layout */
+- void WriteSxAutoformat( XclExpStream& rStrm ) const;
++ void WriteSxViewEx9( XclExpStream& rStrm ) const;
+
+ // ------------------------------------------------------------------------
+ private:
+@@ -423,7 +425,7 @@ private:
+ const XclExpPivotCache& mrPCache; /// The pivot cache this pivot table bases on.
+ XclPTInfo maPTInfo; /// Info about the pivot table (SXVIEW record).
+ XclPTExtInfo maPTExtInfo; /// Extended info about the pivot table (SXEX record).
+- XclPTAutoFormat maPTAutoFormat; /// The selected autoformat (SXVIEWEX9)
++ XclPTViewEx9Info maPTViewEx9Info; /// The selected autoformat (SXVIEWEX9)
+ XclExpPTFieldList maFieldList; /// All fields in pivot cache order.
+ ScfUInt16Vec maRowFields; /// Row field indexes.
+ ScfUInt16Vec maColFields; /// Column field indexes.
+diff --git sc/source/filter/inc/xestring.hxx sc/source/filter/inc/xestring.hxx
+index 535ae30..94fab98 100644
+--- sc/source/filter/inc/xestring.hxx
++++ sc/source/filter/inc/xestring.hxx
+@@ -315,6 +315,7 @@ private:
+ bool mbSmartFlags; /// true = omit flags on empty string; false = always write flags.
+ bool mbSkipFormats; /// true = skip formats on export; false = write complete formatted string.
+ bool mbWrapped; /// true = text contains several paragraphs.
++ bool mbSkipHeader; /// ture = skip length and flags when writing string bytes.
+ };
+
+ inline bool operator==( const XclExpString& rLeft, const XclExpString& rRight )
+diff --git sc/source/filter/inc/xipivot.hxx sc/source/filter/inc/xipivot.hxx
+index 365ea53..813963c 100644
+--- sc/source/filter/inc/xipivot.hxx
++++ sc/source/filter/inc/xipivot.hxx
+@@ -186,6 +186,8 @@ public:
+ /** Reads the entire pivot cache stream. Uses decrypter from passed stream. */
+ void ReadPivotCacheStream( XclImpStream& rStrm );
+
++ bool IsRefreshOnLoad() const;
++
+ private:
+ typedef ::std::vector< XclImpPCFieldRef > XclImpPCFieldVec;
+
+@@ -359,6 +361,8 @@ public:
+ /** Inserts the pivot table into the Calc document. */
+ void Convert();
+
++ void MaybeRefresh();
++
+ // ------------------------------------------------------------------------
+ private:
+ typedef ::std::vector< XclImpPTFieldRef > XclImpPTFieldVec;
+@@ -367,7 +371,7 @@ private:
+
+ XclPTInfo maPTInfo; /// General info about the pivot table (SXVIEW record).
+ XclPTExtInfo maPTExtInfo; /// Extended info about the pivot table (SXEX record).
+- XclPTAutoFormat maPTAutoFormat; /// The selected autoformat (SX_AUTOFORMAT)
++ XclPTViewEx9Info maPTViewEx9Info; /// (SXVIEWEX9 record)
+ XclImpPTFieldVec maFields; /// Vector containing all fields.
+ XclImpPTFieldRef mxCurrField; /// Current field for importing additional info.
+ ScfStringVec maVisFieldNames; /// Vector containing all visible field names.
+@@ -378,6 +382,7 @@ private:
+ ScfUInt16Vec maFiltDataFields; /// Filtered data field indexes.
+ XclImpPTField maDataOrientField; /// Special data field orientation field.
+ ScRange maOutScRange; /// Output range in the Calc document.
++ ScDPObject* mpDPObj;
+ };
+
+ typedef ScfRef< XclImpPivotTable > XclImpPivotTableRef;
+@@ -437,6 +442,8 @@ public:
+ /** Inserts all pivot tables into the Calc document. */
+ void ConvertPivotTables();
+
++ void MaybeRefreshPivotTables();
++
+ private:
+ typedef ::std::vector< XclImpPivotCacheRef > XclImpPivotCacheVec;
+ typedef ::std::vector< XclImpPivotTableRef > XclImpPivotTableVec;
+diff --git sc/source/filter/inc/xlpivot.hxx sc/source/filter/inc/xlpivot.hxx
+index 22b4e1f..379a7e9 100644
+--- sc/source/filter/inc/xlpivot.hxx
++++ sc/source/filter/inc/xlpivot.hxx
+@@ -43,6 +43,8 @@
+ #include "xladdress.hxx"
+ #include "dpobject.hxx"
+
++#include <memory>
++
+ class XclImpStream;
+ class XclExpStream;
+
+@@ -671,6 +673,8 @@ struct XclPTFieldExtInfo
+ sal_uInt32 mnFlags; /// Several flags and number of items for AutoShow.
+ sal_uInt16 mnSortField; /// Index to data field sorting bases on.
+ sal_uInt16 mnShowField; /// Index to data field AutoShow bases on.
++ sal_uInt16 mnNumFmt;
++ ::std::auto_ptr<rtl::OUString> mpFieldTotalName;
+
+ explicit XclPTFieldExtInfo();
+
+@@ -796,19 +800,20 @@ XclExpStream& operator<<( XclExpStream& rStrm, const XclPTExtInfo& rInfo );
+
+ // Pivot table autoformat settings ==============================================
+
+-/** Pivot table autoformat settings (SX_AUTOFORMAT record). */
+-struct XclPTAutoFormat
++/** Pivot table autoformat settings (SXVIEWEX9 record). */
++struct XclPTViewEx9Info
+ {
+ sal_uInt32 mbReport; /// 2 for report* fmts ?
+ sal_uInt8 mnAutoFormat; /// AutoFormat ID
+ sal_uInt8 mnGridLayout; /// 0 == gridlayout, 0x10 == modern
++ String maGrandTotalName;
+
+- explicit XclPTAutoFormat();
++ explicit XclPTViewEx9Info();
+ void Init( const ScDPObject& rDPObj );
+ };
+
+-XclImpStream& operator>>( XclImpStream& rStrm, XclPTAutoFormat& rInfo );
+-XclExpStream& operator<<( XclExpStream& rStrm, const XclPTAutoFormat& rInfo );
++XclImpStream& operator>>( XclImpStream& rStrm, XclPTViewEx9Info& rInfo );
++XclExpStream& operator<<( XclExpStream& rStrm, const XclPTViewEx9Info& rInfo );
+
+ // ============================================================================
+ #endif
+diff --git sc/source/filter/inc/xlstring.hxx sc/source/filter/inc/xlstring.hxx
+index 626b11b..0a078a4 100644
+--- sc/source/filter/inc/xlstring.hxx
++++ sc/source/filter/inc/xlstring.hxx
+@@ -43,6 +43,7 @@ const XclStrFlags EXC_STR_FORCEUNICODE = 0x0001; /// Always use UCS-2 cha
+ const XclStrFlags EXC_STR_8BITLENGTH = 0x0002; /// 8-bit string length field (default: 16-bit).
+ const XclStrFlags EXC_STR_SMARTFLAGS = 0x0004; /// Omit flags on empty string (default: read/write always). BIFF8 only.
+ const XclStrFlags EXC_STR_SEPARATEFORMATS = 0x0008; /// Import: Keep old formats when reading unformatted string (default: clear formats); Export: Write unformatted string.
++const XclStrFlags EXC_STR_NOHEADER = 0x0010; /// Export: Don't write the length and flag fields.
+
+ // ----------------------------------------------------------------------------
+
+diff --git sc/source/ui/dbgui/pvfundlg.cxx sc/source/ui/dbgui/pvfundlg.cxx
+index 7f68fae..2d3cba6 100644
+--- sc/source/ui/dbgui/pvfundlg.cxx
++++ sc/source/ui/dbgui/pvfundlg.cxx
+@@ -714,7 +714,9 @@ ScDPShowDetailDlg::ScDPShowDetailDlg( Window* pParent, ScDPObject& rDPObj, USHOR
+ maLbDims ( this, ScResId( LB_DIMS ) ),
+ maBtnOk ( this, ScResId( BTN_OK ) ),
+ maBtnCancel ( this, ScResId( BTN_CANCEL ) ),
+- maBtnHelp ( this, ScResId( BTN_HELP ) )
++ maBtnHelp ( this, ScResId( BTN_HELP ) ),
++
++ mrDPObj(rDPObj)
+ {
+ FreeResource();
+
+@@ -728,7 +730,13 @@ ScDPShowDetailDlg::ScDPShowDetailDlg( Window* pParent, ScDPObject& rDPObj, USHOR
+ {
+ const ScDPSaveDimension* pDimension = pSaveData ? pSaveData->GetExistingDimensionByName(aName) : 0;
+ if ( !pDimension || (pDimension->GetOrientation() != nOrient) )
++ {
++ const OUString* pLayoutName = pDimension->GetLayoutName();
++ if (pLayoutName)
++ aName = *pLayoutName;
+ maLbDims.InsertEntry( aName );
++ maNameIndexMap.insert(DimNameIndexMap::value_type(aName, nDim));
++ }
+ }
+ }
+ if( maLbDims.GetEntryCount() )
+@@ -744,7 +752,17 @@ short ScDPShowDetailDlg::Execute()
+
+ String ScDPShowDetailDlg::GetDimensionName() const
+ {
+- return maLbDims.GetSelectEntry();
++ // Look up the internal dimension name which may be different from the
++ // displayed field name.
++ String aSelectedName = maLbDims.GetSelectEntry();
++ DimNameIndexMap::const_iterator itr = maNameIndexMap.find(aSelectedName);
++ if (itr == maNameIndexMap.end())
++ // This should never happen!
++ return aSelectedName;
++
++ long nDim = itr->second;
++ BOOL bIsDataLayout = false;
++ return mrDPObj.GetDimName(nDim, bIsDataLayout);
+ }
+
+ IMPL_LINK( ScDPShowDetailDlg, DblClickHdl, ListBox*, pLBox )
+diff --git sc/source/ui/dbgui/pvlaydlg.cxx sc/source/ui/dbgui/pvlaydlg.cxx
+index 0c0bdd5..fb60d32 100644
+--- sc/source/ui/dbgui/pvlaydlg.cxx
++++ sc/source/ui/dbgui/pvlaydlg.cxx
+@@ -63,6 +63,7 @@
+ #include "sc.hrc" //CHINA001
+ #include "scabstdlg.hxx" //CHINA001
+ using namespace com::sun::star;
++using ::rtl::OUString;
+
+ //----------------------------------------------------------------------------
+
+@@ -1494,6 +1495,8 @@ IMPL_LINK( ScDPLayoutDlg, OkHdl, OKButton *, EMPTYARG )
+ nPageCount, nColCount, nRowCount, nDataCount );
+ if ( bFit )
+ {
++ ScDPSaveData* pOldSaveData = xDlgDPObject->GetSaveData();
++
+ ScRange aOutRange( aAdrDest ); // bToNewTable is passed separately
+
+ ScDPSaveData aSaveData;
+@@ -1525,6 +1528,22 @@ IMPL_LINK( ScDPLayoutDlg, OkHdl, OKButton *, EMPTYARG )
+ pDim->SetSortInfo( &aIt->maSortInfo );
+ pDim->SetLayoutInfo( &aIt->maLayoutInfo );
+ pDim->SetAutoShowInfo( &aIt->maShowInfo );
++ ScDPSaveDimension* pOldDim = NULL;
++ if (pOldSaveData)
++ {
++ // Transfer the existing layout names to new dimension instance.
++ pOldDim = pOldSaveData->GetExistingDimensionByName(aIt->maName);
++ if (pOldDim)
++ {
++ const OUString* pLayoutName = pOldDim->GetLayoutName();
++ if (pLayoutName)
++ pDim->SetLayoutName(*pLayoutName);
++
++ const OUString* pSubtotalName = pOldDim->GetSubtotalName();
++ if (pSubtotalName)
++ pDim->SetSubtotalName(*pSubtotalName);
++ }
++ }
+
+ bool bManualSort = ( aIt->maSortInfo.Mode == sheet::DataPilotFieldSortMode::MANUAL );
+
+@@ -1545,11 +1564,33 @@ IMPL_LINK( ScDPLayoutDlg, OkHdl, OKButton *, EMPTYARG )
+ ScDPSaveMember* pMember = pDim->GetMemberByName( *pItem );
+ pMember->SetIsVisible( bIsVisible );
+ pMember->SetShowDetails( bShowDetails );
++ if (pOldDim)
++ {
++ // Transfer the existing layout name.
++ ScDPSaveMember* pOldMember = pOldDim->GetMemberByName(*pItem);
++ if (pOldMember)
++ {
++ const OUString* pLayoutName = pOldMember->GetLayoutName();
++ if (pLayoutName)
++ pMember->SetLayoutName(*pLayoutName);
++ }
++ }
+ }
+ }
+ }
+ }
+ }
++ ScDPSaveDimension* pDim = aSaveData.GetDataLayoutDimension();
++ if (pDim && pOldSaveData)
++ {
++ ScDPSaveDimension* pOldDim = pOldSaveData->GetDataLayoutDimension();
++ if (pOldDim)
++ {
++ const OUString* pLayoutName = pOldDim->GetLayoutName();
++ if (pLayoutName)
++ pDim->SetLayoutName(*pLayoutName);
++ }
++ }
+
+ USHORT nWhichPivot = SC_MOD()->GetPool().GetWhich( SID_PIVOT_TABLE );
+ ScPivotItem aOutItem( nWhichPivot, &aSaveData, &aOutRange, bToNewTable );
+diff --git sc/source/ui/inc/pvfundlg.hxx sc/source/ui/inc/pvfundlg.hxx
+index f916289..85c8173 100644
+--- sc/source/ui/inc/pvfundlg.hxx
++++ sc/source/ui/inc/pvfundlg.hxx
+@@ -55,6 +55,8 @@
+ #include <sfx2/itemconnect.hxx>
+ #include "pivot.hxx"
+
++#include <hash_map>
++
+ // ============================================================================
+
+ typedef sfx::ListBoxWrapper< sal_Int32 > ScDPListBoxWrapper;
+@@ -218,6 +220,11 @@ public:
+
+ virtual short Execute();
+
++ /**
++ * @return String internal name of the selected field. Note that this may
++ * be different from the name displayed in the dialog if the field
++ * has a layout name.
++ */
+ String GetDimensionName() const;
+
+ private:
+@@ -229,6 +236,10 @@ private:
+ OKButton maBtnOk;
+ CancelButton maBtnCancel;
+ HelpButton maBtnHelp;
++
++ typedef ::std::hash_map<String, long, ScStringHashCode> DimNameIndexMap;
++ DimNameIndexMap maNameIndexMap;
++ ScDPObject& mrDPObj;
+ };
+
+ // ============================================================================
+diff --git sc/source/ui/unoobj/dapiuno.cxx sc/source/ui/unoobj/dapiuno.cxx
+index 5be1cad..c474a20 100644
+--- sc/source/ui/unoobj/dapiuno.cxx
++++ sc/source/ui/unoobj/dapiuno.cxx
+@@ -1647,7 +1647,13 @@ rtl::OUString SAL_CALL ScDataPilotFieldObj::getName() throw(uno::RuntimeExceptio
+ if (pDim->IsDataLayout())
+ return String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM(SC_DATALAYOUT_NAME));
+ else
+- sRet = pDim->GetLayoutName();
++ {
++ const rtl::OUString* pLayoutName = pDim->GetLayoutName();
++ if (pLayoutName)
++ sRet = *pLayoutName;
++ else
++ sRet = pDim->GetName();
++ }
+ }
+ }
+ return sRet;
+@@ -1667,7 +1673,7 @@ void SAL_CALL ScDataPilotFieldObj::setName( const rtl::OUString& aNewName )
+ if (!pDim->IsDataLayout())
+ {
+ String aName(aNewName);
+- pDim->SetLayoutName(&aName);
++ pDim->SetLayoutName(aName);
+ pParent->SetDPObject(pDPObj);
+ }
+ }
+diff --git sc/source/ui/view/dbfunc3.cxx sc/source/ui/view/dbfunc3.cxx
+index cc98745..13a52c6 100644
+--- sc/source/ui/view/dbfunc3.cxx
++++ sc/source/ui/view/dbfunc3.cxx
+@@ -91,6 +91,10 @@ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::beans::XPropertySet;
++using ::com::sun::star::container::XNameAccess;
++using ::com::sun::star::sheet::XDimensionsSupplier;
++using ::rtl::OUString;
++using ::rtl::OUStringBuffer;
+ using ::std::auto_ptr;
+
+ // STATIC DATA -----------------------------------------------------------
+@@ -1377,123 +1381,303 @@ void ScDBFunc::UngroupDataPilot()
+ }
+ }
+
++OUString lcl_replaceMemberNameInSubtotal(const OUString& rSubtotal, const OUString& rMemberName)
++{
++ sal_Int32 n = rSubtotal.getLength();
++ const sal_Unicode* p = rSubtotal.getStr();
++ OUStringBuffer aBuf, aWordBuf;
++ for (sal_Int32 i = 0; i < n; ++i)
++ {
++ sal_Unicode c = p[i];
++ if (c == sal_Unicode(' '))
++ {
++ OUString aWord = aWordBuf.makeStringAndClear();
++ if (aWord.equals(rMemberName))
++ aBuf.append(sal_Unicode('?'));
++ else
++ aBuf.append(aWord);
++ aBuf.append(c);
++ }
++ else
++ aWordBuf.append(c);
++ }
++
++ if (aWordBuf.getLength() > 0)
++ {
++ OUString aWord = aWordBuf.makeStringAndClear();
++ if (aWord.equals(rMemberName))
++ aBuf.append(sal_Unicode('?'));
++ else
++ aBuf.append(aWord);
++ }
++
++ return aBuf.makeStringAndClear();
++}
++
+ void ScDBFunc::DataPilotInput( const ScAddress& rPos, const String& rString )
+ {
++ using namespace ::com::sun::star::sheet;
++
+ String aNewName( rString );
+
+ ScDocument* pDoc = GetViewData()->GetDocument();
+ ScDPObject* pDPObj = pDoc->GetDPAtCursor( rPos.Col(), rPos.Row(), rPos.Tab() );
+- if ( pDPObj )
++ if (!pDPObj)
++ return;
++
++ String aOldText;
++ pDoc->GetString( rPos.Col(), rPos.Row(), rPos.Tab(), aOldText );
++
++ if ( aOldText == rString )
+ {
+- String aOldText;
+- pDoc->GetString( rPos.Col(), rPos.Row(), rPos.Tab(), aOldText );
++ // nothing to do: silently exit
++ return;
++ }
+
+- if ( aOldText == rString )
++ USHORT nErrorId = 0;
++
++ ScDPSaveData aData( *pDPObj->GetSaveData() );
++ BOOL bChange = FALSE;
++
++ USHORT nOrient = DataPilotFieldOrientation_HIDDEN;
++ long nField = pDPObj->GetHeaderDim( rPos, nOrient );
++ if ( nField >= 0 )
++ {
++ // changing a field title
++ if ( aData.GetExistingDimensionData() )
+ {
+- // nothing to do: silently exit
+- return;
+- }
++ // only group dimensions can be renamed
+
+- USHORT nErrorId = 0;
++ ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
++ ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aOldText );
++ if ( pGroupDim )
++ {
++ // valid name: not empty, no existing dimension (group or other)
++ if ( rString.Len() && !pDPObj->IsDimNameInUse(rString) )
++ {
++ pGroupDim->Rename( aNewName );
+
+- ScDPSaveData aData( *pDPObj->GetSaveData() );
+- BOOL bChange = FALSE;
++ // also rename in SaveData to preserve the field settings
++ ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aOldText );
++ pSaveDim->SetName( aNewName );
+
+- USHORT nOrient = sheet::DataPilotFieldOrientation_HIDDEN;
+- long nField = pDPObj->GetHeaderDim( rPos, nOrient );
+- if ( nField >= 0 )
++ bChange = TRUE;
++ }
++ else
++ nErrorId = STR_INVALIDNAME;
++ }
++ }
++ else if (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW)
+ {
+- // changing a field title
++ BOOL bDataLayout = false;
++ String aDimName = pDPObj->GetDimName(nField, bDataLayout);
++ ScDPSaveDimension* pDim = bDataLayout ? aData.GetDataLayoutDimension() : aData.GetDimensionByName(aDimName);
++ if (pDim)
++ {
++ if (rString.Len())
++ {
++ if (rString.EqualsIgnoreCaseAscii(aDimName))
++ {
++ pDim->RemoveLayoutName();
++ bChange = true;
++ }
++ else if (!pDPObj->IsDimNameInUse(rString))
++ {
++ pDim->SetLayoutName(rString);
++ bChange = true;
++ }
++ else
++ nErrorId = STR_INVALIDNAME;
++ }
++ else
++ nErrorId = STR_INVALIDNAME;
++ }
++ }
++ }
++ else if (pDPObj->IsDataDescriptionCell(rPos))
++ {
++ // There is only one data dimension.
++ ScDPSaveDimension* pDim = aData.GetFirstDimension(sheet::DataPilotFieldOrientation_DATA);
++ if (pDim)
++ {
++ if (rString.Len())
++ {
++ if (rString.EqualsIgnoreCaseAscii(pDim->GetName()))
++ {
++ pDim->RemoveLayoutName();
++ bChange = true;
++ }
++ else if (!pDPObj->IsDimNameInUse(rString))
++ {
++ pDim->SetLayoutName(rString);
++ bChange = true;
++ }
++ else
++ nErrorId = STR_INVALIDNAME;
++ }
++ else
++ nErrorId = STR_INVALIDNAME;
++ }
++ }
++ else
++ {
++ // This is not a field header.
++ sheet::DataPilotTableHeaderData aPosData;
++ pDPObj->GetHeaderPositionData(rPos, aPosData);
+
+- if ( aData.GetExistingDimensionData() )
++ if ( (aPosData.Flags & MemberResultFlags::HASMEMBER) && aOldText.Len() )
++ {
++ if ( aData.GetExistingDimensionData() && !(aPosData.Flags & MemberResultFlags::SUBTOTAL))
+ {
+- // only group dimensions can be renamed
++ BOOL bIsDataLayout;
++ String aDimName = pDPObj->GetDimName( aPosData.Dimension, bIsDataLayout );
+
+ ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
+- ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aOldText );
++ ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
+ if ( pGroupDim )
+ {
+- // valid name: not empty, no existing dimension (group or other)
+- if ( aNewName.Len() && !pDPObj->IsDimNameInUse( aNewName ) )
++ // valid name: not empty, no existing group in this dimension
++ //! ignore case?
++ if ( aNewName.Len() && !pGroupDim->GetNamedGroup( aNewName ) )
+ {
+- pGroupDim->Rename( aNewName );
++ ScDPSaveGroupItem* pGroup = pGroupDim->GetNamedGroupAcc( aOldText );
++ if ( pGroup )
++ pGroup->Rename( aNewName ); // rename the existing group
++ else
++ {
++ // create a new group to replace the automatic group
++ ScDPSaveGroupItem aGroup( aNewName );
++ aGroup.AddElement( aOldText );
++ pGroupDim->AddGroupItem( aGroup );
++ }
+
+- // also rename in SaveData to preserve the field settings
+- ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aOldText );
+- pSaveDim->SetName( aNewName );
++ // in both cases also adjust savedata, to preserve member settings (show details)
++ ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aDimName );
++ ScDPSaveMember* pSaveMember = pSaveDim->GetExistingMemberByName( aOldText );
++ if ( pSaveMember )
++ pSaveMember->SetName( aNewName );
+
+ bChange = TRUE;
+ }
+ else
+ nErrorId = STR_INVALIDNAME;
+- }
++ }
+ }
+- }
+- else
+- {
+- // renaming a group (item)?
+- // allow only on the item name itself - not on empty cells, not on subtotals
+-
+- sheet::DataPilotTableHeaderData aPosData;
+- pDPObj->GetHeaderPositionData(rPos, aPosData);
+- if ( ( aPosData.Flags & sheet::MemberResultFlags::HASMEMBER ) &&
+- ! ( aPosData.Flags & sheet::MemberResultFlags::SUBTOTAL ) &&
+- aOldText.Len() )
++ else if ((aPosData.Flags & MemberResultFlags::GRANDTOTAL))
+ {
+- if ( aData.GetExistingDimensionData() )
++ aData.SetGrandTotalName(rString);
++ bChange = true;
++ }
++ else if (aPosData.Dimension >= 0 && aPosData.MemberName.getLength() > 0)
++ {
++ BOOL bDataLayout = false;
++ String aDimName = pDPObj->GetDimName(static_cast<long>(aPosData.Dimension), bDataLayout);
++ if (bDataLayout)
+ {
+- BOOL bIsDataLayout;
+- String aDimName = pDPObj->GetDimName( aPosData.Dimension, bIsDataLayout );
++ // data dimension
++ do
++ {
++ if ((aPosData.Flags & MemberResultFlags::SUBTOTAL))
++ break;
++
++ ScDPSaveDimension* pDim = aData.GetDimensionByName(aPosData.MemberName);
++ if (!pDim)
++ break;
+
+- ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
+- ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
+- if ( pGroupDim )
++ if (!rString.Len())
++ {
++ nErrorId = STR_INVALIDNAME;
++ break;
++ }
++
++ if (aPosData.MemberName.equalsIgnoreAsciiCase(rString))
++ {
++ pDim->RemoveLayoutName();
++ bChange = true;
++ }
++ else if (!pDPObj->IsDimNameInUse(rString))
++ {
++ pDim->SetLayoutName(rString);
++ bChange = true;
++ }
++ else
++ nErrorId = STR_INVALIDNAME;
++ }
++ while (false);
++ }
++ else
++ {
++ // field member
++ do
+ {
+- // valid name: not empty, no existing group in this dimension
+- //! ignore case?
+- if ( aNewName.Len() && !pGroupDim->GetNamedGroup( aNewName ) )
++ ScDPSaveDimension* pDim = aData.GetDimensionByName(aDimName);
++ if (!pDim)
++ break;
++
++ ScDPSaveMember* pMem = pDim->GetExistingMemberByName(aPosData.MemberName);
++ if (!pMem)
++ break;
++
++ if ((aPosData.Flags & MemberResultFlags::SUBTOTAL))
+ {
+- ScDPSaveGroupItem* pGroup = pGroupDim->GetNamedGroupAcc( aOldText );
+- if ( pGroup )
+- pGroup->Rename( aNewName ); // rename the existing group
++ // Change subtotal only when the table has one data dimension.
++ if (aData.GetDataDimensionCount() > 1)
++ break;
++
++ const OUString* pLayoutName = pMem->GetLayoutName();
++ String aMemberName;
++ if (pLayoutName)
++ aMemberName = *pLayoutName;
+ else
+- {
+- // create a new group to replace the automatic group
+- ScDPSaveGroupItem aGroup( aNewName );
+- aGroup.AddElement( aOldText );
+- pGroupDim->AddGroupItem( aGroup );
+- }
++ aMemberName = aPosData.MemberName;
+
+- // in both cases also adjust savedata, to preserve member settings (show details)
+- ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aDimName );
+- ScDPSaveMember* pSaveMember = pSaveDim->GetExistingMemberByName( aOldText );
+- if ( pSaveMember )
+- pSaveMember->SetName( aNewName );
+-
+- bChange = TRUE;
++ String aNew = lcl_replaceMemberNameInSubtotal(rString, aMemberName);
++ pDim->SetSubtotalName(aNew);
++ bChange = true;
+ }
+ else
+- nErrorId = STR_INVALIDNAME;
++ {
++ // Check to make sure the member name isn't
++ // already used.
++ if (rString.Len())
++ {
++ if (rString.EqualsIgnoreCaseAscii(pMem->GetName()))
++ {
++ pMem->RemoveLayoutName();
++ bChange = true;
++ }
++ else if (!pDim->IsMemberNameInUse(rString))
++ {
++ pMem->SetLayoutName(rString);
++ bChange = true;
++ }
++ else
++ nErrorId = STR_INVALIDNAME;
++ }
++ else
++ nErrorId = STR_INVALIDNAME;
++ }
+ }
++ while (false);
+ }
+ }
+ }
++ }
+
+- if ( bChange )
+- {
+- // apply changes
+- ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
+- ScDPObject* pNewObj = new ScDPObject( *pDPObj );
+- pNewObj->SetSaveData( aData );
+- aFunc.DataPilotUpdate( pDPObj, pNewObj, TRUE, FALSE );
+- delete pNewObj;
+- }
+- else
+- {
+- if ( !nErrorId )
+- nErrorId = STR_ERR_DATAPILOT_INPUT;
+- ErrorMessage( nErrorId );
+- }
++ if ( bChange )
++ {
++ // apply changes
++ ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
++ ScDPObject* pNewObj = new ScDPObject( *pDPObj );
++ pNewObj->SetSaveData( aData );
++ aFunc.DataPilotUpdate( pDPObj, pNewObj, TRUE, FALSE );
++ delete pNewObj;
++ }
++ else
++ {
++ if ( !nErrorId )
++ nErrorId = STR_ERR_DATAPILOT_INPUT;
++ ErrorMessage( nErrorId );
+ }
+ }
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]