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