fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / ui / docshell / dbdocfun.cxx
blobff748c484cfa62aadd7eb661f03695f5ca0215c1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sfx2/app.hxx>
21 #include <vcl/msgbox.hxx>
22 #include <vcl/waitobj.hxx>
23 #include <svx/dataaccessdescriptor.hxx>
25 #include <com/sun/star/sdb/CommandType.hpp>
27 #include "dbdocfun.hxx"
28 #include "sc.hrc"
29 #include "dbdata.hxx"
30 #include "undodat.hxx"
31 #include "docsh.hxx"
32 #include "docfunc.hxx"
33 #include "globstr.hrc"
34 #include "globalnames.hxx"
35 #include "tabvwsh.hxx"
36 #include "patattr.hxx"
37 #include "rangenam.hxx"
38 #include "olinetab.hxx"
39 #include "dpobject.hxx"
40 #include "dpsave.hxx"
41 #include "dociter.hxx"
42 #include "editable.hxx"
43 #include "attrib.hxx"
44 #include "drwlayer.hxx"
45 #include "dpshttab.hxx"
46 #include "hints.hxx"
47 #include "queryentry.hxx"
48 #include "markdata.hxx"
49 #include "progress.hxx"
50 #include <undosort.hxx>
51 #include <inputopt.hxx>
53 #include <set>
54 #include <memory>
56 using namespace ::com::sun::star;
58 bool ScDBDocFunc::AddDBRange( const OUString& rName, const ScRange& rRange, bool /* bApi */ )
61 ScDocShellModificator aModificator( rDocShell );
63 ScDocument& rDoc = rDocShell.GetDocument();
64 ScDBCollection* pDocColl = rDoc.GetDBCollection();
65 bool bUndo (rDoc.IsUndoEnabled());
67 ScDBCollection* pUndoColl = NULL;
68 if (bUndo)
69 pUndoColl = new ScDBCollection( *pDocColl );
71 ScDBData* pNew = new ScDBData( rName, rRange.aStart.Tab(),
72 rRange.aStart.Col(), rRange.aStart.Row(),
73 rRange.aEnd.Col(), rRange.aEnd.Row() );
75 // #i55926# While loading XML, formula cells only have a single string token,
76 // so CompileDBFormula would never find any name (index) tokens, and would
77 // unnecessarily loop through all cells.
78 bool bCompile = !rDoc.IsImportingXML();
79 bool bOk;
80 if ( bCompile )
81 rDoc.PreprocessDBDataUpdate();
82 if ( rName == STR_DB_LOCAL_NONAME )
84 rDoc.SetAnonymousDBData(rRange.aStart.Tab() , pNew);
85 bOk = true;
87 else
89 bOk = pDocColl->getNamedDBs().insert(pNew);
91 if ( bCompile )
92 rDoc.CompileHybridFormula();
94 if (!bOk)
96 delete pNew;
97 delete pUndoColl;
98 return false;
101 if (bUndo)
103 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
104 rDocShell.GetUndoManager()->AddUndoAction(
105 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
108 aModificator.SetDocumentModified();
109 SfxGetpApp()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
110 return true;
113 bool ScDBDocFunc::DeleteDBRange(const OUString& rName)
115 bool bDone = false;
116 ScDocument& rDoc = rDocShell.GetDocument();
117 ScDBCollection* pDocColl = rDoc.GetDBCollection();
118 bool bUndo = rDoc.IsUndoEnabled();
120 ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
121 const ScDBData* p = rDBs.findByUpperName(ScGlobal::pCharClass->uppercase(rName));
122 if (p)
124 ScDocShellModificator aModificator( rDocShell );
126 ScDBCollection* pUndoColl = NULL;
127 if (bUndo)
128 pUndoColl = new ScDBCollection( *pDocColl );
130 rDoc.PreprocessDBDataUpdate();
131 rDBs.erase(*p);
132 rDoc.CompileHybridFormula();
134 if (bUndo)
136 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
137 rDocShell.GetUndoManager()->AddUndoAction(
138 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
141 aModificator.SetDocumentModified();
142 SfxGetpApp()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
143 bDone = true;
146 return bDone;
149 bool ScDBDocFunc::RenameDBRange( const OUString& rOld, const OUString& rNew )
151 bool bDone = false;
152 ScDocument& rDoc = rDocShell.GetDocument();
153 ScDBCollection* pDocColl = rDoc.GetDBCollection();
154 bool bUndo = rDoc.IsUndoEnabled();
155 ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
156 const ScDBData* pOld = rDBs.findByUpperName(ScGlobal::pCharClass->uppercase(rOld));
157 const ScDBData* pNew = rDBs.findByUpperName(ScGlobal::pCharClass->uppercase(rNew));
158 if (pOld && !pNew)
160 ScDocShellModificator aModificator( rDocShell );
162 ScDBData* pNewData = new ScDBData(rNew, *pOld);
164 ScDBCollection* pUndoColl = new ScDBCollection( *pDocColl );
166 rDoc.PreprocessDBDataUpdate();
167 rDBs.erase(*pOld);
168 bool bInserted = rDBs.insert(pNewData);
169 if (!bInserted) // Fehler -> alten Zustand wiederherstellen
171 delete pNewData;
172 rDoc.SetDBCollection(pUndoColl); // gehoert dann dem Dokument
175 rDoc.CompileHybridFormula();
177 if (bInserted) // Einfuegen hat geklappt
179 if (bUndo)
181 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
182 rDocShell.GetUndoManager()->AddUndoAction(
183 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
185 else
186 delete pUndoColl;
188 aModificator.SetDocumentModified();
189 SfxGetpApp()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
190 bDone = true;
194 return bDone;
197 bool ScDBDocFunc::ModifyDBData( const ScDBData& rNewData )
199 bool bDone = false;
200 ScDocument& rDoc = rDocShell.GetDocument();
201 ScDBCollection* pDocColl = rDoc.GetDBCollection();
202 bool bUndo = rDoc.IsUndoEnabled();
204 ScDBData* pData = NULL;
205 if (rNewData.GetName() == STR_DB_LOCAL_NONAME)
207 ScRange aRange;
208 rNewData.GetArea(aRange);
209 SCTAB nTab = aRange.aStart.Tab();
210 pData = rDoc.GetAnonymousDBData(nTab);
212 else
213 pData = pDocColl->getNamedDBs().findByUpperName(rNewData.GetUpperName());
215 if (pData)
217 ScDocShellModificator aModificator( rDocShell );
218 ScRange aOldRange, aNewRange;
219 pData->GetArea(aOldRange);
220 rNewData.GetArea(aNewRange);
221 bool bAreaChanged = ( aOldRange != aNewRange ); // dann muss neu compiliert werden
223 ScDBCollection* pUndoColl = NULL;
224 if (bUndo)
225 pUndoColl = new ScDBCollection( *pDocColl );
227 *pData = rNewData;
228 if (bAreaChanged)
229 rDoc.CompileDBFormula();
231 if (bUndo)
233 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
234 rDocShell.GetUndoManager()->AddUndoAction(
235 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
238 aModificator.SetDocumentModified();
239 bDone = true;
242 return bDone;
245 void ScDBDocFunc::ModifyAllDBData( const ScDBCollection& rNewColl, const std::vector<ScRange>& rDelAreaList )
247 ScDocShellModificator aModificator(rDocShell);
248 ScDocument& rDoc = rDocShell.GetDocument();
249 ScDBCollection* pOldColl = rDoc.GetDBCollection();
250 ScDBCollection* pUndoColl = NULL;
251 bool bRecord = rDoc.IsUndoEnabled();
253 std::vector<ScRange>::const_iterator iter;
254 for (iter = rDelAreaList.begin(); iter != rDelAreaList.end(); ++iter)
256 // unregistering target in SBA no longer necessary
257 const ScAddress& rStart = iter->aStart;
258 const ScAddress& rEnd = iter->aEnd;
259 rDocShell.DBAreaDeleted(
260 rStart.Tab(), rStart.Col(), rStart.Row(), rEnd.Col(), rEnd.Row());
263 if (bRecord)
264 pUndoColl = new ScDBCollection( *pOldColl );
266 // register target in SBA no longer necessary
268 rDoc.PreprocessDBDataUpdate();
269 rDoc.SetDBCollection( new ScDBCollection( rNewColl ) );
270 rDoc.CompileHybridFormula();
271 pOldColl = NULL;
272 rDocShell.PostPaint(ScRange(0, 0, 0, MAXCOL, MAXROW, MAXTAB), PAINT_GRID);
273 aModificator.SetDocumentModified();
274 SfxGetpApp()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
276 if (bRecord)
278 ScDBCollection* pRedoColl = new ScDBCollection(rNewColl);
279 rDocShell.GetUndoManager()->AddUndoAction(
280 new ScUndoDBData(&rDocShell, pUndoColl, pRedoColl));
284 bool ScDBDocFunc::RepeatDB( const OUString& rDBName, bool bRecord, bool bApi, bool bIsUnnamed, SCTAB aTab )
286 //! auch fuer ScDBFunc::RepeatDB benutzen!
288 bool bDone = false;
289 ScDocument& rDoc = rDocShell.GetDocument();
290 if (bRecord && !rDoc.IsUndoEnabled())
291 bRecord = false;
292 ScDBData* pDBData = NULL;
293 if (bIsUnnamed)
295 pDBData = rDoc.GetAnonymousDBData( aTab );
297 else
299 ScDBCollection* pColl = rDoc.GetDBCollection();
300 if (pColl)
301 pDBData = pColl->getNamedDBs().findByUpperName(ScGlobal::pCharClass->uppercase(rDBName));
304 if ( pDBData )
306 ScQueryParam aQueryParam;
307 pDBData->GetQueryParam( aQueryParam );
308 bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
310 ScSortParam aSortParam;
311 pDBData->GetSortParam( aSortParam );
312 bool bSort = aSortParam.maKeyState[0].bDoSort;
314 ScSubTotalParam aSubTotalParam;
315 pDBData->GetSubTotalParam( aSubTotalParam );
316 bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
318 if ( bQuery || bSort || bSubTotal )
320 bool bQuerySize = false;
321 ScRange aOldQuery;
322 ScRange aNewQuery;
323 if (bQuery && !aQueryParam.bInplace)
325 ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
326 aQueryParam.nDestTab, true );
327 if (pDest && pDest->IsDoSize())
329 pDest->GetArea( aOldQuery );
330 bQuerySize = true;
334 SCTAB nTab;
335 SCCOL nStartCol;
336 SCROW nStartRow;
337 SCCOL nEndCol;
338 SCROW nEndRow;
339 pDBData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
341 //! Undo nur benoetigte Daten ?
343 ScDocument* pUndoDoc = NULL;
344 ScOutlineTable* pUndoTab = NULL;
345 ScRangeName* pUndoRange = NULL;
346 ScDBCollection* pUndoDB = NULL;
348 if (bRecord)
350 SCTAB nTabCount = rDoc.GetTableCount();
351 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
352 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
353 if (pTable)
355 pUndoTab = new ScOutlineTable( *pTable );
357 // column/row state
358 SCCOLROW nOutStartCol, nOutEndCol;
359 SCCOLROW nOutStartRow, nOutEndRow;
360 pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
361 pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
363 pUndoDoc->InitUndo( &rDoc, nTab, nTab, true, true );
364 rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0,
365 nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab,
366 IDF_NONE, false, pUndoDoc );
367 rDoc.CopyToDocument( 0, static_cast<SCROW>(nOutStartRow),
368 nTab, MAXCOL, static_cast<SCROW>(nOutEndRow), nTab,
369 IDF_NONE, false, pUndoDoc );
371 else
372 pUndoDoc->InitUndo( &rDoc, nTab, nTab, false, true );
374 // Datenbereich sichern - incl. Filter-Ergebnis
375 rDoc.CopyToDocument( 0,nStartRow,nTab, MAXCOL,nEndRow,nTab, IDF_ALL, false, pUndoDoc );
377 // alle Formeln wegen Referenzen
378 rDoc.CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, IDF_FORMULA, false, pUndoDoc );
380 // DB- und andere Bereiche
381 ScRangeName* pDocRange = rDoc.GetRangeName();
382 if (!pDocRange->empty())
383 pUndoRange = new ScRangeName( *pDocRange );
384 ScDBCollection* pDocDB = rDoc.GetDBCollection();
385 if (!pDocDB->empty())
386 pUndoDB = new ScDBCollection( *pDocDB );
389 if (bSort && bSubTotal)
391 // Sortieren ohne SubTotals
393 aSubTotalParam.bRemoveOnly = true; // wird unten wieder zurueckgesetzt
394 DoSubTotals( nTab, aSubTotalParam, NULL, false, bApi );
397 if (bSort)
399 pDBData->GetSortParam( aSortParam ); // Bereich kann sich geaendert haben
400 (void)Sort( nTab, aSortParam, false, false, bApi );
402 if (bQuery)
404 pDBData->GetQueryParam( aQueryParam ); // Bereich kann sich geaendert haben
405 ScRange aAdvSource;
406 if (pDBData->GetAdvancedQuerySource(aAdvSource))
407 Query( nTab, aQueryParam, &aAdvSource, false, bApi );
408 else
409 Query( nTab, aQueryParam, NULL, false, bApi );
411 // bei nicht-inplace kann die Tabelle umgestellt worden sein
412 // if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
413 // SetTabNo( nTab );
415 if (bSubTotal)
417 pDBData->GetSubTotalParam( aSubTotalParam ); // Bereich kann sich geaendert haben
418 aSubTotalParam.bRemoveOnly = false;
419 DoSubTotals( nTab, aSubTotalParam, NULL, false, bApi );
422 if (bRecord)
424 SCTAB nDummyTab;
425 SCCOL nDummyCol;
426 SCROW nDummyRow;
427 SCROW nNewEndRow;
428 pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
430 const ScRange* pOld = NULL;
431 const ScRange* pNew = NULL;
432 if (bQuerySize)
434 ScDBData* pDest = rDoc.GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
435 aQueryParam.nDestTab, true );
436 if (pDest)
438 pDest->GetArea( aNewQuery );
439 pOld = &aOldQuery;
440 pNew = &aNewQuery;
444 rDocShell.GetUndoManager()->AddUndoAction(
445 new ScUndoRepeatDB( &rDocShell, nTab,
446 nStartCol, nStartRow, nEndCol, nEndRow,
447 nNewEndRow,
448 //nCurX, nCurY,
449 nStartCol, nStartRow,
450 pUndoDoc, pUndoTab,
451 pUndoRange, pUndoDB,
452 pOld, pNew ) );
455 rDocShell.PostPaint(ScRange(0, 0, nTab, MAXCOL, MAXROW, nTab),
456 PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE);
457 bDone = true;
459 else if (!bApi) // "Keine Operationen auszufuehren"
460 rDocShell.ErrorMessage(STR_MSSG_REPEATDB_0);
463 return bDone;
466 bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
467 bool bRecord, bool bPaint, bool bApi )
469 ScDocShellModificator aModificator( rDocShell );
471 ScDocument& rDoc = rDocShell.GetDocument();
472 if (bRecord && !rDoc.IsUndoEnabled())
473 bRecord = false;
475 ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
476 rSortParam.nCol2, rSortParam.nRow2 );
477 if (!pDBData)
479 OSL_FAIL( "Sort: keine DBData" );
480 return false;
483 bool bCopy = !rSortParam.bInplace;
484 if ( bCopy && rSortParam.nDestCol == rSortParam.nCol1 &&
485 rSortParam.nDestRow == rSortParam.nRow1 && rSortParam.nDestTab == nTab )
486 bCopy = false;
488 ScSortParam aLocalParam( rSortParam );
489 if ( bCopy )
491 // Copy the data range to the destination then move the sort range to it.
492 ScRange aSrcRange(rSortParam.nCol1, rSortParam.nRow1, nTab, rSortParam.nCol2, rSortParam.nRow2, nTab);
493 ScAddress aDestPos(rSortParam.nDestCol,rSortParam.nDestRow,rSortParam.nDestTab);
495 ScDocFunc& rDocFunc = rDocShell.GetDocFunc();
496 bool bRet = rDocFunc.MoveBlock(aSrcRange, aDestPos, false, bRecord, bPaint, bApi);
498 if (!bRet)
499 return false;
501 aLocalParam.MoveToDest();
502 nTab = aLocalParam.nDestTab;
505 ScEditableTester aTester( &rDoc, nTab, aLocalParam.nCol1,aLocalParam.nRow1,
506 aLocalParam.nCol2,aLocalParam.nRow2 );
507 if (!aTester.IsEditable())
509 if (!bApi)
510 rDocShell.ErrorMessage(aTester.GetMessageId());
511 return false;
514 if ( aLocalParam.bIncludePattern && rDoc.HasAttrib(
515 aLocalParam.nCol1, aLocalParam.nRow1, nTab,
516 aLocalParam.nCol2, aLocalParam.nRow2, nTab,
517 HASATTR_MERGED | HASATTR_OVERLAPPED ) )
519 // Merge-Attribute wuerden beim Sortieren durcheinanderkommen
520 if (!bApi)
521 rDocShell.ErrorMessage(STR_SORT_ERR_MERGED);
522 return false;
525 // ausfuehren
527 WaitObject aWait( ScDocShell::GetActiveDialogParent() );
529 // Adjust aLocalParam cols/rows to used data area. Keep sticky top row or
530 // column (depending on direction) in any case, not just if it has headers,
531 // so empty leading cells will be sorted to the end.
532 bool bShrunk = false;
533 rDoc.ShrinkToUsedDataArea( bShrunk, nTab, aLocalParam.nCol1, aLocalParam.nRow1,
534 aLocalParam.nCol2, aLocalParam.nRow2, false, aLocalParam.bByRow, !aLocalParam.bByRow);
536 SCROW nStartRow = aLocalParam.nRow1;
537 if (aLocalParam.bByRow && aLocalParam.bHasHeader && nStartRow < aLocalParam.nRow2)
538 ++nStartRow;
540 // Calculate the script types for all cells in the sort range beforehand.
541 // This will speed up the row height adjustment that takes place after the
542 // sort.
543 rDoc.UpdateScriptTypes(
544 ScAddress(aLocalParam.nCol1,nStartRow,nTab),
545 aLocalParam.nCol2-aLocalParam.nCol1+1,
546 aLocalParam.nRow2-nStartRow+1);
548 // No point adjusting row heights after the sort when all rows have the same height.
549 bool bUniformRowHeight =
550 rDoc.HasUniformRowHeight(nTab, nStartRow, aLocalParam.nRow2);
552 bool bRepeatQuery = false; // bestehenden Filter wiederholen?
553 ScQueryParam aQueryParam;
554 pDBData->GetQueryParam( aQueryParam );
555 if ( aQueryParam.GetEntry(0).bDoQuery )
556 bRepeatQuery = true;
558 sc::ReorderParam aUndoParam;
560 // don't call ScDocument::Sort with an empty SortParam (may be empty here if bCopy is set)
561 if (aLocalParam.GetSortKeyCount() && aLocalParam.maKeyState[0].bDoSort)
563 ScInputOptions aInputOption = SC_MOD()->GetInputOptions();
564 bool bUpdateRefs = aInputOption.GetSortRefUpdate();
565 ScProgress aProgress(&rDocShell, ScGlobal::GetRscString(STR_PROGRESS_SORTING), 0);
566 rDoc.Sort(nTab, aLocalParam, bRepeatQuery, bUpdateRefs, &aProgress, &aUndoParam);
569 if (bRecord)
571 // Set up an undo object.
572 sc::UndoSort* pUndoAction = new sc::UndoSort(&rDocShell, aUndoParam);
573 rDocShell.GetUndoManager()->AddUndoAction(pUndoAction);
576 pDBData->SetSortParam(rSortParam);
577 // Remember additional settings on anonymous database ranges.
578 if (pDBData == rDoc.GetAnonymousDBData( nTab) || rDoc.GetDBCollection()->getAnonDBs().has( pDBData))
579 pDBData->UpdateFromSortParam( rSortParam);
581 if (nStartRow <= aLocalParam.nRow2)
583 ScRange aDirtyRange(
584 aLocalParam.nCol1, nStartRow, nTab,
585 aLocalParam.nCol2, aLocalParam.nRow2, nTab);
586 rDoc.SetDirty( aDirtyRange, true );
589 if (bPaint)
591 sal_uInt16 nPaint = PAINT_GRID;
592 SCCOL nStartX = aLocalParam.nCol1;
593 SCROW nStartY = aLocalParam.nRow1;
594 SCCOL nEndX = aLocalParam.nCol2;
595 SCROW nEndY = aLocalParam.nRow2;
596 if ( bRepeatQuery )
598 nPaint |= PAINT_LEFT;
599 nStartX = 0;
600 nEndX = MAXCOL;
602 rDocShell.PostPaint(ScRange(nStartX, nStartY, nTab, nEndX, nEndY, nTab), nPaint);
605 if (!bUniformRowHeight && nStartRow <= aLocalParam.nRow2)
606 rDocShell.AdjustRowHeight(nStartRow, aLocalParam.nRow2, nTab);
608 aModificator.SetDocumentModified();
610 return true;
613 bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam,
614 const ScRange* pAdvSource, bool bRecord, bool bApi )
616 ScDocShellModificator aModificator( rDocShell );
618 ScDocument& rDoc = rDocShell.GetDocument();
619 if (bRecord && !rDoc.IsUndoEnabled())
620 bRecord = false;
621 ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rQueryParam.nCol1, rQueryParam.nRow1,
622 rQueryParam.nCol2, rQueryParam.nRow2 );
623 if (!pDBData)
625 OSL_FAIL( "Query: keine DBData" );
626 return false;
629 // Wechsel von Inplace auf nicht-Inplace, dann erst Inplace aufheben:
630 // (nur, wenn im Dialog "Persistent" ausgewaehlt ist)
632 if ( !rQueryParam.bInplace && pDBData->HasQueryParam() && rQueryParam.bDestPers )
634 ScQueryParam aOldQuery;
635 pDBData->GetQueryParam(aOldQuery);
636 if (aOldQuery.bInplace)
638 // alte Filterung aufheben
640 SCSIZE nEC = aOldQuery.GetEntryCount();
641 for (SCSIZE i=0; i<nEC; i++)
642 aOldQuery.GetEntry(i).bDoQuery = false;
643 aOldQuery.bDuplicate = true;
644 Query( nTab, aOldQuery, NULL, bRecord, bApi );
648 ScQueryParam aLocalParam( rQueryParam ); // fuer Paint / Zielbereich
649 bool bCopy = !rQueryParam.bInplace; // kopiert wird in Table::Query
650 ScDBData* pDestData = NULL; // Bereich, in den kopiert wird
651 bool bDoSize = false; // Zielgroesse anpassen (einf./loeschen)
652 SCCOL nFormulaCols = 0; // nur bei bDoSize
653 bool bKeepFmt = false;
654 ScRange aOldDest;
655 ScRange aDestTotal;
656 if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 &&
657 rQueryParam.nDestRow == rQueryParam.nRow1 && rQueryParam.nDestTab == nTab )
658 bCopy = false;
659 SCTAB nDestTab = nTab;
660 if ( bCopy )
662 aLocalParam.MoveToDest();
663 nDestTab = rQueryParam.nDestTab;
664 if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
666 if (!bApi)
667 rDocShell.ErrorMessage(STR_PASTE_FULL);
668 return false;
671 ScEditableTester aTester( &rDoc, nDestTab, aLocalParam.nCol1,aLocalParam.nRow1,
672 aLocalParam.nCol2,aLocalParam.nRow2);
673 if (!aTester.IsEditable())
675 if (!bApi)
676 rDocShell.ErrorMessage(aTester.GetMessageId());
677 return false;
680 pDestData = rDoc.GetDBAtCursor( rQueryParam.nDestCol, rQueryParam.nDestRow,
681 rQueryParam.nDestTab, true );
682 if (pDestData)
684 pDestData->GetArea( aOldDest );
685 aDestTotal=ScRange( rQueryParam.nDestCol,
686 rQueryParam.nDestRow,
687 nDestTab,
688 rQueryParam.nDestCol + rQueryParam.nCol2 - rQueryParam.nCol1,
689 rQueryParam.nDestRow + rQueryParam.nRow2 - rQueryParam.nRow1,
690 nDestTab );
692 bDoSize = pDestData->IsDoSize();
693 // Test, ob Formeln aufgefuellt werden muessen (nFormulaCols):
694 if ( bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col() )
696 SCCOL nTestCol = aOldDest.aEnd.Col() + 1; // neben dem Bereich
697 SCROW nTestRow = rQueryParam.nDestRow +
698 ( aLocalParam.bHasHeader ? 1 : 0 );
699 while ( nTestCol <= MAXCOL &&
700 rDoc.GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA )
701 ++nTestCol, ++nFormulaCols;
704 bKeepFmt = pDestData->IsKeepFmt();
705 if ( bDoSize && !rDoc.CanFitBlock( aOldDest, aDestTotal ) )
707 if (!bApi)
708 rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2); // kann keine Zeilen einfuegen
709 return false;
714 // ausfuehren
716 WaitObject aWait( ScDocShell::GetActiveDialogParent() );
718 bool bKeepSub = false; // bestehende Teilergebnisse wiederholen?
719 ScSubTotalParam aSubTotalParam;
720 if (rQueryParam.GetEntry(0).bDoQuery) // nicht beim Aufheben
722 pDBData->GetSubTotalParam( aSubTotalParam ); // Teilergebnisse vorhanden?
724 if ( aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly )
725 bKeepSub = true;
728 ScDocument* pUndoDoc = NULL;
729 ScDBCollection* pUndoDB = NULL;
730 const ScRange* pOld = NULL;
732 if ( bRecord )
734 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
735 if (bCopy)
737 pUndoDoc->InitUndo( &rDoc, nDestTab, nDestTab, false, true );
738 rDoc.CopyToDocument( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
739 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab,
740 IDF_ALL, false, pUndoDoc );
741 // Attribute sichern, falls beim Filtern mitkopiert
743 if (pDestData)
745 rDoc.CopyToDocument( aOldDest, IDF_ALL, false, pUndoDoc );
746 pOld = &aOldDest;
749 else
751 pUndoDoc->InitUndo( &rDoc, nTab, nTab, false, true );
752 rDoc.CopyToDocument( 0, rQueryParam.nRow1, nTab, MAXCOL, rQueryParam.nRow2, nTab,
753 IDF_NONE, false, pUndoDoc );
756 ScDBCollection* pDocDB = rDoc.GetDBCollection();
757 if (!pDocDB->empty())
758 pUndoDB = new ScDBCollection( *pDocDB );
760 rDoc.BeginDrawUndo();
763 ScDocument* pAttribDoc = NULL;
764 ScRange aAttribRange;
765 if (pDestData) // Zielbereich loeschen
767 if ( bKeepFmt )
769 // kleinere der End-Spalten, Header+1 Zeile
770 aAttribRange = aOldDest;
771 if ( aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col() )
772 aAttribRange.aEnd.SetCol( aDestTotal.aEnd.Col() );
773 aAttribRange.aEnd.SetRow( aAttribRange.aStart.Row() +
774 ( aLocalParam.bHasHeader ? 1 : 0 ) );
776 // auch fuer aufgefuellte Formeln
777 aAttribRange.aEnd.SetCol( aAttribRange.aEnd.Col() + nFormulaCols );
779 pAttribDoc = new ScDocument( SCDOCMODE_UNDO );
780 pAttribDoc->InitUndo( &rDoc, nDestTab, nDestTab, false, true );
781 rDoc.CopyToDocument( aAttribRange, IDF_ATTRIB, false, pAttribDoc );
784 if ( bDoSize )
785 rDoc.FitBlock( aOldDest, aDestTotal );
786 else
787 rDoc.DeleteAreaTab(aOldDest, IDF_ALL); // einfach loeschen
790 // Filtern am Dokument ausfuehren
791 SCSIZE nCount = rDoc.Query( nTab, rQueryParam, bKeepSub );
792 if (bCopy)
794 aLocalParam.nRow2 = aLocalParam.nRow1 + nCount;
795 if (!aLocalParam.bHasHeader && nCount > 0)
796 --aLocalParam.nRow2;
798 if ( bDoSize )
800 // auf wirklichen Ergebnis-Bereich anpassen
801 // (das hier ist immer eine Verkleinerung)
803 ScRange aNewDest( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
804 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab );
805 rDoc.FitBlock( aDestTotal, aNewDest, false ); // sal_False - nicht loeschen
807 if ( nFormulaCols > 0 )
809 // Formeln ausfuellen
810 //! Undo (Query und Repeat) !!!
812 ScRange aNewForm( aLocalParam.nCol2+1, aLocalParam.nRow1, nDestTab,
813 aLocalParam.nCol2+nFormulaCols, aLocalParam.nRow2, nDestTab );
814 ScRange aOldForm = aNewForm;
815 aOldForm.aEnd.SetRow( aOldDest.aEnd.Row() );
816 rDoc.FitBlock( aOldForm, aNewForm, false );
818 ScMarkData aMark;
819 aMark.SelectOneTable(nDestTab);
820 SCROW nFStartY = aLocalParam.nRow1 + ( aLocalParam.bHasHeader ? 1 : 0 );
822 sal_uLong nProgCount = nFormulaCols;
823 nProgCount *= aLocalParam.nRow2 - nFStartY;
824 ScProgress aProgress( rDoc.GetDocumentShell(),
825 ScGlobal::GetRscString(STR_FILL_SERIES_PROGRESS), nProgCount );
827 rDoc.Fill( aLocalParam.nCol2+1, nFStartY,
828 aLocalParam.nCol2+nFormulaCols, nFStartY, &aProgress, aMark,
829 aLocalParam.nRow2 - nFStartY,
830 FILL_TO_BOTTOM, FILL_SIMPLE );
834 if ( pAttribDoc ) // gemerkte Attribute zurueckkopieren
836 // Header
837 if (aLocalParam.bHasHeader)
839 ScRange aHdrRange = aAttribRange;
840 aHdrRange.aEnd.SetRow( aHdrRange.aStart.Row() );
841 pAttribDoc->CopyToDocument( aHdrRange, IDF_ATTRIB, false, &rDoc );
844 // Daten
845 SCCOL nAttrEndCol = aAttribRange.aEnd.Col();
846 SCROW nAttrRow = aAttribRange.aStart.Row() + ( aLocalParam.bHasHeader ? 1 : 0 );
847 for (SCCOL nCol = aAttribRange.aStart.Col(); nCol<=nAttrEndCol; nCol++)
849 const ScPatternAttr* pSrcPattern = pAttribDoc->GetPattern(
850 nCol, nAttrRow, nDestTab );
851 OSL_ENSURE(pSrcPattern,"Pattern ist 0");
852 if (pSrcPattern)
854 rDoc.ApplyPatternAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
855 nDestTab, *pSrcPattern );
856 const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
857 if (pStyle)
858 rDoc.ApplyStyleAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
859 nDestTab, *pStyle );
863 delete pAttribDoc;
867 // speichern: Inplace immer, sonst je nach Einstellung
868 // alter Inplace-Filter ist ggf. schon aufgehoben
870 bool bSave = rQueryParam.bInplace || rQueryParam.bDestPers;
871 if (bSave) // merken
873 pDBData->SetQueryParam( rQueryParam );
874 pDBData->SetHeader( rQueryParam.bHasHeader ); //! ???
875 pDBData->SetAdvancedQuerySource( pAdvSource ); // after SetQueryParam
878 if (bCopy) // neuen DB-Bereich merken
880 // selektieren wird hinterher von aussen (dbfunc)
881 // momentan ueber DB-Bereich an der Zielposition, darum muss dort
882 // auf jeden Fall ein Bereich angelegt werden.
884 ScDBData* pNewData;
885 if (pDestData)
886 pNewData = pDestData; // Bereich vorhanden -> anpassen (immer!)
887 else // Bereich anlegen
888 pNewData = rDocShell.GetDBData(
889 ScRange( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
890 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab ),
891 SC_DB_MAKE, SC_DBSEL_FORCE_MARK );
893 if (pNewData)
895 pNewData->SetArea( nDestTab, aLocalParam.nCol1, aLocalParam.nRow1,
896 aLocalParam.nCol2, aLocalParam.nRow2 );
898 // Query-Param wird am Ziel nicht mehr eingestellt, fuehrt nur zu Verwirrung
899 // und Verwechslung mit dem Query-Param am Quellbereich (#37187#)
901 else
903 OSL_FAIL("Zielbereich nicht da");
907 if (!bCopy)
909 rDoc.InvalidatePageBreaks(nTab);
910 rDoc.UpdatePageBreaks( nTab );
913 // #i23299# Subtotal functions depend on cell's filtered states.
914 ScRange aDirtyRange(0 , aLocalParam.nRow1, nDestTab, MAXCOL, aLocalParam.nRow2, nDestTab);
915 rDoc.SetSubTotalCellsDirty(aDirtyRange);
917 if ( bRecord )
919 // create undo action after executing, because of drawing layer undo
920 rDocShell.GetUndoManager()->AddUndoAction(
921 new ScUndoQuery( &rDocShell, nTab, rQueryParam, pUndoDoc, pUndoDB,
922 pOld, bDoSize, pAdvSource ) );
925 if (bCopy)
927 SCCOL nEndX = aLocalParam.nCol2;
928 SCROW nEndY = aLocalParam.nRow2;
929 if (pDestData)
931 if ( aOldDest.aEnd.Col() > nEndX )
932 nEndX = aOldDest.aEnd.Col();
933 if ( aOldDest.aEnd.Row() > nEndY )
934 nEndY = aOldDest.aEnd.Row();
936 if (bDoSize)
937 nEndY = MAXROW;
938 rDocShell.PostPaint(
939 ScRange(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, nEndX, nEndY, nDestTab),
940 PAINT_GRID);
942 else
943 rDocShell.PostPaint(
944 ScRange(0, rQueryParam.nRow1, nTab, MAXCOL, MAXROW, nTab),
945 PAINT_GRID | PAINT_LEFT);
946 aModificator.SetDocumentModified();
948 return true;
951 bool ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
952 const ScSortParam* pForceNewSort, bool bRecord, bool bApi )
954 //! auch fuer ScDBFunc::DoSubTotals benutzen!
955 // dann bleibt aussen:
956 // - neuen Bereich (aus DBData) markieren
957 // - SelectionChanged (?)
959 bool bDo = !rParam.bRemoveOnly; // sal_False = nur loeschen
960 bool bRet = false;
962 ScDocument& rDoc = rDocShell.GetDocument();
963 if (bRecord && !rDoc.IsUndoEnabled())
964 bRecord = false;
965 ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
966 rParam.nCol2, rParam.nRow2 );
967 if (!pDBData)
969 OSL_FAIL( "SubTotals: keine DBData" );
970 return false;
973 ScEditableTester aTester( &rDoc, nTab, 0,rParam.nRow1+1, MAXCOL,MAXROW );
974 if (!aTester.IsEditable())
976 if (!bApi)
977 rDocShell.ErrorMessage(aTester.GetMessageId());
978 return false;
981 if (rDoc.HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
982 rParam.nCol2, rParam.nRow2, nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ))
984 if (!bApi)
985 rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); // nicht in zusammengefasste einfuegen
986 return false;
989 bool bOk = true;
990 if (rParam.bReplace)
991 if (rDoc.TestRemoveSubTotals( nTab, rParam ))
993 bOk = ( ScopedVclPtr<MessBox>::Create( ScDocShell::GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
994 // "StarCalc" "Daten loeschen?"
995 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ),
996 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_1 ) )->Execute()
997 == RET_YES );
1000 if (bOk)
1002 WaitObject aWait( ScDocShell::GetActiveDialogParent() );
1003 ScDocShellModificator aModificator( rDocShell );
1005 ScSubTotalParam aNewParam( rParam ); // Bereichsende wird veraendert
1006 ScDocument* pUndoDoc = NULL;
1007 ScOutlineTable* pUndoTab = NULL;
1008 ScRangeName* pUndoRange = NULL;
1009 ScDBCollection* pUndoDB = NULL;
1011 if (bRecord) // alte Daten sichern
1013 bool bOldFilter = bDo && rParam.bDoSort;
1015 SCTAB nTabCount = rDoc.GetTableCount();
1016 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
1017 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
1018 if (pTable)
1020 pUndoTab = new ScOutlineTable( *pTable );
1022 // column/row state
1023 SCCOLROW nOutStartCol, nOutEndCol;
1024 SCCOLROW nOutStartRow, nOutEndRow;
1025 pTable->GetColArray().GetRange( nOutStartCol, nOutEndCol );
1026 pTable->GetRowArray().GetRange( nOutStartRow, nOutEndRow );
1028 pUndoDoc->InitUndo( &rDoc, nTab, nTab, true, true );
1029 rDoc.CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, false, pUndoDoc );
1030 rDoc.CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, false, pUndoDoc );
1032 else
1033 pUndoDoc->InitUndo( &rDoc, nTab, nTab, false, bOldFilter );
1035 // Datenbereich sichern - incl. Filter-Ergebnis
1036 rDoc.CopyToDocument( 0,rParam.nRow1+1,nTab, MAXCOL,rParam.nRow2,nTab,
1037 IDF_ALL, false, pUndoDoc );
1039 // alle Formeln wegen Referenzen
1040 rDoc.CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1,
1041 IDF_FORMULA, false, pUndoDoc );
1043 // DB- und andere Bereiche
1044 ScRangeName* pDocRange = rDoc.GetRangeName();
1045 if (!pDocRange->empty())
1046 pUndoRange = new ScRangeName( *pDocRange );
1047 ScDBCollection* pDocDB = rDoc.GetDBCollection();
1048 if (!pDocDB->empty())
1049 pUndoDB = new ScDBCollection( *pDocDB );
1052 // rDoc.SetOutlineTable( nTab, NULL );
1053 ScOutlineTable* pOut = rDoc.GetOutlineTable( nTab );
1054 if (pOut)
1055 pOut->GetRowArray().RemoveAll(); // nur Zeilen-Outlines loeschen
1057 if (rParam.bReplace)
1058 rDoc.RemoveSubTotals( nTab, aNewParam );
1059 bool bSuccess = true;
1060 if (bDo)
1062 // Sortieren
1063 if ( rParam.bDoSort || pForceNewSort )
1065 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1067 // Teilergebnis-Felder vor die Sortierung setzen
1068 // (doppelte werden weggelassen, kann darum auch wieder aufgerufen werden)
1070 ScSortParam aOldSort;
1071 pDBData->GetSortParam( aOldSort );
1072 ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
1073 Sort( nTab, aSortParam, false, false, bApi );
1076 bSuccess = rDoc.DoSubTotals( nTab, aNewParam );
1077 rDoc.SetDrawPageSize(nTab);
1079 ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
1080 aNewParam.nCol2, aNewParam.nRow2, nTab );
1081 rDoc.SetDirty( aDirtyRange, true );
1083 if (bRecord)
1085 // ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
1086 rDocShell.GetUndoManager()->AddUndoAction(
1087 new ScUndoSubTotals( &rDocShell, nTab,
1088 rParam, aNewParam.nRow2,
1089 pUndoDoc, pUndoTab, // pUndoDBData,
1090 pUndoRange, pUndoDB ) );
1093 if (!bSuccess)
1095 // "Kann keine Zeilen einfuegen"
1096 if (!bApi)
1097 rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
1100 // merken
1101 pDBData->SetSubTotalParam( aNewParam );
1102 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1103 rDoc.CompileDBFormula();
1105 rDocShell.PostPaint(ScRange(0, 0, nTab, MAXCOL,MAXROW,nTab),
1106 PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE);
1107 aModificator.SetDocumentModified();
1109 bRet = bSuccess;
1111 return bRet;
1114 namespace {
1116 bool lcl_EmptyExcept( ScDocument* pDoc, const ScRange& rRange, const ScRange& rExcept )
1118 ScCellIterator aIter( pDoc, rRange );
1119 for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
1121 if (!aIter.isEmpty()) // real content?
1123 if (!rExcept.In(aIter.GetPos()))
1124 return false; // cell found
1128 return true; // nothing found - empty
1131 bool isEditable(ScDocShell& rDocShell, const ScRangeList& rRanges, bool bApi)
1133 ScDocument& rDoc = rDocShell.GetDocument();
1134 if (!rDocShell.IsEditable() || rDoc.GetChangeTrack())
1136 // not recorded -> disallow
1137 if (!bApi)
1138 rDocShell.ErrorMessage(STR_PROTECTIONERR);
1140 return false;
1143 for (size_t i = 0, n = rRanges.size(); i < n; ++i)
1145 const ScRange* p = rRanges[i];
1146 ScEditableTester aTester(&rDoc, *p);
1147 if (!aTester.IsEditable())
1149 if (!bApi)
1150 rDocShell.ErrorMessage(aTester.GetMessageId());
1152 return false;
1156 return true;
1159 void createUndoDoc(std::unique_ptr<ScDocument>& pUndoDoc, ScDocument* pDoc, const ScRange& rRange)
1161 SCTAB nTab = rRange.aStart.Tab();
1162 pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
1163 pUndoDoc->InitUndo(pDoc, nTab, nTab);
1164 pDoc->CopyToDocument(rRange, IDF_ALL, false, pUndoDoc.get());
1167 bool checkNewOutputRange(ScDPObject& rDPObj, ScDocShell& rDocShell, ScRange& rNewOut, bool bApi)
1169 ScDocument& rDoc = rDocShell.GetDocument();
1171 bool bOverflow = false;
1172 rNewOut = rDPObj.GetNewOutputRange(bOverflow);
1174 // Test for overlap with source data range.
1175 // TODO: Check with other pivot tables as well.
1176 const ScSheetSourceDesc* pSheetDesc = rDPObj.GetSheetDesc();
1177 if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rNewOut))
1179 // New output range intersepts with the source data. Move it up to
1180 // where the old range is and see if that works.
1181 ScRange aOldRange = rDPObj.GetOutRange();
1182 SCsROW nDiff = aOldRange.aStart.Row() - rNewOut.aStart.Row();
1183 rNewOut.aStart.SetRow(aOldRange.aStart.Row());
1184 rNewOut.aEnd.IncRow(nDiff);
1185 if (!ValidRow(rNewOut.aStart.Row()) || !ValidRow(rNewOut.aEnd.Row()))
1186 bOverflow = true;
1189 if (bOverflow)
1191 if (!bApi)
1192 rDocShell.ErrorMessage(STR_PIVOT_ERROR);
1194 return false;
1197 ScEditableTester aTester(&rDoc, rNewOut);
1198 if (!aTester.IsEditable())
1200 // destination area isn't editable
1201 if (!bApi)
1202 rDocShell.ErrorMessage(aTester.GetMessageId());
1204 return false;
1207 return true;
1212 bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj,
1213 bool bRecord, bool bApi, bool bAllowMove )
1215 if (!pOldObj)
1217 if (!pNewObj)
1218 return false;
1220 return CreatePivotTable(*pNewObj, bRecord, bApi);
1223 if (pOldObj)
1225 if (!pNewObj)
1226 return RemovePivotTable(*pOldObj, bRecord, bApi);
1228 if (pOldObj == pNewObj)
1229 return UpdatePivotTable(*pOldObj, bRecord, bApi);
1232 OSL_ASSERT(pOldObj && pNewObj && pOldObj != pNewObj);
1234 ScDocShellModificator aModificator( rDocShell );
1235 WaitObject aWait( ScDocShell::GetActiveDialogParent() );
1237 ScRangeList aRanges;
1238 aRanges.Append(pOldObj->GetOutRange());
1239 aRanges.Append(pNewObj->GetOutRange().aStart); // at least one cell in the output position must be editable.
1240 if (!isEditable(rDocShell, aRanges, bApi))
1241 return false;
1243 std::unique_ptr<ScDocument> pOldUndoDoc;
1244 std::unique_ptr<ScDocument> pNewUndoDoc;
1246 ScDPObject aUndoDPObj(*pOldObj); // for undo or revert on failure
1248 ScDocument& rDoc = rDocShell.GetDocument();
1249 if (bRecord && !rDoc.IsUndoEnabled())
1250 bRecord = false;
1252 if (bRecord)
1253 createUndoDoc(pOldUndoDoc, &rDoc, pOldObj->GetOutRange());
1255 pNewObj->WriteSourceDataTo(*pOldObj); // copy source data
1257 ScDPSaveData* pData = pNewObj->GetSaveData();
1258 OSL_ENSURE( pData, "no SaveData from living DPObject" );
1259 if (pData)
1260 pOldObj->SetSaveData(*pData); // copy SaveData
1262 pOldObj->SetAllowMove(bAllowMove);
1263 pOldObj->ReloadGroupTableData();
1264 pOldObj->SyncAllDimensionMembers();
1265 pOldObj->InvalidateData(); // before getting the new output area
1267 // make sure the table has a name (not set by dialog)
1268 if (pOldObj->GetName().isEmpty())
1269 pOldObj->SetName( rDoc.GetDPCollection()->CreateNewName() );
1271 ScRange aNewOut;
1272 if (!checkNewOutputRange(*pOldObj, rDocShell, aNewOut, bApi))
1274 *pOldObj = aUndoDPObj;
1275 return false;
1278 // test if new output area is empty except for old area
1279 if (!bApi)
1281 // OutRange of pOldObj (pDestObj) is still old area
1282 if (!lcl_EmptyExcept(&rDoc, aNewOut, pOldObj->GetOutRange()))
1284 ScopedVclPtrInstance<QueryBox> aBox( ScDocShell::GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1285 ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY) );
1286 if (aBox->Execute() == RET_NO)
1288 //! like above (not editable)
1289 *pOldObj = aUndoDPObj;
1290 return false;
1295 if (bRecord)
1296 createUndoDoc(pNewUndoDoc, &rDoc, aNewOut);
1298 pOldObj->Output(aNewOut.aStart);
1299 rDocShell.PostPaintGridAll(); //! only necessary parts
1301 if (bRecord)
1303 rDocShell.GetUndoManager()->AddUndoAction(
1304 new ScUndoDataPilot(
1305 &rDocShell, pOldUndoDoc.release(), pNewUndoDoc.release(), &aUndoDPObj, pOldObj, bAllowMove));
1308 // notify API objects
1309 rDoc.BroadcastUno( ScDataPilotModifiedHint(pOldObj->GetName()) );
1310 aModificator.SetDocumentModified();
1312 return true;
1315 bool ScDBDocFunc::RemovePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi)
1317 ScDocShellModificator aModificator(rDocShell);
1318 WaitObject aWait(ScDocShell::GetActiveDialogParent());
1320 if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
1321 return false;
1323 std::unique_ptr<ScDocument> pOldUndoDoc;
1324 std::unique_ptr<ScDPObject> pUndoDPObj;
1326 if (bRecord)
1327 pUndoDPObj.reset(new ScDPObject(rDPObj)); // copy old settings for undo
1329 ScDocument& rDoc = rDocShell.GetDocument();
1330 if (bRecord && !rDoc.IsUndoEnabled())
1331 bRecord = false;
1333 // delete table
1335 ScRange aRange = rDPObj.GetOutRange();
1336 SCTAB nTab = aRange.aStart.Tab();
1338 if (bRecord)
1339 createUndoDoc(pOldUndoDoc, &rDoc, aRange);
1341 rDoc.DeleteAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
1342 aRange.aEnd.Col(), aRange.aEnd.Row(),
1343 nTab, IDF_ALL );
1344 rDoc.RemoveFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
1345 aRange.aEnd.Col(), aRange.aEnd.Row(),
1346 nTab, SC_MF_AUTO );
1348 rDoc.GetDPCollection()->FreeTable(&rDPObj); // object is deleted here
1350 rDocShell.PostPaintGridAll(); //! only necessary parts
1351 rDocShell.PostPaint(aRange, PAINT_GRID);
1353 if (bRecord)
1355 rDocShell.GetUndoManager()->AddUndoAction(
1356 new ScUndoDataPilot(
1357 &rDocShell, pOldUndoDoc.release(), NULL, pUndoDPObj.get(), NULL, false));
1359 // pUndoDPObj is copied
1362 aModificator.SetDocumentModified();
1363 return true;
1366 bool ScDBDocFunc::CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi)
1368 ScDocShellModificator aModificator(rDocShell);
1369 WaitObject aWait(ScDocShell::GetActiveDialogParent());
1371 // At least one cell in the output range should be editable. Check in advance.
1372 if (!isEditable(rDocShell, ScRange(rDPObj.GetOutRange().aStart), bApi))
1373 return false;
1375 std::unique_ptr<ScDocument> pNewUndoDoc;
1377 ScDocument& rDoc = rDocShell.GetDocument();
1378 if (bRecord && !rDoc.IsUndoEnabled())
1379 bRecord = false;
1381 // output range must be set at pNewObj
1382 std::unique_ptr<ScDPObject> pDestObj(new ScDPObject(rDPObj));
1384 ScDPObject& rDestObj = *pDestObj;
1386 // #i94570# When changing the output position in the dialog, a new table is created
1387 // with the settings from the old table, including the name.
1388 // So we have to check for duplicate names here (before inserting).
1389 if (rDoc.GetDPCollection()->GetByName(rDestObj.GetName()))
1390 rDestObj.SetName(OUString()); // ignore the invalid name, create a new name below
1392 if (!rDoc.GetDPCollection()->InsertNewTable(pDestObj.release()))
1393 // Insertion into collection failed.
1394 return false;
1396 rDestObj.ReloadGroupTableData();
1397 rDestObj.SyncAllDimensionMembers();
1398 rDestObj.InvalidateData(); // before getting the new output area
1400 // make sure the table has a name (not set by dialog)
1401 if (rDestObj.GetName().isEmpty())
1402 rDestObj.SetName(rDoc.GetDPCollection()->CreateNewName());
1404 bool bOverflow = false;
1405 ScRange aNewOut = rDestObj.GetNewOutputRange(bOverflow);
1407 if (bOverflow)
1409 if (!bApi)
1410 rDocShell.ErrorMessage(STR_PIVOT_ERROR);
1412 return false;
1416 ScEditableTester aTester(&rDoc, aNewOut);
1417 if (!aTester.IsEditable())
1419 // destination area isn't editable
1420 if (!bApi)
1421 rDocShell.ErrorMessage(aTester.GetMessageId());
1423 return false;
1427 // test if new output area is empty except for old area
1428 if (!bApi)
1430 bool bEmpty = rDoc.IsBlockEmpty(
1431 aNewOut.aStart.Tab(), aNewOut.aStart.Col(), aNewOut.aStart.Row(),
1432 aNewOut.aEnd.Col(), aNewOut.aEnd.Row());
1434 if (!bEmpty)
1436 ScopedVclPtrInstance<QueryBox> aBox(
1437 ScDocShell::GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1438 ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY));
1440 if (aBox->Execute() == RET_NO)
1442 //! like above (not editable)
1443 return false;
1448 if (bRecord)
1449 createUndoDoc(pNewUndoDoc, &rDoc, aNewOut);
1451 rDestObj.Output(aNewOut.aStart);
1452 rDocShell.PostPaintGridAll(); //! only necessary parts
1454 if (bRecord)
1456 rDocShell.GetUndoManager()->AddUndoAction(
1457 new ScUndoDataPilot(&rDocShell, NULL, pNewUndoDoc.release(), NULL, &rDestObj, false));
1460 // notify API objects
1461 rDoc.BroadcastUno(ScDataPilotModifiedHint(rDestObj.GetName()));
1462 aModificator.SetDocumentModified();
1464 return true;
1467 bool ScDBDocFunc::UpdatePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi)
1469 ScDocShellModificator aModificator( rDocShell );
1470 WaitObject aWait( ScDocShell::GetActiveDialogParent() );
1472 if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
1473 return false;
1475 std::unique_ptr<ScDocument> pOldUndoDoc;
1476 std::unique_ptr<ScDocument> pNewUndoDoc;
1478 ScDPObject aUndoDPObj(rDPObj); // For undo or revert on failure.
1480 ScDocument& rDoc = rDocShell.GetDocument();
1481 if (bRecord && !rDoc.IsUndoEnabled())
1482 bRecord = false;
1484 if (bRecord)
1485 createUndoDoc(pOldUndoDoc, &rDoc, rDPObj.GetOutRange());
1487 rDPObj.SetAllowMove(false);
1488 rDPObj.ReloadGroupTableData();
1489 if (!rDPObj.SyncAllDimensionMembers())
1490 return false;
1492 rDPObj.InvalidateData(); // before getting the new output area
1494 // make sure the table has a name (not set by dialog)
1495 if (rDPObj.GetName().isEmpty())
1496 rDPObj.SetName( rDoc.GetDPCollection()->CreateNewName() );
1498 ScRange aNewOut;
1499 if (!checkNewOutputRange(rDPObj, rDocShell, aNewOut, bApi))
1501 rDPObj = aUndoDPObj;
1502 return false;
1505 // test if new output area is empty except for old area
1506 if (!bApi)
1508 if (!lcl_EmptyExcept(&rDoc, aNewOut, rDPObj.GetOutRange()))
1510 ScopedVclPtrInstance<QueryBox> aBox( ScDocShell::GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1511 ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY) );
1512 if (aBox->Execute() == RET_NO)
1514 rDPObj = aUndoDPObj;
1515 return false;
1520 if (bRecord)
1521 createUndoDoc(pNewUndoDoc, &rDoc, aNewOut);
1523 rDPObj.Output(aNewOut.aStart);
1524 rDocShell.PostPaintGridAll(); //! only necessary parts
1526 if (bRecord)
1528 rDocShell.GetUndoManager()->AddUndoAction(
1529 new ScUndoDataPilot(
1530 &rDocShell, pOldUndoDoc.release(), pNewUndoDoc.release(), &aUndoDPObj, &rDPObj, false));
1533 // notify API objects
1534 rDoc.BroadcastUno( ScDataPilotModifiedHint(rDPObj.GetName()) );
1535 aModificator.SetDocumentModified();
1536 return true;
1539 sal_uLong ScDBDocFunc::RefreshPivotTables(ScDPObject* pDPObj, bool bApi)
1541 ScDPCollection* pDPs = rDocShell.GetDocument().GetDPCollection();
1542 if (!pDPs)
1543 return 0;
1545 std::set<ScDPObject*> aRefs;
1546 sal_uLong nErrId = pDPs->ReloadCache(pDPObj, aRefs);
1547 if (nErrId)
1548 return nErrId;
1550 std::set<ScDPObject*>::iterator it = aRefs.begin(), itEnd = aRefs.end();
1551 for (; it != itEnd; ++it)
1553 ScDPObject* pObj = *it;
1555 // This action is intentionally not undoable since it modifies cache.
1556 UpdatePivotTable(*pObj, false, bApi);
1559 return 0;
1562 void ScDBDocFunc::RefreshPivotTableGroups(ScDPObject* pDPObj)
1564 if (!pDPObj)
1565 return;
1567 ScDPCollection* pDPs = rDocShell.GetDocument().GetDPCollection();
1568 if (!pDPs)
1569 return;
1571 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1572 if (!pSaveData)
1573 return;
1575 std::set<ScDPObject*> aRefs;
1576 if (!pDPs->ReloadGroupsInCache(pDPObj, aRefs))
1577 return;
1579 // We allow pDimData being NULL.
1580 const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData();
1581 std::set<ScDPObject*>::iterator it = aRefs.begin(), itEnd = aRefs.end();
1582 for (; it != itEnd; ++it)
1584 ScDPObject* pObj = *it;
1585 if (pObj != pDPObj)
1587 pSaveData = pObj->GetSaveData();
1588 if (pSaveData)
1589 pSaveData->SetDimensionData(pDimData);
1592 // This action is intentionally not undoable since it modifies cache.
1593 UpdatePivotTable(*pObj, false, false);
1597 // database import
1599 void ScDBDocFunc::UpdateImport( const OUString& rTarget, const svx::ODataAccessDescriptor& rDescriptor )
1601 // rTarget is the name of a database range
1603 ScDocument& rDoc = rDocShell.GetDocument();
1604 ScDBCollection& rDBColl = *rDoc.GetDBCollection();
1605 const ScDBData* pData = rDBColl.getNamedDBs().findByUpperName(ScGlobal::pCharClass->uppercase(rTarget));
1606 if (!pData)
1608 ScopedVclPtrInstance<InfoBox> aInfoBox( ScDocShell::GetActiveDialogParent(),
1609 ScGlobal::GetRscString( STR_TARGETNOTFOUND ) );
1610 aInfoBox->Execute();
1611 return;
1614 SCTAB nTab;
1615 SCCOL nDummyCol;
1616 SCROW nDummyRow;
1617 pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow );
1619 ScImportParam aImportParam;
1620 pData->GetImportParam( aImportParam );
1622 OUString sDBName;
1623 OUString sDBTable;
1624 sal_Int32 nCommandType = 0;
1625 sDBName = rDescriptor.getDataSource();
1626 rDescriptor[svx::daCommand] >>= sDBTable;
1627 rDescriptor[svx::daCommandType] >>= nCommandType;
1629 aImportParam.aDBName = sDBName;
1630 aImportParam.bSql = ( nCommandType == sdb::CommandType::COMMAND );
1631 aImportParam.aStatement = sDBTable;
1632 aImportParam.bNative = false;
1633 aImportParam.nType = static_cast<sal_uInt8>( ( nCommandType == sdb::CommandType::QUERY ) ? ScDbQuery : ScDbTable );
1634 aImportParam.bImport = true;
1636 bool bContinue = DoImport( nTab, aImportParam, &rDescriptor, true );
1638 // DB-Operationen wiederholen
1640 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
1641 if (pViewSh)
1643 ScRange aRange;
1644 pData->GetArea(aRange);
1645 pViewSh->MarkRange(aRange); // selektieren
1647 if ( bContinue ) // Fehler beim Import -> Abbruch
1649 // interne Operationen, wenn welche gespeichert
1651 if ( pData->HasQueryParam() || pData->HasSortParam() || pData->HasSubTotalParam() )
1652 pViewSh->RepeatDB();
1654 // Pivottabellen die den Bereich als Quelldaten haben
1656 rDocShell.RefreshPivotTables(aRange);
1661 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */