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