xref: /trunk/main/sc/source/ui/view/dbfunc3.cxx (revision b3f79822)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26 
27 
28 
29 // INCLUDE ---------------------------------------------------------------
30 
31 #include "dbfunc.hxx"
32 #include "scitems.hxx"
33 #include <sfx2/bindings.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/msgbox.hxx>
36 #include <vcl/sound.hxx>
37 #include <vcl/waitobj.hxx>
38 #include <svl/zforlist.hxx>
39 #include <sfx2/app.hxx>
40 #include <com/sun/star/beans/XPropertySet.hpp>
41 #include <com/sun/star/container/XNameAccess.hpp>
42 #include <com/sun/star/sheet/DataPilotFieldFilter.hpp>
43 #include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp>
44 #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
45 #include <com/sun/star/sheet/DataPilotFieldSortMode.hpp>
46 #include <com/sun/star/sheet/DataPilotTableHeaderData.hpp>
47 #include <com/sun/star/sheet/GeneralFunction.hpp>
48 #include <com/sun/star/sheet/MemberResultFlags.hpp>
49 #include <com/sun/star/sheet/XDimensionsSupplier.hpp>
50 #include <com/sun/star/sheet/XDrillDownDataSupplier.hpp>
51 
52 #include "global.hxx"
53 #include "globstr.hrc"
54 #include "sc.hrc"
55 #include "undotab.hxx"
56 #include "undodat.hxx"
57 #include "dbcolect.hxx"
58 #include "rangenam.hxx"
59 #include "rangeutl.hxx"
60 #include "docsh.hxx"
61 #include "olinetab.hxx"
62 #include "consoli.hxx"
63 #include "olinefun.hxx"
64 #include "dpobject.hxx"
65 #include "dpsave.hxx"
66 #include "dpdimsave.hxx"
67 #include "dbdocfun.hxx"
68 #include "dpoutput.hxx"
69 #include "dptabsrc.hxx"
70 #include "editable.hxx"
71 #include "docpool.hxx"
72 #include "patattr.hxx"
73 #include "unonames.hxx"
74 #include "cell.hxx"
75 #include "userlist.hxx"
76 
77 #include <hash_set>
78 #include <hash_map>
79 #include <memory>
80 #include <list>
81 #include <vector>
82 
83 using namespace com::sun::star;
84 using ::com::sun::star::uno::Any;
85 using ::com::sun::star::uno::Sequence;
86 using ::com::sun::star::uno::Reference;
87 using ::com::sun::star::uno::UNO_QUERY;
88 using ::com::sun::star::beans::XPropertySet;
89 using ::com::sun::star::container::XNameAccess;
90 using ::com::sun::star::sheet::XDimensionsSupplier;
91 using ::rtl::OUString;
92 using ::rtl::OUStringHash;
93 using ::rtl::OUStringBuffer;
94 using ::std::auto_ptr;
95 using ::std::list;
96 using ::std::vector;
97 using ::std::hash_map;
98 using ::std::hash_set;
99 
100 // STATIC DATA -----------------------------------------------------------
101 
102 
103 //==================================================================
104 
105 //
106 //			Outliner
107 //
108 
109 //	Outline-Gruppierung erzeugen
110 
111 void ScDBFunc::MakeOutline( sal_Bool bColumns, sal_Bool bRecord )
112 {
113 	ScRange aRange;
114 	if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
115 	{
116 		ScDocShell* pDocSh = GetViewData()->GetDocShell();
117 		ScOutlineDocFunc aFunc(*pDocSh);
118 		aFunc.MakeOutline( aRange, bColumns, bRecord, sal_False );
119 	}
120 	else
121 		ErrorMessage(STR_NOMULTISELECT);
122 }
123 
124 //	Outline-Gruppierung loeschen
125 
126 void ScDBFunc::RemoveOutline( sal_Bool bColumns, sal_Bool bRecord )
127 {
128 	ScRange aRange;
129 	if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
130 	{
131 		ScDocShell* pDocSh = GetViewData()->GetDocShell();
132 		ScOutlineDocFunc aFunc(*pDocSh);
133 		aFunc.RemoveOutline( aRange, bColumns, bRecord, sal_False );
134 	}
135 	else
136 		ErrorMessage(STR_NOMULTISELECT);
137 }
138 
139 //	Menue-Status: Outlines loeschen
140 
141 void ScDBFunc::TestRemoveOutline( sal_Bool& rCol, sal_Bool& rRow )
142 {
143 	sal_Bool bColFound = sal_False;
144 	sal_Bool bRowFound = sal_False;
145 
146 	SCCOL nStartCol, nEndCol;
147 	SCROW nStartRow, nEndRow;
148 	SCTAB nStartTab, nEndTab;
149 	if (GetViewData()->GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
150 	{
151 		SCTAB nTab = nStartTab;
152 		ScDocument* pDoc = GetViewData()->GetDocument();
153 		ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
154 		if (pTable)
155 		{
156 			ScOutlineArray* pArray;
157 			ScOutlineEntry* pEntry;
158 			SCCOLROW nStart;
159 			SCCOLROW nEnd;
160 			sal_Bool bColMarked = ( nStartRow == 0 && nEndRow == MAXROW );
161 			sal_Bool bRowMarked = ( nStartCol == 0 && nEndCol == MAXCOL );
162 
163 			//	Spalten
164 
165 			if ( !bRowMarked || bColMarked )		// nicht wenn ganze Zeilen markiert
166 			{
167 				pArray = pTable->GetColArray();
168 				ScSubOutlineIterator aColIter( pArray );
169 				while ((pEntry=aColIter.GetNext()) != NULL && !bColFound)
170 				{
171 					nStart = pEntry->GetStart();
172 					nEnd   = pEntry->GetEnd();
173 					if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
174 						bColFound = sal_True;
175 				}
176 			}
177 
178 			//	Zeilen
179 
180 			if ( !bColMarked || bRowMarked )		// nicht wenn ganze Spalten markiert
181 			{
182 				pArray = pTable->GetRowArray();
183 				ScSubOutlineIterator aRowIter( pArray );
184 				while ((pEntry=aRowIter.GetNext()) != NULL && !bRowFound)
185 				{
186 					nStart = pEntry->GetStart();
187 					nEnd   = pEntry->GetEnd();
188 					if ( nStartRow<=nEnd && nEndRow>=nStart )
189 						bRowFound = sal_True;
190 				}
191 			}
192 		}
193 	}
194 
195 	rCol = bColFound;
196 	rRow = bRowFound;
197 }
198 
199 void ScDBFunc::RemoveAllOutlines( sal_Bool bRecord )
200 {
201 	SCTAB nTab = GetViewData()->GetTabNo();
202 	ScDocShell* pDocSh = GetViewData()->GetDocShell();
203 	ScOutlineDocFunc aFunc(*pDocSh);
204 
205 	HideCursor();
206 	sal_Bool bOk = aFunc.RemoveAllOutlines( nTab, bRecord, sal_False );
207 	ShowCursor();
208 
209 	if (bOk)
210 		UpdateScrollBars();
211 }
212 
213 //	Auto-Outlines
214 
215 void ScDBFunc::AutoOutline( sal_Bool bRecord )
216 {
217 	SCTAB nTab = GetViewData()->GetTabNo();
218 	ScRange aRange( 0,0,nTab, MAXCOL,MAXROW,nTab );		// ganze Tabelle, wenn nichts markiert
219 	ScMarkData& rMark = GetViewData()->GetMarkData();
220 	if ( rMark.IsMarked() || rMark.IsMultiMarked() )
221 	{
222 		rMark.MarkToMulti();
223 		rMark.GetMultiMarkArea( aRange );
224 	}
225 
226 	ScDocShell* pDocSh = GetViewData()->GetDocShell();
227 	ScOutlineDocFunc aFunc(*pDocSh);
228 	aFunc.AutoOutline( aRange, bRecord, sal_False );
229 }
230 
231 //	Outline-Ebene auswaehlen
232 
233 void ScDBFunc::SelectLevel( sal_Bool bColumns, sal_uInt16 nLevel, sal_Bool bRecord, sal_Bool bPaint )
234 {
235 	SCTAB nTab = GetViewData()->GetTabNo();
236 	ScDocShell* pDocSh = GetViewData()->GetDocShell();
237 	ScOutlineDocFunc aFunc(*pDocSh);
238 
239 	HideCursor();
240 	sal_Bool bOk = aFunc.SelectLevel( nTab, bColumns, nLevel, bRecord, bPaint, sal_False );
241 	ShowCursor();
242 
243 	if (bOk)
244 		UpdateScrollBars();
245 }
246 
247 //	einzelne Outline-Gruppe einblenden
248 
249 void ScDBFunc::ShowOutline( sal_Bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, sal_Bool bRecord, sal_Bool bPaint )
250 {
251 	SCTAB nTab = GetViewData()->GetTabNo();
252 	ScDocShell* pDocSh = GetViewData()->GetDocShell();
253 	ScOutlineDocFunc aFunc(*pDocSh);
254 
255 	HideCursor();
256 	sal_Bool bOk = aFunc.ShowOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint, sal_False );
257 	ShowCursor();
258 
259 	if ( bOk && bPaint )
260 		UpdateScrollBars();
261 }
262 
263 //	einzelne Outline-Gruppe ausblenden
264 
265 void ScDBFunc::HideOutline( sal_Bool bColumns, sal_uInt16 nLevel, sal_uInt16 nEntry, sal_Bool bRecord, sal_Bool bPaint )
266 {
267 	SCTAB nTab = GetViewData()->GetTabNo();
268 	ScDocShell* pDocSh = GetViewData()->GetDocShell();
269 	ScOutlineDocFunc aFunc(*pDocSh);
270 
271 	HideCursor();
272 	sal_Bool bOk = aFunc.HideOutline( nTab, bColumns, nLevel, nEntry, bRecord, bPaint, sal_False );
273 	ShowCursor();
274 
275 	if ( bOk && bPaint )
276 		UpdateScrollBars();
277 }
278 
279 //	Menue-Status: markierten Bereich ein-/ausblenden
280 
281 sal_Bool ScDBFunc::OutlinePossible(sal_Bool bHide)
282 {
283 	sal_Bool bEnable = sal_False;
284 
285 	SCCOL nStartCol;
286 	SCROW nStartRow;
287 	SCTAB nStartTab;
288 	SCCOL nEndCol;
289 	SCROW nEndRow;
290 	SCTAB nEndTab;
291 
292 	if (GetViewData()->GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE)
293 	{
294 		ScDocument* pDoc = GetViewData()->GetDocument();
295 		SCTAB nTab = GetViewData()->GetTabNo();
296 		ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
297 		if (pTable)
298 		{
299 			ScOutlineArray* pArray;
300 			ScOutlineEntry* pEntry;
301 			SCCOLROW nStart;
302 			SCCOLROW nEnd;
303 
304 			//	Spalten
305 
306 			pArray = pTable->GetColArray();
307 			ScSubOutlineIterator aColIter( pArray );
308 			while ((pEntry=aColIter.GetNext()) != NULL && !bEnable)
309 			{
310 				nStart = pEntry->GetStart();
311 				nEnd   = pEntry->GetEnd();
312 				if ( bHide )
313 				{
314 					if ( nStartCol<=static_cast<SCCOL>(nEnd) && nEndCol>=static_cast<SCCOL>(nStart) )
315 						if (!pEntry->IsHidden())
316 							bEnable = sal_True;
317 				}
318 				else
319 				{
320 					if ( nStart>=nStartCol && nEnd<=nEndCol )
321 						if (pEntry->IsHidden())
322 							bEnable = sal_True;
323 				}
324 			}
325 
326 			//	Zeilen
327 
328 			pArray = pTable->GetRowArray();
329 			ScSubOutlineIterator aRowIter( pArray );
330 			while ((pEntry=aRowIter.GetNext()) != NULL)
331 			{
332 				nStart = pEntry->GetStart();
333 				nEnd   = pEntry->GetEnd();
334 				if ( bHide )
335 				{
336 					if ( nStartRow<=nEnd && nEndRow>=nStart )
337 						if (!pEntry->IsHidden())
338 							bEnable = sal_True;
339 				}
340 				else
341 				{
342 					if ( nStart>=nStartRow && nEnd<=nEndRow )
343 						if (pEntry->IsHidden())
344 							bEnable = sal_True;
345 				}
346 			}
347 		}
348 	}
349 
350 	return bEnable;
351 }
352 
353 //	markierten Bereich einblenden
354 
355 void ScDBFunc::ShowMarkedOutlines( sal_Bool bRecord )
356 {
357 	ScRange aRange;
358 	if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
359 	{
360 		ScDocShell* pDocSh = GetViewData()->GetDocShell();
361 		ScOutlineDocFunc aFunc(*pDocSh);
362 		HideCursor();
363 		sal_Bool bDone = aFunc.ShowMarkedOutlines( aRange, bRecord, sal_False );
364 		ShowCursor();
365 		if (bDone)
366 			UpdateScrollBars();
367 	}
368 	else
369 		ErrorMessage(STR_NOMULTISELECT);
370 }
371 
372 //	markierten Bereich ausblenden
373 
374 void ScDBFunc::HideMarkedOutlines( sal_Bool bRecord )
375 {
376 	ScRange aRange;
377 	if (GetViewData()->GetSimpleArea(aRange) == SC_MARK_SIMPLE)
378 	{
379 		ScDocShell* pDocSh = GetViewData()->GetDocShell();
380 		ScOutlineDocFunc aFunc(*pDocSh);
381 		HideCursor();
382 		sal_Bool bDone = aFunc.HideMarkedOutlines( aRange, bRecord, sal_False );
383 		ShowCursor();
384 		if (bDone)
385 			UpdateScrollBars();
386 	}
387 	else
388 		ErrorMessage(STR_NOMULTISELECT);
389 }
390 
391 //	--------------------------------------------------------------------------
392 
393 //
394 //			Teilergebnisse
395 //
396 
397 void ScDBFunc::DoSubTotals( const ScSubTotalParam& rParam, sal_Bool bRecord,
398 							const ScSortParam* pForceNewSort )
399 {
400 	sal_Bool bDo = !rParam.bRemoveOnly;							// sal_False = nur loeschen
401 
402 	ScDocShell* pDocSh = GetViewData()->GetDocShell();
403 	ScDocument* pDoc = pDocSh->GetDocument();
404 	ScMarkData& rMark = GetViewData()->GetMarkData();
405 	SCTAB nTab = GetViewData()->GetTabNo();
406 	if (bRecord && !pDoc->IsUndoEnabled())
407 		bRecord = sal_False;
408 
409 	ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
410 												rParam.nCol2, rParam.nRow2 );
411 	if (!pDBData)
412 	{
413 		DBG_ERROR( "SubTotals: keine DBData" );
414 		return;
415 	}
416 
417 	ScEditableTester aTester( pDoc, nTab, 0,rParam.nRow1+1, MAXCOL,MAXROW );
418 	if (!aTester.IsEditable())
419 	{
420 		ErrorMessage(aTester.GetMessageId());
421 		return;
422 	}
423 
424 	if (pDoc->HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
425 						 rParam.nCol2, rParam.nRow2, nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ))
426 	{
427 		ErrorMessage(STR_MSSG_INSERTCELLS_0);	// nicht in zusammengefasste einfuegen
428 		return;
429 	}
430 
431 	WaitObject aWait( GetViewData()->GetDialogParent() );
432 	sal_Bool bOk = sal_True;
433 	sal_Bool bDelete = sal_False;
434 	if (rParam.bReplace)
435 		if (pDoc->TestRemoveSubTotals( nTab, rParam ))
436 		{
437 			bDelete = sal_True;
438 			bOk = ( MessBox( GetViewData()->GetDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
439 				// "StarCalc" "Daten loeschen?"
440 				ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ),
441 				ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_1 ) ).Execute()
442 				== RET_YES );
443 		}
444 
445 	if (bOk)
446 	{
447 		ScDocShellModificator aModificator( *pDocSh );
448 
449 		ScSubTotalParam aNewParam( rParam );		// Bereichsende wird veraendert
450 		ScDocument*		pUndoDoc = NULL;
451 		ScOutlineTable*	pUndoTab = NULL;
452 		ScRangeName*	pUndoRange = NULL;
453 		ScDBCollection* pUndoDB = NULL;
454 		SCTAB 			nTabCount = 0;				// fuer Referenz-Undo
455 
456 		if (bRecord)										// alte Daten sichern
457 		{
458 			sal_Bool bOldFilter = bDo && rParam.bDoSort;
459 
460 			nTabCount = pDoc->GetTableCount();
461 			pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
462 			ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
463 			if (pTable)
464 			{
465 				pUndoTab = new ScOutlineTable( *pTable );
466 
467 				SCCOLROW nOutStartCol;							// Zeilen/Spaltenstatus
468 				SCCOLROW nOutStartRow;
469 				SCCOLROW nOutEndCol;
470 				SCCOLROW nOutEndRow;
471 				pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
472 				pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
473 
474 				pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
475 				pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, sal_False, pUndoDoc );
476 				pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, sal_False, pUndoDoc );
477 			}
478 			else
479 				pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, bOldFilter );
480 
481 			//	Datenbereich sichern - incl. Filter-Ergebnis
482 			pDoc->CopyToDocument( 0,rParam.nRow1+1,nTab, MAXCOL,rParam.nRow2,nTab,
483 									IDF_ALL, sal_False, pUndoDoc );
484 
485 			//	alle Formeln wegen Referenzen
486 			pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1,
487 										IDF_FORMULA, sal_False, pUndoDoc );
488 
489 			//	DB- und andere Bereiche
490 			ScRangeName* pDocRange = pDoc->GetRangeName();
491 			if (pDocRange->GetCount())
492 				pUndoRange = new ScRangeName( *pDocRange );
493 			ScDBCollection* pDocDB = pDoc->GetDBCollection();
494 			if (pDocDB->GetCount())
495 				pUndoDB = new ScDBCollection( *pDocDB );
496 		}
497 
498 //		pDoc->SetOutlineTable( nTab, NULL );
499 		ScOutlineTable*	pOut = pDoc->GetOutlineTable( nTab );
500 		if (pOut)
501 			pOut->GetRowArray()->RemoveAll();		// nur Zeilen-Outlines loeschen
502 
503 		if (rParam.bReplace)
504 			pDoc->RemoveSubTotals( nTab, aNewParam );
505 		sal_Bool bSuccess = sal_True;
506 		if (bDo)
507 		{
508 			// Sortieren
509 			if ( rParam.bDoSort || pForceNewSort )
510 			{
511 				pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
512 
513 				//	Teilergebnis-Felder vor die Sortierung setzen
514 				//	(doppelte werden weggelassen, kann darum auch wieder aufgerufen werden)
515 
516 				ScSortParam aOldSort;
517 				pDBData->GetSortParam( aOldSort );
518 				ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
519 				Sort( aSortParam, sal_False, sal_False );
520 			}
521 
522 			bSuccess = pDoc->DoSubTotals( nTab, aNewParam );
523 		}
524 		ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
525 			aNewParam.nCol2, aNewParam.nRow2, nTab );
526 		pDoc->SetDirty( aDirtyRange );
527 
528 		if (bRecord)
529 		{
530 //			ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
531 			pDocSh->GetUndoManager()->AddUndoAction(
532 				new ScUndoSubTotals( pDocSh, nTab,
533 										rParam, aNewParam.nRow2,
534 										pUndoDoc, pUndoTab, // pUndoDBData,
535 										pUndoRange, pUndoDB ) );
536 		}
537 
538 		if (!bSuccess)
539 		{
540 			// "Kann keine Zeilen einfuegen"
541 			ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
542 		}
543 
544 													// merken
545 		pDBData->SetSubTotalParam( aNewParam );
546 		pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
547 		pDoc->CompileDBFormula();
548 
549 		DoneBlockMode();
550 		InitOwnBlockMode();
551 		rMark.SetMarkArea( ScRange( aNewParam.nCol1,aNewParam.nRow1,nTab,
552 									aNewParam.nCol2,aNewParam.nRow2,nTab ) );
553         MarkDataChanged();
554 
555 		pDocSh->PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab,
556 												PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE );
557 
558 		aModificator.SetDocumentModified();
559 
560 		SelectionChanged();
561 	}
562 }
563 
564 //
565 //			Consolidate
566 //
567 
568 void ScDBFunc::Consolidate( const ScConsolidateParam& rParam, sal_Bool bRecord )
569 {
570 	ScDocShell* pDocShell = GetViewData()->GetDocShell();
571 	pDocShell->DoConsolidate( rParam, bRecord );
572 	SetTabNo( rParam.nTab, sal_True );
573 }
574 
575 //
576 //			Pivot
577 //
578 
579 String lcl_MakePivotTabName( const String& rPrefix, SCTAB nNumber )
580 {
581 	String aName = rPrefix;
582 	aName += String::CreateFromInt32( nNumber );
583 	return aName;
584 }
585 
586 bool ScDBFunc::MakePivotTable( const ScDPSaveData& rData, const ScRange& rDest, sal_Bool bNewTable,
587 								const ScDPObject& rSource, sal_Bool bApi )
588 {
589 	//	#70096# error message if no fields are set
590 	//	this must be removed when drag&drop of fields from a toolbox is available
591 
592 	if ( rData.IsEmpty() && !bApi )
593 	{
594 		ErrorMessage(STR_PIVOT_NODATA);
595 		return false;
596 	}
597 
598 	ScDocShell* pDocSh	= GetViewData()->GetDocShell();
599 	ScDocument* pDoc 	= GetViewData()->GetDocument();
600 	sal_Bool bUndo(pDoc->IsUndoEnabled());
601 
602 	ScRange aDestRange = rDest;
603 	if ( bNewTable )
604 	{
605 		SCTAB nSrcTab = GetViewData()->GetTabNo();
606 
607 		String aName( ScGlobal::GetRscString(STR_PIVOT_TABLE) );
608 		String aStr;
609 
610 		pDoc->GetName( nSrcTab, aStr );
611 		aName += '_';
612 		aName += aStr;
613 		aName += '_';
614 
615 		SCTAB nNewTab = nSrcTab+1;
616 
617 		SCTAB i=1;
618 		while ( !pDoc->InsertTab( nNewTab, lcl_MakePivotTabName( aName, i ) ) && i <= MAXTAB )
619 			i++;
620 
621 		sal_Bool bAppend = ( nNewTab+1 == pDoc->GetTableCount() );
622 		if (bUndo)
623 		{
624 			pDocSh->GetUndoManager()->AddUndoAction(
625 						new ScUndoInsertTab( pDocSh, nNewTab, bAppend, lcl_MakePivotTabName( aName, i ) ));
626 		}
627 
628 		GetViewData()->InsertTab( nNewTab );
629 		SetTabNo( nNewTab, sal_True );
630 
631 		aDestRange = ScRange( 0, 0, nNewTab );
632 	}
633 
634 	ScDPObject* pDPObj = pDoc->GetDPAtCursor(
635 							aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aStart.Tab() );
636 
637 	ScDPObject aObj( rSource );
638 	aObj.SetOutRange( aDestRange );
639     if ( pDPObj && !rData.GetExistingDimensionData() )
640     {
641         // copy dimension data from old object - lost in the dialog
642         //! change the dialog to keep the dimension data
643 
644         ScDPSaveData aNewData( rData );
645         const ScDPSaveData* pOldData = pDPObj->GetSaveData();
646         if ( pOldData )
647         {
648             const ScDPDimensionSaveData* pDimSave = pOldData->GetExistingDimensionData();
649             aNewData.SetDimensionData( pDimSave );
650         }
651         aObj.SetSaveData( aNewData );
652     }
653     else
654         aObj.SetSaveData( rData );
655 
656 	sal_Bool bAllowMove = ( pDPObj != NULL );	// allow re-positioning when editing existing table
657 
658 	ScDBDocFunc aFunc( *pDocSh );
659 	bool bSuccess = aFunc.DataPilotUpdate( pDPObj, &aObj, sal_True, sal_False, bAllowMove );
660 
661 	CursorPosChanged();		// shells may be switched
662 
663 	if ( bNewTable )
664 	{
665 		pDocSh->PostPaintExtras();
666 		SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_TABLES_CHANGED ) );
667 	}
668 
669 	return bSuccess;
670 }
671 
672 void ScDBFunc::DeletePivotTable()
673 {
674 	ScDocShell* pDocSh	  = GetViewData()->GetDocShell();
675 	ScDocument* pDoc	  = pDocSh->GetDocument();
676 	ScDPObject*	pDPObj    = pDoc->GetDPAtCursor( GetViewData()->GetCurX(),
677 												  GetViewData()->GetCurY(),
678 												  GetViewData()->GetTabNo() );
679 	if ( pDPObj )
680 	{
681 		ScDBDocFunc aFunc( *pDocSh );
682 		aFunc.DataPilotUpdate( pDPObj, NULL, sal_True, sal_False );
683 		CursorPosChanged();		// shells may be switched
684 	}
685 	else
686 		ErrorMessage(STR_PIVOT_NOTFOUND);
687 }
688 sal_uLong RefreshDPObject( ScDPObject *pDPObj, ScDocument *pDoc, ScDocShell *pDocSh, sal_Bool bRecord, sal_Bool bApi )
689 {
690 	if( !pDPObj )
691 		return STR_PIVOT_NOTFOUND;
692 
693     if ( pDocSh && !pDoc )
694         pDoc = pDocSh->GetDocument();
695 
696 	if( !pDoc  )
697         return static_cast<sal_uLong>(-1);
698 
699 	if( !pDocSh && ( pDocSh = PTR_CAST( ScDocShell, pDoc->GetDocumentShell() ) ) == NULL )
700         return static_cast<sal_uLong>(-1);
701 
702 	if( sal_uLong nErrId = pDPObj->RefreshCache() )
703 		return nErrId;
704 	else if ( nErrId == 0 )
705 	{
706 		//Refresh all dpobjects
707 		ScDPCollection* pDPCollection = pDoc->GetDPCollection();
708 		sal_uInt16 nCount = pDPCollection->GetCount();
709 		for (sal_uInt16 i=0; i<nCount; i++)
710 		{
711 			if ( (*pDPCollection)[i]->GetCacheId() == pDPObj->GetCacheId()  )
712 			{
713 				ScDBDocFunc aFunc( * pDocSh );
714 				if ( !aFunc.DataPilotUpdate( (*pDPCollection)[i], (*pDPCollection)[i], bRecord, bApi ) )
715 					break;
716 			}
717 		}
718 
719 		return nErrId;
720 	}
721 
722 	return 0U;
723 }
724 
725 sal_uLong  ScDBFunc::RecalcPivotTable()
726 {
727 	ScDocShell* pDocSh  = GetViewData()->GetDocShell();
728 	ScDocument* pDoc	= GetViewData()->GetDocument();
729 
730 	//	old pivot not used any more
731 
732 	ScDPObject*	pDPObj	= pDoc->GetDPAtCursor( GetViewData()->GetCurX(),
733 												  GetViewData()->GetCurY(),
734 												  GetViewData()->GetTabNo() );
735 	if ( pDPObj )
736 	{
737         // Wang Xu Ming -- 2009-6-17
738         // DataPilot Migration
739         //ScDBDocFunc aFunc( *pDocSh );
740         //aFunc.DataPilotUpdate( pDPObj, pDPObj, sal_True, sal_False );
741         //CursorPosChanged();      // shells may be switched
742         sal_uLong nErrId = RefreshDPObject( pDPObj, pDoc, pDocSh, sal_True, sal_False );//pDPObj->RefreshCache();
743         if ( nErrId == 0 )
744         {
745             // There is no undo for the refresh of the cache table, but the undo history for cell changes
746             // remains valid and should be preserved, so the history isn't cleared here.
747             //GetViewData()->GetDocShell()->GetUndoManager()->Clear();
748         }
749         else if (nErrId <= USHRT_MAX)
750             ErrorMessage(static_cast<sal_uInt16>(nErrId));
751       return nErrId;
752       // End Comments
753 	}
754 	else
755 		ErrorMessage(STR_PIVOT_NOTFOUND);
756     return STR_PIVOT_NOTFOUND;
757 }
758 
759 void ScDBFunc::GetSelectedMemberList( ScStrCollection& rEntries, long& rDimension )
760 {
761     ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
762             							GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
763     if ( !pDPObj )
764         return;
765 
766     long nStartDimension = -1;
767     long nStartHierarchy = -1;
768     long nStartLevel     = -1;
769 
770     ScRangeListRef xRanges;
771     GetViewData()->GetMultiArea( xRanges );         // incl. cursor if nothing is selected
772     sal_uLong nRangeCount = xRanges->Count();
773     sal_Bool bContinue = sal_True;
774 
775     for (sal_uLong nRangePos=0; nRangePos<nRangeCount && bContinue; nRangePos++)
776     {
777         ScRange aRange = *xRanges->GetObject(nRangePos);
778         SCCOL nStartCol = aRange.aStart.Col();
779         SCROW nStartRow = aRange.aStart.Row();
780         SCCOL nEndCol = aRange.aEnd.Col();
781         SCROW nEndRow = aRange.aEnd.Row();
782         SCTAB nTab = aRange.aStart.Tab();
783 
784         for (SCROW nRow=nStartRow; nRow<=nEndRow && bContinue; nRow++)
785             for (SCCOL nCol=nStartCol; nCol<=nEndCol && bContinue; nCol++)
786             {
787                 sheet::DataPilotTableHeaderData aData;
788                 pDPObj->GetHeaderPositionData(ScAddress(nCol, nRow, nTab), aData);
789                 if ( aData.Dimension < 0 )
790                     bContinue = sal_False;              // not part of any dimension
791                 else
792                 {
793                     if ( nStartDimension < 0 )      // first member?
794                     {
795                         nStartDimension = aData.Dimension;
796                         nStartHierarchy = aData.Hierarchy;
797                         nStartLevel     = aData.Level;
798                     }
799                     if ( aData.Dimension != nStartDimension ||
800                          aData.Hierarchy != nStartHierarchy ||
801                          aData.Level     != nStartLevel )
802                     {
803                         bContinue = sal_False;          // cannot mix dimensions
804                     }
805                 }
806                 if ( bContinue )
807                 {
808                     // accept any part of a member description, also subtotals,
809                     // but don't stop if empty parts are contained
810                     if ( aData.Flags & sheet::MemberResultFlags::HASMEMBER )
811                     {
812                         StrData* pNew = new StrData( aData.MemberName );
813                         if ( !rEntries.Insert( pNew ) )
814                             delete pNew;
815                     }
816                 }
817             }
818     }
819 
820     rDimension = nStartDimension;   // dimension from which the found members came
821     if (!bContinue)
822         rEntries.FreeAll();         // remove all if not valid
823 }
824 
825 sal_Bool ScDBFunc::HasSelectionForDateGroup( ScDPNumGroupInfo& rOldInfo, sal_Int32& rParts )
826 {
827     // determine if the date group dialog has to be shown for the current selection
828 
829     sal_Bool bFound = sal_False;
830 
831     SCCOL nCurX = GetViewData()->GetCurX();
832     SCROW nCurY = GetViewData()->GetCurY();
833     SCTAB nTab = GetViewData()->GetTabNo();
834     ScDocument* pDoc = GetViewData()->GetDocument();
835 
836     ScDPObject* pDPObj = pDoc->GetDPAtCursor( nCurX, nCurY, nTab );
837     if ( pDPObj )
838     {
839         ScStrCollection aEntries;
840         long nSelectDimension = -1;
841         GetSelectedMemberList( aEntries, nSelectDimension );
842 
843         if ( aEntries.GetCount() > 0 )
844         {
845             sal_Bool bIsDataLayout;
846             String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
847             String aBaseDimName( aDimName );
848 
849             sal_Bool bInGroupDim = sal_False;
850             sal_Bool bFoundParts = sal_False;
851 
852             ScDPDimensionSaveData* pDimData =
853                 const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
854             if ( pDimData )
855             {
856                 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
857                 const ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDim( aDimName );
858                 if ( pNumGroupDim )
859                 {
860                     //  existing num group dimension
861 
862                     if ( pNumGroupDim->GetDatePart() != 0 )
863                     {
864                         //  dimension has date info -> edit settings of this dimension
865                         //  (parts are collected below)
866 
867                         rOldInfo = pNumGroupDim->GetDateInfo();
868                         bFound = sal_True;
869                     }
870                     else if ( pNumGroupDim->GetInfo().DateValues )
871                     {
872                         //  Numerical grouping with DateValues flag is used for grouping
873                         //  of days with a "Number of days" value.
874 
875                         rOldInfo = pNumGroupDim->GetInfo();
876                         rParts = com::sun::star::sheet::DataPilotFieldGroupBy::DAYS;               // not found in CollectDateParts
877                         bFoundParts = sal_True;
878                         bFound = sal_True;
879                     }
880                     bInGroupDim = sal_True;
881                 }
882                 else if ( pGroupDim )
883                 {
884                     //  existing additional group dimension
885 
886                     if ( pGroupDim->GetDatePart() != 0 )
887                     {
888                         //  dimension has date info -> edit settings of this dimension
889                         //  (parts are collected below)
890 
891                         rOldInfo = pGroupDim->GetDateInfo();
892                         aBaseDimName = pGroupDim->GetSourceDimName();
893                         bFound = sal_True;
894                     }
895                     bInGroupDim = sal_True;
896                 }
897             }
898             if ( bFound && !bFoundParts )
899             {
900                 // collect date parts from all group dimensions
901                 rParts = pDimData->CollectDateParts( aBaseDimName );
902             }
903             if ( !bFound && !bInGroupDim )
904             {
905                 // create new date group dimensions if the selection is a single cell
906                 // in a normal dimension with date content
907 
908                 ScRange aSelRange;
909                 if ( (GetViewData()->GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
910                         aSelRange.aStart == aSelRange.aEnd )
911                 {
912                     SCCOL nSelCol = aSelRange.aStart.Col();
913                     SCROW nSelRow = aSelRange.aStart.Row();
914                     SCTAB nSelTab = aSelRange.aStart.Tab();
915                     if ( pDoc->HasValueData( nSelCol, nSelRow, nSelTab ) )
916                     {
917                         sal_uLong nIndex = static_cast<const SfxUInt32Item*>(pDoc->GetAttr(
918                                         nSelCol, nSelRow, nSelTab, ATTR_VALUE_FORMAT))->GetValue();
919                         short nType = pDoc->GetFormatTable()->GetType(nIndex);
920                         if ( nType == NUMBERFORMAT_DATE || nType == NUMBERFORMAT_TIME || nType == NUMBERFORMAT_DATETIME )
921                         {
922                             bFound = sal_True;
923                             // use currently selected value for automatic limits
924                             if( rOldInfo.AutoStart )
925                                 rOldInfo.Start = pDoc->GetValue( aSelRange.aStart );
926                             if( rOldInfo.AutoEnd )
927                                 rOldInfo.End = pDoc->GetValue( aSelRange.aStart );
928                         }
929                     }
930                 }
931             }
932         }
933     }
934 
935     return bFound;
936 }
937 
938 sal_Bool ScDBFunc::HasSelectionForNumGroup( ScDPNumGroupInfo& rOldInfo )
939 {
940     // determine if the numeric group dialog has to be shown for the current selection
941 
942     sal_Bool bFound = sal_False;
943 
944     SCCOL nCurX = GetViewData()->GetCurX();
945     SCROW nCurY = GetViewData()->GetCurY();
946     SCTAB nTab = GetViewData()->GetTabNo();
947     ScDocument* pDoc = GetViewData()->GetDocument();
948 
949     ScDPObject* pDPObj = pDoc->GetDPAtCursor( nCurX, nCurY, nTab );
950     if ( pDPObj )
951     {
952         ScStrCollection aEntries;
953         long nSelectDimension = -1;
954         GetSelectedMemberList( aEntries, nSelectDimension );
955 
956         if ( aEntries.GetCount() > 0 )
957         {
958             sal_Bool bIsDataLayout;
959             String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
960 
961             sal_Bool bInGroupDim = sal_False;
962 
963             ScDPDimensionSaveData* pDimData =
964                 const_cast<ScDPDimensionSaveData*>( pDPObj->GetSaveData()->GetExistingDimensionData() );
965             if ( pDimData )
966             {
967                 const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
968                 if ( pNumGroupDim )
969                 {
970                     //  existing num group dimension
971                     //  -> edit settings of this dimension
972 
973                     rOldInfo = pNumGroupDim->GetInfo();
974                     bFound = sal_True;
975                 }
976                 else if ( pDimData->GetNamedGroupDim( aDimName ) )
977                     bInGroupDim = sal_True;                                    // in a group dimension
978             }
979             if ( !bFound && !bInGroupDim )
980             {
981                 // create a new num group dimension if the selection is a single cell
982                 // in a normal dimension with numeric content
983 
984                 ScRange aSelRange;
985                 if ( (GetViewData()->GetSimpleArea( aSelRange ) == SC_MARK_SIMPLE) &&
986                         aSelRange.aStart == aSelRange.aEnd )
987                 {
988                     if ( pDoc->HasValueData( aSelRange.aStart.Col(), aSelRange.aStart.Row(),
989                                              aSelRange.aStart.Tab() ) )
990                     {
991                         bFound = sal_True;
992                         // use currently selected value for automatic limits
993                         if( rOldInfo.AutoStart )
994                             rOldInfo.Start = pDoc->GetValue( aSelRange.aStart );
995                         if( rOldInfo.AutoEnd )
996                             rOldInfo.End = pDoc->GetValue( aSelRange.aStart );
997                     }
998                 }
999             }
1000         }
1001     }
1002 
1003     return bFound;
1004 }
1005 
1006 void ScDBFunc::DateGroupDataPilot( const ScDPNumGroupInfo& rInfo, sal_Int32 nParts )
1007 {
1008     ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
1009                                         GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
1010     if ( pDPObj )
1011     {
1012         ScStrCollection aEntries;
1013         long nSelectDimension = -1;
1014         GetSelectedMemberList( aEntries, nSelectDimension );
1015 
1016         if ( aEntries.GetCount() > 0 )
1017         {
1018             sal_Bool bIsDataLayout;
1019             String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1020 
1021             ScDPSaveData aData( *pDPObj->GetSaveData() );
1022             ScDPDimensionSaveData* pDimData = aData.GetDimensionData();     // created if not there
1023 
1024             // find original base
1025             String aBaseDimName = aDimName;
1026             if( const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName ) )
1027                 aBaseDimName = pBaseGroupDim->GetSourceDimName();
1028 
1029             // remove all existing parts (the grouping is built completely new)
1030 
1031             /*  Remove numeric group dimension (exists once at most). No need
1032                 to delete anything in save data (grouping was done inplace in
1033                 an existing base dimension). */
1034             pDimData->RemoveNumGroupDimension( aBaseDimName );
1035 
1036             /*  Remove named group dimension(s). Collect deleted dimension
1037                 names which may be reused while recreating the groups.
1038                 Dimensions have to be removed from dimension save data and from
1039                 save data too. */
1040             std::vector< String > aDeletedNames;
1041             const ScDPSaveGroupDimension* pExistingGroup = pDimData->GetGroupDimForBase( aBaseDimName );
1042             while ( pExistingGroup )
1043             {
1044                 String aGroupDimName = pExistingGroup->GetGroupDimName();
1045                 pDimData->RemoveGroupDimension( aGroupDimName );     // pExistingGroup is deleted
1046 
1047                 // also remove SaveData settings for the dimension that no longer exists
1048                 aData.RemoveDimensionByName( aGroupDimName );
1049 
1050                 /*  The name can be used for the new group dimensions, although
1051                     it is still in use with the DataPilotSource. */
1052                 aDeletedNames.push_back( aGroupDimName );
1053 
1054                 // see if there are more group dimensions
1055                 pExistingGroup = pDimData->GetGroupDimForBase( aBaseDimName );
1056 
1057                 if ( pExistingGroup && pExistingGroup->GetGroupDimName() == aGroupDimName )
1058                 {
1059                     // still get the same group dimension?
1060                     DBG_ERROR("couldn't remove group dimension");
1061                     pExistingGroup = NULL;      // avoid endless loop
1062                 }
1063             }
1064 
1065             if ( nParts )
1066             {
1067                 // create date group dimensions
1068 
1069                 ScDPNumGroupInfo aEmpty;
1070                 bool bFirst = true;
1071                 sal_Int32 nMask = 1;
1072                 for (sal_uInt16 nBit=0; nBit<32; nBit++)
1073                 {
1074                     if ( nParts & nMask )
1075                     {
1076                         if ( bFirst )
1077                         {
1078                             // innermost part: create NumGroupDimension (replacing original values)
1079                             // Dimension name is left unchanged
1080 
1081                             if ( (nParts == sheet::DataPilotFieldGroupBy::DAYS) && (rInfo.Step >= 1.0) )
1082                             {
1083                                 // only days, and a step value specified: use numerical grouping
1084                                 // with DateValues flag, not date grouping
1085 
1086                                 ScDPNumGroupInfo aNumInfo( rInfo );
1087                                 aNumInfo.DateValues = sal_True;
1088 
1089                                 ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, aNumInfo );
1090                                 pDimData->AddNumGroupDimension( aNumGroupDim );
1091                             }
1092                             else
1093                             {
1094                                 ScDPSaveNumGroupDimension aNumGroupDim( aBaseDimName, rInfo, nMask );
1095                                 pDimData->AddNumGroupDimension( aNumGroupDim );
1096                             }
1097 
1098                             bFirst = false;
1099                         }
1100                         else
1101                         {
1102                             // additional parts: create GroupDimension (shown as additional dimensions)
1103                             String aGroupDimName = pDimData->CreateDateGroupDimName( nMask, *pDPObj, true, &aDeletedNames );
1104                             ScDPSaveGroupDimension aGroupDim( aBaseDimName, aGroupDimName );
1105                             aGroupDim.SetDateInfo( rInfo, nMask );
1106                             pDimData->AddGroupDimension( aGroupDim );
1107 
1108                             // set orientation
1109                             ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
1110                             if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
1111                             {
1112                                 ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aBaseDimName );
1113                                 pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
1114                                 long nPosition = 0;     //! before (immediate) base
1115                                 aData.SetPosition( pSaveDimension, nPosition );
1116                             }
1117                         }
1118                     }
1119                     nMask *= 2;
1120                 }
1121             }
1122 
1123             // apply changes
1124             ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
1125             ScDPObject* pNewObj = new ScDPObject( *pDPObj );
1126             pNewObj->SetSaveData( aData );
1127             aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
1128             delete pNewObj;
1129 
1130             // unmark cell selection
1131             Unmark();
1132         }
1133     }
1134 }
1135 
1136 void ScDBFunc::NumGroupDataPilot( const ScDPNumGroupInfo& rInfo )
1137 {
1138     ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
1139                                         GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
1140     if ( pDPObj )
1141     {
1142         ScStrCollection aEntries;
1143         long nSelectDimension = -1;
1144         GetSelectedMemberList( aEntries, nSelectDimension );
1145 
1146         if ( aEntries.GetCount() > 0 )
1147         {
1148             sal_Bool bIsDataLayout;
1149             String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1150 
1151             ScDPSaveData aData( *pDPObj->GetSaveData() );
1152             ScDPDimensionSaveData* pDimData = aData.GetDimensionData();     // created if not there
1153 
1154             ScDPSaveNumGroupDimension* pExisting = pDimData->GetNumGroupDimAcc( aDimName );
1155             if ( pExisting )
1156             {
1157                 // modify existing group dimension
1158                 pExisting->SetGroupInfo( rInfo );
1159             }
1160             else
1161             {
1162                 // create new group dimension
1163                 ScDPSaveNumGroupDimension aNumGroupDim( aDimName, rInfo );
1164                 pDimData->AddNumGroupDimension( aNumGroupDim );
1165             }
1166 
1167             // apply changes
1168             ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
1169             ScDPObject* pNewObj = new ScDPObject( *pDPObj );
1170             pNewObj->SetSaveData( aData );
1171             aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
1172             delete pNewObj;
1173 
1174             // unmark cell selection
1175             Unmark();
1176         }
1177     }
1178 }
1179 
1180 void ScDBFunc::GroupDataPilot()
1181 {
1182     ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
1183                                         GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
1184     if ( pDPObj )
1185     {
1186         ScStrCollection aEntries;
1187         long nSelectDimension = -1;
1188         GetSelectedMemberList( aEntries, nSelectDimension );
1189 
1190         if ( aEntries.GetCount() > 0 )
1191         {
1192             sal_Bool bIsDataLayout;
1193             String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1194 
1195             ScDPSaveData aData( *pDPObj->GetSaveData() );
1196             ScDPDimensionSaveData* pDimData = aData.GetDimensionData();     // created if not there
1197 
1198             // find original base
1199             String aBaseDimName( aDimName );
1200             const ScDPSaveGroupDimension* pBaseGroupDim = pDimData->GetNamedGroupDim( aDimName );
1201             if ( pBaseGroupDim )
1202             {
1203                 // any entry's SourceDimName is the original base
1204                 aBaseDimName = pBaseGroupDim->GetSourceDimName();
1205             }
1206 
1207             // find existing group dimension
1208             // (using the selected dim, can be intermediate group dim)
1209             ScDPSaveGroupDimension* pGroupDimension = pDimData->GetGroupDimAccForBase( aDimName );
1210 
1211             // remove the selected items from their groups
1212             // (empty groups are removed, too)
1213             sal_uInt16 nEntryCount = aEntries.GetCount();
1214             sal_uInt16 nEntry;
1215             if ( pGroupDimension )
1216             {
1217                 for (nEntry=0; nEntry<nEntryCount; nEntry++)
1218                 {
1219                     String aEntryName = aEntries[nEntry]->GetString();
1220                     if ( pBaseGroupDim )
1221                     {
1222                         // for each selected (intermediate) group, remove all its items
1223                         // (same logic as for adding, below)
1224                         const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
1225                         if ( pBaseGroup )
1226                             pBaseGroup->RemoveElementsFromGroups( *pGroupDimension );   // remove all elements
1227                         else
1228                             pGroupDimension->RemoveFromGroups( aEntryName );
1229                     }
1230                     else
1231                         pGroupDimension->RemoveFromGroups( aEntryName );
1232                 }
1233             }
1234 
1235             ScDPSaveGroupDimension* pNewGroupDim = NULL;
1236             if ( !pGroupDimension )
1237             {
1238                 // create a new group dimension
1239                 String aGroupDimName = pDimData->CreateGroupDimName( aBaseDimName, *pDPObj, false, NULL );
1240                 pNewGroupDim = new ScDPSaveGroupDimension( aBaseDimName, aGroupDimName );
1241 
1242                 pGroupDimension = pNewGroupDim;     // make changes to the new dim if none existed
1243 
1244                 if ( pBaseGroupDim )
1245                 {
1246                     // If it's a higher-order group dimension, pre-allocate groups for all
1247                     // non-selected original groups, so the individual base members aren't
1248                     // used for automatic groups (this would make the original groups hard
1249                     // to find).
1250                     //! Also do this when removing groups?
1251                     //! Handle this case dynamically with automatic groups?
1252 
1253                     long nGroupCount = pBaseGroupDim->GetGroupCount();
1254                     for ( long nGroup = 0; nGroup < nGroupCount; nGroup++ )
1255                     {
1256                         const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetGroupByIndex( nGroup );
1257 
1258                         StrData aStrData( pBaseGroup->GetGroupName() );
1259                         sal_uInt16 nCollIndex;
1260                         if ( !aEntries.Search( &aStrData, nCollIndex ) )    //! ignore case?
1261                         {
1262                             // add an additional group for each item that is not in the selection
1263                             ScDPSaveGroupItem aGroup( pBaseGroup->GetGroupName() );
1264                             aGroup.AddElementsFromGroup( *pBaseGroup );
1265                             pGroupDimension->AddGroupItem( aGroup );
1266                         }
1267                     }
1268                 }
1269             }
1270             String aGroupDimName = pGroupDimension->GetGroupDimName();
1271 
1272             //! localized prefix string
1273             String aGroupName = pGroupDimension->CreateGroupName( String::CreateFromAscii("Group") );
1274             ScDPSaveGroupItem aGroup( aGroupName );
1275             for (nEntry=0; nEntry<nEntryCount; nEntry++)
1276             {
1277                 String aEntryName = aEntries[nEntry]->GetString();
1278                 if ( pBaseGroupDim )
1279                 {
1280                     // for each selected (intermediate) group, add all its items
1281                     const ScDPSaveGroupItem* pBaseGroup = pBaseGroupDim->GetNamedGroup( aEntryName );
1282                     if ( pBaseGroup )
1283                         aGroup.AddElementsFromGroup( *pBaseGroup );
1284                     else
1285                         aGroup.AddElement( aEntryName );    // no group found -> automatic group, add the item itself
1286                 }
1287                 else
1288                     aGroup.AddElement( aEntryName );        // no group dimension, add all items directly
1289             }
1290 
1291             pGroupDimension->AddGroupItem( aGroup );
1292 
1293             if ( pNewGroupDim )
1294             {
1295                 pDimData->AddGroupDimension( *pNewGroupDim );
1296                 delete pNewGroupDim;        // AddGroupDimension copies the object
1297                 // don't access pGroupDimension after here
1298             }
1299             pGroupDimension = pNewGroupDim = NULL;
1300 
1301             // set orientation
1302             ScDPSaveDimension* pSaveDimension = aData.GetDimensionByName( aGroupDimName );
1303             if ( pSaveDimension->GetOrientation() == sheet::DataPilotFieldOrientation_HIDDEN )
1304             {
1305                 ScDPSaveDimension* pOldDimension = aData.GetDimensionByName( aDimName );
1306                 pSaveDimension->SetOrientation( pOldDimension->GetOrientation() );
1307                 long nPosition = 0;     //! before (immediate) base
1308                 aData.SetPosition( pSaveDimension, nPosition );
1309             }
1310 
1311             // apply changes
1312             ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
1313             ScDPObject* pNewObj = new ScDPObject( *pDPObj );
1314             pNewObj->SetSaveData( aData );
1315             aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
1316             delete pNewObj;
1317 
1318             // unmark cell selection
1319             Unmark();
1320         }
1321     }
1322 }
1323 
1324 void ScDBFunc::UngroupDataPilot()
1325 {
1326     ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
1327                                         GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
1328     if ( pDPObj )
1329     {
1330         ScStrCollection aEntries;
1331         long nSelectDimension = -1;
1332         GetSelectedMemberList( aEntries, nSelectDimension );
1333 
1334         if ( aEntries.GetCount() > 0 )
1335         {
1336             sal_Bool bIsDataLayout;
1337             String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
1338 
1339             ScDPSaveData aData( *pDPObj->GetSaveData() );
1340             ScDPDimensionSaveData* pDimData = aData.GetDimensionData();     // created if not there
1341             //! test first if DimensionData exists?
1342 
1343             sal_Bool bApply = sal_False;
1344 
1345             ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
1346             const ScDPSaveNumGroupDimension* pNumGroupDim = pDimData->GetNumGroupDim( aDimName );
1347             if ( ( pGroupDim && pGroupDim->GetDatePart() != 0 ) ||
1348                  ( pNumGroupDim && pNumGroupDim->GetDatePart() != 0 ) )
1349             {
1350                 // Date grouping: need to remove all affected group dimensions.
1351                 // This is done using DateGroupDataPilot with nParts=0.
1352 
1353                 DateGroupDataPilot( ScDPNumGroupInfo(), 0 );
1354                 // bApply remains FALSE
1355                 // dimension pointers become invalid
1356             }
1357             else if ( pGroupDim )
1358             {
1359                 sal_uInt16 nEntryCount = aEntries.GetCount();
1360                 for (sal_uInt16 nEntry=0; nEntry<nEntryCount; nEntry++)
1361                 {
1362                     String aEntryName = aEntries[nEntry]->GetString();
1363                     pGroupDim->RemoveGroup( aEntryName );
1364                 }
1365                 // remove group dimension if empty
1366                 bool bEmptyDim = pGroupDim->IsEmpty();
1367                 if ( !bEmptyDim )
1368                 {
1369                     // If all remaining groups in the dimension aren't shown, remove
1370                     // the dimension too, as if it was completely empty.
1371                     ScStrCollection aVisibleEntries;
1372                     pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
1373                     bEmptyDim = pGroupDim->HasOnlyHidden( aVisibleEntries );
1374                 }
1375                 if ( bEmptyDim )
1376                 {
1377                     pDimData->RemoveGroupDimension( aDimName );     // pGroupDim is deleted
1378 
1379                     // also remove SaveData settings for the dimension that no longer exists
1380                     aData.RemoveDimensionByName( aDimName );
1381                 }
1382                 bApply = sal_True;
1383             }
1384             else if ( pNumGroupDim )
1385             {
1386                 // remove the numerical grouping
1387                 pDimData->RemoveNumGroupDimension( aDimName );
1388                 // SaveData settings can remain unchanged - the same dimension still exists
1389                 bApply = sal_True;
1390             }
1391 
1392             if ( bApply )
1393             {
1394                 // apply changes
1395                 ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
1396                 ScDPObject* pNewObj = new ScDPObject( *pDPObj );
1397                 pNewObj->SetSaveData( aData );
1398                 aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
1399                 delete pNewObj;
1400 
1401                 // unmark cell selection
1402                 Unmark();
1403             }
1404         }
1405     }
1406 }
1407 
1408 static OUString lcl_replaceMemberNameInSubtotal(const OUString& rSubtotal, const OUString& rMemberName)
1409 {
1410     sal_Int32 n = rSubtotal.getLength();
1411     const sal_Unicode* p = rSubtotal.getStr();
1412     OUStringBuffer aBuf, aWordBuf;
1413     for (sal_Int32 i = 0; i < n; ++i)
1414     {
1415         sal_Unicode c = p[i];
1416         if (c == sal_Unicode(' '))
1417         {
1418             OUString aWord = aWordBuf.makeStringAndClear();
1419             if (aWord.equals(rMemberName))
1420                 aBuf.append(sal_Unicode('?'));
1421             else
1422                 aBuf.append(aWord);
1423             aBuf.append(c);
1424         }
1425         else if (c == sal_Unicode('\\'))
1426         {
1427             // Escape a backslash character.
1428             aWordBuf.append(c);
1429             aWordBuf.append(c);
1430         }
1431         else if (c == sal_Unicode('?'))
1432         {
1433             // A literal '?' must be escaped with a backslash ('\');
1434             aWordBuf.append(sal_Unicode('\\'));
1435             aWordBuf.append(c);
1436         }
1437         else
1438             aWordBuf.append(c);
1439     }
1440 
1441     if (aWordBuf.getLength() > 0)
1442     {
1443         OUString aWord = aWordBuf.makeStringAndClear();
1444         if (aWord.equals(rMemberName))
1445             aBuf.append(sal_Unicode('?'));
1446         else
1447             aBuf.append(aWord);
1448     }
1449 
1450     return aBuf.makeStringAndClear();
1451 }
1452 
1453 void ScDBFunc::DataPilotInput( const ScAddress& rPos, const String& rString )
1454 {
1455     using namespace ::com::sun::star::sheet;
1456 
1457     String aNewName( rString );
1458 
1459     ScDocument* pDoc = GetViewData()->GetDocument();
1460     ScDPObject* pDPObj = pDoc->GetDPAtCursor( rPos.Col(), rPos.Row(), rPos.Tab() );
1461     if (!pDPObj)
1462         return;
1463 
1464     String aOldText;
1465     pDoc->GetString( rPos.Col(), rPos.Row(), rPos.Tab(), aOldText );
1466 
1467     if ( aOldText == rString )
1468     {
1469         // nothing to do: silently exit
1470         return;
1471     }
1472 
1473     sal_uInt16 nErrorId = 0;
1474 
1475     pDPObj->BuildAllDimensionMembers();
1476     ScDPSaveData aData( *pDPObj->GetSaveData() );
1477     sal_Bool bChange = sal_False;
1478 
1479     sal_uInt16 nOrient = DataPilotFieldOrientation_HIDDEN;
1480     long nField = pDPObj->GetHeaderDim( rPos, nOrient );
1481     if ( nField >= 0 )
1482     {
1483         // changing a field title
1484         if ( aData.GetExistingDimensionData() )
1485         {
1486             // only group dimensions can be renamed
1487 
1488             ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1489             ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aOldText );
1490             if ( pGroupDim )
1491             {
1492                 // valid name: not empty, no existing dimension (group or other)
1493                 if ( rString.Len() && !pDPObj->IsDimNameInUse(rString) )
1494                 {
1495                     pGroupDim->Rename( aNewName );
1496 
1497                     // also rename in SaveData to preserve the field settings
1498                     ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aOldText );
1499                     pSaveDim->SetName( aNewName );
1500 
1501                     bChange = sal_True;
1502                 }
1503                 else
1504                     nErrorId = STR_INVALIDNAME;
1505             }
1506         }
1507         else if (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW)
1508         {
1509             sal_Bool bDataLayout = false;
1510             String aDimName = pDPObj->GetDimName(nField, bDataLayout);
1511             ScDPSaveDimension* pDim = bDataLayout ? aData.GetDataLayoutDimension() : aData.GetDimensionByName(aDimName);
1512             if (pDim)
1513             {
1514                 if (rString.Len())
1515                 {
1516                     if (rString.EqualsIgnoreCaseAscii(aDimName))
1517                     {
1518                         pDim->RemoveLayoutName();
1519                         bChange = true;
1520                     }
1521                     else if (!pDPObj->IsDimNameInUse(rString))
1522                     {
1523                         pDim->SetLayoutName(rString);
1524                         bChange = true;
1525                     }
1526                     else
1527                         nErrorId = STR_INVALIDNAME;
1528                 }
1529                 else
1530                     nErrorId = STR_INVALIDNAME;
1531             }
1532         }
1533     }
1534     else if (pDPObj->IsDataDescriptionCell(rPos))
1535     {
1536         // There is only one data dimension.
1537         ScDPSaveDimension* pDim = aData.GetFirstDimension(sheet::DataPilotFieldOrientation_DATA);
1538         if (pDim)
1539         {
1540             if (rString.Len())
1541             {
1542                 if (rString.EqualsIgnoreCaseAscii(pDim->GetName()))
1543                 {
1544                     pDim->RemoveLayoutName();
1545                     bChange = true;
1546                 }
1547                 else if (!pDPObj->IsDimNameInUse(rString))
1548                 {
1549                     pDim->SetLayoutName(rString);
1550                     bChange = true;
1551                 }
1552                 else
1553                     nErrorId = STR_INVALIDNAME;
1554             }
1555             else
1556                 nErrorId = STR_INVALIDNAME;
1557         }
1558     }
1559     else
1560     {
1561         // This is not a field header.
1562         sheet::DataPilotTableHeaderData aPosData;
1563         pDPObj->GetHeaderPositionData(rPos, aPosData);
1564 
1565         if ( (aPosData.Flags & MemberResultFlags::HASMEMBER) && aOldText.Len() )
1566         {
1567             if ( aData.GetExistingDimensionData() && !(aPosData.Flags & MemberResultFlags::SUBTOTAL))
1568             {
1569                 sal_Bool bIsDataLayout;
1570                 String aDimName = pDPObj->GetDimName( aPosData.Dimension, bIsDataLayout );
1571 
1572                 ScDPDimensionSaveData* pDimData = aData.GetDimensionData();
1573                 ScDPSaveGroupDimension* pGroupDim = pDimData->GetNamedGroupDimAcc( aDimName );
1574                 if ( pGroupDim )
1575                 {
1576                     // valid name: not empty, no existing group in this dimension
1577                     //! ignore case?
1578                     if ( aNewName.Len() && !pGroupDim->GetNamedGroup( aNewName ) )
1579                     {
1580                         ScDPSaveGroupItem* pGroup = pGroupDim->GetNamedGroupAcc( aOldText );
1581                         if ( pGroup )
1582                             pGroup->Rename( aNewName );     // rename the existing group
1583                         else
1584                         {
1585                             // create a new group to replace the automatic group
1586                             ScDPSaveGroupItem aGroup( aNewName );
1587                             aGroup.AddElement( aOldText );
1588                             pGroupDim->AddGroupItem( aGroup );
1589                         }
1590 
1591                         // in both cases also adjust savedata, to preserve member settings (show details)
1592                         ScDPSaveDimension* pSaveDim = aData.GetDimensionByName( aDimName );
1593                         ScDPSaveMember* pSaveMember = pSaveDim->GetExistingMemberByName( aOldText );
1594                         if ( pSaveMember )
1595                             pSaveMember->SetName( aNewName );
1596 
1597                         bChange = sal_True;
1598                     }
1599                     else
1600                         nErrorId = STR_INVALIDNAME;
1601                  }
1602             }
1603             else if ((aPosData.Flags & MemberResultFlags::GRANDTOTAL))
1604             {
1605                 aData.SetGrandTotalName(rString);
1606                 bChange = true;
1607             }
1608             else if (aPosData.Dimension >= 0 && aPosData.MemberName.getLength() > 0)
1609             {
1610                 sal_Bool bDataLayout = false;
1611                 String aDimName = pDPObj->GetDimName(static_cast<long>(aPosData.Dimension), bDataLayout);
1612                 if (bDataLayout)
1613                 {
1614                     // data dimension
1615                     do
1616                     {
1617                         if ((aPosData.Flags & MemberResultFlags::SUBTOTAL))
1618                             break;
1619 
1620                         ScDPSaveDimension* pDim = aData.GetDimensionByName(aPosData.MemberName);
1621                         if (!pDim)
1622                             break;
1623 
1624                         if (!rString.Len())
1625                         {
1626                             nErrorId = STR_INVALIDNAME;
1627                             break;
1628                         }
1629 
1630                         if (aPosData.MemberName.equalsIgnoreAsciiCase(rString))
1631                         {
1632                             pDim->RemoveLayoutName();
1633                             bChange = true;
1634                         }
1635                         else if (!pDPObj->IsDimNameInUse(rString))
1636                         {
1637                             pDim->SetLayoutName(rString);
1638                             bChange = true;
1639                         }
1640                         else
1641                             nErrorId = STR_INVALIDNAME;
1642                     }
1643                     while (false);
1644                 }
1645                 else
1646                 {
1647                     // field member
1648                     do
1649                     {
1650                         ScDPSaveDimension* pDim = aData.GetDimensionByName(aDimName);
1651                         if (!pDim)
1652                             break;
1653 
1654                         ScDPSaveMember* pMem = pDim->GetExistingMemberByName(aPosData.MemberName);
1655                         if (!pMem)
1656                             break;
1657 
1658                         if ((aPosData.Flags & MemberResultFlags::SUBTOTAL))
1659                         {
1660                             // Change subtotal only when the table has one data dimension.
1661                             if (aData.GetDataDimensionCount() > 1)
1662                                 break;
1663 
1664                             // display name for subtotal is allowed only if the subtotal type is 'Automatic'.
1665                             if (pDim->GetSubTotalsCount() != 1)
1666                                 break;
1667 
1668                             if (pDim->GetSubTotalFunc(0) != sheet::GeneralFunction_AUTO)
1669                                 break;
1670 
1671                             const OUString* pLayoutName = pMem->GetLayoutName();
1672                             String aMemberName;
1673                             if (pLayoutName)
1674                                 aMemberName = *pLayoutName;
1675                             else
1676                                 aMemberName = aPosData.MemberName;
1677 
1678                             String aNew = lcl_replaceMemberNameInSubtotal(rString, aMemberName);
1679                             pDim->SetSubtotalName(aNew);
1680                             bChange = true;
1681                         }
1682                         else
1683                         {
1684                             // Check to make sure the member name isn't
1685                             // already used.
1686                             if (rString.Len())
1687                             {
1688                                 if (rString.EqualsIgnoreCaseAscii(pMem->GetName()))
1689                                 {
1690                                     pMem->RemoveLayoutName();
1691                                     bChange = true;
1692                                 }
1693                                 else if (!pDim->IsMemberNameInUse(rString))
1694                                 {
1695                                     pMem->SetLayoutName(rString);
1696                                     bChange = true;
1697                                 }
1698                                 else
1699                                     nErrorId = STR_INVALIDNAME;
1700                             }
1701                             else
1702                                 nErrorId = STR_INVALIDNAME;
1703                         }
1704                     }
1705                     while (false);
1706                 }
1707             }
1708         }
1709     }
1710 
1711     if ( bChange )
1712     {
1713         // apply changes
1714         ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
1715         ScDPObject* pNewObj = new ScDPObject( *pDPObj );
1716         pNewObj->SetSaveData( aData );
1717         aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
1718         delete pNewObj;
1719     }
1720     else
1721     {
1722         if ( !nErrorId )
1723             nErrorId = STR_ERR_DATAPILOT_INPUT;
1724         ErrorMessage( nErrorId );
1725     }
1726 }
1727 
1728 void lcl_MoveToEnd( ScDPSaveDimension& rDim, const String& rItemName )
1729 {
1730     ScDPSaveMember* pNewMember = NULL;
1731     const ScDPSaveMember* pOldMember = rDim.GetExistingMemberByName( rItemName );
1732     if ( pOldMember )
1733         pNewMember = new ScDPSaveMember( *pOldMember );
1734     else
1735         pNewMember = new ScDPSaveMember( rItemName );
1736     rDim.AddMember( pNewMember );
1737     // AddMember takes ownership of the new pointer,
1738     // puts it to the end of the list even if it was in the list before.
1739 }
1740 
1741 struct ScOUStringCollate
1742 {
1743     CollatorWrapper* mpCollator;
1744 
1745     ScOUStringCollate(CollatorWrapper* pColl) : mpCollator(pColl) {}
1746 
1747     bool operator()(const rtl::OUString& rStr1, const rtl::OUString& rStr2) const
1748     {
1749         return ( mpCollator->compareString(rStr1, rStr2) < 0 );
1750     }
1751 };
1752 
1753 bool ScDBFunc::DataPilotSort( const ScAddress& rPos, bool bAscending, sal_uInt16* pUserListId )
1754 {
1755     ScDocument* pDoc = GetViewData()->GetDocument();
1756     ScDPObject* pDPObj = pDoc->GetDPAtCursor(rPos.Col(), rPos.Row(), rPos.Tab());
1757     if (!pDPObj)
1758         return false;
1759 
1760     // We need to run this to get all members later.
1761     if ( pUserListId )
1762         pDPObj->BuildAllDimensionMembers();
1763 
1764     sal_uInt16 nOrientation;
1765     long nDimIndex = pDPObj->GetHeaderDim(rPos, nOrientation);
1766     if (nDimIndex < 0)
1767         // Invalid dimension index.  Bail out.
1768         return false;
1769 
1770     sal_Bool bDataLayout;
1771     ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1772     if (!pSaveData)
1773         return false;
1774 
1775     ScDPSaveData aNewSaveData(*pSaveData);
1776     String aDimName = pDPObj->GetDimName(nDimIndex, bDataLayout);
1777     ScDPSaveDimension* pSaveDim = aNewSaveData.GetDimensionByName(aDimName);
1778     if (!pSaveDim)
1779         return false;
1780 
1781     // manual evaluation of sort order is only needed if a user list id is given
1782     if ( pUserListId )
1783     {
1784         typedef ScDPSaveDimension::MemberList MemList;
1785         const MemList& rDimMembers = pSaveDim->GetMembers();
1786         list<OUString> aMembers;
1787         hash_set<OUString, ::rtl::OUStringHash> aMemberSet;
1788         size_t nMemberCount = 0;
1789         for (MemList::const_iterator itr = rDimMembers.begin(), itrEnd = rDimMembers.end();
1790               itr != itrEnd; ++itr)
1791         {
1792             ScDPSaveMember* pMem = *itr;
1793             aMembers.push_back(pMem->GetName());
1794             aMemberSet.insert(pMem->GetName());
1795             ++nMemberCount;
1796         }
1797 
1798         // Sort the member list in ascending order.
1799         ScOUStringCollate aCollate( ScGlobal::GetCollator() );
1800         aMembers.sort(aCollate);
1801 
1802         // Collect and rank those custom sort strings that also exist in the member name list.
1803 
1804         typedef hash_map<OUString, sal_uInt16, OUStringHash> UserSortMap;
1805         UserSortMap aSubStrs;
1806         sal_uInt16 nSubCount = 0;
1807         if (pUserListId)
1808         {
1809             ScUserList* pUserList = ScGlobal::GetUserList();
1810             if (!pUserList)
1811                 return false;
1812 
1813             {
1814                 sal_uInt16 n = pUserList->GetCount();
1815                 if (!n || *pUserListId >= n)
1816                     return false;
1817             }
1818 
1819             ScUserListData* pData = static_cast<ScUserListData*>((*pUserList)[*pUserListId]);
1820             if (pData)
1821             {
1822                 sal_uInt16 n = pData->GetSubCount();
1823                 for (sal_uInt16 i = 0; i < n; ++i)
1824                 {
1825                     OUString aSub = pData->GetSubStr(i);
1826                     if (!aMemberSet.count(aSub))
1827                         // This string doesn't exist in the member name set.  Don't add this.
1828                         continue;
1829 
1830                     aSubStrs.insert(UserSortMap::value_type(aSub, nSubCount++));
1831                 }
1832             }
1833         }
1834 
1835         // Rank all members.
1836 
1837         vector<OUString> aRankedNames(nMemberCount);
1838         sal_uInt16 nCurStrId = 0;
1839         for (list<OUString>::const_iterator itr = aMembers.begin(), itrEnd = aMembers.end();
1840               itr != itrEnd; ++itr)
1841         {
1842             OUString aName = *itr;
1843             sal_uInt16 nRank = 0;
1844             UserSortMap::const_iterator itrSub = aSubStrs.find(aName);
1845             if (itrSub == aSubStrs.end())
1846                 nRank = nSubCount + nCurStrId++;
1847             else
1848                 nRank = itrSub->second;
1849 
1850             if (!bAscending)
1851                 nRank = static_cast< sal_uInt16 >( nMemberCount - nRank - 1 );
1852 
1853             aRankedNames[nRank] = aName;
1854         }
1855 
1856         // Re-order ScDPSaveMember instances with the new ranks.
1857 
1858         for (vector<OUString>::const_iterator itr = aRankedNames.begin(), itrEnd = aRankedNames.end();
1859               itr != itrEnd; ++itr)
1860         {
1861             const ScDPSaveMember* pOldMem = pSaveDim->GetExistingMemberByName(*itr);
1862             if (!pOldMem)
1863                 // All members are supposed to be present.
1864                 continue;
1865 
1866             ScDPSaveMember* pNewMem = new ScDPSaveMember(*pOldMem);
1867             pSaveDim->AddMember(pNewMem);
1868         }
1869 
1870         // Set the sorting mode to manual for now.  We may introduce a new sorting
1871         // mode later on.
1872 
1873         sheet::DataPilotFieldSortInfo aSortInfo;
1874         aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
1875         pSaveDim->SetSortInfo(&aSortInfo);
1876     }
1877     else
1878     {
1879         // without user list id, just apply sorting mode
1880 
1881         sheet::DataPilotFieldSortInfo aSortInfo;
1882         aSortInfo.Mode = sheet::DataPilotFieldSortMode::NAME;
1883         aSortInfo.IsAscending = bAscending;
1884         pSaveDim->SetSortInfo(&aSortInfo);
1885     }
1886 
1887     // Update the datapilot with the newly sorted field members.
1888 
1889     auto_ptr<ScDPObject> pNewObj(new ScDPObject(*pDPObj));
1890     pNewObj->SetSaveData(aNewSaveData);
1891     ScDBDocFunc aFunc(*GetViewData()->GetDocShell());
1892 
1893     return aFunc.DataPilotUpdate(pDPObj, pNewObj.get(), true, false);
1894 }
1895 
1896 sal_Bool ScDBFunc::DataPilotMove( const ScRange& rSource, const ScAddress& rDest )
1897 {
1898     sal_Bool bRet = sal_False;
1899     ScDocument* pDoc = GetViewData()->GetDocument();
1900     ScDPObject* pDPObj = pDoc->GetDPAtCursor( rSource.aStart.Col(), rSource.aStart.Row(), rSource.aStart.Tab() );
1901     if ( pDPObj && pDPObj == pDoc->GetDPAtCursor( rDest.Col(), rDest.Row(), rDest.Tab() ) )
1902     {
1903         sheet::DataPilotTableHeaderData aDestData;
1904         pDPObj->GetHeaderPositionData( rDest, aDestData );
1905         bool bValid = ( aDestData.Dimension >= 0 );        // dropping onto a field
1906 
1907         // look through the source range
1908         std::hash_set< rtl::OUString, rtl::OUStringHash, std::equal_to<rtl::OUString> > aMembersSet;   // for lookup
1909         std::vector< rtl::OUString > aMembersVector;  // members in original order, for inserting
1910         aMembersVector.reserve( std::max( static_cast<SCSIZE>( rSource.aEnd.Col() - rSource.aStart.Col() + 1 ),
1911                                           static_cast<SCSIZE>( rSource.aEnd.Row() - rSource.aStart.Row() + 1 ) ) );
1912         for (SCROW nRow = rSource.aStart.Row(); bValid && nRow <= rSource.aEnd.Row(); ++nRow )
1913             for (SCCOL nCol = rSource.aStart.Col(); bValid && nCol <= rSource.aEnd.Col(); ++nCol )
1914             {
1915                 sheet::DataPilotTableHeaderData aSourceData;
1916                 pDPObj->GetHeaderPositionData( ScAddress( nCol, nRow, rSource.aStart.Tab() ), aSourceData );
1917                 if ( aSourceData.Dimension == aDestData.Dimension && aSourceData.MemberName.getLength() )
1918                 {
1919                     if ( aMembersSet.find( aSourceData.MemberName ) == aMembersSet.end() )
1920                     {
1921                         aMembersSet.insert( aSourceData.MemberName );
1922                         aMembersVector.push_back( aSourceData.MemberName );
1923                     }
1924                     // duplicates are ignored
1925                 }
1926                 else
1927                     bValid = false;     // empty (subtotal) or different field
1928             }
1929 
1930         if ( bValid )
1931         {
1932             sal_Bool bIsDataLayout;
1933             String aDimName = pDPObj->GetDimName( aDestData.Dimension, bIsDataLayout );
1934             if ( !bIsDataLayout )
1935             {
1936                 ScDPSaveData aData( *pDPObj->GetSaveData() );
1937                 ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
1938 
1939                 // get all member names in source order
1940                 uno::Sequence<rtl::OUString> aMemberNames;
1941                 pDPObj->GetMemberNames( aDestData.Dimension, aMemberNames );
1942 
1943                 bool bInserted = false;
1944 
1945                 sal_Int32 nMemberCount = aMemberNames.getLength();
1946                 for (sal_Int32 nMemberPos=0; nMemberPos<nMemberCount; ++nMemberPos)
1947                 {
1948                     String aMemberStr( aMemberNames[nMemberPos] );
1949 
1950                     if ( !bInserted && aMemberNames[nMemberPos] == aDestData.MemberName )
1951                     {
1952                         // insert dragged items before this item
1953                         for ( std::vector<rtl::OUString>::const_iterator aIter = aMembersVector.begin();
1954                               aIter != aMembersVector.end(); ++aIter )
1955                             lcl_MoveToEnd( *pDim, *aIter );
1956                         bInserted = true;
1957                     }
1958 
1959                     if ( aMembersSet.find( aMemberStr ) == aMembersSet.end() )  // skip dragged items
1960                         lcl_MoveToEnd( *pDim, aMemberStr );
1961                 }
1962                 // insert dragged item at end if dest wasn't found (for example, empty)
1963                 if ( !bInserted )
1964                     for ( std::vector<rtl::OUString>::const_iterator aIter = aMembersVector.begin();
1965                           aIter != aMembersVector.end(); ++aIter )
1966                         lcl_MoveToEnd( *pDim, *aIter );
1967 
1968                 // Items that were in SaveData, but not in the source, end up at the start of the list.
1969 
1970                 // set flag for manual sorting
1971                 sheet::DataPilotFieldSortInfo aSortInfo;
1972                 aSortInfo.Mode = sheet::DataPilotFieldSortMode::MANUAL;
1973                 pDim->SetSortInfo( &aSortInfo );
1974 
1975                 // apply changes
1976                 ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
1977                 ScDPObject* pNewObj = new ScDPObject( *pDPObj );
1978                 pNewObj->SetSaveData( aData );
1979                 aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );      //! bApi for drag&drop?
1980                 delete pNewObj;
1981 
1982                 Unmark();       // entry was moved - no use in leaving the old cell selected
1983 
1984                 bRet = sal_True;
1985             }
1986         }
1987     }
1988 
1989     return bRet;
1990 }
1991 
1992 sal_Bool ScDBFunc::HasSelectionForDrillDown( sal_uInt16& rOrientation )
1993 {
1994     sal_Bool bRet = sal_False;
1995 
1996     ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
1997             							GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
1998     if ( pDPObj )
1999     {
2000         ScStrCollection aEntries;
2001         long nSelectDimension = -1;
2002         GetSelectedMemberList( aEntries, nSelectDimension );
2003 
2004         if ( aEntries.GetCount() > 0 )
2005         {
2006             sal_Bool bIsDataLayout;
2007             String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
2008             if ( !bIsDataLayout )
2009             {
2010                 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
2011             	ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName( aDimName );
2012             	if ( pDim )
2013             	{
2014             	    sal_uInt16 nDimOrient = pDim->GetOrientation();
2015             	    ScDPSaveDimension* pInner = pSaveData->GetInnermostDimension( nDimOrient );
2016             	    if ( pDim == pInner )
2017             	    {
2018             	        rOrientation = nDimOrient;
2019             	        bRet = sal_True;
2020             	    }
2021             	}
2022             }
2023         }
2024     }
2025 
2026     return bRet;
2027 }
2028 
2029 void ScDBFunc::SetDataPilotDetails( sal_Bool bShow, const String* pNewDimensionName )
2030 {
2031     ScDPObject* pDPObj = GetViewData()->GetDocument()->GetDPAtCursor( GetViewData()->GetCurX(),
2032             							GetViewData()->GetCurY(), GetViewData()->GetTabNo() );
2033     if ( pDPObj )
2034     {
2035         ScStrCollection aEntries;
2036         long nSelectDimension = -1;
2037         GetSelectedMemberList( aEntries, nSelectDimension );
2038 
2039         if ( aEntries.GetCount() > 0 )
2040         {
2041             sal_Bool bIsDataLayout;
2042             String aDimName = pDPObj->GetDimName( nSelectDimension, bIsDataLayout );
2043             if ( !bIsDataLayout )
2044             {
2045                 ScDPSaveData aData( *pDPObj->GetSaveData() );
2046                 ScDPSaveDimension* pDim = aData.GetDimensionByName( aDimName );
2047 
2048                 if ( bShow && pNewDimensionName )
2049                 {
2050                     //  add the new dimension with the same orientation, at the end
2051 
2052                     ScDPSaveDimension* pNewDim = aData.GetDimensionByName( *pNewDimensionName );
2053                     ScDPSaveDimension* pDuplicated = NULL;
2054                     if ( pNewDim->GetOrientation() == sheet::DataPilotFieldOrientation_DATA )
2055                     {
2056                         // Need to duplicate the dimension, create column/row in addition to data:
2057                         // The duplicated dimension inherits the existing settings, pNewDim is modified below.
2058                         pDuplicated = aData.DuplicateDimension( *pNewDimensionName );
2059                     }
2060 
2061                     sal_uInt16 nOrientation = pDim->GetOrientation();
2062                     pNewDim->SetOrientation( nOrientation );
2063 
2064                     long nPosition = LONG_MAX;
2065                     aData.SetPosition( pNewDim, nPosition );
2066 
2067                     ScDPSaveDimension* pDataLayout = aData.GetDataLayoutDimension();
2068                     if ( pDataLayout->GetOrientation() == nOrientation &&
2069                          aData.GetDataDimensionCount() <= 1 )
2070                     {
2071                         // If there is only one data dimension, the data layout dimension
2072                         // must still be the last one in its orientation.
2073                         aData.SetPosition( pDataLayout, nPosition );
2074                     }
2075 
2076                     if ( pDuplicated )
2077                     {
2078                         // The duplicated (data) dimension needs to be behind the original dimension
2079                         aData.SetPosition( pDuplicated, nPosition );
2080                     }
2081 
2082                     //  Hide details for all visible members (selected are changed below).
2083                     //! Use all members from source level instead (including non-visible)?
2084 
2085                     ScStrCollection aVisibleEntries;
2086                     pDPObj->GetMemberResultNames( aVisibleEntries, nSelectDimension );
2087 
2088                     sal_uInt16 nVisCount = aVisibleEntries.GetCount();
2089                     for (sal_uInt16 nVisPos=0; nVisPos<nVisCount; nVisPos++)
2090                     {
2091                         String aVisName = aVisibleEntries[nVisPos]->GetString();
2092                         ScDPSaveMember* pMember = pDim->GetMemberByName( aVisName );
2093                         pMember->SetShowDetails( sal_False );
2094                     }
2095                 }
2096 
2097                 sal_uInt16 nEntryCount = aEntries.GetCount();
2098                 for (sal_uInt16 nEntry=0; nEntry<nEntryCount; nEntry++)
2099                 {
2100                     String aEntryName = aEntries[nEntry]->GetString();
2101                     ScDPSaveMember* pMember = pDim->GetMemberByName( aEntryName );
2102                     pMember->SetShowDetails( bShow );
2103                 }
2104 
2105                 // apply changes
2106                 ScDBDocFunc aFunc( *GetViewData()->GetDocShell() );
2107                 ScDPObject* pNewObj = new ScDPObject( *pDPObj );
2108                 pNewObj->SetSaveData( aData );
2109                 aFunc.DataPilotUpdate( pDPObj, pNewObj, sal_True, sal_False );
2110                 delete pNewObj;
2111 
2112                 // unmark cell selection
2113                 Unmark();
2114             }
2115         }
2116     }
2117 }
2118 
2119 void ScDBFunc::ShowDataPilotSourceData( ScDPObject& rDPObj, const Sequence<sheet::DataPilotFieldFilter>& rFilters )
2120 {
2121     ScDocument* pDoc = GetViewData()->GetDocument();
2122     if (pDoc->GetDocumentShell()->IsReadOnly())
2123     {
2124         ErrorMessage(STR_READONLYERR);
2125         return;
2126     }
2127 
2128     Reference<sheet::XDimensionsSupplier> xDimSupplier = rDPObj.GetSource();
2129     Reference<container::XNameAccess> xDims = xDimSupplier->getDimensions();
2130     Reference<sheet::XDrillDownDataSupplier> xDDSupplier(xDimSupplier, UNO_QUERY);
2131     if (!xDDSupplier.is())
2132         return;
2133 
2134     Sequence< Sequence<Any> > aTabData = xDDSupplier->getDrillDownData(rFilters);
2135     sal_Int32 nRowSize = aTabData.getLength();
2136     if (nRowSize <= 1)
2137         // There is no data to show.  Bail out.
2138         return;
2139 
2140     sal_Int32 nColSize = aTabData[0].getLength();
2141 
2142     SCTAB nNewTab = GetViewData()->GetTabNo();
2143 
2144     auto_ptr<ScDocument> pInsDoc(new ScDocument(SCDOCMODE_CLIP));
2145     pInsDoc->ResetClip( pDoc, nNewTab );
2146     for (SCROW nRow = 0; nRow < nRowSize; ++nRow)
2147     {
2148         for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
2149         {
2150             const Any& rAny = aTabData[nRow][nCol];
2151             rtl::OUString aStr;
2152             double fVal;
2153             if (rAny >>= aStr)
2154                 pInsDoc->PutCell( ScAddress(nCol, nRow, nNewTab), new ScStringCell(String(aStr)) );
2155             else if (rAny >>= fVal)
2156                 pInsDoc->SetValue(nCol, nRow, nNewTab, fVal);
2157         }
2158     }
2159 
2160     // set number format (important for dates)
2161     for (SCCOL nCol = 0; nCol < nColSize; ++nCol)
2162     {
2163         rtl::OUString aStr;
2164         if (!(aTabData[0][nCol] >>= aStr))
2165             continue;
2166 
2167         Reference<XPropertySet> xPropSet(xDims->getByName(aStr), UNO_QUERY);
2168         if (!xPropSet.is())
2169             continue;
2170 
2171         Any any = xPropSet->getPropertyValue( rtl::OUString::createFromAscii(SC_UNO_NUMBERFO) );
2172         sal_Int32 nNumFmt = 0;
2173         if (!(any >>= nNumFmt))
2174             continue;
2175 
2176         ScPatternAttr aPattern( pInsDoc->GetPool() );
2177         aPattern.GetItemSet().Put( SfxUInt32Item(ATTR_VALUE_FORMAT, static_cast<sal_uInt32>(nNumFmt)) );
2178         pInsDoc->ApplyPatternAreaTab(nCol, 1, nCol, nRowSize-1, nNewTab, aPattern);
2179     }
2180 
2181     SCCOL nEndCol = 0;
2182     SCROW nEndRow = 0;
2183     pInsDoc->GetCellArea( nNewTab, nEndCol, nEndRow );
2184     pInsDoc->SetClipArea( ScRange( 0, 0, nNewTab, nEndCol, nEndRow, nNewTab ) );
2185 
2186     ::svl::IUndoManager* pMgr = GetViewData()->GetDocShell()->GetUndoManager();
2187     String aUndo = ScGlobal::GetRscString( STR_UNDO_DOOUTLINE );
2188     pMgr->EnterListAction( aUndo, aUndo );
2189 
2190     String aNewTabName;
2191     pDoc->CreateValidTabName(aNewTabName);
2192     if ( InsertTable(aNewTabName, nNewTab) )
2193         PasteFromClip( IDF_ALL, pInsDoc.get() );
2194 
2195     pMgr->LeaveListAction();
2196 }
2197 
2198 //
2199 //			DB-Operationen (Sortieren, Filtern, Teilergebnisse) wiederholen
2200 //
2201 
2202 void ScDBFunc::RepeatDB( sal_Bool bRecord )
2203 {
2204 	SCCOL nCurX = GetViewData()->GetCurX();
2205 	SCROW nCurY = GetViewData()->GetCurY();
2206 	SCTAB nTab = GetViewData()->GetTabNo();
2207 	ScDocument* pDoc = GetViewData()->GetDocument();
2208 	ScDBData* pDBData = GetDBData();
2209 	if (bRecord && !pDoc->IsUndoEnabled())
2210 		bRecord = sal_False;
2211 
2212 	ScQueryParam aQueryParam;
2213 	pDBData->GetQueryParam( aQueryParam );
2214 	sal_Bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
2215 
2216 	ScSortParam aSortParam;
2217 	pDBData->GetSortParam( aSortParam );
2218 	sal_Bool bSort = aSortParam.bDoSort[0];
2219 
2220 	ScSubTotalParam aSubTotalParam;
2221 	pDBData->GetSubTotalParam( aSubTotalParam );
2222 	sal_Bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
2223 
2224 	if ( bQuery || bSort || bSubTotal )
2225 	{
2226 		sal_Bool bQuerySize = sal_False;
2227 		ScRange aOldQuery;
2228 		ScRange aNewQuery;
2229 		if (bQuery && !aQueryParam.bInplace)
2230 		{
2231 			ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
2232 													aQueryParam.nDestTab, sal_True );
2233 			if (pDest && pDest->IsDoSize())
2234 			{
2235 				pDest->GetArea( aOldQuery );
2236 				bQuerySize = sal_True;
2237 			}
2238 		}
2239 
2240 		SCTAB nDummy;
2241 		SCCOL nStartCol;
2242 		SCROW nStartRow;
2243 		SCCOL nEndCol;
2244 		SCROW nEndRow;
2245 		pDBData->GetArea( nDummy, nStartCol, nStartRow, nEndCol, nEndRow );
2246 
2247 		//!		Undo nur benoetigte Daten ?
2248 
2249 		ScDocument* pUndoDoc = NULL;
2250 		ScOutlineTable* pUndoTab = NULL;
2251 		ScRangeName* pUndoRange = NULL;
2252 		ScDBCollection* pUndoDB = NULL;
2253 
2254 		if (bRecord)
2255 		{
2256 			SCTAB nTabCount = pDoc->GetTableCount();
2257 			pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
2258 			ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
2259 			if (pTable)
2260 			{
2261 				pUndoTab = new ScOutlineTable( *pTable );
2262 
2263 				SCCOLROW nOutStartCol;							// Zeilen/Spaltenstatus
2264 				SCCOLROW nOutStartRow;
2265 				SCCOLROW nOutEndCol;
2266 				SCCOLROW nOutEndRow;
2267 				pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
2268 				pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
2269 
2270 				pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
2271 				pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, sal_False, pUndoDoc );
2272 				pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, sal_False, pUndoDoc );
2273 			}
2274 			else
2275 				pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True );
2276 
2277 			//	Datenbereich sichern - incl. Filter-Ergebnis
2278 			pDoc->CopyToDocument( 0,nStartRow,nTab, MAXCOL,nEndRow,nTab, IDF_ALL, sal_False, pUndoDoc );
2279 
2280 			//	alle Formeln wegen Referenzen
2281 			pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, IDF_FORMULA, sal_False, pUndoDoc );
2282 
2283 			//	DB- und andere Bereiche
2284 			ScRangeName* pDocRange = pDoc->GetRangeName();
2285 			if (pDocRange->GetCount())
2286 				pUndoRange = new ScRangeName( *pDocRange );
2287 			ScDBCollection* pDocDB = pDoc->GetDBCollection();
2288 			if (pDocDB->GetCount())
2289 				pUndoDB = new ScDBCollection( *pDocDB );
2290 		}
2291 
2292 		if (bSort && bSubTotal)
2293 		{
2294 			//	Sortieren ohne SubTotals
2295 
2296 			aSubTotalParam.bRemoveOnly = sal_True;		// wird unten wieder zurueckgesetzt
2297 			DoSubTotals( aSubTotalParam, sal_False );
2298 		}
2299 
2300 		if (bSort)
2301 		{
2302 			pDBData->GetSortParam( aSortParam );			// Bereich kann sich geaendert haben
2303 			Sort( aSortParam, sal_False, sal_False);
2304 		}
2305 		if (bQuery)
2306 		{
2307 			pDBData->GetQueryParam( aQueryParam );			// Bereich kann sich geaendert haben
2308 			ScRange aAdvSource;
2309 			if (pDBData->GetAdvancedQuerySource(aAdvSource))
2310 			{
2311 				pDoc->CreateQueryParam(
2312 					aAdvSource.aStart.Col(), aAdvSource.aStart.Row(),
2313 					aAdvSource.aEnd.Col(), aAdvSource.aEnd.Row(),
2314 					aAdvSource.aStart.Tab(), aQueryParam );
2315 				Query( aQueryParam, &aAdvSource, sal_False );
2316 			}
2317 			else
2318 				Query( aQueryParam, NULL, sal_False );
2319 
2320 			//	bei nicht-inplace kann die Tabelle umgestellt worden sein
2321 			if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
2322 				SetTabNo( nTab );
2323 		}
2324 		if (bSubTotal)
2325 		{
2326 			pDBData->GetSubTotalParam( aSubTotalParam );	// Bereich kann sich geaendert haben
2327 			aSubTotalParam.bRemoveOnly = sal_False;
2328 			DoSubTotals( aSubTotalParam, sal_False );
2329 		}
2330 
2331 		if (bRecord)
2332 		{
2333 			SCTAB nDummyTab;
2334             SCCOL nDummyCol;
2335 			SCROW nDummyRow, nNewEndRow;
2336 			pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
2337 
2338 			const ScRange* pOld = NULL;
2339 			const ScRange* pNew = NULL;
2340 			if (bQuerySize)
2341 			{
2342 				ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
2343 														aQueryParam.nDestTab, sal_True );
2344 				if (pDest)
2345 				{
2346 					pDest->GetArea( aNewQuery );
2347 					pOld = &aOldQuery;
2348 					pNew = &aNewQuery;
2349 				}
2350 			}
2351 
2352 			GetViewData()->GetDocShell()->GetUndoManager()->AddUndoAction(
2353 				new ScUndoRepeatDB( GetViewData()->GetDocShell(), nTab,
2354 										nStartCol, nStartRow, nEndCol, nEndRow,
2355 										nNewEndRow,
2356 										nCurX, nCurY,
2357 										pUndoDoc, pUndoTab,
2358 										pUndoRange, pUndoDB,
2359 										pOld, pNew ) );
2360 		}
2361 
2362 		GetViewData()->GetDocShell()->PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab,
2363 													PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE );
2364 	}
2365 	else		// "Keine Operationen auszufuehren"
2366 		ErrorMessage(STR_MSSG_REPEATDB_0);
2367 }
2368 
2369 
2370 
2371 
2372