xref: /trunk/main/sc/source/ui/docshell/dbdocfun.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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