diff options
Diffstat (limited to 'sc/source/ui/docshell/dbdocfun.cxx')
-rw-r--r-- | sc/source/ui/docshell/dbdocfun.cxx | 1503 |
1 files changed, 1503 insertions, 0 deletions
diff --git a/sc/source/ui/docshell/dbdocfun.cxx b/sc/source/ui/docshell/dbdocfun.cxx new file mode 100644 index 000000000000..cf149d258062 --- /dev/null +++ b/sc/source/ui/docshell/dbdocfun.cxx @@ -0,0 +1,1503 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + + + +// INCLUDE --------------------------------------------------------------- + +#include <sfx2/app.hxx> +#include <vcl/msgbox.hxx> +#include <vcl/waitobj.hxx> + +#include <com/sun/star/sdbc/XResultSet.hpp> + +#include "dbdocfun.hxx" +#include "sc.hrc" +#include "dbcolect.hxx" +#include "undodat.hxx" +#include "docsh.hxx" +#include "docfunc.hxx" +#include "globstr.hrc" +#include "tabvwsh.hxx" +#include "patattr.hxx" +#include "rangenam.hxx" +#include "olinetab.hxx" +#include "dpobject.hxx" +#include "dociter.hxx" // for lcl_EmptyExcept +#include "cell.hxx" // for lcl_EmptyExcept +#include "editable.hxx" +#include "attrib.hxx" +#include "drwlayer.hxx" +#include "dpshttab.hxx" +#include "hints.hxx" + +// ----------------------------------------------------------------- + +BOOL ScDBDocFunc::AddDBRange( const String& rName, const ScRange& rRange, BOOL /* bApi */ ) +{ + + ScDocShellModificator aModificator( rDocShell ); + + ScDocument* pDoc = rDocShell.GetDocument(); + ScDBCollection* pDocColl = pDoc->GetDBCollection(); + 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(); + + if ( bCompile ) + pDoc->CompileDBFormula( TRUE ); // CreateFormulaString + BOOL bOk = pDocColl->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 String& rName, BOOL /* bApi */ ) +{ + BOOL bDone = FALSE; + ScDocument* pDoc = rDocShell.GetDocument(); + ScDBCollection* pDocColl = pDoc->GetDBCollection(); + BOOL bUndo (pDoc->IsUndoEnabled()); + + USHORT nPos = 0; + if (pDocColl->SearchName( rName, nPos )) + { + ScDocShellModificator aModificator( rDocShell ); + + ScDBCollection* pUndoColl = NULL; + if (bUndo) + pUndoColl = new ScDBCollection( *pDocColl ); + + pDoc->CompileDBFormula( TRUE ); // CreateFormulaString + pDocColl->AtFree( nPos ); + 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 String& rOld, const String& rNew, BOOL /* bApi */ ) +{ + BOOL bDone = FALSE; + ScDocument* pDoc = rDocShell.GetDocument(); + ScDBCollection* pDocColl = pDoc->GetDBCollection(); + BOOL bUndo (pDoc->IsUndoEnabled()); + + USHORT nPos = 0; + USHORT nDummy = 0; + if ( pDocColl->SearchName( rOld, nPos ) && + !pDocColl->SearchName( rNew, nDummy ) ) + { + ScDocShellModificator aModificator( rDocShell ); + + ScDBData* pData = (*pDocColl)[nPos]; + ScDBData* pNewData = new ScDBData(*pData); + pNewData->SetName(rNew); + + ScDBCollection* pUndoColl = new ScDBCollection( *pDocColl ); + + pDoc->CompileDBFormula( TRUE ); // CreateFormulaString + pDocColl->AtFree( nPos ); + BOOL bInserted = pDocColl->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 /* bApi */ ) +{ + BOOL bDone = FALSE; + ScDocument* pDoc = rDocShell.GetDocument(); + ScDBCollection* pDocColl = pDoc->GetDBCollection(); + BOOL bUndo (pDoc->IsUndoEnabled()); + + USHORT nPos = 0; + if (pDocColl->SearchName( rNewData.GetName(), nPos )) + { + ScDocShellModificator aModificator( rDocShell ); + + ScDBData* pData = (*pDocColl)[nPos]; + + 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 String& rDBName, BOOL bRecord, BOOL bApi ) +{ + //! auch fuer ScDBFunc::RepeatDB benutzen! + + BOOL bDone = FALSE; + ScDocument* pDoc = rDocShell.GetDocument(); + if (bRecord && !pDoc->IsUndoEnabled()) + bRecord = FALSE; + ScDBCollection* pColl = pDoc->GetDBCollection(); + USHORT nIndex; + if ( pColl && pColl->SearchName( rDBName, nIndex ) ) + { + ScDBData* pDBData = (*pColl)[nIndex]; + + ScQueryParam aQueryParam; + pDBData->GetQueryParam( aQueryParam ); + BOOL bQuery = aQueryParam.GetEntry(0).bDoQuery; + + ScSortParam aSortParam; + pDBData->GetSortParam( aSortParam ); + BOOL bSort = aSortParam.bDoSort[0]; + + ScSubTotalParam aSubTotalParam; + pDBData->GetSubTotalParam( aSubTotalParam ); + BOOL bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly; + + if ( bQuery || bSort || bSubTotal ) + { + 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 = 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->GetCount()) + pUndoRange = new ScRangeName( *pDocRange ); + ScDBCollection* pDocDB = pDoc->GetDBCollection(); + if (pDocDB->GetCount()) + 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( 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) + { + DBG_ERROR( "Sort: keine DBData" ); + return FALSE; + } + + ScDBData* pDestData = NULL; + ScRange aOldDest; + 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() ); + + BOOL bRepeatQuery = FALSE; // bestehenden Filter wiederholen? + ScQueryParam aQueryParam; + pDBData->GetQueryParam( aQueryParam ); + if ( aQueryParam.GetEntry(0).bDoQuery ) + bRepeatQuery = 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->GetCount()) + pUndoDB = new ScDBCollection( *pDocDB ); + + pUndoAction = new ScUndoSort( &rDocShell, nTab, rSortParam, bRepeatQuery, pUndoDoc, pUndoDB, pR ); + rDocShell.GetUndoManager()->AddUndoAction( pUndoAction ); + + // #i59745# collect all drawing undo actions affecting cell note captions + if( pDrawLayer ) + pDrawLayer->BeginCalcUndo(); + } + + 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 ); + } + + // #105780# don't call ScDocument::Sort with an empty SortParam (may be empty here if bCopy is set) + if ( aLocalParam.bDoSort[0] ) + pDoc->Sort( nTab, aLocalParam, bRepeatQuery ); + + BOOL bSave = TRUE; + if (bCopy) + { + ScSortParam aOldSortParam; + pDBData->GetSortParam( aOldSortParam ); + if ( aOldSortParam.bDoSort[0] && aOldSortParam.bInplace ) // Inplace-Sortierung gemerkt? + { + 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 + { + DBG_ERROR("Zielbereich nicht da"); + } + } + + ScRange aDirtyRange( aLocalParam.nCol1, aLocalParam.nRow1, nTab, + aLocalParam.nCol2, aLocalParam.nRow2, nTab ); + pDoc->SetDirty( aDirtyRange ); + + if (bPaint) + { + USHORT 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( 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) + { + DBG_ERROR( "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 + BOOL bCopy = !rQueryParam.bInplace; // kopiert wird in Table::Query + ScDBData* pDestData = NULL; // Bereich, in den kopiert wird + BOOL bDoSize = FALSE; // Zielgroesse anpassen (einf./loeschen) + SCCOL nFormulaCols = 0; // nur bei bDoSize + 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() ); + + 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 = 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->GetCount()) + 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 ); // 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 ); + pDoc->Fill( aLocalParam.nCol2+1, nFStartY, + aLocalParam.nCol2+nFormulaCols, nFStartY, 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 ); + DBG_ASSERT(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 + { + DBG_ERROR("Zielbereich nicht da"); + } + } + + if (!bCopy) + { + pDoc->InvalidatePageBreaks(nTab); + pDoc->UpdatePageBreaks( nTab ); + } + + // #i23299# because of Subtotal functions, the whole rows must be set dirty + ScRange aDirtyRange( 0 , aLocalParam.nRow1, nDestTab, + MAXCOL, aLocalParam.nRow2, nDestTab ); + pDoc->SetDirty( 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( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, + nEndX, nEndY, nDestTab, PAINT_GRID ); + } + else + rDocShell.PostPaint( 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 (?) + + BOOL bDo = !rParam.bRemoveOnly; // FALSE = nur loeschen + 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) + { + DBG_ERROR( "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; + } + + BOOL bOk = TRUE; + BOOL bDelete = FALSE; + if (rParam.bReplace) + if (pDoc->TestRemoveSubTotals( nTab, rParam )) + { + bDelete = TRUE; + 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; + SCTAB nTabCount = 0; // fuer Referenz-Undo + + if (bRecord) // alte Daten sichern + { + BOOL bOldFilter = bDo && rParam.bDoSort; + + 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->GetCount()) + pUndoRange = new ScRangeName( *pDocRange ); + ScDBCollection* pDocDB = pDoc->GetDBCollection(); + if (pDocDB->GetCount()) + 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 ); + BOOL bSuccess = 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 ); + } + 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( 0,0,nTab, MAXCOL,MAXROW,nTab, + PAINT_GRID | PAINT_LEFT | PAINT_TOP | PAINT_SIZE ); + aModificator.SetDocumentModified(); + + bRet = bSuccess; + } + return bRet; +} + +//================================================================== + +BOOL lcl_EmptyExcept( ScDocument* pDoc, const ScRange& rRange, const ScRange& rExcept ) +{ + ScCellIterator aIter( pDoc, rRange ); + ScBaseCell* pCell = aIter.GetFirst(); + while (pCell) + { + if ( !pCell->IsBlank() ) // real content? + { + if ( !rExcept.In( ScAddress( aIter.GetCol(), aIter.GetRow(), aIter.GetTab() ) ) ) + return FALSE; // cell found + } + pCell = aIter.GetNext(); + } + + return TRUE; // nothing found - empty +} + +BOOL ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj, + BOOL bRecord, BOOL bApi, BOOL bAllowMove ) +{ + ScDocShellModificator aModificator( rDocShell ); + WaitObject aWait( rDocShell.GetActiveDialogParent() ); + + BOOL bDone = FALSE; + BOOL bUndoSelf = FALSE; + USHORT nErrId = 0; + + ScDocument* pOldUndoDoc = NULL; + ScDocument* pNewUndoDoc = NULL; + ScDPObject* pUndoDPObj = NULL; + if ( bRecord && pOldObj ) + pUndoDPObj = new ScDPObject( *pOldObj ); // copy old settings for undo + + ScDocument* pDoc = rDocShell.GetDocument(); + if (bRecord && !pDoc->IsUndoEnabled()) + bRecord = FALSE; + if ( !rDocShell.IsEditable() || pDoc->GetChangeTrack() ) + { + // not recorded -> disallow + //! different error messages? + + nErrId = STR_PROTECTIONERR; + } + if ( pOldObj && !nErrId ) + { + ScRange aOldOut = pOldObj->GetOutRange(); + ScEditableTester aTester( pDoc, aOldOut ); + if ( !aTester.IsEditable() ) + nErrId = aTester.GetMessageId(); + } + if ( pNewObj && !nErrId ) + { + // at least one cell at the output position must be editable + // -> check in advance + // (start of output range in pNewObj is valid) + + ScRange aNewStart( pNewObj->GetOutRange().aStart ); + ScEditableTester aTester( pDoc, aNewStart ); + if ( !aTester.IsEditable() ) + nErrId = aTester.GetMessageId(); + } + + ScDPObject* pDestObj = NULL; + if ( !nErrId ) + { + if ( pOldObj && !pNewObj ) + { + // delete table + + ScRange aRange = pOldObj->GetOutRange(); + SCTAB nTab = aRange.aStart.Tab(); + + if ( bRecord ) + { + pOldUndoDoc = new ScDocument( SCDOCMODE_UNDO ); + pOldUndoDoc->InitUndo( pDoc, nTab, nTab ); + pDoc->CopyToDocument( aRange, IDF_ALL, FALSE, pOldUndoDoc ); + } + + 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( pOldObj ); // object is deleted here + + rDocShell.PostPaintGridAll(); //! only necessary parts + rDocShell.PostPaint( aRange.aStart.Col(), aRange.aStart.Row(), nTab, + aRange.aEnd.Col(), aRange.aEnd.Row(), nTab, + PAINT_GRID ); + bDone = TRUE; + } + else if ( pNewObj ) + { + if ( pOldObj ) + { + if ( bRecord ) + { + ScRange aRange = pOldObj->GetOutRange(); + SCTAB nTab = aRange.aStart.Tab(); + pOldUndoDoc = new ScDocument( SCDOCMODE_UNDO ); + pOldUndoDoc->InitUndo( pDoc, nTab, nTab ); + pDoc->CopyToDocument( aRange, IDF_ALL, FALSE, pOldUndoDoc ); + } + + if ( pNewObj == pOldObj ) + { + // refresh only - no settings modified + } + else + { + pNewObj->WriteSourceDataTo( *pOldObj ); // copy source data + + ScDPSaveData* pData = pNewObj->GetSaveData(); + DBG_ASSERT( pData, "no SaveData from living DPObject" ); + if ( pData ) + pOldObj->SetSaveData( *pData ); // copy SaveData + } + + pDestObj = pOldObj; + pDestObj->SetAllowMove( bAllowMove ); + } + else + { + // output range must be set at pNewObj + + pDestObj = new ScDPObject( *pNewObj ); + + // #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(pDestObj->GetName()) ) + pDestObj->SetName( String() ); // ignore the invalid name, create a new name below + + pDestObj->SetAlive(TRUE); + if ( !pDoc->GetDPCollection()->InsertNewTable(pDestObj) ) + { + DBG_ERROR("cannot insert DPObject"); + DELETEZ( pDestObj ); + } + } + if ( pDestObj ) + { + // #78541# create new database connection for "refresh" + // (and re-read column entry collections) + // so all changes take effect + if ( pNewObj == pOldObj && pDestObj->IsImportData() ) + pDestObj->InvalidateSource(); + + pDestObj->InvalidateData(); // before getting the new output area + + // make sure the table has a name (not set by dialog) + if ( !pDestObj->GetName().Len() ) + pDestObj->SetName( pDoc->GetDPCollection()->CreateNewName() ); + + BOOL bOverflow = FALSE; + ScRange aNewOut = pDestObj->GetNewOutputRange( bOverflow ); + + //! test for overlap with other data pilot tables + if( pOldObj ) + { + const ScSheetSourceDesc* pSheetDesc = pOldObj->GetSheetDesc(); + if( pSheetDesc && pSheetDesc->aSourceRange.Intersects( aNewOut ) ) + { + ScRange aOldRange = pOldObj->GetOutRange(); + SCsROW nDiff = aOldRange.aStart.Row()-aNewOut.aStart.Row(); + aNewOut.aStart.SetRow( aOldRange.aStart.Row() ); + aNewOut.aEnd.SetRow( aNewOut.aEnd.Row()+nDiff ); + if( !ValidRow( aNewOut.aStart.Row() ) || !ValidRow( aNewOut.aEnd.Row() ) ) + bOverflow = TRUE; + } + } + + if ( bOverflow ) + { + // like with STR_PROTECTIONERR, use undo to reverse everything + DBG_ASSERT( bRecord, "DataPilotUpdate: can't undo" ); + bUndoSelf = TRUE; + nErrId = STR_PIVOT_ERROR; + } + else + { + ScEditableTester aTester( pDoc, aNewOut ); + if ( !aTester.IsEditable() ) + { + // destination area isn't editable + //! reverse everything done so far, don't proceed + + // quick solution: proceed to end, use undo action + // to reverse everything: + DBG_ASSERT( bRecord, "DataPilotUpdate: can't undo" ); + bUndoSelf = TRUE; + nErrId = aTester.GetMessageId(); + } + } + + // test if new output area is empty except for old area + if ( !bApi ) + { + BOOL bEmpty; + if ( pOldObj ) // OutRange of pOldObj (pDestObj) is still old area + bEmpty = lcl_EmptyExcept( pDoc, aNewOut, pOldObj->GetOutRange() ); + else + 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), use undo to reverse everything + DBG_ASSERT( bRecord, "DataPilotUpdate: can't undo" ); + bUndoSelf = TRUE; + } + } + } + + if ( bRecord ) + { + SCTAB nTab = aNewOut.aStart.Tab(); + pNewUndoDoc = new ScDocument( SCDOCMODE_UNDO ); + pNewUndoDoc->InitUndo( pDoc, nTab, nTab ); + pDoc->CopyToDocument( aNewOut, IDF_ALL, FALSE, pNewUndoDoc ); + } + + pDestObj->Output( aNewOut.aStart ); + + rDocShell.PostPaintGridAll(); //! only necessary parts + bDone = TRUE; + } + } + // else nothing (no old, no new) + } + + if ( bRecord && bDone ) + { + SfxUndoAction* pAction = new ScUndoDataPilot( &rDocShell, + pOldUndoDoc, pNewUndoDoc, pUndoDPObj, pDestObj, bAllowMove ); + pOldUndoDoc = NULL; + pNewUndoDoc = NULL; // pointers are used in undo action + // pUndoDPObj is copied + + if (bUndoSelf) + { + // use undo action to restore original state + //! prevent setting the document modified? (ScDocShellModificator) + + pAction->Undo(); + delete pAction; + bDone = FALSE; + } + else + rDocShell.GetUndoManager()->AddUndoAction( pAction ); + } + + delete pOldUndoDoc; // if not used for undo + delete pNewUndoDoc; + delete pUndoDPObj; + + if (bDone) + { + // notify API objects + if (pDestObj) + pDoc->BroadcastUno( ScDataPilotModifiedHint( pDestObj->GetName() ) ); + aModificator.SetDocumentModified(); + } + + if ( nErrId && !bApi ) + rDocShell.ErrorMessage( nErrId ); + + return bDone; +} + +//================================================================== +// +// Datenbank-Import... + +void ScDBDocFunc::UpdateImport( const String& rTarget, const String& rDBName, + const String& rTableName, const String& rStatement, BOOL bNative, + BYTE nType, const ::com::sun::star::uno::Reference< + ::com::sun::star::sdbc::XResultSet >& xResultSet, + const SbaSelectionList* pSelection ) +{ + // Target ist jetzt einfach der Bereichsname + + ScDocument* pDoc = rDocShell.GetDocument(); + ScDBCollection& rDBColl = *pDoc->GetDBCollection(); + ScDBData* pData = NULL; + ScImportParam aImportParam; + BOOL bFound = FALSE; + USHORT nCount = rDBColl.GetCount(); + for (USHORT i=0; i<nCount && !bFound; i++) + { + pData = rDBColl[i]; + if (pData->GetName() == rTarget) + bFound = TRUE; + } + if (!bFound) + { + InfoBox aInfoBox(rDocShell.GetActiveDialogParent(), + ScGlobal::GetRscString( STR_TARGETNOTFOUND ) ); + aInfoBox.Execute(); + return; + } + + SCTAB nTab; + SCCOL nDummyCol; + SCROW nDummyRow; + pData->GetArea( nTab, nDummyCol,nDummyRow,nDummyCol,nDummyRow ); + pData->GetImportParam( aImportParam ); + + BOOL bSql = ( rStatement.Len() != 0 ); + + aImportParam.aDBName = rDBName; + aImportParam.bSql = bSql; + aImportParam.aStatement = bSql ? rStatement : rTableName; + aImportParam.bNative = bNative; + aImportParam.nType = nType; + aImportParam.bImport = TRUE; + BOOL bContinue = DoImport( nTab, aImportParam, xResultSet, pSelection, TRUE ); + + // DB-Operationen wiederholen + + ScTabViewShell* pViewSh = rDocShell.GetBestViewShell(); + if (pViewSh) + { + ScRange aRange; + pData->GetArea(aRange); + pViewSh->MarkRange(aRange); // selektieren + + if ( bContinue ) // #41905# 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); + } + } +} + + + + |