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