tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / view / dbfunc3.cxx
blob515a99add7188ab7f7ac1db1705725220c800739
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 <patattr.hxx>
56 #include <unonames.hxx>
57 #include <userlist.hxx>
58 #include <queryentry.hxx>
59 #include <markdata.hxx>
60 #include <tabvwsh.hxx>
61 #include <generalfunction.hxx>
62 #include <sortparam.hxx>
64 #include <comphelper/lok.hxx>
65 #include <osl/diagnose.h>
67 #include <memory>
68 #include <string_view>
69 #include <unordered_set>
70 #include <unordered_map>
71 #include <vector>
72 #include <algorithm>
74 using namespace com::sun::star;
75 using ::com::sun::star::uno::Any;
76 using ::com::sun::star::uno::Sequence;
77 using ::com::sun::star::uno::Reference;
78 using ::com::sun::star::uno::UNO_QUERY;
79 using ::com::sun::star::beans::XPropertySet;
80 using ::com::sun::star::container::XNameAccess;
81 using ::com::sun::star::sheet::XDimensionsSupplier;
82 using ::std::vector;
84 // outliner
86 // create outline grouping
88 void ScDBFunc::MakeOutline( bool bColumns, bool bRecord )
90 ScRange aRange;
91 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
93 ScDocShell* pDocSh = GetViewData().GetDocShell();
94 ScOutlineDocFunc aFunc(*pDocSh);
95 aFunc.MakeOutline( aRange, bColumns, bRecord, false );
97 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), bColumns ? COLUMN_HEADER : ROW_HEADER, GetViewData().GetTabNo());
98 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
99 bColumns, !bColumns, false /* bSizes*/,
100 false /* bHidden */, false /* bFiltered */,
101 true /* bGroups */, GetViewData().GetTabNo());
103 else
104 ErrorMessage(STR_NOMULTISELECT);
107 // delete outline grouping
109 void ScDBFunc::RemoveOutline( bool bColumns, bool bRecord )
111 ScRange aRange;
112 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
114 ScDocShell* pDocSh = GetViewData().GetDocShell();
115 ScOutlineDocFunc aFunc(*pDocSh);
116 aFunc.RemoveOutline( aRange, bColumns, bRecord, false );
118 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), bColumns ? COLUMN_HEADER : ROW_HEADER, GetViewData().GetTabNo());
119 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
120 bColumns, !bColumns, false /* bSizes*/,
121 true /* bHidden */, true /* bFiltered */,
122 true /* bGroups */, GetViewData().GetTabNo());
124 else
125 ErrorMessage(STR_NOMULTISELECT);
128 // menu status: delete outlines
130 void ScDBFunc::TestRemoveOutline( bool& rCol, bool& rRow )
132 bool bColFound = false;
133 bool bRowFound = false;
135 SCCOL nStartCol, nEndCol;
136 SCROW nStartRow, nEndRow;
137 SCTAB nStartTab, nEndTab;
138 if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
140 SCTAB nTab = nStartTab;
141 ScDocument& rDoc = GetViewData().GetDocument();
142 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
143 if (pTable)
145 ScOutlineEntry* pEntry;
146 SCCOLROW nStart;
147 SCCOLROW nEnd;
148 bool bColMarked = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
149 bool bRowMarked = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
151 // columns
153 if ( !bRowMarked || bColMarked ) // not when entire rows are marked
155 ScOutlineArray& rArray = pTable->GetColArray();
156 ScSubOutlineIterator aColIter( &rArray );
157 while (!bColFound)
159 pEntry=aColIter.GetNext();
160 if (!pEntry)
161 break;
162 nStart = pEntry->GetStart();
163 nEnd = pEntry->GetEnd();
164 if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
165 bColFound = true;
169 // rows
171 if ( !bColMarked || bRowMarked ) // not when entire columns are marked
173 ScOutlineArray& rArray = pTable->GetRowArray();
174 ScSubOutlineIterator aRowIter( &rArray );
175 while (!bRowFound)
177 pEntry=aRowIter.GetNext();
178 if (!pEntry)
179 break;
180 nStart = pEntry->GetStart();
181 nEnd = pEntry->GetEnd();
182 if ( nStartRow<=nEnd && nEndRow>=nStart )
183 bRowFound = true;
189 rCol = bColFound;
190 rRow = bRowFound;
193 void ScDBFunc::RemoveAllOutlines( bool bRecord )
195 SCTAB nTab = GetViewData().GetTabNo();
196 ScDocShell* pDocSh = GetViewData().GetDocShell();
197 ScOutlineDocFunc aFunc(*pDocSh);
199 bool bOk = aFunc.RemoveAllOutlines( nTab, bRecord );
201 if (bOk)
203 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
204 true /* bColumns */, true /* bRows */, false /* bSizes*/,
205 true /* bHidden */, true /* bFiltered */,
206 true /* bGroups */, nTab);
207 UpdateScrollBars(BOTH_HEADERS);
211 // auto outlines
213 void ScDBFunc::AutoOutline( )
215 ScDocument& rDoc = GetViewData().GetDocument();
216 SCTAB nTab = GetViewData().GetTabNo();
217 ScRange aRange( 0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab ); // the complete sheet, if nothing is marked
218 ScMarkData& rMark = GetViewData().GetMarkData();
219 if ( rMark.IsMarked() || rMark.IsMultiMarked() )
221 rMark.MarkToMulti();
222 aRange = rMark.GetMultiMarkArea();
225 ScDocShell* pDocSh = GetViewData().GetDocShell();
226 ScOutlineDocFunc aFunc(*pDocSh);
227 aFunc.AutoOutline( aRange, true );
230 // select outline level
232 void ScDBFunc::SelectLevel( bool bColumns, sal_uInt16 nLevel, bool bRecord )
234 SCTAB nTab = GetViewData().GetTabNo();
235 ScDocShell* pDocSh = GetViewData().GetDocShell();
236 ScOutlineDocFunc aFunc(*pDocSh);
238 bool bOk = aFunc.SelectLevel( nTab, bColumns, nLevel, bRecord, true/*bPaint*/ );
240 if (bOk)
242 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
243 bColumns, !bColumns, false /* bSizes*/,
244 true /* bHidden */, true /* bFiltered */,
245 true /* bGroups */, nTab);
246 UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
250 // show individual outline groups
252 void ScDBFunc::SetOutlineState( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bHidden)
254 const sal_uInt16 nHeadEntry = static_cast< sal_uInt16 >( -1 );
255 if ( nEntry == nHeadEntry)
256 SelectLevel( bColumns, sal::static_int_cast<sal_uInt16>(nLevel) );
257 else
259 if ( !bHidden )
260 ShowOutline( bColumns, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
261 else
262 HideOutline( bColumns, sal::static_int_cast<sal_uInt16>(nLevel), sal::static_int_cast<sal_uInt16>(nEntry) );
266 void ScDBFunc::ShowOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bRecord, bool bPaint )
268 SCTAB nTab = GetViewData().GetTabNo();
269 ScDocShell* pDocSh = GetViewData().GetDocShell();
270 ScOutlineDocFunc aFunc(*pDocSh);
272 aFunc.ShowOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint );
274 if ( bPaint )
276 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
277 bColumns, !bColumns, false /* bSizes*/,
278 true /* bHidden */, true /* bFiltered */,
279 true /* bGroups */, nTab);
280 UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
284 // hide individual outline groups
286 void ScDBFunc::HideOutline( bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, bool bRecord, bool bPaint )
288 SCTAB nTab = GetViewData().GetTabNo();
289 ScDocShell* pDocSh = GetViewData().GetDocShell();
290 ScOutlineDocFunc aFunc(*pDocSh);
292 bool bOk = aFunc.HideOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint );
294 if ( bOk && bPaint )
296 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
297 bColumns, !bColumns, false /* bSizes*/,
298 true /* bHidden */, true /* bFiltered */,
299 true /* bGroups */, nTab);
300 UpdateScrollBars(bColumns ? COLUMN_HEADER : ROW_HEADER);
304 // menu status: show/hide marked range
306 bool ScDBFunc::OutlinePossible(bool bHide)
308 bool bEnable = false;
310 SCCOL nStartCol;
311 SCROW nStartRow;
312 SCTAB nStartTab;
313 SCCOL nEndCol;
314 SCROW nEndRow;
315 SCTAB nEndTab;
317 if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
319 ScDocument& rDoc = GetViewData().GetDocument();
320 SCTAB nTab = GetViewData().GetTabNo();
321 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
322 if (pTable)
324 SCCOLROW nStart;
325 SCCOLROW nEnd;
327 // columns
329 ScOutlineArray& rColArray = pTable->GetColArray();
330 ScSubOutlineIterator aColIter( &rColArray );
331 while (!bEnable)
333 ScOutlineEntry* pEntry = aColIter.GetNext();
334 if (!pEntry)
335 break;
336 nStart = pEntry->GetStart();
337 nEnd = pEntry->GetEnd();
338 if ( bHide )
340 if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
341 if (!pEntry->IsHidden())
342 bEnable = true;
344 else
346 if ( nStart>=nStartCol && nEnd<=nEndCol )
347 if (pEntry->IsHidden())
348 bEnable = true;
352 // rows
354 ScOutlineArray& rRowArray = pTable->GetRowArray();
355 ScSubOutlineIterator aRowIter( &rRowArray );
356 for (;;)
358 ScOutlineEntry* pEntry = aRowIter.GetNext();
359 if (!pEntry)
360 break;
361 nStart = pEntry->GetStart();
362 nEnd = pEntry->GetEnd();
363 if ( bHide )
365 if ( nStartRow<=nEnd && nEndRow>=nStart )
366 if (!pEntry->IsHidden())
367 bEnable = true;
369 else
371 if ( nStart>=nStartRow && nEnd<=nEndRow )
372 if (pEntry->IsHidden())
373 bEnable = true;
379 return bEnable;
382 // show marked range
384 void ScDBFunc::ShowMarkedOutlines( bool bRecord )
386 ScRange aRange;
387 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
389 ScDocShell* pDocSh = GetViewData().GetDocShell();
390 ScOutlineDocFunc aFunc(*pDocSh);
391 bool bDone = aFunc.ShowMarkedOutlines( aRange, bRecord );
392 if (bDone)
394 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
395 GetViewData().GetViewShell(), true, true,
396 false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
397 true /* bGroups */, GetViewData().GetTabNo());
398 UpdateScrollBars();
401 else
402 ErrorMessage(STR_NOMULTISELECT);
405 // hide marked range
407 void ScDBFunc::HideMarkedOutlines( bool bRecord )
409 ScRange aRange;
410 if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE)
412 ScDocShell* pDocSh = GetViewData().GetDocShell();
413 ScOutlineDocFunc aFunc(*pDocSh);
414 bool bDone = aFunc.HideMarkedOutlines( aRange, bRecord );
415 if (bDone)
417 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
418 GetViewData().GetViewShell(), true, true,
419 false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
420 true /* bGroups */, GetViewData().GetTabNo());
421 UpdateScrollBars();
424 else
425 ErrorMessage(STR_NOMULTISELECT);
428 // sub totals
430 void ScDBFunc::DoSubTotals( const ScSubTotalParam& rParam, bool bRecord,
431 const ScSortParam* pForceNewSort )
433 bool bDo = !rParam.bRemoveOnly; // sal_False = only delete
435 ScDocShell* pDocSh = GetViewData().GetDocShell();
436 ScDocument& rDoc = pDocSh->GetDocument();
437 ScMarkData& rMark = GetViewData().GetMarkData();
438 SCTAB nTab = GetViewData().GetTabNo();
439 if (bRecord && !rDoc.IsUndoEnabled())
440 bRecord = false;
442 ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
443 rParam.nCol2, rParam.nRow2 );
444 if (!pDBData)
446 OSL_FAIL( "SubTotals: no DBData" );
447 return;
450 ScEditableTester aTester( rDoc, nTab, 0,rParam.nRow1+1, rDoc.MaxCol(),rDoc.MaxRow() );
451 if (!aTester.IsEditable())
453 ErrorMessage(aTester.GetMessageId());
454 return;
457 if (rDoc.HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
458 rParam.nCol2, rParam.nRow2, nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
460 ErrorMessage(STR_MSSG_INSERTCELLS_0); // do not insert into merged
461 return;
464 weld::WaitObject aWait(GetViewData().GetDialogParent());
465 bool bOk = true;
466 if (rParam.bReplace)
468 if (rDoc.TestRemoveSubTotals( nTab, rParam ))
470 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
471 VclMessageType::Question, VclButtonsType::YesNo,
472 ScResId(STR_MSSG_DOSUBTOTALS_1))); // "delete data?"
473 xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
474 xBox->set_default_response(RET_YES);
475 bOk = xBox->run() == RET_YES;
479 if (!bOk)
480 return;
482 ScDocShellModificator aModificator( *pDocSh );
484 ScSubTotalParam aNewParam( rParam ); // change end of range
485 ScDocumentUniquePtr pUndoDoc;
486 std::unique_ptr<ScOutlineTable> pUndoTab;
487 std::unique_ptr<ScRangeName> pUndoRange;
488 std::unique_ptr<ScDBCollection> pUndoDB;
490 if (bRecord) // record old data
492 bool bOldFilter = bDo && rParam.bDoSort;
493 SCTAB nTabCount = rDoc.GetTableCount();
494 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
495 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
496 if (pTable)
498 pUndoTab.reset(new ScOutlineTable( *pTable ));
500 SCCOLROW nOutStartCol; // row/column status
501 SCCOLROW nOutStartRow;
502 SCCOLROW nOutEndCol;
503 SCCOLROW nOutEndRow;
504 pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
505 pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
507 pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
508 rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
509 rDoc.CopyToDocument( 0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
511 else
512 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, bOldFilter );
514 // record data range - including filter results
515 rDoc.CopyToDocument( 0,rParam.nRow1+1,nTab, rDoc.MaxCol(),rParam.nRow2,nTab,
516 InsertDeleteFlags::ALL, false, *pUndoDoc );
518 // all formulas for reference
519 rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1,
520 InsertDeleteFlags::FORMULA, false, *pUndoDoc );
522 // database and other ranges
523 ScRangeName* pDocRange = rDoc.GetRangeName();
524 if (!pDocRange->empty())
525 pUndoRange.reset(new ScRangeName( *pDocRange ));
526 ScDBCollection* pDocDB = rDoc.GetDBCollection();
527 if (!pDocDB->empty())
528 pUndoDB.reset(new ScDBCollection( *pDocDB ));
531 ScOutlineTable* pOut = rDoc.GetOutlineTable( nTab );
532 if (pOut)
534 // Remove all existing outlines in the specified range.
535 ScOutlineArray& rRowArray = pOut->GetRowArray();
536 sal_uInt16 nDepth = rRowArray.GetDepth();
537 for (sal_uInt16 i = 0; i < nDepth; ++i)
539 bool bSize;
540 rRowArray.Remove(aNewParam.nRow1, aNewParam.nRow2, bSize);
544 if (rParam.bReplace)
545 rDoc.RemoveSubTotals( nTab, aNewParam );
546 bool bSuccess = true;
547 if (bDo)
549 // Sort
550 if ( rParam.bDoSort || pForceNewSort )
552 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
554 // set subtotal fields before sorting
555 // (duplicate values are dropped, so that they can be called again)
557 ScSortParam aOldSort;
558 pDBData->GetSortParam( aOldSort );
559 ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
560 Sort( aSortParam, false, false );
563 bSuccess = rDoc.DoSubTotals( nTab, aNewParam );
565 ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
566 aNewParam.nCol2, aNewParam.nRow2, nTab );
567 rDoc.SetDirty( aDirtyRange, true );
569 if (bRecord)
571 pDocSh->GetUndoManager()->AddUndoAction(
572 std::make_unique<ScUndoSubTotals>( pDocSh, nTab,
573 rParam, aNewParam.nRow2,
574 std::move(pUndoDoc), std::move(pUndoTab), // pUndoDBData,
575 std::move(pUndoRange), std::move(pUndoDB) ) );
578 if (!bSuccess)
580 // "Can not insert any rows"
581 ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
584 // store
585 pDBData->SetSubTotalParam( aNewParam );
586 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
587 rDoc.CompileDBFormula();
589 const ScRange aMarkRange( aNewParam.nCol1, aNewParam.nRow1, nTab, aNewParam.nCol2, aNewParam.nRow2, nTab);
590 DoneBlockMode();
591 InitOwnBlockMode( aMarkRange );
592 rMark.SetMarkArea( aMarkRange );
593 MarkDataChanged();
595 pDocSh->PostPaint(ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
596 PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
598 aModificator.SetDocumentModified();
600 SelectionChanged();
603 // consolidate
605 void ScDBFunc::Consolidate( const ScConsolidateParam& rParam )
607 ScDocShell* pDocShell = GetViewData().GetDocShell();
608 pDocShell->DoConsolidate( rParam );
609 SetTabNo( rParam.nTab, true );
612 // pivot
614 static OUString lcl_MakePivotTabName( std::u16string_view rPrefix, SCTAB nNumber )
616 OUString aName = rPrefix + OUString::number( nNumber );
617 return aName;
620 bool ScDBFunc::MakePivotTable(
621 const ScDPSaveData& rData, const ScRange& rDest, bool bNewTable,
622 const ScDPObject& rSource )
624 // error message if no fields are set
625 // this must be removed when drag&drop of fields from a toolbox is available
627 if ( rData.IsEmpty() )
629 ErrorMessage(STR_PIVOT_NODATA);
630 return false;
633 ScDocShell* pDocSh = GetViewData().GetDocShell();
634 ScDocument& rDoc = GetViewData().GetDocument();
635 bool bUndo = rDoc.IsUndoEnabled();
637 ScRange aDestRange = rDest;
638 if ( bNewTable )
640 SCTAB nSrcTab = GetViewData().GetTabNo();
642 OUString aName( ScResId(STR_PIVOT_TABLE) );
643 OUString aStr;
645 rDoc.GetName( nSrcTab, aStr );
646 aName += "_" + aStr + "_";
648 SCTAB nNewTab = nSrcTab+1;
650 SCTAB i=1;
651 while ( !rDoc.InsertTab( nNewTab, lcl_MakePivotTabName( aName, i ) ) && i <= MAXTAB )
652 i++;
654 bool bAppend = ( nNewTab+1 == rDoc.GetTableCount() );
655 if (bUndo)
657 pDocSh->GetUndoManager()->AddUndoAction(
658 std::make_unique<ScUndoInsertTab>( pDocSh, nNewTab, bAppend, lcl_MakePivotTabName( aName, i ) ));
661 GetViewData().InsertTab( nNewTab );
662 SetTabNo(nNewTab, true);
664 aDestRange = ScRange( 0, 0, nNewTab );
667 ScDPObject* pDPObj = rDoc.GetDPAtCursor(
668 aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aStart.Tab() );
670 ScDPObject aObj( rSource );
671 aObj.SetOutRange( aDestRange );
672 if ( pDPObj && !rData.GetExistingDimensionData() )
674 // copy dimension data from old object - lost in the dialog
675 //! change the dialog to keep the dimension data
677 ScDPSaveData aNewData( rData );
678 const ScDPSaveData* pOldData = pDPObj->GetSaveData();
679 if ( pOldData )
681 const ScDPDimensionSaveData* pDimSave = pOldData->GetExistingDimensionData();
682 aNewData.SetDimensionData( pDimSave );
684 aObj.SetSaveData( aNewData );
686 else
687 aObj.SetSaveData( rData );
689 bool bAllowMove = (pDPObj != nullptr); // allow re-positioning when editing existing table
691 ScDBDocFunc aFunc( *pDocSh );
692 bool bSuccess = aFunc.DataPilotUpdate(pDPObj, &aObj, true, false, bAllowMove);
694 CursorPosChanged(); // shells may be switched
696 if ( bNewTable )
698 pDocSh->PostPaintExtras();
699 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
702 return bSuccess;
705 void ScDBFunc::DeletePivotTable()
707 ScDocShell* pDocSh = GetViewData().GetDocShell();
708 ScDocument& rDoc = pDocSh->GetDocument();
709 ScDPObject* pDPObj = rDoc.GetDPAtCursor( GetViewData().GetCurX(),
710 GetViewData().GetCurY(),
711 GetViewData().GetTabNo() );
712 if ( pDPObj )
714 ScDBDocFunc aFunc( *pDocSh );
715 aFunc.RemovePivotTable(*pDPObj, true, false);
716 CursorPosChanged(); // shells may be switched
718 else
719 ErrorMessage(STR_PIVOT_NOTFOUND);
722 void ScDBFunc::RecalcPivotTable()
724 ScDocShell* pDocSh = GetViewData().GetDocShell();
725 ScDocument& rDoc = GetViewData().GetDocument();
727 ScDPObject* pDPObj = rDoc.GetDPAtCursor( GetViewData().GetCurX(),
728 GetViewData().GetCurY(),
729 GetViewData().GetTabNo() );
730 if (pDPObj)
732 // Remove existing data cache for the data that this datapilot uses,
733 // to force re-build data cache.
734 ScDBDocFunc aFunc(*pDocSh);
735 aFunc.RefreshPivotTables(pDPObj, false);
737 CursorPosChanged(); // shells may be switched
739 else
740 ErrorMessage(STR_PIVOT_NOTFOUND);
743 void ScDBFunc::GetSelectedMemberList(ScDPUniqueStringSet& rEntries, tools::Long& rDimension)
745 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
746 GetViewData().GetCurY(), GetViewData().GetTabNo() );
747 if ( !pDPObj )
748 return;
750 tools::Long nStartDimension = -1;
751 tools::Long nStartHierarchy = -1;
752 tools::Long nStartLevel = -1;
754 ScRangeListRef xRanges;
755 GetViewData().GetMultiArea( xRanges ); // incl. cursor if nothing is selected
756 size_t nRangeCount = xRanges->size();
757 bool bContinue = true;
759 for (size_t nRangePos=0; nRangePos < nRangeCount && bContinue; nRangePos++)
761 ScRange const & rRange = (*xRanges)[nRangePos];
762 SCCOL nStartCol = rRange.aStart.Col();
763 SCROW nStartRow = rRange.aStart.Row();
764 SCCOL nEndCol = rRange.aEnd.Col();
765 SCROW nEndRow = rRange.aEnd.Row();
766 SCTAB nTab = rRange.aStart.Tab();
768 for (SCROW nRow=nStartRow; nRow<=nEndRow && bContinue; nRow++)
769 for (SCCOL nCol=nStartCol; nCol<=nEndCol && bContinue; nCol++)
771 sheet::DataPilotTableHeaderData aData;
772 pDPObj->GetHeaderPositionData(ScAddress(nCol, nRow, nTab), aData);
773 if ( aData.Dimension < 0 )
774 bContinue = false; // not part of any dimension
775 else
777 if ( nStartDimension < 0 ) // first member?
779 nStartDimension = aData.Dimension;
780 nStartHierarchy = aData.Hierarchy;
781 nStartLevel = aData.Level;
783 if ( aData.Dimension != nStartDimension ||
784 aData.Hierarchy != nStartHierarchy ||
785 aData.Level != nStartLevel )
787 bContinue = false; // cannot mix dimensions
790 if ( bContinue )
792 // accept any part of a member description, also subtotals,
793 // but don't stop if empty parts are contained
794 if ( aData.Flags & sheet::MemberResultFlags::HASMEMBER )
795 rEntries.insert(aData.MemberName);
800 rDimension = nStartDimension; // dimension from which the found members came
801 if (!bContinue)
802 rEntries.clear(); // remove all if not valid
805 bool ScDBFunc::HasSelectionForDateGroup( ScDPNumGroupInfo& rOldInfo, sal_Int32& rParts )
807 // determine if the date group dialog has to be shown for the current selection
809 bool bFound = false;
811 SCCOL nCurX = GetViewData().GetCurX();
812 SCROW nCurY = GetViewData().GetCurY();
813 SCTAB nTab = GetViewData().GetTabNo();
814 ScDocument& rDoc = GetViewData().GetDocument();
816 ScDPObject* pDPObj = rDoc.GetDPAtCursor( nCurX, nCurY, nTab );
817 if ( pDPObj )
819 ScDPUniqueStringSet aEntries;
820 tools::Long nSelectDimension = -1;
821 GetSelectedMemberList( aEntries, nSelectDimension );
823 if (!aEntries.empty())
825 bool bIsDataLayout;
826 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
827 OUString aBaseDimName( aDimName );
829 bool bInGroupDim = false;
830 bool bFoundParts = false;
832 ScDPDimensionSaveData* pDimData =
833 const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
834 if ( pDimData )
836 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
837 const ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDim( aDimName );
838 if ( pNumGroupDim )
840 // existing num group dimension
842 if ( pNumGroupDim->GetDatePart() != 0 )
844 // dimension has date info -> edit settings of this dimension
845 // (parts are collected below)
847 rOldInfo = pNumGroupDim->GetDateInfo();
848 bFound = true;
850 else if ( pNumGroupDim->GetInfo().mbDateValues )
852 // Numerical grouping with DateValues flag is used for grouping
853 // of days with a "Number of days" value.
855 rOldInfo = pNumGroupDim->GetInfo();
856 rParts = css::sheet::DataPilotFieldGroupBy::DAYS; // not found in CollectDateParts
857 bFoundParts = true;
858 bFound = true;
860 bInGroupDim = true;
862 else if ( pGroupDim )
864 // existing additional group dimension
866 if ( pGroupDim->GetDatePart() != 0 )
868 // dimension has date info -> edit settings of this dimension
869 // (parts are collected below)
871 rOldInfo = pGroupDim->GetDateInfo();
872 aBaseDimName = pGroupDim->GetSourceDimName();
873 bFound = true;
875 bInGroupDim = true;
878 if ( bFound && !bFoundParts )
880 // collect date parts from all group dimensions
881 rParts = pDimData->CollectDateParts( aBaseDimName );
883 if ( !bFound && !bInGroupDim )
885 // create new date group dimensions if the selection is a single cell
886 // in a normal dimension with date content
888 ScRange aSelRange;
889 if ( (GetViewData().GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
890 aSelRange.aStart == aSelRange.aEnd )
892 SCCOL nSelCol = aSelRange.aStart.Col();
893 SCROW nSelRow = aSelRange.aStart.Row();
894 SCTAB nSelTab = aSelRange.aStart.Tab();
895 if ( rDoc.HasValueData( nSelCol, nSelRow, nSelTab ) )
897 sal_uLong nIndex = rDoc.GetAttr(
898 nSelCol, nSelRow, nSelTab, ATTR_VALUE_FORMAT)->GetValue();
899 SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nIndex);
900 if ( nType == SvNumFormatType::DATE || nType == SvNumFormatType::TIME || nType == SvNumFormatType::DATETIME )
902 bFound = true;
903 // use currently selected value for automatic limits
904 if( rOldInfo.mbAutoStart )
905 rOldInfo.mfStart = rDoc.GetValue( aSelRange.aStart );
906 if( rOldInfo.mbAutoEnd )
907 rOldInfo.mfEnd = rDoc.GetValue( aSelRange.aStart );
915 return bFound;
918 bool ScDBFunc::HasSelectionForNumGroup( ScDPNumGroupInfo& rOldInfo )
920 // determine if the numeric group dialog has to be shown for the current selection
922 bool bFound = false;
924 SCCOL nCurX = GetViewData().GetCurX();
925 SCROW nCurY = GetViewData().GetCurY();
926 SCTAB nTab = GetViewData().GetTabNo();
927 ScDocument& rDoc = GetViewData().GetDocument();
929 ScDPObject* pDPObj = rDoc.GetDPAtCursor( nCurX, nCurY, nTab );
930 if ( pDPObj )
932 ScDPUniqueStringSet aEntries;
933 tools::Long nSelectDimension = -1;
934 GetSelectedMemberList( aEntries, nSelectDimension );
936 if (!aEntries.empty())
938 bool bIsDataLayout;
939 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
941 bool bInGroupDim = false;
943 ScDPDimensionSaveData* pDimData =
944 const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
945 if ( pDimData )
947 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
948 if ( pNumGroupDim )
950 // existing num group dimension
951 // -> edit settings of this dimension
953 rOldInfo = pNumGroupDim->GetInfo();
954 bFound = true;
956 else if ( pDimData->GetNamedGroupDim( aDimName ) )
957 bInGroupDim = true; // in a group dimension
959 if ( !bFound && !bInGroupDim )
961 // create a new num group dimension if the selection is a single cell
962 // in a normal dimension with numeric content
964 ScRange aSelRange;
965 if ( (GetViewData().GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
966 aSelRange.aStart == aSelRange.aEnd )
968 if ( rDoc.HasValueData( aSelRange.aStart.Col(), aSelRange.aStart.Row(),
969 aSelRange.aStart.Tab() ) )
971 bFound = true;
972 // use currently selected value for automatic limits
973 if( rOldInfo.mbAutoStart )
974 rOldInfo.mfStart = rDoc.GetValue( aSelRange.aStart );
975 if( rOldInfo.mbAutoEnd )
976 rOldInfo.mfEnd = rDoc.GetValue( aSelRange.aStart );
983 return bFound;
986 void ScDBFunc::DateGroupDataPilot( const ScDPNumGroupInfo& rInfo, sal_Int32 nParts )
988 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
989 GetViewData().GetCurY(), GetViewData().GetTabNo() );
990 if (!pDPObj)
991 return;
993 ScDPUniqueStringSet aEntries;
994 tools::Long nSelectDimension = -1;
995 GetSelectedMemberList( aEntries, nSelectDimension );
997 if (aEntries.empty())
998 return;
1000 std::vector<OUString> aDeletedNames;
1001 bool bIsDataLayout;
1002 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1004 ScDPSaveData aData( *pDPObj->GetSaveData() );
1005 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
1007 // find the source dimension name.
1008 OUString aBaseDimName = aDimName;
1009 if( const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ) )
1010 aBaseDimName = pBaseGroupDim->GetSourceDimName();
1012 // Remove all group dimensions associated with this source dimension. For
1013 // date grouping, we need to remove all existing groups for the affected
1014 // source dimension and build new one(s) from scratch. Keep the deleted
1015 // names so that they can be reused during re-construction.
1016 aData.RemoveAllGroupDimensions(aBaseDimName, &aDeletedNames);
1018 if ( nParts )
1020 // create date group dimensions
1022 bool bFirst = true;
1023 sal_Int32 nMask = 1;
1024 for (sal_uInt16 nBit=0; nBit<32; nBit++)
1026 if ( nParts & nMask )
1028 if ( bFirst )
1030 // innermost part: create NumGroupDimension (replacing original values)
1031 // Dimension name is left unchanged
1033 if ( (nParts == sheet::DataPilotFieldGroupBy::DAYS) && (rInfo.mfStep >= 1.0) )
1035 // only days, and a step value specified: use numerical grouping
1036 // with DateValues flag, not date grouping
1038 ScDPNumGroupInfo aNumInfo( rInfo );
1039 aNumInfo.mbDateValues = true;
1041 ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, aNumInfo );
1042 pDimData->AddNumGroupDimension( aNumGroupDim );
1044 else
1046 ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, rInfo, nMask );
1047 pDimData->AddNumGroupDimension( aNumGroupDim );
1050 bFirst = false;
1052 else
1054 // additional parts: create GroupDimension (shown as additional dimensions)
1055 OUString aGroupDimName =
1056 pDimData->CreateDateGroupDimName(nMask, *pDPObj, true, &aDeletedNames);
1057 ScDPSaveGroupDimension aGroupDim( aBaseDimName, aGroupDimName );
1058 aGroupDim.SetDateInfo( rInfo, nMask );
1059 pDimData->AddGroupDimension( aGroupDim );
1061 // set orientation
1062 ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
1063 if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
1065 ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aBaseDimName );
1066 pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
1067 aData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
1071 nMask *= 2;
1075 // apply changes
1076 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1077 pDPObj->SetSaveData( aData );
1078 aFunc.RefreshPivotTableGroups(pDPObj);
1080 // unmark cell selection
1081 Unmark();
1084 void ScDBFunc::NumGroupDataPilot( const ScDPNumGroupInfo& rInfo )
1086 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1087 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1088 if (!pDPObj)
1089 return;
1091 ScDPUniqueStringSet aEntries;
1092 tools::Long nSelectDimension = -1;
1093 GetSelectedMemberList( aEntries, nSelectDimension );
1095 if (aEntries.empty())
1096 return;
1098 bool bIsDataLayout;
1099 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1101 ScDPSaveData aData( *pDPObj->GetSaveData() );
1102 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
1104 ScDPSaveNumGroupDimension* pExisting = pDimData->GetNumGroupDimAcc( aDimName );
1105 if ( pExisting )
1107 // modify existing group dimension
1108 pExisting->SetGroupInfo( rInfo );
1110 else
1112 // create new group dimension
1113 ScDPSaveNumGroupDimension aNumGroupDim( aDimName, rInfo );
1114 pDimData->AddNumGroupDimension( aNumGroupDim );
1117 // apply changes
1118 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1119 pDPObj->SetSaveData( aData );
1120 aFunc.RefreshPivotTableGroups(pDPObj);
1122 // unmark cell selection
1123 Unmark();
1126 void ScDBFunc::GroupDataPilot()
1128 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1129 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1130 if (!pDPObj)
1131 return;
1133 ScDPUniqueStringSet aEntries;
1134 tools::Long nSelectDimension = -1;
1135 GetSelectedMemberList( aEntries, nSelectDimension );
1137 if (aEntries.empty())
1138 return;
1140 bool bIsDataLayout;
1141 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1143 ScDPSaveData aData( *pDPObj->GetSaveData() );
1144 ScDPDimensionSaveData* pDimData = aData.GetDimensionData(); // created if not there
1146 // find original base
1147 OUString aBaseDimName = aDimName;
1148 const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName );
1149 if ( pBaseGroupDim )
1151 // any entry's SourceDimName is the original base
1152 aBaseDimName = pBaseGroupDim->GetSourceDimName();
1155 // find existing group dimension
1156 // (using the selected dim, can be intermediate group dim)
1157 ScDPSaveGroupDimension* pGroupDimension = pDimData->GetGroupDimAccForBase( aDimName );
1159 // remove the selected items from their groups
1160 // (empty groups are removed, too)
1161 if ( pGroupDimension )
1163 for (const OUString& aEntryName : aEntries)
1165 if ( pBaseGroupDim )
1167 // for each selected (intermediate) group, remove all its items
1168 // (same logic as for adding, below)
1169 const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
1170 if ( pBaseGroup )
1171 pBaseGroup->RemoveElementsFromGroups( *pGroupDimension ); // remove all elements
1172 else
1173 pGroupDimension->RemoveFromGroups( aEntryName );
1175 else
1176 pGroupDimension->RemoveFromGroups( aEntryName );
1180 std::unique_ptr<ScDPSaveGroupDimension> pNewGroupDim;
1181 if ( !pGroupDimension )
1183 // create a new group dimension
1184 OUString aGroupDimName =
1185 pDimData->CreateGroupDimName(aBaseDimName, *pDPObj, false, nullptr);
1186 pNewGroupDim.reset(new ScDPSaveGroupDimension( aBaseDimName, aGroupDimName ));
1188 pGroupDimension = pNewGroupDim.get(); // make changes to the new dim if none existed
1190 if ( pBaseGroupDim )
1192 // If it's a higher-order group dimension, pre-allocate groups for all
1193 // non-selected original groups, so the individual base members aren't
1194 // used for automatic groups (this would make the original groups hard
1195 // to find).
1196 //! Also do this when removing groups?
1197 //! Handle this case dynamically with automatic groups?
1199 tools::Long nGroupCount = pBaseGroupDim->GetGroupCount();
1200 for ( tools::Long nGroup = 0; nGroup < nGroupCount; nGroup++ )
1202 const ScDPSaveGroupItem& rBaseGroup = pBaseGroupDim->GetGroupByIndex( nGroup );
1204 if (!aEntries.count(rBaseGroup.GetGroupName()))
1206 // add an additional group for each item that is not in the selection
1207 ScDPSaveGroupItem aGroup( rBaseGroup.GetGroupName() );
1208 aGroup.AddElementsFromGroup( rBaseGroup );
1209 pGroupDimension->AddGroupItem( aGroup );
1214 OUString aGroupDimName = pGroupDimension->GetGroupDimName();
1216 ScDPSaveGroupItem aGroup(pGroupDimension->CreateGroupName(ScResId(STR_PIVOT_GROUP)));
1217 for (const OUString& aEntryName : aEntries)
1219 if ( pBaseGroupDim )
1221 // for each selected (intermediate) group, add all its items
1222 const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
1223 if ( pBaseGroup )
1224 aGroup.AddElementsFromGroup( *pBaseGroup );
1225 else
1226 aGroup.AddElement( aEntryName ); // no group found -> automatic group, add the item itself
1228 else
1229 aGroup.AddElement( aEntryName ); // no group dimension, add all items directly
1232 pGroupDimension->AddGroupItem( aGroup );
1234 if ( pNewGroupDim )
1236 pDimData->AddGroupDimension( *pNewGroupDim );
1237 pNewGroupDim.reset(); // AddGroupDimension copies the object
1238 // don't access pGroupDimension after here
1240 pGroupDimension = nullptr;
1242 // set orientation
1243 ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
1244 if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
1246 ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aDimName );
1247 pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
1248 aData.SetPosition( pSaveDimension, 0 ); //! before (immediate) base
1251 // apply changes
1252 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1253 pDPObj->SetSaveData( aData );
1254 aFunc.RefreshPivotTableGroups(pDPObj);
1256 // unmark cell selection
1257 Unmark();
1260 void ScDBFunc::UngroupDataPilot()
1262 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1263 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1264 if (!pDPObj)
1265 return;
1267 ScDPUniqueStringSet aEntries;
1268 tools::Long nSelectDimension = -1;
1269 GetSelectedMemberList( aEntries, nSelectDimension );
1271 if (aEntries.empty())
1272 return;
1274 bool bIsDataLayout;
1275 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1277 ScDPSaveData aData( *pDPObj->GetSaveData() );
1278 if (!aData.GetExistingDimensionData())
1279 // There is nothing to ungroup.
1280 return;
1282 ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1284 ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
1285 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
1286 if ( ( pGroupDim && pGroupDim->GetDatePart() != 0 ) ||
1287 ( pNumGroupDim && pNumGroupDim->GetDatePart() != 0 ) )
1289 // Date grouping: need to remove all affected group dimensions.
1290 // This is done using DateGroupDataPilot with nParts=0.
1292 DateGroupDataPilot( ScDPNumGroupInfo(), 0 );
1293 return;
1296 if ( pGroupDim )
1298 for (const auto& rEntry : aEntries)
1299 pGroupDim->RemoveGroup(rEntry);
1301 // remove group dimension if empty
1302 bool bEmptyDim = pGroupDim->IsEmpty();
1303 if ( !bEmptyDim )
1305 // If all remaining groups in the dimension aren't shown, remove
1306 // the dimension too, as if it was completely empty.
1307 ScDPUniqueStringSet aVisibleEntries;
1308 pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
1309 bEmptyDim = pGroupDim->HasOnlyHidden( aVisibleEntries );
1311 if ( bEmptyDim )
1313 pDimData->RemoveGroupDimension( aDimName ); // pGroupDim is deleted
1315 // also remove SaveData settings for the dimension that no longer exists
1316 aData.RemoveDimensionByName( aDimName );
1319 else if ( pNumGroupDim )
1321 // remove the numerical grouping
1322 pDimData->RemoveNumGroupDimension( aDimName );
1323 // SaveData settings can remain unchanged - the same dimension still exists
1326 // apply changes
1327 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1328 pDPObj->SetSaveData( aData );
1329 aFunc.RefreshPivotTableGroups(pDPObj);
1331 // unmark cell selection
1332 Unmark();
1335 static OUString lcl_replaceMemberNameInSubtotal(const OUString& rSubtotal, std::u16string_view rMemberName)
1337 sal_Int32 n = rSubtotal.getLength();
1338 const sal_Unicode* p = rSubtotal.getStr();
1339 OUStringBuffer aBuf, aWordBuf;
1340 for (sal_Int32 i = 0; i < n; ++i)
1342 sal_Unicode c = p[i];
1343 if (c == ' ')
1345 OUString aWord = aWordBuf.makeStringAndClear();
1346 if (aWord == rMemberName)
1347 aBuf.append('?');
1348 else
1349 aBuf.append(aWord);
1350 aBuf.append(c);
1352 else if (c == '\\')
1354 // Escape a backslash character.
1355 aWordBuf.append(OUStringChar(c) + OUStringChar(c));
1357 else if (c == '?')
1359 // A literal '?' must be escaped with a backslash ('\');
1360 aWordBuf.append("\\" + OUStringChar(c));
1362 else
1363 aWordBuf.append(c);
1366 if (!aWordBuf.isEmpty())
1368 OUString aWord = aWordBuf.makeStringAndClear();
1369 if (aWord == rMemberName)
1370 aBuf.append('?');
1371 else
1372 aBuf.append(aWord);
1375 return aBuf.makeStringAndClear();
1378 void ScDBFunc::DataPilotInput( const ScAddress& rPos, const OUString& rString )
1380 using namespace ::com::sun::star::sheet;
1382 ScDocument& rDoc = GetViewData().GetDocument();
1383 ScDPObject* pDPObj = rDoc.GetDPAtCursor( rPos.Col(), rPos.Row(), rPos.Tab() );
1384 if (!pDPObj)
1385 return;
1387 OUString aOldText = rDoc.GetString(rPos.Col(), rPos.Row(), rPos.Tab());
1389 if ( aOldText == rString )
1391 // nothing to do: silently exit
1392 return;
1395 TranslateId pErrorId;
1397 pDPObj->BuildAllDimensionMembers();
1398 ScDPSaveData aData( *pDPObj->GetSaveData() );
1399 bool bChange = false;
1400 bool bNeedReloadGroups = false;
1402 DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN;
1403 tools::Long nField = pDPObj->GetHeaderDim( rPos, nOrient );
1404 if ( nField >= 0 )
1406 // changing a field title
1407 if ( aData.GetExistingDimensionData() )
1409 // only group dimensions can be renamed
1411 ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1412 ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aOldText );
1413 if ( pGroupDim )
1415 // valid name: not empty, no existing dimension (group or other)
1416 if (!rString.isEmpty() && !pDPObj->IsDimNameInUse(rString))
1418 pGroupDim->Rename( rString );
1420 // also rename in SaveData to preserve the field settings
1421 ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aOldText );
1422 pSaveDim->SetName( rString );
1424 bChange = true;
1426 else
1427 pErrorId = STR_INVALIDNAME;
1430 else if (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW)
1432 bool bDataLayout = false;
1433 OUString aDimName = pDPObj->GetDimName(nField, bDataLayout);
1434 ScDPSaveDimension* pDim = bDataLayout ? aData.GetDataLayoutDimension() : aData.GetDimensionByName(aDimName);
1435 if (pDim)
1437 if (!rString.isEmpty())
1439 if (rString.equalsIgnoreAsciiCase(aDimName))
1441 pDim->RemoveLayoutName();
1442 bChange = true;
1444 else if (!pDPObj->IsDimNameInUse(rString))
1446 pDim->SetLayoutName(rString);
1447 bChange = true;
1449 else
1450 pErrorId = STR_INVALIDNAME;
1452 else
1453 pErrorId = STR_INVALIDNAME;
1457 else if (pDPObj->IsDataDescriptionCell(rPos))
1459 // There is only one data dimension.
1460 ScDPSaveDimension* pDim = aData.GetFirstDimension(sheet::DataPilotFieldOrientation_DATA);
1461 if (pDim)
1463 if (!rString.isEmpty())
1465 if (pDim->GetName().equalsIgnoreAsciiCase(rString))
1467 pDim->RemoveLayoutName();
1468 bChange = true;
1470 else if (!pDPObj->IsDimNameInUse(rString))
1472 pDim->SetLayoutName(rString);
1473 bChange = true;
1475 else
1476 pErrorId = STR_INVALIDNAME;
1478 else
1479 pErrorId = STR_INVALIDNAME;
1482 else
1484 // This is not a field header.
1485 sheet::DataPilotTableHeaderData aPosData;
1486 pDPObj->GetHeaderPositionData(rPos, aPosData);
1488 if ((aPosData.Flags & MemberResultFlags::HASMEMBER) && !aOldText.isEmpty())
1490 if ( aData.GetExistingDimensionData() && !(aPosData.Flags & MemberResultFlags::SUBTOTAL))
1492 bool bIsDataLayout;
1493 OUString aDimName = pDPObj->GetDimName( aPosData.Dimension, bIsDataLayout );
1495 ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1496 ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
1497 if ( pGroupDim )
1499 // valid name: not empty, no existing group in this dimension
1500 //! ignore case?
1501 if (!rString.isEmpty() && !pGroupDim->GetNamedGroup(rString))
1503 ScDPSaveGroupItem* pGroup = pGroupDim->GetNamedGroupAcc( aOldText );
1504 if ( pGroup )
1505 pGroup->Rename( rString ); // rename the existing group
1506 else
1508 // create a new group to replace the automatic group
1509 ScDPSaveGroupItem aGroup( rString );
1510 aGroup.AddElement( aOldText );
1511 pGroupDim->AddGroupItem( aGroup );
1514 // in both cases also adjust savedata, to preserve member settings (show details)
1515 ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aDimName );
1516 ScDPSaveMember* pSaveMember = pSaveDim->GetExistingMemberByName( aOldText );
1517 if ( pSaveMember )
1518 pSaveMember->SetName( rString );
1520 bChange = true;
1521 bNeedReloadGroups = true;
1523 else
1524 pErrorId = STR_INVALIDNAME;
1527 else if (aPosData.Flags & MemberResultFlags::GRANDTOTAL)
1529 aData.SetGrandTotalName(rString);
1530 bChange = true;
1532 else if (aPosData.Dimension >= 0 && !aPosData.MemberName.isEmpty())
1534 bool bDataLayout = false;
1535 OUString aDimName = pDPObj->GetDimName(static_cast<tools::Long>(aPosData.Dimension), bDataLayout);
1536 if (bDataLayout)
1538 // data dimension
1541 if (aPosData.Flags & MemberResultFlags::SUBTOTAL)
1542 break;
1544 ScDPSaveDimension* pDim = aData.GetDimensionByName(aPosData.MemberName);
1545 if (!pDim)
1546 break;
1548 if (rString.isEmpty())
1550 pErrorId = STR_INVALIDNAME;
1551 break;
1554 if (aPosData.MemberName.equalsIgnoreAsciiCase(rString))
1556 pDim->RemoveLayoutName();
1557 bChange = true;
1559 else if (!pDPObj->IsDimNameInUse(rString))
1561 pDim->SetLayoutName(rString);
1562 bChange = true;
1564 else
1565 pErrorId = STR_INVALIDNAME;
1567 while (false);
1569 else
1571 // field member
1574 ScDPSaveDimension* pDim = aData.GetDimensionByName(aDimName);
1575 if (!pDim)
1576 break;
1578 ScDPSaveMember* pMem = pDim->GetExistingMemberByName(aPosData.MemberName);
1579 if (!pMem)
1580 break;
1582 if (aPosData.Flags & MemberResultFlags::SUBTOTAL)
1584 // Change subtotal only when the table has one data dimension.
1585 if (aData.GetDataDimensionCount() > 1)
1586 break;
1588 // display name for subtotal is allowed only if the subtotal type is 'Automatic'.
1589 if (pDim->GetSubTotalsCount() != 1)
1590 break;
1592 if (pDim->GetSubTotalFunc(0) != ScGeneralFunction::AUTO)
1593 break;
1595 const std::optional<OUString> & pLayoutName = pMem->GetLayoutName();
1596 OUString aMemberName;
1597 if (pLayoutName)
1598 aMemberName = *pLayoutName;
1599 else
1600 aMemberName = aPosData.MemberName;
1602 OUString aNew = lcl_replaceMemberNameInSubtotal(rString, aMemberName);
1603 pDim->SetSubtotalName(aNew);
1604 bChange = true;
1606 else
1608 // Check to make sure the member name isn't
1609 // already used.
1610 if (!rString.isEmpty())
1612 if (rString.equalsIgnoreAsciiCase(pMem->GetName()))
1614 pMem->RemoveLayoutName();
1615 bChange = true;
1617 else if (!pDim->IsMemberNameInUse(rString))
1619 pMem->SetLayoutName(rString);
1620 bChange = true;
1622 else
1623 pErrorId = STR_INVALIDNAME;
1625 else
1626 pErrorId = STR_INVALIDNAME;
1629 while (false);
1635 if ( bChange )
1637 // apply changes
1638 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1639 pDPObj->SetSaveData( aData );
1640 if (bNeedReloadGroups)
1642 ScDPCollection* pDPs = rDoc.GetDPCollection();
1643 if (pDPs)
1645 o3tl::sorted_vector<ScDPObject*> aRefs;
1646 // tdf#111305: Reload groups in cache after modifications.
1647 pDPs->ReloadGroupsInCache(pDPObj, aRefs);
1648 } // pDPs
1649 } // bNeedReloadGroups
1650 aFunc.UpdatePivotTable(*pDPObj, true, false);
1652 else
1654 if (!pErrorId)
1655 pErrorId = STR_ERR_DATAPILOT_INPUT;
1656 ErrorMessage(pErrorId);
1660 static void lcl_MoveToEnd( ScDPSaveDimension& rDim, const OUString& rItemName )
1662 std::unique_ptr<ScDPSaveMember> pNewMember;
1663 const ScDPSaveMember* pOldMember = rDim.GetExistingMemberByName( rItemName );
1664 if ( pOldMember )
1665 pNewMember.reset(new ScDPSaveMember( *pOldMember ));
1666 else
1667 pNewMember.reset(new ScDPSaveMember( rItemName ));
1668 rDim.AddMember( std::move(pNewMember) );
1669 // AddMember takes ownership of the new pointer,
1670 // puts it to the end of the list even if it was in the list before.
1673 namespace {
1675 struct ScOUStringCollate
1677 CollatorWrapper* mpCollator;
1679 explicit ScOUStringCollate(CollatorWrapper* pColl) : mpCollator(pColl) {}
1681 bool operator()(const OUString& rStr1, const OUString& rStr2) const
1683 return ( mpCollator->compareString(rStr1, rStr2) < 0 );
1689 void ScDBFunc::DataPilotSort(ScDPObject* pDPObj, tools::Long nDimIndex, bool bAscending, const sal_uInt16* pUserListId)
1691 if (!pDPObj)
1692 return;
1694 // We need to run this to get all members later.
1695 if ( pUserListId )
1696 pDPObj->BuildAllDimensionMembers();
1698 if (nDimIndex < 0)
1699 // Invalid dimension index. Bail out.
1700 return;
1702 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1703 if (!pSaveData)
1704 return;
1706 ScDPSaveData aNewSaveData(*pSaveData);
1707 bool bDataLayout;
1708 OUString aDimName = pDPObj->GetDimName(nDimIndex, bDataLayout);
1709 ScDPSaveDimension* pSaveDim = aNewSaveData.GetDimensionByName(aDimName);
1710 if (!pSaveDim)
1711 return;
1713 // manual evaluation of sort order is only needed if a user list id is given
1714 if ( pUserListId )
1716 typedef ScDPSaveDimension::MemberList MemList;
1717 const MemList& rDimMembers = pSaveDim->GetMembers();
1718 vector<OUString> aMembers;
1719 std::unordered_set<OUString> aMemberSet;
1720 size_t nMemberCount = 0;
1721 for (ScDPSaveMember* pMem : rDimMembers)
1723 aMembers.push_back(pMem->GetName());
1724 aMemberSet.insert(pMem->GetName());
1725 ++nMemberCount;
1728 // Sort the member list in ascending order.
1729 ScOUStringCollate aCollate( &ScGlobal::GetCollator() );
1730 std::stable_sort(aMembers.begin(), aMembers.end(), aCollate);
1732 // Collect and rank those custom sort strings that also exist in the member name list.
1734 typedef std::unordered_map<OUString, sal_uInt16> UserSortMap;
1735 UserSortMap aSubStrs;
1736 sal_uInt16 nSubCount = 0;
1737 ScUserList& rUserList = ScGlobal::GetUserList();
1738 size_t nUserListSize = rUserList.size();
1739 if (!nUserListSize || *pUserListId >= static_cast<sal_uInt16>(nUserListSize))
1740 return;
1742 const ScUserListData& rData = rUserList[*pUserListId];
1743 sal_uInt16 n = rData.GetSubCount();
1744 for (sal_uInt16 i = 0; i < n; ++i)
1746 OUString aSub = rData.GetSubStr(i);
1747 if (!aMemberSet.count(aSub))
1748 // This string doesn't exist in the member name set. Don't add this.
1749 continue;
1751 aSubStrs.emplace(aSub, nSubCount++);
1754 // Rank all members.
1756 vector<OUString> aRankedNames(nMemberCount);
1757 sal_uInt16 nCurStrId = 0;
1758 for (auto const& aMemberName : aMembers)
1760 sal_uInt16 nRank = 0;
1761 UserSortMap::const_iterator itrSub = aSubStrs.find(aMemberName);
1762 if (itrSub == aSubStrs.end())
1763 nRank = nSubCount + nCurStrId++;
1764 else
1765 nRank = itrSub->second;
1767 if (!bAscending)
1768 nRank = static_cast< sal_uInt16 >( nMemberCount - nRank - 1 );
1770 aRankedNames[nRank] = aMemberName;
1773 // Re-order ScDPSaveMember instances with the new ranks.
1774 for (auto const& aRankedName : aRankedNames)
1776 const ScDPSaveMember* pOldMem = pSaveDim->GetExistingMemberByName(aRankedName);
1777 if (!pOldMem)
1778 // All members are supposed to be present.
1779 continue;
1781 pSaveDim->AddMember(std::unique_ptr<ScDPSaveMember>(new ScDPSaveMember(*pOldMem)));
1784 // Set the sorting mode to manual for now. We may introduce a new sorting
1785 // mode later on.
1787 sheet::DataPilotFieldSortInfo aSortInfo;
1788 aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
1789 pSaveDim->SetSortInfo(&aSortInfo);
1791 else
1793 // without user list id, just apply sorting mode
1795 sheet::DataPilotFieldSortInfo aSortInfo;
1796 aSortInfo.Mode = sheet::DataPilotFieldSortMode::NAME;
1797 aSortInfo.IsAscending = bAscending;
1798 pSaveDim->SetSortInfo(&aSortInfo);
1801 // Update the datapilot with the newly sorted field members.
1803 std::unique_ptr<ScDPObject> pNewObj(new ScDPObject(*pDPObj));
1804 pNewObj->SetSaveData(aNewSaveData);
1805 ScDBDocFunc aFunc(*GetViewData().GetDocShell());
1807 aFunc.DataPilotUpdate(pDPObj, pNewObj.get(), true, false);
1810 bool ScDBFunc::DataPilotMove( const ScRange& rSource, const ScAddress& rDest )
1812 bool bRet = false;
1813 ScDocument& rDoc = GetViewData().GetDocument();
1814 ScDPObject* pDPObj = rDoc.GetDPAtCursor( rSource.aStart.Col(), rSource.aStart.Row(), rSource.aStart.Tab() );
1815 if ( pDPObj && pDPObj == rDoc.GetDPAtCursor( rDest.Col(), rDest.Row(), rDest.Tab() ) )
1817 sheet::DataPilotTableHeaderData aDestData;
1818 pDPObj->GetHeaderPositionData( rDest, aDestData );
1819 bool bValid = ( aDestData.Dimension >= 0 ); // dropping onto a field
1821 // look through the source range
1822 std::unordered_set< OUString > aMembersSet; // for lookup
1823 std::vector< OUString > aMembersVector; // members in original order, for inserting
1824 aMembersVector.reserve( std::max( static_cast<SCSIZE>( rSource.aEnd.Col() - rSource.aStart.Col() + 1 ),
1825 static_cast<SCSIZE>( rSource.aEnd.Row() - rSource.aStart.Row() + 1 ) ) );
1826 for (SCROW nRow = rSource.aStart.Row(); bValid && nRow <= rSource.aEnd.Row(); ++nRow )
1827 for (SCCOL nCol = rSource.aStart.Col(); bValid && nCol <= rSource.aEnd.Col(); ++nCol )
1829 sheet::DataPilotTableHeaderData aSourceData;
1830 pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, rSource.aStart.Tab() ), aSourceData );
1831 if ( aSourceData.Dimension == aDestData.Dimension && !aSourceData.MemberName.isEmpty() )
1833 if ( aMembersSet.insert( aSourceData.MemberName ).second )
1835 aMembersVector.push_back( aSourceData.MemberName );
1837 // duplicates are ignored
1839 else
1840 bValid = false; // empty (subtotal) or different field
1843 if ( bValid )
1845 bool bIsDataLayout;
1846 OUString aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
1847 if ( !bIsDataLayout )
1849 ScDPSaveData aData( *pDPObj->GetSaveData() );
1850 ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
1852 // get all member names in source order
1853 uno::Sequence<OUString> aMemberNames;
1854 pDPObj->GetMemberNames( aDestData.Dimension, aMemberNames );
1856 bool bInserted = false;
1858 for (const OUString& aMemberStr : aMemberNames)
1860 if ( !bInserted && aMemberStr == aDestData.MemberName )
1862 // insert dragged items before this item
1863 for ( const auto& rMember : aMembersVector )
1864 lcl_MoveToEnd( *pDim, rMember );
1865 bInserted = true;
1868 if ( aMembersSet.find( aMemberStr ) == aMembersSet.end() ) // skip dragged items
1869 lcl_MoveToEnd( *pDim, aMemberStr );
1871 // insert dragged item at end if dest wasn't found (for example, empty)
1872 if ( !bInserted )
1873 for ( const auto& rMember : aMembersVector )
1874 lcl_MoveToEnd( *pDim, rMember );
1876 // Items that were in SaveData, but not in the source, end up at the start of the list.
1878 // set flag for manual sorting
1879 sheet::DataPilotFieldSortInfo aSortInfo;
1880 aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
1881 pDim->SetSortInfo( &aSortInfo );
1883 // apply changes
1884 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
1885 std::unique_ptr<ScDPObject> pNewObj(new ScDPObject( *pDPObj ));
1886 pNewObj->SetSaveData( aData );
1887 aFunc.DataPilotUpdate( pDPObj, pNewObj.get(), true, false ); //! bApi for drag&drop?
1888 pNewObj.reset();
1890 Unmark(); // entry was moved - no use in leaving the old cell selected
1892 bRet = true;
1897 return bRet;
1900 bool ScDBFunc::HasSelectionForDrillDown( css::sheet::DataPilotFieldOrientation& rOrientation )
1902 bool bRet = false;
1904 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1905 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1906 if ( pDPObj )
1908 ScDPUniqueStringSet aEntries;
1909 tools::Long nSelectDimension = -1;
1910 GetSelectedMemberList( aEntries, nSelectDimension );
1912 if (!aEntries.empty())
1914 bool bIsDataLayout;
1915 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1916 if ( !bIsDataLayout )
1918 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1919 ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName( aDimName );
1920 if ( pDim )
1922 css::sheet::DataPilotFieldOrientation nDimOrient = pDim->GetOrientation();
1923 ScDPSaveDimension* pInner = pSaveData->GetInnermostDimension( nDimOrient );
1924 if ( pDim == pInner )
1926 rOrientation = nDimOrient;
1927 bRet = true;
1934 return bRet;
1937 void ScDBFunc::SetDataPilotDetails(bool bShow, const OUString* pNewDimensionName)
1939 ScDPObject* pDPObj = GetViewData().GetDocument().GetDPAtCursor( GetViewData().GetCurX(),
1940 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1941 if ( !pDPObj )
1942 return;
1944 ScDPUniqueStringSet aEntries;
1945 tools::Long nSelectDimension = -1;
1946 GetSelectedMemberList( aEntries, nSelectDimension );
1948 if (aEntries.empty())
1949 return;
1951 bool bIsDataLayout;
1952 OUString aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1953 if ( bIsDataLayout )
1954 return;
1956 ScDPSaveData aData( *pDPObj->GetSaveData() );
1957 ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
1959 if ( bShow && pNewDimensionName )
1961 // add the new dimension with the same orientation, at the end
1963 ScDPSaveDimension* pNewDim = aData.GetDimensionByName( *pNewDimensionName );
1964 ScDPSaveDimension* pDuplicated = nullptr;
1965 if ( pNewDim->GetOrientation() == sheet::DataPilotFieldOrientation_DATA )
1967 // Need to duplicate the dimension, create column/row in addition to data:
1968 // The duplicated dimension inherits the existing settings, pNewDim is modified below.
1969 pDuplicated = aData.DuplicateDimension( *pNewDimensionName );
1972 css::sheet::DataPilotFieldOrientation nOrientation = pDim->GetOrientation();
1973 pNewDim->SetOrientation( nOrientation );
1975 tools::Long nPosition = LONG_MAX;
1976 aData.SetPosition( pNewDim, nPosition );
1978 ScDPSaveDimension* pDataLayout = aData.GetDataLayoutDimension();
1979 if ( pDataLayout->GetOrientation() == nOrientation &&
1980 aData.GetDataDimensionCount() <= 1 )
1982 // If there is only one data dimension, the data layout dimension
1983 // must still be the last one in its orientation.
1984 aData.SetPosition( pDataLayout, nPosition );
1987 if ( pDuplicated )
1989 // The duplicated (data) dimension needs to be behind the original dimension
1990 aData.SetPosition( pDuplicated, nPosition );
1993 // Hide details for all visible members (selected are changed below).
1994 //! Use all members from source level instead (including non-visible)?
1996 ScDPUniqueStringSet aVisibleEntries;
1997 pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
1999 for (const OUString& aVisName : aVisibleEntries)
2001 ScDPSaveMember* pMember = pDim->GetMemberByName( aVisName );
2002 pMember->SetShowDetails( false );
2006 for (const auto& rEntry : aEntries)
2008 ScDPSaveMember* pMember = pDim->GetMemberByName(rEntry);
2009 pMember->SetShowDetails( bShow );
2012 // apply changes
2013 ScDBDocFunc aFunc( *GetViewData().GetDocShell() );
2014 std::unique_ptr<ScDPObject> pNewObj(new ScDPObject( *pDPObj ));
2015 pNewObj->SetSaveData( aData );
2016 aFunc.DataPilotUpdate( pDPObj, pNewObj.get(), true, false );
2017 pNewObj.reset();
2019 // unmark cell selection
2020 Unmark();
2023 void ScDBFunc::ShowDataPilotSourceData( ScDPObject& rDPObj, const Sequence<sheet::DataPilotFieldFilter>& rFilters )
2025 ScDocument& rDoc = GetViewData().GetDocument();
2026 if (rDoc.GetDocumentShell()->IsReadOnly())
2028 ErrorMessage(STR_READONLYERR);
2029 return;
2032 Reference<sheet::XDimensionsSupplier> xDimSupplier = rDPObj.GetSource();
2033 Reference<container::XNameAccess> xDims = xDimSupplier->getDimensions();
2034 Reference<sheet::XDrillDownDataSupplier> xDDSupplier(xDimSupplier, UNO_QUERY);
2035 if (!xDDSupplier.is())
2036 return;
2038 Sequence< Sequence<Any> > aTabData = xDDSupplier->getDrillDownData(rFilters);
2039 sal_Int32 nRowSize = aTabData.getLength();
2040 if (nRowSize <= 1)
2041 // There is no data to show. Bail out.
2042 return;
2044 SCCOL nColSize = aTabData[0].getLength();
2046 SCTAB nNewTab = GetViewData().GetTabNo();
2048 ScDocumentUniquePtr pInsDoc(new ScDocument(SCDOCMODE_CLIP));
2049 pInsDoc->ResetClip( &rDoc, nNewTab );
2050 for (SCROW nRow = 0; nRow < nRowSize; ++nRow)
2052 for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
2054 const Any& rAny = aTabData[nRow][nCol];
2055 OUString aStr;
2056 double fVal;
2057 if (rAny >>= aStr)
2059 pInsDoc->SetString(ScAddress(nCol,nRow,nNewTab), aStr);
2061 else if (rAny >>= fVal)
2062 pInsDoc->SetValue(nCol, nRow, nNewTab, fVal);
2066 // set number format (important for dates)
2067 for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
2069 OUString aStr;
2070 if (!(aTabData[0][nCol] >>= aStr))
2071 continue;
2073 Reference<XPropertySet> xPropSet(xDims->getByName(aStr), UNO_QUERY);
2074 if (!xPropSet.is())
2075 continue;
2077 Any any = xPropSet->getPropertyValue( SC_UNO_DP_NUMBERFO );
2078 sal_Int32 nNumFmt = 0;
2079 if (!(any >>= nNumFmt))
2080 continue;
2082 ScPatternAttr aPattern(pInsDoc->getCellAttributeHelper());
2083 aPattern.GetItemSet().Put( SfxUInt32Item(ATTR_VALUE_FORMAT, static_cast<sal_uInt32>(nNumFmt)) );
2084 pInsDoc->ApplyPatternAreaTab(nCol, 1, nCol, nRowSize-1, nNewTab, aPattern);
2087 SCCOL nEndCol = 0;
2088 SCROW nEndRow = 0;
2089 pInsDoc->GetCellArea( nNewTab, nEndCol, nEndRow );
2090 pInsDoc->SetClipArea( ScRange( 0, 0, nNewTab, nEndCol, nEndRow, nNewTab ) );
2092 SfxUndoManager* pMgr = GetViewData().GetDocShell()->GetUndoManager();
2093 OUString aUndo = ScResId( STR_UNDO_DOOUTLINE );
2094 pMgr->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2096 OUString aNewTabName;
2097 rDoc.CreateValidTabName(aNewTabName);
2098 if ( InsertTable(aNewTabName, nNewTab) )
2099 PasteFromClip( InsertDeleteFlags::ALL, pInsDoc.get() );
2101 pMgr->LeaveListAction();
2104 // repeat data base operations (sorting, filtering, subtotals)
2106 void ScDBFunc::RepeatDB( bool bRecord )
2108 SCCOL nCurX = GetViewData().GetCurX();
2109 SCROW nCurY = GetViewData().GetCurY();
2110 SCTAB nTab = GetViewData().GetTabNo();
2111 ScDocument& rDoc = GetViewData().GetDocument();
2112 ScDBData* pDBData = GetDBData();
2113 if (bRecord && !rDoc.IsUndoEnabled())
2114 bRecord = false;
2116 ScQueryParam aQueryParam;
2117 pDBData->GetQueryParam( aQueryParam );
2118 bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
2120 ScSortParam aSortParam;
2121 pDBData->GetSortParam( aSortParam );
2122 bool bSort = aSortParam.maKeyState[0].bDoSort;
2124 ScSubTotalParam aSubTotalParam;
2125 pDBData->GetSubTotalParam( aSubTotalParam );
2126 bool bSubTotal = aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly;
2128 if ( bQuery || bSort || bSubTotal )
2130 bool bQuerySize = false;
2131 ScRange aOldQuery;
2132 ScRange aNewQuery;
2133 if (bQuery && !aQueryParam.bInplace)
2135 ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
2136 aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
2137 if (pDest && pDest->IsDoSize())
2139 pDest->GetArea( aOldQuery );
2140 bQuerySize = true;
2144 SCTAB nDummy;
2145 SCCOL nStartCol;
2146 SCROW nStartRow;
2147 SCCOL nEndCol;
2148 SCROW nEndRow;
2149 pDBData->GetArea( nDummy, nStartCol, nStartRow, nEndCol, nEndRow );
2151 //! undo only needed data ?
2153 ScDocumentUniquePtr pUndoDoc;
2154 std::unique_ptr<ScOutlineTable> pUndoTab;
2155 std::unique_ptr<ScRangeName> pUndoRange;
2156 std::unique_ptr<ScDBCollection> pUndoDB;
2158 if (bRecord)
2160 SCTAB nTabCount = rDoc.GetTableCount();
2161 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2162 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
2163 if (pTable)
2165 pUndoTab.reset(new ScOutlineTable( *pTable ));
2167 SCCOLROW nOutStartCol; // row/column status
2168 SCCOLROW nOutStartRow;
2169 SCCOLROW nOutEndCol;
2170 SCCOLROW nOutEndRow;
2171 pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
2172 pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
2174 pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true );
2175 rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
2176 rDoc.CopyToDocument( 0, nOutStartRow, nTab, rDoc.MaxCol(), nOutEndRow, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
2178 else
2179 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
2181 // Record data range - including filter results
2182 rDoc.CopyToDocument( 0,nStartRow,nTab, rDoc.MaxCol(),nEndRow,nTab, InsertDeleteFlags::ALL, false, *pUndoDoc );
2184 // all formulas for reference
2185 rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(),rDoc.MaxRow(),nTabCount-1, InsertDeleteFlags::FORMULA, false, *pUndoDoc );
2187 // data base and other ranges
2188 ScRangeName* pDocRange = rDoc.GetRangeName();
2189 if (!pDocRange->empty())
2190 pUndoRange.reset(new ScRangeName( *pDocRange ));
2191 ScDBCollection* pDocDB = rDoc.GetDBCollection();
2192 if (!pDocDB->empty())
2193 pUndoDB.reset(new ScDBCollection( *pDocDB ));
2196 if (bSort && bSubTotal)
2198 // sort without subtotals
2200 aSubTotalParam.bRemoveOnly = true; // is reset below
2201 DoSubTotals( aSubTotalParam, false );
2204 if (bSort)
2206 pDBData->GetSortParam( aSortParam ); // range may have changed
2207 Sort( aSortParam, false, false);
2209 if (bQuery)
2211 pDBData->GetQueryParam( aQueryParam ); // range may have changed
2212 ScRange aAdvSource;
2213 if (pDBData->GetAdvancedQuerySource(aAdvSource))
2215 rDoc.CreateQueryParam(aAdvSource, aQueryParam);
2216 Query( aQueryParam, &aAdvSource, false );
2218 else
2219 Query( aQueryParam, nullptr, false );
2221 // if not inplace the sheet may have changed
2222 if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
2223 SetTabNo( nTab );
2225 if (bSubTotal)
2227 pDBData->GetSubTotalParam( aSubTotalParam ); // range may have changed
2228 aSubTotalParam.bRemoveOnly = false;
2229 DoSubTotals( aSubTotalParam, false );
2232 if (bRecord)
2234 SCTAB nDummyTab;
2235 SCCOL nDummyCol;
2236 SCROW nDummyRow, nNewEndRow;
2237 pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
2239 const ScRange* pOld = nullptr;
2240 const ScRange* pNew = nullptr;
2241 if (bQuerySize)
2243 ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
2244 aQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
2245 if (pDest)
2247 pDest->GetArea( aNewQuery );
2248 pOld = &aOldQuery;
2249 pNew = &aNewQuery;
2253 GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction(
2254 std::make_unique<ScUndoRepeatDB>( GetViewData().GetDocShell(), nTab,
2255 nStartCol, nStartRow, nEndCol, nEndRow,
2256 nNewEndRow,
2257 nCurX, nCurY,
2258 std::move(pUndoDoc), std::move(pUndoTab),
2259 std::move(pUndoRange), std::move(pUndoDB),
2260 pOld, pNew ) );
2263 GetViewData().GetDocShell()->PostPaint(
2264 ScRange(0, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
2265 PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top | PaintPartFlags::Size);
2267 else // "no not execute any operations"
2268 ErrorMessage(STR_MSSG_REPEATDB_0);
2271 void ScDBFunc::OnLOKShowHideColRow(bool bColumns, SCCOLROW nStart)
2273 if (!comphelper::LibreOfficeKit::isActive())
2274 return;
2276 SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
2277 SfxViewShell* pThisViewShell = GetViewData().GetViewShell();
2278 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
2279 while (pViewShell)
2281 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
2282 if (pTabViewShell && pTabViewShell->GetDocId() == pThisViewShell->GetDocId())
2284 if (bColumns)
2286 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
2287 pPosHelper->invalidateByIndex(nStart);
2289 else
2291 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
2292 pPosHelper->invalidateByIndex(nStart);
2295 if (pTabViewShell->getPart() == nCurrentTabIndex)
2297 pTabViewShell->ShowCursor();
2298 pTabViewShell->MarkDataChanged();
2301 pViewShell = SfxViewShell::GetNext(*pViewShell);
2305 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */