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