problem pasting to calc an image copied from firefox (windows)
[LibreOffice.git] / sc / source / ui / view / dbfunc3.cxx
blob29479c7ef78f304b7dba7b4e499038dddc7be670
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <dbfunc.hxx>
21 #include <scitems.hxx>
22 #include <vcl/svapp.hxx>
23 #include <vcl/weld.hxx>
24 #include <svl/numformat.hxx>
25 #include <svl/zforlist.hxx>
26 #include <sfx2/app.hxx>
27 #include <unotools/collatorwrapper.hxx>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/container/XNameAccess.hpp>
30 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
31 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
32 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
33 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
34 #include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
35 #include <com/sun/star/sheet/MemberResultFlags.hpp>
36 #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
37 #include <com/sun/star/sheet/XDrillDownDataSupplier.hpp>
39 #include <global.hxx>
40 #include <scresid.hxx>
41 #include <globstr.hrc>
42 #include <undotab.hxx>
43 #include <undodat.hxx>
44 #include <dbdata.hxx>
45 #include <rangenam.hxx>
46 #include <docsh.hxx>
47 #include <olinetab.hxx>
48 #include <olinefun.hxx>
49 #include <dpobject.hxx>
50 #include <dpsave.hxx>
51 #include <dpdimsave.hxx>
52 #include <dbdocfun.hxx>
53 #include <dpoutput.hxx>
54 #include <editable.hxx>
55 #include <docpool.hxx>
56 #include <patattr.hxx>
57 #include <unonames.hxx>
58 #include <userlist.hxx>
59 #include <queryentry.hxx>
60 #include <markdata.hxx>
61 #include <tabvwsh.hxx>
62 #include <generalfunction.hxx>
63 #include <sortparam.hxx>
65 #include <comphelper/lok.hxx>
66 #include <osl/diagnose.h>
68 #include <memory>
69 #include <string_view>
70 #include <unordered_set>
71 #include <unordered_map>
72 #include <vector>
73 #include <algorithm>
75 using namespace com::sun::star;
76 using ::com::sun::star::uno::Any;
77 using ::com::sun::star::uno::Sequence;
78 using ::com::sun::star::uno::Reference;
79 using ::com::sun::star::uno::UNO_QUERY;
80 using ::com::sun::star::beans::XPropertySet;
81 using ::com::sun::star::container::XNameAccess;
82 using ::com::sun::star::sheet::XDimensionsSupplier;
83 using ::std::vector;
85 // outliner
87 // create outline grouping
89 void ScDBFunc::MakeOutline( bool bColumns, bool bRecord )
91 ScRange aRange;
92 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
94 ScDocShell* pDocSh = GetViewData().GetDocShell();
95 ScOutlineDocFunc aFunc(*pDocSh);
96 aFunc.MakeOutline( aRange, bColumns, bRecord, false );
98 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), bColumns ? COLUMN_HEADER : ROW_HEADER, GetViewData().GetTabNo());
99 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
100 bColumns, !bColumns, false /* bSizes*/,
101 false /* bHidden */, false /* bFiltered */,
102 true /* bGroups */, GetViewData().GetTabNo());
104 else
105 ErrorMessage(STR_NOMULTISELECT);
108 // delete outline grouping
110 void ScDBFunc::RemoveOutline( bool bColumns, bool bRecord )
112 ScRange aRange;
113 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
115 ScDocShell* pDocSh = GetViewData().GetDocShell();
116 ScOutlineDocFunc aFunc(*pDocSh);
117 aFunc.RemoveOutline( aRange, bColumns, bRecord, false );
119 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), bColumns ? COLUMN_HEADER : ROW_HEADER, GetViewData().GetTabNo());
120 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
121 bColumns, !bColumns, false /* bSizes*/,
122 true /* bHidden */, true /* bFiltered */,
123 true /* bGroups */, GetViewData().GetTabNo());
125 else
126 ErrorMessage(STR_NOMULTISELECT);
129 // menu status: delete outlines
131 void ScDBFunc::TestRemoveOutline( bool& rCol, bool& rRow )
133 bool bColFound = false;
134 bool bRowFound = false;
136 SCCOL nStartCol, nEndCol;
137 SCROW nStartRow, nEndRow;
138 SCTAB nStartTab, nEndTab;
139 if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
141 SCTAB nTab = nStartTab;
142 ScDocument& rDoc = GetViewData().GetDocument();
143 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
144 if (pTable)
146 ScOutlineEntry* pEntry;
147 SCCOLROW nStart;
148 SCCOLROW nEnd;
149 bool bColMarked = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
150 bool bRowMarked = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
152 // columns
154 if ( !bRowMarked || bColMarked ) // not when entire rows are marked
156 ScOutlineArray& rArray = pTable->GetColArray();
157 ScSubOutlineIterator aColIter( &rArray );
158 while (!bColFound)
160 pEntry=aColIter.GetNext();
161 if (!pEntry)
162 break;
163 nStart = pEntry->GetStart();
164 nEnd = pEntry->GetEnd();
165 if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
166 bColFound = true;
170 // rows
172 if ( !bColMarked || bRowMarked ) // not when entire columns are marked
174 ScOutlineArray& rArray = pTable->GetRowArray();
175 ScSubOutlineIterator aRowIter( &rArray );
176 while (!bRowFound)
178 pEntry=aRowIter.GetNext();
179 if (!pEntry)
180 break;
181 nStart = pEntry->GetStart();
182 nEnd = pEntry->GetEnd();
183 if ( nStartRow<=nEnd && nEndRow>=nStart )
184 bRowFound = true;
190 rCol = bColFound;
191 rRow = bRowFound;
194 void ScDBFunc::RemoveAllOutlines( bool bRecord )
196 SCTAB nTab = GetViewData().GetTabNo();
197 ScDocShell* pDocSh = GetViewData().GetDocShell();
198 ScOutlineDocFunc aFunc(*pDocSh);
200 bool bOk = aFunc.RemoveAllOutlines( nTab, bRecord );
202 if (bOk)
204 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
205 true /* bColumns */, true /* bRows */, false /* bSizes*/,
206 true /* bHidden */, true /* bFiltered */,
207 true /* bGroups */, nTab);
208 UpdateScrollBars(BOTH_HEADERS);
212 // auto outlines
214 void ScDBFunc::AutoOutline( )
216 ScDocument& rDoc = GetViewData().GetDocument();
217 SCTAB nTab = GetViewData().GetTabNo();
218 ScRange aRange( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab ); // the complete sheet, if nothing is marked
219 ScMarkData& rMark = GetViewData().GetMarkData();
220 if ( rMark.IsMarked() || rMark.IsMultiMarked() )
222 rMark.MarkToMulti();
223 rMark.GetMultiMarkArea( aRange );
226 ScDocShell* pDocSh = GetViewData().GetDocShell();
227 ScOutlineDocFunc aFunc(*pDocSh);
228 aFunc.AutoOutline( aRange, true );
231 // select outline level
233 void ScDBFunc::SelectLevel( bool bColumns, sal_uInt16 nLevel, bool bRecord )
235 SCTAB nTab = GetViewData().GetTabNo();
236 ScDocShell* pDocSh = GetViewData().GetDocShell();
237 ScOutlineDocFunc aFunc(*pDocSh);
239 bool bOk = aFunc.SelectLevel( nTab, bColumns, nLevel, bRecord, true/*bPaint*/ );
241 if (bOk)
243 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
244 bColumns, !bColumns, false /* bSizes*/,
245 true /* bHidden */, true /* bFiltered */,
246 true /* bGroups */, nTab);
247 UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
251 // show individual outline groups
253 void ScDBFunc::SetOutlineState( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bHidden)
255 const sal_uInt16 nHeadEntry = static_cast< sal_uInt16 >( -1 );
256 if ( nEntry == nHeadEntry)
257 SelectLevel( bColumns, sal::static_int_cast<sal_uInt16>(nLevel) );
258 else
260 if ( !bHidden )
261 ShowOutline( bColumns, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
262 else
263 HideOutline( bColumns, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
267 void ScDBFunc::ShowOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bRecord, bool bPaint )
269 SCTAB nTab = GetViewData().GetTabNo();
270 ScDocShell* pDocSh = GetViewData().GetDocShell();
271 ScOutlineDocFunc aFunc(*pDocSh);
273 aFunc.ShowOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint );
275 if ( bPaint )
277 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
278 bColumns, !bColumns, false /* bSizes*/,
279 true /* bHidden */, true /* bFiltered */,
280 true /* bGroups */, nTab);
281 UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
285 // hide individual outline groups
287 void ScDBFunc::HideOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bRecord, bool bPaint )
289 SCTAB nTab = GetViewData().GetTabNo();
290 ScDocShell* pDocSh = GetViewData().GetDocShell();
291 ScOutlineDocFunc aFunc(*pDocSh);
293 bool bOk = aFunc.HideOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint );
295 if ( bOk && bPaint )
297 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
298 bColumns, !bColumns, false /* bSizes*/,
299 true /* bHidden */, true /* bFiltered */,
300 true /* bGroups */, nTab);
301 UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
305 // menu status: show/hide marked range
307 bool ScDBFunc::OutlinePossible(bool bHide)
309 bool bEnable = false;
311 SCCOL nStartCol;
312 SCROW nStartRow;
313 SCTAB nStartTab;
314 SCCOL nEndCol;
315 SCROW nEndRow;
316 SCTAB nEndTab;
318 if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
320 ScDocument& rDoc = GetViewData().GetDocument();
321 SCTAB nTab = GetViewData().GetTabNo();
322 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
323 if (pTable)
325 SCCOLROW nStart;
326 SCCOLROW nEnd;
328 // columns
330 ScOutlineArray& rColArray = pTable->GetColArray();
331 ScSubOutlineIterator aColIter( &rColArray );
332 while (!bEnable)
334 ScOutlineEntry* pEntry = aColIter.GetNext();
335 if (!pEntry)
336 break;
337 nStart = pEntry->GetStart();
338 nEnd = pEntry->GetEnd();
339 if ( bHide )
341 if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
342 if (!pEntry->IsHidden())
343 bEnable = true;
345 else
347 if ( nStart>=nStartCol && nEnd<=nEndCol )
348 if (pEntry->IsHidden())
349 bEnable = true;
353 // rows
355 ScOutlineArray& rRowArray = pTable->GetRowArray();
356 ScSubOutlineIterator aRowIter( &rRowArray );
357 for (;;)
359 ScOutlineEntry* pEntry = aRowIter.GetNext();
360 if (!pEntry)
361 break;
362 nStart = pEntry->GetStart();
363 nEnd = pEntry->GetEnd();
364 if ( bHide )
366 if ( nStartRow<=nEnd && nEndRow>=nStart )
367 if (!pEntry->IsHidden())
368 bEnable = true;
370 else
372 if ( nStart>=nStartRow && nEnd<=nEndRow )
373 if (pEntry->IsHidden())
374 bEnable = true;
380 return bEnable;
383 // show marked range
385 void ScDBFunc::ShowMarkedOutlines( bool bRecord )
387 ScRange aRange;
388 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
390 ScDocShell* pDocSh = GetViewData().GetDocShell();
391 ScOutlineDocFunc aFunc(*pDocSh);
392 bool bDone = aFunc.ShowMarkedOutlines( aRange, bRecord );
393 if (bDone)
395 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
396 GetViewData().GetViewShell(), true, true,
397 false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
398 true /* bGroups */, GetViewData().GetTabNo());
399 UpdateScrollBars();
402 else
403 ErrorMessage(STR_NOMULTISELECT);
406 // hide marked range
408 void ScDBFunc::HideMarkedOutlines( bool bRecord )
410 ScRange aRange;
411 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
413 ScDocShell* pDocSh = GetViewData().GetDocShell();
414 ScOutlineDocFunc aFunc(*pDocSh);
415 bool bDone = aFunc.HideMarkedOutlines( aRange, bRecord );
416 if (bDone)
418 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
419 GetViewData().GetViewShell(), true, true,
420 false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
421 true /* bGroups */, GetViewData().GetTabNo());
422 UpdateScrollBars();
425 else
426 ErrorMessage(STR_NOMULTISELECT);
429 // sub totals
431 void ScDBFunc::DoSubTotals( const ScSubTotalParam& rParam, bool bRecord,
432 const ScSortParam* pForceNewSort )
434 bool bDo = !rParam.bRemoveOnly; // sal_False = only delete
436 ScDocShell* pDocSh = GetViewData().GetDocShell();
437 ScDocument& rDoc = pDocSh->GetDocument();
438 ScMarkData& rMark = GetViewData().GetMarkData();
439 SCTAB nTab = GetViewData().GetTabNo();
440 if (bRecord && !rDoc.IsUndoEnabled())
441 bRecord = false;
443 ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
444 rParam.nCol2, rParam.nRow2 );
445 if (!pDBData)
447 OSL_FAIL( "SubTotals: no DBData" );
448 return;
451 ScEditableTester aTester( rDoc, nTab, 0,rParam.nRow1+1, rDoc.MaxCol(),rDoc.MaxRow() );
452 if (!aTester.IsEditable())
454 ErrorMessage(aTester.GetMessageId());
455 return;
458 if (rDoc.HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
459 rParam.nCol2, rParam.nRow2, nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
461 ErrorMessage(STR_MSSG_INSERTCELLS_0); // do not insert into merged
462 return;
465 weld::WaitObject aWait(GetViewData().GetDialogParent());
466 bool bOk = true;
467 if (rParam.bReplace)
469 if (rDoc.TestRemoveSubTotals( nTab, rParam ))
471 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
472 VclMessageType::Question, VclButtonsType::YesNo,
473 ScResId(STR_MSSG_DOSUBTOTALS_1))); // "delete data?"
474 xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
475 xBox->set_default_response(RET_YES);
476 bOk = xBox->run() == RET_YES;
480 if (!bOk)
481 return;
483 ScDocShellModificator aModificator( *pDocSh );
485 ScSubTotalParam aNewParam( rParam ); // change end of range
486 ScDocumentUniquePtr pUndoDoc;
487 std::unique_ptr<ScOutlineTable> pUndoTab;
488 std::unique_ptr<ScRangeName> pUndoRange;
489 std::unique_ptr<ScDBCollection> pUndoDB;
491 if (bRecord) // record old data
493 bool bOldFilter = bDo && rParam.bDoSort;
494 SCTAB nTabCount = rDoc.GetTableCount();
495 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
496 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
497 if (pTable)
499 pUndoTab.reset(new ScOutlineTable( *pTable ));
501 SCCOLROW nOutStartCol; // row/column status
502 SCCOLROW nOutStartRow;
503 SCCOLROW nOutEndCol;
504 SCCOLROW nOutEndRow;
505 pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
506 pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
508 pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
509 rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
510 rDoc.CopyToDocument( 0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
512 else
513 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, bOldFilter );
515 // record data range - including filter results
516 rDoc.CopyToDocument( 0,rParam.nRow1+1,nTab, rDoc.MaxCol(),rParam.nRow2,nTab,
517 InsertDeleteFlags::ALL, false, *pUndoDoc );
519 // all formulas for reference
520 rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1,
521 InsertDeleteFlags::FORMULA, false, *pUndoDoc );
523 // database and other ranges
524 ScRangeName* pDocRange = rDoc.GetRangeName();
525 if (!pDocRange->empty())
526 pUndoRange.reset(new ScRangeName( *pDocRange ));
527 ScDBCollection* pDocDB = rDoc.GetDBCollection();
528 if (!pDocDB->empty())
529 pUndoDB.reset(new ScDBCollection( *pDocDB ));
532 ScOutlineTable* pOut = rDoc.GetOutlineTable( nTab );
533 if (pOut)
535 // Remove all existing outlines in the specified range.
536 ScOutlineArray& rRowArray = pOut->GetRowArray();
537 sal_uInt16 nDepth = rRowArray.GetDepth();
538 for (sal_uInt16 i = 0; i < nDepth; ++i)
540 bool bSize;
541 rRowArray.Remove(aNewParam.nRow1, aNewParam.nRow2, bSize);
545 if (rParam.bReplace)
546 rDoc.RemoveSubTotals( nTab, aNewParam );
547 bool bSuccess = true;
548 if (bDo)
550 // Sort
551 if ( rParam.bDoSort || pForceNewSort )
553 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
555 // set subtotal fields before sorting
556 // (duplicate values are dropped, so that they can be called again)
558 ScSortParam aOldSort;
559 pDBData->GetSortParam( aOldSort );
560 ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
561 Sort( aSortParam, false, false );
564 bSuccess = rDoc.DoSubTotals( nTab, aNewParam );
566 ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
567 aNewParam.nCol2, aNewParam.nRow2, nTab );
568 rDoc.SetDirty( aDirtyRange, true );
570 if (bRecord)
572 pDocSh->GetUndoManager()->AddUndoAction(
573 std::make_unique<ScUndoSubTotals>( pDocSh, nTab,
574 rParam, aNewParam.nRow2,
575 std::move(pUndoDoc), std::move(pUndoTab), // pUndoDBData,
576 std::move(pUndoRange), std::move(pUndoDB) ) );
579 if (!bSuccess)
581 // "Can not insert any rows"
582 ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
585 // store
586 pDBData->SetSubTotalParam( aNewParam );
587 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
588 rDoc.CompileDBFormula();
590 DoneBlockMode();
591 InitOwnBlockMode();
592 rMark.SetMarkArea( ScRange( aNewParam.nCol1,aNewParam.nRow1,nTab,
593 aNewParam.nCol2,aNewParam.nRow2,nTab ) );
594 MarkDataChanged();
596 pDocSh->PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
597 PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
599 aModificator.SetDocumentModified();
601 SelectionChanged();
604 // consolidate
606 void ScDBFunc::Consolidate( const ScConsolidateParam& rParam )
608 ScDocShell* pDocShell = GetViewData().GetDocShell();
609 pDocShell->DoConsolidate( rParam );
610 SetTabNo( rParam.nTab, true );
613 // pivot
615 static OUString lcl_MakePivotTabName( std::u16string_view rPrefix, SCTAB nNumber )
617 OUString aName = rPrefix + OUString::number( nNumber );
618 return aName;
621 bool ScDBFunc::MakePivotTable(
622 const ScDPSaveData& rData, const ScRange& rDest, bool bNewTable,
623 const ScDPObject& rSource )
625 // error message if no fields are set
626 // this must be removed when drag&drop of fields from a toolbox is available
628 if ( rData.IsEmpty() )
630 ErrorMessage(STR_PIVOT_NODATA);
631 return false;
634 ScDocShell* pDocSh = GetViewData().GetDocShell();
635 ScDocument& rDoc = GetViewData().GetDocument();
636 bool bUndo = rDoc.IsUndoEnabled();
638 ScRange aDestRange = rDest;
639 if ( bNewTable )
641 SCTAB nSrcTab = GetViewData().GetTabNo();
643 OUString aName( ScResId(STR_PIVOT_TABLE) );
644 OUString aStr;
646 rDoc.GetName( nSrcTab, aStr );
647 aName += "_" + aStr + "_";
649 SCTAB nNewTab = nSrcTab+1;
651 SCTAB i=1;
652 while ( !rDoc.InsertTab( nNewTab, lcl_MakePivotTabName( aName, i ) ) && i <= MAXTAB )
653 i++;
655 bool bAppend = ( nNewTab+1 == rDoc.GetTableCount() );
656 if (bUndo)
658 pDocSh->GetUndoManager()->AddUndoAction(
659 std::make_unique<ScUndoInsertTab>( pDocSh, nNewTab, bAppend, lcl_MakePivotTabName( aName, i ) ));
662 GetViewData().InsertTab( nNewTab );
663 SetTabNo(nNewTab, true);
665 aDestRange = ScRange( 0, 0, nNewTab );
668 ScDPObject* pDPObj = rDoc.GetDPAtCursor(
669 aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aStart.Tab() );
671 ScDPObject aObj( rSource );
672 aObj.SetOutRange( aDestRange );
673 if ( pDPObj && !rData.GetExistingDimensionData() )
675 // copy dimension data from old object - lost in the dialog
676 //! change the dialog to keep the dimension data
678 ScDPSaveData aNewData( rData );
679 const ScDPSaveData* pOldData = pDPObj->GetSaveData();
680 if ( pOldData )
682 const ScDPDimensionSaveData* pDimSave = pOldData->GetExistingDimensionData();
683 aNewData.SetDimensionData( pDimSave );
685 aObj.SetSaveData( aNewData );
687 else
688 aObj.SetSaveData( rData );
690 bool bAllowMove = (pDPObj != nullptr); // allow re-positioning when editing existing table
692 ScDBDocFunc aFunc( *pDocSh );
693 bool bSuccess = aFunc.DataPilotUpdate(pDPObj, &aObj, true, false, bAllowMove);
695 CursorPosChanged(); // shells may be switched
697 if ( bNewTable )
699 pDocSh->PostPaintExtras();
700 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
703 return bSuccess;
706 void ScDBFunc::DeletePivotTable()
708 ScDocShell* pDocSh = GetViewData().GetDocShell();
709 ScDocument& rDoc = pDocSh->GetDocument();
710 ScDPObject* pDPObj = rDoc.GetDPAtCursor( GetViewData().GetCurX(),
711 GetViewData().GetCurY(),
712 GetViewData().GetTabNo() );
713 if ( pDPObj )
715 ScDBDocFunc aFunc( *pDocSh );
716 aFunc.RemovePivotTable(*pDPObj, true, false);
717 CursorPosChanged(); // shells may be switched
719 else
720 ErrorMessage(STR_PIVOT_NOTFOUND);
723 void ScDBFunc::RecalcPivotTable()
725 ScDocShell* pDocSh = GetViewData().GetDocShell();
726 ScDocument& rDoc = GetViewData().GetDocument();
728 ScDPObject* pDPObj = rDoc.GetDPAtCursor( GetViewData().GetCurX(),
729 GetViewData().GetCurY(),
730 GetViewData().GetTabNo() );
731 if (pDPObj)
733 // Remove existing data cache for the data that this datapilot uses,
734 // to force re-build data cache.
735 ScDBDocFunc aFunc(*pDocSh);
736 aFunc.RefreshPivotTables(pDPObj, false);
738 CursorPosChanged(); // shells may be switched
740 else
741 ErrorMessage(STR_PIVOT_NOTFOUND);
744 void ScDBFunc::GetSelectedMemberList(ScDPUniqueStringSet& rEntries, tools::Long& rDimension)
746 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
747 GetViewData().GetCurY(), GetViewData().GetTabNo() );
748 if ( !pDPObj )
749 return;
751 tools::Long nStartDimension = -1;
752 tools::Long nStartHierarchy = -1;
753 tools::Long nStartLevel = -1;
755 ScRangeListRef xRanges;
756 GetViewData().GetMultiArea( xRanges ); // incl. cursor if nothing is selected
757 size_t nRangeCount = xRanges->size();
758 bool bContinue = true;
760 for (size_t nRangePos=0; nRangePos < nRangeCount && bContinue; nRangePos++)
762 ScRange const & rRange = (*xRanges)[nRangePos];
763 SCCOL nStartCol = rRange.aStart.Col();
764 SCROW nStartRow = rRange.aStart.Row();
765 SCCOL nEndCol = rRange.aEnd.Col();
766 SCROW nEndRow = rRange.aEnd.Row();
767 SCTAB nTab = rRange.aStart.Tab();
769 for (SCROW nRow=nStartRow; nRow<=nEndRow && bContinue; nRow++)
770 for (SCCOL nCol=nStartCol; nCol<=nEndCol && bContinue; nCol++)
772 sheet::DataPilotTableHeaderData aData;
773 pDPObj->GetHeaderPositionData(ScAddress(nCol, nRow, nTab), aData);
774 if ( aData.Dimension < 0 )
775 bContinue = false; // not part of any dimension
776 else
778 if ( nStartDimension < 0 ) // first member?
780 nStartDimension = aData.Dimension;
781 nStartHierarchy = aData.Hierarchy;
782 nStartLevel = aData.Level;
784 if ( aData.Dimension != nStartDimension ||
785 aData.Hierarchy != nStartHierarchy ||
786 aData.Level != nStartLevel )
788 bContinue = false; // cannot mix dimensions
791 if ( bContinue )
793 // accept any part of a member description, also subtotals,
794 // but don't stop if empty parts are contained
795 if ( aData.Flags & sheet::MemberResultFlags::HASMEMBER )
796 rEntries.insert(aData.MemberName);
801 rDimension = nStartDimension; // dimension from which the found members came
802 if (!bContinue)
803 rEntries.clear(); // remove all if not valid
806 bool ScDBFunc::HasSelectionForDateGroup( ScDPNumGroupInfo& rOldInfo, sal_Int32& rParts )
808 // determine if the date group dialog has to be shown for the current selection
810 bool bFound = false;
812 SCCOL nCurX = GetViewData().GetCurX();
813 SCROW nCurY = GetViewData().GetCurY();
814 SCTAB nTab = GetViewData().GetTabNo();
815 ScDocument& rDoc = GetViewData().GetDocument();
817 ScDPObject* pDPObj = rDoc.GetDPAtCursor( nCurX, nCurY, nTab );
818 if ( pDPObj )
820 ScDPUniqueStringSet aEntries;
821 tools::Long nSelectDimension = -1;
822 GetSelectedMemberList( aEntries, nSelectDimension );
824 if (!aEntries.empty())
826 bool bIsDataLayout;
827 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
828 OUString aBaseDimName( aDimName );
830 bool bInGroupDim = false;
831 bool bFoundParts = false;
833 ScDPDimensionSaveData* pDimData =
834 const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
835 if ( pDimData )
837 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
838 const ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDim( aDimName );
839 if ( pNumGroupDim )
841 // existing num group dimension
843 if ( pNumGroupDim->GetDatePart() != 0 )
845 // dimension has date info -> edit settings of this dimension
846 // (parts are collected below)
848 rOldInfo = pNumGroupDim->GetDateInfo();
849 bFound = true;
851 else if ( pNumGroupDim->GetInfo().mbDateValues )
853 // Numerical grouping with DateValues flag is used for grouping
854 // of days with a "Number of days" value.
856 rOldInfo = pNumGroupDim->GetInfo();
857 rParts = css::sheet::DataPilotFieldGroupBy::DAYS; // not found in CollectDateParts
858 bFoundParts = true;
859 bFound = true;
861 bInGroupDim = true;
863 else if ( pGroupDim )
865 // existing additional group dimension
867 if ( pGroupDim->GetDatePart() != 0 )
869 // dimension has date info -> edit settings of this dimension
870 // (parts are collected below)
872 rOldInfo = pGroupDim->GetDateInfo();
873 aBaseDimName = pGroupDim->GetSourceDimName();
874 bFound = true;
876 bInGroupDim = true;
879 if ( bFound && !bFoundParts )
881 // collect date parts from all group dimensions
882 rParts = pDimData->CollectDateParts( aBaseDimName );
884 if ( !bFound && !bInGroupDim )
886 // create new date group dimensions if the selection is a single cell
887 // in a normal dimension with date content
889 ScRange aSelRange;
890 if ( (GetViewData().GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
891 aSelRange.aStart == aSelRange.aEnd )
893 SCCOL nSelCol = aSelRange.aStart.Col();
894 SCROW nSelRow = aSelRange.aStart.Row();
895 SCTAB nSelTab = aSelRange.aStart.Tab();
896 if ( rDoc.HasValueData( nSelCol, nSelRow, nSelTab ) )
898 sal_uLong nIndex = rDoc.GetAttr(
899 nSelCol, nSelRow, nSelTab, ATTR_VALUE_FORMAT)->GetValue();
900 SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nIndex);
901 if ( nType == SvNumFormatType::DATE || nType == SvNumFormatType::TIME || nType == SvNumFormatType::DATETIME )
903 bFound = true;
904 // use currently selected value for automatic limits
905 if( rOldInfo.mbAutoStart )
906 rOldInfo.mfStart = rDoc.GetValue( aSelRange.aStart );
907 if( rOldInfo.mbAutoEnd )
908 rOldInfo.mfEnd = rDoc.GetValue( aSelRange.aStart );
916 return bFound;
919 bool ScDBFunc::HasSelectionForNumGroup( ScDPNumGroupInfo& rOldInfo )
921 // determine if the numeric group dialog has to be shown for the current selection
923 bool bFound = false;
925 SCCOL nCurX = GetViewData().GetCurX();
926 SCROW nCurY = GetViewData().GetCurY();
927 SCTAB nTab = GetViewData().GetTabNo();
928 ScDocument& rDoc = GetViewData().GetDocument();
930 ScDPObject* pDPObj = rDoc.GetDPAtCursor( nCurX, nCurY, nTab );
931 if ( pDPObj )
933 ScDPUniqueStringSet aEntries;
934 tools::Long nSelectDimension = -1;
935 GetSelectedMemberList( aEntries, nSelectDimension );
937 if (!aEntries.empty())
939 bool bIsDataLayout;
940 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
942 bool bInGroupDim = false;
944 ScDPDimensionSaveData* pDimData =
945 const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
946 if ( pDimData )
948 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
949 if ( pNumGroupDim )
951 // existing num group dimension
952 // -> edit settings of this dimension
954 rOldInfo = pNumGroupDim->GetInfo();
955 bFound = true;
957 else if ( pDimData->GetNamedGroupDim( aDimName ) )
958 bInGroupDim = true; // in a group dimension
960 if ( !bFound && !bInGroupDim )
962 // create a new num group dimension if the selection is a single cell
963 // in a normal dimension with numeric content
965 ScRange aSelRange;
966 if ( (GetViewData().GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
967 aSelRange.aStart == aSelRange.aEnd )
969 if ( rDoc.HasValueData( aSelRange.aStart.Col(), aSelRange.aStart.Row(),
970 aSelRange.aStart.Tab() ) )
972 bFound = true;
973 // use currently selected value for automatic limits
974 if( rOldInfo.mbAutoStart )
975 rOldInfo.mfStart = rDoc.GetValue( aSelRange.aStart );
976 if( rOldInfo.mbAutoEnd )
977 rOldInfo.mfEnd = rDoc.GetValue( aSelRange.aStart );
984 return bFound;
987 void ScDBFunc::DateGroupDataPilot( const ScDPNumGroupInfo& rInfo, sal_Int32 nParts )
989 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
990 GetViewData().GetCurY(), GetViewData().GetTabNo() );
991 if (!pDPObj)
992 return;
994 ScDPUniqueStringSet aEntries;
995 tools::Long nSelectDimension = -1;
996 GetSelectedMemberList( aEntries, nSelectDimension );
998 if (aEntries.empty())
999 return;
1001 std::vector<OUString> aDeletedNames;
1002 bool bIsDataLayout;
1003 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1005 ScDPSaveData aData( *pDPObj->GetSaveData() );
1006 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
1008 // find the source dimension name.
1009 OUString aBaseDimName = aDimName;
1010 if( const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ) )
1011 aBaseDimName = pBaseGroupDim->GetSourceDimName();
1013 // Remove all group dimensions associated with this source dimension. For
1014 // date grouping, we need to remove all existing groups for the affected
1015 // source dimension and build new one(s) from scratch. Keep the deleted
1016 // names so that they can be reused during re-construction.
1017 aData.RemoveAllGroupDimensions(aBaseDimName, &aDeletedNames);
1019 if ( nParts )
1021 // create date group dimensions
1023 bool bFirst = true;
1024 sal_Int32 nMask = 1;
1025 for (sal_uInt16 nBit=0; nBit<32; nBit++)
1027 if ( nParts & nMask )
1029 if ( bFirst )
1031 // innermost part: create NumGroupDimension (replacing original values)
1032 // Dimension name is left unchanged
1034 if ( (nParts == sheet::DataPilotFieldGroupBy::DAYS) && (rInfo.mfStep >= 1.0) )
1036 // only days, and a step value specified: use numerical grouping
1037 // with DateValues flag, not date grouping
1039 ScDPNumGroupInfo aNumInfo( rInfo );
1040 aNumInfo.mbDateValues = true;
1042 ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, aNumInfo );
1043 pDimData->AddNumGroupDimension( aNumGroupDim );
1045 else
1047 ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, rInfo, nMask );
1048 pDimData->AddNumGroupDimension( aNumGroupDim );
1051 bFirst = false;
1053 else
1055 // additional parts: create GroupDimension (shown as additional dimensions)
1056 OUString aGroupDimName =
1057 pDimData->CreateDateGroupDimName(nMask, *pDPObj, true, &aDeletedNames);
1058 ScDPSaveGroupDimension aGroupDim( aBaseDimName, aGroupDimName );
1059 aGroupDim.SetDateInfo( rInfo, nMask );
1060 pDimData->AddGroupDimension( aGroupDim );
1062 // set orientation
1063 ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
1064 if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
1066 ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aBaseDimName );
1067 pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
1068 aData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
1072 nMask *= 2;
1076 // apply changes
1077 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1078 pDPObj->SetSaveData( aData );
1079 aFunc.RefreshPivotTableGroups(pDPObj);
1081 // unmark cell selection
1082 Unmark();
1085 void ScDBFunc::NumGroupDataPilot( const ScDPNumGroupInfo& rInfo )
1087 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1088 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1089 if (!pDPObj)
1090 return;
1092 ScDPUniqueStringSet aEntries;
1093 tools::Long nSelectDimension = -1;
1094 GetSelectedMemberList( aEntries, nSelectDimension );
1096 if (aEntries.empty())
1097 return;
1099 bool bIsDataLayout;
1100 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1102 ScDPSaveData aData( *pDPObj->GetSaveData() );
1103 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
1105 ScDPSaveNumGroupDimension* pExisting = pDimData->GetNumGroupDimAcc( aDimName );
1106 if ( pExisting )
1108 // modify existing group dimension
1109 pExisting->SetGroupInfo( rInfo );
1111 else
1113 // create new group dimension
1114 ScDPSaveNumGroupDimension aNumGroupDim( aDimName, rInfo );
1115 pDimData->AddNumGroupDimension( aNumGroupDim );
1118 // apply changes
1119 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1120 pDPObj->SetSaveData( aData );
1121 aFunc.RefreshPivotTableGroups(pDPObj);
1123 // unmark cell selection
1124 Unmark();
1127 void ScDBFunc::GroupDataPilot()
1129 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1130 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1131 if (!pDPObj)
1132 return;
1134 ScDPUniqueStringSet aEntries;
1135 tools::Long nSelectDimension = -1;
1136 GetSelectedMemberList( aEntries, nSelectDimension );
1138 if (aEntries.empty())
1139 return;
1141 bool bIsDataLayout;
1142 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1144 ScDPSaveData aData( *pDPObj->GetSaveData() );
1145 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
1147 // find original base
1148 OUString aBaseDimName = aDimName;
1149 const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName );
1150 if ( pBaseGroupDim )
1152 // any entry's SourceDimName is the original base
1153 aBaseDimName = pBaseGroupDim->GetSourceDimName();
1156 // find existing group dimension
1157 // (using the selected dim, can be intermediate group dim)
1158 ScDPSaveGroupDimension* pGroupDimension = pDimData->GetGroupDimAccForBase( aDimName );
1160 // remove the selected items from their groups
1161 // (empty groups are removed, too)
1162 if ( pGroupDimension )
1164 for (const OUString& aEntryName : aEntries)
1166 if ( pBaseGroupDim )
1168 // for each selected (intermediate) group, remove all its items
1169 // (same logic as for adding, below)
1170 const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
1171 if ( pBaseGroup )
1172 pBaseGroup->RemoveElementsFromGroups( *pGroupDimension ); // remove all elements
1173 else
1174 pGroupDimension->RemoveFromGroups( aEntryName );
1176 else
1177 pGroupDimension->RemoveFromGroups( aEntryName );
1181 std::unique_ptr<ScDPSaveGroupDimension> pNewGroupDim;
1182 if ( !pGroupDimension )
1184 // create a new group dimension
1185 OUString aGroupDimName =
1186 pDimData->CreateGroupDimName(aBaseDimName, *pDPObj, false, nullptr);
1187 pNewGroupDim.reset(new ScDPSaveGroupDimension( aBaseDimName, aGroupDimName ));
1189 pGroupDimension = pNewGroupDim.get(); // make changes to the new dim if none existed
1191 if ( pBaseGroupDim )
1193 // If it's a higher-order group dimension, pre-allocate groups for all
1194 // non-selected original groups, so the individual base members aren't
1195 // used for automatic groups (this would make the original groups hard
1196 // to find).
1197 //! Also do this when removing groups?
1198 //! Handle this case dynamically with automatic groups?
1200 tools::Long nGroupCount = pBaseGroupDim->GetGroupCount();
1201 for ( tools::Long nGroup = 0; nGroup < nGroupCount; nGroup++ )
1203 const ScDPSaveGroupItem& rBaseGroup = pBaseGroupDim->GetGroupByIndex( nGroup );
1205 if (!aEntries.count(rBaseGroup.GetGroupName()))
1207 // add an additional group for each item that is not in the selection
1208 ScDPSaveGroupItem aGroup( rBaseGroup.GetGroupName() );
1209 aGroup.AddElementsFromGroup( rBaseGroup );
1210 pGroupDimension->AddGroupItem( aGroup );
1215 OUString aGroupDimName = pGroupDimension->GetGroupDimName();
1217 OUString aGroupName = pGroupDimension->CreateGroupName(ScResId(STR_PIVOT_GROUP));
1218 ScDPSaveGroupItem aGroup( aGroupName );
1219 for (const OUString& aEntryName : aEntries)
1221 if ( pBaseGroupDim )
1223 // for each selected (intermediate) group, add all its items
1224 const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
1225 if ( pBaseGroup )
1226 aGroup.AddElementsFromGroup( *pBaseGroup );
1227 else
1228 aGroup.AddElement( aEntryName ); // no group found -> automatic group, add the item itself
1230 else
1231 aGroup.AddElement( aEntryName ); // no group dimension, add all items directly
1234 pGroupDimension->AddGroupItem( aGroup );
1236 if ( pNewGroupDim )
1238 pDimData->AddGroupDimension( *pNewGroupDim );
1239 pNewGroupDim.reset(); // AddGroupDimension copies the object
1240 // don't access pGroupDimension after here
1242 pGroupDimension = nullptr;
1244 // set orientation
1245 ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
1246 if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
1248 ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aDimName );
1249 pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
1250 aData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
1253 // apply changes
1254 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1255 pDPObj->SetSaveData( aData );
1256 aFunc.RefreshPivotTableGroups(pDPObj);
1258 // unmark cell selection
1259 Unmark();
1262 void ScDBFunc::UngroupDataPilot()
1264 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1265 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1266 if (!pDPObj)
1267 return;
1269 ScDPUniqueStringSet aEntries;
1270 tools::Long nSelectDimension = -1;
1271 GetSelectedMemberList( aEntries, nSelectDimension );
1273 if (aEntries.empty())
1274 return;
1276 bool bIsDataLayout;
1277 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1279 ScDPSaveData aData( *pDPObj->GetSaveData() );
1280 if (!aData.GetExistingDimensionData())
1281 // There is nothing to ungroup.
1282 return;
1284 ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1286 ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
1287 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
1288 if ( ( pGroupDim && pGroupDim->GetDatePart() != 0 ) ||
1289 ( pNumGroupDim && pNumGroupDim->GetDatePart() != 0 ) )
1291 // Date grouping: need to remove all affected group dimensions.
1292 // This is done using DateGroupDataPilot with nParts=0.
1294 DateGroupDataPilot( ScDPNumGroupInfo(), 0 );
1295 return;
1298 if ( pGroupDim )
1300 for (const auto& rEntry : aEntries)
1301 pGroupDim->RemoveGroup(rEntry);
1303 // remove group dimension if empty
1304 bool bEmptyDim = pGroupDim->IsEmpty();
1305 if ( !bEmptyDim )
1307 // If all remaining groups in the dimension aren't shown, remove
1308 // the dimension too, as if it was completely empty.
1309 ScDPUniqueStringSet aVisibleEntries;
1310 pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
1311 bEmptyDim = pGroupDim->HasOnlyHidden( aVisibleEntries );
1313 if ( bEmptyDim )
1315 pDimData->RemoveGroupDimension( aDimName ); // pGroupDim is deleted
1317 // also remove SaveData settings for the dimension that no longer exists
1318 aData.RemoveDimensionByName( aDimName );
1321 else if ( pNumGroupDim )
1323 // remove the numerical grouping
1324 pDimData->RemoveNumGroupDimension( aDimName );
1325 // SaveData settings can remain unchanged - the same dimension still exists
1328 // apply changes
1329 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1330 pDPObj->SetSaveData( aData );
1331 aFunc.RefreshPivotTableGroups(pDPObj);
1333 // unmark cell selection
1334 Unmark();
1337 static OUString lcl_replaceMemberNameInSubtotal(const OUString& rSubtotal, std::u16string_view rMemberName)
1339 sal_Int32 n = rSubtotal.getLength();
1340 const sal_Unicode* p = rSubtotal.getStr();
1341 OUStringBuffer aBuf, aWordBuf;
1342 for (sal_Int32 i = 0; i < n; ++i)
1344 sal_Unicode c = p[i];
1345 if (c == ' ')
1347 OUString aWord = aWordBuf.makeStringAndClear();
1348 if (aWord == rMemberName)
1349 aBuf.append('?');
1350 else
1351 aBuf.append(aWord);
1352 aBuf.append(c);
1354 else if (c == '\\')
1356 // Escape a backslash character.
1357 aWordBuf.append(c);
1358 aWordBuf.append(c);
1360 else if (c == '?')
1362 // A literal '?' must be escaped with a backslash ('\');
1363 aWordBuf.append('\\');
1364 aWordBuf.append(c);
1366 else
1367 aWordBuf.append(c);
1370 if (!aWordBuf.isEmpty())
1372 OUString aWord = aWordBuf.makeStringAndClear();
1373 if (aWord == rMemberName)
1374 aBuf.append('?');
1375 else
1376 aBuf.append(aWord);
1379 return aBuf.makeStringAndClear();
1382 void ScDBFunc::DataPilotInput( const ScAddress& rPos, const OUString& rString )
1384 using namespace ::com::sun::star::sheet;
1386 ScDocument& rDoc = GetViewData().GetDocument();
1387 ScDPObject* pDPObj = rDoc.GetDPAtCursor( rPos.Col(), rPos.Row(), rPos.Tab() );
1388 if (!pDPObj)
1389 return;
1391 OUString aOldText = rDoc.GetString(rPos.Col(), rPos.Row(), rPos.Tab());
1393 if ( aOldText == rString )
1395 // nothing to do: silently exit
1396 return;
1399 TranslateId pErrorId;
1401 pDPObj->BuildAllDimensionMembers();
1402 ScDPSaveData aData( *pDPObj->GetSaveData() );
1403 bool bChange = false;
1404 bool bNeedReloadGroups = false;
1406 DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN;
1407 tools::Long nField = pDPObj->GetHeaderDim( rPos, nOrient );
1408 if ( nField >= 0 )
1410 // changing a field title
1411 if ( aData.GetExistingDimensionData() )
1413 // only group dimensions can be renamed
1415 ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1416 ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aOldText );
1417 if ( pGroupDim )
1419 // valid name: not empty, no existing dimension (group or other)
1420 if (!rString.isEmpty() && !pDPObj->IsDimNameInUse(rString))
1422 pGroupDim->Rename( rString );
1424 // also rename in SaveData to preserve the field settings
1425 ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aOldText );
1426 pSaveDim->SetName( rString );
1428 bChange = true;
1430 else
1431 pErrorId = STR_INVALIDNAME;
1434 else if (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW)
1436 bool bDataLayout = false;
1437 OUString aDimName = pDPObj->GetDimName(nField, bDataLayout);
1438 ScDPSaveDimension* pDim = bDataLayout ? aData.GetDataLayoutDimension() : aData.GetDimensionByName(aDimName);
1439 if (pDim)
1441 if (!rString.isEmpty())
1443 if (rString.equalsIgnoreAsciiCase(aDimName))
1445 pDim->RemoveLayoutName();
1446 bChange = true;
1448 else if (!pDPObj->IsDimNameInUse(rString))
1450 pDim->SetLayoutName(rString);
1451 bChange = true;
1453 else
1454 pErrorId = STR_INVALIDNAME;
1456 else
1457 pErrorId = STR_INVALIDNAME;
1461 else if (pDPObj->IsDataDescriptionCell(rPos))
1463 // There is only one data dimension.
1464 ScDPSaveDimension* pDim = aData.GetFirstDimension(sheet::DataPilotFieldOrientation_DATA);
1465 if (pDim)
1467 if (!rString.isEmpty())
1469 if (pDim->GetName().equalsIgnoreAsciiCase(rString))
1471 pDim->RemoveLayoutName();
1472 bChange = true;
1474 else if (!pDPObj->IsDimNameInUse(rString))
1476 pDim->SetLayoutName(rString);
1477 bChange = true;
1479 else
1480 pErrorId = STR_INVALIDNAME;
1482 else
1483 pErrorId = STR_INVALIDNAME;
1486 else
1488 // This is not a field header.
1489 sheet::DataPilotTableHeaderData aPosData;
1490 pDPObj->GetHeaderPositionData(rPos, aPosData);
1492 if ((aPosData.Flags & MemberResultFlags::HASMEMBER) && !aOldText.isEmpty())
1494 if ( aData.GetExistingDimensionData() && !(aPosData.Flags & MemberResultFlags::SUBTOTAL))
1496 bool bIsDataLayout;
1497 OUString aDimName = pDPObj->GetDimName( aPosData.Dimension, bIsDataLayout );
1499 ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1500 ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
1501 if ( pGroupDim )
1503 // valid name: not empty, no existing group in this dimension
1504 //! ignore case?
1505 if (!rString.isEmpty() && !pGroupDim->GetNamedGroup(rString))
1507 ScDPSaveGroupItem* pGroup = pGroupDim->GetNamedGroupAcc( aOldText );
1508 if ( pGroup )
1509 pGroup->Rename( rString ); // rename the existing group
1510 else
1512 // create a new group to replace the automatic group
1513 ScDPSaveGroupItem aGroup( rString );
1514 aGroup.AddElement( aOldText );
1515 pGroupDim->AddGroupItem( aGroup );
1518 // in both cases also adjust savedata, to preserve member settings (show details)
1519 ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aDimName );
1520 ScDPSaveMember* pSaveMember = pSaveDim->GetExistingMemberByName( aOldText );
1521 if ( pSaveMember )
1522 pSaveMember->SetName( rString );
1524 bChange = true;
1525 bNeedReloadGroups = true;
1527 else
1528 pErrorId = STR_INVALIDNAME;
1531 else if (aPosData.Flags & MemberResultFlags::GRANDTOTAL)
1533 aData.SetGrandTotalName(rString);
1534 bChange = true;
1536 else if (aPosData.Dimension >= 0 && !aPosData.MemberName.isEmpty())
1538 bool bDataLayout = false;
1539 OUString aDimName = pDPObj->GetDimName(static_cast<tools::Long>(aPosData.Dimension), bDataLayout);
1540 if (bDataLayout)
1542 // data dimension
1545 if (aPosData.Flags & MemberResultFlags::SUBTOTAL)
1546 break;
1548 ScDPSaveDimension* pDim = aData.GetDimensionByName(aPosData.MemberName);
1549 if (!pDim)
1550 break;
1552 if (rString.isEmpty())
1554 pErrorId = STR_INVALIDNAME;
1555 break;
1558 if (aPosData.MemberName.equalsIgnoreAsciiCase(rString))
1560 pDim->RemoveLayoutName();
1561 bChange = true;
1563 else if (!pDPObj->IsDimNameInUse(rString))
1565 pDim->SetLayoutName(rString);
1566 bChange = true;
1568 else
1569 pErrorId = STR_INVALIDNAME;
1571 while (false);
1573 else
1575 // field member
1578 ScDPSaveDimension* pDim = aData.GetDimensionByName(aDimName);
1579 if (!pDim)
1580 break;
1582 ScDPSaveMember* pMem = pDim->GetExistingMemberByName(aPosData.MemberName);
1583 if (!pMem)
1584 break;
1586 if (aPosData.Flags & MemberResultFlags::SUBTOTAL)
1588 // Change subtotal only when the table has one data dimension.
1589 if (aData.GetDataDimensionCount() > 1)
1590 break;
1592 // display name for subtotal is allowed only if the subtotal type is 'Automatic'.
1593 if (pDim->GetSubTotalsCount() != 1)
1594 break;
1596 if (pDim->GetSubTotalFunc(0) != ScGeneralFunction::AUTO)
1597 break;
1599 const std::optional<OUString> & pLayoutName = pMem->GetLayoutName();
1600 OUString aMemberName;
1601 if (pLayoutName)
1602 aMemberName = *pLayoutName;
1603 else
1604 aMemberName = aPosData.MemberName;
1606 OUString aNew = lcl_replaceMemberNameInSubtotal(rString, aMemberName);
1607 pDim->SetSubtotalName(aNew);
1608 bChange = true;
1610 else
1612 // Check to make sure the member name isn't
1613 // already used.
1614 if (!rString.isEmpty())
1616 if (rString.equalsIgnoreAsciiCase(pMem->GetName()))
1618 pMem->RemoveLayoutName();
1619 bChange = true;
1621 else if (!pDim->IsMemberNameInUse(rString))
1623 pMem->SetLayoutName(rString);
1624 bChange = true;
1626 else
1627 pErrorId = STR_INVALIDNAME;
1629 else
1630 pErrorId = STR_INVALIDNAME;
1633 while (false);
1639 if ( bChange )
1641 // apply changes
1642 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1643 pDPObj->SetSaveData( aData );
1644 if (bNeedReloadGroups)
1646 ScDPCollection* pDPs = rDoc.GetDPCollection();
1647 if (pDPs)
1649 o3tl::sorted_vector<ScDPObject*> aRefs;
1650 // tdf#111305: Reload groups in cache after modifications.
1651 pDPs->ReloadGroupsInCache(pDPObj, aRefs);
1652 } // pDPs
1653 } // bNeedReloadGroups
1654 aFunc.UpdatePivotTable(*pDPObj, true, false);
1656 else
1658 if (!pErrorId)
1659 pErrorId = STR_ERR_DATAPILOT_INPUT;
1660 ErrorMessage(pErrorId);
1664 static void lcl_MoveToEnd( ScDPSaveDimension& rDim, const OUString& rItemName )
1666 std::unique_ptr<ScDPSaveMember> pNewMember;
1667 const ScDPSaveMember* pOldMember = rDim.GetExistingMemberByName( rItemName );
1668 if ( pOldMember )
1669 pNewMember.reset(new ScDPSaveMember( *pOldMember ));
1670 else
1671 pNewMember.reset(new ScDPSaveMember( rItemName ));
1672 rDim.AddMember( std::move(pNewMember) );
1673 // AddMember takes ownership of the new pointer,
1674 // puts it to the end of the list even if it was in the list before.
1677 namespace {
1679 struct ScOUStringCollate
1681 CollatorWrapper* mpCollator;
1683 explicit ScOUStringCollate(CollatorWrapper* pColl) : mpCollator(pColl) {}
1685 bool operator()(const OUString& rStr1, const OUString& rStr2) const
1687 return ( mpCollator->compareString(rStr1, rStr2) < 0 );
1693 void ScDBFunc::DataPilotSort(ScDPObject* pDPObj, tools::Long nDimIndex, bool bAscending, const sal_uInt16* pUserListId)
1695 if (!pDPObj)
1696 return;
1698 // We need to run this to get all members later.
1699 if ( pUserListId )
1700 pDPObj->BuildAllDimensionMembers();
1702 if (nDimIndex < 0)
1703 // Invalid dimension index. Bail out.
1704 return;
1706 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1707 if (!pSaveData)
1708 return;
1710 ScDPSaveData aNewSaveData(*pSaveData);
1711 bool bDataLayout;
1712 OUString aDimName = pDPObj->GetDimName(nDimIndex, bDataLayout);
1713 ScDPSaveDimension* pSaveDim = aNewSaveData.GetDimensionByName(aDimName);
1714 if (!pSaveDim)
1715 return;
1717 // manual evaluation of sort order is only needed if a user list id is given
1718 if ( pUserListId )
1720 typedef ScDPSaveDimension::MemberList MemList;
1721 const MemList& rDimMembers = pSaveDim->GetMembers();
1722 vector<OUString> aMembers;
1723 std::unordered_set<OUString> aMemberSet;
1724 size_t nMemberCount = 0;
1725 for (ScDPSaveMember* pMem : rDimMembers)
1727 aMembers.push_back(pMem->GetName());
1728 aMemberSet.insert(pMem->GetName());
1729 ++nMemberCount;
1732 // Sort the member list in ascending order.
1733 ScOUStringCollate aCollate( &ScGlobal::GetCollator() );
1734 std::stable_sort(aMembers.begin(), aMembers.end(), aCollate);
1736 // Collect and rank those custom sort strings that also exist in the member name list.
1738 typedef std::unordered_map<OUString, sal_uInt16> UserSortMap;
1739 UserSortMap aSubStrs;
1740 sal_uInt16 nSubCount = 0;
1741 ScUserList* pUserList = ScGlobal::GetUserList();
1742 if (!pUserList)
1743 return;
1746 size_t n = pUserList->size();
1747 if (!n || *pUserListId >= static_cast<sal_uInt16>(n))
1748 return;
1751 const ScUserListData& rData = (*pUserList)[*pUserListId];
1752 sal_uInt16 n = rData.GetSubCount();
1753 for (sal_uInt16 i = 0; i < n; ++i)
1755 OUString aSub = rData.GetSubStr(i);
1756 if (!aMemberSet.count(aSub))
1757 // This string doesn't exist in the member name set. Don't add this.
1758 continue;
1760 aSubStrs.emplace(aSub, nSubCount++);
1763 // Rank all members.
1765 vector<OUString> aRankedNames(nMemberCount);
1766 sal_uInt16 nCurStrId = 0;
1767 for (auto const& aMemberName : aMembers)
1769 sal_uInt16 nRank = 0;
1770 UserSortMap::const_iterator itrSub = aSubStrs.find(aMemberName);
1771 if (itrSub == aSubStrs.end())
1772 nRank = nSubCount + nCurStrId++;
1773 else
1774 nRank = itrSub->second;
1776 if (!bAscending)
1777 nRank = static_cast< sal_uInt16 >( nMemberCount - nRank - 1 );
1779 aRankedNames[nRank] = aMemberName;
1782 // Re-order ScDPSaveMember instances with the new ranks.
1783 for (auto const& aRankedName : aRankedNames)
1785 const ScDPSaveMember* pOldMem = pSaveDim->GetExistingMemberByName(aRankedName);
1786 if (!pOldMem)
1787 // All members are supposed to be present.
1788 continue;
1790 pSaveDim->AddMember(std::unique_ptr<ScDPSaveMember>(new ScDPSaveMember(*pOldMem)));
1793 // Set the sorting mode to manual for now. We may introduce a new sorting
1794 // mode later on.
1796 sheet::DataPilotFieldSortInfo aSortInfo;
1797 aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
1798 pSaveDim->SetSortInfo(&aSortInfo);
1800 else
1802 // without user list id, just apply sorting mode
1804 sheet::DataPilotFieldSortInfo aSortInfo;
1805 aSortInfo.Mode = sheet::DataPilotFieldSortMode::NAME;
1806 aSortInfo.IsAscending = bAscending;
1807 pSaveDim->SetSortInfo(&aSortInfo);
1810 // Update the datapilot with the newly sorted field members.
1812 std::unique_ptr<ScDPObject> pNewObj(new ScDPObject(*pDPObj));
1813 pNewObj->SetSaveData(aNewSaveData);
1814 ScDBDocFunc aFunc(*GetViewData().GetDocShell());
1816 aFunc.DataPilotUpdate(pDPObj, pNewObj.get(), true, false);
1819 bool ScDBFunc::DataPilotMove( const ScRange& rSource, const ScAddress& rDest )
1821 bool bRet = false;
1822 ScDocument& rDoc = GetViewData().GetDocument();
1823 ScDPObject* pDPObj = rDoc.GetDPAtCursor( rSource.aStart.Col(), rSource.aStart.Row(), rSource.aStart.Tab() );
1824 if ( pDPObj && pDPObj == rDoc.GetDPAtCursor( rDest.Col(), rDest.Row(), rDest.Tab() ) )
1826 sheet::DataPilotTableHeaderData aDestData;
1827 pDPObj->GetHeaderPositionData( rDest, aDestData );
1828 bool bValid = ( aDestData.Dimension >= 0 ); // dropping onto a field
1830 // look through the source range
1831 std::unordered_set< OUString > aMembersSet; // for lookup
1832 std::vector< OUString > aMembersVector; // members in original order, for inserting
1833 aMembersVector.reserve( std::max( static_cast<SCSIZE>( rSource.aEnd.Col() - rSource.aStart.Col() + 1 ),
1834 static_cast<SCSIZE>( rSource.aEnd.Row() - rSource.aStart.Row() + 1 ) ) );
1835 for (SCROW nRow = rSource.aStart.Row(); bValid && nRow <= rSource.aEnd.Row(); ++nRow )
1836 for (SCCOL nCol = rSource.aStart.Col(); bValid && nCol <= rSource.aEnd.Col(); ++nCol )
1838 sheet::DataPilotTableHeaderData aSourceData;
1839 pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, rSource.aStart.Tab() ), aSourceData );
1840 if ( aSourceData.Dimension == aDestData.Dimension && !aSourceData.MemberName.isEmpty() )
1842 if ( aMembersSet.insert( aSourceData.MemberName ).second )
1844 aMembersVector.push_back( aSourceData.MemberName );
1846 // duplicates are ignored
1848 else
1849 bValid = false; // empty (subtotal) or different field
1852 if ( bValid )
1854 bool bIsDataLayout;
1855 OUString aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
1856 if ( !bIsDataLayout )
1858 ScDPSaveData aData( *pDPObj->GetSaveData() );
1859 ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
1861 // get all member names in source order
1862 uno::Sequence<OUString> aMemberNames;
1863 pDPObj->GetMemberNames( aDestData.Dimension, aMemberNames );
1865 bool bInserted = false;
1867 for (const OUString& aMemberStr : std::as_const(aMemberNames))
1869 if ( !bInserted && aMemberStr == aDestData.MemberName )
1871 // insert dragged items before this item
1872 for ( const auto& rMember : aMembersVector )
1873 lcl_MoveToEnd( *pDim, rMember );
1874 bInserted = true;
1877 if ( aMembersSet.find( aMemberStr ) == aMembersSet.end() ) // skip dragged items
1878 lcl_MoveToEnd( *pDim, aMemberStr );
1880 // insert dragged item at end if dest wasn't found (for example, empty)
1881 if ( !bInserted )
1882 for ( const auto& rMember : aMembersVector )
1883 lcl_MoveToEnd( *pDim, rMember );
1885 // Items that were in SaveData, but not in the source, end up at the start of the list.
1887 // set flag for manual sorting
1888 sheet::DataPilotFieldSortInfo aSortInfo;
1889 aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
1890 pDim->SetSortInfo( &aSortInfo );
1892 // apply changes
1893 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1894 std::unique_ptr<ScDPObject> pNewObj(new ScDPObject( *pDPObj ));
1895 pNewObj->SetSaveData( aData );
1896 aFunc.DataPilotUpdate( pDPObj, pNewObj.get(), true, false ); //! bApi for drag&drop?
1897 pNewObj.reset();
1899 Unmark(); // entry was moved - no use in leaving the old cell selected
1901 bRet = true;
1906 return bRet;
1909 bool ScDBFunc::HasSelectionForDrillDown( css::sheet::DataPilotFieldOrientation& rOrientation )
1911 bool bRet = false;
1913 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1914 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1915 if ( pDPObj )
1917 ScDPUniqueStringSet aEntries;
1918 tools::Long nSelectDimension = -1;
1919 GetSelectedMemberList( aEntries, nSelectDimension );
1921 if (!aEntries.empty())
1923 bool bIsDataLayout;
1924 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1925 if ( !bIsDataLayout )
1927 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1928 ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName( aDimName );
1929 if ( pDim )
1931 css::sheet::DataPilotFieldOrientation nDimOrient = pDim->GetOrientation();
1932 ScDPSaveDimension* pInner = pSaveData->GetInnermostDimension( nDimOrient );
1933 if ( pDim == pInner )
1935 rOrientation = nDimOrient;
1936 bRet = true;
1943 return bRet;
1946 void ScDBFunc::SetDataPilotDetails(bool bShow, const OUString* pNewDimensionName)
1948 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1949 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1950 if ( !pDPObj )
1951 return;
1953 ScDPUniqueStringSet aEntries;
1954 tools::Long nSelectDimension = -1;
1955 GetSelectedMemberList( aEntries, nSelectDimension );
1957 if (aEntries.empty())
1958 return;
1960 bool bIsDataLayout;
1961 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1962 if ( bIsDataLayout )
1963 return;
1965 ScDPSaveData aData( *pDPObj->GetSaveData() );
1966 ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
1968 if ( bShow && pNewDimensionName )
1970 // add the new dimension with the same orientation, at the end
1972 ScDPSaveDimension* pNewDim = aData.GetDimensionByName( *pNewDimensionName );
1973 ScDPSaveDimension* pDuplicated = nullptr;
1974 if ( pNewDim->GetOrientation() == sheet::DataPilotFieldOrientation_DATA )
1976 // Need to duplicate the dimension, create column/row in addition to data:
1977 // The duplicated dimension inherits the existing settings, pNewDim is modified below.
1978 pDuplicated = aData.DuplicateDimension( *pNewDimensionName );
1981 css::sheet::DataPilotFieldOrientation nOrientation = pDim->GetOrientation();
1982 pNewDim->SetOrientation( nOrientation );
1984 tools::Long nPosition = LONG_MAX;
1985 aData.SetPosition( pNewDim, nPosition );
1987 ScDPSaveDimension* pDataLayout = aData.GetDataLayoutDimension();
1988 if ( pDataLayout->GetOrientation() == nOrientation &&
1989 aData.GetDataDimensionCount() <= 1 )
1991 // If there is only one data dimension, the data layout dimension
1992 // must still be the last one in its orientation.
1993 aData.SetPosition( pDataLayout, nPosition );
1996 if ( pDuplicated )
1998 // The duplicated (data) dimension needs to be behind the original dimension
1999 aData.SetPosition( pDuplicated, nPosition );
2002 // Hide details for all visible members (selected are changed below).
2003 //! Use all members from source level instead (including non-visible)?
2005 ScDPUniqueStringSet aVisibleEntries;
2006 pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
2008 for (const OUString& aVisName : aVisibleEntries)
2010 ScDPSaveMember* pMember = pDim->GetMemberByName( aVisName );
2011 pMember->SetShowDetails( false );
2015 for (const auto& rEntry : aEntries)
2017 ScDPSaveMember* pMember = pDim->GetMemberByName(rEntry);
2018 pMember->SetShowDetails( bShow );
2021 // apply changes
2022 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
2023 std::unique_ptr<ScDPObject> pNewObj(new ScDPObject( *pDPObj ));
2024 pNewObj->SetSaveData( aData );
2025 aFunc.DataPilotUpdate( pDPObj, pNewObj.get(), true, false );
2026 pNewObj.reset();
2028 // unmark cell selection
2029 Unmark();
2032 void ScDBFunc::ShowDataPilotSourceData( ScDPObject& rDPObj, const Sequence<sheet::DataPilotFieldFilter>& rFilters )
2034 ScDocument& rDoc = GetViewData().GetDocument();
2035 if (rDoc.GetDocumentShell()->IsReadOnly())
2037 ErrorMessage(STR_READONLYERR);
2038 return;
2041 Reference<sheet::XDimensionsSupplier> xDimSupplier = rDPObj.GetSource();
2042 Reference<container::XNameAccess> xDims = xDimSupplier->getDimensions();
2043 Reference<sheet::XDrillDownDataSupplier> xDDSupplier(xDimSupplier, UNO_QUERY);
2044 if (!xDDSupplier.is())
2045 return;
2047 Sequence< Sequence<Any> > aTabData = xDDSupplier->getDrillDownData(rFilters);
2048 sal_Int32 nRowSize = aTabData.getLength();
2049 if (nRowSize <= 1)
2050 // There is no data to show. Bail out.
2051 return;
2053 SCCOL nColSize = aTabData[0].getLength();
2055 SCTAB nNewTab = GetViewData().GetTabNo();
2057 ScDocumentUniquePtr pInsDoc(new ScDocument(SCDOCMODE_CLIP));
2058 pInsDoc->ResetClip( &rDoc, nNewTab );
2059 for (SCROW nRow = 0; nRow < nRowSize; ++nRow)
2061 for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
2063 const Any& rAny = aTabData[nRow][nCol];
2064 OUString aStr;
2065 double fVal;
2066 if (rAny >>= aStr)
2068 pInsDoc->SetString(ScAddress(nCol,nRow,nNewTab), aStr);
2070 else if (rAny >>= fVal)
2071 pInsDoc->SetValue(nCol, nRow, nNewTab, fVal);
2075 // set number format (important for dates)
2076 for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
2078 OUString aStr;
2079 if (!(aTabData[0][nCol] >>= aStr))
2080 continue;
2082 Reference<XPropertySet> xPropSet(xDims->getByName(aStr), UNO_QUERY);
2083 if (!xPropSet.is())
2084 continue;
2086 Any any = xPropSet->getPropertyValue( SC_UNO_DP_NUMBERFO );
2087 sal_Int32 nNumFmt = 0;
2088 if (!(any >>= nNumFmt))
2089 continue;
2091 ScPatternAttr aPattern( pInsDoc->GetPool() );
2092 aPattern.GetItemSet().Put( SfxUInt32Item(ATTR_VALUE_FORMAT, static_cast<sal_uInt32>(nNumFmt)) );
2093 pInsDoc->ApplyPatternAreaTab(nCol, 1, nCol, nRowSize-1, nNewTab, aPattern);
2096 SCCOL nEndCol = 0;
2097 SCROW nEndRow = 0;
2098 pInsDoc->GetCellArea( nNewTab, nEndCol, nEndRow );
2099 pInsDoc->SetClipArea( ScRange( 0, 0, nNewTab, nEndCol, nEndRow, nNewTab ) );
2101 SfxUndoManager* pMgr = GetViewData().GetDocShell()->GetUndoManager();
2102 OUString aUndo = ScResId( STR_UNDO_DOOUTLINE );
2103 pMgr->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2105 OUString aNewTabName;
2106 rDoc.CreateValidTabName(aNewTabName);
2107 if ( InsertTable(aNewTabName, nNewTab) )
2108 PasteFromClip( InsertDeleteFlags::ALL, pInsDoc.get() );
2110 pMgr->LeaveListAction();
2113 // repeat data base operations (sorting, filtering, subtotals)
2115 void ScDBFunc::RepeatDB( bool bRecord )
2117 SCCOL nCurX = GetViewData().GetCurX();
2118 SCROW nCurY = GetViewData().GetCurY();
2119 SCTAB nTab = GetViewData().GetTabNo();
2120 ScDocument& rDoc = GetViewData().GetDocument();
2121 ScDBData* pDBData = GetDBData();
2122 if (bRecord && !rDoc.IsUndoEnabled())
2123 bRecord = false;
2125 ScQueryParam aQueryParam;
2126 pDBData->GetQueryParam( aQueryParam );
2127 bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
2129 ScSortParam aSortParam;
2130 pDBData->GetSortParam( aSortParam );
2131 bool bSort = aSortParam.maKeyState[0].bDoSort;
2133 ScSubTotalParam aSubTotalParam;
2134 pDBData->GetSubTotalParam( aSubTotalParam );
2135 bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
2137 if ( bQuery || bSort || bSubTotal )
2139 bool bQuerySize = false;
2140 ScRange aOldQuery;
2141 ScRange aNewQuery;
2142 if (bQuery && !aQueryParam.bInplace)
2144 ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
2145 aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
2146 if (pDest && pDest->IsDoSize())
2148 pDest->GetArea( aOldQuery );
2149 bQuerySize = true;
2153 SCTAB nDummy;
2154 SCCOL nStartCol;
2155 SCROW nStartRow;
2156 SCCOL nEndCol;
2157 SCROW nEndRow;
2158 pDBData->GetArea( nDummy, nStartCol, nStartRow, nEndCol, nEndRow );
2160 //! undo only needed data ?
2162 ScDocumentUniquePtr pUndoDoc;
2163 std::unique_ptr<ScOutlineTable> pUndoTab;
2164 std::unique_ptr<ScRangeName> pUndoRange;
2165 std::unique_ptr<ScDBCollection> pUndoDB;
2167 if (bRecord)
2169 SCTAB nTabCount = rDoc.GetTableCount();
2170 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2171 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
2172 if (pTable)
2174 pUndoTab.reset(new ScOutlineTable( *pTable ));
2176 SCCOLROW nOutStartCol; // row/column status
2177 SCCOLROW nOutStartRow;
2178 SCCOLROW nOutEndCol;
2179 SCCOLROW nOutEndRow;
2180 pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
2181 pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
2183 pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
2184 rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
2185 rDoc.CopyToDocument( 0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
2187 else
2188 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
2190 // Record data range - including filter results
2191 rDoc.CopyToDocument( 0,nStartRow,nTab, rDoc.MaxCol(),nEndRow,nTab, InsertDeleteFlags::ALL, false, *pUndoDoc );
2193 // all formulas for reference
2194 rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1, InsertDeleteFlags::FORMULA, false, *pUndoDoc );
2196 // data base and other ranges
2197 ScRangeName* pDocRange = rDoc.GetRangeName();
2198 if (!pDocRange->empty())
2199 pUndoRange.reset(new ScRangeName( *pDocRange ));
2200 ScDBCollection* pDocDB = rDoc.GetDBCollection();
2201 if (!pDocDB->empty())
2202 pUndoDB.reset(new ScDBCollection( *pDocDB ));
2205 if (bSort && bSubTotal)
2207 // sort without subtotals
2209 aSubTotalParam.bRemoveOnly = true; // is reset below
2210 DoSubTotals( aSubTotalParam, false );
2213 if (bSort)
2215 pDBData->GetSortParam( aSortParam ); // range may have changed
2216 Sort( aSortParam, false, false);
2218 if (bQuery)
2220 pDBData->GetQueryParam( aQueryParam ); // range may have changed
2221 ScRange aAdvSource;
2222 if (pDBData->GetAdvancedQuerySource(aAdvSource))
2224 rDoc.CreateQueryParam(aAdvSource, aQueryParam);
2225 Query( aQueryParam, &aAdvSource, false );
2227 else
2228 Query( aQueryParam, nullptr, false );
2230 // if not inplace the sheet may have changed
2231 if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
2232 SetTabNo( nTab );
2234 if (bSubTotal)
2236 pDBData->GetSubTotalParam( aSubTotalParam ); // range may have changed
2237 aSubTotalParam.bRemoveOnly = false;
2238 DoSubTotals( aSubTotalParam, false );
2241 if (bRecord)
2243 SCTAB nDummyTab;
2244 SCCOL nDummyCol;
2245 SCROW nDummyRow, nNewEndRow;
2246 pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
2248 const ScRange* pOld = nullptr;
2249 const ScRange* pNew = nullptr;
2250 if (bQuerySize)
2252 ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
2253 aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
2254 if (pDest)
2256 pDest->GetArea( aNewQuery );
2257 pOld = &aOldQuery;
2258 pNew = &aNewQuery;
2262 GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
2263 std::make_unique<ScUndoRepeatDB>( GetViewData().GetDocShell(), nTab,
2264 nStartCol, nStartRow, nEndCol, nEndRow,
2265 nNewEndRow,
2266 nCurX, nCurY,
2267 std::move(pUndoDoc), std::move(pUndoTab),
2268 std::move(pUndoRange), std::move(pUndoDB),
2269 pOld, pNew ) );
2272 GetViewData().GetDocShell()->PostPaint(
2273 ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
2274 PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
2276 else // "no not execute any operations"
2277 ErrorMessage(STR_MSSG_REPEATDB_0);
2280 void ScDBFunc::OnLOKShowHideColRow(bool bColumns, SCCOLROW nStart)
2282 if (!comphelper::LibreOfficeKit::isActive())
2283 return;
2285 SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
2286 SfxViewShell* pThisViewShell = GetViewData().GetViewShell();
2287 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
2288 while (pViewShell)
2290 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
2291 if (pTabViewShell && pTabViewShell->GetDocId() == pThisViewShell->GetDocId())
2293 if (bColumns)
2295 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
2296 pPosHelper->invalidateByIndex(nStart);
2298 else
2300 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
2301 pPosHelper->invalidateByIndex(nStart);
2304 if (pTabViewShell->getPart() == nCurrentTabIndex)
2306 pTabViewShell->ShowCursor();
2307 pTabViewShell->MarkDataChanged();
2310 pViewShell = SfxViewShell::GetNext(*pViewShell);
2314 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */