xref: /aoo42x/main/sc/source/ui/docshell/dbdocfun.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sc.hxx"
30 
31 
32 
33 // INCLUDE ---------------------------------------------------------------
34 
35 #include <sfx2/app.hxx>
36 #include <vcl/msgbox.hxx>
37 #include <vcl/waitobj.hxx>
38 #include <svx/dataaccessdescriptor.hxx>
39 
40 #include <com/sun/star/sdb/CommandType.hpp>
41 
42 #include "dbdocfun.hxx"
43 #include "sc.hrc"
44 #include "dbcolect.hxx"
45 #include "undodat.hxx"
46 #include "docsh.hxx"
47 #include "docfunc.hxx"
48 #include "globstr.hrc"
49 #include "tabvwsh.hxx"
50 #include "patattr.hxx"
51 #include "rangenam.hxx"
52 #include "olinetab.hxx"
53 #include "dpobject.hxx"
54 #include "dociter.hxx"		// for lcl_EmptyExcept
55 #include "cell.hxx"			// for lcl_EmptyExcept
56 #include "editable.hxx"
57 #include "attrib.hxx"
58 #include "drwlayer.hxx"
59 #include "dpshttab.hxx"
60 #include "hints.hxx"
61 
62 using namespace ::com::sun::star;
63 
64 // -----------------------------------------------------------------
65 
66 sal_Bool ScDBDocFunc::AddDBRange( const String& rName, const ScRange& rRange, sal_Bool /* bApi */ )
67 {
68 
69 	ScDocShellModificator aModificator( rDocShell );
70 
71 	ScDocument* pDoc = rDocShell.GetDocument();
72 	ScDBCollection* pDocColl = pDoc->GetDBCollection();
73 	sal_Bool bUndo (pDoc->IsUndoEnabled());
74 
75 	ScDBCollection* pUndoColl = NULL;
76 	if (bUndo)
77 		pUndoColl = new ScDBCollection( *pDocColl );
78 
79 	ScDBData* pNew = new ScDBData( rName, rRange.aStart.Tab(),
80 									rRange.aStart.Col(), rRange.aStart.Row(),
81 									rRange.aEnd.Col(), rRange.aEnd.Row() );
82 
83     // #i55926# While loading XML, formula cells only have a single string token,
84     // so CompileDBFormula would never find any name (index) tokens, and would
85     // unnecessarily loop through all cells.
86     sal_Bool bCompile = !pDoc->IsImportingXML();
87 
88     if ( bCompile )
89         pDoc->CompileDBFormula( sal_True );     // CreateFormulaString
90 	sal_Bool bOk = pDocColl->Insert( pNew );
91     if ( bCompile )
92         pDoc->CompileDBFormula( sal_False );    // CompileFormulaString
93 
94 	if (!bOk)
95 	{
96 		delete pNew;
97 		delete pUndoColl;
98 		return sal_False;
99 	}
100 
101 	if (bUndo)
102 	{
103 		ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
104 		rDocShell.GetUndoManager()->AddUndoAction(
105 						new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
106 	}
107 
108 	aModificator.SetDocumentModified();
109 	SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
110 	return sal_True;
111 }
112 
113 sal_Bool ScDBDocFunc::DeleteDBRange( const String& rName, sal_Bool /* bApi */ )
114 {
115 	sal_Bool bDone = sal_False;
116 	ScDocument* pDoc = rDocShell.GetDocument();
117 	ScDBCollection* pDocColl = pDoc->GetDBCollection();
118 	sal_Bool bUndo (pDoc->IsUndoEnabled());
119 
120 	sal_uInt16 nPos = 0;
121 	if (pDocColl->SearchName( rName, nPos ))
122 	{
123 		ScDocShellModificator aModificator( rDocShell );
124 
125 		ScDBCollection* pUndoColl = NULL;
126 		if (bUndo)
127 			pUndoColl = new ScDBCollection( *pDocColl );
128 
129 		pDoc->CompileDBFormula( sal_True );		// CreateFormulaString
130 		pDocColl->AtFree( nPos );
131 		pDoc->CompileDBFormula( sal_False );	// CompileFormulaString
132 
133 		if (bUndo)
134 		{
135 			ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
136 			rDocShell.GetUndoManager()->AddUndoAction(
137 							new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
138 		}
139 
140 		aModificator.SetDocumentModified();
141 		SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
142 		bDone = sal_True;
143 	}
144 
145 	return bDone;
146 }
147 
148 sal_Bool ScDBDocFunc::RenameDBRange( const String& rOld, const String& rNew, sal_Bool /* bApi */ )
149 {
150 	sal_Bool bDone = sal_False;
151 	ScDocument* pDoc = rDocShell.GetDocument();
152 	ScDBCollection* pDocColl = pDoc->GetDBCollection();
153 	sal_Bool bUndo (pDoc->IsUndoEnabled());
154 
155 	sal_uInt16 nPos = 0;
156 	sal_uInt16 nDummy = 0;
157 	if ( pDocColl->SearchName( rOld, nPos ) &&
158 		 !pDocColl->SearchName( rNew, nDummy ) )
159 	{
160 		ScDocShellModificator aModificator( rDocShell );
161 
162 		ScDBData* pData = (*pDocColl)[nPos];
163 		ScDBData* pNewData = new ScDBData(*pData);
164 		pNewData->SetName(rNew);
165 
166 		ScDBCollection* pUndoColl = new ScDBCollection( *pDocColl );
167 
168 		pDoc->CompileDBFormula( sal_True );				// CreateFormulaString
169 		pDocColl->AtFree( nPos );
170 		sal_Bool bInserted = pDocColl->Insert( pNewData );
171 		if (!bInserted)								// Fehler -> alten Zustand wiederherstellen
172 		{
173 			delete pNewData;
174 			pDoc->SetDBCollection( pUndoColl );		// gehoert dann dem Dokument
175 		}
176 		pDoc->CompileDBFormula( sal_False );			// CompileFormulaString
177 
178 		if (bInserted)								// Einfuegen hat geklappt
179 		{
180 			if (bUndo)
181 			{
182 				ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
183 				rDocShell.GetUndoManager()->AddUndoAction(
184 								new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
185 			}
186 			else
187 				delete pUndoColl;
188 
189 			aModificator.SetDocumentModified();
190 			SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
191 			bDone = sal_True;
192 		}
193 	}
194 
195 	return bDone;
196 }
197 
198 sal_Bool ScDBDocFunc::ModifyDBData( const ScDBData& rNewData, sal_Bool /* bApi */ )
199 {
200 	sal_Bool bDone = sal_False;
201 	ScDocument* pDoc = rDocShell.GetDocument();
202 	ScDBCollection* pDocColl = pDoc->GetDBCollection();
203 	sal_Bool bUndo (pDoc->IsUndoEnabled());
204 
205 	sal_uInt16 nPos = 0;
206 	if (pDocColl->SearchName( rNewData.GetName(), nPos ))
207 	{
208 		ScDocShellModificator aModificator( rDocShell );
209 
210 		ScDBData* pData = (*pDocColl)[nPos];
211 
212 		ScRange aOldRange, aNewRange;
213 		pData->GetArea(aOldRange);
214 		rNewData.GetArea(aNewRange);
215 		sal_Bool bAreaChanged = ( aOldRange != aNewRange );		// dann muss neu compiliert werden
216 
217 		ScDBCollection* pUndoColl = NULL;
218 		if (bUndo)
219 			pUndoColl = new ScDBCollection( *pDocColl );
220 
221 		*pData = rNewData;
222 		if (bAreaChanged)
223 			pDoc->CompileDBFormula();
224 
225 		if (bUndo)
226 		{
227 			ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
228 			rDocShell.GetUndoManager()->AddUndoAction(
229 							new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
230 		}
231 
232 		aModificator.SetDocumentModified();
233 		bDone = sal_True;
234 	}
235 
236 	return bDone;
237 }
238 
239 // -----------------------------------------------------------------
240 
241 sal_Bool ScDBDocFunc::RepeatDB( const String& rDBName, sal_Bool bRecord, sal_Bool bApi )
242 {
243 	//!	auch fuer ScDBFunc::RepeatDB benutzen!
244 
245 	sal_Bool bDone = sal_False;
246 	ScDocument* pDoc = rDocShell.GetDocument();
247 	if (bRecord && !pDoc->IsUndoEnabled())
248 		bRecord = sal_False;
249 	ScDBCollection*	pColl = pDoc->GetDBCollection();
250 	sal_uInt16 nIndex;
251 	if ( pColl && pColl->SearchName( rDBName, nIndex ) )
252 	{
253 		ScDBData* pDBData = (*pColl)[nIndex];
254 
255 		ScQueryParam aQueryParam;
256 		pDBData->GetQueryParam( aQueryParam );
257 		sal_Bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
258 
259 		ScSortParam aSortParam;
260 		pDBData->GetSortParam( aSortParam );
261 		sal_Bool bSort = aSortParam.bDoSort[0];
262 
263 		ScSubTotalParam aSubTotalParam;
264 		pDBData->GetSubTotalParam( aSubTotalParam );
265 		sal_Bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
266 
267 		if ( bQuery || bSort || bSubTotal )
268 		{
269 			sal_Bool bQuerySize = sal_False;
270 			ScRange aOldQuery;
271 			ScRange aNewQuery;
272 			if (bQuery && !aQueryParam.bInplace)
273 			{
274 				ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
275 														aQueryParam.nDestTab, sal_True );
276 				if (pDest && pDest->IsDoSize())
277 				{
278 					pDest->GetArea( aOldQuery );
279 					bQuerySize = sal_True;
280 				}
281 			}
282 
283 			SCTAB nTab;
284 			SCCOL nStartCol;
285 			SCROW nStartRow;
286 			SCCOL nEndCol;
287 			SCROW nEndRow;
288 			pDBData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
289 
290 			//!		Undo nur benoetigte Daten ?
291 
292 			ScDocument* pUndoDoc = NULL;
293 			ScOutlineTable* pUndoTab = NULL;
294 			ScRangeName* pUndoRange = NULL;
295 			ScDBCollection* pUndoDB = NULL;
296 
297 			if (bRecord)
298 			{
299 				SCTAB nTabCount = pDoc->GetTableCount();
300 				pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
301 				ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
302 				if (pTable)
303 				{
304 					pUndoTab = new ScOutlineTable( *pTable );
305 
306                     // column/row state
307 					SCCOLROW nOutStartCol, nOutEndCol;
308 					SCCOLROW nOutStartRow, nOutEndRow;
309 					pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
310 					pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
311 
312 					pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
313                     pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0,
314                             nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab,
315                             IDF_NONE, sal_False, pUndoDoc );
316                     pDoc->CopyToDocument( 0, static_cast<SCROW>(nOutStartRow),
317                             nTab, MAXCOL, static_cast<SCROW>(nOutEndRow), nTab,
318                             IDF_NONE, sal_False, pUndoDoc );
319 				}
320 				else
321 					pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True );
322 
323 				//	Datenbereich sichern - incl. Filter-Ergebnis
324 				pDoc->CopyToDocument( 0,nStartRow,nTab, MAXCOL,nEndRow,nTab, IDF_ALL, sal_False, pUndoDoc );
325 
326 				//	alle Formeln wegen Referenzen
327 				pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, IDF_FORMULA, sal_False, pUndoDoc );
328 
329 				//	DB- und andere Bereiche
330 				ScRangeName* pDocRange = pDoc->GetRangeName();
331 				if (pDocRange->GetCount())
332 					pUndoRange = new ScRangeName( *pDocRange );
333 				ScDBCollection* pDocDB = pDoc->GetDBCollection();
334 				if (pDocDB->GetCount())
335 					pUndoDB = new ScDBCollection( *pDocDB );
336 			}
337 
338 			if (bSort && bSubTotal)
339 			{
340 				//	Sortieren ohne SubTotals
341 
342 				aSubTotalParam.bRemoveOnly = sal_True;		// wird unten wieder zurueckgesetzt
343 				DoSubTotals( nTab, aSubTotalParam, NULL, sal_False, bApi );
344 			}
345 
346 			if (bSort)
347 			{
348 				pDBData->GetSortParam( aSortParam );			// Bereich kann sich geaendert haben
349 				Sort( nTab, aSortParam, sal_False, sal_False, bApi );
350 			}
351 			if (bQuery)
352 			{
353 				pDBData->GetQueryParam( aQueryParam );			// Bereich kann sich geaendert haben
354 				ScRange aAdvSource;
355 				if (pDBData->GetAdvancedQuerySource(aAdvSource))
356 					Query( nTab, aQueryParam, &aAdvSource, sal_False, bApi );
357 				else
358 					Query( nTab, aQueryParam, NULL, sal_False, bApi );
359 
360 				//	bei nicht-inplace kann die Tabelle umgestellt worden sein
361 //				if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
362 //					SetTabNo( nTab );
363 			}
364 			if (bSubTotal)
365 			{
366 				pDBData->GetSubTotalParam( aSubTotalParam );	// Bereich kann sich geaendert haben
367 				aSubTotalParam.bRemoveOnly = sal_False;
368 				DoSubTotals( nTab, aSubTotalParam, NULL, sal_False, bApi );
369 			}
370 
371 			if (bRecord)
372 			{
373                 SCTAB nDummyTab;
374                 SCCOL nDummyCol;
375                 SCROW nDummyRow;
376 				SCROW nNewEndRow;
377 				pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
378 
379 				const ScRange* pOld = NULL;
380 				const ScRange* pNew = NULL;
381 				if (bQuerySize)
382 				{
383 					ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
384 															aQueryParam.nDestTab, sal_True );
385 					if (pDest)
386 					{
387 						pDest->GetArea( aNewQuery );
388 						pOld = &aOldQuery;
389 						pNew = &aNewQuery;
390 					}
391 				}
392 
393 				rDocShell.GetUndoManager()->AddUndoAction(
394 					new ScUndoRepeatDB( &rDocShell, nTab,
395 											nStartCol, nStartRow, nEndCol, nEndRow,
396 											nNewEndRow,
397 											//nCurX, nCurY,
398 											nStartCol, nStartRow,
399 											pUndoDoc, pUndoTab,
400 											pUndoRange, pUndoDB,
401 											pOld, pNew ) );
402 			}
403 
404 			rDocShell.PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab,
405 									PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE );
406 			bDone = sal_True;
407 		}
408 		else if (!bApi)		// "Keine Operationen auszufuehren"
409 			rDocShell.ErrorMessage(STR_MSSG_REPEATDB_0);
410 	}
411 
412 	return bDone;
413 }
414 
415 // -----------------------------------------------------------------
416 
417 sal_Bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
418 							sal_Bool bRecord, sal_Bool bPaint, sal_Bool bApi )
419 {
420 	ScDocShellModificator aModificator( rDocShell );
421 
422 	ScDocument* pDoc = rDocShell.GetDocument();
423 	if (bRecord && !pDoc->IsUndoEnabled())
424 		bRecord = sal_False;
425 	SCTAB nSrcTab = nTab;
426     ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
427 
428 	ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
429 													rSortParam.nCol2, rSortParam.nRow2 );
430 	if (!pDBData)
431 	{
432 		DBG_ERROR( "Sort: keine DBData" );
433 		return sal_False;
434 	}
435 
436 	ScDBData* pDestData = NULL;
437 	ScRange aOldDest;
438 	sal_Bool bCopy = !rSortParam.bInplace;
439 	if ( bCopy && rSortParam.nDestCol == rSortParam.nCol1 &&
440 				  rSortParam.nDestRow == rSortParam.nRow1 && rSortParam.nDestTab == nTab )
441 		bCopy = sal_False;
442 	ScSortParam aLocalParam( rSortParam );
443 	if ( bCopy )
444 	{
445 		aLocalParam.MoveToDest();
446         if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
447         {
448             if (!bApi)
449                 rDocShell.ErrorMessage(STR_PASTE_FULL);
450             return sal_False;
451         }
452 
453 		nTab = rSortParam.nDestTab;
454 		pDestData = pDoc->GetDBAtCursor( rSortParam.nDestCol, rSortParam.nDestRow,
455 											rSortParam.nDestTab, sal_True );
456 		if (pDestData)
457 			pDestData->GetArea(aOldDest);
458 	}
459 
460 	ScEditableTester aTester( pDoc, nTab, aLocalParam.nCol1,aLocalParam.nRow1,
461 										aLocalParam.nCol2,aLocalParam.nRow2 );
462 	if (!aTester.IsEditable())
463 	{
464 		if (!bApi)
465 			rDocShell.ErrorMessage(aTester.GetMessageId());
466 		return sal_False;
467 	}
468 
469 	if ( aLocalParam.bIncludePattern && pDoc->HasAttrib(
470 										aLocalParam.nCol1, aLocalParam.nRow1, nTab,
471 										aLocalParam.nCol2, aLocalParam.nRow2, nTab,
472 										HASATTR_MERGED | HASATTR_OVERLAPPED ) )
473 	{
474 		//	Merge-Attribute wuerden beim Sortieren durcheinanderkommen
475 		if (!bApi)
476 			rDocShell.ErrorMessage(STR_SORT_ERR_MERGED);
477 		return sal_False;
478 	}
479 
480 
481 	//		ausfuehren
482 
483 	WaitObject aWait( rDocShell.GetActiveDialogParent() );
484 
485 	sal_Bool bRepeatQuery = sal_False;							// bestehenden Filter wiederholen?
486 	ScQueryParam aQueryParam;
487 	pDBData->GetQueryParam( aQueryParam );
488 	if ( aQueryParam.GetEntry(0).bDoQuery )
489 		bRepeatQuery = sal_True;
490 
491 	if (bRepeatQuery && bCopy)
492 	{
493 		if ( aQueryParam.bInplace ||
494 				aQueryParam.nDestCol != rSortParam.nDestCol ||
495 				aQueryParam.nDestRow != rSortParam.nDestRow ||
496 				aQueryParam.nDestTab != rSortParam.nDestTab )		// Query auf selben Zielbereich?
497 			bRepeatQuery = sal_False;
498 	}
499 
500     ScUndoSort* pUndoAction = 0;
501     if ( bRecord )
502     {
503         //  Referenzen ausserhalb des Bereichs werden nicht veraendert !
504 
505         ScDocument* pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
506         //  Zeilenhoehen immer (wegen automatischer Anpassung)
507         //! auf ScBlockUndo umstellen
508         pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True );
509 
510         /*  #i59745# Do not copy note captions to undo document. All existing
511             caption objects will be repositioned while sorting which is tracked
512             in drawing undo. When undo is executed, the old positions will be
513             restored, and the cells with the old notes (which still refer to the
514             existing captions) will be copied back into the source document. */
515         pDoc->CopyToDocument( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
516                                 aLocalParam.nCol2, aLocalParam.nRow2, nTab,
517                                 IDF_ALL|IDF_NOCAPTIONS, sal_False, pUndoDoc );
518 
519         const ScRange* pR = 0;
520         if (pDestData)
521         {
522             /*  #i59745# Do not copy note captions from destination range to
523                 undo document. All existing caption objects will be removed
524                 which is tracked in drawing undo. When undo is executed, the
525                 caption objects are reinserted with drawing undo, and the cells
526                 with the old notes (which still refer to the existing captions)
527                 will be copied back into the source document. */
528             pDoc->CopyToDocument( aOldDest, IDF_ALL|IDF_NOCAPTIONS, sal_False, pUndoDoc );
529             pR = &aOldDest;
530         }
531 
532 		//	Zeilenhoehen immer (wegen automatischer Anpassung)
533 		//!	auf ScBlockUndo umstellen
534 //        if (bRepeatQuery)
535             pDoc->CopyToDocument( 0, aLocalParam.nRow1, nTab, MAXCOL, aLocalParam.nRow2, nTab,
536                                     IDF_NONE, sal_False, pUndoDoc );
537 
538         ScDBCollection* pUndoDB = NULL;
539         ScDBCollection* pDocDB = pDoc->GetDBCollection();
540         if (pDocDB->GetCount())
541             pUndoDB = new ScDBCollection( *pDocDB );
542 
543         pUndoAction = new ScUndoSort( &rDocShell, nTab, rSortParam, bRepeatQuery, pUndoDoc, pUndoDB, pR );
544         rDocShell.GetUndoManager()->AddUndoAction( pUndoAction );
545 
546         // #i59745# collect all drawing undo actions affecting cell note captions
547         if( pDrawLayer )
548             pDrawLayer->BeginCalcUndo();
549     }
550 
551 	if ( bCopy )
552 	{
553 		if (pDestData)
554 			pDoc->DeleteAreaTab(aOldDest, IDF_CONTENTS);			// Zielbereich vorher loeschen
555 
556 		ScRange aSource( rSortParam.nCol1,rSortParam.nRow1,nSrcTab,
557 							rSortParam.nCol2,rSortParam.nRow2,nSrcTab );
558 		ScAddress aDest( rSortParam.nDestCol, rSortParam.nDestRow, rSortParam.nDestTab );
559 
560 		rDocShell.GetDocFunc().MoveBlock( aSource, aDest, sal_False, sal_False, sal_False, sal_True );
561 	}
562 
563 	// #105780# don't call ScDocument::Sort with an empty SortParam (may be empty here if bCopy is set)
564 	if ( aLocalParam.bDoSort[0] )
565 		pDoc->Sort( nTab, aLocalParam, bRepeatQuery );
566 
567 	sal_Bool bSave = sal_True;
568 	if (bCopy)
569 	{
570 		ScSortParam aOldSortParam;
571 		pDBData->GetSortParam( aOldSortParam );
572 		if ( aOldSortParam.bDoSort[0] && aOldSortParam.bInplace )	// Inplace-Sortierung gemerkt?
573 		{
574 			bSave = sal_False;
575 			aOldSortParam.nDestCol = rSortParam.nDestCol;
576 			aOldSortParam.nDestRow = rSortParam.nDestRow;
577 			aOldSortParam.nDestTab = rSortParam.nDestTab;
578 			pDBData->SetSortParam( aOldSortParam ); 				// dann nur DestPos merken
579 		}
580 	}
581 	if (bSave)												// Parameter merken
582 	{
583 		pDBData->SetSortParam( rSortParam );
584 		pDBData->SetHeader( rSortParam.bHasHeader );		//! ???
585 		pDBData->SetByRow( rSortParam.bByRow );				//! ???
586 	}
587 
588 	if (bCopy)											// neuen DB-Bereich merken
589 	{
590 		//	Tabelle umschalten von aussen (View)
591 		//!	SetCursor ??!?!
592 
593 		ScRange aDestPos( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
594 							aLocalParam.nCol2, aLocalParam.nRow2, nTab );
595 		ScDBData* pNewData;
596 		if (pDestData)
597 			pNewData = pDestData;				// Bereich vorhanden -> anpassen
598 		else									// Bereich ab Cursor/Markierung wird angelegt
599 			pNewData = rDocShell.GetDBData(aDestPos, SC_DB_MAKE, SC_DBSEL_FORCE_MARK );
600 		if (pNewData)
601 		{
602 			pNewData->SetArea( nTab,
603 								aLocalParam.nCol1,aLocalParam.nRow1,
604 								aLocalParam.nCol2,aLocalParam.nRow2 );
605 			pNewData->SetSortParam( aLocalParam );
606 			pNewData->SetHeader( aLocalParam.bHasHeader );		//! ???
607 			pNewData->SetByRow( aLocalParam.bByRow );
608 		}
609 		else
610 		{
611 			DBG_ERROR("Zielbereich nicht da");
612 		}
613 	}
614 
615 	ScRange aDirtyRange( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
616 		aLocalParam.nCol2, aLocalParam.nRow2, nTab );
617 	pDoc->SetDirty( aDirtyRange );
618 
619 	if (bPaint)
620 	{
621 		sal_uInt16 nPaint = PAINT_GRID;
622 		SCCOL nStartX = aLocalParam.nCol1;
623 		SCROW nStartY = aLocalParam.nRow1;
624 		SCCOL nEndX = aLocalParam.nCol2;
625 		SCROW nEndY = aLocalParam.nRow2;
626 		if ( bRepeatQuery )
627 		{
628 			nPaint |= PAINT_LEFT;
629 			nStartX = 0;
630 			nEndX = MAXCOL;
631 		}
632 		if (pDestData)
633 		{
634 			if ( nEndX < aOldDest.aEnd.Col() )
635 				nEndX = aOldDest.aEnd.Col();
636 			if ( nEndY < aOldDest.aEnd.Row() )
637 				nEndY = aOldDest.aEnd.Row();
638 		}
639 		rDocShell.PostPaint( nStartX, nStartY, nTab, nEndX, nEndY, nTab, nPaint );
640 	}
641 
642     //  AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, bPaint );
643 	rDocShell.AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, nTab );
644 
645     // #i59745# set collected drawing undo actions at sorting undo action
646     if( pUndoAction && pDrawLayer )
647         pUndoAction->SetDrawUndoAction( pDrawLayer->GetCalcUndo() );
648 
649 	aModificator.SetDocumentModified();
650 
651 	return sal_True;
652 }
653 
654 // -----------------------------------------------------------------
655 
656 sal_Bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam,
657 						const ScRange* pAdvSource, sal_Bool bRecord, sal_Bool bApi )
658 {
659 	ScDocShellModificator aModificator( rDocShell );
660 
661 	ScDocument* pDoc = rDocShell.GetDocument();
662 	if (bRecord && !pDoc->IsUndoEnabled())
663 		bRecord = sal_False;
664 	ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rQueryParam.nCol1, rQueryParam.nRow1,
665 													rQueryParam.nCol2, rQueryParam.nRow2 );
666 	if (!pDBData)
667 	{
668 		DBG_ERROR( "Query: keine DBData" );
669 		return sal_False;
670 	}
671 
672 	//	Wechsel von Inplace auf nicht-Inplace, dann erst Inplace aufheben:
673 	//	(nur, wenn im Dialog "Persistent" ausgewaehlt ist)
674 
675 	if ( !rQueryParam.bInplace && pDBData->HasQueryParam() && rQueryParam.bDestPers )
676 	{
677 		ScQueryParam aOldQuery;
678 		pDBData->GetQueryParam(aOldQuery);
679 		if (aOldQuery.bInplace)
680 		{
681 			//	alte Filterung aufheben
682 
683 			SCSIZE nEC = aOldQuery.GetEntryCount();
684 			for (SCSIZE i=0; i<nEC; i++)
685 				aOldQuery.GetEntry(i).bDoQuery = sal_False;
686 			aOldQuery.bDuplicate = sal_True;
687 			Query( nTab, aOldQuery, NULL, bRecord, bApi );
688 		}
689 	}
690 
691 	ScQueryParam aLocalParam( rQueryParam );		// fuer Paint / Zielbereich
692 	sal_Bool bCopy = !rQueryParam.bInplace; 			// kopiert wird in Table::Query
693 	ScDBData* pDestData = NULL;						// Bereich, in den kopiert wird
694 	sal_Bool bDoSize = sal_False;							// Zielgroesse anpassen (einf./loeschen)
695 	SCCOL nFormulaCols = 0;						// nur bei bDoSize
696 	sal_Bool bKeepFmt = sal_False;
697 	ScRange aOldDest;
698 	ScRange aDestTotal;
699 	if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 &&
700 				  rQueryParam.nDestRow == rQueryParam.nRow1 && rQueryParam.nDestTab == nTab )
701 		bCopy = sal_False;
702 	SCTAB nDestTab = nTab;
703 	if ( bCopy )
704 	{
705 		aLocalParam.MoveToDest();
706 		nDestTab = rQueryParam.nDestTab;
707         if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
708         {
709             if (!bApi)
710                 rDocShell.ErrorMessage(STR_PASTE_FULL);
711             return sal_False;
712         }
713 
714 		ScEditableTester aTester( pDoc, nDestTab, aLocalParam.nCol1,aLocalParam.nRow1,
715 												aLocalParam.nCol2,aLocalParam.nRow2);
716 		if (!aTester.IsEditable())
717 		{
718 			if (!bApi)
719 				rDocShell.ErrorMessage(aTester.GetMessageId());
720 			return sal_False;
721 		}
722 
723 		pDestData = pDoc->GetDBAtCursor( rQueryParam.nDestCol, rQueryParam.nDestRow,
724 											rQueryParam.nDestTab, sal_True );
725 		if (pDestData)
726 		{
727 			pDestData->GetArea( aOldDest );
728 			aDestTotal=ScRange( rQueryParam.nDestCol,
729 								rQueryParam.nDestRow,
730 								nDestTab,
731 								rQueryParam.nDestCol + rQueryParam.nCol2 - rQueryParam.nCol1,
732 								rQueryParam.nDestRow + rQueryParam.nRow2 - rQueryParam.nRow1,
733 								nDestTab );
734 
735 			bDoSize = pDestData->IsDoSize();
736 			//	Test, ob Formeln aufgefuellt werden muessen (nFormulaCols):
737 			if ( bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col() )
738 			{
739 				SCCOL nTestCol = aOldDest.aEnd.Col() + 1;		// neben dem Bereich
740 				SCROW nTestRow = rQueryParam.nDestRow +
741 									( aLocalParam.bHasHeader ? 1 : 0 );
742 				while ( nTestCol <= MAXCOL &&
743 						pDoc->GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA )
744 					++nTestCol, ++nFormulaCols;
745 			}
746 
747 			bKeepFmt = pDestData->IsKeepFmt();
748 			if ( bDoSize && !pDoc->CanFitBlock( aOldDest, aDestTotal ) )
749 			{
750 				if (!bApi)
751 					rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);		// kann keine Zeilen einfuegen
752 				return sal_False;
753 			}
754 		}
755 	}
756 
757 	//		ausfuehren
758 
759 	WaitObject aWait( rDocShell.GetActiveDialogParent() );
760 
761 	sal_Bool bKeepSub = sal_False;							// bestehende Teilergebnisse wiederholen?
762 	ScSubTotalParam aSubTotalParam;
763 	if (rQueryParam.GetEntry(0).bDoQuery)			// nicht beim Aufheben
764 	{
765 		pDBData->GetSubTotalParam( aSubTotalParam );	// Teilergebnisse vorhanden?
766 
767 		if ( aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly )
768 			bKeepSub = sal_True;
769 	}
770 
771     ScDocument* pUndoDoc = NULL;
772     ScDBCollection* pUndoDB = NULL;
773     const ScRange* pOld = NULL;
774 
775 	if ( bRecord )
776 	{
777 		pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
778 		if (bCopy)
779 		{
780 			pUndoDoc->InitUndo( pDoc, nDestTab, nDestTab, sal_False, sal_True );
781 			pDoc->CopyToDocument( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
782 									aLocalParam.nCol2, aLocalParam.nRow2, nDestTab,
783 									IDF_ALL, sal_False, pUndoDoc );
784 			//	Attribute sichern, falls beim Filtern mitkopiert
785 
786 			if (pDestData)
787 			{
788 				pDoc->CopyToDocument( aOldDest, IDF_ALL, sal_False, pUndoDoc );
789 				pOld = &aOldDest;
790 			}
791 		}
792 		else
793 		{
794 			pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True );
795 			pDoc->CopyToDocument( 0, rQueryParam.nRow1, nTab, MAXCOL, rQueryParam.nRow2, nTab,
796 										IDF_NONE, sal_False, pUndoDoc );
797 		}
798 
799 		ScDBCollection* pDocDB = pDoc->GetDBCollection();
800 		if (pDocDB->GetCount())
801 			pUndoDB = new ScDBCollection( *pDocDB );
802 
803         pDoc->BeginDrawUndo();
804 	}
805 
806 	ScDocument* pAttribDoc = NULL;
807 	ScRange aAttribRange;
808 	if (pDestData)										// Zielbereich loeschen
809 	{
810 		if ( bKeepFmt )
811 		{
812 			//	kleinere der End-Spalten, Header+1 Zeile
813 			aAttribRange = aOldDest;
814 			if ( aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col() )
815 				aAttribRange.aEnd.SetCol( aDestTotal.aEnd.Col() );
816 			aAttribRange.aEnd.SetRow( aAttribRange.aStart.Row() +
817 										( aLocalParam.bHasHeader ? 1 : 0 ) );
818 
819 			//	auch fuer aufgefuellte Formeln
820 			aAttribRange.aEnd.SetCol( aAttribRange.aEnd.Col() + nFormulaCols );
821 
822 			pAttribDoc = new ScDocument( SCDOCMODE_UNDO );
823 			pAttribDoc->InitUndo( pDoc, nDestTab, nDestTab, sal_False, sal_True );
824 			pDoc->CopyToDocument( aAttribRange, IDF_ATTRIB, sal_False, pAttribDoc );
825 		}
826 
827 		if ( bDoSize )
828 			pDoc->FitBlock( aOldDest, aDestTotal );
829 		else
830 			pDoc->DeleteAreaTab(aOldDest, IDF_ALL);			// einfach loeschen
831 	}
832 
833 	//	Filtern am Dokument ausfuehren
834 	SCSIZE nCount = pDoc->Query( nTab, rQueryParam, bKeepSub );
835 	if (bCopy)
836 	{
837 		aLocalParam.nRow2 = aLocalParam.nRow1 + nCount;
838 		if (!aLocalParam.bHasHeader && nCount > 0)
839 			--aLocalParam.nRow2;
840 
841 		if ( bDoSize )
842 		{
843 			//	auf wirklichen Ergebnis-Bereich anpassen
844 			//	(das hier ist immer eine Verkleinerung)
845 
846 			ScRange aNewDest( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
847 								aLocalParam.nCol2, aLocalParam.nRow2, nDestTab );
848 			pDoc->FitBlock( aDestTotal, aNewDest, sal_False );		// sal_False - nicht loeschen
849 
850 			if ( nFormulaCols > 0 )
851 			{
852 				//	Formeln ausfuellen
853 				//!	Undo (Query und Repeat) !!!
854 
855 				ScRange aNewForm( aLocalParam.nCol2+1, aLocalParam.nRow1, nDestTab,
856 								  aLocalParam.nCol2+nFormulaCols, aLocalParam.nRow2, nDestTab );
857 				ScRange aOldForm = aNewForm;
858 				aOldForm.aEnd.SetRow( aOldDest.aEnd.Row() );
859 				pDoc->FitBlock( aOldForm, aNewForm, sal_False );
860 
861 				ScMarkData aMark;
862 				aMark.SelectOneTable(nDestTab);
863 				SCROW nFStartY = aLocalParam.nRow1 + ( aLocalParam.bHasHeader ? 1 : 0 );
864 				pDoc->Fill( aLocalParam.nCol2+1, nFStartY,
865 							aLocalParam.nCol2+nFormulaCols, nFStartY, aMark,
866 							aLocalParam.nRow2 - nFStartY,
867 							FILL_TO_BOTTOM, FILL_SIMPLE );
868 			}
869 		}
870 
871 		if ( pAttribDoc )		// gemerkte Attribute zurueckkopieren
872 		{
873 			//	Header
874 			if (aLocalParam.bHasHeader)
875 			{
876 				ScRange aHdrRange = aAttribRange;
877 				aHdrRange.aEnd.SetRow( aHdrRange.aStart.Row() );
878 				pAttribDoc->CopyToDocument( aHdrRange, IDF_ATTRIB, sal_False, pDoc );
879 			}
880 
881 			//	Daten
882 			SCCOL nAttrEndCol = aAttribRange.aEnd.Col();
883 			SCROW nAttrRow = aAttribRange.aStart.Row() + ( aLocalParam.bHasHeader ? 1 : 0 );
884 			for (SCCOL nCol = aAttribRange.aStart.Col(); nCol<=nAttrEndCol; nCol++)
885 			{
886 				const ScPatternAttr* pSrcPattern = pAttribDoc->GetPattern(
887 													nCol, nAttrRow, nDestTab );
888 				DBG_ASSERT(pSrcPattern,"Pattern ist 0");
889 				if (pSrcPattern)
890 					pDoc->ApplyPatternAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
891 													nDestTab, *pSrcPattern );
892 				const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
893 				if (pStyle)
894 					pDoc->ApplyStyleAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
895 													nDestTab, *pStyle );
896 			}
897 
898 			delete pAttribDoc;
899 		}
900 	}
901 
902 	//	speichern: Inplace immer, sonst je nach Einstellung
903 	//			   alter Inplace-Filter ist ggf. schon aufgehoben
904 
905 	sal_Bool bSave = rQueryParam.bInplace || rQueryParam.bDestPers;
906 	if (bSave)													// merken
907 	{
908 		pDBData->SetQueryParam( rQueryParam );
909 		pDBData->SetHeader( rQueryParam.bHasHeader );		//! ???
910 		pDBData->SetAdvancedQuerySource( pAdvSource );		// after SetQueryParam
911 	}
912 
913 	if (bCopy)												// neuen DB-Bereich merken
914 	{
915 		//	selektieren wird hinterher von aussen (dbfunc)
916 		//	momentan ueber DB-Bereich an der Zielposition, darum muss dort
917 		//	auf jeden Fall ein Bereich angelegt werden.
918 
919 		ScDBData* pNewData;
920 		if (pDestData)
921 			pNewData = pDestData;				// Bereich vorhanden -> anpassen (immer!)
922 		else									// Bereich anlegen
923 			pNewData = rDocShell.GetDBData(
924 							ScRange( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
925 									 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab ),
926 							SC_DB_MAKE, SC_DBSEL_FORCE_MARK );
927 
928 		if (pNewData)
929 		{
930 			pNewData->SetArea( nDestTab, aLocalParam.nCol1, aLocalParam.nRow1,
931 											aLocalParam.nCol2, aLocalParam.nRow2 );
932 
933 			//	Query-Param wird am Ziel nicht mehr eingestellt, fuehrt nur zu Verwirrung
934 			//	und Verwechslung mit dem Query-Param am Quellbereich (#37187#)
935 		}
936 		else
937 		{
938 			DBG_ERROR("Zielbereich nicht da");
939 		}
940 	}
941 
942 	if (!bCopy)
943     {
944         pDoc->InvalidatePageBreaks(nTab);
945 		pDoc->UpdatePageBreaks( nTab );
946     }
947 
948     // #i23299# because of Subtotal functions, the whole rows must be set dirty
949 	ScRange aDirtyRange( 0 , aLocalParam.nRow1, nDestTab,
950 		MAXCOL, aLocalParam.nRow2, nDestTab );
951 	pDoc->SetDirty( aDirtyRange );
952 
953     if ( bRecord )
954     {
955         // create undo action after executing, because of drawing layer undo
956 		rDocShell.GetUndoManager()->AddUndoAction(
957 					new ScUndoQuery( &rDocShell, nTab, rQueryParam, pUndoDoc, pUndoDB,
958 										pOld, bDoSize, pAdvSource ) );
959     }
960 
961 
962 	if (bCopy)
963 	{
964 		SCCOL nEndX = aLocalParam.nCol2;
965 		SCROW nEndY = aLocalParam.nRow2;
966 		if (pDestData)
967 		{
968 			if ( aOldDest.aEnd.Col() > nEndX )
969 				nEndX = aOldDest.aEnd.Col();
970 			if ( aOldDest.aEnd.Row() > nEndY )
971 				nEndY = aOldDest.aEnd.Row();
972 		}
973 		if (bDoSize)
974 			nEndY = MAXROW;
975 		rDocShell.PostPaint( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
976 									nEndX, nEndY, nDestTab, PAINT_GRID );
977 	}
978 	else
979 		rDocShell.PostPaint( 0, rQueryParam.nRow1, nTab, MAXCOL, MAXROW, nTab,
980 												PAINT_GRID | PAINT_LEFT );
981 	aModificator.SetDocumentModified();
982 
983 	return sal_True;
984 }
985 
986 // -----------------------------------------------------------------
987 
988 sal_Bool ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
989 								const ScSortParam* pForceNewSort, sal_Bool bRecord, sal_Bool bApi )
990 {
991 	//!	auch fuer ScDBFunc::DoSubTotals benutzen!
992 	//	dann bleibt aussen:
993 	//	- neuen Bereich (aus DBData) markieren
994 	//	- SelectionChanged (?)
995 
996 	sal_Bool bDo = !rParam.bRemoveOnly;							// sal_False = nur loeschen
997 	sal_Bool bRet = sal_False;
998 
999 	ScDocument* pDoc = rDocShell.GetDocument();
1000 	if (bRecord && !pDoc->IsUndoEnabled())
1001 		bRecord = sal_False;
1002 	ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
1003 												rParam.nCol2, rParam.nRow2 );
1004 	if (!pDBData)
1005 	{
1006 		DBG_ERROR( "SubTotals: keine DBData" );
1007 		return sal_False;
1008 	}
1009 
1010 	ScEditableTester aTester( pDoc, nTab, 0,rParam.nRow1+1, MAXCOL,MAXROW );
1011 	if (!aTester.IsEditable())
1012 	{
1013 		if (!bApi)
1014 			rDocShell.ErrorMessage(aTester.GetMessageId());
1015 		return sal_False;
1016 	}
1017 
1018 	if (pDoc->HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
1019 						 rParam.nCol2, rParam.nRow2, nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ))
1020 	{
1021 		if (!bApi)
1022 			rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);	// nicht in zusammengefasste einfuegen
1023 		return sal_False;
1024 	}
1025 
1026 	sal_Bool bOk = sal_True;
1027 	sal_Bool bDelete = sal_False;
1028 	if (rParam.bReplace)
1029 		if (pDoc->TestRemoveSubTotals( nTab, rParam ))
1030 		{
1031 			bDelete = sal_True;
1032 			bOk = ( MessBox( rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1033 				// "StarCalc" "Daten loeschen?"
1034 				ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ),
1035 				ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_1 ) ).Execute()
1036 				== RET_YES );
1037 		}
1038 
1039 	if (bOk)
1040 	{
1041 		WaitObject aWait( rDocShell.GetActiveDialogParent() );
1042 		ScDocShellModificator aModificator( rDocShell );
1043 
1044 		ScSubTotalParam aNewParam( rParam );		// Bereichsende wird veraendert
1045 		ScDocument*		pUndoDoc = NULL;
1046 		ScOutlineTable*	pUndoTab = NULL;
1047 		ScRangeName*	pUndoRange = NULL;
1048 		ScDBCollection* pUndoDB = NULL;
1049 		SCTAB 			nTabCount = 0;				// fuer Referenz-Undo
1050 
1051 		if (bRecord)										// alte Daten sichern
1052 		{
1053 			sal_Bool bOldFilter = bDo && rParam.bDoSort;
1054 
1055 			nTabCount = pDoc->GetTableCount();
1056 			pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
1057 			ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
1058 			if (pTable)
1059 			{
1060 				pUndoTab = new ScOutlineTable( *pTable );
1061 
1062                 // column/row state
1063                 SCCOLROW nOutStartCol, nOutEndCol;
1064                 SCCOLROW nOutStartRow, nOutEndRow;
1065 				pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
1066 				pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
1067 
1068 				pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
1069 				pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, sal_False, pUndoDoc );
1070 				pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, sal_False, pUndoDoc );
1071 			}
1072 			else
1073 				pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, bOldFilter );
1074 
1075 			//	Datenbereich sichern - incl. Filter-Ergebnis
1076 			pDoc->CopyToDocument( 0,rParam.nRow1+1,nTab, MAXCOL,rParam.nRow2,nTab,
1077 									IDF_ALL, sal_False, pUndoDoc );
1078 
1079 			//	alle Formeln wegen Referenzen
1080 			pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1,
1081 										IDF_FORMULA, sal_False, pUndoDoc );
1082 
1083 			//	DB- und andere Bereiche
1084 			ScRangeName* pDocRange = pDoc->GetRangeName();
1085 			if (pDocRange->GetCount())
1086 				pUndoRange = new ScRangeName( *pDocRange );
1087 			ScDBCollection* pDocDB = pDoc->GetDBCollection();
1088 			if (pDocDB->GetCount())
1089 				pUndoDB = new ScDBCollection( *pDocDB );
1090 		}
1091 
1092 //		pDoc->SetOutlineTable( nTab, NULL );
1093 		ScOutlineTable*	pOut = pDoc->GetOutlineTable( nTab );
1094 		if (pOut)
1095 			pOut->GetRowArray()->RemoveAll();		// nur Zeilen-Outlines loeschen
1096 
1097 		if (rParam.bReplace)
1098 			pDoc->RemoveSubTotals( nTab, aNewParam );
1099 		sal_Bool bSuccess = sal_True;
1100 		if (bDo)
1101 		{
1102 			// Sortieren
1103 			if ( rParam.bDoSort || pForceNewSort )
1104 			{
1105 				pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1106 
1107 				//	Teilergebnis-Felder vor die Sortierung setzen
1108 				//	(doppelte werden weggelassen, kann darum auch wieder aufgerufen werden)
1109 
1110 				ScSortParam aOldSort;
1111 				pDBData->GetSortParam( aOldSort );
1112 				ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
1113 				Sort( nTab, aSortParam, sal_False, sal_False, bApi );
1114 			}
1115 
1116 			bSuccess = pDoc->DoSubTotals( nTab, aNewParam );
1117 		}
1118 		ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
1119 			aNewParam.nCol2, aNewParam.nRow2, nTab );
1120 		pDoc->SetDirty( aDirtyRange );
1121 
1122 		if (bRecord)
1123 		{
1124 //			ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
1125 			rDocShell.GetUndoManager()->AddUndoAction(
1126 				new ScUndoSubTotals( &rDocShell, nTab,
1127 										rParam, aNewParam.nRow2,
1128 										pUndoDoc, pUndoTab, // pUndoDBData,
1129 										pUndoRange, pUndoDB ) );
1130 		}
1131 
1132 		if (!bSuccess)
1133 		{
1134 			// "Kann keine Zeilen einfuegen"
1135 			if (!bApi)
1136 				rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
1137 		}
1138 
1139 													// merken
1140 		pDBData->SetSubTotalParam( aNewParam );
1141 		pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1142 		pDoc->CompileDBFormula();
1143 
1144 		rDocShell.PostPaint( 0,0,nTab, MAXCOL,MAXROW,nTab,
1145 												PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE );
1146 		aModificator.SetDocumentModified();
1147 
1148 		bRet = bSuccess;
1149 	}
1150 	return bRet;
1151 }
1152 
1153 //==================================================================
1154 
1155 sal_Bool lcl_EmptyExcept( ScDocument* pDoc, const ScRange& rRange, const ScRange& rExcept )
1156 {
1157 	ScCellIterator aIter( pDoc, rRange );
1158 	ScBaseCell* pCell = aIter.GetFirst();
1159 	while (pCell)
1160 	{
1161         if ( !pCell->IsBlank() )      // real content?
1162 		{
1163 			if ( !rExcept.In( ScAddress( aIter.GetCol(), aIter.GetRow(), aIter.GetTab() ) ) )
1164 				return sal_False;		// cell found
1165 		}
1166 		pCell = aIter.GetNext();
1167 	}
1168 
1169 	return sal_True;		// nothing found - empty
1170 }
1171 
1172 sal_Bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj,
1173 										sal_Bool bRecord, sal_Bool bApi, sal_Bool bAllowMove )
1174 {
1175 	ScDocShellModificator aModificator( rDocShell );
1176 	WaitObject aWait( rDocShell.GetActiveDialogParent() );
1177 
1178 	sal_Bool bDone = sal_False;
1179 	sal_Bool bUndoSelf = sal_False;
1180 	sal_uInt16 nErrId = 0;
1181 
1182 	ScDocument* pOldUndoDoc = NULL;
1183 	ScDocument* pNewUndoDoc = NULL;
1184 	ScDPObject* pUndoDPObj = NULL;
1185 	if ( bRecord && pOldObj )
1186 		pUndoDPObj = new ScDPObject( *pOldObj );	// copy old settings for undo
1187 
1188 	ScDocument* pDoc = rDocShell.GetDocument();
1189 	if (bRecord && !pDoc->IsUndoEnabled())
1190 		bRecord = sal_False;
1191 	if ( !rDocShell.IsEditable() || pDoc->GetChangeTrack() )
1192 	{
1193 		//	not recorded -> disallow
1194 		//!	different error messages?
1195 
1196 		nErrId = STR_PROTECTIONERR;
1197 	}
1198 	if ( pOldObj && !nErrId )
1199 	{
1200 		ScRange aOldOut = pOldObj->GetOutRange();
1201 		ScEditableTester aTester( pDoc, aOldOut );
1202 		if ( !aTester.IsEditable() )
1203 			nErrId = aTester.GetMessageId();
1204 	}
1205 	if ( pNewObj && !nErrId )
1206 	{
1207 		//	at least one cell at the output position must be editable
1208 		//	-> check in advance
1209 		//	(start of output range in pNewObj is valid)
1210 
1211 		ScRange aNewStart( pNewObj->GetOutRange().aStart );
1212 		ScEditableTester aTester( pDoc, aNewStart );
1213 		if ( !aTester.IsEditable() )
1214 			nErrId = aTester.GetMessageId();
1215 	}
1216 
1217 	ScDPObject* pDestObj = NULL;
1218 	if ( !nErrId )
1219 	{
1220 		if ( pOldObj && !pNewObj )
1221 		{
1222 			//	delete table
1223 
1224 			ScRange aRange = pOldObj->GetOutRange();
1225 			SCTAB nTab = aRange.aStart.Tab();
1226 
1227 			if ( bRecord )
1228 			{
1229 				pOldUndoDoc = new ScDocument( SCDOCMODE_UNDO );
1230 				pOldUndoDoc->InitUndo( pDoc, nTab, nTab );
1231 				pDoc->CopyToDocument( aRange, IDF_ALL, sal_False, pOldUndoDoc );
1232 			}
1233 
1234 			pDoc->DeleteAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
1235 								 aRange.aEnd.Col(),   aRange.aEnd.Row(),
1236 								 nTab, IDF_ALL );
1237 			pDoc->RemoveFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
1238 								  aRange.aEnd.Col(),   aRange.aEnd.Row(),
1239 								  nTab, SC_MF_AUTO );
1240 
1241             pDoc->GetDPCollection()->FreeTable( pOldObj );  // object is deleted here
1242 
1243 			rDocShell.PostPaintGridAll();	//! only necessary parts
1244 			rDocShell.PostPaint( aRange.aStart.Col(), aRange.aStart.Row(), nTab,
1245 								 aRange.aEnd.Col(),   aRange.aEnd.Row(),   nTab,
1246 								 PAINT_GRID );
1247 			bDone = sal_True;
1248 		}
1249 		else if ( pNewObj )
1250 		{
1251 			if ( pOldObj )
1252 			{
1253 				if ( bRecord )
1254 				{
1255 					ScRange aRange = pOldObj->GetOutRange();
1256 					SCTAB nTab = aRange.aStart.Tab();
1257 					pOldUndoDoc = new ScDocument( SCDOCMODE_UNDO );
1258 					pOldUndoDoc->InitUndo( pDoc, nTab, nTab );
1259 					pDoc->CopyToDocument( aRange, IDF_ALL, sal_False, pOldUndoDoc );
1260 				}
1261 
1262 				if ( pNewObj == pOldObj )
1263 				{
1264 					//	refresh only - no settings modified
1265 				}
1266 				else
1267 				{
1268 					pNewObj->WriteSourceDataTo( *pOldObj );		// copy source data
1269 
1270 					ScDPSaveData* pData = pNewObj->GetSaveData();
1271 					DBG_ASSERT( pData, "no SaveData from living DPObject" );
1272 					if ( pData )
1273 						pOldObj->SetSaveData( *pData );		// copy SaveData
1274 				}
1275 
1276 				pDestObj = pOldObj;
1277 				pDestObj->SetAllowMove( bAllowMove );
1278 			}
1279 			else
1280 			{
1281 				//	output range must be set at pNewObj
1282 
1283 				pDestObj = new ScDPObject( *pNewObj );
1284 
1285                 // #i94570# When changing the output position in the dialog, a new table is created
1286                 // with the settings from the old table, including the name.
1287                 // So we have to check for duplicate names here (before inserting).
1288                 if ( pDoc->GetDPCollection()->GetByName(pDestObj->GetName()) )
1289                     pDestObj->SetName( String() );      // ignore the invalid name, create a new name below
1290 
1291 				pDestObj->SetAlive(sal_True);
1292                 if ( !pDoc->GetDPCollection()->InsertNewTable(pDestObj) )
1293 				{
1294 					DBG_ERROR("cannot insert DPObject");
1295 					DELETEZ( pDestObj );
1296 				}
1297 			}
1298 			if ( pDestObj )
1299 			{
1300 				// #78541# create new database connection for "refresh"
1301 				// (and re-read column entry collections)
1302 				// so all changes take effect
1303 				if ( pNewObj == pOldObj && pDestObj->IsImportData() )
1304 					pDestObj->InvalidateSource();
1305 
1306 				pDestObj->InvalidateData();				// before getting the new output area
1307 
1308 				//	make sure the table has a name (not set by dialog)
1309 				if ( !pDestObj->GetName().Len() )
1310 					pDestObj->SetName( pDoc->GetDPCollection()->CreateNewName() );
1311 
1312 				sal_Bool bOverflow = sal_False;
1313 				ScRange aNewOut = pDestObj->GetNewOutputRange( bOverflow );
1314 
1315                 //!	test for overlap with other data pilot tables
1316                 if( pOldObj )
1317                 {
1318                     const ScSheetSourceDesc* pSheetDesc = pOldObj->GetSheetDesc();
1319                     if( pSheetDesc && pSheetDesc->aSourceRange.Intersects( aNewOut ) )
1320                     {
1321                         ScRange aOldRange = pOldObj->GetOutRange();
1322                         SCsROW nDiff = aOldRange.aStart.Row()-aNewOut.aStart.Row();
1323                         aNewOut.aStart.SetRow( aOldRange.aStart.Row() );
1324                         aNewOut.aEnd.SetRow( aNewOut.aEnd.Row()+nDiff );
1325                         if( !ValidRow( aNewOut.aStart.Row() ) || !ValidRow( aNewOut.aEnd.Row() ) )
1326                             bOverflow = sal_True;
1327                     }
1328                 }
1329 
1330 				if ( bOverflow )
1331 				{
1332 					//	like with STR_PROTECTIONERR, use undo to reverse everything
1333 					DBG_ASSERT( bRecord, "DataPilotUpdate: can't undo" );
1334 					bUndoSelf = sal_True;
1335 					nErrId = STR_PIVOT_ERROR;
1336 				}
1337 				else
1338 				{
1339 					ScEditableTester aTester( pDoc, aNewOut );
1340 					if ( !aTester.IsEditable() )
1341 					{
1342 						//	destination area isn't editable
1343 						//!	reverse everything done so far, don't proceed
1344 
1345 						//	quick solution: proceed to end, use undo action
1346 						//	to reverse everything:
1347 						DBG_ASSERT( bRecord, "DataPilotUpdate: can't undo" );
1348 						bUndoSelf = sal_True;
1349 						nErrId = aTester.GetMessageId();
1350 					}
1351 				}
1352 
1353 				//	test if new output area is empty except for old area
1354 				if ( !bApi )
1355 				{
1356 					sal_Bool bEmpty;
1357 					if ( pOldObj )	// OutRange of pOldObj (pDestObj) is still old area
1358 						bEmpty = lcl_EmptyExcept( pDoc, aNewOut, pOldObj->GetOutRange() );
1359 					else
1360 						bEmpty = pDoc->IsBlockEmpty( aNewOut.aStart.Tab(),
1361 											aNewOut.aStart.Col(), aNewOut.aStart.Row(),
1362 											aNewOut.aEnd.Col(), aNewOut.aEnd.Row() );
1363 
1364 					if ( !bEmpty )
1365 					{
1366 						QueryBox aBox( rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1367 										 ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY) );
1368 						if (aBox.Execute() == RET_NO)
1369 						{
1370 							//!	like above (not editable), use undo to reverse everything
1371 							DBG_ASSERT( bRecord, "DataPilotUpdate: can't undo" );
1372 							bUndoSelf = sal_True;
1373 						}
1374 					}
1375 				}
1376 
1377 				if ( bRecord )
1378 				{
1379 					SCTAB nTab = aNewOut.aStart.Tab();
1380 					pNewUndoDoc = new ScDocument( SCDOCMODE_UNDO );
1381 					pNewUndoDoc->InitUndo( pDoc, nTab, nTab );
1382 					pDoc->CopyToDocument( aNewOut, IDF_ALL, sal_False, pNewUndoDoc );
1383 				}
1384 
1385 				pDestObj->Output( aNewOut.aStart );
1386 
1387 				rDocShell.PostPaintGridAll();			//! only necessary parts
1388 				bDone = sal_True;
1389 			}
1390 		}
1391 		// else nothing (no old, no new)
1392 	}
1393 
1394 	if ( bRecord && bDone )
1395 	{
1396 		SfxUndoAction* pAction = new ScUndoDataPilot( &rDocShell,
1397 									pOldUndoDoc, pNewUndoDoc, pUndoDPObj, pDestObj, bAllowMove );
1398 		pOldUndoDoc = NULL;
1399 		pNewUndoDoc = NULL;		// pointers are used in undo action
1400 		// pUndoDPObj is copied
1401 
1402 		if (bUndoSelf)
1403 		{
1404 			//	use undo action to restore original state
1405 			//!	prevent setting the document modified? (ScDocShellModificator)
1406 
1407 			pAction->Undo();
1408 			delete pAction;
1409 			bDone = sal_False;
1410 		}
1411 		else
1412 			rDocShell.GetUndoManager()->AddUndoAction( pAction );
1413 	}
1414 
1415 	delete pOldUndoDoc;		// if not used for undo
1416 	delete pNewUndoDoc;
1417 	delete pUndoDPObj;
1418 
1419 	if (bDone)
1420     {
1421         // notify API objects
1422         if (pDestObj)
1423             pDoc->BroadcastUno( ScDataPilotModifiedHint( pDestObj->GetName() ) );
1424 		aModificator.SetDocumentModified();
1425     }
1426 
1427 	if ( nErrId && !bApi )
1428 		rDocShell.ErrorMessage( nErrId );
1429 
1430 	return bDone;
1431 }
1432 
1433 //==================================================================
1434 //
1435 //      database import
1436 
1437 void ScDBDocFunc::UpdateImport( const String& rTarget, const svx::ODataAccessDescriptor& rDescriptor )
1438 {
1439     // rTarget is the name of a database range
1440 
1441 	ScDocument* pDoc = rDocShell.GetDocument();
1442 	ScDBCollection& rDBColl = *pDoc->GetDBCollection();
1443     ScDBData* pData = NULL;
1444 	ScImportParam aImportParam;
1445 	sal_Bool bFound = sal_False;
1446 	sal_uInt16 nCount = rDBColl.GetCount();
1447 	for (sal_uInt16 i=0; i<nCount && !bFound; i++)
1448 	{
1449 		pData = rDBColl[i];
1450 		if (pData->GetName() == rTarget)
1451 			bFound = sal_True;
1452 	}
1453 	if (!bFound)
1454 	{
1455 		InfoBox aInfoBox(rDocShell.GetActiveDialogParent(),
1456 					ScGlobal::GetRscString( STR_TARGETNOTFOUND ) );
1457 		aInfoBox.Execute();
1458 		return;
1459 	}
1460 
1461 	SCTAB nTab;
1462 	SCCOL nDummyCol;
1463     SCROW nDummyRow;
1464 	pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow );
1465 	pData->GetImportParam( aImportParam );
1466 
1467     rtl::OUString sDBName;
1468     rtl::OUString sDBTable;
1469     sal_Int32 nCommandType = 0;
1470     rDescriptor[svx::daDataSource]  >>= sDBName;
1471     rDescriptor[svx::daCommand]     >>= sDBTable;
1472     rDescriptor[svx::daCommandType] >>= nCommandType;
1473 
1474     aImportParam.aDBName    = sDBName;
1475     aImportParam.bSql       = ( nCommandType == sdb::CommandType::COMMAND );
1476     aImportParam.aStatement = sDBTable;
1477     aImportParam.bNative    = sal_False;
1478     aImportParam.nType      = static_cast<sal_uInt8>( ( nCommandType == sdb::CommandType::QUERY ) ? ScDbQuery : ScDbTable );
1479     aImportParam.bImport	= sal_True;
1480 
1481     sal_Bool bContinue = DoImport( nTab, aImportParam, &rDescriptor, sal_True );
1482 
1483 	//	DB-Operationen wiederholen
1484 
1485 	ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
1486 	if (pViewSh)
1487 	{
1488 		ScRange aRange;
1489 		pData->GetArea(aRange);
1490 		pViewSh->MarkRange(aRange);			// selektieren
1491 
1492 		if ( bContinue )		// #41905# Fehler beim Import -> Abbruch
1493 		{
1494 			//	interne Operationen, wenn welche gespeichert
1495 
1496 			if ( pData->HasQueryParam() || pData->HasSortParam() || pData->HasSubTotalParam() )
1497 				pViewSh->RepeatDB();
1498 
1499 			//	Pivottabellen die den Bereich als Quelldaten haben
1500 
1501 			rDocShell.RefreshPivotTables(aRange);
1502 		}
1503 	}
1504 }
1505 
1506 
1507 
1508 
1509