Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / ui / docshell / dbdocfun.cxx
blob13f42cabf30410d95de8c59fb8c4aa82292ed006
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"
51 #include <set>
52 #include <memory>
54 using namespace ::com::sun::star;
56 // -----------------------------------------------------------------
58 bool ScDBDocFunc::AddDBRange( const OUString& rName, const ScRange& rRange, sal_Bool /* bApi */ )
61 ScDocShellModificator aModificator( rDocShell );
63 ScDocument* pDoc = rDocShell.GetDocument();
64 ScDBCollection* pDocColl = pDoc->GetDBCollection();
65 sal_Bool bUndo (pDoc->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 = !pDoc->IsImportingXML();
79 bool bOk;
80 if ( bCompile )
81 pDoc->CompileDBFormula( sal_True ); // CreateFormulaString
82 if ( rName == STR_DB_LOCAL_NONAME )
84 pDoc->SetAnonymousDBData(rRange.aStart.Tab() , pNew);
85 bOk = true;
87 else
89 bOk = pDocColl->getNamedDBs().insert(pNew);
91 if ( bCompile )
92 pDoc->CompileDBFormula( false ); // CompileFormulaString
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 SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
110 return true;
113 bool ScDBDocFunc::DeleteDBRange(const OUString& rName)
115 bool bDone = false;
116 ScDocument* pDoc = rDocShell.GetDocument();
117 ScDBCollection* pDocColl = pDoc->GetDBCollection();
118 bool bUndo = pDoc->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 pDoc->CompileDBFormula( true ); // CreateFormulaString
131 rDBs.erase(*p);
132 pDoc->CompileDBFormula( false ); // CompileFormulaString
134 if (bUndo)
136 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
137 rDocShell.GetUndoManager()->AddUndoAction(
138 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
141 aModificator.SetDocumentModified();
142 SFX_APP()->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* pDoc = rDocShell.GetDocument();
153 ScDBCollection* pDocColl = pDoc->GetDBCollection();
154 bool bUndo = pDoc->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 pDoc->CompileDBFormula(true); // CreateFormulaString
167 rDBs.erase(*pOld);
168 bool bInserted = rDBs.insert(pNewData);
169 if (!bInserted) // Fehler -> alten Zustand wiederherstellen
170 pDoc->SetDBCollection(pUndoColl); // gehoert dann dem Dokument
172 pDoc->CompileDBFormula( false ); // CompileFormulaString
174 if (bInserted) // Einfuegen hat geklappt
176 if (bUndo)
178 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
179 rDocShell.GetUndoManager()->AddUndoAction(
180 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
182 else
183 delete pUndoColl;
185 aModificator.SetDocumentModified();
186 SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
187 bDone = true;
191 return bDone;
194 bool ScDBDocFunc::ModifyDBData( const ScDBData& rNewData )
196 bool bDone = false;
197 ScDocument* pDoc = rDocShell.GetDocument();
198 ScDBCollection* pDocColl = pDoc->GetDBCollection();
199 bool bUndo = pDoc->IsUndoEnabled();
201 ScDBData* pData = NULL;
202 if (rNewData.GetName() == STR_DB_LOCAL_NONAME)
204 ScRange aRange;
205 rNewData.GetArea(aRange);
206 SCTAB nTab = aRange.aStart.Tab();
207 pData = pDoc->GetAnonymousDBData(nTab);
209 else
210 pData = pDocColl->getNamedDBs().findByUpperName(rNewData.GetUpperName());
212 if (pData)
214 ScDocShellModificator aModificator( rDocShell );
215 ScRange aOldRange, aNewRange;
216 pData->GetArea(aOldRange);
217 rNewData.GetArea(aNewRange);
218 bool bAreaChanged = ( aOldRange != aNewRange ); // dann muss neu compiliert werden
220 ScDBCollection* pUndoColl = NULL;
221 if (bUndo)
222 pUndoColl = new ScDBCollection( *pDocColl );
224 *pData = rNewData;
225 if (bAreaChanged)
226 pDoc->CompileDBFormula();
228 if (bUndo)
230 ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
231 rDocShell.GetUndoManager()->AddUndoAction(
232 new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
235 aModificator.SetDocumentModified();
236 bDone = true;
239 return bDone;
242 // -----------------------------------------------------------------
244 bool ScDBDocFunc::RepeatDB( const OUString& rDBName, bool bRecord, bool bApi, bool bIsUnnamed, SCTAB aTab )
246 //! auch fuer ScDBFunc::RepeatDB benutzen!
248 bool bDone = false;
249 ScDocument* pDoc = rDocShell.GetDocument();
250 if (bRecord && !pDoc->IsUndoEnabled())
251 bRecord = false;
252 ScDBData* pDBData = NULL;
253 if (bIsUnnamed)
255 pDBData = pDoc->GetAnonymousDBData( aTab );
257 else
259 ScDBCollection* pColl = pDoc->GetDBCollection();
260 if (pColl)
261 pDBData = pColl->getNamedDBs().findByUpperName(ScGlobal::pCharClass->uppercase(rDBName));
264 if ( pDBData )
266 ScQueryParam aQueryParam;
267 pDBData->GetQueryParam( aQueryParam );
268 sal_Bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
270 ScSortParam aSortParam;
271 pDBData->GetSortParam( aSortParam );
272 sal_Bool bSort = aSortParam.maKeyState[0].bDoSort;
274 ScSubTotalParam aSubTotalParam;
275 pDBData->GetSubTotalParam( aSubTotalParam );
276 sal_Bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
278 if ( bQuery || bSort || bSubTotal )
280 sal_Bool bQuerySize = false;
281 ScRange aOldQuery;
282 ScRange aNewQuery;
283 if (bQuery && !aQueryParam.bInplace)
285 ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
286 aQueryParam.nDestTab, sal_True );
287 if (pDest && pDest->IsDoSize())
289 pDest->GetArea( aOldQuery );
290 bQuerySize = sal_True;
294 SCTAB nTab;
295 SCCOL nStartCol;
296 SCROW nStartRow;
297 SCCOL nEndCol;
298 SCROW nEndRow;
299 pDBData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
301 //! Undo nur benoetigte Daten ?
303 ScDocument* pUndoDoc = NULL;
304 ScOutlineTable* pUndoTab = NULL;
305 ScRangeName* pUndoRange = NULL;
306 ScDBCollection* pUndoDB = NULL;
308 if (bRecord)
310 SCTAB nTabCount = pDoc->GetTableCount();
311 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
312 ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
313 if (pTable)
315 pUndoTab = new ScOutlineTable( *pTable );
317 // column/row state
318 SCCOLROW nOutStartCol, nOutEndCol;
319 SCCOLROW nOutStartRow, nOutEndRow;
320 pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
321 pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
323 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
324 pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0,
325 nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab,
326 IDF_NONE, false, pUndoDoc );
327 pDoc->CopyToDocument( 0, static_cast<SCROW>(nOutStartRow),
328 nTab, MAXCOL, static_cast<SCROW>(nOutEndRow), nTab,
329 IDF_NONE, false, pUndoDoc );
331 else
332 pUndoDoc->InitUndo( pDoc, nTab, nTab, false, sal_True );
334 // Datenbereich sichern - incl. Filter-Ergebnis
335 pDoc->CopyToDocument( 0,nStartRow,nTab, MAXCOL,nEndRow,nTab, IDF_ALL, false, pUndoDoc );
337 // alle Formeln wegen Referenzen
338 pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, IDF_FORMULA, false, pUndoDoc );
340 // DB- und andere Bereiche
341 ScRangeName* pDocRange = pDoc->GetRangeName();
342 if (!pDocRange->empty())
343 pUndoRange = new ScRangeName( *pDocRange );
344 ScDBCollection* pDocDB = pDoc->GetDBCollection();
345 if (!pDocDB->empty())
346 pUndoDB = new ScDBCollection( *pDocDB );
349 if (bSort && bSubTotal)
351 // Sortieren ohne SubTotals
353 aSubTotalParam.bRemoveOnly = sal_True; // wird unten wieder zurueckgesetzt
354 DoSubTotals( nTab, aSubTotalParam, NULL, false, bApi );
357 if (bSort)
359 pDBData->GetSortParam( aSortParam ); // Bereich kann sich geaendert haben
360 Sort( nTab, aSortParam, false, false, bApi );
362 if (bQuery)
364 pDBData->GetQueryParam( aQueryParam ); // Bereich kann sich geaendert haben
365 ScRange aAdvSource;
366 if (pDBData->GetAdvancedQuerySource(aAdvSource))
367 Query( nTab, aQueryParam, &aAdvSource, false, bApi );
368 else
369 Query( nTab, aQueryParam, NULL, false, bApi );
371 // bei nicht-inplace kann die Tabelle umgestellt worden sein
372 // if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
373 // SetTabNo( nTab );
375 if (bSubTotal)
377 pDBData->GetSubTotalParam( aSubTotalParam ); // Bereich kann sich geaendert haben
378 aSubTotalParam.bRemoveOnly = false;
379 DoSubTotals( nTab, aSubTotalParam, NULL, false, bApi );
382 if (bRecord)
384 SCTAB nDummyTab;
385 SCCOL nDummyCol;
386 SCROW nDummyRow;
387 SCROW nNewEndRow;
388 pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
390 const ScRange* pOld = NULL;
391 const ScRange* pNew = NULL;
392 if (bQuerySize)
394 ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
395 aQueryParam.nDestTab, sal_True );
396 if (pDest)
398 pDest->GetArea( aNewQuery );
399 pOld = &aOldQuery;
400 pNew = &aNewQuery;
404 rDocShell.GetUndoManager()->AddUndoAction(
405 new ScUndoRepeatDB( &rDocShell, nTab,
406 nStartCol, nStartRow, nEndCol, nEndRow,
407 nNewEndRow,
408 //nCurX, nCurY,
409 nStartCol, nStartRow,
410 pUndoDoc, pUndoTab,
411 pUndoRange, pUndoDB,
412 pOld, pNew ) );
415 rDocShell.PostPaint(ScRange(0, 0, nTab, MAXCOL, MAXROW, nTab),
416 PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE);
417 bDone = sal_True;
419 else if (!bApi) // "Keine Operationen auszufuehren"
420 rDocShell.ErrorMessage(STR_MSSG_REPEATDB_0);
423 return bDone;
426 // -----------------------------------------------------------------
428 sal_Bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
429 sal_Bool bRecord, sal_Bool bPaint, sal_Bool bApi )
431 ScDocShellModificator aModificator( rDocShell );
433 ScDocument* pDoc = rDocShell.GetDocument();
434 if (bRecord && !pDoc->IsUndoEnabled())
435 bRecord = false;
436 SCTAB nSrcTab = nTab;
437 ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
439 ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
440 rSortParam.nCol2, rSortParam.nRow2 );
441 if (!pDBData)
443 OSL_FAIL( "Sort: keine DBData" );
444 return false;
447 ScDBData* pDestData = NULL;
448 ScRange aOldDest;
449 sal_Bool bCopy = !rSortParam.bInplace;
450 if ( bCopy && rSortParam.nDestCol == rSortParam.nCol1 &&
451 rSortParam.nDestRow == rSortParam.nRow1 && rSortParam.nDestTab == nTab )
452 bCopy = false;
453 ScSortParam aLocalParam( rSortParam );
454 if ( bCopy )
456 aLocalParam.MoveToDest();
457 if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
459 if (!bApi)
460 rDocShell.ErrorMessage(STR_PASTE_FULL);
461 return false;
464 nTab = rSortParam.nDestTab;
465 pDestData = pDoc->GetDBAtCursor( rSortParam.nDestCol, rSortParam.nDestRow,
466 rSortParam.nDestTab, sal_True );
467 if (pDestData)
468 pDestData->GetArea(aOldDest);
471 ScEditableTester aTester( pDoc, nTab, aLocalParam.nCol1,aLocalParam.nRow1,
472 aLocalParam.nCol2,aLocalParam.nRow2 );
473 if (!aTester.IsEditable())
475 if (!bApi)
476 rDocShell.ErrorMessage(aTester.GetMessageId());
477 return false;
480 if ( aLocalParam.bIncludePattern && pDoc->HasAttrib(
481 aLocalParam.nCol1, aLocalParam.nRow1, nTab,
482 aLocalParam.nCol2, aLocalParam.nRow2, nTab,
483 HASATTR_MERGED | HASATTR_OVERLAPPED ) )
485 // Merge-Attribute wuerden beim Sortieren durcheinanderkommen
486 if (!bApi)
487 rDocShell.ErrorMessage(STR_SORT_ERR_MERGED);
488 return false;
492 // ausfuehren
494 WaitObject aWait( rDocShell.GetActiveDialogParent() );
496 sal_Bool bRepeatQuery = false; // bestehenden Filter wiederholen?
497 ScQueryParam aQueryParam;
498 pDBData->GetQueryParam( aQueryParam );
499 if ( aQueryParam.GetEntry(0).bDoQuery )
500 bRepeatQuery = sal_True;
502 if (bRepeatQuery && bCopy)
504 if ( aQueryParam.bInplace ||
505 aQueryParam.nDestCol != rSortParam.nDestCol ||
506 aQueryParam.nDestRow != rSortParam.nDestRow ||
507 aQueryParam.nDestTab != rSortParam.nDestTab ) // Query auf selben Zielbereich?
508 bRepeatQuery = false;
511 ScUndoSort* pUndoAction = 0;
512 if ( bRecord )
514 // Referenzen ausserhalb des Bereichs werden nicht veraendert !
516 ScDocument* pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
517 // Zeilenhoehen immer (wegen automatischer Anpassung)
518 //! auf ScBlockUndo umstellen
519 pUndoDoc->InitUndo( pDoc, nTab, nTab, false, sal_True );
521 /* #i59745# Do not copy note captions to undo document. All existing
522 caption objects will be repositioned while sorting which is tracked
523 in drawing undo. When undo is executed, the old positions will be
524 restored, and the cells with the old notes (which still refer to the
525 existing captions) will be copied back into the source document. */
526 pDoc->CopyToDocument( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
527 aLocalParam.nCol2, aLocalParam.nRow2, nTab,
528 IDF_ALL|IDF_NOCAPTIONS, false, pUndoDoc );
530 const ScRange* pR = 0;
531 if (pDestData)
533 /* #i59745# Do not copy note captions from destination range to
534 undo document. All existing caption objects will be removed
535 which is tracked in drawing undo. When undo is executed, the
536 caption objects are reinserted with drawing undo, and the cells
537 with the old notes (which still refer to the existing captions)
538 will be copied back into the source document. */
539 pDoc->CopyToDocument( aOldDest, IDF_ALL|IDF_NOCAPTIONS, false, pUndoDoc );
540 pR = &aOldDest;
543 // Zeilenhoehen immer (wegen automatischer Anpassung)
544 //! auf ScBlockUndo umstellen
545 // if (bRepeatQuery)
546 pDoc->CopyToDocument( 0, aLocalParam.nRow1, nTab, MAXCOL, aLocalParam.nRow2, nTab,
547 IDF_NONE, false, pUndoDoc );
549 ScDBCollection* pUndoDB = NULL;
550 ScDBCollection* pDocDB = pDoc->GetDBCollection();
551 if (!pDocDB->empty())
552 pUndoDB = new ScDBCollection( *pDocDB );
554 pUndoAction = new ScUndoSort( &rDocShell, nTab, rSortParam, pUndoDoc, pUndoDB, pR );
555 rDocShell.GetUndoManager()->AddUndoAction( pUndoAction );
557 // #i59745# collect all drawing undo actions affecting cell note captions
558 if( pDrawLayer )
559 pDrawLayer->BeginCalcUndo(false);
562 if ( bCopy )
564 if (pDestData)
565 pDoc->DeleteAreaTab(aOldDest, IDF_CONTENTS); // Zielbereich vorher loeschen
567 ScRange aSource( rSortParam.nCol1,rSortParam.nRow1,nSrcTab,
568 rSortParam.nCol2,rSortParam.nRow2,nSrcTab );
569 ScAddress aDest( rSortParam.nDestCol, rSortParam.nDestRow, rSortParam.nDestTab );
571 rDocShell.GetDocFunc().MoveBlock( aSource, aDest, false, false, false, sal_True );
574 // don't call ScDocument::Sort with an empty SortParam (may be empty here if bCopy is set)
575 if (aLocalParam.GetSortKeyCount() && aLocalParam.maKeyState[0].bDoSort)
577 ScProgress aProgress(&rDocShell, ScGlobal::GetRscString(STR_PROGRESS_SORTING), 0);
578 pDoc->Sort( nTab, aLocalParam, bRepeatQuery, &aProgress );
581 sal_Bool bSave = sal_True;
582 if (bCopy)
584 ScSortParam aOldSortParam;
585 pDBData->GetSortParam( aOldSortParam );
586 if (aOldSortParam.GetSortKeyCount() &&
587 aOldSortParam.maKeyState[0].bDoSort && aOldSortParam.bInplace)
589 bSave = false;
590 aOldSortParam.nDestCol = rSortParam.nDestCol;
591 aOldSortParam.nDestRow = rSortParam.nDestRow;
592 aOldSortParam.nDestTab = rSortParam.nDestTab;
593 pDBData->SetSortParam( aOldSortParam ); // dann nur DestPos merken
596 if (bSave) // Parameter merken
598 pDBData->SetSortParam( rSortParam );
599 pDBData->SetHeader( rSortParam.bHasHeader ); //! ???
600 pDBData->SetByRow( rSortParam.bByRow ); //! ???
603 if (bCopy) // neuen DB-Bereich merken
605 // Tabelle umschalten von aussen (View)
606 //! SetCursor ??!?!
608 ScRange aDestPos( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
609 aLocalParam.nCol2, aLocalParam.nRow2, nTab );
610 ScDBData* pNewData;
611 if (pDestData)
612 pNewData = pDestData; // Bereich vorhanden -> anpassen
613 else // Bereich ab Cursor/Markierung wird angelegt
614 pNewData = rDocShell.GetDBData(aDestPos, SC_DB_MAKE, SC_DBSEL_FORCE_MARK );
615 if (pNewData)
617 pNewData->SetArea( nTab,
618 aLocalParam.nCol1,aLocalParam.nRow1,
619 aLocalParam.nCol2,aLocalParam.nRow2 );
620 pNewData->SetSortParam( aLocalParam );
621 pNewData->SetHeader( aLocalParam.bHasHeader ); //! ???
622 pNewData->SetByRow( aLocalParam.bByRow );
624 else
626 OSL_FAIL("Zielbereich nicht da");
630 ScRange aDirtyRange( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
631 aLocalParam.nCol2, aLocalParam.nRow2, nTab );
632 pDoc->SetDirty( aDirtyRange );
634 if (bPaint)
636 sal_uInt16 nPaint = PAINT_GRID;
637 SCCOL nStartX = aLocalParam.nCol1;
638 SCROW nStartY = aLocalParam.nRow1;
639 SCCOL nEndX = aLocalParam.nCol2;
640 SCROW nEndY = aLocalParam.nRow2;
641 if ( bRepeatQuery )
643 nPaint |= PAINT_LEFT;
644 nStartX = 0;
645 nEndX = MAXCOL;
647 if (pDestData)
649 if ( nEndX < aOldDest.aEnd.Col() )
650 nEndX = aOldDest.aEnd.Col();
651 if ( nEndY < aOldDest.aEnd.Row() )
652 nEndY = aOldDest.aEnd.Row();
654 rDocShell.PostPaint(ScRange(nStartX, nStartY, nTab, nEndX, nEndY, nTab), nPaint);
657 // AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, bPaint );
658 rDocShell.AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, nTab );
660 // #i59745# set collected drawing undo actions at sorting undo action
661 if( pUndoAction && pDrawLayer )
662 pUndoAction->SetDrawUndoAction( pDrawLayer->GetCalcUndo() );
664 aModificator.SetDocumentModified();
666 return sal_True;
669 // -----------------------------------------------------------------
671 sal_Bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam,
672 const ScRange* pAdvSource, sal_Bool bRecord, sal_Bool bApi )
674 ScDocShellModificator aModificator( rDocShell );
676 ScDocument* pDoc = rDocShell.GetDocument();
677 if (bRecord && !pDoc->IsUndoEnabled())
678 bRecord = false;
679 ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rQueryParam.nCol1, rQueryParam.nRow1,
680 rQueryParam.nCol2, rQueryParam.nRow2 );
681 if (!pDBData)
683 OSL_FAIL( "Query: keine DBData" );
684 return false;
687 // Wechsel von Inplace auf nicht-Inplace, dann erst Inplace aufheben:
688 // (nur, wenn im Dialog "Persistent" ausgewaehlt ist)
690 if ( !rQueryParam.bInplace && pDBData->HasQueryParam() && rQueryParam.bDestPers )
692 ScQueryParam aOldQuery;
693 pDBData->GetQueryParam(aOldQuery);
694 if (aOldQuery.bInplace)
696 // alte Filterung aufheben
698 SCSIZE nEC = aOldQuery.GetEntryCount();
699 for (SCSIZE i=0; i<nEC; i++)
700 aOldQuery.GetEntry(i).bDoQuery = false;
701 aOldQuery.bDuplicate = sal_True;
702 Query( nTab, aOldQuery, NULL, bRecord, bApi );
706 ScQueryParam aLocalParam( rQueryParam ); // fuer Paint / Zielbereich
707 sal_Bool bCopy = !rQueryParam.bInplace; // kopiert wird in Table::Query
708 ScDBData* pDestData = NULL; // Bereich, in den kopiert wird
709 sal_Bool bDoSize = false; // Zielgroesse anpassen (einf./loeschen)
710 SCCOL nFormulaCols = 0; // nur bei bDoSize
711 sal_Bool bKeepFmt = false;
712 ScRange aOldDest;
713 ScRange aDestTotal;
714 if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 &&
715 rQueryParam.nDestRow == rQueryParam.nRow1 && rQueryParam.nDestTab == nTab )
716 bCopy = false;
717 SCTAB nDestTab = nTab;
718 if ( bCopy )
720 aLocalParam.MoveToDest();
721 nDestTab = rQueryParam.nDestTab;
722 if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
724 if (!bApi)
725 rDocShell.ErrorMessage(STR_PASTE_FULL);
726 return false;
729 ScEditableTester aTester( pDoc, nDestTab, aLocalParam.nCol1,aLocalParam.nRow1,
730 aLocalParam.nCol2,aLocalParam.nRow2);
731 if (!aTester.IsEditable())
733 if (!bApi)
734 rDocShell.ErrorMessage(aTester.GetMessageId());
735 return false;
738 pDestData = pDoc->GetDBAtCursor( rQueryParam.nDestCol, rQueryParam.nDestRow,
739 rQueryParam.nDestTab, sal_True );
740 if (pDestData)
742 pDestData->GetArea( aOldDest );
743 aDestTotal=ScRange( rQueryParam.nDestCol,
744 rQueryParam.nDestRow,
745 nDestTab,
746 rQueryParam.nDestCol + rQueryParam.nCol2 - rQueryParam.nCol1,
747 rQueryParam.nDestRow + rQueryParam.nRow2 - rQueryParam.nRow1,
748 nDestTab );
750 bDoSize = pDestData->IsDoSize();
751 // Test, ob Formeln aufgefuellt werden muessen (nFormulaCols):
752 if ( bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col() )
754 SCCOL nTestCol = aOldDest.aEnd.Col() + 1; // neben dem Bereich
755 SCROW nTestRow = rQueryParam.nDestRow +
756 ( aLocalParam.bHasHeader ? 1 : 0 );
757 while ( nTestCol <= MAXCOL &&
758 pDoc->GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA )
759 ++nTestCol, ++nFormulaCols;
762 bKeepFmt = pDestData->IsKeepFmt();
763 if ( bDoSize && !pDoc->CanFitBlock( aOldDest, aDestTotal ) )
765 if (!bApi)
766 rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2); // kann keine Zeilen einfuegen
767 return false;
772 // ausfuehren
774 WaitObject aWait( rDocShell.GetActiveDialogParent() );
776 sal_Bool bKeepSub = false; // bestehende Teilergebnisse wiederholen?
777 ScSubTotalParam aSubTotalParam;
778 if (rQueryParam.GetEntry(0).bDoQuery) // nicht beim Aufheben
780 pDBData->GetSubTotalParam( aSubTotalParam ); // Teilergebnisse vorhanden?
782 if ( aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly )
783 bKeepSub = sal_True;
786 ScDocument* pUndoDoc = NULL;
787 ScDBCollection* pUndoDB = NULL;
788 const ScRange* pOld = NULL;
790 if ( bRecord )
792 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
793 if (bCopy)
795 pUndoDoc->InitUndo( pDoc, nDestTab, nDestTab, false, sal_True );
796 pDoc->CopyToDocument( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
797 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab,
798 IDF_ALL, false, pUndoDoc );
799 // Attribute sichern, falls beim Filtern mitkopiert
801 if (pDestData)
803 pDoc->CopyToDocument( aOldDest, IDF_ALL, false, pUndoDoc );
804 pOld = &aOldDest;
807 else
809 pUndoDoc->InitUndo( pDoc, nTab, nTab, false, sal_True );
810 pDoc->CopyToDocument( 0, rQueryParam.nRow1, nTab, MAXCOL, rQueryParam.nRow2, nTab,
811 IDF_NONE, false, pUndoDoc );
814 ScDBCollection* pDocDB = pDoc->GetDBCollection();
815 if (!pDocDB->empty())
816 pUndoDB = new ScDBCollection( *pDocDB );
818 pDoc->BeginDrawUndo();
821 ScDocument* pAttribDoc = NULL;
822 ScRange aAttribRange;
823 if (pDestData) // Zielbereich loeschen
825 if ( bKeepFmt )
827 // kleinere der End-Spalten, Header+1 Zeile
828 aAttribRange = aOldDest;
829 if ( aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col() )
830 aAttribRange.aEnd.SetCol( aDestTotal.aEnd.Col() );
831 aAttribRange.aEnd.SetRow( aAttribRange.aStart.Row() +
832 ( aLocalParam.bHasHeader ? 1 : 0 ) );
834 // auch fuer aufgefuellte Formeln
835 aAttribRange.aEnd.SetCol( aAttribRange.aEnd.Col() + nFormulaCols );
837 pAttribDoc = new ScDocument( SCDOCMODE_UNDO );
838 pAttribDoc->InitUndo( pDoc, nDestTab, nDestTab, false, sal_True );
839 pDoc->CopyToDocument( aAttribRange, IDF_ATTRIB, false, pAttribDoc );
842 if ( bDoSize )
843 pDoc->FitBlock( aOldDest, aDestTotal );
844 else
845 pDoc->DeleteAreaTab(aOldDest, IDF_ALL); // einfach loeschen
848 // Filtern am Dokument ausfuehren
849 SCSIZE nCount = pDoc->Query( nTab, rQueryParam, bKeepSub );
850 if (bCopy)
852 aLocalParam.nRow2 = aLocalParam.nRow1 + nCount;
853 if (!aLocalParam.bHasHeader && nCount > 0)
854 --aLocalParam.nRow2;
856 if ( bDoSize )
858 // auf wirklichen Ergebnis-Bereich anpassen
859 // (das hier ist immer eine Verkleinerung)
861 ScRange aNewDest( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
862 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab );
863 pDoc->FitBlock( aDestTotal, aNewDest, false ); // sal_False - nicht loeschen
865 if ( nFormulaCols > 0 )
867 // Formeln ausfuellen
868 //! Undo (Query und Repeat) !!!
870 ScRange aNewForm( aLocalParam.nCol2+1, aLocalParam.nRow1, nDestTab,
871 aLocalParam.nCol2+nFormulaCols, aLocalParam.nRow2, nDestTab );
872 ScRange aOldForm = aNewForm;
873 aOldForm.aEnd.SetRow( aOldDest.aEnd.Row() );
874 pDoc->FitBlock( aOldForm, aNewForm, false );
876 ScMarkData aMark;
877 aMark.SelectOneTable(nDestTab);
878 SCROW nFStartY = aLocalParam.nRow1 + ( aLocalParam.bHasHeader ? 1 : 0 );
880 sal_uLong nProgCount = nFormulaCols;
881 nProgCount *= aLocalParam.nRow2 - nFStartY;
882 ScProgress aProgress( pDoc->GetDocumentShell(),
883 ScGlobal::GetRscString(STR_FILL_SERIES_PROGRESS), nProgCount );
885 pDoc->Fill( aLocalParam.nCol2+1, nFStartY,
886 aLocalParam.nCol2+nFormulaCols, nFStartY, &aProgress, aMark,
887 aLocalParam.nRow2 - nFStartY,
888 FILL_TO_BOTTOM, FILL_SIMPLE );
892 if ( pAttribDoc ) // gemerkte Attribute zurueckkopieren
894 // Header
895 if (aLocalParam.bHasHeader)
897 ScRange aHdrRange = aAttribRange;
898 aHdrRange.aEnd.SetRow( aHdrRange.aStart.Row() );
899 pAttribDoc->CopyToDocument( aHdrRange, IDF_ATTRIB, false, pDoc );
902 // Daten
903 SCCOL nAttrEndCol = aAttribRange.aEnd.Col();
904 SCROW nAttrRow = aAttribRange.aStart.Row() + ( aLocalParam.bHasHeader ? 1 : 0 );
905 for (SCCOL nCol = aAttribRange.aStart.Col(); nCol<=nAttrEndCol; nCol++)
907 const ScPatternAttr* pSrcPattern = pAttribDoc->GetPattern(
908 nCol, nAttrRow, nDestTab );
909 OSL_ENSURE(pSrcPattern,"Pattern ist 0");
910 if (pSrcPattern)
912 pDoc->ApplyPatternAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
913 nDestTab, *pSrcPattern );
914 const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
915 if (pStyle)
916 pDoc->ApplyStyleAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
917 nDestTab, *pStyle );
921 delete pAttribDoc;
925 // speichern: Inplace immer, sonst je nach Einstellung
926 // alter Inplace-Filter ist ggf. schon aufgehoben
928 sal_Bool bSave = rQueryParam.bInplace || rQueryParam.bDestPers;
929 if (bSave) // merken
931 pDBData->SetQueryParam( rQueryParam );
932 pDBData->SetHeader( rQueryParam.bHasHeader ); //! ???
933 pDBData->SetAdvancedQuerySource( pAdvSource ); // after SetQueryParam
936 if (bCopy) // neuen DB-Bereich merken
938 // selektieren wird hinterher von aussen (dbfunc)
939 // momentan ueber DB-Bereich an der Zielposition, darum muss dort
940 // auf jeden Fall ein Bereich angelegt werden.
942 ScDBData* pNewData;
943 if (pDestData)
944 pNewData = pDestData; // Bereich vorhanden -> anpassen (immer!)
945 else // Bereich anlegen
946 pNewData = rDocShell.GetDBData(
947 ScRange( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
948 aLocalParam.nCol2, aLocalParam.nRow2, nDestTab ),
949 SC_DB_MAKE, SC_DBSEL_FORCE_MARK );
951 if (pNewData)
953 pNewData->SetArea( nDestTab, aLocalParam.nCol1, aLocalParam.nRow1,
954 aLocalParam.nCol2, aLocalParam.nRow2 );
956 // Query-Param wird am Ziel nicht mehr eingestellt, fuehrt nur zu Verwirrung
957 // und Verwechslung mit dem Query-Param am Quellbereich (#37187#)
959 else
961 OSL_FAIL("Zielbereich nicht da");
965 if (!bCopy)
967 pDoc->InvalidatePageBreaks(nTab);
968 pDoc->UpdatePageBreaks( nTab );
971 // #i23299# Subtotal functions depend on cell's filtered states.
972 ScRange aDirtyRange(0 , aLocalParam.nRow1, nDestTab, MAXCOL, aLocalParam.nRow2, nDestTab);
973 pDoc->SetSubTotalCellsDirty(aDirtyRange);
975 if ( bRecord )
977 // create undo action after executing, because of drawing layer undo
978 rDocShell.GetUndoManager()->AddUndoAction(
979 new ScUndoQuery( &rDocShell, nTab, rQueryParam, pUndoDoc, pUndoDB,
980 pOld, bDoSize, pAdvSource ) );
984 if (bCopy)
986 SCCOL nEndX = aLocalParam.nCol2;
987 SCROW nEndY = aLocalParam.nRow2;
988 if (pDestData)
990 if ( aOldDest.aEnd.Col() > nEndX )
991 nEndX = aOldDest.aEnd.Col();
992 if ( aOldDest.aEnd.Row() > nEndY )
993 nEndY = aOldDest.aEnd.Row();
995 if (bDoSize)
996 nEndY = MAXROW;
997 rDocShell.PostPaint(
998 ScRange(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, nEndX, nEndY, nDestTab),
999 PAINT_GRID);
1001 else
1002 rDocShell.PostPaint(
1003 ScRange(0, rQueryParam.nRow1, nTab, MAXCOL, MAXROW, nTab),
1004 PAINT_GRID | PAINT_LEFT);
1005 aModificator.SetDocumentModified();
1007 return sal_True;
1010 // -----------------------------------------------------------------
1012 sal_Bool ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
1013 const ScSortParam* pForceNewSort, sal_Bool bRecord, sal_Bool bApi )
1015 //! auch fuer ScDBFunc::DoSubTotals benutzen!
1016 // dann bleibt aussen:
1017 // - neuen Bereich (aus DBData) markieren
1018 // - SelectionChanged (?)
1020 sal_Bool bDo = !rParam.bRemoveOnly; // sal_False = nur loeschen
1021 sal_Bool bRet = false;
1023 ScDocument* pDoc = rDocShell.GetDocument();
1024 if (bRecord && !pDoc->IsUndoEnabled())
1025 bRecord = false;
1026 ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
1027 rParam.nCol2, rParam.nRow2 );
1028 if (!pDBData)
1030 OSL_FAIL( "SubTotals: keine DBData" );
1031 return false;
1034 ScEditableTester aTester( pDoc, nTab, 0,rParam.nRow1+1, MAXCOL,MAXROW );
1035 if (!aTester.IsEditable())
1037 if (!bApi)
1038 rDocShell.ErrorMessage(aTester.GetMessageId());
1039 return false;
1042 if (pDoc->HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
1043 rParam.nCol2, rParam.nRow2, nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ))
1045 if (!bApi)
1046 rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); // nicht in zusammengefasste einfuegen
1047 return false;
1050 sal_Bool bOk = true;
1051 if (rParam.bReplace)
1052 if (pDoc->TestRemoveSubTotals( nTab, rParam ))
1054 bOk = ( MessBox( rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1055 // "StarCalc" "Daten loeschen?"
1056 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ),
1057 ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_1 ) ).Execute()
1058 == RET_YES );
1061 if (bOk)
1063 WaitObject aWait( rDocShell.GetActiveDialogParent() );
1064 ScDocShellModificator aModificator( rDocShell );
1066 ScSubTotalParam aNewParam( rParam ); // Bereichsende wird veraendert
1067 ScDocument* pUndoDoc = NULL;
1068 ScOutlineTable* pUndoTab = NULL;
1069 ScRangeName* pUndoRange = NULL;
1070 ScDBCollection* pUndoDB = NULL;
1072 if (bRecord) // alte Daten sichern
1074 sal_Bool bOldFilter = bDo && rParam.bDoSort;
1076 SCTAB nTabCount = pDoc->GetTableCount();
1077 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
1078 ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
1079 if (pTable)
1081 pUndoTab = new ScOutlineTable( *pTable );
1083 // column/row state
1084 SCCOLROW nOutStartCol, nOutEndCol;
1085 SCCOLROW nOutStartRow, nOutEndRow;
1086 pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
1087 pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
1089 pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_True, sal_True );
1090 pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, false, pUndoDoc );
1091 pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, false, pUndoDoc );
1093 else
1094 pUndoDoc->InitUndo( pDoc, nTab, nTab, false, bOldFilter );
1096 // Datenbereich sichern - incl. Filter-Ergebnis
1097 pDoc->CopyToDocument( 0,rParam.nRow1+1,nTab, MAXCOL,rParam.nRow2,nTab,
1098 IDF_ALL, false, pUndoDoc );
1100 // alle Formeln wegen Referenzen
1101 pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1,
1102 IDF_FORMULA, false, pUndoDoc );
1104 // DB- und andere Bereiche
1105 ScRangeName* pDocRange = pDoc->GetRangeName();
1106 if (!pDocRange->empty())
1107 pUndoRange = new ScRangeName( *pDocRange );
1108 ScDBCollection* pDocDB = pDoc->GetDBCollection();
1109 if (!pDocDB->empty())
1110 pUndoDB = new ScDBCollection( *pDocDB );
1113 // pDoc->SetOutlineTable( nTab, NULL );
1114 ScOutlineTable* pOut = pDoc->GetOutlineTable( nTab );
1115 if (pOut)
1116 pOut->GetRowArray()->RemoveAll(); // nur Zeilen-Outlines loeschen
1118 if (rParam.bReplace)
1119 pDoc->RemoveSubTotals( nTab, aNewParam );
1120 sal_Bool bSuccess = sal_True;
1121 if (bDo)
1123 // Sortieren
1124 if ( rParam.bDoSort || pForceNewSort )
1126 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1128 // Teilergebnis-Felder vor die Sortierung setzen
1129 // (doppelte werden weggelassen, kann darum auch wieder aufgerufen werden)
1131 ScSortParam aOldSort;
1132 pDBData->GetSortParam( aOldSort );
1133 ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
1134 Sort( nTab, aSortParam, false, false, bApi );
1137 bSuccess = pDoc->DoSubTotals( nTab, aNewParam );
1138 pDoc->SetDrawPageSize(nTab);
1140 ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
1141 aNewParam.nCol2, aNewParam.nRow2, nTab );
1142 pDoc->SetDirty( aDirtyRange );
1144 if (bRecord)
1146 // ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
1147 rDocShell.GetUndoManager()->AddUndoAction(
1148 new ScUndoSubTotals( &rDocShell, nTab,
1149 rParam, aNewParam.nRow2,
1150 pUndoDoc, pUndoTab, // pUndoDBData,
1151 pUndoRange, pUndoDB ) );
1154 if (!bSuccess)
1156 // "Kann keine Zeilen einfuegen"
1157 if (!bApi)
1158 rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
1161 // merken
1162 pDBData->SetSubTotalParam( aNewParam );
1163 pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
1164 pDoc->CompileDBFormula();
1166 rDocShell.PostPaint(ScRange(0, 0, nTab, MAXCOL,MAXROW,nTab),
1167 PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE);
1168 aModificator.SetDocumentModified();
1170 bRet = bSuccess;
1172 return bRet;
1175 namespace {
1177 bool lcl_EmptyExcept( ScDocument* pDoc, const ScRange& rRange, const ScRange& rExcept )
1179 ScCellIterator aIter( pDoc, rRange );
1180 for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
1182 if (!aIter.isEmpty()) // real content?
1184 if (!rExcept.In(aIter.GetPos()))
1185 return false; // cell found
1189 return true; // nothing found - empty
1192 bool isEditable(ScDocShell& rDocShell, const ScRangeList& rRanges, bool bApi)
1194 ScDocument* pDoc = rDocShell.GetDocument();
1195 if (!rDocShell.IsEditable() || pDoc->GetChangeTrack())
1197 // not recorded -> disallow
1198 if (!bApi)
1199 rDocShell.ErrorMessage(STR_PROTECTIONERR);
1201 return false;
1204 for (size_t i = 0, n = rRanges.size(); i < n; ++i)
1206 const ScRange* p = rRanges[i];
1207 ScEditableTester aTester(pDoc, *p);
1208 if (!aTester.IsEditable())
1210 if (!bApi)
1211 rDocShell.ErrorMessage(aTester.GetMessageId());
1213 return false;
1217 return true;
1220 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1221 void createUndoDoc(std::auto_ptr<ScDocument>& pUndoDoc, ScDocument* pDoc, const ScRange& rRange)
1223 SCTAB nTab = rRange.aStart.Tab();
1224 pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
1225 pUndoDoc->InitUndo(pDoc, nTab, nTab);
1226 pDoc->CopyToDocument(rRange, IDF_ALL, false, pUndoDoc.get());
1228 SAL_WNODEPRECATED_DECLARATIONS_POP
1230 bool checkNewOutputRange(ScDPObject& rDPObj, ScDocShell& rDocShell, ScRange& rNewOut, bool bApi)
1232 ScDocument* pDoc = rDocShell.GetDocument();
1234 bool bOverflow = false;
1235 rNewOut = rDPObj.GetNewOutputRange(bOverflow);
1237 // Test for overlap with source data range.
1238 // TODO: Check with other pivot tables as well.
1239 const ScSheetSourceDesc* pSheetDesc = rDPObj.GetSheetDesc();
1240 if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rNewOut))
1242 // New output range intersepts with the source data. Move it up to
1243 // where the old range is and see if that works.
1244 ScRange aOldRange = rDPObj.GetOutRange();
1245 SCsROW nDiff = aOldRange.aStart.Row() - rNewOut.aStart.Row();
1246 rNewOut.aStart.SetRow(aOldRange.aStart.Row());
1247 rNewOut.aEnd.IncRow(nDiff);
1248 if (!ValidRow(rNewOut.aStart.Row()) || !ValidRow(rNewOut.aEnd.Row()))
1249 bOverflow = true;
1252 if (bOverflow)
1254 if (!bApi)
1255 rDocShell.ErrorMessage(STR_PIVOT_ERROR);
1257 return false;
1260 ScEditableTester aTester(pDoc, rNewOut);
1261 if (!aTester.IsEditable())
1263 // destination area isn't editable
1264 if (!bApi)
1265 rDocShell.ErrorMessage(aTester.GetMessageId());
1267 return false;
1270 return true;
1275 bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj,
1276 bool bRecord, bool bApi, bool bAllowMove )
1278 if (!pOldObj)
1280 if (!pNewObj)
1281 return false;
1283 return CreatePivotTable(*pNewObj, bRecord, bApi);
1286 if (pOldObj)
1288 if (!pNewObj)
1289 return RemovePivotTable(*pOldObj, bRecord, bApi);
1291 if (pOldObj == pNewObj)
1292 return UpdatePivotTable(*pOldObj, bRecord, bApi);
1295 OSL_ASSERT(pOldObj && pNewObj && pOldObj != pNewObj);
1297 ScDocShellModificator aModificator( rDocShell );
1298 WaitObject aWait( rDocShell.GetActiveDialogParent() );
1300 ScRangeList aRanges;
1301 aRanges.Append(pOldObj->GetOutRange());
1302 aRanges.Append(pNewObj->GetOutRange().aStart); // at least one cell in the output position must be editable.
1303 if (!isEditable(rDocShell, aRanges, bApi))
1304 return false;
1306 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1307 std::auto_ptr<ScDocument> pOldUndoDoc;
1308 std::auto_ptr<ScDocument> pNewUndoDoc;
1309 SAL_WNODEPRECATED_DECLARATIONS_POP
1311 ScDPObject aUndoDPObj(*pOldObj); // for undo or revert on failure
1313 ScDocument* pDoc = rDocShell.GetDocument();
1314 if (bRecord && !pDoc->IsUndoEnabled())
1315 bRecord = false;
1317 if (bRecord)
1318 createUndoDoc(pOldUndoDoc, pDoc, pOldObj->GetOutRange());
1320 pNewObj->WriteSourceDataTo(*pOldObj); // copy source data
1322 ScDPSaveData* pData = pNewObj->GetSaveData();
1323 OSL_ENSURE( pData, "no SaveData from living DPObject" );
1324 if (pData)
1325 pOldObj->SetSaveData(*pData); // copy SaveData
1327 pOldObj->SetAllowMove(bAllowMove);
1328 pOldObj->ReloadGroupTableData();
1329 pOldObj->SyncAllDimensionMembers();
1330 pOldObj->InvalidateData(); // before getting the new output area
1332 // make sure the table has a name (not set by dialog)
1333 if (pOldObj->GetName().isEmpty())
1334 pOldObj->SetName( pDoc->GetDPCollection()->CreateNewName() );
1336 ScRange aNewOut;
1337 if (!checkNewOutputRange(*pOldObj, rDocShell, aNewOut, bApi))
1339 *pOldObj = aUndoDPObj;
1340 return false;
1343 // test if new output area is empty except for old area
1344 if (!bApi)
1346 // OutRange of pOldObj (pDestObj) is still old area
1347 if (!lcl_EmptyExcept(pDoc, aNewOut, pOldObj->GetOutRange()))
1349 QueryBox aBox( rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1350 ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY) );
1351 if (aBox.Execute() == RET_NO)
1353 //! like above (not editable)
1354 *pOldObj = aUndoDPObj;
1355 return false;
1360 if (bRecord)
1361 createUndoDoc(pNewUndoDoc, pDoc, aNewOut);
1363 pOldObj->Output(aNewOut.aStart);
1364 rDocShell.PostPaintGridAll(); //! only necessary parts
1366 if (bRecord)
1368 rDocShell.GetUndoManager()->AddUndoAction(
1369 new ScUndoDataPilot(
1370 &rDocShell, pOldUndoDoc.release(), pNewUndoDoc.release(), &aUndoDPObj, pOldObj, bAllowMove));
1373 // notify API objects
1374 pDoc->BroadcastUno( ScDataPilotModifiedHint(pOldObj->GetName()) );
1375 aModificator.SetDocumentModified();
1377 return true;
1380 bool ScDBDocFunc::RemovePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi)
1382 ScDocShellModificator aModificator(rDocShell);
1383 WaitObject aWait(rDocShell.GetActiveDialogParent());
1385 if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
1386 return false;
1388 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1389 std::auto_ptr<ScDocument> pOldUndoDoc;
1390 std::auto_ptr<ScDPObject> pUndoDPObj;
1391 SAL_WNODEPRECATED_DECLARATIONS_POP
1393 if (bRecord)
1394 pUndoDPObj.reset(new ScDPObject(rDPObj)); // copy old settings for undo
1396 ScDocument* pDoc = rDocShell.GetDocument();
1397 if (bRecord && !pDoc->IsUndoEnabled())
1398 bRecord = false;
1400 // delete table
1402 ScRange aRange = rDPObj.GetOutRange();
1403 SCTAB nTab = aRange.aStart.Tab();
1405 if (bRecord)
1406 createUndoDoc(pOldUndoDoc, pDoc, aRange);
1408 pDoc->DeleteAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
1409 aRange.aEnd.Col(), aRange.aEnd.Row(),
1410 nTab, IDF_ALL );
1411 pDoc->RemoveFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
1412 aRange.aEnd.Col(), aRange.aEnd.Row(),
1413 nTab, SC_MF_AUTO );
1415 pDoc->GetDPCollection()->FreeTable(&rDPObj); // object is deleted here
1417 rDocShell.PostPaintGridAll(); //! only necessary parts
1418 rDocShell.PostPaint(aRange, PAINT_GRID);
1420 if (bRecord)
1422 rDocShell.GetUndoManager()->AddUndoAction(
1423 new ScUndoDataPilot(
1424 &rDocShell, pOldUndoDoc.release(), NULL, pUndoDPObj.get(), NULL, false));
1426 // pUndoDPObj is copied
1429 aModificator.SetDocumentModified();
1430 return true;
1433 bool ScDBDocFunc::CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi)
1435 ScDocShellModificator aModificator(rDocShell);
1436 WaitObject aWait(rDocShell.GetActiveDialogParent());
1438 // At least one cell in the output range should be editable. Check in advance.
1439 if (!isEditable(rDocShell, ScRange(rDPObj.GetOutRange().aStart), bApi))
1440 return false;
1442 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1443 std::auto_ptr<ScDocument> pNewUndoDoc;
1444 SAL_WNODEPRECATED_DECLARATIONS_POP
1446 ScDocument* pDoc = rDocShell.GetDocument();
1447 if (bRecord && !pDoc->IsUndoEnabled())
1448 bRecord = false;
1450 // output range must be set at pNewObj
1451 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1452 std::auto_ptr<ScDPObject> pDestObj(new ScDPObject(rDPObj));
1453 SAL_WNODEPRECATED_DECLARATIONS_POP
1455 ScDPObject& rDestObj = *pDestObj;
1457 // #i94570# When changing the output position in the dialog, a new table is created
1458 // with the settings from the old table, including the name.
1459 // So we have to check for duplicate names here (before inserting).
1460 if (pDoc->GetDPCollection()->GetByName(rDestObj.GetName()))
1461 rDestObj.SetName(OUString()); // ignore the invalid name, create a new name below
1463 if (!pDoc->GetDPCollection()->InsertNewTable(pDestObj.release()))
1464 // Insertion into collection failed.
1465 return false;
1467 rDestObj.ReloadGroupTableData();
1468 rDestObj.SyncAllDimensionMembers();
1469 rDestObj.InvalidateData(); // before getting the new output area
1471 // make sure the table has a name (not set by dialog)
1472 if (rDestObj.GetName().isEmpty())
1473 rDestObj.SetName(pDoc->GetDPCollection()->CreateNewName());
1475 bool bOverflow = false;
1476 ScRange aNewOut = rDestObj.GetNewOutputRange(bOverflow);
1478 if (bOverflow)
1480 if (!bApi)
1481 rDocShell.ErrorMessage(STR_PIVOT_ERROR);
1483 return false;
1487 ScEditableTester aTester(pDoc, aNewOut);
1488 if (!aTester.IsEditable())
1490 // destination area isn't editable
1491 if (!bApi)
1492 rDocShell.ErrorMessage(aTester.GetMessageId());
1494 return false;
1498 // test if new output area is empty except for old area
1499 if (!bApi)
1501 bool bEmpty = pDoc->IsBlockEmpty(
1502 aNewOut.aStart.Tab(), aNewOut.aStart.Col(), aNewOut.aStart.Row(),
1503 aNewOut.aEnd.Col(), aNewOut.aEnd.Row());
1505 if (!bEmpty)
1507 QueryBox aBox(
1508 rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1509 ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY));
1511 if (aBox.Execute() == RET_NO)
1513 //! like above (not editable)
1514 return false;
1519 if (bRecord)
1520 createUndoDoc(pNewUndoDoc, pDoc, aNewOut);
1522 rDestObj.Output(aNewOut.aStart);
1523 rDocShell.PostPaintGridAll(); //! only necessary parts
1525 if (bRecord)
1527 rDocShell.GetUndoManager()->AddUndoAction(
1528 new ScUndoDataPilot(&rDocShell, NULL, pNewUndoDoc.release(), NULL, &rDestObj, false));
1531 // notify API objects
1532 pDoc->BroadcastUno(ScDataPilotModifiedHint(rDestObj.GetName()));
1533 aModificator.SetDocumentModified();
1535 return true;
1538 bool ScDBDocFunc::UpdatePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi)
1540 ScDocShellModificator aModificator( rDocShell );
1541 WaitObject aWait( rDocShell.GetActiveDialogParent() );
1543 if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
1544 return false;
1546 SAL_WNODEPRECATED_DECLARATIONS_PUSH
1547 std::auto_ptr<ScDocument> pOldUndoDoc;
1548 std::auto_ptr<ScDocument> pNewUndoDoc;
1549 SAL_WNODEPRECATED_DECLARATIONS_POP
1551 ScDPObject aUndoDPObj(rDPObj); // For undo or revert on failure.
1553 ScDocument* pDoc = rDocShell.GetDocument();
1554 if (bRecord && !pDoc->IsUndoEnabled())
1555 bRecord = false;
1557 if (bRecord)
1558 createUndoDoc(pOldUndoDoc, pDoc, rDPObj.GetOutRange());
1560 rDPObj.SetAllowMove(false);
1561 rDPObj.ReloadGroupTableData();
1562 if (!rDPObj.SyncAllDimensionMembers())
1563 return false;
1565 rDPObj.InvalidateData(); // before getting the new output area
1567 // make sure the table has a name (not set by dialog)
1568 if (rDPObj.GetName().isEmpty())
1569 rDPObj.SetName( pDoc->GetDPCollection()->CreateNewName() );
1571 ScRange aNewOut;
1572 if (!checkNewOutputRange(rDPObj, rDocShell, aNewOut, bApi))
1574 rDPObj = aUndoDPObj;
1575 return false;
1578 // test if new output area is empty except for old area
1579 if (!bApi)
1581 if (!lcl_EmptyExcept(pDoc, aNewOut, rDPObj.GetOutRange()))
1583 QueryBox aBox( rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
1584 ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY) );
1585 if (aBox.Execute() == RET_NO)
1587 rDPObj = aUndoDPObj;
1588 return false;
1593 if (bRecord)
1594 createUndoDoc(pNewUndoDoc, pDoc, aNewOut);
1596 rDPObj.Output(aNewOut.aStart);
1597 rDocShell.PostPaintGridAll(); //! only necessary parts
1599 if (bRecord)
1601 rDocShell.GetUndoManager()->AddUndoAction(
1602 new ScUndoDataPilot(
1603 &rDocShell, pOldUndoDoc.release(), pNewUndoDoc.release(), &aUndoDPObj, &rDPObj, false));
1606 // notify API objects
1607 pDoc->BroadcastUno( ScDataPilotModifiedHint(rDPObj.GetName()) );
1608 aModificator.SetDocumentModified();
1609 return true;
1612 sal_uLong ScDBDocFunc::RefreshPivotTables(ScDPObject* pDPObj, bool bApi)
1614 ScDPCollection* pDPs = rDocShell.GetDocument()->GetDPCollection();
1615 if (!pDPs)
1616 return 0;
1618 std::set<ScDPObject*> aRefs;
1619 sal_uLong nErrId = pDPs->ReloadCache(pDPObj, aRefs);
1620 if (nErrId)
1621 return nErrId;
1623 std::set<ScDPObject*>::iterator it = aRefs.begin(), itEnd = aRefs.end();
1624 for (; it != itEnd; ++it)
1626 ScDPObject* pObj = *it;
1628 // This action is intentionally not undoable since it modifies cache.
1629 UpdatePivotTable(*pObj, false, bApi);
1632 return 0;
1635 void ScDBDocFunc::RefreshPivotTableGroups(ScDPObject* pDPObj)
1637 if (!pDPObj)
1638 return;
1640 ScDPCollection* pDPs = rDocShell.GetDocument()->GetDPCollection();
1641 if (!pDPs)
1642 return;
1644 ScDPSaveData* pSaveData = pDPObj->GetSaveData();
1645 if (!pSaveData)
1646 return;
1648 std::set<ScDPObject*> aRefs;
1649 if (!pDPs->ReloadGroupsInCache(pDPObj, aRefs))
1650 return;
1652 // We allow pDimData being NULL.
1653 const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData();
1654 std::set<ScDPObject*>::iterator it = aRefs.begin(), itEnd = aRefs.end();
1655 for (; it != itEnd; ++it)
1657 ScDPObject* pObj = *it;
1658 if (pObj != pDPObj)
1660 pSaveData = pObj->GetSaveData();
1661 if (pSaveData)
1662 pSaveData->SetDimensionData(pDimData);
1665 // This action is intentionally not undoable since it modifies cache.
1666 UpdatePivotTable(*pObj, false, false);
1670 //==================================================================
1672 // database import
1674 void ScDBDocFunc::UpdateImport( const OUString& rTarget, const svx::ODataAccessDescriptor& rDescriptor )
1676 // rTarget is the name of a database range
1678 ScDocument* pDoc = rDocShell.GetDocument();
1679 ScDBCollection& rDBColl = *pDoc->GetDBCollection();
1680 const ScDBData* pData = rDBColl.getNamedDBs().findByUpperName(ScGlobal::pCharClass->uppercase(rTarget));
1681 if (!pData)
1683 InfoBox aInfoBox(rDocShell.GetActiveDialogParent(),
1684 ScGlobal::GetRscString( STR_TARGETNOTFOUND ) );
1685 aInfoBox.Execute();
1686 return;
1689 SCTAB nTab;
1690 SCCOL nDummyCol;
1691 SCROW nDummyRow;
1692 pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow );
1694 ScImportParam aImportParam;
1695 pData->GetImportParam( aImportParam );
1697 OUString sDBName;
1698 OUString sDBTable;
1699 sal_Int32 nCommandType = 0;
1700 sDBName = rDescriptor.getDataSource();
1701 rDescriptor[svx::daCommand] >>= sDBTable;
1702 rDescriptor[svx::daCommandType] >>= nCommandType;
1704 aImportParam.aDBName = sDBName;
1705 aImportParam.bSql = ( nCommandType == sdb::CommandType::COMMAND );
1706 aImportParam.aStatement = sDBTable;
1707 aImportParam.bNative = false;
1708 aImportParam.nType = static_cast<sal_uInt8>( ( nCommandType == sdb::CommandType::QUERY ) ? ScDbQuery : ScDbTable );
1709 aImportParam.bImport = true;
1711 bool bContinue = DoImport( nTab, aImportParam, &rDescriptor, true );
1713 // DB-Operationen wiederholen
1715 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
1716 if (pViewSh)
1718 ScRange aRange;
1719 pData->GetArea(aRange);
1720 pViewSh->MarkRange(aRange); // selektieren
1722 if ( bContinue ) // Fehler beim Import -> Abbruch
1724 // interne Operationen, wenn welche gespeichert
1726 if ( pData->HasQueryParam() || pData->HasSortParam() || pData->HasSubTotalParam() )
1727 pViewSh->RepeatDB();
1729 // Pivottabellen die den Bereich als Quelldaten haben
1731 rDocShell.RefreshPivotTables(aRange);
1739 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */