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