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