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