Files
libreoffice/sc/source/ui/docshell/dbdocfun.cxx
Stephan Bergmann 2acdcb2374 coverity#1158232 Fix ownership of NamedDBs::insert argument
f70d03436b "coverity#1158232 have a stab at
silencing warning with function markup" claimed that NamedDBs::insert always
takes ownerhip of its argument, but boost::ptr_set::insert(std::auto_ptr<U> x)
simply calls insert(x.release()), so only takes ownership when it returns true.

ScDBDocFunc::AddDBRange (sc/source/ui/docshell/dbdocfun.cxx) relies on this
behavior, deleting the argument when insert failed.

ScDBDocFunc::RenameDBRange (sc/source/ui/docshell/dbdocfun.cxx) relied on this
behavior, deleting the argument when insert failed, until
f55cc330de "Fixed the fallout of the changes in
ScDBCollection" removed the delete (presumably in error?).  I put it back in
now.

All other uses of NamedDBs::insert ignored the return value (witnessed with
SAL_WARN_UNUSED_RESULT).  Some are insert-if-not-found cases, where I added
asserts now (Sc10Import::LoadDataBaseCollection,
sc/source/filter/starcalc/scflt.cxx, is not entirely clear to me, so I added a
TODO), while others would have potentially leaked the argument, in which cases I
fixed the code.

Change-Id: Iad40fbeb625c8ce6b0a61cbf16298d71cdc7de80
2014-03-12 16:24:43 +01:00

1743 lines
60 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sfx2/app.hxx>
#include <vcl/msgbox.hxx>
#include <vcl/waitobj.hxx>
#include <svx/dataaccessdescriptor.hxx>
#include <com/sun/star/sdb/CommandType.hpp>
#include "dbdocfun.hxx"
#include "sc.hrc"
#include "dbdata.hxx"
#include "undodat.hxx"
#include "docsh.hxx"
#include "docfunc.hxx"
#include "globstr.hrc"
#include "globalnames.hxx"
#include "tabvwsh.hxx"
#include "patattr.hxx"
#include "rangenam.hxx"
#include "olinetab.hxx"
#include "dpobject.hxx"
#include "dpsave.hxx"
#include "dociter.hxx"
#include "editable.hxx"
#include "attrib.hxx"
#include "drwlayer.hxx"
#include "dpshttab.hxx"
#include "hints.hxx"
#include "queryentry.hxx"
#include "markdata.hxx"
#include "progress.hxx"
#include <set>
#include <memory>
using namespace ::com::sun::star;
bool ScDBDocFunc::AddDBRange( const OUString& rName, const ScRange& rRange, bool /* bApi */ )
{
ScDocShellModificator aModificator( rDocShell );
ScDocument* pDoc = rDocShell.GetDocument();
ScDBCollection* pDocColl = pDoc->GetDBCollection();
sal_Bool bUndo (pDoc->IsUndoEnabled());
ScDBCollection* pUndoColl = NULL;
if (bUndo)
pUndoColl = new ScDBCollection( *pDocColl );
ScDBData* pNew = new ScDBData( rName, rRange.aStart.Tab(),
rRange.aStart.Col(), rRange.aStart.Row(),
rRange.aEnd.Col(), rRange.aEnd.Row() );
// #i55926# While loading XML, formula cells only have a single string token,
// so CompileDBFormula would never find any name (index) tokens, and would
// unnecessarily loop through all cells.
bool bCompile = !pDoc->IsImportingXML();
bool bOk;
if ( bCompile )
pDoc->CompileDBFormula( true ); // CreateFormulaString
if ( rName == STR_DB_LOCAL_NONAME )
{
pDoc->SetAnonymousDBData(rRange.aStart.Tab() , pNew);
bOk = true;
}
else
{
bOk = pDocColl->getNamedDBs().insert(pNew);
}
if ( bCompile )
pDoc->CompileDBFormula( false ); // CompileFormulaString
if (!bOk)
{
delete pNew;
delete pUndoColl;
return false;
}
if (bUndo)
{
ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
}
aModificator.SetDocumentModified();
SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
return true;
}
bool ScDBDocFunc::DeleteDBRange(const OUString& rName)
{
bool bDone = false;
ScDocument* pDoc = rDocShell.GetDocument();
ScDBCollection* pDocColl = pDoc->GetDBCollection();
bool bUndo = pDoc->IsUndoEnabled();
ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
const ScDBData* p = rDBs.findByUpperName(ScGlobal::pCharClass->uppercase(rName));
if (p)
{
ScDocShellModificator aModificator( rDocShell );
ScDBCollection* pUndoColl = NULL;
if (bUndo)
pUndoColl = new ScDBCollection( *pDocColl );
pDoc->CompileDBFormula( true ); // CreateFormulaString
rDBs.erase(*p);
pDoc->CompileDBFormula( false ); // CompileFormulaString
if (bUndo)
{
ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
}
aModificator.SetDocumentModified();
SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
bDone = true;
}
return bDone;
}
bool ScDBDocFunc::RenameDBRange( const OUString& rOld, const OUString& rNew )
{
bool bDone = false;
ScDocument* pDoc = rDocShell.GetDocument();
ScDBCollection* pDocColl = pDoc->GetDBCollection();
bool bUndo = pDoc->IsUndoEnabled();
ScDBCollection::NamedDBs& rDBs = pDocColl->getNamedDBs();
const ScDBData* pOld = rDBs.findByUpperName(ScGlobal::pCharClass->uppercase(rOld));
const ScDBData* pNew = rDBs.findByUpperName(ScGlobal::pCharClass->uppercase(rNew));
if (pOld && !pNew)
{
ScDocShellModificator aModificator( rDocShell );
ScDBData* pNewData = new ScDBData(rNew, *pOld);
ScDBCollection* pUndoColl = new ScDBCollection( *pDocColl );
pDoc->CompileDBFormula(true); // CreateFormulaString
rDBs.erase(*pOld);
bool bInserted = rDBs.insert(pNewData);
if (!bInserted) // Fehler -> alten Zustand wiederherstellen
{
delete pNewData;
pDoc->SetDBCollection(pUndoColl); // gehoert dann dem Dokument
}
pDoc->CompileDBFormula( false ); // CompileFormulaString
if (bInserted) // Einfuegen hat geklappt
{
if (bUndo)
{
ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
}
else
delete pUndoColl;
aModificator.SetDocumentModified();
SFX_APP()->Broadcast( SfxSimpleHint( SC_HINT_DBAREAS_CHANGED ) );
bDone = true;
}
}
return bDone;
}
bool ScDBDocFunc::ModifyDBData( const ScDBData& rNewData )
{
bool bDone = false;
ScDocument* pDoc = rDocShell.GetDocument();
ScDBCollection* pDocColl = pDoc->GetDBCollection();
bool bUndo = pDoc->IsUndoEnabled();
ScDBData* pData = NULL;
if (rNewData.GetName() == STR_DB_LOCAL_NONAME)
{
ScRange aRange;
rNewData.GetArea(aRange);
SCTAB nTab = aRange.aStart.Tab();
pData = pDoc->GetAnonymousDBData(nTab);
}
else
pData = pDocColl->getNamedDBs().findByUpperName(rNewData.GetUpperName());
if (pData)
{
ScDocShellModificator aModificator( rDocShell );
ScRange aOldRange, aNewRange;
pData->GetArea(aOldRange);
rNewData.GetArea(aNewRange);
bool bAreaChanged = ( aOldRange != aNewRange ); // dann muss neu compiliert werden
ScDBCollection* pUndoColl = NULL;
if (bUndo)
pUndoColl = new ScDBCollection( *pDocColl );
*pData = rNewData;
if (bAreaChanged)
pDoc->CompileDBFormula();
if (bUndo)
{
ScDBCollection* pRedoColl = new ScDBCollection( *pDocColl );
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoDBData( &rDocShell, pUndoColl, pRedoColl ) );
}
aModificator.SetDocumentModified();
bDone = true;
}
return bDone;
}
bool ScDBDocFunc::RepeatDB( const OUString& rDBName, bool bRecord, bool bApi, bool bIsUnnamed, SCTAB aTab )
{
//! auch fuer ScDBFunc::RepeatDB benutzen!
bool bDone = false;
ScDocument* pDoc = rDocShell.GetDocument();
if (bRecord && !pDoc->IsUndoEnabled())
bRecord = false;
ScDBData* pDBData = NULL;
if (bIsUnnamed)
{
pDBData = pDoc->GetAnonymousDBData( aTab );
}
else
{
ScDBCollection* pColl = pDoc->GetDBCollection();
if (pColl)
pDBData = pColl->getNamedDBs().findByUpperName(ScGlobal::pCharClass->uppercase(rDBName));
}
if ( pDBData )
{
ScQueryParam aQueryParam;
pDBData->GetQueryParam( aQueryParam );
sal_Bool bQuery = aQueryParam.GetEntry(0).bDoQuery;
ScSortParam aSortParam;
pDBData->GetSortParam( aSortParam );
sal_Bool bSort = aSortParam.maKeyState[0].bDoSort;
ScSubTotalParam aSubTotalParam;
pDBData->GetSubTotalParam( aSubTotalParam );
sal_Bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly;
if ( bQuery || bSort || bSubTotal )
{
sal_Bool bQuerySize = false;
ScRange aOldQuery;
ScRange aNewQuery;
if (bQuery && !aQueryParam.bInplace)
{
ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
aQueryParam.nDestTab, true );
if (pDest && pDest->IsDoSize())
{
pDest->GetArea( aOldQuery );
bQuerySize = sal_True;
}
}
SCTAB nTab;
SCCOL nStartCol;
SCROW nStartRow;
SCCOL nEndCol;
SCROW nEndRow;
pDBData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
//! Undo nur benoetigte Daten ?
ScDocument* pUndoDoc = NULL;
ScOutlineTable* pUndoTab = NULL;
ScRangeName* pUndoRange = NULL;
ScDBCollection* pUndoDB = NULL;
if (bRecord)
{
SCTAB nTabCount = pDoc->GetTableCount();
pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
if (pTable)
{
pUndoTab = new ScOutlineTable( *pTable );
// column/row state
SCCOLROW nOutStartCol, nOutEndCol;
SCCOLROW nOutStartRow, nOutEndRow;
pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
pUndoDoc->InitUndo( pDoc, nTab, nTab, true, true );
pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0,
nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab,
IDF_NONE, false, pUndoDoc );
pDoc->CopyToDocument( 0, static_cast<SCROW>(nOutStartRow),
nTab, MAXCOL, static_cast<SCROW>(nOutEndRow), nTab,
IDF_NONE, false, pUndoDoc );
}
else
pUndoDoc->InitUndo( pDoc, nTab, nTab, false, true );
// Datenbereich sichern - incl. Filter-Ergebnis
pDoc->CopyToDocument( 0,nStartRow,nTab, MAXCOL,nEndRow,nTab, IDF_ALL, false, pUndoDoc );
// alle Formeln wegen Referenzen
pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, IDF_FORMULA, false, pUndoDoc );
// DB- und andere Bereiche
ScRangeName* pDocRange = pDoc->GetRangeName();
if (!pDocRange->empty())
pUndoRange = new ScRangeName( *pDocRange );
ScDBCollection* pDocDB = pDoc->GetDBCollection();
if (!pDocDB->empty())
pUndoDB = new ScDBCollection( *pDocDB );
}
if (bSort && bSubTotal)
{
// Sortieren ohne SubTotals
aSubTotalParam.bRemoveOnly = true; // wird unten wieder zurueckgesetzt
DoSubTotals( nTab, aSubTotalParam, NULL, false, bApi );
}
if (bSort)
{
pDBData->GetSortParam( aSortParam ); // Bereich kann sich geaendert haben
Sort( nTab, aSortParam, false, false, bApi );
}
if (bQuery)
{
pDBData->GetQueryParam( aQueryParam ); // Bereich kann sich geaendert haben
ScRange aAdvSource;
if (pDBData->GetAdvancedQuerySource(aAdvSource))
Query( nTab, aQueryParam, &aAdvSource, false, bApi );
else
Query( nTab, aQueryParam, NULL, false, bApi );
// bei nicht-inplace kann die Tabelle umgestellt worden sein
// if ( !aQueryParam.bInplace && aQueryParam.nDestTab != nTab )
// SetTabNo( nTab );
}
if (bSubTotal)
{
pDBData->GetSubTotalParam( aSubTotalParam ); // Bereich kann sich geaendert haben
aSubTotalParam.bRemoveOnly = false;
DoSubTotals( nTab, aSubTotalParam, NULL, false, bApi );
}
if (bRecord)
{
SCTAB nDummyTab;
SCCOL nDummyCol;
SCROW nDummyRow;
SCROW nNewEndRow;
pDBData->GetArea( nDummyTab, nDummyCol,nDummyRow, nDummyCol,nNewEndRow );
const ScRange* pOld = NULL;
const ScRange* pNew = NULL;
if (bQuerySize)
{
ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow,
aQueryParam.nDestTab, true );
if (pDest)
{
pDest->GetArea( aNewQuery );
pOld = &aOldQuery;
pNew = &aNewQuery;
}
}
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoRepeatDB( &rDocShell, nTab,
nStartCol, nStartRow, nEndCol, nEndRow,
nNewEndRow,
//nCurX, nCurY,
nStartCol, nStartRow,
pUndoDoc, pUndoTab,
pUndoRange, pUndoDB,
pOld, pNew ) );
}
rDocShell.PostPaint(ScRange(0, 0, nTab, MAXCOL, MAXROW, nTab),
PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE);
bDone = true;
}
else if (!bApi) // "Keine Operationen auszufuehren"
rDocShell.ErrorMessage(STR_MSSG_REPEATDB_0);
}
return bDone;
}
bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam,
bool bRecord, bool bPaint, bool bApi )
{
ScDocShellModificator aModificator( rDocShell );
ScDocument* pDoc = rDocShell.GetDocument();
if (bRecord && !pDoc->IsUndoEnabled())
bRecord = false;
SCTAB nSrcTab = nTab;
ScDrawLayer* pDrawLayer = pDoc->GetDrawLayer();
ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
rSortParam.nCol2, rSortParam.nRow2 );
if (!pDBData)
{
OSL_FAIL( "Sort: keine DBData" );
return false;
}
ScDBData* pDestData = NULL;
ScRange aOldDest;
sal_Bool bCopy = !rSortParam.bInplace;
if ( bCopy && rSortParam.nDestCol == rSortParam.nCol1 &&
rSortParam.nDestRow == rSortParam.nRow1 && rSortParam.nDestTab == nTab )
bCopy = false;
ScSortParam aLocalParam( rSortParam );
if ( bCopy )
{
aLocalParam.MoveToDest();
if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
{
if (!bApi)
rDocShell.ErrorMessage(STR_PASTE_FULL);
return false;
}
nTab = rSortParam.nDestTab;
pDestData = pDoc->GetDBAtCursor( rSortParam.nDestCol, rSortParam.nDestRow,
rSortParam.nDestTab, true );
if (pDestData)
pDestData->GetArea(aOldDest);
}
ScEditableTester aTester( pDoc, nTab, aLocalParam.nCol1,aLocalParam.nRow1,
aLocalParam.nCol2,aLocalParam.nRow2 );
if (!aTester.IsEditable())
{
if (!bApi)
rDocShell.ErrorMessage(aTester.GetMessageId());
return false;
}
if ( aLocalParam.bIncludePattern && pDoc->HasAttrib(
aLocalParam.nCol1, aLocalParam.nRow1, nTab,
aLocalParam.nCol2, aLocalParam.nRow2, nTab,
HASATTR_MERGED | HASATTR_OVERLAPPED ) )
{
// Merge-Attribute wuerden beim Sortieren durcheinanderkommen
if (!bApi)
rDocShell.ErrorMessage(STR_SORT_ERR_MERGED);
return false;
}
// ausfuehren
WaitObject aWait( rDocShell.GetActiveDialogParent() );
sal_Bool bRepeatQuery = false; // bestehenden Filter wiederholen?
ScQueryParam aQueryParam;
pDBData->GetQueryParam( aQueryParam );
if ( aQueryParam.GetEntry(0).bDoQuery )
bRepeatQuery = sal_True;
if (bRepeatQuery && bCopy)
{
if ( aQueryParam.bInplace ||
aQueryParam.nDestCol != rSortParam.nDestCol ||
aQueryParam.nDestRow != rSortParam.nDestRow ||
aQueryParam.nDestTab != rSortParam.nDestTab ) // Query auf selben Zielbereich?
bRepeatQuery = false;
}
ScUndoSort* pUndoAction = 0;
if ( bRecord )
{
// Referenzen ausserhalb des Bereichs werden nicht veraendert !
ScDocument* pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
// Zeilenhoehen immer (wegen automatischer Anpassung)
//! auf ScBlockUndo umstellen
pUndoDoc->InitUndo( pDoc, nTab, nTab, false, true );
/* #i59745# Do not copy note captions to undo document. All existing
caption objects will be repositioned while sorting which is tracked
in drawing undo. When undo is executed, the old positions will be
restored, and the cells with the old notes (which still refer to the
existing captions) will be copied back into the source document. */
pDoc->CopyToDocument( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
aLocalParam.nCol2, aLocalParam.nRow2, nTab,
IDF_ALL|IDF_NOCAPTIONS, false, pUndoDoc );
const ScRange* pR = 0;
if (pDestData)
{
/* #i59745# Do not copy note captions from destination range to
undo document. All existing caption objects will be removed
which is tracked in drawing undo. When undo is executed, the
caption objects are reinserted with drawing undo, and the cells
with the old notes (which still refer to the existing captions)
will be copied back into the source document. */
pDoc->CopyToDocument( aOldDest, IDF_ALL|IDF_NOCAPTIONS, false, pUndoDoc );
pR = &aOldDest;
}
// Zeilenhoehen immer (wegen automatischer Anpassung)
//! auf ScBlockUndo umstellen
// if (bRepeatQuery)
pDoc->CopyToDocument( 0, aLocalParam.nRow1, nTab, MAXCOL, aLocalParam.nRow2, nTab,
IDF_NONE, false, pUndoDoc );
ScDBCollection* pUndoDB = NULL;
ScDBCollection* pDocDB = pDoc->GetDBCollection();
if (!pDocDB->empty())
pUndoDB = new ScDBCollection( *pDocDB );
pUndoAction = new ScUndoSort( &rDocShell, nTab, rSortParam, pUndoDoc, pUndoDB, pR );
rDocShell.GetUndoManager()->AddUndoAction( pUndoAction );
// #i59745# collect all drawing undo actions affecting cell note captions
if( pDrawLayer )
pDrawLayer->BeginCalcUndo(false);
}
if ( bCopy )
{
if (pDestData)
pDoc->DeleteAreaTab(aOldDest, IDF_CONTENTS); // Zielbereich vorher loeschen
ScRange aSource( rSortParam.nCol1,rSortParam.nRow1,nSrcTab,
rSortParam.nCol2,rSortParam.nRow2,nSrcTab );
ScAddress aDest( rSortParam.nDestCol, rSortParam.nDestRow, rSortParam.nDestTab );
rDocShell.GetDocFunc().MoveBlock( aSource, aDest, false, false, false, true );
}
// don't call ScDocument::Sort with an empty SortParam (may be empty here if bCopy is set)
if (aLocalParam.GetSortKeyCount() && aLocalParam.maKeyState[0].bDoSort)
{
ScProgress aProgress(&rDocShell, ScGlobal::GetRscString(STR_PROGRESS_SORTING), 0);
pDoc->Sort( nTab, aLocalParam, bRepeatQuery, &aProgress );
}
bool bSave = true;
if (bCopy)
{
ScSortParam aOldSortParam;
pDBData->GetSortParam( aOldSortParam );
if (aOldSortParam.GetSortKeyCount() &&
aOldSortParam.maKeyState[0].bDoSort && aOldSortParam.bInplace)
{
bSave = false;
aOldSortParam.nDestCol = rSortParam.nDestCol;
aOldSortParam.nDestRow = rSortParam.nDestRow;
aOldSortParam.nDestTab = rSortParam.nDestTab;
pDBData->SetSortParam( aOldSortParam ); // dann nur DestPos merken
}
}
if (bSave) // Parameter merken
{
pDBData->SetSortParam( rSortParam );
pDBData->SetHeader( rSortParam.bHasHeader ); //! ???
pDBData->SetByRow( rSortParam.bByRow ); //! ???
}
if (bCopy) // neuen DB-Bereich merken
{
// Tabelle umschalten von aussen (View)
//! SetCursor ??!?!
ScRange aDestPos( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
aLocalParam.nCol2, aLocalParam.nRow2, nTab );
ScDBData* pNewData;
if (pDestData)
pNewData = pDestData; // Bereich vorhanden -> anpassen
else // Bereich ab Cursor/Markierung wird angelegt
pNewData = rDocShell.GetDBData(aDestPos, SC_DB_MAKE, SC_DBSEL_FORCE_MARK );
if (pNewData)
{
pNewData->SetArea( nTab,
aLocalParam.nCol1,aLocalParam.nRow1,
aLocalParam.nCol2,aLocalParam.nRow2 );
pNewData->SetSortParam( aLocalParam );
pNewData->SetHeader( aLocalParam.bHasHeader ); //! ???
pNewData->SetByRow( aLocalParam.bByRow );
}
else
{
OSL_FAIL("Zielbereich nicht da");
}
}
ScRange aDirtyRange( aLocalParam.nCol1, aLocalParam.nRow1, nTab,
aLocalParam.nCol2, aLocalParam.nRow2, nTab );
pDoc->SetDirty( aDirtyRange );
if (bPaint)
{
sal_uInt16 nPaint = PAINT_GRID;
SCCOL nStartX = aLocalParam.nCol1;
SCROW nStartY = aLocalParam.nRow1;
SCCOL nEndX = aLocalParam.nCol2;
SCROW nEndY = aLocalParam.nRow2;
if ( bRepeatQuery )
{
nPaint |= PAINT_LEFT;
nStartX = 0;
nEndX = MAXCOL;
}
if (pDestData)
{
if ( nEndX < aOldDest.aEnd.Col() )
nEndX = aOldDest.aEnd.Col();
if ( nEndY < aOldDest.aEnd.Row() )
nEndY = aOldDest.aEnd.Row();
}
rDocShell.PostPaint(ScRange(nStartX, nStartY, nTab, nEndX, nEndY, nTab), nPaint);
}
// AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, bPaint );
rDocShell.AdjustRowHeight( aLocalParam.nRow1, aLocalParam.nRow2, nTab );
// #i59745# set collected drawing undo actions at sorting undo action
if( pUndoAction && pDrawLayer )
pUndoAction->SetDrawUndoAction( pDrawLayer->GetCalcUndo() );
aModificator.SetDocumentModified();
return true;
}
bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam,
const ScRange* pAdvSource, bool bRecord, bool bApi )
{
ScDocShellModificator aModificator( rDocShell );
ScDocument* pDoc = rDocShell.GetDocument();
if (bRecord && !pDoc->IsUndoEnabled())
bRecord = false;
ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rQueryParam.nCol1, rQueryParam.nRow1,
rQueryParam.nCol2, rQueryParam.nRow2 );
if (!pDBData)
{
OSL_FAIL( "Query: keine DBData" );
return false;
}
// Wechsel von Inplace auf nicht-Inplace, dann erst Inplace aufheben:
// (nur, wenn im Dialog "Persistent" ausgewaehlt ist)
if ( !rQueryParam.bInplace && pDBData->HasQueryParam() && rQueryParam.bDestPers )
{
ScQueryParam aOldQuery;
pDBData->GetQueryParam(aOldQuery);
if (aOldQuery.bInplace)
{
// alte Filterung aufheben
SCSIZE nEC = aOldQuery.GetEntryCount();
for (SCSIZE i=0; i<nEC; i++)
aOldQuery.GetEntry(i).bDoQuery = false;
aOldQuery.bDuplicate = true;
Query( nTab, aOldQuery, NULL, bRecord, bApi );
}
}
ScQueryParam aLocalParam( rQueryParam ); // fuer Paint / Zielbereich
sal_Bool bCopy = !rQueryParam.bInplace; // kopiert wird in Table::Query
ScDBData* pDestData = NULL; // Bereich, in den kopiert wird
sal_Bool bDoSize = false; // Zielgroesse anpassen (einf./loeschen)
SCCOL nFormulaCols = 0; // nur bei bDoSize
sal_Bool bKeepFmt = false;
ScRange aOldDest;
ScRange aDestTotal;
if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 &&
rQueryParam.nDestRow == rQueryParam.nRow1 && rQueryParam.nDestTab == nTab )
bCopy = false;
SCTAB nDestTab = nTab;
if ( bCopy )
{
aLocalParam.MoveToDest();
nDestTab = rQueryParam.nDestTab;
if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
{
if (!bApi)
rDocShell.ErrorMessage(STR_PASTE_FULL);
return false;
}
ScEditableTester aTester( pDoc, nDestTab, aLocalParam.nCol1,aLocalParam.nRow1,
aLocalParam.nCol2,aLocalParam.nRow2);
if (!aTester.IsEditable())
{
if (!bApi)
rDocShell.ErrorMessage(aTester.GetMessageId());
return false;
}
pDestData = pDoc->GetDBAtCursor( rQueryParam.nDestCol, rQueryParam.nDestRow,
rQueryParam.nDestTab, true );
if (pDestData)
{
pDestData->GetArea( aOldDest );
aDestTotal=ScRange( rQueryParam.nDestCol,
rQueryParam.nDestRow,
nDestTab,
rQueryParam.nDestCol + rQueryParam.nCol2 - rQueryParam.nCol1,
rQueryParam.nDestRow + rQueryParam.nRow2 - rQueryParam.nRow1,
nDestTab );
bDoSize = pDestData->IsDoSize();
// Test, ob Formeln aufgefuellt werden muessen (nFormulaCols):
if ( bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col() )
{
SCCOL nTestCol = aOldDest.aEnd.Col() + 1; // neben dem Bereich
SCROW nTestRow = rQueryParam.nDestRow +
( aLocalParam.bHasHeader ? 1 : 0 );
while ( nTestCol <= MAXCOL &&
pDoc->GetCellType(ScAddress( nTestCol, nTestRow, nTab )) == CELLTYPE_FORMULA )
++nTestCol, ++nFormulaCols;
}
bKeepFmt = pDestData->IsKeepFmt();
if ( bDoSize && !pDoc->CanFitBlock( aOldDest, aDestTotal ) )
{
if (!bApi)
rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2); // kann keine Zeilen einfuegen
return false;
}
}
}
// ausfuehren
WaitObject aWait( rDocShell.GetActiveDialogParent() );
sal_Bool bKeepSub = false; // bestehende Teilergebnisse wiederholen?
ScSubTotalParam aSubTotalParam;
if (rQueryParam.GetEntry(0).bDoQuery) // nicht beim Aufheben
{
pDBData->GetSubTotalParam( aSubTotalParam ); // Teilergebnisse vorhanden?
if ( aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly )
bKeepSub = sal_True;
}
ScDocument* pUndoDoc = NULL;
ScDBCollection* pUndoDB = NULL;
const ScRange* pOld = NULL;
if ( bRecord )
{
pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
if (bCopy)
{
pUndoDoc->InitUndo( pDoc, nDestTab, nDestTab, false, true );
pDoc->CopyToDocument( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
aLocalParam.nCol2, aLocalParam.nRow2, nDestTab,
IDF_ALL, false, pUndoDoc );
// Attribute sichern, falls beim Filtern mitkopiert
if (pDestData)
{
pDoc->CopyToDocument( aOldDest, IDF_ALL, false, pUndoDoc );
pOld = &aOldDest;
}
}
else
{
pUndoDoc->InitUndo( pDoc, nTab, nTab, false, true );
pDoc->CopyToDocument( 0, rQueryParam.nRow1, nTab, MAXCOL, rQueryParam.nRow2, nTab,
IDF_NONE, false, pUndoDoc );
}
ScDBCollection* pDocDB = pDoc->GetDBCollection();
if (!pDocDB->empty())
pUndoDB = new ScDBCollection( *pDocDB );
pDoc->BeginDrawUndo();
}
ScDocument* pAttribDoc = NULL;
ScRange aAttribRange;
if (pDestData) // Zielbereich loeschen
{
if ( bKeepFmt )
{
// kleinere der End-Spalten, Header+1 Zeile
aAttribRange = aOldDest;
if ( aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col() )
aAttribRange.aEnd.SetCol( aDestTotal.aEnd.Col() );
aAttribRange.aEnd.SetRow( aAttribRange.aStart.Row() +
( aLocalParam.bHasHeader ? 1 : 0 ) );
// auch fuer aufgefuellte Formeln
aAttribRange.aEnd.SetCol( aAttribRange.aEnd.Col() + nFormulaCols );
pAttribDoc = new ScDocument( SCDOCMODE_UNDO );
pAttribDoc->InitUndo( pDoc, nDestTab, nDestTab, false, true );
pDoc->CopyToDocument( aAttribRange, IDF_ATTRIB, false, pAttribDoc );
}
if ( bDoSize )
pDoc->FitBlock( aOldDest, aDestTotal );
else
pDoc->DeleteAreaTab(aOldDest, IDF_ALL); // einfach loeschen
}
// Filtern am Dokument ausfuehren
SCSIZE nCount = pDoc->Query( nTab, rQueryParam, bKeepSub );
if (bCopy)
{
aLocalParam.nRow2 = aLocalParam.nRow1 + nCount;
if (!aLocalParam.bHasHeader && nCount > 0)
--aLocalParam.nRow2;
if ( bDoSize )
{
// auf wirklichen Ergebnis-Bereich anpassen
// (das hier ist immer eine Verkleinerung)
ScRange aNewDest( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
aLocalParam.nCol2, aLocalParam.nRow2, nDestTab );
pDoc->FitBlock( aDestTotal, aNewDest, false ); // sal_False - nicht loeschen
if ( nFormulaCols > 0 )
{
// Formeln ausfuellen
//! Undo (Query und Repeat) !!!
ScRange aNewForm( aLocalParam.nCol2+1, aLocalParam.nRow1, nDestTab,
aLocalParam.nCol2+nFormulaCols, aLocalParam.nRow2, nDestTab );
ScRange aOldForm = aNewForm;
aOldForm.aEnd.SetRow( aOldDest.aEnd.Row() );
pDoc->FitBlock( aOldForm, aNewForm, false );
ScMarkData aMark;
aMark.SelectOneTable(nDestTab);
SCROW nFStartY = aLocalParam.nRow1 + ( aLocalParam.bHasHeader ? 1 : 0 );
sal_uLong nProgCount = nFormulaCols;
nProgCount *= aLocalParam.nRow2 - nFStartY;
ScProgress aProgress( pDoc->GetDocumentShell(),
ScGlobal::GetRscString(STR_FILL_SERIES_PROGRESS), nProgCount );
pDoc->Fill( aLocalParam.nCol2+1, nFStartY,
aLocalParam.nCol2+nFormulaCols, nFStartY, &aProgress, aMark,
aLocalParam.nRow2 - nFStartY,
FILL_TO_BOTTOM, FILL_SIMPLE );
}
}
if ( pAttribDoc ) // gemerkte Attribute zurueckkopieren
{
// Header
if (aLocalParam.bHasHeader)
{
ScRange aHdrRange = aAttribRange;
aHdrRange.aEnd.SetRow( aHdrRange.aStart.Row() );
pAttribDoc->CopyToDocument( aHdrRange, IDF_ATTRIB, false, pDoc );
}
// Daten
SCCOL nAttrEndCol = aAttribRange.aEnd.Col();
SCROW nAttrRow = aAttribRange.aStart.Row() + ( aLocalParam.bHasHeader ? 1 : 0 );
for (SCCOL nCol = aAttribRange.aStart.Col(); nCol<=nAttrEndCol; nCol++)
{
const ScPatternAttr* pSrcPattern = pAttribDoc->GetPattern(
nCol, nAttrRow, nDestTab );
OSL_ENSURE(pSrcPattern,"Pattern ist 0");
if (pSrcPattern)
{
pDoc->ApplyPatternAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
nDestTab, *pSrcPattern );
const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
if (pStyle)
pDoc->ApplyStyleAreaTab( nCol, nAttrRow, nCol, aLocalParam.nRow2,
nDestTab, *pStyle );
}
}
delete pAttribDoc;
}
}
// speichern: Inplace immer, sonst je nach Einstellung
// alter Inplace-Filter ist ggf. schon aufgehoben
bool bSave = rQueryParam.bInplace || rQueryParam.bDestPers;
if (bSave) // merken
{
pDBData->SetQueryParam( rQueryParam );
pDBData->SetHeader( rQueryParam.bHasHeader ); //! ???
pDBData->SetAdvancedQuerySource( pAdvSource ); // after SetQueryParam
}
if (bCopy) // neuen DB-Bereich merken
{
// selektieren wird hinterher von aussen (dbfunc)
// momentan ueber DB-Bereich an der Zielposition, darum muss dort
// auf jeden Fall ein Bereich angelegt werden.
ScDBData* pNewData;
if (pDestData)
pNewData = pDestData; // Bereich vorhanden -> anpassen (immer!)
else // Bereich anlegen
pNewData = rDocShell.GetDBData(
ScRange( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
aLocalParam.nCol2, aLocalParam.nRow2, nDestTab ),
SC_DB_MAKE, SC_DBSEL_FORCE_MARK );
if (pNewData)
{
pNewData->SetArea( nDestTab, aLocalParam.nCol1, aLocalParam.nRow1,
aLocalParam.nCol2, aLocalParam.nRow2 );
// Query-Param wird am Ziel nicht mehr eingestellt, fuehrt nur zu Verwirrung
// und Verwechslung mit dem Query-Param am Quellbereich (#37187#)
}
else
{
OSL_FAIL("Zielbereich nicht da");
}
}
if (!bCopy)
{
pDoc->InvalidatePageBreaks(nTab);
pDoc->UpdatePageBreaks( nTab );
}
// #i23299# Subtotal functions depend on cell's filtered states.
ScRange aDirtyRange(0 , aLocalParam.nRow1, nDestTab, MAXCOL, aLocalParam.nRow2, nDestTab);
pDoc->SetSubTotalCellsDirty(aDirtyRange);
if ( bRecord )
{
// create undo action after executing, because of drawing layer undo
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoQuery( &rDocShell, nTab, rQueryParam, pUndoDoc, pUndoDB,
pOld, bDoSize, pAdvSource ) );
}
if (bCopy)
{
SCCOL nEndX = aLocalParam.nCol2;
SCROW nEndY = aLocalParam.nRow2;
if (pDestData)
{
if ( aOldDest.aEnd.Col() > nEndX )
nEndX = aOldDest.aEnd.Col();
if ( aOldDest.aEnd.Row() > nEndY )
nEndY = aOldDest.aEnd.Row();
}
if (bDoSize)
nEndY = MAXROW;
rDocShell.PostPaint(
ScRange(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, nEndX, nEndY, nDestTab),
PAINT_GRID);
}
else
rDocShell.PostPaint(
ScRange(0, rQueryParam.nRow1, nTab, MAXCOL, MAXROW, nTab),
PAINT_GRID | PAINT_LEFT);
aModificator.SetDocumentModified();
return true;
}
bool ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
const ScSortParam* pForceNewSort, bool bRecord, bool bApi )
{
//! auch fuer ScDBFunc::DoSubTotals benutzen!
// dann bleibt aussen:
// - neuen Bereich (aus DBData) markieren
// - SelectionChanged (?)
sal_Bool bDo = !rParam.bRemoveOnly; // sal_False = nur loeschen
sal_Bool bRet = false;
ScDocument* pDoc = rDocShell.GetDocument();
if (bRecord && !pDoc->IsUndoEnabled())
bRecord = false;
ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1,
rParam.nCol2, rParam.nRow2 );
if (!pDBData)
{
OSL_FAIL( "SubTotals: keine DBData" );
return false;
}
ScEditableTester aTester( pDoc, nTab, 0,rParam.nRow1+1, MAXCOL,MAXROW );
if (!aTester.IsEditable())
{
if (!bApi)
rDocShell.ErrorMessage(aTester.GetMessageId());
return false;
}
if (pDoc->HasAttrib( rParam.nCol1, rParam.nRow1+1, nTab,
rParam.nCol2, rParam.nRow2, nTab, HASATTR_MERGED | HASATTR_OVERLAPPED ))
{
if (!bApi)
rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); // nicht in zusammengefasste einfuegen
return false;
}
sal_Bool bOk = true;
if (rParam.bReplace)
if (pDoc->TestRemoveSubTotals( nTab, rParam ))
{
bOk = ( MessBox( rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
// "StarCalc" "Daten loeschen?"
ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ),
ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_1 ) ).Execute()
== RET_YES );
}
if (bOk)
{
WaitObject aWait( rDocShell.GetActiveDialogParent() );
ScDocShellModificator aModificator( rDocShell );
ScSubTotalParam aNewParam( rParam ); // Bereichsende wird veraendert
ScDocument* pUndoDoc = NULL;
ScOutlineTable* pUndoTab = NULL;
ScRangeName* pUndoRange = NULL;
ScDBCollection* pUndoDB = NULL;
if (bRecord) // alte Daten sichern
{
sal_Bool bOldFilter = bDo && rParam.bDoSort;
SCTAB nTabCount = pDoc->GetTableCount();
pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
ScOutlineTable* pTable = pDoc->GetOutlineTable( nTab );
if (pTable)
{
pUndoTab = new ScOutlineTable( *pTable );
// column/row state
SCCOLROW nOutStartCol, nOutEndCol;
SCCOLROW nOutStartRow, nOutEndRow;
pTable->GetColArray()->GetRange( nOutStartCol, nOutEndCol );
pTable->GetRowArray()->GetRange( nOutStartRow, nOutEndRow );
pUndoDoc->InitUndo( pDoc, nTab, nTab, true, true );
pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, false, pUndoDoc );
pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, false, pUndoDoc );
}
else
pUndoDoc->InitUndo( pDoc, nTab, nTab, false, bOldFilter );
// Datenbereich sichern - incl. Filter-Ergebnis
pDoc->CopyToDocument( 0,rParam.nRow1+1,nTab, MAXCOL,rParam.nRow2,nTab,
IDF_ALL, false, pUndoDoc );
// alle Formeln wegen Referenzen
pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1,
IDF_FORMULA, false, pUndoDoc );
// DB- und andere Bereiche
ScRangeName* pDocRange = pDoc->GetRangeName();
if (!pDocRange->empty())
pUndoRange = new ScRangeName( *pDocRange );
ScDBCollection* pDocDB = pDoc->GetDBCollection();
if (!pDocDB->empty())
pUndoDB = new ScDBCollection( *pDocDB );
}
// pDoc->SetOutlineTable( nTab, NULL );
ScOutlineTable* pOut = pDoc->GetOutlineTable( nTab );
if (pOut)
pOut->GetRowArray()->RemoveAll(); // nur Zeilen-Outlines loeschen
if (rParam.bReplace)
pDoc->RemoveSubTotals( nTab, aNewParam );
sal_Bool bSuccess = sal_True;
if (bDo)
{
// Sortieren
if ( rParam.bDoSort || pForceNewSort )
{
pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
// Teilergebnis-Felder vor die Sortierung setzen
// (doppelte werden weggelassen, kann darum auch wieder aufgerufen werden)
ScSortParam aOldSort;
pDBData->GetSortParam( aOldSort );
ScSortParam aSortParam( aNewParam, pForceNewSort ? *pForceNewSort : aOldSort );
Sort( nTab, aSortParam, false, false, bApi );
}
bSuccess = pDoc->DoSubTotals( nTab, aNewParam );
pDoc->SetDrawPageSize(nTab);
}
ScRange aDirtyRange( aNewParam.nCol1, aNewParam.nRow1, nTab,
aNewParam.nCol2, aNewParam.nRow2, nTab );
pDoc->SetDirty( aDirtyRange );
if (bRecord)
{
// ScDBData* pUndoDBData = pDBData ? new ScDBData( *pDBData ) : NULL;
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoSubTotals( &rDocShell, nTab,
rParam, aNewParam.nRow2,
pUndoDoc, pUndoTab, // pUndoDBData,
pUndoRange, pUndoDB ) );
}
if (!bSuccess)
{
// "Kann keine Zeilen einfuegen"
if (!bApi)
rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);
}
// merken
pDBData->SetSubTotalParam( aNewParam );
pDBData->SetArea( nTab, aNewParam.nCol1,aNewParam.nRow1, aNewParam.nCol2,aNewParam.nRow2 );
pDoc->CompileDBFormula();
rDocShell.PostPaint(ScRange(0, 0, nTab, MAXCOL,MAXROW,nTab),
PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE);
aModificator.SetDocumentModified();
bRet = bSuccess;
}
return bRet;
}
namespace {
bool lcl_EmptyExcept( ScDocument* pDoc, const ScRange& rRange, const ScRange& rExcept )
{
ScCellIterator aIter( pDoc, rRange );
for (bool bHasCell = aIter.first(); bHasCell; bHasCell = aIter.next())
{
if (!aIter.isEmpty()) // real content?
{
if (!rExcept.In(aIter.GetPos()))
return false; // cell found
}
}
return true; // nothing found - empty
}
bool isEditable(ScDocShell& rDocShell, const ScRangeList& rRanges, bool bApi)
{
ScDocument* pDoc = rDocShell.GetDocument();
if (!rDocShell.IsEditable() || pDoc->GetChangeTrack())
{
// not recorded -> disallow
if (!bApi)
rDocShell.ErrorMessage(STR_PROTECTIONERR);
return false;
}
for (size_t i = 0, n = rRanges.size(); i < n; ++i)
{
const ScRange* p = rRanges[i];
ScEditableTester aTester(pDoc, *p);
if (!aTester.IsEditable())
{
if (!bApi)
rDocShell.ErrorMessage(aTester.GetMessageId());
return false;
}
}
return true;
}
SAL_WNODEPRECATED_DECLARATIONS_PUSH
void createUndoDoc(std::auto_ptr<ScDocument>& pUndoDoc, ScDocument* pDoc, const ScRange& rRange)
{
SCTAB nTab = rRange.aStart.Tab();
pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
pUndoDoc->InitUndo(pDoc, nTab, nTab);
pDoc->CopyToDocument(rRange, IDF_ALL, false, pUndoDoc.get());
}
SAL_WNODEPRECATED_DECLARATIONS_POP
bool checkNewOutputRange(ScDPObject& rDPObj, ScDocShell& rDocShell, ScRange& rNewOut, bool bApi)
{
ScDocument* pDoc = rDocShell.GetDocument();
bool bOverflow = false;
rNewOut = rDPObj.GetNewOutputRange(bOverflow);
// Test for overlap with source data range.
// TODO: Check with other pivot tables as well.
const ScSheetSourceDesc* pSheetDesc = rDPObj.GetSheetDesc();
if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rNewOut))
{
// New output range intersepts with the source data. Move it up to
// where the old range is and see if that works.
ScRange aOldRange = rDPObj.GetOutRange();
SCsROW nDiff = aOldRange.aStart.Row() - rNewOut.aStart.Row();
rNewOut.aStart.SetRow(aOldRange.aStart.Row());
rNewOut.aEnd.IncRow(nDiff);
if (!ValidRow(rNewOut.aStart.Row()) || !ValidRow(rNewOut.aEnd.Row()))
bOverflow = true;
}
if (bOverflow)
{
if (!bApi)
rDocShell.ErrorMessage(STR_PIVOT_ERROR);
return false;
}
ScEditableTester aTester(pDoc, rNewOut);
if (!aTester.IsEditable())
{
// destination area isn't editable
if (!bApi)
rDocShell.ErrorMessage(aTester.GetMessageId());
return false;
}
return true;
}
}
bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj,
bool bRecord, bool bApi, bool bAllowMove )
{
if (!pOldObj)
{
if (!pNewObj)
return false;
return CreatePivotTable(*pNewObj, bRecord, bApi);
}
if (pOldObj)
{
if (!pNewObj)
return RemovePivotTable(*pOldObj, bRecord, bApi);
if (pOldObj == pNewObj)
return UpdatePivotTable(*pOldObj, bRecord, bApi);
}
OSL_ASSERT(pOldObj && pNewObj && pOldObj != pNewObj);
ScDocShellModificator aModificator( rDocShell );
WaitObject aWait( rDocShell.GetActiveDialogParent() );
ScRangeList aRanges;
aRanges.Append(pOldObj->GetOutRange());
aRanges.Append(pNewObj->GetOutRange().aStart); // at least one cell in the output position must be editable.
if (!isEditable(rDocShell, aRanges, bApi))
return false;
SAL_WNODEPRECATED_DECLARATIONS_PUSH
std::auto_ptr<ScDocument> pOldUndoDoc;
std::auto_ptr<ScDocument> pNewUndoDoc;
SAL_WNODEPRECATED_DECLARATIONS_POP
ScDPObject aUndoDPObj(*pOldObj); // for undo or revert on failure
ScDocument* pDoc = rDocShell.GetDocument();
if (bRecord && !pDoc->IsUndoEnabled())
bRecord = false;
if (bRecord)
createUndoDoc(pOldUndoDoc, pDoc, pOldObj->GetOutRange());
pNewObj->WriteSourceDataTo(*pOldObj); // copy source data
ScDPSaveData* pData = pNewObj->GetSaveData();
OSL_ENSURE( pData, "no SaveData from living DPObject" );
if (pData)
pOldObj->SetSaveData(*pData); // copy SaveData
pOldObj->SetAllowMove(bAllowMove);
pOldObj->ReloadGroupTableData();
pOldObj->SyncAllDimensionMembers();
pOldObj->InvalidateData(); // before getting the new output area
// make sure the table has a name (not set by dialog)
if (pOldObj->GetName().isEmpty())
pOldObj->SetName( pDoc->GetDPCollection()->CreateNewName() );
ScRange aNewOut;
if (!checkNewOutputRange(*pOldObj, rDocShell, aNewOut, bApi))
{
*pOldObj = aUndoDPObj;
return false;
}
// test if new output area is empty except for old area
if (!bApi)
{
// OutRange of pOldObj (pDestObj) is still old area
if (!lcl_EmptyExcept(pDoc, aNewOut, pOldObj->GetOutRange()))
{
QueryBox aBox( rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY) );
if (aBox.Execute() == RET_NO)
{
//! like above (not editable)
*pOldObj = aUndoDPObj;
return false;
}
}
}
if (bRecord)
createUndoDoc(pNewUndoDoc, pDoc, aNewOut);
pOldObj->Output(aNewOut.aStart);
rDocShell.PostPaintGridAll(); //! only necessary parts
if (bRecord)
{
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoDataPilot(
&rDocShell, pOldUndoDoc.release(), pNewUndoDoc.release(), &aUndoDPObj, pOldObj, bAllowMove));
}
// notify API objects
pDoc->BroadcastUno( ScDataPilotModifiedHint(pOldObj->GetName()) );
aModificator.SetDocumentModified();
return true;
}
bool ScDBDocFunc::RemovePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi)
{
ScDocShellModificator aModificator(rDocShell);
WaitObject aWait(rDocShell.GetActiveDialogParent());
if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
return false;
SAL_WNODEPRECATED_DECLARATIONS_PUSH
std::auto_ptr<ScDocument> pOldUndoDoc;
std::auto_ptr<ScDPObject> pUndoDPObj;
SAL_WNODEPRECATED_DECLARATIONS_POP
if (bRecord)
pUndoDPObj.reset(new ScDPObject(rDPObj)); // copy old settings for undo
ScDocument* pDoc = rDocShell.GetDocument();
if (bRecord && !pDoc->IsUndoEnabled())
bRecord = false;
// delete table
ScRange aRange = rDPObj.GetOutRange();
SCTAB nTab = aRange.aStart.Tab();
if (bRecord)
createUndoDoc(pOldUndoDoc, pDoc, aRange);
pDoc->DeleteAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
aRange.aEnd.Col(), aRange.aEnd.Row(),
nTab, IDF_ALL );
pDoc->RemoveFlagsTab( aRange.aStart.Col(), aRange.aStart.Row(),
aRange.aEnd.Col(), aRange.aEnd.Row(),
nTab, SC_MF_AUTO );
pDoc->GetDPCollection()->FreeTable(&rDPObj); // object is deleted here
rDocShell.PostPaintGridAll(); //! only necessary parts
rDocShell.PostPaint(aRange, PAINT_GRID);
if (bRecord)
{
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoDataPilot(
&rDocShell, pOldUndoDoc.release(), NULL, pUndoDPObj.get(), NULL, false));
// pUndoDPObj is copied
}
aModificator.SetDocumentModified();
return true;
}
bool ScDBDocFunc::CreatePivotTable(const ScDPObject& rDPObj, bool bRecord, bool bApi)
{
ScDocShellModificator aModificator(rDocShell);
WaitObject aWait(rDocShell.GetActiveDialogParent());
// At least one cell in the output range should be editable. Check in advance.
if (!isEditable(rDocShell, ScRange(rDPObj.GetOutRange().aStart), bApi))
return false;
SAL_WNODEPRECATED_DECLARATIONS_PUSH
std::auto_ptr<ScDocument> pNewUndoDoc;
SAL_WNODEPRECATED_DECLARATIONS_POP
ScDocument* pDoc = rDocShell.GetDocument();
if (bRecord && !pDoc->IsUndoEnabled())
bRecord = false;
// output range must be set at pNewObj
SAL_WNODEPRECATED_DECLARATIONS_PUSH
std::auto_ptr<ScDPObject> pDestObj(new ScDPObject(rDPObj));
SAL_WNODEPRECATED_DECLARATIONS_POP
ScDPObject& rDestObj = *pDestObj;
// #i94570# When changing the output position in the dialog, a new table is created
// with the settings from the old table, including the name.
// So we have to check for duplicate names here (before inserting).
if (pDoc->GetDPCollection()->GetByName(rDestObj.GetName()))
rDestObj.SetName(OUString()); // ignore the invalid name, create a new name below
if (!pDoc->GetDPCollection()->InsertNewTable(pDestObj.release()))
// Insertion into collection failed.
return false;
rDestObj.ReloadGroupTableData();
rDestObj.SyncAllDimensionMembers();
rDestObj.InvalidateData(); // before getting the new output area
// make sure the table has a name (not set by dialog)
if (rDestObj.GetName().isEmpty())
rDestObj.SetName(pDoc->GetDPCollection()->CreateNewName());
bool bOverflow = false;
ScRange aNewOut = rDestObj.GetNewOutputRange(bOverflow);
if (bOverflow)
{
if (!bApi)
rDocShell.ErrorMessage(STR_PIVOT_ERROR);
return false;
}
{
ScEditableTester aTester(pDoc, aNewOut);
if (!aTester.IsEditable())
{
// destination area isn't editable
if (!bApi)
rDocShell.ErrorMessage(aTester.GetMessageId());
return false;
}
}
// test if new output area is empty except for old area
if (!bApi)
{
bool bEmpty = pDoc->IsBlockEmpty(
aNewOut.aStart.Tab(), aNewOut.aStart.Col(), aNewOut.aStart.Row(),
aNewOut.aEnd.Col(), aNewOut.aEnd.Row());
if (!bEmpty)
{
QueryBox aBox(
rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY));
if (aBox.Execute() == RET_NO)
{
//! like above (not editable)
return false;
}
}
}
if (bRecord)
createUndoDoc(pNewUndoDoc, pDoc, aNewOut);
rDestObj.Output(aNewOut.aStart);
rDocShell.PostPaintGridAll(); //! only necessary parts
if (bRecord)
{
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoDataPilot(&rDocShell, NULL, pNewUndoDoc.release(), NULL, &rDestObj, false));
}
// notify API objects
pDoc->BroadcastUno(ScDataPilotModifiedHint(rDestObj.GetName()));
aModificator.SetDocumentModified();
return true;
}
bool ScDBDocFunc::UpdatePivotTable(ScDPObject& rDPObj, bool bRecord, bool bApi)
{
ScDocShellModificator aModificator( rDocShell );
WaitObject aWait( rDocShell.GetActiveDialogParent() );
if (!isEditable(rDocShell, rDPObj.GetOutRange(), bApi))
return false;
SAL_WNODEPRECATED_DECLARATIONS_PUSH
std::auto_ptr<ScDocument> pOldUndoDoc;
std::auto_ptr<ScDocument> pNewUndoDoc;
SAL_WNODEPRECATED_DECLARATIONS_POP
ScDPObject aUndoDPObj(rDPObj); // For undo or revert on failure.
ScDocument* pDoc = rDocShell.GetDocument();
if (bRecord && !pDoc->IsUndoEnabled())
bRecord = false;
if (bRecord)
createUndoDoc(pOldUndoDoc, pDoc, rDPObj.GetOutRange());
rDPObj.SetAllowMove(false);
rDPObj.ReloadGroupTableData();
if (!rDPObj.SyncAllDimensionMembers())
return false;
rDPObj.InvalidateData(); // before getting the new output area
// make sure the table has a name (not set by dialog)
if (rDPObj.GetName().isEmpty())
rDPObj.SetName( pDoc->GetDPCollection()->CreateNewName() );
ScRange aNewOut;
if (!checkNewOutputRange(rDPObj, rDocShell, aNewOut, bApi))
{
rDPObj = aUndoDPObj;
return false;
}
// test if new output area is empty except for old area
if (!bApi)
{
if (!lcl_EmptyExcept(pDoc, aNewOut, rDPObj.GetOutRange()))
{
QueryBox aBox( rDocShell.GetActiveDialogParent(), WinBits(WB_YES_NO | WB_DEF_YES),
ScGlobal::GetRscString(STR_PIVOT_NOTEMPTY) );
if (aBox.Execute() == RET_NO)
{
rDPObj = aUndoDPObj;
return false;
}
}
}
if (bRecord)
createUndoDoc(pNewUndoDoc, pDoc, aNewOut);
rDPObj.Output(aNewOut.aStart);
rDocShell.PostPaintGridAll(); //! only necessary parts
if (bRecord)
{
rDocShell.GetUndoManager()->AddUndoAction(
new ScUndoDataPilot(
&rDocShell, pOldUndoDoc.release(), pNewUndoDoc.release(), &aUndoDPObj, &rDPObj, false));
}
// notify API objects
pDoc->BroadcastUno( ScDataPilotModifiedHint(rDPObj.GetName()) );
aModificator.SetDocumentModified();
return true;
}
sal_uLong ScDBDocFunc::RefreshPivotTables(ScDPObject* pDPObj, bool bApi)
{
ScDPCollection* pDPs = rDocShell.GetDocument()->GetDPCollection();
if (!pDPs)
return 0;
std::set<ScDPObject*> aRefs;
sal_uLong nErrId = pDPs->ReloadCache(pDPObj, aRefs);
if (nErrId)
return nErrId;
std::set<ScDPObject*>::iterator it = aRefs.begin(), itEnd = aRefs.end();
for (; it != itEnd; ++it)
{
ScDPObject* pObj = *it;
// This action is intentionally not undoable since it modifies cache.
UpdatePivotTable(*pObj, false, bApi);
}
return 0;
}
void ScDBDocFunc::RefreshPivotTableGroups(ScDPObject* pDPObj)
{
if (!pDPObj)
return;
ScDPCollection* pDPs = rDocShell.GetDocument()->GetDPCollection();
if (!pDPs)
return;
ScDPSaveData* pSaveData = pDPObj->GetSaveData();
if (!pSaveData)
return;
std::set<ScDPObject*> aRefs;
if (!pDPs->ReloadGroupsInCache(pDPObj, aRefs))
return;
// We allow pDimData being NULL.
const ScDPDimensionSaveData* pDimData = pSaveData->GetExistingDimensionData();
std::set<ScDPObject*>::iterator it = aRefs.begin(), itEnd = aRefs.end();
for (; it != itEnd; ++it)
{
ScDPObject* pObj = *it;
if (pObj != pDPObj)
{
pSaveData = pObj->GetSaveData();
if (pSaveData)
pSaveData->SetDimensionData(pDimData);
}
// This action is intentionally not undoable since it modifies cache.
UpdatePivotTable(*pObj, false, false);
}
}
// database import
void ScDBDocFunc::UpdateImport( const OUString& rTarget, const svx::ODataAccessDescriptor& rDescriptor )
{
// rTarget is the name of a database range
ScDocument* pDoc = rDocShell.GetDocument();
ScDBCollection& rDBColl = *pDoc->GetDBCollection();
const ScDBData* pData = rDBColl.getNamedDBs().findByUpperName(ScGlobal::pCharClass->uppercase(rTarget));
if (!pData)
{
InfoBox aInfoBox(rDocShell.GetActiveDialogParent(),
ScGlobal::GetRscString( STR_TARGETNOTFOUND ) );
aInfoBox.Execute();
return;
}
SCTAB nTab;
SCCOL nDummyCol;
SCROW nDummyRow;
pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow );
ScImportParam aImportParam;
pData->GetImportParam( aImportParam );
OUString sDBName;
OUString sDBTable;
sal_Int32 nCommandType = 0;
sDBName = rDescriptor.getDataSource();
rDescriptor[svx::daCommand] >>= sDBTable;
rDescriptor[svx::daCommandType] >>= nCommandType;
aImportParam.aDBName = sDBName;
aImportParam.bSql = ( nCommandType == sdb::CommandType::COMMAND );
aImportParam.aStatement = sDBTable;
aImportParam.bNative = false;
aImportParam.nType = static_cast<sal_uInt8>( ( nCommandType == sdb::CommandType::QUERY ) ? ScDbQuery : ScDbTable );
aImportParam.bImport = true;
bool bContinue = DoImport( nTab, aImportParam, &rDescriptor, true );
// DB-Operationen wiederholen
ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
if (pViewSh)
{
ScRange aRange;
pData->GetArea(aRange);
pViewSh->MarkRange(aRange); // selektieren
if ( bContinue ) // Fehler beim Import -> Abbruch
{
// interne Operationen, wenn welche gespeichert
if ( pData->HasQueryParam() || pData->HasSortParam() || pData->HasSubTotalParam() )
pViewSh->RepeatDB();
// Pivottabellen die den Bereich als Quelldaten haben
rDocShell.RefreshPivotTables(aRange);
}
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */