blob: a51310658f2251fd64e7f1f802286fadb4c906a1 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
// INCLUDE ---------------------------------------------------------------
#include "dbfunc.hxx"
#include "scitems.hxx"
#include <sfx2/bindings.hxx>
#include <vcl/svapp.hxx>
#include <vcl/msgbox.hxx>
#include <vcl/sound.hxx>
#include <vcl/waitobj.hxx>
#include <svl/zforlist.hxx>
#include <sfx2/app.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
#include <com/sun/star/sheet/GeneralFunction.hpp>
#include <com/sun/star/sheet/MemberResultFlags.hpp>
#include <com/sun/star/sheet/XDimensionsSupplier.hpp>
#include <com/sun/star/sheet/XDrillDownDataSupplier.hpp>
#include "global.hxx"
#include "globstr.hrc"
#include "sc.hrc"
#include "undotab.hxx"
#include "undodat.hxx"
#include "dbcolect.hxx"
#include "rangenam.hxx"
#include "rangeutl.hxx"
#include "docsh.hxx"
#include "olinetab.hxx"
#include "consoli.hxx"
#include "olinefun.hxx"
#include "dpobject.hxx"
#include "dpsave.hxx"
#include "dpdimsave.hxx"
#include "dbdocfun.hxx"
#include "dpoutput.hxx"
#include "dptabsrc.hxx"
#include "editable.hxx"
#include "docpool.hxx"
#include "patattr.hxx"
#include "unonames.hxx"
#include "cell.hxx"
#include "userlist.hxx"
#include <hash_set>
#include <hash_map>
#include <memory>
#include <list>
#include <vector>
using namespace com::sun::star;
using ::com::sun::star::uno::Any;
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::OUStringHash;
using ::rtl::OUStringBuffer;
using ::std::auto_ptr;
using ::std::list;
using ::std::vector;
using ::std::hash_map;
using ::std::hash_set;
// STATIC DATA -----------------------------------------------------------
//==================================================================
//
// Outliner
//
// Outline-Gruppierung erzeugen
void ScDBFunc::MakeOutline( sal_Bool bColumns, sal_Bool bRecord )
{
ScRange aRange;
if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
{
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScOutlineDocFunc aFunc(*pDocSh);
aFunc.MakeOutline( aRange, bColumns, bRecord, sal_False );
}
else
ErrorMessage(STR_NOMULTISELECT);
}
// Outline-Gruppierung loeschen
void ScDBFunc::RemoveOutline( sal_Bool bColumns, sal_Bool bRecord )
{
ScRange aRange;
if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
{
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScOutlineDocFunc aFunc(*pDocSh);
aFunc.RemoveOutline( aRange, bColumns, bRecord, sal_False );
}
else
ErrorMessage(STR_NOMULTISELECT);
}
// Menue-Status: Outlines loeschen
void ScDBFunc::TestRemoveOutline( sal_Bool& rCol, sal_Bool& rRow )
{
sal_Bool bColFound = sal_False;
sal_Bool bRowFound = sal_False;
SCCOL nStartCol, nEndCol;
SCROW nStartRow, nEndRow;
SCTAB nStartTab, nEndTab;
if (GetViewData()->GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
{
SCTAB nTab = nStartTab;
ScDocument* pDoc = GetViewData()->GetDocument();
ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
if (pTable)
{
ScOutlineArray* pArray;
ScOutlineEntry* pEntry;
SCCOLROW nStart;
SCCOLROW nEnd;
sal_Bool bColMarked = ( nStartRow == 0 && nEndRow == MAXROW );
sal_Bool bRowMarked = ( nStartCol == 0 && nEndCol == MAXCOL );
// Spalten
if ( !bRowMarked || bColMarked ) // nicht wenn ganze Zeilen markiert
{
pArray = pTable->GetColArray();
ScSubOutlineIterator aColIter( pArray );
while ((pEntry=aColIter.GetNext()) != NULL && !bColFound)
{
nStart = pEntry->GetStart();
nEnd = pEntry->GetEnd();
if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
bColFound = sal_True;
}
}
// Zeilen
if ( !bColMarked || bRowMarked ) // nicht wenn ganze Spalten markiert
{
pArray = pTable->GetRowArray();
ScSubOutlineIterator aRowIter( pArray );
while ((pEntry=aRowIter.GetNext()) != NULL && !bRowFound)
{
nStart = pEntry->GetStart();
nEnd = pEntry->GetEnd();
if ( nStartRow<=nEnd && nEndRow>=nStart )
bRowFound = sal_True;
}
}
}
}
rCol = bColFound;
rRow = bRowFound;
}
void ScDBFunc::RemoveAllOutlines( sal_Bool bRecord )
{
SCTAB nTab = GetViewData()->GetTabNo();
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScOutlineDocFunc aFunc(*pDocSh);
HideCursor();
sal_Bool bOk = aFunc.RemoveAllOutlines( nTab, bRecord, sal_False );
ShowCursor();
if (bOk)
UpdateScrollBars();
}
// Auto-Outlines
void ScDBFunc::AutoOutline( sal_Bool bRecord )
{
SCTAB nTab = GetViewData()->GetTabNo();
ScRange aRange( 0,0,nTab, MAXCOL,MAXROW,nTab ); // ganze Tabelle, wenn nichts markiert
ScMarkData& rMark = GetViewData()->GetMarkData();
if ( rMark.IsMarked() || rMark.IsMultiMarked() )
{
rMark.MarkToMulti();
rMark.GetMultiMarkArea( aRange );
}
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScOutlineDocFunc aFunc(*pDocSh);
aFunc.AutoOutline( aRange, bRecord, sal_False );
}
// Outline-Ebene auswaehlen
void ScDBFunc::SelectLevel( sal_Bool bColumns, sal_uInt16 nLevel, sal_Bool bRecord, sal_Bool bPaint )
{
SCTAB nTab = GetViewData()->GetTabNo();
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScOutlineDocFunc aFunc(*pDocSh);
HideCursor();
sal_Bool bOk = aFunc.SelectLevel( nTab, bColumns, nLevel, bRecord, bPaint, sal_False );
ShowCursor();
if (bOk)
UpdateScrollBars();
}
// einzelne Outline-Gruppe einblenden
void ScDBFunc::ShowOutline( sal_Bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, sal_Bool bRecord, sal_Bool bPaint )
{
SCTAB nTab = GetViewData()->GetTabNo();
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScOutlineDocFunc aFunc(*pDocSh);
HideCursor();
sal_Bool bOk = aFunc.ShowOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint, sal_False );
ShowCursor();
if ( bOk && bPaint )
UpdateScrollBars();
}
// einzelne Outline-Gruppe ausblenden
void ScDBFunc::HideOutline( sal_Bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, sal_Bool bRecord, sal_Bool bPaint )
{
SCTAB nTab = GetViewData()->GetTabNo();
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScOutlineDocFunc aFunc(*pDocSh);
HideCursor();
sal_Bool bOk = aFunc.HideOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint, sal_False );
ShowCursor();
if ( bOk && bPaint )
UpdateScrollBars();
}
// Menue-Status: markierten Bereich ein-/ausblenden
sal_Bool ScDBFunc::OutlinePossible(sal_Bool bHide)
{
sal_Bool bEnable = sal_False;
SCCOL nStartCol;
SCROW nStartRow;
SCTAB nStartTab;
SCCOL nEndCol;
SCROW nEndRow;
SCTAB nEndTab;
if (GetViewData()->GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
{
ScDocument* pDoc = GetViewData()->GetDocument();
SCTAB nTab = GetViewData()->GetTabNo();
ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
if (pTable)
{
ScOutlineArray* pArray;
ScOutlineEntry* pEntry;
SCCOLROW nStart;
SCCOLROW nEnd;
// Spalten
pArray = pTable->GetColArray();
ScSubOutlineIterator aColIter( pArray );
while ((pEntry=aColIter.GetNext()) != NULL && !bEnable)
{
nStart = pEntry->GetStart();
nEnd = pEntry->GetEnd();
if ( bHide )
{
if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
if (!pEntry->IsHidden())
bEnable = sal_True;
}
else
{
if ( nStart>=nStartCol && nEnd<=nEndCol )
if (pEntry->IsHidden())
bEnable = sal_True;
}
}
// Zeilen
pArray = pTable->GetRowArray();
ScSubOutlineIterator aRowIter( pArray );
while ((pEntry=aRowIter.GetNext()) != NULL)
{
nStart = pEntry->GetStart();
nEnd = pEntry->GetEnd();
if ( bHide )
{
if ( nStartRow<=nEnd && nEndRow>=nStart )
if (!pEntry->IsHidden())
bEnable = sal_True;
}
else
{
if ( nStart>=nStartRow && nEnd<=nEndRow )
if (pEntry->IsHidden())
bEnable = sal_True;
}
}
}
}
return bEnable;
}
// markierten Bereich einblenden
void ScDBFunc::ShowMarkedOutlines( sal_Bool bRecord )
{
ScRange aRange;
if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
{
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScOutlineDocFunc aFunc(*pDocSh);
HideCursor();
sal_Bool bDone = aFunc.ShowMarkedOutlines( aRange, bRecord, sal_False );
ShowCursor();
if (bDone)
UpdateScrollBars();
}
else
ErrorMessage(STR_NOMULTISELECT);
}
// markierten Bereich ausblenden
void ScDBFunc::HideMarkedOutlines( sal_Bool bRecord )
{
ScRange aRange;
if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
{
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScOutlineDocFunc aFunc(*pDocSh);
HideCursor();
sal_Bool bDone = aFunc.HideMarkedOutlines( aRange, bRecord, sal_False );
ShowCursor();
if (bDone)
UpdateScrollBars();
}
else
ErrorMessage(STR_NOMULTISELECT);
}
// --------------------------------------------------------------------------
//
// Teilergebnisse
//
void ScDBFunc::DoSubTotals( const ScSubTotalParam& rParam, sal_Bool bRecord,
const ScSortParam* pForceNewSort )
{
sal_Bool bDo = !rParam.bRemoveOnly; // sal_False = nur loeschen
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScDocument* pDoc = pDocSh->GetDocument();
ScMarkData& rMark = GetViewData()->GetMarkData();
SCTAB nTab = GetViewData()->GetTabNo();
if (bRecord && !pDoc->IsUndoEnabled())
bRecord = sal_False;
ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
rParam.nCol2, rParam.nRow2 );
if (!pDBData)
{
DBG_ERROR( "SubTotals: keine DBData" );
return;
}
ScEditableTester aTester( pDoc, nTab, 0,rParam.nRow1+1, MAXCOL,MAXROW );
if (!aTester.IsEditable())
{
ErrorMessage(aTester.GetMessageId());
return;
}
if (pDoc->HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
rParam.nCol2, rParam.nRow2, nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ))
{
ErrorMessage(STR_MSSG_INSERTCELLS_0); // nicht in zusammengefasste einfuegen
return;
}
WaitObject aWait( GetViewData()->GetDialogParent() );
sal_Bool bOk = sal_True;
sal_Bool bDelete = sal_False;
if (rParam.bReplace)
if (pDoc->TestRemoveSubTotals( nTab, rParam ))
{
bDelete = sal_True;
bOk = ( MessBox( GetViewData()->GetDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
// "StarCalc" "Daten loeschen?"
ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ),
ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_1 ) ).Execute()
== RET_YES );
}
if (bOk)
{
ScDocShellModificator aModificator( *pDocSh );
ScSubTotalParam aNewParam( rParam ); // Bereichsende wird veraendert
ScDocument* pUndoDoc = NULL;
ScOutlineTable* pUndoTab = NULL;
ScRangeName* pUndoRange = NULL;
ScDBCollection* pUndoDB = NULL;
SCTAB nTabCount = 0; // fuer Referenz-Undo
if (bRecord) // alte Daten sichern
{
sal_Bool bOldFilter = bDo && rParam.bDoSort;
nTabCount = pDoc->GetTableCount();
pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
if (pTable)
{
pUndoTab = new ScOutlineTable( *pTable );
SCCOLROW nOutStartCol; // Zeilen/Spaltenstatus
SCCOLROW nOutStartRow;
SCCOLROW nOutEndCol;
SCCOLROW nOutEndRow;
pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, sal_False, pUndoDoc );
pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, sal_False, pUndoDoc );
}
else
pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, bOldFilter );
// Datenbereich sichern - incl. Filter-Ergebnis
pDoc->CopyToDocument( 0,rParam.nRow1+1,nTab, MAXCOL,rParam.nRow2,nTab,
IDF_ALL, sal_False, pUndoDoc );
// alle Formeln wegen Referenzen
pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1,
IDF_FORMULA, sal_False, pUndoDoc );
// DB- und andere Bereiche
ScRangeName* pDocRange = pDoc->GetRangeName();
if (pDocRange->GetCount())
pUndoRange = new ScRangeName( *pDocRange );
ScDBCollection* pDocDB = pDoc->GetDBCollection();
if (pDocDB->GetCount())
pUndoDB = new ScDBCollection( *pDocDB );
}
// pDoc->SetOutlineTable( nTab, NULL );
ScOutlineTable* pOut = pDoc->GetOutlineTable( nTab );
if (pOut)
pOut->GetRowArray()->RemoveAll(); // nur Zeilen-Outlines loeschen
if (rParam.bReplace)
pDoc->RemoveSubTotals( nTab, aNewParam );
sal_Bool bSuccess = sal_True;
if (bDo)
{
// Sortieren
if ( rParam.bDoSort || pForceNewSort )
{
pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
// Teilergebnis-Felder vor die Sortierung setzen
// (doppelte werden weggelassen, kann darum auch wieder aufgerufen werden)
ScSortParam aOldSort;
pDBData->GetSortParam( aOldSort );
ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
Sort( aSortParam, sal_False, sal_False );
}
bSuccess = pDoc->DoSubTotals( nTab, aNewParam );
}
ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
aNewParam.nCol2, aNewParam.nRow2, nTab );
pDoc->SetDirty( aDirtyRange );
if (bRecord)
{
// ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
pDocSh->GetUndoManager()->AddUndoAction(
new ScUndoSubTotals( pDocSh, nTab,
rParam, aNewParam.nRow2,
pUndoDoc, pUndoTab, // pUndoDBData,
pUndoRange, pUndoDB ) );
}
if (!bSuccess)
{
// "Kann keine Zeilen einfuegen"
ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
}
// merken
pDBData->SetSubTotalParam( aNewParam );
pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
pDoc->CompileDBFormula();
DoneBlockMode();
InitOwnBlockMode();
rMark.SetMarkArea( ScRange( aNewParam.nCol1,aNewParam.nRow1,nTab,
aNewParam.nCol2,aNewParam.nRow2,nTab ) );
MarkDataChanged();
pDocSh->PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab,
PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE );
aModificator.SetDocumentModified();
SelectionChanged();
}
}
//
// Consolidate
//
void ScDBFunc::Consolidate( const ScConsolidateParam& rParam, sal_Bool bRecord )
{
ScDocShell* pDocShell = GetViewData()->GetDocShell();
pDocShell->DoConsolidate( rParam, bRecord );
SetTabNo( rParam.nTab, sal_True );
}
//
// Pivot
//
String lcl_MakePivotTabName( const String& rPrefix, SCTAB nNumber )
{
String aName = rPrefix;
aName += String::CreateFromInt32( nNumber );
return aName;
}
bool ScDBFunc::MakePivotTable( const ScDPSaveData& rData, const ScRange& rDest, sal_Bool bNewTable,
const ScDPObject& rSource, sal_Bool bApi )
{
// #70096# error message if no fields are set
// this must be removed when drag&drop of fields from a toolbox is available
if ( rData.IsEmpty() && !bApi )
{
ErrorMessage(STR_PIVOT_NODATA);
return false;
}
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScDocument* pDoc = GetViewData()->GetDocument();
sal_Bool bUndo(pDoc->IsUndoEnabled());
ScRange aDestRange = rDest;
if ( bNewTable )
{
SCTAB nSrcTab = GetViewData()->GetTabNo();
String aName( ScGlobal::GetRscString(STR_PIVOT_TABLE) );
String aStr;
pDoc->GetName( nSrcTab, aStr );
aName += '_';
aName += aStr;
aName += '_';
SCTAB nNewTab = nSrcTab+1;
const bool bDrawUndo = ( bUndo && !pDoc->IsDrawRecording() );
if( bDrawUndo )
pDoc->BeginDrawUndo();
SCTAB i=1;
while ( !pDoc->InsertTab( nNewTab, lcl_MakePivotTabName( aName, i ) ) && i <= MAXTAB )
i++;
sal_Bool bAppend = ( nNewTab+1 == pDoc->GetTableCount() );
if (bUndo)
{
pDocSh->GetUndoManager()->AddUndoAction(
new ScUndoInsertTab( pDocSh, nNewTab, bAppend, lcl_MakePivotTabName( aName, i ) ));
}
GetViewData()->InsertTab( nNewTab );
SetTabNo( nNewTab, sal_True );
aDestRange = ScRange( 0, 0, nNewTab );
if( bDrawUndo )
pDoc->EndDrawUndo();
}
ScDPObject* pDPObj = pDoc->GetDPAtCursor(
aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aStart.Tab() );
ScDPObject aObj( rSource );
aObj.SetOutRange( aDestRange );
if ( pDPObj && !rData.GetExistingDimensionData() )
{
// copy dimension data from old object - lost in the dialog
//! change the dialog to keep the dimension data
ScDPSaveData aNewData( rData );
const ScDPSaveData* pOldData = pDPObj->GetSaveData();
if ( pOldData )
{
const ScDPDimensionSaveData* pDimSave = pOldData->GetExistingDimensionData();
aNewData.SetDimensionData( pDimSave );
}
aObj.SetSaveData( aNewData );
}
else
aObj.SetSaveData( rData );
sal_Bool bAllowMove = ( pDPObj != NULL ); // allow re-positioning when editing existing table
ScDBDocFunc aFunc( *pDocSh );
bool bSuccess = aFunc.DataPilotUpdate( pDPObj, &aObj, sal_True, sal_False, bAllowMove );
CursorPosChanged(); // shells may be switched
if ( bNewTable )
{
pDocSh->PostPaintExtras();
SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_TABLES_CHANGED ) );
}
return bSuccess;
}
void ScDBFunc::DeletePivotTable()
{
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScDocument* pDoc = pDocSh->GetDocument();
ScDPObject* pDPObj = pDoc->GetDPAtCursor( GetViewData()->GetCurX(),
GetViewData()->GetCurY(),
GetViewData()->GetTabNo() );
if ( pDPObj )
{
ScDBDocFunc aFunc( *pDocSh );
aFunc.DataPilotUpdate( pDPObj, NULL, sal_True, sal_False );
CursorPosChanged(); // shells may be switched
}
else
ErrorMessage(STR_PIVOT_NOTFOUND);
}
sal_uLong RefreshDPObject( ScDPObject *pDPObj, ScDocument *pDoc, ScDocShell *pDocSh, sal_Bool bRecord, sal_Bool bApi )
{
if( !pDPObj )
return STR_PIVOT_NOTFOUND;
if ( pDocSh && !pDoc )
pDoc = pDocSh->GetDocument();
if( !pDoc )
return static_cast<sal_uLong>(-1);
if( !pDocSh && ( pDocSh = PTR_CAST( ScDocShell, pDoc->GetDocumentShell() ) ) == NULL )
return static_cast<sal_uLong>(-1);
if( sal_uLong nErrId = pDPObj->RefreshCache() )
return nErrId;
else if ( nErrId == 0 )
{
//Refresh all dpobjects
ScDPCollection* pDPCollection = pDoc->GetDPCollection();
sal_uInt16 nCount = pDPCollection->GetCount();
for (sal_uInt16 i=0; i<nCount; i++)
{
if ( (*pDPCollection)[i]->GetCacheId() == pDPObj->GetCacheId() )
{
ScDBDocFunc aFunc( * pDocSh );
if ( !aFunc.DataPilotUpdate( (*pDPCollection)[i], (*pDPCollection)[i], bRecord, bApi ) )
break;
}
}
return nErrId;
}
return 0U;
}
sal_uLong ScDBFunc::RecalcPivotTable()
{
ScDocShell* pDocSh = GetViewData()->GetDocShell();
ScDocument* pDoc = GetViewData()->GetDocument();
// old pivot not used any more
ScDPObject* pDPObj = pDoc->GetDPAtCursor( GetViewData()->GetCurX(),
GetViewData()->GetCurY(),
GetViewData()->GetTabNo() );
if ( pDPObj )
{
// Wang Xu Ming -- 2009-6-17
// DataPilot Migration
//ScDBDocFunc aFunc( *pDocSh );
//aFunc.DataPilotUpdate( pDPObj, pDPObj, sal_True, sal_False );
//CursorPosChanged(); // shells may be switched
sal_uLong nErrId = RefreshDPObject( pDPObj, pDoc, pDocSh, sal_True, sal_False );//pDPObj->RefreshCache();
if ( nErrId == 0 )
{
// There is no undo for the refresh of the cache table, but the undo history for cell changes
// remains valid and should be preserved, so the history isn't cleared here.
//GetViewData()->GetDocShell()->GetUndoManager()->Clear();
}
else if (nErrId <= USHRT_MAX)
ErrorMessage(static_cast<sal_uInt16>(nErrId));
return nErrId;
// End Comments
}
else
ErrorMessage(STR_PIVOT_NOTFOUND);
return STR_PIVOT_NOTFOUND;
}
void ScDBFunc::GetSelectedMemberList( ScStrCollection& rEntries, long& rDimension )
{
ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
if ( !pDPObj )
return;
long nStartDimension = -1;
long nStartHierarchy = -1;
long nStartLevel = -1;
ScRangeListRef xRanges;
GetViewData()->GetMultiArea( xRanges ); // incl. cursor if nothing is selected
sal_uLong nRangeCount = xRanges->Count();
sal_Bool bContinue = sal_True;
for (sal_uLong nRangePos=0; nRangePos<nRangeCount && bContinue; nRangePos++)
{
ScRange aRange = *xRanges->GetObject(nRangePos);
SCCOL nStartCol = aRange.aStart.Col();
SCROW nStartRow = aRange.aStart.Row();
SCCOL nEndCol = aRange.aEnd.Col();
SCROW nEndRow = aRange.aEnd.Row();
SCTAB nTab = aRange.aStart.Tab();
for (SCROW nRow=nStartRow; nRow<=nEndRow && bContinue; nRow++)
for (SCCOL nCol=nStartCol; nCol<=nEndCol && bContinue; nCol++)
{
sheet::DataPilotTableHeaderData aData;
pDPObj->GetHeaderPositionData(ScAddress(nCol, nRow, nTab), aData);
if ( aData.Dimension < 0 )
bContinue = sal_False; // not part of any dimension
else
{
if ( nStartDimension < 0 ) // first member?
{
nStartDimension = aData.Dimension;
nStartHierarchy = aData.Hierarchy;
nStartLevel = aData.Level;
}
if ( aData.Dimension != nStartDimension ||
aData.Hierarchy != nStartHierarchy ||
aData.Level != nStartLevel )
{
bContinue = sal_False; // cannot mix dimensions
}
}
if ( bContinue )
{
// accept any part of a member description, also subtotals,
// but don't stop if empty parts are contained
if ( aData.Flags & sheet::MemberResultFlags::HASMEMBER )
{
StrData* pNew = new StrData( aData.MemberName );
if ( !rEntries.Insert( pNew ) )
delete pNew;
}
}
}
}
rDimension = nStartDimension; // dimension from which the found members came
if (!bContinue)
rEntries.FreeAll(); // remove all if not valid
}
sal_Bool ScDBFunc::HasSelectionForDateGroup( ScDPNumGroupInfo& rOldInfo, sal_Int32& rParts )
{
// determine if the date group dialog has to be shown for the current selection
sal_Bool bFound = sal_False;
SCCOL nCurX = GetViewData()->GetCurX();
SCROW nCurY = GetViewData()->GetCurY();
SCTAB nTab = GetViewData()->GetTabNo();
ScDocument* pDoc = GetViewData()->GetDocument();
ScDPObject* pDPObj = pDoc->GetDPAtCursor( nCurX, nCurY, nTab );
if ( pDPObj )
{
ScStrCollection aEntries;
long nSelectDimension = -1;
GetSelectedMemberList( aEntries, nSelectDimension );
if ( aEntries.GetCount() > 0 )
{
sal_Bool bIsDataLayout;
String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
String aBaseDimName( aDimName );
sal_Bool bInGroupDim = sal_False;
sal_Bool bFoundParts = sal_False;
ScDPDimensionSaveData* pDimData =
const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
if ( pDimData )
{
const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
const ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDim( aDimName );
if ( pNumGroupDim )
{
// existing num group dimension
if ( pNumGroupDim->GetDatePart() != 0 )
{
// dimension has date info -> edit settings of this dimension
// (parts are collected below)
rOldInfo = pNumGroupDim->GetDateInfo();
bFound = sal_True;
}
else if ( pNumGroupDim->GetInfo().DateValues )
{
// Numerical grouping with DateValues flag is used for grouping
// of days with a "Number of days" value.
rOldInfo = pNumGroupDim->GetInfo();
rParts = com::sun::star::sheet::DataPilotFieldGroupBy::DAYS; // not found in CollectDateParts
bFoundParts = sal_True;
bFound = sal_True;
}
bInGroupDim = sal_True;
}
else if ( pGroupDim )
{
// existing additional group dimension
if ( pGroupDim->GetDatePart() != 0 )
{
// dimension has date info -> edit settings of this dimension
// (parts are collected below)
rOldInfo = pGroupDim->GetDateInfo();
aBaseDimName = pGroupDim->GetSourceDimName();
bFound = sal_True;
}
bInGroupDim = sal_True;
}
}
if ( bFound && !bFoundParts )
{
// collect date parts from all group dimensions
rParts = pDimData->CollectDateParts( aBaseDimName );
}
if ( !bFound && !bInGroupDim )
{
// create new date group dimensions if the selection is a single cell
// in a normal dimension with date content
ScRange aSelRange;
if ( (GetViewData()->GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
aSelRange.aStart == aSelRange.aEnd )
{
SCCOL nSelCol = aSelRange.aStart.Col();
SCROW nSelRow = aSelRange.aStart.Row();
SCTAB nSelTab = aSelRange.aStart.Tab();
if ( pDoc->HasValueData( nSelCol, nSelRow, nSelTab ) )
{
sal_uLong nIndex = static_cast<const SfxUInt32Item*>(pDoc->GetAttr(
nSelCol, nSelRow, nSelTab, ATTR_VALUE_FORMAT))->GetValue();
short nType = pDoc->GetFormatTable()->GetType(nIndex);
if ( nType == NUMBERFORMAT_DATE || nType == NUMBERFORMAT_TIME || nType == NUMBERFORMAT_DATETIME )
{
bFound = sal_True;
// use currently selected value for automatic limits
if( rOldInfo.AutoStart )
rOldInfo.Start = pDoc->GetValue( aSelRange.aStart );
if( rOldInfo.AutoEnd )
rOldInfo.End = pDoc->GetValue( aSelRange.aStart );
}
}
}
}
}
}
return bFound;
}
sal_Bool ScDBFunc::HasSelectionForNumGroup( ScDPNumGroupInfo& rOldInfo )
{
// determine if the numeric group dialog has to be shown for the current selection
sal_Bool bFound = sal_False;
SCCOL nCurX = GetViewData()->GetCurX();
SCROW nCurY = GetViewData()->GetCurY();
SCTAB nTab = GetViewData()->GetTabNo();
ScDocument* pDoc = GetViewData()->GetDocument();
ScDPObject* pDPObj = pDoc->GetDPAtCursor( nCurX, nCurY, nTab );
if ( pDPObj )
{
ScStrCollection aEntries;
long nSelectDimension = -1;
GetSelectedMemberList( aEntries, nSelectDimension );
if ( aEntries.GetCount() > 0 )
{
sal_Bool bIsDataLayout;
String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
sal_Bool bInGroupDim = sal_False;
ScDPDimensionSaveData* pDimData =
const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
if ( pDimData )
{
const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
if ( pNumGroupDim )
{
// existing num group dimension
// -> edit settings of this dimension
rOldInfo = pNumGroupDim->GetInfo();
bFound = sal_True;
}
else if ( pDimData->GetNamedGroupDim( aDimName ) )
bInGroupDim = sal_True; // in a group dimension
}
if ( !bFound && !bInGroupDim )
{
// create a new num group dimension if the selection is a single cell
// in a normal dimension with numeric content
ScRange aSelRange;
if ( (GetViewData()->GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
aSelRange.aStart == aSelRange.aEnd )
{
if ( pDoc->HasValueData( aSelRange.aStart.Col(), aSelRange.aStart.Row(),
aSelRange.aStart.Tab() ) )
{
bFound = sal_True;
// use currently selected value for automatic limits
if( rOldInfo.AutoStart )
rOldInfo.Start = pDoc->GetValue( aSelRange.aStart );
if( rOldInfo.AutoEnd )
rOldInfo.End = pDoc->GetValue( aSelRange.aStart );
}
}
}
}
}
return bFound;
}
void ScDBFunc::DateGroupDataPilot( const ScDPNumGroupInfo& rInfo, sal_Int32 nParts )
{
ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
if ( pDPObj )
{
ScStrCollection aEntries;
long nSelectDimension = -1;
GetSelectedMemberList( aEntries, nSelectDimension );
if ( aEntries.GetCount() > 0 )
{
sal_Bool bIsDataLayout;
String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
ScDPSaveData aData( *pDPObj->GetSaveData() );
ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
// find original base
String aBaseDimName = aDimName;
if( const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ) )
aBaseDimName = pBaseGroupDim->GetSourceDimName();
// remove all existing parts (the grouping is built completely new)
/* Remove numeric group dimension (exists once at most). No need
to delete anything in save data (grouping was done inplace in
an existing base dimension). */
pDimData->RemoveNumGroupDimension( aBaseDimName );
/* Remove named group dimension(s). Collect deleted dimension
names which may be reused while recreating the groups.
Dimensions have to be removed from dimension save data and from
save data too. */
std::vector< String > aDeletedNames;
const ScDPSaveGroupDimension* pExistingGroup = pDimData->GetGroupDimForBase( aBaseDimName );
while ( pExistingGroup )
{
String aGroupDimName = pExistingGroup->GetGroupDimName();
pDimData->RemoveGroupDimension( aGroupDimName ); // pExistingGroup is deleted
// also remove SaveData settings for the dimension that no longer exists
aData.RemoveDimensionByName( aGroupDimName );
/* The name can be used for the new group dimensions, although
it is still in use with the DataPilotSource. */
aDeletedNames.push_back( aGroupDimName );
// see if there are more group dimensions
pExistingGroup = pDimData->GetGroupDimForBase( aBaseDimName );
if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName )
{
// still get the same group dimension?
DBG_ERROR("couldn't remove group dimension");
pExistingGroup = NULL; // avoid endless loop
}
}
if ( nParts )
{
// create date group dimensions
ScDPNumGroupInfo aEmpty;
bool bFirst = true;
sal_Int32 nMask = 1;
for (sal_uInt16 nBit=0; nBit<32; nBit++)
{
if ( nParts & nMask )
{
if ( bFirst )
{
// innermost part: create NumGroupDimension (replacing original values)
// Dimension name is left unchanged
if ( (nParts == sheet::DataPilotFieldGroupBy::DAYS) && (rInfo.Step >= 1.0) )
{
// only days, and a step value specified: use numerical grouping
// with DateValues flag, not date grouping
ScDPNumGroupInfo aNumInfo( rInfo );
aNumInfo.DateValues = sal_True;
ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, aNumInfo );
pDimData->AddNumGroupDimension( aNumGroupDim );
}
else
{
ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, rInfo, nMask );
pDimData->AddNumGroupDimension( aNumGroupDim );
}
bFirst = false;
}
else
{
// additional parts: create GroupDimension (shown as additional dimensions)
String aGroupDimName = pDimData->CreateDateGroupDimName( nMask, *pDPObj, true, &aDeletedNames );
ScDPSaveGroupDimension aGroupDim( aBaseDimName, aGroupDimName );
aGroupDim.SetDateInfo( rInfo, nMask );
pDimData->AddGroupDimension( aGroupDim );
// set orientation
ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
{
ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aBaseDimName );
pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
long nPosition = 0; //! before (immediate) base
aData.SetPosition( pSaveDimension, nPosition );
}
}
}
nMask *= 2;
}
}
// apply changes
ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
ScDPObject* pNewObj = new ScDPObject( *pDPObj );
pNewObj->SetSaveData( aData );
aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
delete pNewObj;
// unmark cell selection
Unmark();
}
}
}
void ScDBFunc::NumGroupDataPilot( const ScDPNumGroupInfo& rInfo )
{
ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
if ( pDPObj )
{
ScStrCollection aEntries;
long nSelectDimension = -1;
GetSelectedMemberList( aEntries, nSelectDimension );
if ( aEntries.GetCount() > 0 )
{
sal_Bool bIsDataLayout;
String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
ScDPSaveData aData( *pDPObj->GetSaveData() );
ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
ScDPSaveNumGroupDimension* pExisting = pDimData->GetNumGroupDimAcc( aDimName );
if ( pExisting )
{
// modify existing group dimension
pExisting->SetGroupInfo( rInfo );
}
else
{
// create new group dimension
ScDPSaveNumGroupDimension aNumGroupDim( aDimName, rInfo );
pDimData->AddNumGroupDimension( aNumGroupDim );
}
// apply changes
ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
ScDPObject* pNewObj = new ScDPObject( *pDPObj );
pNewObj->SetSaveData( aData );
aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
delete pNewObj;
// unmark cell selection
Unmark();
}
}
}
void ScDBFunc::GroupDataPilot()
{
ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
if ( pDPObj )
{
ScStrCollection aEntries;
long nSelectDimension = -1;
GetSelectedMemberList( aEntries, nSelectDimension );
if ( aEntries.GetCount() > 0 )
{
sal_Bool bIsDataLayout;
String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
ScDPSaveData aData( *pDPObj->GetSaveData() );
ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
// find original base
String aBaseDimName( aDimName );
const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName );
if ( pBaseGroupDim )
{
// any entry's SourceDimName is the original base
aBaseDimName = pBaseGroupDim->GetSourceDimName();
}
// find existing group dimension
// (using the selected dim, can be intermediate group dim)
ScDPSaveGroupDimension* pGroupDimension = pDimData->GetGroupDimAccForBase( aDimName );
// remove the selected items from their groups
// (empty groups are removed, too)
sal_uInt16 nEntryCount = aEntries.GetCount();
sal_uInt16 nEntry;
if ( pGroupDimension )
{
for (nEntry=0; nEntry<nEntryCount; nEntry++)
{
String aEntryName = aEntries[nEntry]->GetString();
if ( pBaseGroupDim )
{
// for each selected (intermediate) group, remove all its items
// (same logic as for adding, below)
const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
if ( pBaseGroup )
pBaseGroup->RemoveElementsFromGroups( *pGroupDimension ); // remove all elements
else
pGroupDimension->RemoveFromGroups( aEntryName );
}
else
pGroupDimension->RemoveFromGroups( aEntryName );
}
}
ScDPSaveGroupDimension* pNewGroupDim = NULL;
if ( !pGroupDimension )
{
// create a new group dimension
String aGroupDimName = pDimData->CreateGroupDimName( aBaseDimName, *pDPObj, false, NULL );
pNewGroupDim = new ScDPSaveGroupDimension( aBaseDimName, aGroupDimName );
pGroupDimension = pNewGroupDim; // make changes to the new dim if none existed
if ( pBaseGroupDim )
{
// If it's a higher-order group dimension, pre-allocate groups for all
// non-selected original groups, so the individual base members aren't
// used for automatic groups (this would make the original groups hard
// to find).
//! Also do this when removing groups?
//! Handle this case dynamically with automatic groups?
long nGroupCount = pBaseGroupDim->GetGroupCount();
for ( long nGroup = 0; nGroup < nGroupCount; nGroup++ )
{
const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetGroupByIndex( nGroup );
StrData aStrData( pBaseGroup->GetGroupName() );
sal_uInt16 nCollIndex;
if ( !aEntries.Search( &aStrData, nCollIndex ) ) //! ignore case?
{
// add an additional group for each item that is not in the selection
ScDPSaveGroupItem aGroup( pBaseGroup->GetGroupName() );
aGroup.AddElementsFromGroup( *pBaseGroup );
pGroupDimension->AddGroupItem( aGroup );
}
}
}
}
String aGroupDimName = pGroupDimension->GetGroupDimName();
//! localized prefix string
String aGroupName = pGroupDimension->CreateGroupName( String::CreateFromAscii("Group") );
ScDPSaveGroupItem aGroup( aGroupName );
for (nEntry=0; nEntry<nEntryCount; nEntry++)
{
String aEntryName = aEntries[nEntry]->GetString();
if ( pBaseGroupDim )
{
// for each selected (intermediate) group, add all its items
const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
if ( pBaseGroup )
aGroup.AddElementsFromGroup( *pBaseGroup );
else
aGroup.AddElement( aEntryName ); // no group found -> automatic group, add the item itself
}
else
aGroup.AddElement( aEntryName ); // no group dimension, add all items directly
}
pGroupDimension->AddGroupItem( aGroup );
if ( pNewGroupDim )
{
pDimData->AddGroupDimension( *pNewGroupDim );
delete pNewGroupDim; // AddGroupDimension copies the object
// don't access pGroupDimension after here
}
pGroupDimension = pNewGroupDim = NULL;
// set orientation
ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
{
ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aDimName );
pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
long nPosition = 0; //! before (immediate) base
aData.SetPosition( pSaveDimension, nPosition );
}
// apply changes
ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
ScDPObject* pNewObj = new ScDPObject( *pDPObj );
pNewObj->SetSaveData( aData );
aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
delete pNewObj;
// unmark cell selection
Unmark();
}
}
}
void ScDBFunc::UngroupDataPilot()
{
ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
if ( pDPObj )
{
ScStrCollection aEntries;
long nSelectDimension = -1;
GetSelectedMemberList( aEntries, nSelectDimension );
if ( aEntries.GetCount() > 0 )
{
sal_Bool bIsDataLayout;
String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
ScDPSaveData aData( *pDPObj->GetSaveData() );
ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
//! test first if DimensionData exists?
sal_Bool bApply = sal_False;
ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
if ( ( pGroupDim && pGroupDim->GetDatePart() != 0 ) ||
( pNumGroupDim && pNumGroupDim->GetDatePart() != 0 ) )
{
// Date grouping: need to remove all affected group dimensions.
// This is done using DateGroupDataPilot with nParts=0.
DateGroupDataPilot( ScDPNumGroupInfo(), 0 );
// bApply remains FALSE
// dimension pointers become invalid
}
else if ( pGroupDim )
{
sal_uInt16 nEntryCount = aEntries.GetCount();
for (sal_uInt16 nEntry=0; nEntry<nEntryCount; nEntry++)
{
String aEntryName = aEntries[nEntry]->GetString();
pGroupDim->RemoveGroup( aEntryName );
}
// remove group dimension if empty
bool bEmptyDim = pGroupDim->IsEmpty();
if ( !bEmptyDim )
{
// If all remaining groups in the dimension aren't shown, remove
// the dimension too, as if it was completely empty.
ScStrCollection aVisibleEntries;
pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
bEmptyDim = pGroupDim->HasOnlyHidden( aVisibleEntries );
}
if ( bEmptyDim )
{
pDimData->RemoveGroupDimension( aDimName ); // pGroupDim is deleted
// also remove SaveData settings for the dimension that no longer exists
aData.RemoveDimensionByName( aDimName );
}
bApply = sal_True;
}
else if ( pNumGroupDim )
{
// remove the numerical grouping
pDimData->RemoveNumGroupDimension( aDimName );
// SaveData settings can remain unchanged - the same dimension still exists
bApply = sal_True;
}
if ( bApply )
{
// apply changes
ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
ScDPObject* pNewObj = new ScDPObject( *pDPObj );
pNewObj->SetSaveData( aData );
aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
delete pNewObj;
// unmark cell selection
Unmark();
}
}
}
}
static 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 if (c == sal_Unicode('\\'))
{
// Escape a backslash character.
aWordBuf.append(c);
aWordBuf.append(c);
}
else if (c == sal_Unicode('?'))
{
// A literal '?' must be escaped with a backslash ('\');
aWordBuf.append(sal_Unicode('\\'));
aWordBuf.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)
return;
String aOldText;
pDoc->GetString( rPos.Col(), rPos.Row(), rPos.Tab(), aOldText );
if ( aOldText == rString )
{
// nothing to do: silently exit
return;
}
sal_uInt16 nErrorId = 0;
pDPObj->BuildAllDimensionMembers();
ScDPSaveData aData( *pDPObj->GetSaveData() );
sal_Bool bChange = sal_False;
sal_uInt16 nOrient = DataPilotFieldOrientation_HIDDEN;
long nField = pDPObj->GetHeaderDim( rPos, nOrient );
if ( nField >= 0 )
{
// changing a field title
if ( aData.GetExistingDimensionData() )
{
// only group dimensions can be renamed
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 );
// also rename in SaveData to preserve the field settings
ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aOldText );
pSaveDim->SetName( aNewName );
bChange = sal_True;
}
else
nErrorId = STR_INVALIDNAME;
}
}
else if (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW)
{
sal_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 ( (aPosData.Flags & MemberResultFlags::HASMEMBER) && aOldText.Len() )
{
if ( aData.GetExistingDimensionData() && !(aPosData.Flags & MemberResultFlags::SUBTOTAL))
{
sal_Bool bIsDataLayout;
String aDimName = pDPObj->GetDimName( aPosData.Dimension, bIsDataLayout );
ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
if ( pGroupDim )
{
// valid name: not empty, no existing group in this dimension
//! ignore case?
if ( aNewName.Len() && !pGroupDim->GetNamedGroup( 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 );
}
// 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 = sal_True;
}
else
nErrorId = STR_INVALIDNAME;
}
}
else if ((aPosData.Flags & MemberResultFlags::GRANDTOTAL))
{
aData.SetGrandTotalName(rString);
bChange = true;
}
else if (aPosData.Dimension >= 0 && aPosData.MemberName.getLength() > 0)
{
sal_Bool bDataLayout = false;
String aDimName = pDPObj->GetDimName(static_cast<long>(aPosData.Dimension), bDataLayout);
if (bDataLayout)
{
// data dimension
do
{
if ((aPosData.Flags & MemberResultFlags::SUBTOTAL))
break;
ScDPSaveDimension* pDim = aData.GetDimensionByName(aPosData.MemberName);
if (!pDim)
break;
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
{
ScDPSaveDimension* pDim = aData.GetDimensionByName(aDimName);
if (!pDim)
break;
ScDPSaveMember* pMem = pDim->GetExistingMemberByName(aPosData.MemberName);
if (!pMem)
break;
if ((aPosData.Flags & MemberResultFlags::SUBTOTAL))
{
// Change subtotal only when the table has one data dimension.
if (aData.GetDataDimensionCount() > 1)
break;
// display name for subtotal is allowed only if the subtotal type is 'Automatic'.
if (pDim->GetSubTotalsCount() != 1)
break;
if (pDim->GetSubTotalFunc(0) != sheet::GeneralFunction_AUTO)
break;
const OUString* pLayoutName = pMem->GetLayoutName();
String aMemberName;
if (pLayoutName)
aMemberName = *pLayoutName;
else
aMemberName = aPosData.MemberName;
String aNew = lcl_replaceMemberNameInSubtotal(rString, aMemberName);
pDim->SetSubtotalName(aNew);
bChange = true;
}
else
{
// 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, sal_True, sal_False );
delete pNewObj;
}
else
{
if ( !nErrorId )
nErrorId = STR_ERR_DATAPILOT_INPUT;
ErrorMessage( nErrorId );
}
}
void lcl_MoveToEnd( ScDPSaveDimension& rDim, const String& rItemName )
{
ScDPSaveMember* pNewMember = NULL;
const ScDPSaveMember* pOldMember = rDim.GetExistingMemberByName( rItemName );
if ( pOldMember )
pNewMember = new ScDPSaveMember( *pOldMember );
else
pNewMember = new ScDPSaveMember( rItemName );
rDim.AddMember( pNewMember );
// AddMember takes ownership of the new pointer,
// puts it to the end of the list even if it was in the list before.
}
struct ScOUStringCollate
{
CollatorWrapper* mpCollator;
ScOUStringCollate(CollatorWrapper* pColl) : mpCollator(pColl) {}
bool operator()(const rtl::OUString& rStr1, const rtl::OUString& rStr2) const
{
return ( mpCollator->compareString(rStr1, rStr2) < 0 );
}
};
bool ScDBFunc::DataPilotSort( const ScAddress& rPos, bool bAscending, sal_uInt16* pUserListId )
{
ScDocument* pDoc = GetViewData()->GetDocument();
ScDPObject* pDPObj = pDoc->GetDPAtCursor(rPos.Col(), rPos.Row(), rPos.Tab());
if (!pDPObj)
return false;
// We need to run this to get all members later.
if ( pUserListId )
pDPObj->BuildAllDimensionMembers();
sal_uInt16 nOrientation;
long nDimIndex = pDPObj->GetHeaderDim(rPos, nOrientation);
if (nDimIndex < 0)
// Invalid dimension index. Bail out.
return false;
sal_Bool bDataLayout;
ScDPSaveData* pSaveData = pDPObj->GetSaveData();
if (!pSaveData)
return false;
ScDPSaveData aNewSaveData(*pSaveData);
String aDimName = pDPObj->GetDimName(nDimIndex, bDataLayout);
ScDPSaveDimension* pSaveDim = aNewSaveData.GetDimensionByName(aDimName);
if (!pSaveDim)
return false;
// manual evaluation of sort order is only needed if a user list id is given
if ( pUserListId )
{
typedef ScDPSaveDimension::MemberList MemList;
const MemList& rDimMembers = pSaveDim->GetMembers();
list<OUString> aMembers;
hash_set<OUString, ::rtl::OUStringHash> aMemberSet;
size_t nMemberCount = 0;
for (MemList::const_iterator itr = rDimMembers.begin(), itrEnd = rDimMembers.end();
itr != itrEnd; ++itr)
{
ScDPSaveMember* pMem = *itr;
aMembers.push_back(pMem->GetName());
aMemberSet.insert(pMem->GetName());
++nMemberCount;
}
// Sort the member list in ascending order.
ScOUStringCollate aCollate( ScGlobal::GetCollator() );
aMembers.sort(aCollate);
// Collect and rank those custom sort strings that also exist in the member name list.
typedef hash_map<OUString, sal_uInt16, OUStringHash> UserSortMap;
UserSortMap aSubStrs;
sal_uInt16 nSubCount = 0;
if (pUserListId)
{
ScUserList* pUserList = ScGlobal::GetUserList();
if (!pUserList)
return false;
{
sal_uInt16 n = pUserList->GetCount();
if (!n || *pUserListId >= n)
return false;
}
ScUserListData* pData = static_cast<ScUserListData*>((*pUserList)[*pUserListId]);
if (pData)
{
sal_uInt16 n = pData->GetSubCount();
for (sal_uInt16 i = 0; i < n; ++i)
{
OUString aSub = pData->GetSubStr(i);
if (!aMemberSet.count(aSub))
// This string doesn't exist in the member name set. Don't add this.
continue;
aSubStrs.insert(UserSortMap::value_type(aSub, nSubCount++));
}
}
}
// Rank all members.
vector<OUString> aRankedNames(nMemberCount);
sal_uInt16 nCurStrId = 0;
for (list<OUString>::const_iterator itr = aMembers.begin(), itrEnd = aMembers.end();
itr != itrEnd; ++itr)
{
OUString aName = *itr;
sal_uInt16 nRank = 0;
UserSortMap::const_iterator itrSub = aSubStrs.find(aName);
if (itrSub == aSubStrs.end())
nRank = nSubCount + nCurStrId++;
else
nRank = itrSub->second;
if (!bAscending)
nRank = static_cast< sal_uInt16 >( nMemberCount - nRank - 1 );
aRankedNames[nRank] = aName;
}
// Re-order ScDPSaveMember instances with the new ranks.
for (vector<OUString>::const_iterator itr = aRankedNames.begin(), itrEnd = aRankedNames.end();
itr != itrEnd; ++itr)
{
const ScDPSaveMember* pOldMem = pSaveDim->GetExistingMemberByName(*itr);
if (!pOldMem)
// All members are supposed to be present.
continue;
ScDPSaveMember* pNewMem = new ScDPSaveMember(*pOldMem);
pSaveDim->AddMember(pNewMem);
}
// Set the sorting mode to manual for now. We may introduce a new sorting
// mode later on.
sheet::DataPilotFieldSortInfo aSortInfo;
aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
pSaveDim->SetSortInfo(&aSortInfo);
}
else
{
// without user list id, just apply sorting mode
sheet::DataPilotFieldSortInfo aSortInfo;
aSortInfo.Mode = sheet::DataPilotFieldSortMode::NAME;
aSortInfo.IsAscending = bAscending;
pSaveDim->SetSortInfo(&aSortInfo);
}
// Update the datapilot with the newly sorted field members.
auto_ptr<ScDPObject> pNewObj(new ScDPObject(*pDPObj));
pNewObj->SetSaveData(aNewSaveData);
ScDBDocFunc aFunc(*GetViewData()->GetDocShell());
return aFunc.DataPilotUpdate(pDPObj, pNewObj.get(), true, false);
}
sal_Bool ScDBFunc::DataPilotMove( const ScRange& rSource, const ScAddress& rDest )
{
sal_Bool bRet = sal_False;
ScDocument* pDoc = GetViewData()->GetDocument();
ScDPObject* pDPObj = pDoc->GetDPAtCursor( rSource.aStart.Col(), rSource.aStart.Row(), rSource.aStart.Tab() );
if ( pDPObj && pDPObj == pDoc->GetDPAtCursor( rDest.Col(), rDest.Row(), rDest.Tab() ) )
{
sheet::DataPilotTableHeaderData aDestData;
pDPObj->GetHeaderPositionData( rDest, aDestData );
bool bValid = ( aDestData.Dimension >= 0 ); // dropping onto a field
// look through the source range
std::hash_set< rtl::OUString, rtl::OUStringHash, std::equal_to<rtl::OUString> > aMembersSet; // for lookup
std::vector< rtl::OUString > aMembersVector; // members in original order, for inserting
aMembersVector.reserve( std::max( static_cast<SCSIZE>( rSource.aEnd.Col() - rSource.aStart.Col() + 1 ),
static_cast<SCSIZE>( rSource.aEnd.Row() - rSource.aStart.Row() + 1 ) ) );
for (SCROW nRow = rSource.aStart.Row(); bValid && nRow <= rSource.aEnd.Row(); ++nRow )
for (SCCOL nCol = rSource.aStart.Col(); bValid && nCol <= rSource.aEnd.Col(); ++nCol )
{
sheet::DataPilotTableHeaderData aSourceData;
pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, rSource.aStart.Tab() ), aSourceData );
if ( aSourceData.Dimension == aDestData.Dimension && aSourceData.MemberName.getLength() )
{
if ( aMembersSet.find( aSourceData.MemberName ) == aMembersSet.end() )
{
aMembersSet.insert( aSourceData.MemberName );
aMembersVector.push_back( aSourceData.MemberName );
}
// duplicates are ignored
}
else
bValid = false; // empty (subtotal) or different field
}
if ( bValid )
{
sal_Bool bIsDataLayout;
String aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
if ( !bIsDataLayout )
{
ScDPSaveData aData( *pDPObj->GetSaveData() );
ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
// get all member names in source order
uno::Sequence<rtl::OUString> aMemberNames;
pDPObj->GetMemberNames( aDestData.Dimension, aMemberNames );
bool bInserted = false;
sal_Int32 nMemberCount = aMemberNames.getLength();
for (sal_Int32 nMemberPos=0; nMemberPos<nMemberCount; ++nMemberPos)
{
String aMemberStr( aMemberNames[nMemberPos] );
if ( !bInserted && aMemberNames[nMemberPos] == aDestData.MemberName )
{
// insert dragged items before this item
for ( std::vector<rtl::OUString>::const_iterator aIter = aMembersVector.begin();
aIter != aMembersVector.end(); ++aIter )
lcl_MoveToEnd( *pDim, *aIter );
bInserted = true;
}
if ( aMembersSet.find( aMemberStr ) == aMembersSet.end() ) // skip dragged items
lcl_MoveToEnd( *pDim, aMemberStr );
}
// insert dragged item at end if dest wasn't found (for example, empty)
if ( !bInserted )
for ( std::vector<rtl::OUString>::const_iterator aIter = aMembersVector.begin();
aIter != aMembersVector.end(); ++aIter )
lcl_MoveToEnd( *pDim, *aIter );
// Items that were in SaveData, but not in the source, end up at the start of the list.
// set flag for manual sorting
sheet::DataPilotFieldSortInfo aSortInfo;
aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
pDim->SetSortInfo( &aSortInfo );
// apply changes
ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
ScDPObject* pNewObj = new ScDPObject( *pDPObj );
pNewObj->SetSaveData( aData );
aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False ); //! bApi for drag&drop?
delete pNewObj;
Unmark(); // entry was moved - no use in leaving the old cell selected
bRet = sal_True;
}
}
}
return bRet;
}
sal_Bool ScDBFunc::HasSelectionForDrillDown( sal_uInt16& rOrientation )
{
sal_Bool bRet = sal_False;
ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
if ( pDPObj )
{
ScStrCollection aEntries;
long nSelectDimension = -1;
GetSelectedMemberList( aEntries, nSelectDimension );
if ( aEntries.GetCount() > 0 )
{
sal_Bool bIsDataLayout;
String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
if ( !bIsDataLayout )
{
ScDPSaveData* pSaveData = pDPObj->GetSaveData();
ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName( aDimName );
if ( pDim )
{
sal_uInt16 nDimOrient = pDim->GetOrientation();
ScDPSaveDimension* pInner = pSaveData->GetInnermostDimension( nDimOrient );
if ( pDim == pInner )
{
rOrientation = nDimOrient;
bRet = sal_True;
}
}
}
}
}
return bRet;
}
void ScDBFunc::SetDataPilotDetails( sal_Bool bShow, const String* pNewDimensionName )
{
ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
if ( pDPObj )
{
ScStrCollection aEntries;
long nSelectDimension = -1;
GetSelectedMemberList( aEntries, nSelectDimension );
if ( aEntries.GetCount() > 0 )
{
sal_Bool bIsDataLayout;
String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
if ( !bIsDataLayout )
{
ScDPSaveData aData( *pDPObj->GetSaveData() );
ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
if ( bShow && pNewDimensionName )
{
// add the new dimension with the same orientation, at the end
ScDPSaveDimension* pNewDim = aData.GetDimensionByName( *pNewDimensionName );
ScDPSaveDimension* pDuplicated = NULL;
if ( pNewDim->GetOrientation() == sheet::DataPilotFieldOrientation_DATA )
{
// Need to duplicate the dimension, create column/row in addition to data:
// The duplicated dimension inherits the existing settings, pNewDim is modified below.
pDuplicated = aData.DuplicateDimension( *pNewDimensionName );
}
sal_uInt16 nOrientation = pDim->GetOrientation();
pNewDim->SetOrientation( nOrientation );
long nPosition = LONG_MAX;
aData.SetPosition( pNewDim, nPosition );
ScDPSaveDimension* pDataLayout = aData.GetDataLayoutDimension();
if ( pDataLayout->GetOrientation() == nOrientation &&
aData.GetDataDimensionCount() <= 1 )
{
// If there is only one data dimension, the data layout dimension
// must still be the last one in its orientation.
aData.SetPosition( pDataLayout, nPosition );
}
if ( pDuplicated )
{
// The duplicated (data) dimension needs to be behind the original dimension
aData.SetPosition( pDuplicated, nPosition );
}
// Hide details for all visible members (selected are changed below).
//! Use all members from source level instead (including non-visible)?
ScStrCollection aVisibleEntries;
pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
sal_uInt16 nVisCount = aVisibleEntries.GetCount();
for (sal_uInt16 nVisPos=0; nVisPos<nVisCount; nVisPos++)
{
String aVisName = aVisibleEntries[nVisPos]->GetString();
ScDPSaveMember* pMember = pDim->GetMemberByName( aVisName );
pMember->SetShowDetails( sal_False );
}
}
sal_uInt16 nEntryCount = aEntries.GetCount();
for (sal_uInt16 nEntry=0; nEntry<nEntryCount; nEntry++)
{
String aEntryName = aEntries[nEntry]->GetString();
ScDPSaveMember* pMember = pDim->GetMemberByName( aEntryName );
pMember->SetShowDetails( bShow );
}
// apply changes
ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
ScDPObject* pNewObj = new ScDPObject( *pDPObj );
pNewObj->SetSaveData( aData );
aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
delete pNewObj;
// unmark cell selection
Unmark();
}
}
}
}
void ScDBFunc::ShowDataPilotSourceData( ScDPObject& rDPObj, const Sequence<sheet::DataPilotFieldFilter>& rFilters )
{
ScDocument* pDoc = GetViewData()->GetDocument();
if (pDoc->GetDocumentShell()->IsReadOnly())
{
ErrorMessage(STR_READONLYERR);
return;
}
Reference<sheet::XDimensionsSupplier> xDimSupplier = rDPObj.GetSource();
Reference<container::XNameAccess> xDims = xDimSupplier->getDimensions();
Reference<sheet::XDrillDownDataSupplier> xDDSupplier(xDimSupplier, UNO_QUERY);
if (!xDDSupplier.is())
return;
Sequence< Sequence<Any> > aTabData = xDDSupplier->getDrillDownData(rFilters);
sal_Int32 nRowSize = aTabData.getLength();
if (nRowSize <= 1)
// There is no data to show. Bail out.
return;
sal_Int32 nColSize = aTabData[0].getLength();
SCTAB nNewTab = GetViewData()->GetTabNo();
auto_ptr<ScDocument> pInsDoc(new ScDocument(SCDOCMODE_CLIP));
pInsDoc->ResetClip( pDoc, nNewTab );
for (SCROW nRow = 0; nRow < nRowSize; ++nRow)
{
for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
{
const Any& rAny = aTabData[nRow][nCol];
rtl::OUString aStr;
double fVal;
if (rAny >>= aStr)
pInsDoc->PutCell( ScAddress(nCol, nRow, nNewTab), new ScStringCell(String(aStr)) );
else if (rAny >>= fVal)
pInsDoc->SetValue(nCol, nRow, nNewTab, fVal);
}
}
// set number format (important for dates)
for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
{
rtl::OUString aStr;
if (!(aTabData[0][nCol] >>= aStr))
continue;
Reference<XPropertySet> xPropSet(xDims->getByName(aStr), UNO_QUERY);
if (!xPropSet.is())
continue;
Any any = xPropSet->getPropertyValue( rtl::OUString::createFromAscii(SC_UNO_NUMBERFO) );
sal_Int32 nNumFmt = 0;
if (!(any >>= nNumFmt))
continue;
ScPatternAttr aPattern( pInsDoc->GetPool() );
aPattern.GetItemSet().Put( SfxUInt32Item(ATTR_VALUE_FORMAT, static_cast<sal_uInt32>(nNumFmt)) );
pInsDoc->ApplyPatternAreaTab(nCol, 1, nCol, nRowSize-1, nNewTab, aPattern);
}
SCCOL nEndCol = 0;
SCROW nEndRow = 0;
pInsDoc->GetCellArea( nNewTab, nEndCol, nEndRow );
pInsDoc->SetClipArea( ScRange( 0, 0, nNewTab, nEndCol, nEndRow, nNewTab ) );
::svl::IUndoManager* pMgr = GetViewData()->GetDocShell()->GetUndoManager();
String aUndo = ScGlobal::GetRscString( STR_UNDO_DOOUTLINE );
pMgr->EnterListAction( aUndo, aUndo );
String aNewTabName;
pDoc->CreateValidTabName(aNewTabName);
if ( InsertTable(aNewTabName, nNewTab) )
PasteFromClip( IDF_ALL, pInsDoc.get() );
pMgr->LeaveListAction();
}
//
// DB-Operationen (Sortieren, Filtern, Teilergebnisse) wiederholen
//
void ScDBFunc::RepeatDB( sal_Bool bRecord )
{
SCCOL nCurX = GetViewData()->GetCurX();
SCROW nCurY = GetViewData()->GetCurY();
SCTAB nTab = GetViewData()->GetTabNo();
ScDocument* pDoc = GetViewData()->GetDocument();
ScDBData* pDBData = GetDBData();
if (bRecord && !pDoc->IsUndoEnabled())
bRecord = sal_False;
ScQueryParam aQueryParam;
pDBData->GetQueryParam( aQueryParam );
sal_Bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
ScSortParam aSortParam;
pDBData->GetSortParam( aSortParam );
sal_Bool bSort = aSortParam.bDoSort[0];
ScSubTotalParam aSubTotalParam;
pDBData->GetSubTotalParam( aSubTotalParam );
sal_Bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
if ( bQuery || bSort || bSubTotal )
{
sal_Bool bQuerySize = sal_False;
ScRange aOldQuery;
ScRange aNewQuery;
if (bQuery && !aQueryParam.bInplace)
{
ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
aQueryParam.nDestTab, sal_True );
if (pDest && pDest->IsDoSize())
{
pDest->GetArea( aOldQuery );
bQuerySize = sal_True;
}
}
SCTAB nDummy;
SCCOL nStartCol;
SCROW nStartRow;
SCCOL nEndCol;
SCROW nEndRow;
pDBData->GetArea( nDummy, nStartCol, nStartRow, nEndCol, nEndRow );
//! Undo nur benoetigte Daten ?
ScDocument* pUndoDoc = NULL;
ScOutlineTable* pUndoTab = NULL;
ScRangeName* pUndoRange = NULL;
ScDBCollection* pUndoDB = NULL;
if (bRecord)
{
SCTAB nTabCount = pDoc->GetTableCount();
pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
if (pTable)
{
pUndoTab = new ScOutlineTable( *pTable );
SCCOLROW nOutStartCol; // Zeilen/Spaltenstatus
SCCOLROW nOutStartRow;
SCCOLROW nOutEndCol;
SCCOLROW nOutEndRow;
pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, sal_False, pUndoDoc );
pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, sal_False, pUndoDoc );
}
else
pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True );
// Datenbereich sichern - incl. Filter-Ergebnis
pDoc->CopyToDocument( 0,nStartRow,nTab, MAXCOL,nEndRow,nTab, IDF_ALL, sal_False, pUndoDoc );
// alle Formeln wegen Referenzen
pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, IDF_FORMULA, sal_False, pUndoDoc );
// DB- und andere Bereiche
ScRangeName* pDocRange = pDoc->GetRangeName();
if (pDocRange->GetCount())
pUndoRange = new ScRangeName( *pDocRange );
ScDBCollection* pDocDB = pDoc->GetDBCollection();
if (pDocDB->GetCount())
pUndoDB = new ScDBCollection( *pDocDB );
}
if (bSort && bSubTotal)
{
// Sortieren ohne SubTotals
aSubTotalParam.bRemoveOnly = sal_True; // wird unten wieder zurueckgesetzt
DoSubTotals( aSubTotalParam, sal_False );
}
if (bSort)
{
pDBData->GetSortParam( aSortParam ); // Bereich kann sich geaendert haben
Sort( aSortParam, sal_False, sal_False);
}
if (bQuery)
{
pDBData->GetQueryParam( aQueryParam ); // Bereich kann sich geaendert haben
ScRange aAdvSource;
if (pDBData->GetAdvancedQuerySource(aAdvSource))
{
pDoc->CreateQueryParam(
aAdvSource.aStart.Col(), aAdvSource.aStart.Row(),
aAdvSource.aEnd.Col(), aAdvSource.aEnd.Row(),
aAdvSource.aStart.Tab(), aQueryParam );
Query( aQueryParam, &aAdvSource, sal_False );
}
else
Query( aQueryParam, NULL, sal_False );
// bei nicht-inplace kann die Tabelle umgestellt worden sein
if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
SetTabNo( nTab );
}
if (bSubTotal)
{
pDBData->GetSubTotalParam( aSubTotalParam ); // Bereich kann sich geaendert haben
aSubTotalParam.bRemoveOnly = sal_False;
DoSubTotals( aSubTotalParam, sal_False );
}
if (bRecord)
{
SCTAB nDummyTab;
SCCOL nDummyCol;
SCROW nDummyRow, nNewEndRow;
pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
const ScRange* pOld = NULL;
const ScRange* pNew = NULL;
if (bQuerySize)
{
ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
aQueryParam.nDestTab, sal_True );
if (pDest)
{
pDest->GetArea( aNewQuery );
pOld = &aOldQuery;
pNew = &aNewQuery;
}
}
GetViewData()->GetDocShell()->GetUndoManager()->AddUndoAction(
new ScUndoRepeatDB( GetViewData()->GetDocShell(), nTab,
nStartCol, nStartRow, nEndCol, nEndRow,
nNewEndRow,
nCurX, nCurY,
pUndoDoc, pUndoTab,
pUndoRange, pUndoDB,
pOld, pNew ) );
}
GetViewData()->GetDocShell()->PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab,
PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE );
}
else // "Keine Operationen auszufuehren"
ErrorMessage(STR_MSSG_REPEATDB_0);
}