| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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 <svx/dataaccessdescriptor.hxx> |
| |
| #include <com/sun/star/sdb/CommandType.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" |
| |
| using namespace ::com::sun::star; |
| |
| // ----------------------------------------------------------------- |
| |
| sal_Bool ScDBDocFunc::AddDBRange( const String& rName, const ScRange& rRange, sal_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. |
| sal_Bool bCompile = !pDoc->IsImportingXML(); |
| |
| if ( bCompile ) |
| pDoc->CompileDBFormula( sal_True ); // CreateFormulaString |
| sal_Bool bOk = pDocColl->Insert( pNew ); |
| if ( bCompile ) |
| pDoc->CompileDBFormula( sal_False ); // CompileFormulaString |
| |
| if (!bOk) |
| { |
| delete pNew; |
| delete pUndoColl; |
| return sal_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 sal_True; |
| } |
| |
| sal_Bool ScDBDocFunc::DeleteDBRange( const String& rName, sal_Bool /* bApi */ ) |
| { |
| sal_Bool bDone = sal_False; |
| ScDocument* pDoc = rDocShell.GetDocument(); |
| ScDBCollection* pDocColl = pDoc->GetDBCollection(); |
| sal_Bool bUndo (pDoc->IsUndoEnabled()); |
| |
| sal_uInt16 nPos = 0; |
| if (pDocColl->SearchName( rName, nPos )) |
| { |
| ScDocShellModificator aModificator( rDocShell ); |
| |
| ScDBCollection* pUndoColl = NULL; |
| if (bUndo) |
| pUndoColl = new ScDBCollection( *pDocColl ); |
| |
| pDoc->CompileDBFormula( sal_True ); // CreateFormulaString |
| pDocColl->AtFree( nPos ); |
| pDoc->CompileDBFormula( sal_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 = sal_True; |
| } |
| |
| return bDone; |
| } |
| |
| sal_Bool ScDBDocFunc::RenameDBRange( const String& rOld, const String& rNew, sal_Bool /* bApi */ ) |
| { |
| sal_Bool bDone = sal_False; |
| ScDocument* pDoc = rDocShell.GetDocument(); |
| ScDBCollection* pDocColl = pDoc->GetDBCollection(); |
| sal_Bool bUndo (pDoc->IsUndoEnabled()); |
| |
| sal_uInt16 nPos = 0; |
| sal_uInt16 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( sal_True ); // CreateFormulaString |
| pDocColl->AtFree( nPos ); |
| sal_Bool bInserted = pDocColl->Insert( pNewData ); |
| if (!bInserted) // Fehler -> alten Zustand wiederherstellen |
| { |
| delete pNewData; |
| pDoc->SetDBCollection( pUndoColl ); // gehoert dann dem Dokument |
| } |
| pDoc->CompileDBFormula( sal_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 = sal_True; |
| } |
| } |
| |
| return bDone; |
| } |
| |
| sal_Bool ScDBDocFunc::ModifyDBData( const ScDBData& rNewData, sal_Bool /* bApi */ ) |
| { |
| sal_Bool bDone = sal_False; |
| ScDocument* pDoc = rDocShell.GetDocument(); |
| ScDBCollection* pDocColl = pDoc->GetDBCollection(); |
| sal_Bool bUndo (pDoc->IsUndoEnabled()); |
| |
| sal_uInt16 nPos = 0; |
| if (pDocColl->SearchName( rNewData.GetName(), nPos )) |
| { |
| ScDocShellModificator aModificator( rDocShell ); |
| |
| ScDBData* pData = (*pDocColl)[nPos]; |
| |
| ScRange aOldRange, aNewRange; |
| pData->GetArea(aOldRange); |
| rNewData.GetArea(aNewRange); |
| sal_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 = sal_True; |
| } |
| |
| return bDone; |
| } |
| |
| // ----------------------------------------------------------------- |
| |
| sal_Bool ScDBDocFunc::RepeatDB( const String& rDBName, sal_Bool bRecord, sal_Bool bApi ) |
| { |
| //! auch fuer ScDBFunc::RepeatDB benutzen! |
| |
| sal_Bool bDone = sal_False; |
| ScDocument* pDoc = rDocShell.GetDocument(); |
| if (bRecord && !pDoc->IsUndoEnabled()) |
| bRecord = sal_False; |
| ScDBCollection* pColl = pDoc->GetDBCollection(); |
| sal_uInt16 nIndex; |
| if ( pColl && pColl->SearchName( rDBName, nIndex ) ) |
| { |
| ScDBData* pDBData = (*pColl)[nIndex]; |
| |
| ScQueryParam aQueryParam; |
| pDBData->GetQueryParam( aQueryParam ); |
| sal_Bool bQuery = aQueryParam.GetEntry(0).bDoQuery; |
| |
| ScSortParam aSortParam; |
| pDBData->GetSortParam( aSortParam ); |
| sal_Bool bSort = aSortParam.bDoSort[0]; |
| |
| ScSubTotalParam aSubTotalParam; |
| pDBData->GetSubTotalParam( aSubTotalParam ); |
| sal_Bool bSubTotal = aSubTotalParam.bGroupActive[0] && !aSubTotalParam.bRemoveOnly; |
| |
| if ( bQuery || bSort || bSubTotal ) |
| { |
| sal_Bool bQuerySize = sal_False; |
| ScRange aOldQuery; |
| ScRange aNewQuery; |
| if (bQuery && !aQueryParam.bInplace) |
| { |
| ScDBData* pDest = pDoc->GetDBAtCursor( aQueryParam.nDestCol, aQueryParam.nDestRow, |
| aQueryParam.nDestTab, sal_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, sal_True, sal_True ); |
| pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, |
| nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, |
| IDF_NONE, sal_False, pUndoDoc ); |
| pDoc->CopyToDocument( 0, static_cast<SCROW>(nOutStartRow), |
| nTab, MAXCOL, static_cast<SCROW>(nOutEndRow), nTab, |
| IDF_NONE, sal_False, pUndoDoc ); |
| } |
| else |
| pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True ); |
| |
| // Datenbereich sichern - incl. Filter-Ergebnis |
| pDoc->CopyToDocument( 0,nStartRow,nTab, MAXCOL,nEndRow,nTab, IDF_ALL, sal_False, pUndoDoc ); |
| |
| // alle Formeln wegen Referenzen |
| pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, IDF_FORMULA, sal_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 = sal_True; // wird unten wieder zurueckgesetzt |
| DoSubTotals( nTab, aSubTotalParam, NULL, sal_False, bApi ); |
| } |
| |
| if (bSort) |
| { |
| pDBData->GetSortParam( aSortParam ); // Bereich kann sich geaendert haben |
| Sort( nTab, aSortParam, sal_False, sal_False, bApi ); |
| } |
| if (bQuery) |
| { |
| pDBData->GetQueryParam( aQueryParam ); // Bereich kann sich geaendert haben |
| ScRange aAdvSource; |
| if (pDBData->GetAdvancedQuerySource(aAdvSource)) |
| Query( nTab, aQueryParam, &aAdvSource, sal_False, bApi ); |
| else |
| Query( nTab, aQueryParam, NULL, sal_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 = sal_False; |
| DoSubTotals( nTab, aSubTotalParam, NULL, sal_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, sal_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 = sal_True; |
| } |
| else if (!bApi) // "Keine Operationen auszufuehren" |
| rDocShell.ErrorMessage(STR_MSSG_REPEATDB_0); |
| } |
| |
| return bDone; |
| } |
| |
| // ----------------------------------------------------------------- |
| |
| sal_Bool ScDBDocFunc::Sort( SCTAB nTab, const ScSortParam& rSortParam, |
| sal_Bool bRecord, sal_Bool bPaint, sal_Bool bApi ) |
| { |
| ScDocShellModificator aModificator( rDocShell ); |
| |
| ScDocument* pDoc = rDocShell.GetDocument(); |
| if (bRecord && !pDoc->IsUndoEnabled()) |
| bRecord = sal_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 sal_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 = sal_False; |
| ScSortParam aLocalParam( rSortParam ); |
| if ( bCopy ) |
| { |
| aLocalParam.MoveToDest(); |
| if ( !ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) ) |
| { |
| if (!bApi) |
| rDocShell.ErrorMessage(STR_PASTE_FULL); |
| return sal_False; |
| } |
| |
| nTab = rSortParam.nDestTab; |
| pDestData = pDoc->GetDBAtCursor( rSortParam.nDestCol, rSortParam.nDestRow, |
| rSortParam.nDestTab, sal_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 sal_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 sal_False; |
| } |
| |
| |
| // ausfuehren |
| |
| WaitObject aWait( rDocShell.GetActiveDialogParent() ); |
| |
| sal_Bool bRepeatQuery = sal_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 = sal_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, sal_False, sal_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, sal_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, sal_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, sal_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(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, sal_False, sal_False, sal_False, sal_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 ); |
| |
| sal_Bool bSave = sal_True; |
| if (bCopy) |
| { |
| ScSortParam aOldSortParam; |
| pDBData->GetSortParam( aOldSortParam ); |
| if ( aOldSortParam.bDoSort[0] && aOldSortParam.bInplace ) // Inplace-Sortierung gemerkt? |
| { |
| bSave = sal_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) |
| { |
| 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( 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 sal_True; |
| } |
| |
| // ----------------------------------------------------------------- |
| |
| sal_Bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam, |
| const ScRange* pAdvSource, sal_Bool bRecord, sal_Bool bApi ) |
| { |
| ScDocShellModificator aModificator( rDocShell ); |
| |
| ScDocument* pDoc = rDocShell.GetDocument(); |
| if (bRecord && !pDoc->IsUndoEnabled()) |
| bRecord = sal_False; |
| ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rQueryParam.nCol1, rQueryParam.nRow1, |
| rQueryParam.nCol2, rQueryParam.nRow2 ); |
| if (!pDBData) |
| { |
| DBG_ERROR( "Query: keine DBData" ); |
| return sal_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 = sal_False; |
| aOldQuery.bDuplicate = sal_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 = sal_False; // Zielgroesse anpassen (einf./loeschen) |
| SCCOL nFormulaCols = 0; // nur bei bDoSize |
| sal_Bool bKeepFmt = sal_False; |
| ScRange aOldDest; |
| ScRange aDestTotal; |
| if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 && |
| rQueryParam.nDestRow == rQueryParam.nRow1 && rQueryParam.nDestTab == nTab ) |
| bCopy = sal_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 sal_False; |
| } |
| |
| ScEditableTester aTester( pDoc, nDestTab, aLocalParam.nCol1,aLocalParam.nRow1, |
| aLocalParam.nCol2,aLocalParam.nRow2); |
| if (!aTester.IsEditable()) |
| { |
| if (!bApi) |
| rDocShell.ErrorMessage(aTester.GetMessageId()); |
| return sal_False; |
| } |
| |
| pDestData = pDoc->GetDBAtCursor( rQueryParam.nDestCol, rQueryParam.nDestRow, |
| rQueryParam.nDestTab, sal_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 sal_False; |
| } |
| } |
| } |
| |
| // ausfuehren |
| |
| WaitObject aWait( rDocShell.GetActiveDialogParent() ); |
| |
| sal_Bool bKeepSub = sal_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, sal_False, sal_True ); |
| pDoc->CopyToDocument( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, |
| aLocalParam.nCol2, aLocalParam.nRow2, nDestTab, |
| IDF_ALL, sal_False, pUndoDoc ); |
| // Attribute sichern, falls beim Filtern mitkopiert |
| |
| if (pDestData) |
| { |
| pDoc->CopyToDocument( aOldDest, IDF_ALL, sal_False, pUndoDoc ); |
| pOld = &aOldDest; |
| } |
| } |
| else |
| { |
| pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, sal_True ); |
| pDoc->CopyToDocument( 0, rQueryParam.nRow1, nTab, MAXCOL, rQueryParam.nRow2, nTab, |
| IDF_NONE, sal_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, sal_False, sal_True ); |
| pDoc->CopyToDocument( aAttribRange, IDF_ATTRIB, sal_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, sal_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, sal_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, sal_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 |
| |
| sal_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 sal_True; |
| } |
| |
| // ----------------------------------------------------------------- |
| |
| sal_Bool ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam, |
| const ScSortParam* pForceNewSort, sal_Bool bRecord, sal_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 = sal_False; |
| |
| ScDocument* pDoc = rDocShell.GetDocument(); |
| if (bRecord && !pDoc->IsUndoEnabled()) |
| bRecord = sal_False; |
| ScDBData* pDBData = pDoc->GetDBAtArea( nTab, rParam.nCol1, rParam.nRow1, |
| rParam.nCol2, rParam.nRow2 ); |
| if (!pDBData) |
| { |
| DBG_ERROR( "SubTotals: keine DBData" ); |
| return sal_False; |
| } |
| |
| ScEditableTester aTester( pDoc, nTab, 0,rParam.nRow1+1, MAXCOL,MAXROW ); |
| if (!aTester.IsEditable()) |
| { |
| if (!bApi) |
| rDocShell.ErrorMessage(aTester.GetMessageId()); |
| return sal_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 sal_False; |
| } |
| |
| sal_Bool bOk = sal_True; |
| sal_Bool bDelete = sal_False; |
| if (rParam.bReplace) |
| if (pDoc->TestRemoveSubTotals( nTab, rParam )) |
| { |
| bDelete = sal_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 |
| { |
| sal_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, sal_True, sal_True ); |
| pDoc->CopyToDocument( static_cast<SCCOL>(nOutStartCol), 0, nTab, static_cast<SCCOL>(nOutEndCol), MAXROW, nTab, IDF_NONE, sal_False, pUndoDoc ); |
| pDoc->CopyToDocument( 0, nOutStartRow, nTab, MAXCOL, nOutEndRow, nTab, IDF_NONE, sal_False, pUndoDoc ); |
| } |
| else |
| pUndoDoc->InitUndo( pDoc, nTab, nTab, sal_False, bOldFilter ); |
| |
| // Datenbereich sichern - incl. Filter-Ergebnis |
| pDoc->CopyToDocument( 0,rParam.nRow1+1,nTab, MAXCOL,rParam.nRow2,nTab, |
| IDF_ALL, sal_False, pUndoDoc ); |
| |
| // alle Formeln wegen Referenzen |
| pDoc->CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTabCount-1, |
| IDF_FORMULA, sal_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 ); |
| 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, sal_False, sal_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; |
| } |
| |
| //================================================================== |
| |
| sal_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 sal_False; // cell found |
| } |
| pCell = aIter.GetNext(); |
| } |
| |
| return sal_True; // nothing found - empty |
| } |
| |
| sal_Bool ScDBDocFunc::DataPilotUpdate( ScDPObject* pOldObj, const ScDPObject* pNewObj, |
| sal_Bool bRecord, sal_Bool bApi, sal_Bool bAllowMove ) |
| { |
| ScDocShellModificator aModificator( rDocShell ); |
| WaitObject aWait( rDocShell.GetActiveDialogParent() ); |
| |
| sal_Bool bDone = sal_False; |
| sal_Bool bUndoSelf = sal_False; |
| sal_uInt16 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 = sal_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, sal_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 = sal_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, sal_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(sal_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() ); |
| |
| sal_Bool bOverflow = sal_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 = sal_True; |
| } |
| } |
| |
| if ( bOverflow ) |
| { |
| // like with STR_PROTECTIONERR, use undo to reverse everything |
| DBG_ASSERT( bRecord, "DataPilotUpdate: can't undo" ); |
| bUndoSelf = sal_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 = sal_True; |
| nErrId = aTester.GetMessageId(); |
| } |
| } |
| |
| // test if new output area is empty except for old area |
| if ( !bApi ) |
| { |
| sal_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 = sal_True; |
| } |
| } |
| } |
| |
| if ( bRecord ) |
| { |
| SCTAB nTab = aNewOut.aStart.Tab(); |
| pNewUndoDoc = new ScDocument( SCDOCMODE_UNDO ); |
| pNewUndoDoc->InitUndo( pDoc, nTab, nTab ); |
| pDoc->CopyToDocument( aNewOut, IDF_ALL, sal_False, pNewUndoDoc ); |
| } |
| |
| pDestObj->Output( aNewOut.aStart ); |
| |
| rDocShell.PostPaintGridAll(); //! only necessary parts |
| bDone = sal_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 = sal_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; |
| } |
| |
| //================================================================== |
| // |
| // database import |
| |
| void ScDBDocFunc::UpdateImport( const String& rTarget, const svx::ODataAccessDescriptor& rDescriptor ) |
| { |
| // rTarget is the name of a database range |
| |
| ScDocument* pDoc = rDocShell.GetDocument(); |
| ScDBCollection& rDBColl = *pDoc->GetDBCollection(); |
| ScDBData* pData = NULL; |
| ScImportParam aImportParam; |
| sal_Bool bFound = sal_False; |
| sal_uInt16 nCount = rDBColl.GetCount(); |
| for (sal_uInt16 i=0; i<nCount && !bFound; i++) |
| { |
| pData = rDBColl[i]; |
| if (pData->GetName() == rTarget) |
| bFound = sal_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 ); |
| |
| rtl::OUString sDBName; |
| rtl::OUString sDBTable; |
| sal_Int32 nCommandType = 0; |
| rDescriptor[svx::daDataSource] >>= sDBName; |
| rDescriptor[svx::daCommand] >>= sDBTable; |
| rDescriptor[svx::daCommandType] >>= nCommandType; |
| |
| aImportParam.aDBName = sDBName; |
| aImportParam.bSql = ( nCommandType == sdb::CommandType::COMMAND ); |
| aImportParam.aStatement = sDBTable; |
| aImportParam.bNative = sal_False; |
| aImportParam.nType = static_cast<sal_uInt8>( ( nCommandType == sdb::CommandType::QUERY ) ? ScDbQuery : ScDbTable ); |
| aImportParam.bImport = sal_True; |
| |
| sal_Bool bContinue = DoImport( nTab, aImportParam, &rDescriptor, sal_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); |
| } |
| } |
| } |
| |
| |
| |
| |