/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice :
*
* Licensed to the Apache Software Foundation ( ASF ) under one or more
* contributor license agreements . See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership . The ASF licenses this file to you under the Apache
* License , Version 2 . 0 ( the " License " ) ; you may not use this file
* except in compliance with the License . You may obtain a copy of
* the License at http : //www.apache.org/licenses/LICENSE-2.0 .
*/
#include <scitems.hxx>
#include <utility>
#include <vcl/virdev.hxx>
#include <editeng/boxitem.hxx>
#include <sfx2/app.hxx>
#include <comphelper/lok.hxx>
#include <osl/diagnose.h>
#include <undoblk.hxx>
#include <undoutil.hxx>
#include <document.hxx>
#include <patattr.hxx>
#include <docsh.hxx>
#include <tabvwsh.hxx>
#include <rangenam.hxx>
#include <rangeutl.hxx>
#include <stlpool.hxx>
#include <stlsheet.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <global.hxx>
#include <target.hxx>
#include <docpool.hxx>
#include <docfunc.hxx>
#include <attrib.hxx>
#include <chgtrack.hxx>
#include <transobj.hxx>
#include <refundo.hxx>
#include <undoolk.hxx>
#include <clipparam.hxx>
#include <rowheightcontext.hxx>
#include <refupdatecontext.hxx>
#include <validat.hxx>
#include <gridwin.hxx>
#include <columnspanset.hxx>
#include <memory>
#include <set>
// TODO:
/*A*/ // SetOptimalHeight on Document, if no View
/*B*/ // linked sheets
/*C*/ // ScArea
//? // check later
ScUndoInsertCells::ScUndoInsertCells( ScDocShell& rNewDocShell,
const ScRange& rRange,
SCTAB nNewCount, std::unique_ptr<SCTAB[]> pNewTabs, std::unique_ptr<SCTAB[]> pNewScenarios,
InsCellCmd eNewCmd, ScDocumentUniquePtr pUndoDocument, std::unique_ptr<ScRefUndoData> pRefData,
bool bNewPartOfPaste ) :
ScMoveUndo( rNewDocShell, std::move(pUndoDocument), std::move(pRefData) ),
aEffRange( rRange ),
nCount( nNewCount ),
pTabs( std::move(pNewTabs) ),
pScenarios( std::move(pNewScenarios) ),
eCmd( eNewCmd ),
bPartOfPaste( bNewPartOfPaste )
{
ScDocument& rDoc = rDocShell.GetDocument();
if (eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER) // whole row?
{
aEffRange.aStart.SetCol(0 );
aEffRange.aEnd.SetCol(rDoc.MaxCol());
}
if (eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER) // whole column?
{
aEffRange.aStart.SetRow(0 );
aEffRange.aEnd.SetRow(rDoc.MaxRow());
}
SetChangeTrack();
}
ScUndoInsertCells::~ScUndoInsertCells()
{
}
OUString ScUndoInsertCells::GetComment() const
{
return ScResId( pPasteUndo ? STR_UNDO_PASTE : STR_UNDO_INSERTCELLS );
}
bool ScUndoInsertCells::Merge( SfxUndoAction* pNextAction )
{
// If a paste undo action has already been added, append (detective) action there.
if ( pPasteUndo )
return pPasteUndo->Merge( pNextAction );
if ( bPartOfPaste )
if ( auto pWrapper = dynamic_cast <ScUndoWrapper*>( pNextAction) )
{
if (dynamic_cast <const ScUndoPaste*>(pWrapper->GetWrappedUndo()))
{
// Store paste action if this is part of paste with inserting cells.
// A list action isn't used because Repeat wouldn't work (insert wrong cells).
// Pass ownership of the wrapped SfxUndoAction* to pPasteUndO
pPasteUndo = pWrapper->ReleaseWrappedUndo();
return true ;
}
}
// Call base class for detective handling
return ScMoveUndo::Merge( pNextAction );
}
void ScUndoInsertCells::SetChangeTrack()
{
ScChangeTrack* pChangeTrack = rDocShell.GetDocument().GetChangeTrack();
if ( pChangeTrack )
{
pChangeTrack->AppendInsert( aEffRange );
nEndChangeAction = pChangeTrack->GetActionMax();
}
else
nEndChangeAction = 0 ;
}
void ScUndoInsertCells::DoChange( const bool bUndo )
{
ScDocument& rDoc = rDocShell.GetDocument();
SCTAB i;
if ( bUndo )
{
ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
if ( pChangeTrack )
pChangeTrack->Undo( nEndChangeAction, nEndChangeAction );
}
else
SetChangeTrack();
// refresh of merged cells has to be after inserting/deleting
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
switch (eCmd)
{
case INS_INSROWS_BEFORE:
case INS_INSROWS_AFTER:
case INS_CELLSDOWN:
for ( i=0 ; i<nCount; i++ )
{
if (bUndo)
rDoc.DeleteRow( aEffRange.aStart.Col(), pTabs[i], aEffRange.aEnd.Col(), pTabs[i]+pScenarios[i],
aEffRange.aStart.Row(), static_cast <SCSIZE>(aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1 ));
else
rDoc.InsertRow( aEffRange.aStart.Col(), pTabs[i], aEffRange.aEnd.Col(), pTabs[i]+pScenarios[i],
aEffRange.aStart.Row(), static_cast <SCSIZE>(aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1 ));
if (pViewShell)
{
const tools::Long nSign = bUndo ? -1 : 1 ;
pViewShell->OnLOKInsertDeleteRow(aEffRange.aStart.Row(), nSign * (aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1 ));
}
}
break ;
case INS_INSCOLS_BEFORE:
case INS_INSCOLS_AFTER:
case INS_CELLSRIGHT:
for ( i=0 ; i<nCount; i++ )
{
if (bUndo)
rDoc.DeleteCol( aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i],
aEffRange.aStart.Col(), static_cast <SCSIZE>(aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1 ));
else
rDoc.InsertCol( aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i],
aEffRange.aStart.Col(), static_cast <SCSIZE>(aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1 ));
if (pViewShell)
{
const tools::Long nSign = bUndo ? -1 : 1 ;
pViewShell->OnLOKInsertDeleteColumn(aEffRange.aStart.Col(), nSign * (aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1 ));
}
}
break ;
default :
{
// added to avoid warnings
}
}
ScRange aWorkRange( aEffRange );
if ( eCmd == INS_CELLSRIGHT ) // only "shift right" requires refresh of the moved area
aWorkRange.aEnd.SetCol(rDoc.MaxCol());
for ( i=0 ; i<nCount; i++ )
{
if ( rDoc.HasAttrib( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), pTabs[i],
aWorkRange.aEnd.Col(), aWorkRange.aEnd.Row(), pTabs[i], HasAttrFlags::Merged ) )
{
SCCOL nEndCol = aWorkRange.aEnd.Col();
SCROW nEndRow = aWorkRange.aEnd.Row();
rDoc.ExtendMerge( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), nEndCol, nEndRow, pTabs[i], true );
}
}
// Undo for displaced attributes?
PaintPartFlags nPaint = PaintPartFlags::Grid;
switch (eCmd)
{
case INS_INSROWS_BEFORE:
case INS_INSROWS_AFTER:
nPaint |= PaintPartFlags::Left;
aWorkRange.aEnd.SetRow(rDoc.MaxRow());
break ;
case INS_CELLSDOWN:
for ( i=0 ; i<nCount; i++ )
{
aWorkRange.aEnd.SetRow(rDoc.MaxRow());
if ( rDocShell.AdjustRowHeight( aWorkRange.aStart.Row(), aWorkRange.aEnd.Row(), pTabs[i] ))
{
aWorkRange.aStart.SetCol(0 );
aWorkRange.aEnd.SetCol(rDoc.MaxCol());
nPaint |= PaintPartFlags::Left;
}
}
break ;
case INS_INSCOLS_BEFORE:
case INS_INSCOLS_AFTER:
nPaint |= PaintPartFlags::Top; // top bar
[[fallthrough]];
case INS_CELLSRIGHT:
for ( i=0 ; i<nCount; i++ )
{
aWorkRange.aEnd.SetCol(rDoc.MaxCol()); // to the far right
if ( rDocShell.AdjustRowHeight( aWorkRange.aStart.Row(), aWorkRange.aEnd.Row(), pTabs[i]) )
{ // AdjustDraw does not paint PaintPartFlags::Top,
aWorkRange.aStart.SetCol(0 ); // thus solved like this
aWorkRange.aEnd.SetRow(rDoc.MaxRow());
nPaint |= PaintPartFlags::Left;
}
}
break ;
default :
{
// added to avoid warnings
}
}
for ( i=0 ; i<nCount; i++ )
{
rDocShell.PostPaint( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), pTabs[i],
aWorkRange.aEnd.Col(), aWorkRange.aEnd.Row(), pTabs[i]+pScenarios[i], nPaint );
}
rDocShell.PostDataChanged();
if (!pViewShell)
return ;
pViewShell->CellContentChanged();
if (!comphelper::LibreOfficeKit::isActive())
return ;
SCTAB nTab = pViewShell->GetViewData().GetTabNo();
bool bColsAffected = (eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER || eCmd == INS_CELLSRIGHT);
bool bRowsAffected = (eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER || eCmd == INS_CELLSDOWN);
if (bColsAffected)
ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, COLUMN_HEADER, nTab);
if (bRowsAffected)
ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, ROW_HEADER, nTab);
ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
pViewShell,
bColsAffected, bRowsAffected,
true /* bSizes*/, true /* bHidden */, true /* bFiltered */,
true /* bGroups */, nTab);
}
void ScUndoInsertCells::Undo()
{
if ( pPasteUndo )
pPasteUndo->Undo(); // undo paste first
weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important due to TrackFormulas in UpdateReference
BeginUndo();
DoChange( true );
EndUndo();
ScDocument& rDoc = rDocShell.GetDocument();
for (SCTAB i = 0 ; i < nCount; ++i)
rDoc.SetDrawPageSize(pTabs[i]);
}
void ScUndoInsertCells::Redo()
{
weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important due to TrackFormulas in UpdateReference
BeginRedo();
DoChange( false );
EndRedo();
if ( pPasteUndo )
pPasteUndo->Redo(); // redo paste last
ScDocument& rDoc = rDocShell.GetDocument();
for (SCTAB i = 0 ; i < nCount; ++i)
rDoc.SetDrawPageSize(pTabs[i]);
}
void ScUndoInsertCells::Repeat(SfxRepeatTarget& rTarget)
{
if (dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr)
{
if ( pPasteUndo )
{
// Repeat for paste with inserting cells is handled completely
// by the Paste undo action
pPasteUndo->Repeat( rTarget );
}
else
static_cast <ScTabViewTarget&>(rTarget).GetViewShell().InsertCells( eCmd );
}
}
bool ScUndoInsertCells::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
ScUndoDeleteCells::ScUndoDeleteCells( ScDocShell& rNewDocShell,
const ScRange& rRange,
SCTAB nNewCount, std::unique_ptr<SCTAB[]> pNewTabs, std::unique_ptr<SCTAB[]> pNewScenarios,
DelCellCmd eNewCmd, ScDocumentUniquePtr pUndoDocument, std::unique_ptr<ScRefUndoData> pRefData ) :
ScMoveUndo( rNewDocShell, std::move(pUndoDocument), std::move(pRefData) ),
aEffRange( rRange ),
nCount( nNewCount ),
pTabs( std::move(pNewTabs) ),
pScenarios( std::move(pNewScenarios) ),
eCmd( eNewCmd )
{
ScDocument& rDoc = rDocShell.GetDocument();
if (eCmd == DelCellCmd::Rows) // whole row?
{
aEffRange.aStart.SetCol(0 );
aEffRange.aEnd.SetCol(rDoc.MaxCol());
}
if (eCmd == DelCellCmd::Cols) // whole column?
{
aEffRange.aStart.SetRow(0 );
aEffRange.aEnd.SetRow(rDoc.MaxRow());
}
SetChangeTrack();
}
ScUndoDeleteCells::~ScUndoDeleteCells()
{
}
OUString ScUndoDeleteCells::GetComment() const
{
return ScResId( STR_UNDO_DELETECELLS ); // "Delete"
}
void ScUndoDeleteCells::SetChangeTrack()
{
ScChangeTrack* pChangeTrack = rDocShell.GetDocument().GetChangeTrack();
if ( pChangeTrack )
pChangeTrack->AppendDeleteRange( aEffRange, pRefUndoDoc.get(),
nStartChangeAction, nEndChangeAction );
else
nStartChangeAction = nEndChangeAction = 0 ;
}
void ScUndoDeleteCells::DoChange( const bool bUndo )
{
ScDocument& rDoc = rDocShell.GetDocument();
SCTAB i;
if ( bUndo )
{
ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
if ( pChangeTrack )
pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
}
else
SetChangeTrack();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
switch (eCmd)
{
case DelCellCmd::Rows:
case DelCellCmd::CellsUp:
for ( i=0 ; i<nCount; i++ )
{
if (bUndo)
rDoc.InsertRow( aEffRange.aStart.Col(), pTabs[i], aEffRange.aEnd.Col(), pTabs[i]+pScenarios[i],
aEffRange.aStart.Row(), static_cast <SCSIZE>(aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1 ));
else
rDoc.DeleteRow( aEffRange.aStart.Col(), pTabs[i], aEffRange.aEnd.Col(), pTabs[i]+pScenarios[i],
aEffRange.aStart.Row(), static_cast <SCSIZE>(aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1 ));
if (pViewShell)
{
const tools::Long nSign = bUndo ? 1 : -1 ;
pViewShell->OnLOKInsertDeleteRow(aEffRange.aStart.Row(), nSign * (aEffRange.aEnd.Row()-aEffRange.aStart.Row()+1 ));
}
}
break ;
case DelCellCmd::Cols:
case DelCellCmd::CellsLeft:
for ( i=0 ; i<nCount; i++ )
{
if (bUndo)
rDoc.InsertCol( aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i],
aEffRange.aStart.Col(), static_cast <SCSIZE>(aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1 ));
else
rDoc.DeleteCol( aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i],
aEffRange.aStart.Col(), static_cast <SCSIZE>(aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1 ));
if (pViewShell)
{
const tools::Long nSign = bUndo ? 1 : -1 ;
pViewShell->OnLOKInsertDeleteColumn(aEffRange.aStart.Col(), nSign * (aEffRange.aEnd.Col()-aEffRange.aStart.Col()+1 ));
}
}
break ;
default :
{
// added to avoid warnings
}
}
// if Undo, restore references
for ( i=0 ; i<nCount && bUndo; i++ )
{
pRefUndoDoc->CopyToDocument(aEffRange.aStart.Col(), aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Col(), aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i],
InsertDeleteFlags::ALL | InsertDeleteFlags::NOCAPTIONS, false , rDoc);
}
ScRange aWorkRange( aEffRange );
if ( eCmd == DelCellCmd::CellsLeft ) // only "shift left" requires refresh of the moved area
aWorkRange.aEnd.SetCol(rDoc.MaxCol());
for ( i=0 ; i<nCount; i++ )
{
if ( rDoc.HasAttrib( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), pTabs[i],
aWorkRange.aEnd.Col(), aWorkRange.aEnd.Row(), pTabs[i], HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
{
// #i51445# old merge flag attributes must be deleted also for single cells,
// not only for whole columns/rows
if ( !bUndo )
{
if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft )
aWorkRange.aEnd.SetCol(rDoc.MaxCol());
if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp )
aWorkRange.aEnd.SetRow(rDoc.MaxRow());
ScMarkData aMarkData(rDoc.GetSheetLimits());
aMarkData.SelectOneTable( aWorkRange.aStart.Tab() );
ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
aPattern.GetItemSet().Put( ScMergeFlagAttr() );
rDoc.ApplyPatternArea( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(),
aWorkRange.aEnd.Col(), aWorkRange.aEnd.Row(),
aMarkData, aPattern );
}
SCCOL nEndCol = aWorkRange.aEnd.Col();
SCROW nEndRow = aWorkRange.aEnd.Row();
rDoc.ExtendMerge( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), nEndCol, nEndRow, pTabs[i], true );
}
}
// paint
PaintPartFlags nPaint = PaintPartFlags::Grid;
switch (eCmd)
{
case DelCellCmd::Rows:
nPaint |= PaintPartFlags::Left;
aWorkRange.aEnd.SetRow(rDoc.MaxRow());
break ;
case DelCellCmd::CellsUp:
for ( i=0 ; i<nCount; i++ )
{
aWorkRange.aEnd.SetRow(rDoc.MaxRow());
if ( rDocShell.AdjustRowHeight( aWorkRange.aStart.Row(), aWorkRange.aEnd.Row(), pTabs[i] ))
{
aWorkRange.aStart.SetCol(0 );
aWorkRange.aEnd.SetCol(rDoc.MaxCol());
nPaint |= PaintPartFlags::Left;
}
}
break ;
case DelCellCmd::Cols:
nPaint |= PaintPartFlags::Top; // top bar
[[fallthrough]];
case DelCellCmd::CellsLeft:
for ( i=0 ; i<nCount; i++ )
{
aWorkRange.aEnd.SetCol(rDoc.MaxCol()); // to the far right
if ( rDocShell.AdjustRowHeight( aWorkRange.aStart.Row(), aWorkRange.aEnd.Row(), pTabs[i] ) )
{
aWorkRange.aStart.SetCol(0 );
aWorkRange.aEnd.SetRow(rDoc.MaxRow());
nPaint |= PaintPartFlags::Left;
}
}
break ;
default :
{
// added to avoid warnings
}
}
for ( i=0 ; i<nCount; i++ )
{
rDocShell.PostPaint( aWorkRange.aStart.Col(), aWorkRange.aStart.Row(), pTabs[i],
aWorkRange.aEnd.Col(), aWorkRange.aEnd.Row(), pTabs[i]+pScenarios[i], nPaint, SC_PF_LINES );
}
// Selection not until EndUndo
rDocShell.PostDataChanged();
// CellContentChanged comes with the selection
if (!pViewShell)
return ;
if (!comphelper::LibreOfficeKit::isActive())
return ;
SCTAB nTab = pViewShell->GetViewData().GetTabNo();
bool bColsAffected = (eCmd == DelCellCmd::Cols || eCmd == DelCellCmd::CellsLeft);
bool bRowsAffected = (eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::CellsUp);
if (bColsAffected)
ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, COLUMN_HEADER, nTab);
if (bRowsAffected)
ScTabViewShell::notifyAllViewsHeaderInvalidation(pViewShell, ROW_HEADER, nTab);
ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
pViewShell,
bColsAffected, bRowsAffected,
true /* bSizes*/, true /* bHidden */, true /* bFiltered */,
true /* bGroups */, nTab);
}
void ScUndoDeleteCells::Undo()
{
weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
BeginUndo();
DoChange( true );
EndUndo();
ScDocument& rDoc = rDocShell.GetDocument();
// Now that DBData have been restored in ScMoveUndo::EndUndo() via its
// pRefUndoDoc we can apply the AutoFilter buttons.
// Add one row for cases undoing deletion right above a cut AutoFilter
// range so the buttons are removed.
SCROW nRefreshEndRow = std::min<SCROW>( aEffRange.aEnd.Row() + 1 , rDoc.MaxRow());
for (SCTAB i=0 ; i < nCount; ++i)
{
rDoc.RefreshAutoFilter( aEffRange.aStart.Col(), aEffRange.aStart.Row(),
aEffRange.aEnd.Col(), nRefreshEndRow, pTabs[i]);
}
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
// Selection not until EndUndo
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if (pViewShell)
{
for ( SCTAB i=0 ; i<nCount; i++ )
{
pViewShell->MarkRange( ScRange(aEffRange.aStart.Col(), aEffRange.aStart.Row(), pTabs[i], aEffRange.aEnd.Col(), aEffRange.aEnd.Row(), pTabs[i]+pScenarios[i]) );
}
}
for (SCTAB i = 0 ; i < nCount; ++i)
rDoc.SetDrawPageSize(pTabs[i]);
}
void ScUndoDeleteCells::Redo()
{
weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
BeginRedo();
DoChange( false );
EndRedo();
ScDocument& rDoc = rDocShell.GetDocument();
for (SCTAB i=0 ; i < nCount; ++i)
{
rDoc.RefreshAutoFilter( aEffRange.aStart.Col(), aEffRange.aStart.Row(),
aEffRange.aEnd.Col(), aEffRange.aEnd.Row(), pTabs[i]);
}
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if (pViewShell)
pViewShell->DoneBlockMode(); // current way
for (SCTAB i = 0 ; i < nCount; ++i)
rDoc.SetDrawPageSize(pTabs[i]);
}
void ScUndoDeleteCells::Repeat(SfxRepeatTarget& rTarget)
{
if (auto pViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget) )
pViewTarget->GetViewShell().DeleteCells( eCmd );
}
bool ScUndoDeleteCells::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
// delete cells in multiselection
ScUndoDeleteMulti::ScUndoDeleteMulti(
ScDocShell& rNewDocShell,
bool bNewRows, bool bNeedsRefresh, SCTAB nNewTab,
std::vector<sc::ColRowSpan>&& rSpans,
ScDocumentUniquePtr pUndoDocument, std::unique_ptr<ScRefUndoData> pRefData ) :
ScMoveUndo( rNewDocShell, std::move(pUndoDocument), std::move(pRefData) ),
mbRows(bNewRows),
mbRefresh(bNeedsRefresh),
nTab( nNewTab ),
maSpans(std::move(rSpans))
{
SetChangeTrack();
}
ScUndoDeleteMulti::~ScUndoDeleteMulti()
{
}
OUString ScUndoDeleteMulti::GetComment() const
{
return ScResId( STR_UNDO_DELETECELLS ); // like DeleteCells
}
void ScUndoDeleteMulti::DoChange() const
{
SCCOL nStartCol;
SCROW nStartRow;
PaintPartFlags nPaint;
ScDocument& rDoc = rDocShell.GetDocument();
if (mbRows)
{
nStartCol = 0 ;
nStartRow = static_cast <SCROW>(maSpans[0 ].mnStart);
nPaint = PaintPartFlags::Grid | PaintPartFlags::Left;
}
else
{
nStartCol = static_cast <SCCOL>(maSpans[0 ].mnStart);
nStartRow = 0 ;
nPaint = PaintPartFlags::Grid | PaintPartFlags::Top;
}
if (mbRefresh)
{
SCCOL nEndCol = rDoc.MaxCol();
SCROW nEndRow = rDoc.MaxRow();
rDoc.RemoveFlagsTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, ScMF::Hor | ScMF::Ver );
rDoc.ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab, true );
}
rDocShell.PostPaint( nStartCol, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, nPaint );
rDocShell.PostDataChanged();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if (pViewShell)
pViewShell->CellContentChanged();
ShowTable( nTab );
}
void ScUndoDeleteMulti::SetChangeTrack()
{
ScChangeTrack* pChangeTrack = rDocShell.GetDocument().GetChangeTrack();
if ( pChangeTrack )
{
ScDocument& rDoc = rDocShell.GetDocument();
nStartChangeAction = pChangeTrack->GetActionMax() + 1 ;
ScRange aRange( 0 , 0 , nTab, 0 , 0 , nTab );
if (mbRows)
aRange.aEnd.SetCol( rDoc.MaxCol() );
else
aRange.aEnd.SetRow( rDoc.MaxRow() );
// delete in reverse
std::vector<sc::ColRowSpan>::const_reverse_iterator ri = maSpans.rbegin(), riEnd = maSpans.rend();
for (; ri != riEnd; ++ri)
{
SCCOLROW nEnd = ri->mnEnd;
SCCOLROW nStart = ri->mnStart;
if (mbRows)
{
aRange.aStart.SetRow( nStart );
aRange.aEnd.SetRow( nEnd );
}
else
{
aRange.aStart.SetCol( static_cast <SCCOL>(nStart) );
aRange.aEnd.SetCol( static_cast <SCCOL>(nEnd) );
}
sal_uLong nDummyStart;
pChangeTrack->AppendDeleteRange( aRange, pRefUndoDoc.get(),
nDummyStart, nEndChangeAction );
}
}
else
nStartChangeAction = nEndChangeAction = 0 ;
}
void ScUndoDeleteMulti::Undo()
{
weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
BeginUndo();
ScDocument& rDoc = rDocShell.GetDocument();
// reverse delete -> forward insert
for (const auto & rSpan : maSpans)
{
SCCOLROW nStart = rSpan.mnStart;
SCCOLROW nEnd = rSpan.mnEnd;
if (mbRows)
rDoc.InsertRow( 0 ,nTab, rDoc.MaxCol(),nTab, nStart,static_cast <SCSIZE>(nEnd-nStart+1 ) );
else
rDoc.InsertCol( 0 ,nTab, rDoc.MaxRow(),nTab, static_cast <SCCOL>(nStart), static_cast <SCSIZE>(nEnd-nStart+1 ) );
}
for (const auto & rSpan : maSpans)
{
SCCOLROW nStart = rSpan.mnStart;
SCCOLROW nEnd = rSpan.mnEnd;
if (mbRows)
pRefUndoDoc->CopyToDocument(0 , nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::ALL, false , rDoc);
else
pRefUndoDoc->CopyToDocument(static_cast <SCCOL>(nStart),0 ,nTab,
static_cast <SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::ALL, false , rDoc);
}
ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
if ( pChangeTrack )
pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
DoChange();
//! redrawing the selection is not possible at the moment
//! since no data for selection exist
EndUndo();
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
}
void ScUndoDeleteMulti::Redo()
{
weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument();
// reverse delete
std::vector<sc::ColRowSpan>::const_reverse_iterator ri = maSpans.rbegin(), riEnd = maSpans.rend();
for (; ri != riEnd; ++ri)
{
SCCOLROW nEnd = ri->mnEnd;
SCCOLROW nStart = ri->mnStart;
if (mbRows)
rDoc.DeleteRow( 0 ,nTab, rDoc.MaxCol(),nTab, nStart,static_cast <SCSIZE>(nEnd-nStart+1 ) );
else
rDoc.DeleteCol( 0 ,nTab, rDoc.MaxRow(),nTab, static_cast <SCCOL>(nStart), static_cast <SCSIZE>(nEnd-nStart+1 ) );
}
SetChangeTrack();
DoChange();
EndRedo();
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
}
void ScUndoDeleteMulti::Repeat(SfxRepeatTarget& rTarget)
{
// if single selection
if (auto pTabViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget))
pTabViewTarget->GetViewShell().DeleteCells( DelCellCmd::Rows );
}
bool ScUndoDeleteMulti::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
ScUndoCut::ScUndoCut(ScDocShell& rNewDocShell, const ScRange& aRange, const ScAddress& aOldEnd,
const ScMarkData& rMark, ScDocumentUniquePtr pNewUndoDoc)
: ScBlockUndo(rNewDocShell, ScRange(aRange.aStart, aOldEnd), SC_UNDO_AUTOHEIGHT)
, aMarkData(rMark)
, pUndoDoc(std::move(pNewUndoDoc))
, aExtendedRange(aRange)
{
SetChangeTrack();
}
ScUndoCut::~ScUndoCut()
{
}
OUString ScUndoCut::GetComment() const
{
return ScResId( STR_UNDO_CUT ); // "cut"
}
void ScUndoCut::SetChangeTrack()
{
ScChangeTrack* pChangeTrack = rDocShell.GetDocument().GetChangeTrack();
if ( pChangeTrack )
pChangeTrack->AppendContentRange( aBlockRange, *pUndoDoc,
nStartChangeAction, nEndChangeAction, SC_CACM_CUT );
else
nStartChangeAction = nEndChangeAction = 0 ;
}
void ScUndoCut::DoChange( const bool bUndo )
{
ScDocument& rDoc = rDocShell.GetDocument();
sal_uInt16 nExtFlags = 0 ;
// do not undo/redo objects and note captions, they are handled via drawing undo
InsertDeleteFlags nUndoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
if (bUndo) // only for Undo
{
// all sheets - CopyToDocument skips those that don't exist in pUndoDoc
SCTAB nTabCount = rDoc.GetTableCount();
ScRange aCopyRange = aExtendedRange;
aCopyRange.aStart.SetTab(0 );
aCopyRange.aEnd.SetTab(nTabCount-1 );
pUndoDoc->CopyToDocument(aCopyRange, nUndoFlags, false , rDoc);
ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
if ( pChangeTrack )
pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
BroadcastChanges(aCopyRange);
}
else // only for Redo
{
rDocShell.UpdatePaintExt( nExtFlags, aExtendedRange );
rDoc.DeleteArea( aBlockRange.aStart.Col(), aBlockRange.aStart.Row(),
aBlockRange.aEnd.Col(), aBlockRange.aEnd.Row(), aMarkData, nUndoFlags );
SetChangeTrack();
}
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if ( !( pViewShell && pViewShell->AdjustBlockHeight() ) )
/*A*/ rDocShell.PostPaint( aExtendedRange, PaintPartFlags::Grid, nExtFlags );
if ( !bUndo ) // draw redo after updating row heights
RedoSdrUndoAction( pDrawUndo.get() ); //! include in ScBlockUndo?
rDocShell.PostDataChanged();
if (pViewShell)
pViewShell->CellContentChanged();
}
void ScUndoCut::Undo()
{
BeginUndo();
DoChange( true );
EndUndo();
}
void ScUndoCut::Redo()
{
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument();
EnableDrawAdjust( &rDoc, false ); //! include in ScBlockUndo?
DoChange( false );
EnableDrawAdjust( &rDoc, true ); //! include in ScBlockUndo?
EndRedo();
}
void ScUndoCut::Repeat(SfxRepeatTarget& rTarget)
{
if (auto pViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget))
pViewTarget->GetViewShell().CutToClip();
}
bool ScUndoCut::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
ScUndoPaste::ScUndoPaste( ScDocShell& rNewDocShell, const ScRangeList& rRanges,
const ScMarkData& rMark,
ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc,
InsertDeleteFlags nNewFlags,
std::unique_ptr<ScRefUndoData> pRefData,
bool bRedoIsFilled, const ScUndoPasteOptions* pOptions ) :
ScMultiBlockUndo( rNewDocShell, rRanges ),
aMarkData( rMark ),
pUndoDoc( std::move(pNewUndoDoc) ),
pRedoDoc( std::move(pNewRedoDoc) ),
nFlags( nNewFlags ),
pRefUndoData( std::move(pRefData) ),
bRedoFilled( bRedoIsFilled )
{
if ( pRefUndoData )
pRefUndoData->DeleteUnchanged( rDocShell.GetDocument() );
if ( pOptions )
aPasteOptions = *pOptions; // used only for Repeat
SetChangeTrack();
}
ScUndoPaste::~ScUndoPaste()
{
pUndoDoc.reset();
pRedoDoc.reset();
pRefUndoData.reset();
pRefRedoData.reset();
}
OUString ScUndoPaste::GetComment() const
{
return ScResId( STR_UNDO_PASTE ); // "paste"
}
void ScUndoPaste::SetChangeTrack()
{
ScChangeTrack* pChangeTrack = rDocShell.GetDocument().GetChangeTrack();
if ( pChangeTrack && (nFlags & InsertDeleteFlags::CONTENTS) )
{
for (size_t i = 0 , n = maBlockRanges.size(); i < n; ++i)
{
pChangeTrack->AppendContentRange(maBlockRanges[i], *pUndoDoc,
nStartChangeAction, nEndChangeAction, SC_CACM_PASTE );
}
}
else
nStartChangeAction = nEndChangeAction = 0 ;
}
void ScUndoPaste::DoChange(bool bUndo)
{
ScDocument& rDoc = rDocShell.GetDocument();
// RefUndoData for redo is created before first undo
// (with DeleteUnchanged after the DoUndo call)
bool bCreateRedoData = ( bUndo && pRefUndoData && !pRefRedoData );
if ( bCreateRedoData )
pRefRedoData.reset( new ScRefUndoData( rDoc ) );
ScRefUndoData* pWorkRefData = bUndo ? pRefUndoData.get() : pRefRedoData.get();
// Always back-up either all or none of the content for Undo
InsertDeleteFlags nUndoFlags = InsertDeleteFlags::NONE;
if (nFlags & InsertDeleteFlags::CONTENTS)
nUndoFlags |= InsertDeleteFlags::CONTENTS;
if (nFlags & InsertDeleteFlags::ATTRIB)
nUndoFlags |= InsertDeleteFlags::ATTRIB;
// do not undo/redo objects and note captions, they are handled via drawing undo
nUndoFlags &= ~InsertDeleteFlags::OBJECTS;
nUndoFlags |= InsertDeleteFlags::NOCAPTIONS;
bool bPaintAll = false ;
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
SCTAB nTabCount = rDoc.GetTableCount();
if ( bUndo && !bRedoFilled )
{
if (!pRedoDoc)
{
bool bColInfo = true ;
bool bRowInfo = true ;
for (size_t i = 0 , n = maBlockRanges.size(); i < n; ++i)
{
const ScRange& r = maBlockRanges[i];
bColInfo &= (r.aStart.Row() == 0 && r.aEnd.Row() == rDoc.MaxRow());
bRowInfo &= (r.aStart.Col() == 0 && r.aEnd.Col() == rDoc.MaxCol());
if (!bColInfo && !bRowInfo)
break ;
}
pRedoDoc.reset( new ScDocument( SCDOCMODE_UNDO ) );
pRedoDoc->InitUndoSelected( rDoc, aMarkData, bColInfo, bRowInfo );
}
// read "redo" data from the document in the first undo
// all sheets - CopyToDocument skips those that don't exist in pRedoDoc
for (size_t i = 0 , n = maBlockRanges.size(); i < n; ++i)
{
ScRange aCopyRange = maBlockRanges[i];
aCopyRange.aStart.SetTab(0 );
aCopyRange.aEnd.SetTab(nTabCount-1 );
rDoc.CopyToDocument(aCopyRange, nUndoFlags, false , *pRedoDoc);
bRedoFilled = true ;
}
}
sal_uInt16 nExtFlags = 0 ;
rDocShell.UpdatePaintExt(nExtFlags, maBlockRanges.Combine());
// tdf#167042 - cell comments texts should not be deleted but
// the note text should be remembered in maNoteData to be able
// to later reconstruct a caption from it.
rDoc.ForgetNoteCaptions(maBlockRanges, true );
aMarkData.MarkToMulti();
rDoc.DeleteSelection(nUndoFlags, aMarkData, false ); // no broadcasting here
for (size_t i = 0 , n = maBlockRanges.size(); i < n; ++i)
rDoc.BroadcastCells(maBlockRanges[i], SfxHintId::ScDataChanged);
aMarkData.MarkToSimple();
SCTAB nFirstSelected = aMarkData.GetFirstSelected();
if ( !bUndo && pRedoDoc ) // Redo: UndoToDocument before handling RefData
{
for (size_t i = 0 , n = maBlockRanges.size(); i < n; ++i)
{
ScRange aRange = maBlockRanges[i];
aRange.aStart.SetTab(nFirstSelected);
aRange.aEnd.SetTab(nFirstSelected);
pRedoDoc->UndoToDocument(aRange, nUndoFlags, false , rDoc);
for (const auto & rTab : aMarkData)
{
if (rTab >= nTabCount)
break ;
if (rTab == nFirstSelected)
continue ;
aRange.aStart.SetTab(rTab);
aRange.aEnd.SetTab(rTab);
pRedoDoc->CopyToDocument(aRange, nUndoFlags, false , rDoc);
}
}
}
if (pWorkRefData)
{
pWorkRefData->DoUndo( rDoc, true ); // true = bSetChartRangeLists for SetChartListenerCollection
if (!maBlockRanges.empty() &&
rDoc.RefreshAutoFilter(0 , 0 , rDoc.MaxCol(), rDoc.MaxRow(), maBlockRanges[0 ].aStart.Tab()))
bPaintAll = true ;
}
if ( bCreateRedoData && pRefRedoData )
pRefRedoData->DeleteUnchanged( rDoc );
if (bUndo) // Undo: UndoToDocument after handling RefData
{
for (size_t i = 0 , n = maBlockRanges.size(); i < n; ++i)
{
ScRange aRange = maBlockRanges[i];
for (const auto & rTab : aMarkData)
{
if (rTab >= nTabCount)
break ;
aRange.aStart.SetTab(rTab);
aRange.aEnd.SetTab(rTab);
pUndoDoc->UndoToDocument(aRange, nUndoFlags, false , rDoc);
}
}
}
if ( bUndo )
{
ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
if ( pChangeTrack )
pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
}
else
SetChangeTrack();
ScRangeList aDrawRanges(maBlockRanges);
PaintPartFlags nPaint = PaintPartFlags::Grid;
// For sheet geometry invalidation.
bool bColsAffected = false ;
bool bRowsAffected = false ;
for (size_t i = 0 , n = aDrawRanges.size(); i < n; ++i)
{
ScRange& rDrawRange = aDrawRanges[i];
rDoc.ExtendMerge(rDrawRange, true ); // only needed for single sheet (text/rtf etc.)
ScRangeList aRangeList(rDrawRange);
ScMarkData aData(rDoc.GetSheetLimits(), aRangeList);
if (bPaintAll)
{
rDrawRange.aStart.SetCol(0 );
rDrawRange.aStart.SetRow(0 );
rDrawRange.aEnd.SetCol(rDoc.MaxCol());
rDrawRange.aEnd.SetRow(rDoc.MaxRow());
nPaint |= PaintPartFlags::Top | PaintPartFlags::Left;
if (pViewShell)
pViewShell->AdjustBlockHeight(false , &aData);
}
else
{
if (maBlockRanges[i].aStart.Row() == 0 && maBlockRanges[i].aEnd.Row() == rDoc.MaxRow()) // whole column
{
nPaint |= PaintPartFlags::Top;
rDrawRange.aEnd.SetCol(rDoc.MaxCol());
bColsAffected = true ;
}
if (maBlockRanges[i].aStart.Col() == 0 && maBlockRanges[i].aEnd.Col() == rDoc.MaxCol()) // whole row
{
nPaint |= PaintPartFlags::Left;
rDrawRange.aEnd.SetRow(rDoc.MaxRow());
bRowsAffected = true ;
}
if (pViewShell && pViewShell->AdjustBlockHeight(false , &aData))
{
rDrawRange.aStart.SetCol(0 );
rDrawRange.aStart.SetRow(0 );
rDrawRange.aEnd.SetCol(rDoc.MaxCol());
rDrawRange.aEnd.SetRow(rDoc.MaxRow());
nPaint |= PaintPartFlags::Left;
}
rDocShell.UpdatePaintExt(nExtFlags, rDrawRange);
}
}
if ( !bUndo ) // draw redo after updating row heights
RedoSdrUndoAction(mpDrawUndo.get());
rDocShell.PostPaint(aDrawRanges, nPaint, nExtFlags);
rDocShell.PostDataChanged();
if (!pViewShell)
return ;
pViewShell->CellContentChanged();
if (bColsAffected || bRowsAffected)
ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
pViewShell,
bColsAffected, bRowsAffected,
true /* bSizes*/, true /* bHidden */, true /* bFiltered */,
true /* bGroups */, aDrawRanges[0].aStart.Tab());
}
void ScUndoPaste::Undo()
{
BeginUndo();
DoChange(true );
if (!maBlockRanges.empty())
ShowTable(maBlockRanges.front());
EndUndo();
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
}
void ScUndoPaste::Redo()
{
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument();
EnableDrawAdjust( &rDoc, false ); //! include in ScBlockUndo?
DoChange( false );
EnableDrawAdjust( &rDoc, true ); //! include in ScBlockUndo?
EndRedo();
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
}
void ScUndoPaste::Repeat(SfxRepeatTarget& rTarget)
{
auto pViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget);
if (!pViewTarget)
return ;
ScTabViewShell& rViewSh = pViewTarget->GetViewShell();
// keep a reference in case the clipboard is changed during PasteFromClip
const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(rViewSh.GetViewData().GetActiveWin()));
if (pOwnClip)
{
rViewSh.PasteFromClip( nFlags, pOwnClip->GetDocument(),
aPasteOptions.nFunction, aPasteOptions.bSkipEmptyCells, aPasteOptions.bTranspose,
aPasteOptions.bAsLink, aPasteOptions.eMoveMode, InsertDeleteFlags::NONE,
true ); // allow warning dialog
}
}
bool ScUndoPaste::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
ScUndoDragDrop::ScUndoDragDrop( ScDocShell& rNewDocShell,
const ScRange& rRange, const ScAddress& aNewDestPos, bool bNewCut,
ScDocumentUniquePtr pUndoDocument, bool bScenario ) :
ScMoveUndo( rNewDocShell, std::move(pUndoDocument), nullptr ),
mnPaintExtFlags( 0 ),
aSrcRange( rRange ),
bCut( bNewCut ),
bKeepScenarioFlags( bScenario )
{
ScAddress aDestEnd(aNewDestPos);
aDestEnd.IncRow(aSrcRange.aEnd.Row() - aSrcRange.aStart.Row());
aDestEnd.IncCol(aSrcRange.aEnd.Col() - aSrcRange.aStart.Col());
aDestEnd.IncTab(aSrcRange.aEnd.Tab() - aSrcRange.aStart.Tab());
bool bIncludeFiltered = bCut;
if ( !bIncludeFiltered )
{
// find number of non-filtered rows
SCROW nPastedCount = rDocShell.GetDocument().CountNonFilteredRows(
aSrcRange.aStart.Row(), aSrcRange.aEnd.Row(), aSrcRange.aStart.Tab());
if ( nPastedCount == 0 )
nPastedCount = 1 ;
aDestEnd.SetRow( aNewDestPos.Row() + nPastedCount - 1 );
}
aDestRange.aStart = aNewDestPos;
aDestRange.aEnd = aDestEnd;
SetChangeTrack();
}
ScUndoDragDrop::~ScUndoDragDrop()
{
}
OUString ScUndoDragDrop::GetComment() const
{ // "Move" : "Copy"
return bCut ?
ScResId( STR_UNDO_MOVE ) :
ScResId( STR_UNDO_COPY );
}
void ScUndoDragDrop::SetChangeTrack()
{
ScChangeTrack* pChangeTrack = rDocShell.GetDocument().GetChangeTrack();
if ( pChangeTrack )
{
if ( bCut )
{
nStartChangeAction = pChangeTrack->GetActionMax() + 1 ;
pChangeTrack->AppendMove( aSrcRange, aDestRange, pRefUndoDoc.get() );
nEndChangeAction = pChangeTrack->GetActionMax();
}
else
pChangeTrack->AppendContentRange( aDestRange, *pRefUndoDoc,
nStartChangeAction, nEndChangeAction );
}
else
nStartChangeAction = nEndChangeAction = 0 ;
}
void ScUndoDragDrop::PaintArea( ScRange aRange, sal_uInt16 nExtFlags ) const
{
PaintPartFlags nPaint = PaintPartFlags::Grid;
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
ScDocument& rDoc = rDocShell.GetDocument();
if (pViewShell)
{
ScopedVclPtrInstance< VirtualDevice > pVirtDev;
ScViewData& rViewData = pViewShell->GetViewData();
sc::RowHeightContext aCxt(
rDoc.MaxRow(), rViewData.GetPPTX(), rViewData.GetPPTY(), rViewData.GetZoomX(), rViewData.GetZoomY(),
pVirtDev);
if (rDoc.SetOptimalHeight(aCxt, aRange.aStart.Row(), aRange.aEnd.Row(), aRange.aStart.Tab(), true ))
{
// tdf#76183: recalculate objects' positions
rDoc.SetDrawPageSize(aRange.aStart.Tab());
aRange.aStart.SetCol(0 );
aRange.aEnd.SetCol(rDoc.MaxCol());
aRange.aEnd.SetRow(rDoc.MaxRow());
nPaint |= PaintPartFlags::Left;
}
}
if ( bKeepScenarioFlags )
{
// Copy scenario -> also paint scenario boarder
aRange.aStart.SetCol(0 );
aRange.aStart.SetRow(0 );
aRange.aEnd.SetCol(rDoc.MaxCol());
aRange.aEnd.SetRow(rDoc.MaxRow());
}
// column/row info (width/height) included if whole columns/rows were copied
if ( aSrcRange.aStart.Col() == 0 && aSrcRange.aEnd.Col() == rDoc.MaxCol() )
{
nPaint |= PaintPartFlags::Left;
aRange.aEnd.SetRow(rDoc.MaxRow());
}
if ( aSrcRange.aStart.Row() == 0 && aSrcRange.aEnd.Row() == rDoc.MaxRow() )
{
nPaint |= PaintPartFlags::Top;
aRange.aEnd.SetCol(rDoc.MaxCol());
}
rDocShell.PostPaint( aRange, nPaint, nExtFlags );
}
void ScUndoDragDrop::DoUndo( ScRange aRange )
{
ScDocument& rDoc = rDocShell.GetDocument();
ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
if ( pChangeTrack )
pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
// Database range before data, so that the Autofilter button match up in ExtendMerge
ScRange aPaintRange = aRange;
rDoc.ExtendMerge( aPaintRange ); // before deleting
rDocShell.UpdatePaintExt(mnPaintExtFlags, aPaintRange);
// do not undo objects and note captions, they are handled via drawing undo
InsertDeleteFlags nUndoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
// Additionally discard/forget caption ownership during deletion, as
// Drag&Drop is a special case in that the Undo holds captions of the
// transferred target range, which would get deleted and
// SdrGroupUndo::Undo() would attempt to access invalidated captions and
// crash, tdf#92995
InsertDeleteFlags nDelFlags = nUndoFlags | InsertDeleteFlags::FORGETCAPTIONS;
rDoc.DeleteAreaTab( aRange, nDelFlags );
pRefUndoDoc->CopyToDocument(aRange, nUndoFlags, false , rDoc);
if ( rDoc.HasAttrib( aRange, HasAttrFlags::Merged ) )
rDoc.ExtendMerge( aRange, true );
aPaintRange.aEnd.SetCol( std::max( aPaintRange.aEnd.Col(), aRange.aEnd.Col() ) );
aPaintRange.aEnd.SetRow( std::max( aPaintRange.aEnd.Row(), aRange.aEnd.Row() ) );
rDocShell.UpdatePaintExt(mnPaintExtFlags, aPaintRange);
maPaintRanges.Join(aPaintRange);
if (ScTabViewShell* pTabViewShell = ScTabViewShell::GetActiveViewShell())
{
if (comphelper::LibreOfficeKit::isActive())
{
pTabViewShell->OnLOKSetWidthOrHeight(aPaintRange.aStart.Col(), true );
pTabViewShell->OnLOKSetWidthOrHeight(aPaintRange.aStart.Row(), false );
}
ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
pTabViewShell,
true /* bColumns */, true /* bRows */,
true /* bSizes */, true /* bHidden */, true /* bFiltered */,
true /* bGroups */, aPaintRange.aStart.Tab());
}
}
void ScUndoDragDrop::Undo()
{
mnPaintExtFlags = 0 ;
maPaintRanges.RemoveAll();
BeginUndo();
if (bCut)
{
// During undo, we move cells from aDestRange to aSrcRange.
ScDocument& rDoc = rDocShell.GetDocument();
SCCOL nColDelta = aSrcRange.aStart.Col() - aDestRange.aStart.Col();
SCROW nRowDelta = aSrcRange.aStart.Row() - aDestRange.aStart.Row();
SCTAB nTabDelta = aSrcRange.aStart.Tab() - aDestRange.aStart.Tab();
sc::RefUpdateContext aCxt(rDoc);
aCxt.meMode = URM_MOVE;
aCxt.maRange = aSrcRange;
aCxt.mnColDelta = nColDelta;
aCxt.mnRowDelta = nRowDelta;
aCxt.mnTabDelta = nTabDelta;
// Global range names.
ScRangeName* pName = rDoc.GetRangeName();
if (pName)
pName->UpdateReference(aCxt);
SCTAB nTabCount = rDoc.GetTableCount();
for (SCTAB nTab = 0 ; nTab < nTabCount; ++nTab)
{
// Sheet-local range names.
pName = rDoc.GetRangeName(nTab);
if (pName)
pName->UpdateReference(aCxt, nTab);
}
ScValidationDataList* pValidList = rDoc.GetValidationList();
if (pValidList)
{
// Update the references of validation entries.
pValidList->UpdateReference(aCxt);
}
DoUndo(aDestRange);
DoUndo(aSrcRange);
rDoc.BroadcastCells(aSrcRange, SfxHintId::ScDataChanged, false );
}
else
DoUndo(aDestRange);
for (size_t i = 0 ; i < maPaintRanges.size(); ++i)
{
const ScRange& r = maPaintRanges[i];
PaintArea(r, mnPaintExtFlags);
}
EndUndo();
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
}
void ScUndoDragDrop::Redo()
{
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument();
ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP ));
EnableDrawAdjust( &rDoc, false ); //! include in ScBlockUndo?
// do not undo/redo objects and note captions, they are handled via drawing undo
constexpr InsertDeleteFlags nRedoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
/* TODO: Redoing note captions is quite tricky due to the fact that a
helper clip document is used . While ( re - ) pasting the contents to the
destination area , the original pointers to the captions created while
dropping have to be restored . A simple CopyFromClip ( ) would create new
caption objects that are not tracked by drawing undo , and the captions
restored by drawing redo would live without cell note objects pointing
to them . So , first , CopyToClip ( ) and CopyFromClip ( ) are called without
cloning the caption objects . This leads to cell notes pointing to the
wrong captions from source area that will be removed by drawing redo
later . Second , the pointers to the new captions have to be restored .
Sadly , currently these pointers are not stored anywhere but in the list
of drawing undo actions. */
SCTAB nTab;
ScMarkData aSourceMark(rDoc.GetSheetLimits());
for (nTab=aSrcRange.aStart.Tab(); nTab<=aSrcRange.aEnd.Tab(); nTab++)
aSourceMark.SelectTable( nTab, true );
// do not clone objects and note captions into clipdoc (see above)
// but at least copy notes
ScClipParam aClipParam(aSrcRange, bCut);
rDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSourceMark, bKeepScenarioFlags, false );
if (bCut)
{
ScRange aSrcPaintRange = aSrcRange;
rDoc.ExtendMerge( aSrcPaintRange ); // before deleting
sal_uInt16 nExtFlags = 0 ;
rDocShell.UpdatePaintExt( nExtFlags, aSrcPaintRange );
rDoc.DeleteAreaTab( aSrcRange, nRedoFlags );
PaintArea( aSrcPaintRange, nExtFlags );
}
ScMarkData aDestMark(rDoc.GetSheetLimits());
for (nTab=aDestRange.aStart.Tab(); nTab<=aDestRange.aEnd.Tab(); nTab++)
aDestMark.SelectTable( nTab, true );
bool bIncludeFiltered = bCut;
// TODO: restore old note captions instead of cloning new captions...
rDoc.CopyFromClip( aDestRange, aDestMark, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS, nullptr, pClipDoc.get(), true , false , bIncludeFiltered );
if (bCut)
for (nTab=aSrcRange.aStart.Tab(); nTab<=aSrcRange.aEnd.Tab(); nTab++)
rDoc.RefreshAutoFilter( aSrcRange.aStart.Col(), aSrcRange.aStart.Row(),
aSrcRange.aEnd.Col(), aSrcRange.aEnd.Row(), nTab );
// skipped rows and merged cells don't mix
if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() )
rDocShell.GetDocFunc().UnmergeCells( aDestRange, false , nullptr );
for (nTab=aDestRange.aStart.Tab(); nTab<=aDestRange.aEnd.Tab(); nTab++)
{
SCCOL nEndCol = aDestRange.aEnd.Col();
SCROW nEndRow = aDestRange.aEnd.Row();
rDoc.ExtendMerge( aDestRange.aStart.Col(), aDestRange.aStart.Row(),
nEndCol, nEndRow, nTab, true );
PaintArea( ScRange( aDestRange.aStart.Col(), aDestRange.aStart.Row(), nTab,
nEndCol, nEndRow, nTab ), 0 );
}
SetChangeTrack();
pClipDoc.reset();
ShowTable( aDestRange.aStart.Tab() );
RedoSdrUndoAction( pDrawUndo.get() ); //! include in ScBlockUndo?
EnableDrawAdjust( &rDoc, true ); //! include in ScBlockUndo?
EndRedo();
SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
if (comphelper::LibreOfficeKit::isActive())
{
SCCOL nStartCol = aDestRange.aStart.Col();
SCROW nStartRow = aDestRange.aStart.Row();
if (bCut)
{
nStartCol = std::min(nStartCol, aSrcRange.aStart.Col());
nStartRow = std::min(nStartRow, aSrcRange.aStart.Row());
}
if (ScTabViewShell* pTabViewShell = ScTabViewShell::GetActiveViewShell())
{
pTabViewShell->OnLOKSetWidthOrHeight(nStartCol, true );
pTabViewShell->OnLOKSetWidthOrHeight(nStartRow, false );
SCTAB nStartTab = aDestRange.aStart.Tab();
SCTAB nEndTab = aDestRange.aEnd.Tab();
if (bCut)
{
nStartTab = std::min(nStartTab, aSrcRange.aStart.Tab());
nEndTab = std::max(nEndTab, aSrcRange.aEnd.Tab());
}
for (nTab = nStartTab; nTab <= nEndTab; ++nTab)
{
ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
pTabViewShell, true /* bColumns */, true /* bRows */,
true /* bSizes */, true /* bHidden */, true /* bFiltered */, true /* bGroups */,
nTab);
}
}
}
}
void ScUndoDragDrop::Repeat(SfxRepeatTarget& /* rTarget */)
{
}
bool ScUndoDragDrop::CanRepeat(SfxRepeatTarget& /* rTarget */) const
{
return false ; // not possible
}
// Insert list containing range names
// (Insert|Name|Insert =>[List])
ScUndoListNames::ScUndoListNames(ScDocShell& rNewDocShell, const ScRange& rRange,
ScDocumentUniquePtr pNewUndoDoc, ScDocumentUniquePtr pNewRedoDoc)
: ScBlockUndo(rNewDocShell, rRange, SC_UNDO_AUTOHEIGHT)
, xUndoDoc(std::move(pNewUndoDoc))
, xRedoDoc(std::move(pNewRedoDoc))
{
}
OUString ScUndoListNames::GetComment() const
{
return ScResId( STR_UNDO_LISTNAMES );
}
void ScUndoListNames::DoChange( ScDocument* pSrcDoc ) const
{
ScDocument& rDoc = rDocShell.GetDocument();
rDoc.DeleteAreaTab( aBlockRange, InsertDeleteFlags::ALL );
pSrcDoc->CopyToDocument(aBlockRange, InsertDeleteFlags::ALL, false , rDoc);
rDocShell.PostPaint( aBlockRange, PaintPartFlags::Grid );
rDocShell.PostDataChanged();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if (pViewShell)
pViewShell->CellContentChanged();
}
void ScUndoListNames::Undo()
{
BeginUndo();
DoChange(xUndoDoc.get());
EndUndo();
}
void ScUndoListNames::Redo()
{
BeginRedo();
DoChange(xRedoDoc.get());
EndRedo();
}
void ScUndoListNames::Repeat(SfxRepeatTarget& rTarget)
{
if (auto pTabViewTarget = dynamic_cast <ScTabViewTarget*>(&rTarget))
pTabViewTarget->GetViewShell().InsertNameList();
}
bool ScUndoListNames::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
ScUndoConditionalFormat::ScUndoConditionalFormat(ScDocShell& rNewDocShell, SCTAB nTab):
ScSimpleUndo( rNewDocShell ),
mnTab(nTab),
mpUndoDoc(createUndoRedoData())
{
}
ScUndoConditionalFormat::~ScUndoConditionalFormat()
{
}
ScDocumentUniquePtr ScUndoConditionalFormat::createUndoRedoData()
{
ScDocument& rDoc = rDocShell.GetDocument();
ScDocumentUniquePtr pUndoRedoDoc(new ScDocument(SCDOCMODE_UNDO));
pUndoRedoDoc->InitUndo(rDoc, mnTab, mnTab);
if (const auto * pList = rDoc.GetCondFormList(mnTab))
pUndoRedoDoc->SetCondFormList(pList->Clone(*pUndoRedoDoc), mnTab);
return pUndoRedoDoc;
}
OUString ScUndoConditionalFormat::GetComment() const
{
return ScResId( STR_UNDO_CONDFORMAT );
}
void ScUndoConditionalFormat::Undo()
{
DoChange(mpUndoDoc.get());
}
void ScUndoConditionalFormat::Redo()
{
DoChange(mpRedoDoc.get());
}
void ScUndoConditionalFormat::DoChange(const ScDocument* pSrcDoc)
{
ScDocument& rDoc = rDocShell.GetDocument();
// Restore all conditional formats in the tab. This is simpler and more reliable, than
// restoring formats in a specific range, and then trying to join selectively the restored
// formats with the other formats in the tab, to get the correct state.
ScRangeList aCombinedRange;
if (const auto * pOldList = rDoc.GetCondFormList(mnTab))
{
aCombinedRange = pOldList->GetCombinedRange();
// Clear all existing CF keys from cells' attributes
for (auto & pFormat : *pOldList)
rDoc.RemoveCondFormatData(aCombinedRange, mnTab, pFormat->GetKey());
}
if (const auto * pNewList = pSrcDoc->GetCondFormList(mnTab))
{
for (const auto & cond : *pNewList)
{
// Restore the CF keys to cell attributes
rDoc.AddCondFormatData(cond->GetRange(), mnTab, cond->GetKey());
for (const auto & range : cond->GetRange())
aCombinedRange.Join(range);
}
rDoc.SetCondFormList(pNewList->Clone(rDoc), mnTab);
}
else
{
rDoc.SetCondFormList(nullptr, mnTab);
}
rDocShell.PostPaint(aCombinedRange, PaintPartFlags::Grid);
rDocShell.PostDataChanged();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if (pViewShell)
pViewShell->CellContentChanged();
}
void ScUndoConditionalFormat::Repeat(SfxRepeatTarget& )
{
}
bool ScUndoConditionalFormat::CanRepeat(SfxRepeatTarget& ) const
{
return false ;
}
ScUndoConditionalFormatList::ScUndoConditionalFormatList(ScDocShell& rNewDocShell,
ScDocumentUniquePtr pUndoDoc, ScDocumentUniquePtr pRedoDoc, SCTAB nTab):
ScSimpleUndo( rNewDocShell ),
mpUndoDoc(std::move(pUndoDoc)),
mpRedoDoc(std::move(pRedoDoc)),
mnTab(nTab)
{
}
ScUndoConditionalFormatList::~ScUndoConditionalFormatList()
{
}
OUString ScUndoConditionalFormatList::GetComment() const
{
return ScResId( STR_UNDO_CONDFORMAT_LIST );
}
void ScUndoConditionalFormatList::Undo()
{
DoChange(mpUndoDoc.get());
}
void ScUndoConditionalFormatList::Redo()
{
DoChange(mpRedoDoc.get());
}
void ScUndoConditionalFormatList::DoChange(const ScDocument* pSrcDoc)
{
ScDocument& rDoc = rDocShell.GetDocument();
if (pSrcDoc == mpUndoDoc.get())
{
mpRedoDoc->GetCondFormList(mnTab)->RemoveFromDocument(rDoc);
mpUndoDoc->GetCondFormList(mnTab)->AddToDocument(rDoc);
}
else
{
mpUndoDoc->GetCondFormList(mnTab)->RemoveFromDocument(rDoc);
mpRedoDoc->GetCondFormList(mnTab)->AddToDocument(rDoc);
}
rDoc.SetCondFormList(pSrcDoc->GetCondFormList(mnTab)->Clone(rDoc), mnTab);
rDocShell.PostPaintGridAll();
rDocShell.PostDataChanged();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if (pViewShell)
pViewShell->CellContentChanged();
}
void ScUndoConditionalFormatList::Repeat(SfxRepeatTarget& )
{
}
bool ScUndoConditionalFormatList::CanRepeat(SfxRepeatTarget& ) const
{
return false ;
}
ScUndoUseScenario::ScUndoUseScenario( ScDocShell& rNewDocShell,
const ScMarkData& rMark,
const ScArea& rDestArea,
ScDocumentUniquePtr pNewUndoDoc,
OUString aNewName ) :
ScSimpleUndo( rNewDocShell ),
pUndoDoc( std::move(pNewUndoDoc) ),
aMarkData( rMark ),
aName(std::move( aNewName ))
{
aRange.aStart.SetCol(rDestArea.nColStart);
aRange.aStart.SetRow(rDestArea.nRowStart);
aRange.aStart.SetTab(rDestArea.nTab);
aRange.aEnd.SetCol(rDestArea.nColEnd);
aRange.aEnd.SetRow(rDestArea.nRowEnd);
aRange.aEnd.SetTab(rDestArea.nTab);
}
ScUndoUseScenario::~ScUndoUseScenario()
{
}
OUString ScUndoUseScenario::GetComment() const
{
return ScResId( STR_UNDO_USESCENARIO );
}
void ScUndoUseScenario::Undo()
{
BeginUndo();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if (pViewShell)
{
pViewShell->DoneBlockMode();
pViewShell->InitOwnBlockMode( aRange );
}
ScDocument& rDoc = rDocShell.GetDocument();
rDoc.DeleteSelection( InsertDeleteFlags::ALL, aMarkData );
pUndoDoc->CopyToDocument(aRange, InsertDeleteFlags::ALL, true , rDoc, &aMarkData);
// scenario table
bool bFrame = false ;
SCTAB nTab = aRange.aStart.Tab();
SCTAB nEndTab = nTab;
while ( pUndoDoc->HasTable(nEndTab+1 ) && pUndoDoc->IsScenario(nEndTab+1 ) )
++nEndTab;
for (SCTAB i = nTab+1 ; i<=nEndTab; i++)
{
// Flags always
OUString aComment;
Color aColor;
ScScenarioFlags nScenFlags;
pUndoDoc->GetScenarioData( i, aComment, aColor, nScenFlags );
rDoc.SetScenarioData( i, aComment, aColor, nScenFlags );
bool bActive = pUndoDoc->IsActiveScenario( i );
rDoc.SetActiveScenario( i, bActive );
// For copy-back scenario also consider content
if ( nScenFlags & ScScenarioFlags::TwoWay )
{
rDoc.DeleteAreaTab( 0 ,0 , rDoc.MaxCol(),rDoc.MaxRow(), i, InsertDeleteFlags::ALL );
pUndoDoc->CopyToDocument(0 ,0 ,i, rDoc.MaxCol(),rDoc.MaxRow(),i, InsertDeleteFlags::ALL,false , rDoc);
}
if ( nScenFlags & ScScenarioFlags::ShowFrame )
bFrame = true ;
}
// if visible borders, then paint all
if (bFrame)
rDocShell.PostPaint( 0 ,0 ,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid | PaintPartFlags::Extras );
else
rDocShell.PostPaint( aRange, PaintPartFlags::Grid | PaintPartFlags::Extras );
rDocShell.PostDataChanged();
if (pViewShell)
pViewShell->CellContentChanged();
ShowTable( aRange.aStart.Tab() );
EndUndo();
}
void ScUndoUseScenario::Redo()
{
SCTAB nTab = aRange.aStart.Tab();
BeginRedo();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if (pViewShell)
{
pViewShell->SetTabNo( nTab );
pViewShell->DoneBlockMode();
pViewShell->InitOwnBlockMode( aRange );
}
rDocShell.UseScenario( nTab, aName, false );
EndRedo();
}
void ScUndoUseScenario::Repeat(SfxRepeatTarget& rTarget)
{
if (auto pViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget))
{
OUString aTemp = aName;
pViewTarget->GetViewShell().UseScenario(aTemp);
}
}
bool ScUndoUseScenario::CanRepeat(SfxRepeatTarget& rTarget) const
{
if (auto pViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget))
{
ScViewData& rViewData = pViewTarget->GetViewShell().GetViewData();
return !rViewData.GetDocument().IsScenario( rViewData.GetTabNo() );
}
return false ;
}
ScUndoSelectionStyle::ScUndoSelectionStyle( ScDocShell& rNewDocShell,
const ScMarkData& rMark,
const ScRange& rRange,
OUString aName,
ScDocumentUniquePtr pNewUndoDoc ) :
ScSimpleUndo( rNewDocShell ),
aMarkData( rMark ),
pUndoDoc( std::move(pNewUndoDoc) ),
aStyleName(std::move( aName )),
aRange( rRange )
{
aMarkData.MarkToMulti();
}
ScUndoSelectionStyle::~ScUndoSelectionStyle()
{
}
OUString ScUndoSelectionStyle::GetComment() const
{
return ScResId( STR_UNDO_APPLYCELLSTYLE );
}
void ScUndoSelectionStyle::DoChange( const bool bUndo )
{
ScDocument& rDoc = rDocShell.GetDocument();
SetViewMarkData( aMarkData );
ScRange aWorkRange( aRange );
if ( rDoc.HasAttrib( aWorkRange, HasAttrFlags::Merged ) ) // Merged cells?
rDoc.ExtendMerge( aWorkRange, true );
sal_uInt16 nExtFlags = 0 ;
rDocShell.UpdatePaintExt( nExtFlags, aWorkRange );
if (bUndo) // if Undo then push back all old data again
{
SCTAB nTabCount = rDoc.GetTableCount();
ScRange aCopyRange = aWorkRange;
aCopyRange.aStart.SetTab(0 );
aCopyRange.aEnd.SetTab(nTabCount-1 );
pUndoDoc->CopyToDocument(aCopyRange, InsertDeleteFlags::ATTRIB, true , rDoc, &aMarkData);
}
else // if Redo, then reapply style
{
ScStyleSheetPool* pStlPool = rDoc.GetStyleSheetPool();
ScStyleSheet* pStyleSheet =
static_cast <ScStyleSheet*>( pStlPool->Find( aStyleName, SfxStyleFamily::Para ) );
if (!pStyleSheet)
{
OSL_FAIL("StyleSheet not found" );
return ;
}
rDoc.ApplySelectionStyle( *pStyleSheet, aMarkData );
}
rDocShell.UpdatePaintExt( nExtFlags, aWorkRange );
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if ( !( pViewShell && pViewShell->AdjustBlockHeight() ) )
/*A*/ rDocShell.PostPaint( aWorkRange, PaintPartFlags::Grid | PaintPartFlags::Extras, nExtFlags );
ShowTable( aWorkRange.aStart.Tab() );
}
void ScUndoSelectionStyle::Undo()
{
BeginUndo();
DoChange( true );
EndUndo();
}
void ScUndoSelectionStyle::Redo()
{
BeginRedo();
DoChange( false );
EndRedo();
}
void ScUndoSelectionStyle::Repeat(SfxRepeatTarget& rTarget)
{
if (dynamic_cast <const ScTabViewTarget*>( &rTarget) == nullptr)
return ;
ScDocument& rDoc = rDocShell.GetDocument();
ScStyleSheetPool* pStlPool = rDoc.GetStyleSheetPool();
ScStyleSheet* pStyleSheet = static_cast <ScStyleSheet*>( pStlPool->
Find( aStyleName, SfxStyleFamily::Para ));
if (!pStyleSheet)
{
OSL_FAIL("StyleSheet not found" );
return ;
}
ScTabViewShell& rViewShell = static_cast <ScTabViewTarget&>(rTarget).GetViewShell();
rViewShell.SetStyleSheetToMarked( pStyleSheet );
}
bool ScUndoSelectionStyle::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
ScUndoEnterMatrix::ScUndoEnterMatrix( ScDocShell& rNewDocShell, const ScRange& rArea,
ScDocumentUniquePtr pNewUndoDoc, OUString aForm ) :
ScBlockUndo( rNewDocShell, rArea, SC_UNDO_SIMPLE ),
pUndoDoc( std::move(pNewUndoDoc) ),
aFormula(std::move( aForm ))
{
SetChangeTrack();
}
ScUndoEnterMatrix::~ScUndoEnterMatrix()
{
}
OUString ScUndoEnterMatrix::GetComment() const
{
return ScResId( STR_UNDO_ENTERMATRIX );
}
void ScUndoEnterMatrix::SetChangeTrack()
{
ScDocument& rDoc = rDocShell.GetDocument();
ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
if ( pChangeTrack )
pChangeTrack->AppendContentRange( aBlockRange, *pUndoDoc,
nStartChangeAction, nEndChangeAction );
else
nStartChangeAction = nEndChangeAction = 0 ;
}
void ScUndoEnterMatrix::Undo()
{
BeginUndo();
ScDocument& rDoc = rDocShell.GetDocument();
rDoc.DeleteAreaTab( aBlockRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE );
pUndoDoc->CopyToDocument(aBlockRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false , rDoc);
rDocShell.PostPaint( aBlockRange, PaintPartFlags::Grid );
rDocShell.PostDataChanged();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
if (pViewShell)
pViewShell->CellContentChanged();
ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack();
if ( pChangeTrack )
pChangeTrack->Undo( nStartChangeAction, nEndChangeAction );
EndUndo();
}
void ScUndoEnterMatrix::Redo()
{
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument();
ScMarkData aDestMark(rDoc.GetSheetLimits());
aDestMark.SelectOneTable( aBlockRange.aStart.Tab() );
aDestMark.SetMarkArea( aBlockRange );
rDoc.InsertMatrixFormula( aBlockRange.aStart.Col(), aBlockRange.aStart.Row(),
aBlockRange.aEnd.Col(), aBlockRange.aEnd.Row(),
aDestMark, aFormula );
SetChangeTrack();
EndRedo();
}
void ScUndoEnterMatrix::Repeat(SfxRepeatTarget& rTarget)
{
if (auto pTabViewTarget = dynamic_cast <ScTabViewTarget*>(&rTarget))
{
OUString aTemp = aFormula;
ScDocument& rDoc = rDocShell.GetDocument();
pTabViewTarget->GetViewShell().EnterMatrix(aTemp, rDoc.GetGrammar());
}
}
bool ScUndoEnterMatrix::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
static ScRange lcl_GetMultiMarkRange( const ScMarkData& rMark )
{
OSL_ENSURE( rMark.IsMultiMarked(), "wrong mark type" );
return rMark.GetMultiMarkArea();
}
ScUndoIndent::ScUndoIndent( ScDocShell& rNewDocShell, const ScMarkData& rMark,
ScDocumentUniquePtr pNewUndoDoc, bool bIncrement ) :
ScBlockUndo( rNewDocShell, lcl_GetMultiMarkRange(rMark), SC_UNDO_AUTOHEIGHT ),
aMarkData( rMark ),
pUndoDoc( std::move(pNewUndoDoc) ),
bIsIncrement( bIncrement )
{
}
ScUndoIndent::~ScUndoIndent()
{
}
OUString ScUndoIndent::GetComment() const
{
TranslateId pId = bIsIncrement ? STR_UNDO_INC_INDENT : STR_UNDO_DEC_INDENT;
return ScResId(pId);
}
void ScUndoIndent::Undo()
{
BeginUndo();
ScDocument& rDoc = rDocShell.GetDocument();
SCTAB nTabCount = rDoc.GetTableCount();
ScRange aCopyRange = aBlockRange;
aCopyRange.aStart.SetTab(0 );
aCopyRange.aEnd.SetTab(nTabCount-1 );
pUndoDoc->CopyToDocument(aCopyRange, InsertDeleteFlags::ATTRIB, true , rDoc, &aMarkData);
rDocShell.PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
EndUndo();
}
void ScUndoIndent::Redo()
{
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument();
rDoc.ChangeSelectionIndent( bIsIncrement, aMarkData );
rDocShell.PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
EndRedo();
}
void ScUndoIndent::Repeat(SfxRepeatTarget& rTarget)
{
if (auto pViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget))
pViewTarget->GetViewShell().ChangeIndent( bIsIncrement );
}
bool ScUndoIndent::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
ScUndoTransliterate::ScUndoTransliterate( ScDocShell& rNewDocShell, const ScMarkData& rMark,
ScDocumentUniquePtr pNewUndoDoc, TransliterationFlags nType ) :
ScBlockUndo( rNewDocShell, lcl_GetMultiMarkRange(rMark), SC_UNDO_AUTOHEIGHT ),
aMarkData( rMark ),
pUndoDoc( std::move(pNewUndoDoc) ),
nTransliterationType( nType )
{
}
ScUndoTransliterate::~ScUndoTransliterate()
{
}
OUString ScUndoTransliterate::GetComment() const
{
return ScResId( STR_UNDO_TRANSLITERATE );
}
void ScUndoTransliterate::Undo()
{
BeginUndo();
ScDocument& rDoc = rDocShell.GetDocument();
SCTAB nTabCount = rDoc.GetTableCount();
ScRange aCopyRange = aBlockRange;
aCopyRange.aStart.SetTab(0 );
aCopyRange.aEnd.SetTab(nTabCount-1 );
pUndoDoc->CopyToDocument(aCopyRange, InsertDeleteFlags::CONTENTS, true , rDoc, &aMarkData);
rDocShell.PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
EndUndo();
}
void ScUndoTransliterate::Redo()
{
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument();
rDoc.TransliterateText( aMarkData, nTransliterationType );
rDocShell.PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
EndRedo();
}
void ScUndoTransliterate::Repeat(SfxRepeatTarget& rTarget)
{
if (auto pViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget))
pViewTarget->GetViewShell().TransliterateText( nTransliterationType );
}
bool ScUndoTransliterate::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
ScUndoClearItems::ScUndoClearItems( ScDocShell& rNewDocShell, const ScMarkData& rMark,
ScDocumentUniquePtr pNewUndoDoc, const sal_uInt16* pW ) :
ScBlockUndo( rNewDocShell, lcl_GetMultiMarkRange(rMark), SC_UNDO_AUTOHEIGHT ),
aMarkData( rMark ),
pUndoDoc( std::move(pNewUndoDoc) )
{
assert(pW && "ScUndoClearItems: Which-Pointer is Null" );
sal_uInt16 nCount = 0 ;
while ( pW[nCount] )
++nCount;
pWhich.reset( new sal_uInt16[nCount+1 ] );
for (sal_uInt16 i=0 ; i<=nCount; i++)
pWhich[i] = pW[i];
}
ScUndoClearItems::~ScUndoClearItems()
{
}
OUString ScUndoClearItems::GetComment() const
{
return ScResId( STR_UNDO_DELETECONTENTS );
}
void ScUndoClearItems::Undo()
{
BeginUndo();
ScDocument& rDoc = rDocShell.GetDocument();
pUndoDoc->CopyToDocument(aBlockRange, InsertDeleteFlags::ATTRIB, true , rDoc, &aMarkData);
rDocShell.PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
EndUndo();
}
void ScUndoClearItems::Redo()
{
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument();
rDoc.ClearSelectionItems( pWhich.get(), aMarkData );
rDocShell.PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
EndRedo();
}
void ScUndoClearItems::Repeat(SfxRepeatTarget& rTarget)
{
if (auto pViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget))
{
ScViewData& rViewData = pViewTarget->GetViewShell().GetViewData();
rViewData.GetDocFunc().ClearItems( rViewData.GetMarkData(), pWhich.get(), false );
}
}
bool ScUndoClearItems::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
// remove all line breaks of a table
ScUndoRemoveBreaks::ScUndoRemoveBreaks( ScDocShell& rNewDocShell,
SCTAB nNewTab, ScDocumentUniquePtr pNewUndoDoc ) :
ScSimpleUndo( rNewDocShell ),
nTab( nNewTab ),
pUndoDoc( std::move(pNewUndoDoc) )
{
}
ScUndoRemoveBreaks::~ScUndoRemoveBreaks()
{
}
OUString ScUndoRemoveBreaks::GetComment() const
{
return ScResId( STR_UNDO_REMOVEBREAKS );
}
void ScUndoRemoveBreaks::Undo()
{
BeginUndo();
ScDocument& rDoc = rDocShell.GetDocument();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
pUndoDoc->CopyToDocument(0 ,0 ,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, InsertDeleteFlags::NONE, false , rDoc);
if (pViewShell)
pViewShell->UpdatePageBreakData( true );
rDocShell.PostPaint( 0 ,0 ,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid );
EndUndo();
}
void ScUndoRemoveBreaks::Redo()
{
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
rDoc.RemoveManualBreaks(nTab);
rDoc.UpdatePageBreaks(nTab);
if (pViewShell)
pViewShell->UpdatePageBreakData( true );
rDocShell.PostPaint( 0 ,0 ,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::Grid );
EndRedo();
}
void ScUndoRemoveBreaks::Repeat(SfxRepeatTarget& rTarget)
{
if (auto pViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget))
{
ScTabViewShell& rViewShell = pViewTarget->GetViewShell();
rViewShell.RemoveManualBreaks();
}
}
bool ScUndoRemoveBreaks::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
ScUndoRemoveMerge::ScUndoRemoveMerge( ScDocShell& rNewDocShell,
const ScCellMergeOption& rOption, ScDocumentUniquePtr pNewUndoDoc ) :
ScBlockUndo( rNewDocShell, rOption.getFirstSingleRange(), SC_UNDO_SIMPLE ),
pUndoDoc( std::move(pNewUndoDoc) )
{
maOptions.push_back( rOption);
}
ScUndoRemoveMerge::ScUndoRemoveMerge( ScDocShell& rNewDocShell,
const ScRange& rRange, ScDocumentUniquePtr pNewUndoDoc ) :
ScBlockUndo( rNewDocShell, rRange, SC_UNDO_SIMPLE ),
pUndoDoc( std::move(pNewUndoDoc) )
{
}
ScUndoRemoveMerge::~ScUndoRemoveMerge()
{
}
OUString ScUndoRemoveMerge::GetComment() const
{
return ScResId( STR_UNDO_REMERGE ); // "remove merge"
}
ScDocument* ScUndoRemoveMerge::GetUndoDoc()
{
return pUndoDoc.get();
}
void ScUndoRemoveMerge::AddCellMergeOption( const ScCellMergeOption& rOption )
{
maOptions.push_back( rOption);
}
void ScUndoRemoveMerge::Undo()
{
using ::std::set;
SetCurTab();
BeginUndo();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
ScDocument& rDoc = rDocShell.GetDocument();
for (const auto & rOption : maOptions)
{
for (const auto & rTab : rOption.maTabs)
{
OSL_ENSURE(pUndoDoc, "NULL pUndoDoc!" );
if (!pUndoDoc)
continue ;
// There is no need to extend merge area because it's already been extended.
ScRange aRange = rOption.getSingleRange(rTab);
rDoc.DeleteAreaTab(aRange, InsertDeleteFlags::ATTRIB);
pUndoDoc->CopyToDocument(aRange, InsertDeleteFlags::ATTRIB, false , rDoc);
bool bDidPaint = false ;
if ( pViewShell )
{
pViewShell->SetTabNo(rTab);
bDidPaint = pViewShell->AdjustRowHeight(rOption.mnStartRow, rOption.mnEndRow, true );
}
if (!bDidPaint)
ScUndoUtil::PaintMore(rDocShell, aRange);
}
}
EndUndo();
}
void ScUndoRemoveMerge::Redo()
{
using ::std::set;
SetCurTab();
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument();
ScTabViewShell* pViewShell = ScTabViewShell::GetActiveViewShell();
for (const auto & rOption : maOptions)
{
for (const SCTAB nTab : rOption.maTabs)
{
// There is no need to extend merge area because it's already been extended.
ScRange aRange = rOption.getSingleRange(nTab);
const SfxPoolItem& rDefAttr = rDoc.GetPool()->GetUserOrPoolDefaultItem( ATTR_MERGE );
ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
aPattern.GetItemSet().Put( rDefAttr );
rDoc.ApplyPatternAreaTab( rOption.mnStartCol, rOption.mnStartRow,
rOption.mnEndCol, rOption.mnEndRow, nTab,
aPattern );
rDoc.RemoveFlagsTab( rOption.mnStartCol, rOption.mnStartRow,
rOption.mnEndCol, rOption.mnEndRow, nTab,
ScMF::Hor | ScMF::Ver );
rDoc.ExtendMerge(aRange, true );
// Paint
bool bDidPaint = false ;
if ( pViewShell )
{
pViewShell->SetTabNo(nTab);
bDidPaint = pViewShell->AdjustRowHeight(rOption.mnStartRow, rOption.mnEndRow, true );
}
if (!bDidPaint)
ScUndoUtil::PaintMore(rDocShell, aRange);
}
}
EndRedo();
}
void ScUndoRemoveMerge::Repeat(SfxRepeatTarget& rTarget)
{
if (auto pViewTarget = dynamic_cast <ScTabViewTarget*>( &rTarget))
pViewTarget->GetViewShell().RemoveMerge();
}
bool ScUndoRemoveMerge::CanRepeat(SfxRepeatTarget& rTarget) const
{
return dynamic_cast <const ScTabViewTarget*>( &rTarget) != nullptr;
}
void ScUndoRemoveMerge::SetCurTab()
{
SCTAB nCurTab = ScDocShell::GetCurTab();
aBlockRange.aStart.SetTab(nCurTab);
aBlockRange.aEnd.SetTab(nCurTab);
}
/** set only border, for ScRangeList (StarOne) */
static ScRange lcl_TotalRange( const ScRangeList& rRanges )
{
ScRange aTotal;
if ( !rRanges.empty() )
{
aTotal = rRanges[ 0 ];
for ( size_t i = 1 , nCount = rRanges.size(); i < nCount; ++i )
{
ScRange const & rRange = rRanges[ i ];
if (rRange.aStart.Col() < aTotal.aStart.Col()) aTotal.aStart.SetCol(rRange.aStart.Col());
if (rRange.aStart.Row() < aTotal.aStart.Row()) aTotal.aStart.SetRow(rRange.aStart.Row());
if (rRange.aStart.Tab() < aTotal.aStart.Tab()) aTotal.aStart.SetTab(rRange.aStart.Tab());
if (rRange.aEnd.Col() > aTotal.aEnd.Col() ) aTotal.aEnd.SetCol( rRange.aEnd.Col() );
if (rRange.aEnd.Row() > aTotal.aEnd.Row() ) aTotal.aEnd.SetRow( rRange.aEnd.Row() );
if (rRange.aEnd.Tab() > aTotal.aEnd.Tab() ) aTotal.aEnd.SetTab(rRange.aEnd.Tab() );
}
}
return aTotal;
}
ScUndoBorder::ScUndoBorder(ScDocShell& rNewDocShell,
const ScRangeList& rRangeList, ScDocumentUniquePtr pNewUndoDoc,
const SvxBoxItem& rNewOuter, const SvxBoxInfoItem& rNewInner)
: ScBlockUndo(rNewDocShell, lcl_TotalRange(rRangeList), SC_UNDO_SIMPLE)
, xUndoDoc(std::move(pNewUndoDoc))
{
xRanges.reset(new ScRangeList(rRangeList));
xOuter.reset(new SvxBoxItem(rNewOuter));
xInner.reset(new SvxBoxInfoItem(rNewInner));
}
OUString ScUndoBorder::GetComment() const
{
return ScResId( STR_UNDO_SELATTRLINES ); //! own string?
}
void ScUndoBorder::Undo()
{
BeginUndo();
ScDocument& rDoc = rDocShell.GetDocument();
ScMarkData aMarkData(rDoc.GetSheetLimits());
aMarkData.MarkFromRangeList(*xRanges, false );
xUndoDoc->CopyToDocument(aBlockRange, InsertDeleteFlags::ATTRIB, true , rDoc, &aMarkData);
rDocShell.PostPaint( aBlockRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
EndUndo();
}
void ScUndoBorder::Redo()
{
BeginRedo();
ScDocument& rDoc = rDocShell.GetDocument(); // call function at docfunc
size_t nCount = xRanges->size();
for (size_t i = 0 ; i < nCount; ++i )
{
ScRange const & rRange = (*xRanges)[i];
SCTAB nTab = rRange.aStart.Tab();
ScMarkData aMark(rDoc.GetSheetLimits());
aMark.SetMarkArea( rRange );
aMark.SelectTable( nTab, true );
rDoc.ApplySelectionFrame(aMark, *xOuter, xInner.get());
}
for (size_t i = 0 ; i < nCount; ++i)
rDocShell.PostPaint( (*xRanges)[i], PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
EndRedo();
}
void ScUndoBorder::Repeat(SfxRepeatTarget& /* rTarget */)
{
//TODO later (when the function has moved from cellsuno to docfunc)
}
bool ScUndoBorder::CanRepeat(SfxRepeatTarget& /* rTarget */) const
{
return false ; // See above
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=93 H=92 G=92
¤ Dauer der Verarbeitung: 0.34 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland