| /************************************************************** |
| * |
| * 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); |
| } |
| |
| |
| |
| |