xref: /core/sc/source/ui/docshell/docfunc.cxx (revision 185e01c0)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <scitems.hxx>
21 
22 #include <comphelper/lok.hxx>
23 #include <sfx2/app.hxx>
24 #include <editeng/editobj.hxx>
25 #include <sfx2/linkmgr.hxx>
26 #include <sfx2/bindings.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/weld.hxx>
29 #include <vcl/waitobj.hxx>
30 #include <svx/svdocapt.hxx>
31 #include <sal/log.hxx>
32 #include <unotools/charclass.hxx>
33 
34 #include <com/sun/star/container/XNameContainer.hpp>
35 #include <com/sun/star/script/ModuleType.hpp>
36 #include <com/sun/star/script/XLibraryContainer.hpp>
37 #include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
38 
39 #include <docfunc.hxx>
40 
41 #include <sc.hrc>
42 #include <strings.hrc>
43 
44 #include <arealink.hxx>
45 #include <attrib.hxx>
46 #include <dociter.hxx>
47 #include <autoform.hxx>
48 #include <formulacell.hxx>
49 #include <cellmergeoption.hxx>
50 #include <detdata.hxx>
51 #include <detfunc.hxx>
52 #include <docpool.hxx>
53 #include <docsh.hxx>
54 #include <drwlayer.hxx>
55 #include <editutil.hxx>
56 #include <globstr.hrc>
57 #include <olinetab.hxx>
58 #include <patattr.hxx>
59 #include <rangenam.hxx>
60 #include <refundo.hxx>
61 #include <scresid.hxx>
62 #include <stlpool.hxx>
63 #include <stlsheet.hxx>
64 #include <tablink.hxx>
65 #include <tabvwsh.hxx>
66 #include <uiitems.hxx>
67 #include <undoblk.hxx>
68 #include <undocell.hxx>
69 #include <undodraw.hxx>
70 #include <undotab.hxx>
71 #include <sizedev.hxx>
72 #include <scmod.hxx>
73 #include <inputhdl.hxx>
74 #include <editable.hxx>
75 #include <compiler.hxx>
76 #include <scui_def.hxx>
77 #include <tabprotection.hxx>
78 #include <clipparam.hxx>
79 #include <externalrefmgr.hxx>
80 #include <undorangename.hxx>
81 #include <progress.hxx>
82 #include <dpobject.hxx>
83 #include <stringutil.hxx>
84 #include <cellvalue.hxx>
85 #include <tokenarray.hxx>
86 #include <rowheightcontext.hxx>
87 #include <cellvalues.hxx>
88 #include <undoconvert.hxx>
89 #include <docfuncutil.hxx>
90 #include <sheetevents.hxx>
91 #include <conditio.hxx>
92 #include <columnspanset.hxx>
93 
94 #include <memory>
95 #include <utility>
96 #include <basic/basmgr.hxx>
97 #include <set>
98 #include <vector>
99 
100 using namespace com::sun::star;
101 using ::std::vector;
102 
103 void ScDocFunc::NotifyDrawUndo( std::unique_ptr<SdrUndoAction> pUndoAction)
104 {
105     // #i101118# if drawing layer collects the undo actions, add it there
106     ScDrawLayer* pDrawLayer = rDocShell.GetDocument().GetDrawLayer();
107     if( pDrawLayer && pDrawLayer->IsRecording() )
108         pDrawLayer->AddCalcUndo( std::move(pUndoAction) );
109     else
110         rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDraw>( std::move(pUndoAction), &rDocShell ) );
111     rDocShell.SetDrawModified();
112 
113     // the affected sheet isn't known, so all stream positions are invalidated
114     ScDocument& rDoc = rDocShell.GetDocument();
115     SCTAB nTabCount = rDoc.GetTableCount();
116     for (SCTAB nTab=0; nTab<nTabCount; nTab++)
117         rDoc.SetStreamValid(nTab, false);
118 }
119 
120 //  paint row above the range (because of lines after AdjustRowHeight)
121 
122 static void lcl_PaintAbove( ScDocShell& rDocShell, const ScRange& rRange )
123 {
124     SCROW nRow = rRange.aStart.Row();
125     if ( nRow > 0 )
126     {
127         SCTAB nTab = rRange.aStart.Tab();   //! all of them?
128         --nRow;
129         rDocShell.PostPaint( ScRange(0,nRow,nTab, MAXCOL,nRow,nTab), PaintPartFlags::Grid );
130     }
131 }
132 
133 bool ScDocFunc::AdjustRowHeight( const ScRange& rRange, bool bPaint )
134 {
135     ScDocument& rDoc = rDocShell.GetDocument();
136     if ( rDoc.IsImportingXML() )
137     {
138         //  for XML import, all row heights are updated together after importing
139         return false;
140     }
141     if ( rDoc.IsAdjustHeightLocked() )
142     {
143         return false;
144     }
145 
146     SCTAB nTab      = rRange.aStart.Tab();
147     SCROW nStartRow = rRange.aStart.Row();
148     SCROW nEndRow   = rRange.aEnd.Row();
149 
150     ScSizeDeviceProvider aProv( &rDocShell );
151     Fraction aOne(1,1);
152 
153     sc::RowHeightContext aCxt(aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice());
154     bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab);
155     // tdf#76183: recalculate objects' positions
156     if (bChanged)
157         rDoc.SetDrawPageSize(nTab);
158 
159     if ( bPaint && bChanged )
160         rDocShell.PostPaint(ScRange(0, nStartRow, nTab, MAXCOL, MAXROW, nTab),
161                             PaintPartFlags::Grid | PaintPartFlags::Left);
162 
163     return bChanged;
164 }
165 
166 bool ScDocFunc::DetectiveAddPred(const ScAddress& rPos)
167 {
168     ScDocShellModificator aModificator( rDocShell );
169 
170     rDocShell.MakeDrawLayer();
171     ScDocument& rDoc = rDocShell.GetDocument();
172     bool bUndo (rDoc.IsUndoEnabled());
173     ScDrawLayer* pModel = rDoc.GetDrawLayer();
174     SCCOL nCol = rPos.Col();
175     SCROW nRow = rPos.Row();
176     SCTAB nTab = rPos.Tab();
177 
178     if (bUndo)
179         pModel->BeginCalcUndo(false);
180     bool bDone = ScDetectiveFunc( &rDoc,nTab ).ShowPred( nCol, nRow );
181     std::unique_ptr<SdrUndoGroup> pUndo;
182     if (bUndo)
183         pUndo = pModel->GetCalcUndo();
184     if (bDone)
185     {
186         ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDPRED );
187         rDoc.AddDetectiveOperation( aOperation );
188         if (bUndo)
189         {
190             rDocShell.GetUndoManager()->AddUndoAction(
191                         std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
192         }
193         aModificator.SetDocumentModified();
194         SfxBindings* pBindings = rDocShell.GetViewBindings();
195         if (pBindings)
196             pBindings->Invalidate( SID_DETECTIVE_REFRESH );
197     }
198 
199     return bDone;
200 }
201 
202 bool ScDocFunc::DetectiveDelPred(const ScAddress& rPos)
203 {
204     ScDocument& rDoc = rDocShell.GetDocument();
205 
206     bool bUndo(rDoc.IsUndoEnabled());
207     ScDrawLayer* pModel = rDoc.GetDrawLayer();
208     if (!pModel)
209         return false;
210 
211     ScDocShellModificator aModificator( rDocShell );
212 
213     SCCOL nCol = rPos.Col();
214     SCROW nRow = rPos.Row();
215     SCTAB nTab = rPos.Tab();
216 
217     if (bUndo)
218         pModel->BeginCalcUndo(false);
219     bool bDone = ScDetectiveFunc( &rDoc,nTab ).DeletePred( nCol, nRow );
220     std::unique_ptr<SdrUndoGroup> pUndo;
221     if (bUndo)
222         pUndo = pModel->GetCalcUndo();
223     if (bDone)
224     {
225         ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELPRED );
226         rDoc.AddDetectiveOperation( aOperation );
227         if (bUndo)
228         {
229             rDocShell.GetUndoManager()->AddUndoAction(
230                         std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
231         }
232         aModificator.SetDocumentModified();
233         SfxBindings* pBindings = rDocShell.GetViewBindings();
234         if (pBindings)
235             pBindings->Invalidate( SID_DETECTIVE_REFRESH );
236     }
237 
238     return bDone;
239 }
240 
241 bool ScDocFunc::DetectiveAddSucc(const ScAddress& rPos)
242 {
243     ScDocShellModificator aModificator( rDocShell );
244 
245     rDocShell.MakeDrawLayer();
246     ScDocument& rDoc = rDocShell.GetDocument();
247 
248     bool bUndo(rDoc.IsUndoEnabled());
249     ScDrawLayer* pModel = rDoc.GetDrawLayer();
250     SCCOL nCol = rPos.Col();
251     SCROW nRow = rPos.Row();
252     SCTAB nTab = rPos.Tab();
253 
254     if (bUndo)
255         pModel->BeginCalcUndo(false);
256     bool bDone = ScDetectiveFunc( &rDoc,nTab ).ShowSucc( nCol, nRow );
257     std::unique_ptr<SdrUndoGroup> pUndo;
258     if (bUndo)
259         pUndo = pModel->GetCalcUndo();
260     if (bDone)
261     {
262         ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDSUCC );
263         rDoc.AddDetectiveOperation( aOperation );
264         if (bUndo)
265         {
266             rDocShell.GetUndoManager()->AddUndoAction(
267                         std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
268         }
269         aModificator.SetDocumentModified();
270         SfxBindings* pBindings = rDocShell.GetViewBindings();
271         if (pBindings)
272             pBindings->Invalidate( SID_DETECTIVE_REFRESH );
273     }
274 
275     return bDone;
276 }
277 
278 bool ScDocFunc::DetectiveDelSucc(const ScAddress& rPos)
279 {
280     ScDocument& rDoc = rDocShell.GetDocument();
281 
282     bool bUndo (rDoc.IsUndoEnabled());
283     ScDrawLayer* pModel = rDoc.GetDrawLayer();
284     if (!pModel)
285         return false;
286 
287     ScDocShellModificator aModificator( rDocShell );
288 
289     SCCOL nCol = rPos.Col();
290     SCROW nRow = rPos.Row();
291     SCTAB nTab = rPos.Tab();
292 
293     if (bUndo)
294         pModel->BeginCalcUndo(false);
295     bool bDone = ScDetectiveFunc( &rDoc,nTab ).DeleteSucc( nCol, nRow );
296     std::unique_ptr<SdrUndoGroup> pUndo;
297     if (bUndo)
298         pUndo = pModel->GetCalcUndo();
299     if (bDone)
300     {
301         ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELSUCC );
302         rDoc.AddDetectiveOperation( aOperation );
303         if (bUndo)
304         {
305             rDocShell.GetUndoManager()->AddUndoAction(
306                         std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
307         }
308         aModificator.SetDocumentModified();
309         SfxBindings* pBindings = rDocShell.GetViewBindings();
310         if (pBindings)
311             pBindings->Invalidate( SID_DETECTIVE_REFRESH );
312     }
313 
314     return bDone;
315 }
316 
317 bool ScDocFunc::DetectiveAddError(const ScAddress& rPos)
318 {
319     ScDocShellModificator aModificator( rDocShell );
320 
321     rDocShell.MakeDrawLayer();
322     ScDocument& rDoc = rDocShell.GetDocument();
323 
324     bool bUndo (rDoc.IsUndoEnabled());
325     ScDrawLayer* pModel = rDoc.GetDrawLayer();
326     SCCOL nCol = rPos.Col();
327     SCROW nRow = rPos.Row();
328     SCTAB nTab = rPos.Tab();
329 
330     if (bUndo)
331         pModel->BeginCalcUndo(false);
332     bool bDone = ScDetectiveFunc( &rDoc,nTab ).ShowError( nCol, nRow );
333     std::unique_ptr<SdrUndoGroup> pUndo;
334     if (bUndo)
335         pUndo = pModel->GetCalcUndo();
336     if (bDone)
337     {
338         ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDERROR );
339         rDoc.AddDetectiveOperation( aOperation );
340         if (bUndo)
341         {
342             rDocShell.GetUndoManager()->AddUndoAction(
343                         std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
344         }
345         aModificator.SetDocumentModified();
346         SfxBindings* pBindings = rDocShell.GetViewBindings();
347         if (pBindings)
348             pBindings->Invalidate( SID_DETECTIVE_REFRESH );
349     }
350 
351     return bDone;
352 }
353 
354 bool ScDocFunc::DetectiveMarkInvalid(SCTAB nTab)
355 {
356     ScDocShellModificator aModificator( rDocShell );
357 
358     rDocShell.MakeDrawLayer();
359     ScDocument& rDoc = rDocShell.GetDocument();
360 
361     bool bUndo (rDoc.IsUndoEnabled());
362     ScDrawLayer* pModel = rDoc.GetDrawLayer();
363 
364     vcl::Window* pWaitWin = ScDocShell::GetActiveDialogParent();
365     if (pWaitWin)
366         pWaitWin->EnterWait();
367     if (bUndo)
368         pModel->BeginCalcUndo(false);
369     bool bOverflow;
370     bool bDone = ScDetectiveFunc( &rDoc,nTab ).MarkInvalid( bOverflow );
371     std::unique_ptr<SdrUndoGroup> pUndo;
372     if (bUndo)
373         pUndo = pModel->GetCalcUndo();
374     if (pWaitWin)
375         pWaitWin->LeaveWait();
376     if (bDone)
377     {
378         if (pUndo && bUndo)
379         {
380             pUndo->SetComment( ScResId( STR_UNDO_DETINVALID ) );
381             rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndo) );
382         }
383         aModificator.SetDocumentModified();
384         if ( bOverflow )
385         {
386             std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
387                                                           VclMessageType::Info, VclButtonsType::Ok,
388                                                           ScResId(STR_DETINVALID_OVERFLOW)));
389             xInfoBox->run();
390         }
391     }
392 
393     return bDone;
394 }
395 
396 bool ScDocFunc::DetectiveDelAll(SCTAB nTab)
397 {
398     ScDocument& rDoc = rDocShell.GetDocument();
399 
400     bool bUndo (rDoc.IsUndoEnabled());
401     ScDrawLayer* pModel = rDoc.GetDrawLayer();
402     if (!pModel)
403         return false;
404 
405     ScDocShellModificator aModificator( rDocShell );
406 
407     if (bUndo)
408         pModel->BeginCalcUndo(false);
409     bool bDone = ScDetectiveFunc( &rDoc,nTab ).DeleteAll( ScDetectiveDelete::Detective );
410     std::unique_ptr<SdrUndoGroup> pUndo;
411     if (bUndo)
412         pUndo = pModel->GetCalcUndo();
413     if (bDone)
414     {
415         ScDetOpList* pOldList = rDoc.GetDetOpList();
416         std::unique_ptr<ScDetOpList> pUndoList;
417         if (bUndo && pOldList)
418             pUndoList.reset(new ScDetOpList(*pOldList));
419 
420         rDoc.ClearDetectiveOperations();
421 
422         if (bUndo)
423         {
424             rDocShell.GetUndoManager()->AddUndoAction(
425                         std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), nullptr, std::move(pUndoList) ) );
426         }
427         aModificator.SetDocumentModified();
428         SfxBindings* pBindings = rDocShell.GetViewBindings();
429         if (pBindings)
430             pBindings->Invalidate( SID_DETECTIVE_REFRESH );
431     }
432 
433     return bDone;
434 }
435 
436 bool ScDocFunc::DetectiveRefresh( bool bAutomatic )
437 {
438     bool bDone = false;
439     ScDocument& rDoc = rDocShell.GetDocument();
440 
441     ScDetOpList* pList = rDoc.GetDetOpList();
442     if ( pList && pList->Count() )
443     {
444         rDocShell.MakeDrawLayer();
445         ScDrawLayer* pModel = rDoc.GetDrawLayer();
446         const bool bUndo (rDoc.IsUndoEnabled());
447         if (bUndo)
448             pModel->BeginCalcUndo(false);
449 
450         //  Delete in all sheets
451 
452         SCTAB nTabCount = rDoc.GetTableCount();
453         for (SCTAB nTab=0; nTab<nTabCount; nTab++)
454             ScDetectiveFunc( &rDoc,nTab ).DeleteAll( ScDetectiveDelete::Arrows );    // don't remove circles
455 
456         //  repeat
457 
458         size_t nCount = pList->Count();
459         for (size_t i=0; i < nCount; ++i)
460         {
461             const ScDetOpData& rData = pList->GetObject(i);
462             const ScAddress& aPos = rData.GetPos();
463             ScDetectiveFunc aFunc( &rDoc, aPos.Tab() );
464             SCCOL nCol = aPos.Col();
465             SCROW nRow = aPos.Row();
466             switch (rData.GetOperation())
467             {
468                 case SCDETOP_ADDSUCC:
469                     aFunc.ShowSucc( nCol, nRow );
470                     break;
471                 case SCDETOP_DELSUCC:
472                     aFunc.DeleteSucc( nCol, nRow );
473                     break;
474                 case SCDETOP_ADDPRED:
475                      aFunc.ShowPred( nCol, nRow );
476                      break;
477                 case SCDETOP_DELPRED:
478                     aFunc.DeletePred( nCol, nRow );
479                     break;
480                 case SCDETOP_ADDERROR:
481                     aFunc.ShowError( nCol, nRow );
482                     break;
483                 default:
484                     OSL_FAIL("wrong operation in DetectiveRefresh");
485             }
486         }
487 
488         if (bUndo)
489         {
490             std::unique_ptr<SdrUndoGroup> pUndo = pModel->GetCalcUndo();
491             if (pUndo)
492             {
493                 pUndo->SetComment( ScResId( STR_UNDO_DETREFRESH ) );
494                 // associate with the last action
495                 rDocShell.GetUndoManager()->AddUndoAction(
496                                                 std::make_unique<ScUndoDraw>( std::move(pUndo), &rDocShell ),
497                                                     bAutomatic );
498             }
499         }
500         rDocShell.SetDrawModified();
501         bDone = true;
502     }
503     return bDone;
504 }
505 
506 static void lcl_collectAllPredOrSuccRanges(
507     const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens, ScDocShell& rDocShell,
508     bool bPred)
509 {
510     ScDocument& rDoc = rDocShell.GetDocument();
511     vector<ScTokenRef> aRefTokens;
512     if (rSrcRanges.empty())
513         return;
514     ScRange const & rFrontRange = rSrcRanges.front();
515     ScDetectiveFunc aDetFunc(&rDoc, rFrontRange.aStart.Tab());
516     for (size_t i = 0, n = rSrcRanges.size(); i < n; ++i)
517     {
518         ScRange const & r = rSrcRanges[i];
519         if (bPred)
520         {
521             aDetFunc.GetAllPreds(
522                 r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens);
523         }
524         else
525         {
526             aDetFunc.GetAllSuccs(
527                 r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens);
528         }
529     }
530     rRefTokens.swap(aRefTokens);
531 }
532 
533 void ScDocFunc::DetectiveCollectAllPreds(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens)
534 {
535     lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, true);
536 }
537 
538 void ScDocFunc::DetectiveCollectAllSuccs(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens)
539 {
540     lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, false);
541 }
542 
543 bool ScDocFunc::DeleteContents(
544     const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi )
545 {
546     ScDocShellModificator aModificator( rDocShell );
547 
548     if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
549     {
550         OSL_FAIL("ScDocFunc::DeleteContents without markings");
551         return false;
552     }
553 
554     ScDocument& rDoc = rDocShell.GetDocument();
555 
556     if (bRecord && !rDoc.IsUndoEnabled())
557         bRecord = false;
558 
559     ScEditableTester aTester( &rDoc, rMark );
560     if (!aTester.IsEditable())
561     {
562         if (!bApi)
563             rDocShell.ErrorMessage(aTester.GetMessageId());
564         return false;
565     }
566 
567     ScRange aMarkRange;
568 
569     ScMarkData aMultiMark = rMark;
570     aMultiMark.SetMarking(false);       // for MarkToMulti
571 
572     ScDocumentUniquePtr pUndoDoc;
573     bool bMulti = aMultiMark.IsMultiMarked();
574     aMultiMark.MarkToMulti();
575     aMultiMark.GetMultiMarkArea( aMarkRange );
576     ScRange aExtendedRange(aMarkRange);
577     if ( rDoc.ExtendMerge( aExtendedRange, true ) )
578         bMulti = false;
579 
580     // no objects on protected tabs
581     bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark);
582 
583     sal_uInt16 nExtFlags = 0;       // extra flags are needed only if attributes are deleted
584     if ( nFlags & InsertDeleteFlags::ATTRIB )
585         rDocShell.UpdatePaintExt( nExtFlags, aMarkRange );
586 
587     //  order of operations:
588     //  1) BeginDrawUndo
589     //  2) Delete objects (DrawUndo will be filled)
590     //  3) Copy content for undo and set up undo actions
591     //  4) Delete content
592 
593     bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE);
594     if (bRecord && bDrawUndo)
595         rDoc.BeginDrawUndo();
596 
597     if (bObjects)
598     {
599         if (bMulti)
600             rDoc.DeleteObjectsInSelection( aMultiMark );
601         else
602             rDoc.DeleteObjectsInArea( aMarkRange.aStart.Col(), aMarkRange.aStart.Row(),
603                                        aMarkRange.aEnd.Col(),   aMarkRange.aEnd.Row(),
604                                        aMultiMark );
605     }
606 
607     // To keep track of all non-empty cells within the deleted area.
608     std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans;
609 
610     if ( bRecord )
611     {
612         pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, aMultiMark, aMarkRange, nFlags, bMulti);
613         pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, aMultiMark, aMarkRange);
614     }
615 
616     rDoc.DeleteSelection( nFlags, aMultiMark );
617 
618     // add undo action after drawing undo is complete (objects and note captions)
619     if( bRecord )
620     {
621         sc::DocFuncUtil::addDeleteContentsUndo(
622             rDocShell.GetUndoManager(), &rDocShell, aMultiMark, aExtendedRange,
623             std::move(pUndoDoc), nFlags, pDataSpans, bMulti, bDrawUndo);
624     }
625 
626     if (!AdjustRowHeight( aExtendedRange ))
627         rDocShell.PostPaint( aExtendedRange, PaintPartFlags::Grid, nExtFlags );
628     else if (nExtFlags & SC_PF_LINES)
629         lcl_PaintAbove( rDocShell, aExtendedRange );    // for lines above the range
630 
631     aModificator.SetDocumentModified();
632 
633     return true;
634 }
635 
636 bool ScDocFunc::DeleteCell(
637     const ScAddress& rPos, const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord )
638 {
639     ScDocShellModificator aModificator(rDocShell);
640 
641     ScDocument& rDoc = rDocShell.GetDocument();
642 
643     if (bRecord && !rDoc.IsUndoEnabled())
644         bRecord = false;
645 
646     ScEditableTester aTester(&rDoc, rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark);
647     if (!aTester.IsEditable())
648     {
649         rDocShell.ErrorMessage(aTester.GetMessageId());
650         return false;
651     }
652 
653     // no objects on protected tabs
654     bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark);
655 
656     sal_uInt16 nExtFlags = 0;       // extra flags are needed only if attributes are deleted
657     if (nFlags & InsertDeleteFlags::ATTRIB)
658         rDocShell.UpdatePaintExt(nExtFlags, rPos);
659 
660     //  order of operations:
661     //  1) BeginDrawUndo
662     //  2) delete objects (DrawUndo is filled)
663     //  3) copy contents for undo
664     //  4) delete contents
665     //  5) add undo-action
666 
667     bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE);     // needed for shown notes
668     if (bDrawUndo && bRecord)
669         rDoc.BeginDrawUndo();
670 
671     if (bObjects)
672         rDoc.DeleteObjectsInArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark);
673 
674     // To keep track of all non-empty cells within the deleted area.
675     std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans;
676 
677     ScDocumentUniquePtr pUndoDoc;
678     if (bRecord)
679     {
680         pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, rMark, rPos, nFlags, false);
681         pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, rMark, rPos);
682     }
683 
684     rDoc.DeleteArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark, nFlags);
685 
686     if (bRecord)
687     {
688         sc::DocFuncUtil::addDeleteContentsUndo(
689             rDocShell.GetUndoManager(), &rDocShell, rMark, rPos, std::move(pUndoDoc),
690             nFlags, pDataSpans, false, bDrawUndo);
691     }
692 
693     if (!AdjustRowHeight(rPos))
694         rDocShell.PostPaint(
695             rPos.Col(), rPos.Row(), rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Tab(),
696             PaintPartFlags::Grid, nExtFlags);
697 
698     aModificator.SetDocumentModified();
699 
700     return true;
701 }
702 
703 bool ScDocFunc::TransliterateText( const ScMarkData& rMark, TransliterationFlags nType,
704                                     bool bApi )
705 {
706     ScDocShellModificator aModificator( rDocShell );
707 
708     ScDocument& rDoc = rDocShell.GetDocument();
709     bool bRecord = true;
710     if (!rDoc.IsUndoEnabled())
711         bRecord = false;
712 
713     ScEditableTester aTester( &rDoc, rMark );
714     if (!aTester.IsEditable())
715     {
716         if (!bApi)
717             rDocShell.ErrorMessage(aTester.GetMessageId());
718         return false;
719     }
720 
721     ScRange aMarkRange;
722     ScMarkData aMultiMark = rMark;
723     aMultiMark.SetMarking(false);       // for MarkToMulti
724     aMultiMark.MarkToMulti();
725     aMultiMark.GetMultiMarkArea( aMarkRange );
726 
727     if (bRecord)
728     {
729         SCTAB nStartTab = aMarkRange.aStart.Tab();
730         SCTAB nTabCount = rDoc.GetTableCount();
731 
732         ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
733         pUndoDoc->InitUndo( &rDoc, nStartTab, nStartTab );
734         for (const auto& rTab : rMark)
735         {
736             if (rTab >= nTabCount)
737                 break;
738 
739             if (rTab != nStartTab)
740                 pUndoDoc->AddUndoTab( rTab, rTab );
741         }
742 
743         ScRange aCopyRange = aMarkRange;
744         aCopyRange.aStart.SetTab(0);
745         aCopyRange.aEnd.SetTab(nTabCount-1);
746         rDoc.CopyToDocument(aCopyRange, InsertDeleteFlags::CONTENTS, true, *pUndoDoc, &aMultiMark);
747 
748         rDocShell.GetUndoManager()->AddUndoAction(
749             std::make_unique<ScUndoTransliterate>( &rDocShell, aMultiMark, std::move(pUndoDoc), nType ) );
750     }
751 
752     rDoc.TransliterateText( aMultiMark, nType );
753 
754     if (!AdjustRowHeight( aMarkRange ))
755         rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid );
756 
757     aModificator.SetDocumentModified();
758 
759     return true;
760 }
761 
762 bool ScDocFunc::SetNormalString( bool& o_rbNumFmtSet, const ScAddress& rPos, const OUString& rText, bool bApi )
763 {
764     ScDocShellModificator aModificator( rDocShell );
765     ScDocument& rDoc = rDocShell.GetDocument();
766 
767     bool bUndo(rDoc.IsUndoEnabled());
768     ScEditableTester aTester( &rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
769     if (!aTester.IsEditable())
770     {
771         if (!bApi)
772             rDocShell.ErrorMessage(aTester.GetMessageId());
773         return false;
774     }
775 
776     bool bEditDeleted = (rDoc.GetCellType(rPos) == CELLTYPE_EDIT);
777     ScUndoEnterData::ValuesType aOldValues;
778 
779     if (bUndo)
780     {
781         ScUndoEnterData::Value aOldValue;
782 
783         aOldValue.mnTab = rPos.Tab();
784         aOldValue.maCell.assign(rDoc, rPos);
785 
786         const SfxPoolItem* pItem;
787         const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(),rPos.Row(),rPos.Tab() );
788         if ( SfxItemState::SET == pPattern->GetItemSet().GetItemState(
789                                 ATTR_VALUE_FORMAT,false,&pItem) )
790         {
791             aOldValue.mbHasFormat = true;
792             aOldValue.mnFormat = static_cast<const SfxUInt32Item*>(pItem)->GetValue();
793         }
794         else
795             aOldValue.mbHasFormat = false;
796 
797         aOldValues.push_back(aOldValue);
798     }
799 
800     o_rbNumFmtSet = rDoc.SetString( rPos.Col(), rPos.Row(), rPos.Tab(), rText );
801 
802     if (bUndo)
803     {
804         //  because of ChangeTracking, UndoAction can be created only after SetString was called
805         rDocShell.GetUndoManager()->AddUndoAction(
806             std::make_unique<ScUndoEnterData>(&rDocShell, rPos, aOldValues, rText, nullptr));
807     }
808 
809     if ( bEditDeleted || rDoc.HasAttrib( ScRange(rPos), HasAttrFlags::NeedHeight ) )
810         AdjustRowHeight( ScRange(rPos) );
811 
812     rDocShell.PostPaintCell( rPos );
813     aModificator.SetDocumentModified();
814 
815     // notify input handler here the same way as in PutCell
816     if (bApi)
817         NotifyInputHandler( rPos );
818 
819     return true;
820 }
821 
822 bool ScDocFunc::SetValueCell( const ScAddress& rPos, double fVal, bool bInteraction )
823 {
824     ScDocShellModificator aModificator( rDocShell );
825     ScDocument& rDoc = rDocShell.GetDocument();
826     bool bUndo = rDoc.IsUndoEnabled();
827 
828     bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
829 
830     ScCellValue aOldVal;
831     if (bUndo)
832         aOldVal.assign(rDoc, rPos);
833 
834     rDoc.SetValue(rPos, fVal);
835 
836     if (bUndo)
837     {
838         SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
839         ScCellValue aNewVal;
840         aNewVal.assign(rDoc, rPos);
841         pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
842     }
843 
844     if (bHeight)
845         AdjustRowHeight(rPos);
846 
847     rDocShell.PostPaintCell( rPos );
848     aModificator.SetDocumentModified();
849 
850     // #103934#; notify editline and cell in edit mode
851     if (!bInteraction)
852         NotifyInputHandler( rPos );
853 
854     return true;
855 }
856 
857 void ScDocFunc::SetValueCells( const ScAddress& rPos, const std::vector<double>& aVals, bool bInteraction )
858 {
859     // Check for invalid range.
860     SCROW nLastRow = rPos.Row() + aVals.size() - 1;
861     if (nLastRow > MAXROW)
862         // out of bound.
863         return;
864 
865     ScRange aRange(rPos);
866     aRange.aEnd.SetRow(nLastRow);
867 
868     ScDocShellModificator aModificator(rDocShell);
869     ScDocument& rDoc = rDocShell.GetDocument();
870 
871     if (rDoc.IsUndoEnabled())
872     {
873         std::unique_ptr<sc::UndoSetCells> pUndoObj(new sc::UndoSetCells(&rDocShell, rPos));
874         rDoc.TransferCellValuesTo(rPos, aVals.size(), pUndoObj->GetOldValues());
875         pUndoObj->SetNewValues(aVals);
876         SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
877         pUndoMgr->AddUndoAction(std::move(pUndoObj));
878     }
879 
880     rDoc.SetValues(rPos, aVals);
881 
882     rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
883     aModificator.SetDocumentModified();
884 
885     // #103934#; notify editline and cell in edit mode
886     if (!bInteraction)
887         NotifyInputHandler(rPos);
888 }
889 
890 bool ScDocFunc::SetStringCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction )
891 {
892     ScDocShellModificator aModificator( rDocShell );
893     ScDocument& rDoc = rDocShell.GetDocument();
894     bool bUndo = rDoc.IsUndoEnabled();
895 
896     bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
897 
898     ScCellValue aOldVal;
899     if (bUndo)
900         aOldVal.assign(rDoc, rPos);
901 
902     ScSetStringParam aParam;
903     aParam.setTextInput();
904     rDoc.SetString(rPos, rStr, &aParam);
905 
906     if (bUndo)
907     {
908         SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
909         ScCellValue aNewVal;
910         aNewVal.assign(rDoc, rPos);
911         pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
912     }
913 
914     if (bHeight)
915         AdjustRowHeight(rPos);
916 
917     rDocShell.PostPaintCell( rPos );
918     aModificator.SetDocumentModified();
919 
920     // #103934#; notify editline and cell in edit mode
921     if (!bInteraction)
922         NotifyInputHandler( rPos );
923 
924     return true;
925 }
926 
927 bool ScDocFunc::SetEditCell( const ScAddress& rPos, const EditTextObject& rStr, bool bInteraction )
928 {
929     ScDocShellModificator aModificator( rDocShell );
930     ScDocument& rDoc = rDocShell.GetDocument();
931     bool bUndo = rDoc.IsUndoEnabled();
932 
933     bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
934 
935     ScCellValue aOldVal;
936     if (bUndo)
937         aOldVal.assign(rDoc, rPos);
938 
939     rDoc.SetEditText(rPos, rStr.Clone());
940 
941     if (bUndo)
942     {
943         SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
944         ScCellValue aNewVal;
945         aNewVal.assign(rDoc, rPos);
946         pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
947     }
948 
949     if (bHeight)
950         AdjustRowHeight(rPos);
951 
952     rDocShell.PostPaintCell( rPos );
953     aModificator.SetDocumentModified();
954 
955     // #103934#; notify editline and cell in edit mode
956     if (!bInteraction)
957         NotifyInputHandler( rPos );
958 
959     return true;
960 }
961 
962 bool ScDocFunc::SetStringOrEditCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction )
963 {
964     ScDocument& rDoc = rDocShell.GetDocument();
965 
966     if (ScStringUtil::isMultiline(rStr))
967     {
968         ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
969         rEngine.SetText(rStr);
970         std::unique_ptr<EditTextObject> pEditText(rEngine.CreateTextObject());
971         return SetEditCell(rPos, *pEditText, bInteraction);
972     }
973     else
974         return SetStringCell(rPos, rStr, bInteraction);
975 }
976 
977 bool ScDocFunc::SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell, bool bInteraction )
978 {
979     std::unique_ptr<ScFormulaCell> xCell(pCell);
980 
981     ScDocShellModificator aModificator( rDocShell );
982     ScDocument& rDoc = rDocShell.GetDocument();
983     bool bUndo = rDoc.IsUndoEnabled();
984 
985     bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight);
986 
987     ScCellValue aOldVal;
988     if (bUndo)
989         aOldVal.assign(rDoc, rPos);
990 
991     pCell = rDoc.SetFormulaCell(rPos, xCell.release());
992 
993     // For performance reasons API calls may disable calculation while
994     // operating and recalculate once when done. If through user interaction
995     // and AutoCalc is disabled, calculate the formula (without its
996     // dependencies) once so the result matches the current document's content.
997     if (bInteraction && !rDoc.GetAutoCalc() && pCell)
998     {
999         // calculate just the cell once and set Dirty again
1000         pCell->Interpret();
1001         pCell->SetDirtyVar();
1002         rDoc.PutInFormulaTree( pCell);
1003     }
1004 
1005     if (bUndo)
1006     {
1007         SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
1008         ScCellValue aNewVal;
1009         aNewVal.assign(rDoc, rPos);
1010         pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
1011     }
1012 
1013     if (bHeight)
1014         AdjustRowHeight(rPos);
1015 
1016     rDocShell.PostPaintCell( rPos );
1017     aModificator.SetDocumentModified();
1018 
1019     // #103934#; notify editline and cell in edit mode
1020     if (!bInteraction)
1021         NotifyInputHandler( rPos );
1022 
1023     return true;
1024 }
1025 
1026 void ScDocFunc::NotifyInputHandler( const ScAddress& rPos )
1027 {
1028     ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
1029     if ( pViewSh && pViewSh->GetViewData().GetDocShell() == &rDocShell )
1030     {
1031         ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl();
1032         if ( pInputHdl && pInputHdl->GetCursorPos() == rPos )
1033         {
1034             bool bIsEditMode(pInputHdl->IsEditMode());
1035 
1036             // set modified if in editmode, because so the string is not set in the InputWindow like in the cell
1037             // (the cell shows the same like the InputWindow)
1038             if (bIsEditMode)
1039                 pInputHdl->SetModified();
1040             pViewSh->UpdateInputHandler(false, !bIsEditMode);
1041         }
1042     }
1043 }
1044 
1045         struct ScMyRememberItem
1046         {
1047             sal_Int32 const   nIndex;
1048             SfxItemSet const  aItemSet;
1049 
1050             ScMyRememberItem(const SfxItemSet& rItemSet, sal_Int32 nTempIndex) :
1051                 nIndex(nTempIndex), aItemSet(rItemSet) {}
1052         };
1053 
1054         typedef ::std::vector<std::unique_ptr<ScMyRememberItem>> ScMyRememberItemVector;
1055 
1056 void ScDocFunc::PutData( const ScAddress& rPos, ScEditEngineDefaulter& rEngine, bool bApi )
1057 {
1058     //  PutData calls PutCell or SetNormalString
1059 
1060     bool bRet = false;
1061     ScDocument& rDoc = rDocShell.GetDocument();
1062     ScEditAttrTester aTester( &rEngine );
1063     bool bEditCell = aTester.NeedsObject();
1064     if ( bEditCell )
1065     {
1066         // #i61702# With bLoseContent set, the content of rEngine isn't restored
1067         // (used in loading XML, where after the removeActionLock call the API object's
1068         // EditEngine isn't accessed again.
1069         bool bLoseContent = rDoc.IsImportingXML();
1070 
1071         bool bUpdateMode(rEngine.GetUpdateMode());
1072         if (bUpdateMode)
1073             rEngine.SetUpdateMode(false);
1074 
1075         ScMyRememberItemVector aRememberItems;
1076 
1077         //  All paragraph attributes must be removed before calling CreateTextObject,
1078         //  not only alignment, so the object doesn't contain the cell attributes as
1079         //  paragraph attributes. Before removing the attributes store them in a vector to
1080         //  set them back to the EditEngine.
1081         sal_Int32 nCount = rEngine.GetParagraphCount();
1082         for (sal_Int32 i=0; i<nCount; i++)
1083         {
1084             const SfxItemSet& rOld = rEngine.GetParaAttribs( i );
1085             if ( rOld.Count() )
1086             {
1087                 if ( !bLoseContent )
1088                 {
1089                     aRememberItems.push_back(std::make_unique<ScMyRememberItem>(rEngine.GetParaAttribs(i), i));
1090                 }
1091                 rEngine.SetParaAttribs( i, SfxItemSet( *rOld.GetPool(), rOld.GetRanges() ) );
1092             }
1093         }
1094 
1095         // A copy of pNewData will be stored in the cell.
1096         std::unique_ptr<EditTextObject> pNewData(rEngine.CreateTextObject());
1097         bRet = SetEditCell(rPos, *pNewData, !bApi);
1098 
1099         // Set the paragraph attributes back to the EditEngine.
1100         for (const auto& rxItem : aRememberItems)
1101         {
1102             rEngine.SetParaAttribs(rxItem->nIndex, rxItem->aItemSet);
1103         }
1104 
1105         // #i61702# if the content isn't accessed, there's no need to set the UpdateMode again
1106         if ( bUpdateMode && !bLoseContent )
1107             rEngine.SetUpdateMode(true);
1108     }
1109     else
1110     {
1111         OUString aText = rEngine.GetText();
1112         if (aText.isEmpty())
1113         {
1114             bool bNumFmtSet = false;
1115             bRet = SetNormalString( bNumFmtSet, rPos, aText, bApi );
1116         }
1117         else
1118             bRet = SetStringCell(rPos, aText, !bApi);
1119     }
1120 
1121     if ( bRet && aTester.NeedsCellAttr() )
1122     {
1123         const SfxItemSet& rEditAttr = aTester.GetAttribs();
1124         ScPatternAttr aPattern( rDoc.GetPool() );
1125         aPattern.GetFromEditItemSet( &rEditAttr );
1126         aPattern.DeleteUnchanged( rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ) );
1127         aPattern.GetItemSet().ClearItem( ATTR_HOR_JUSTIFY );    // wasn't removed above if no edit object
1128         if ( aPattern.GetItemSet().Count() > 0 )
1129         {
1130             ScMarkData aMark;
1131             aMark.SelectTable( rPos.Tab(), true );
1132             aMark.SetMarkArea( ScRange( rPos ) );
1133             ApplyAttributes( aMark, aPattern, bApi );
1134         }
1135     }
1136 }
1137 
1138 bool ScDocFunc::SetCellText(
1139     const ScAddress& rPos, const OUString& rText, bool bInterpret, bool bEnglish, bool bApi,
1140     const formula::FormulaGrammar::Grammar eGrammar )
1141 {
1142     bool bSet = false;
1143     if ( bInterpret )
1144     {
1145         if ( bEnglish )
1146         {
1147             ScDocument& rDoc = rDocShell.GetDocument();
1148 
1149             ::std::unique_ptr<ScExternalRefManager::ApiGuard> pExtRefGuard;
1150             if (bApi)
1151                 pExtRefGuard.reset(new ScExternalRefManager::ApiGuard(&rDoc));
1152 
1153             ScInputStringType aRes =
1154                 ScStringUtil::parseInputString(*rDoc.GetFormatTable(), rText, LANGUAGE_ENGLISH_US);
1155 
1156             switch (aRes.meType)
1157             {
1158                 case ScInputStringType::Formula:
1159                     bSet = SetFormulaCell(rPos, new ScFormulaCell(&rDoc, rPos, aRes.maText, eGrammar), !bApi);
1160                 break;
1161                 case ScInputStringType::Number:
1162                     bSet = SetValueCell(rPos, aRes.mfValue, !bApi);
1163                 break;
1164                 case ScInputStringType::Text:
1165                     bSet = SetStringOrEditCell(rPos, aRes.maText, !bApi);
1166                 break;
1167                 default:
1168                     ;
1169             }
1170         }
1171         // otherwise keep Null -> SetString with local formulas/number formats
1172     }
1173     else if (!rText.isEmpty())
1174     {
1175         bSet = SetStringOrEditCell(rPos, rText, !bApi);
1176     }
1177 
1178     if (!bSet)
1179     {
1180         bool bNumFmtSet = false;
1181         bSet = SetNormalString( bNumFmtSet, rPos, rText, bApi );
1182     }
1183     return bSet;
1184 }
1185 
1186 bool ScDocFunc::ShowNote( const ScAddress& rPos, bool bShow )
1187 {
1188     ScDocument& rDoc = rDocShell.GetDocument();
1189     ScPostIt* pNote = rDoc.GetNote( rPos );
1190     if( !pNote || (bShow == pNote->IsCaptionShown()) ||
1191         (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) )
1192         return false;
1193 
1194     // move the caption to internal or hidden layer and create undo action
1195     pNote->ShowCaption( rPos, bShow );
1196     if( rDoc.IsUndoEnabled() )
1197         rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideNote>( rDocShell, rPos, bShow ) );
1198 
1199     rDoc.SetStreamValid(rPos.Tab(), false);
1200 
1201     ScTabView::OnLOKNoteStateChanged(pNote);
1202 
1203     if (ScViewData* pViewData = ScDocShell::GetViewData())
1204     {
1205         if (ScDrawView* pDrawView = pViewData->GetScDrawView())
1206             pDrawView->SyncForGrid( pNote->GetCaption());
1207     }
1208 
1209     rDocShell.SetDocumentModified();
1210 
1211     return true;
1212 }
1213 
1214 void ScDocFunc::SetNoteText( const ScAddress& rPos, const OUString& rText, bool bApi )
1215 {
1216     ScDocShellModificator aModificator( rDocShell );
1217 
1218     ScDocument& rDoc = rDocShell.GetDocument();
1219     ScEditableTester aTester( &rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
1220     if (!aTester.IsEditable())
1221     {
1222         if (!bApi)
1223             rDocShell.ErrorMessage(aTester.GetMessageId());
1224         return;
1225     }
1226 
1227     OUString aNewText = convertLineEnd(rText, GetSystemLineEnd()); //! is this necessary ???
1228 
1229     if( ScPostIt* pNote = (!aNewText.isEmpty()) ? rDoc.GetOrCreateNote( rPos ) : rDoc.GetNote(rPos) )
1230         pNote->SetText( rPos, aNewText );
1231 
1232     //! Undo !!!
1233 
1234     rDoc.SetStreamValid(rPos.Tab(), false);
1235 
1236     rDocShell.PostPaintCell( rPos );
1237     aModificator.SetDocumentModified();
1238 }
1239 
1240 void ScDocFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate, bool bApi )
1241 {
1242     ScDocShellModificator aModificator( rDocShell );
1243     ScDocument& rDoc = rDocShell.GetDocument();
1244     ScEditableTester aTester( &rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
1245     if (aTester.IsEditable())
1246     {
1247         ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
1248         SfxUndoManager* pUndoMgr = (pDrawLayer && rDoc.IsUndoEnabled()) ? rDocShell.GetUndoManager() : nullptr;
1249 
1250         ScNoteData aOldData;
1251         std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos );
1252         sal_uInt32 nNoteId = 0;
1253         if( pOldNote )
1254         {
1255             nNoteId = pOldNote->GetId();
1256             // ensure existing caption object before draw undo tracking starts
1257             pOldNote->GetOrCreateCaption( rPos );
1258             // rescue note data for undo
1259             aOldData = pOldNote->GetNoteData();
1260         }
1261 
1262         // collect drawing undo actions for deleting/inserting caption objects
1263         if( pUndoMgr )
1264             pDrawLayer->BeginCalcUndo(false);
1265 
1266         // delete the note (creates drawing undo action for the caption object)
1267         bool hadOldNote(pOldNote);
1268         pOldNote.reset();
1269 
1270         // create new note (creates drawing undo action for the new caption object)
1271         ScNoteData aNewData;
1272         ScPostIt* pNewNote = nullptr;
1273         if( (pNewNote = ScNoteUtil::CreateNoteFromString( rDoc, rPos, rNoteText, false, true, nNoteId )) )
1274         {
1275             if( pAuthor ) pNewNote->SetAuthor( *pAuthor );
1276             if( pDate ) pNewNote->SetDate( *pDate );
1277 
1278             // rescue note data for undo
1279             aNewData = pNewNote->GetNoteData();
1280         }
1281 
1282         // create the undo action
1283         if( pUndoMgr && (aOldData.mxCaption || aNewData.mxCaption) )
1284             pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( rDocShell, rPos, aOldData, aNewData, pDrawLayer->GetCalcUndo() ) );
1285 
1286         // repaint cell (to make note marker visible)
1287         rDocShell.PostPaintCell( rPos );
1288 
1289         rDoc.SetStreamValid(rPos.Tab(), false);
1290 
1291         aModificator.SetDocumentModified();
1292 
1293         // Let our LOK clients know about the new/modified note
1294         if (pNewNote)
1295         {
1296             ScDocShell::LOKCommentNotify(hadOldNote ? LOKCommentNotificationType::Modify : LOKCommentNotificationType::Add,
1297                                          &rDoc, rPos, pNewNote);
1298         }
1299     }
1300     else if (!bApi)
1301     {
1302         rDocShell.ErrorMessage(aTester.GetMessageId());
1303     }
1304 }
1305 
1306 bool ScDocFunc::ApplyAttributes( const ScMarkData& rMark, const ScPatternAttr& rPattern,
1307                                     bool bApi )
1308 {
1309     ScDocument& rDoc = rDocShell.GetDocument();
1310     bool bRecord = true;
1311     if ( !rDoc.IsUndoEnabled() )
1312         bRecord = false;
1313 
1314     bool bImportingXML = rDoc.IsImportingXML();
1315     // Cell formats can still be set if the range isn't editable only because of matrix formulas.
1316     // #i62483# When loading XML, the check can be skipped altogether.
1317     bool bOnlyNotBecauseOfMatrix;
1318     if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix )
1319             && !bOnlyNotBecauseOfMatrix )
1320     {
1321         if (!bApi)
1322             rDocShell.ErrorMessage(STR_PROTECTIONERR);
1323         return false;
1324     }
1325 
1326     ScDocShellModificator aModificator( rDocShell );
1327 
1328     //! Border
1329 
1330     ScRange aMultiRange;
1331     bool bMulti = rMark.IsMultiMarked();
1332     if ( bMulti )
1333         rMark.GetMultiMarkArea( aMultiRange );
1334     else
1335         rMark.GetMarkArea( aMultiRange );
1336 
1337     if ( bRecord )
1338     {
1339         ScDocumentUniquePtr pUndoDoc( new ScDocument( SCDOCMODE_UNDO ));
1340         pUndoDoc->InitUndo( &rDoc, aMultiRange.aStart.Tab(), aMultiRange.aEnd.Tab() );
1341         rDoc.CopyToDocument(aMultiRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark);
1342 
1343         rDocShell.GetUndoManager()->AddUndoAction(
1344             std::make_unique<ScUndoSelectionAttr>(
1345                     &rDocShell, rMark,
1346                     aMultiRange.aStart.Col(), aMultiRange.aStart.Row(), aMultiRange.aStart.Tab(),
1347                     aMultiRange.aEnd.Col(), aMultiRange.aEnd.Row(), aMultiRange.aEnd.Tab(),
1348                     std::move(pUndoDoc), bMulti, &rPattern ) );
1349     }
1350 
1351     // While loading XML it is not necessary to ask HasAttrib. It needs too much time.
1352     sal_uInt16 nExtFlags = 0;
1353     if ( !bImportingXML )
1354         rDocShell.UpdatePaintExt( nExtFlags, aMultiRange );     // content before the change
1355 
1356     bool bChanged = false;
1357     rDoc.ApplySelectionPattern( rPattern, rMark, nullptr, &bChanged );
1358 
1359     if(bChanged)
1360     {
1361         if ( !bImportingXML )
1362             rDocShell.UpdatePaintExt( nExtFlags, aMultiRange );     // content after the change
1363 
1364         if (!AdjustRowHeight( aMultiRange ))
1365             rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid, nExtFlags );
1366         else if (nExtFlags & SC_PF_LINES)
1367             lcl_PaintAbove( rDocShell, aMultiRange );   // because of lines above the range
1368 
1369         aModificator.SetDocumentModified();
1370     }
1371 
1372     return true;
1373 }
1374 
1375 bool ScDocFunc::ApplyStyle( const ScMarkData& rMark, const OUString& rStyleName,
1376                                 bool bApi )
1377 {
1378     ScDocument& rDoc = rDocShell.GetDocument();
1379     bool bRecord = true;
1380     if ( !rDoc.IsUndoEnabled() )
1381         bRecord = false;
1382 
1383     bool bImportingXML = rDoc.IsImportingXML();
1384     // Cell formats can still be set if the range isn't editable only because of matrix formulas.
1385     // #i62483# When loading XML, the check can be skipped altogether.
1386     bool bOnlyNotBecauseOfMatrix;
1387     if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix )
1388             && !bOnlyNotBecauseOfMatrix )
1389     {
1390         if (!bApi)
1391             rDocShell.ErrorMessage(STR_PROTECTIONERR);
1392         return false;
1393     }
1394 
1395     ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>( rDoc.GetStyleSheetPool()->Find(
1396                                                 rStyleName, SfxStyleFamily::Para ));
1397     if (!pStyleSheet)
1398         return false;
1399 
1400     ScDocShellModificator aModificator( rDocShell );
1401 
1402     ScRange aMultiRange;
1403     bool bMulti = rMark.IsMultiMarked();
1404     if ( bMulti )
1405         rMark.GetMultiMarkArea( aMultiRange );
1406     else
1407         rMark.GetMarkArea( aMultiRange );
1408 
1409     if ( bRecord )
1410     {
1411         ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1412         SCTAB nStartTab = aMultiRange.aStart.Tab();
1413         SCTAB nTabCount = rDoc.GetTableCount();
1414         pUndoDoc->InitUndo( &rDoc, nStartTab, nStartTab );
1415         for (const auto& rTab : rMark)
1416         {
1417             if (rTab >= nTabCount)
1418                 break;
1419 
1420             if (rTab != nStartTab)
1421                 pUndoDoc->AddUndoTab( rTab, rTab );
1422         }
1423 
1424         ScRange aCopyRange = aMultiRange;
1425         aCopyRange.aStart.SetTab(0);
1426         aCopyRange.aEnd.SetTab(nTabCount-1);
1427         rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark );
1428 
1429         rDocShell.GetUndoManager()->AddUndoAction(
1430             std::make_unique<ScUndoSelectionStyle>(
1431                     &rDocShell, rMark, aMultiRange, rStyleName, std::move(pUndoDoc) ) );
1432 
1433     }
1434 
1435     rDoc.ApplySelectionStyle( *pStyleSheet, rMark );
1436 
1437     if (!AdjustRowHeight( aMultiRange ))
1438         rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid );
1439 
1440     aModificator.SetDocumentModified();
1441 
1442     return true;
1443 }
1444 
1445 namespace {
1446 
1447 /**
1448  * Check if this insertion attempt would end up cutting one or more pivot
1449  * tables in half, which is not desirable.
1450  *
1451  * @return true if this insertion can be done safely without shearing any
1452  *         existing pivot tables, false otherwise.
1453  */
1454 bool canInsertCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, InsCellCmd eCmd, const ScDocument* pDoc)
1455 {
1456     if (!pDoc->HasPivotTable())
1457         // This document has no pivot tables.
1458         return true;
1459 
1460     const ScDPCollection* pDPs = pDoc->GetDPCollection();
1461 
1462     ScRange aRange(rRange); // local copy
1463     switch (eCmd)
1464     {
1465         case INS_INSROWS_BEFORE:
1466         {
1467             aRange.aStart.SetCol(0);
1468             aRange.aEnd.SetCol(MAXCOL);
1469             [[fallthrough]];
1470         }
1471         case INS_CELLSDOWN:
1472         {
1473             auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
1474                 return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); });
1475             if (bIntersects)
1476                 // This column range cuts through at least one pivot table.  Not good.
1477                 return false;
1478 
1479             // Start row must be either at the top or above any pivot tables.
1480             if (aRange.aStart.Row() < 0)
1481                 // I don't know how to handle this case.
1482                 return false;
1483 
1484             if (aRange.aStart.Row() == 0)
1485                 // First row is always allowed.
1486                 return true;
1487 
1488             ScRange aTest(aRange);
1489             aTest.aStart.IncRow(-1); // Test one row up.
1490             aTest.aEnd.SetRow(aTest.aStart.Row());
1491             for (const auto& rTab : rMarkData)
1492             {
1493                 aTest.aStart.SetTab(rTab);
1494                 aTest.aEnd.SetTab(rTab);
1495                 if (pDPs->HasTable(aTest))
1496                     return false;
1497             }
1498         }
1499         break;
1500         case INS_INSCOLS_BEFORE:
1501         {
1502             aRange.aStart.SetRow(0);
1503             aRange.aEnd.SetRow(MAXROW);
1504             [[fallthrough]];
1505         }
1506         case INS_CELLSRIGHT:
1507         {
1508             auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
1509                 return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); });
1510             if (bIntersects)
1511                 // This column range cuts through at least one pivot table.  Not good.
1512                 return false;
1513 
1514             // Start row must be either at the top or above any pivot tables.
1515             if (aRange.aStart.Col() < 0)
1516                 // I don't know how to handle this case.
1517                 return false;
1518 
1519             if (aRange.aStart.Col() == 0)
1520                 // First row is always allowed.
1521                 return true;
1522 
1523             ScRange aTest(aRange);
1524             aTest.aStart.IncCol(-1); // Test one column to the left.
1525             aTest.aEnd.SetCol(aTest.aStart.Col());
1526             for (const auto& rTab : rMarkData)
1527             {
1528                 aTest.aStart.SetTab(rTab);
1529                 aTest.aEnd.SetTab(rTab);
1530                 if (pDPs->HasTable(aTest))
1531                     return false;
1532             }
1533         }
1534         break;
1535         default:
1536             ;
1537     }
1538     return true;
1539 }
1540 
1541 /**
1542  * Check if this deletion attempt would end up cutting one or more pivot
1543  * tables in half, which is not desirable.
1544  *
1545  * @return true if this deletion can be done safely without shearing any
1546  *         existing pivot tables, false otherwise.
1547  */
1548 bool canDeleteCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, DelCellCmd eCmd, const ScDocument* pDoc)
1549 {
1550     if (!pDoc->HasPivotTable())
1551         // This document has no pivot tables.
1552         return true;
1553 
1554     const ScDPCollection* pDPs = pDoc->GetDPCollection();
1555 
1556     ScRange aRange(rRange); // local copy
1557 
1558     switch (eCmd)
1559     {
1560         case DelCellCmd::Rows:
1561         {
1562             aRange.aStart.SetCol(0);
1563             aRange.aEnd.SetCol(MAXCOL);
1564             [[fallthrough]];
1565         }
1566         case DelCellCmd::CellsUp:
1567         {
1568             auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
1569                 return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); });
1570             if (bIntersects)
1571                 // This column range cuts through at least one pivot table.  Not good.
1572                 return false;
1573 
1574             ScRange aTest(aRange);
1575             for (const auto& rTab : rMarkData)
1576             {
1577                 aTest.aStart.SetTab(rTab);
1578                 aTest.aEnd.SetTab(rTab);
1579                 if (pDPs->HasTable(aTest))
1580                     return false;
1581             }
1582         }
1583         break;
1584         case DelCellCmd::Cols:
1585         {
1586             aRange.aStart.SetRow(0);
1587             aRange.aEnd.SetRow(MAXROW);
1588             [[fallthrough]];
1589         }
1590         case DelCellCmd::CellsLeft:
1591         {
1592             auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
1593                 return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); });
1594             if (bIntersects)
1595                 // This column range cuts through at least one pivot table.  Not good.
1596                 return false;
1597 
1598             ScRange aTest(aRange);
1599             for (const auto& rTab : rMarkData)
1600             {
1601                 aTest.aStart.SetTab(rTab);
1602                 aTest.aEnd.SetTab(rTab);
1603                 if (pDPs->HasTable(aTest))
1604                     return false;
1605             }
1606         }
1607         break;
1608         default:
1609             ;
1610     }
1611     return true;
1612 }
1613 
1614 }
1615 
1616 bool ScDocFunc::InsertCells( const ScRange& rRange, const ScMarkData* pTabMark, InsCellCmd eCmd,
1617                              bool bRecord, bool bApi, bool bPartOfPaste )
1618 {
1619     ScDocShellModificator aModificator( rDocShell );
1620 
1621     if (rDocShell.GetDocument().GetChangeTrack() &&
1622             ((eCmd == INS_CELLSDOWN  && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != MAXCOL)) ||
1623              (eCmd == INS_CELLSRIGHT && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != MAXROW))))
1624     {
1625         // We should not reach this via UI disabled slots.
1626         assert(bApi);
1627         SAL_WARN("sc.ui","ScDocFunc::InsertCells - no change-tracking of partial cell shift");
1628         return false;
1629     }
1630 
1631     ScRange aTargetRange( rRange );
1632 
1633     // If insertion is for full cols/rows and after the current
1634     // selection, then shift the range accordingly
1635     if ( eCmd == INS_INSROWS_AFTER )
1636     {
1637         ScRange aErrorRange( ScAddress::UNINITIALIZED );
1638         if (!aTargetRange.Move(0, rRange.aEnd.Row() - rRange.aStart.Row() + 1, 0, aErrorRange))
1639         {
1640             assert(!"can't move");
1641         }
1642     }
1643     if ( eCmd == INS_INSCOLS_AFTER )
1644     {
1645         ScRange aErrorRange( ScAddress::UNINITIALIZED );
1646         if (!aTargetRange.Move(rRange.aEnd.Col() - rRange.aStart.Col() + 1, 0, 0, aErrorRange))
1647         {
1648             assert(!"can't move");
1649         }
1650     }
1651 
1652     SCCOL nStartCol = aTargetRange.aStart.Col();
1653     SCROW nStartRow = aTargetRange.aStart.Row();
1654     SCTAB nStartTab = aTargetRange.aStart.Tab();
1655     SCCOL nEndCol = aTargetRange.aEnd.Col();
1656     SCROW nEndRow = aTargetRange.aEnd.Row();
1657     SCTAB nEndTab = aTargetRange.aEnd.Tab();
1658 
1659     if ( !ValidRow(nStartRow) || !ValidRow(nEndRow) )
1660     {
1661         OSL_FAIL("invalid row in InsertCells");
1662         return false;
1663     }
1664 
1665     ScDocument& rDoc = rDocShell.GetDocument();
1666     SCTAB nTabCount = rDoc.GetTableCount();
1667     SCCOL nPaintStartCol = nStartCol;
1668     SCROW nPaintStartRow = nStartRow;
1669     SCCOL nPaintEndCol = nEndCol;
1670     SCROW nPaintEndRow = nEndRow;
1671     PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
1672     bool bSuccess;
1673 
1674     ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();  //preserve current cursor position
1675     SCCOL nCursorCol = 0;
1676     SCROW nCursorRow = 0;
1677     if( pViewSh )
1678     {
1679         nCursorCol = pViewSh->GetViewData().GetCurX();
1680         nCursorRow = pViewSh->GetViewData().GetCurY();
1681     }
1682 
1683     if (bRecord && !rDoc.IsUndoEnabled())
1684         bRecord = false;
1685 
1686     ScMarkData aMark;
1687     if (pTabMark)
1688         aMark = *pTabMark;
1689     else
1690     {
1691         SCTAB nCount = 0;
1692         for( SCTAB i=0; i<nTabCount; i++ )
1693         {
1694             if( !rDoc.IsScenario(i) )
1695             {
1696                 nCount++;
1697                 if( nCount == nEndTab+1 )
1698                 {
1699                     aMark.SelectTable( i, true );
1700                     break;
1701                 }
1702             }
1703         }
1704     }
1705 
1706     ScMarkData aFullMark( aMark );          // including scenario sheets
1707     for (const auto& rTab : aMark)
1708     {
1709         if (rTab >= nTabCount)
1710             break;
1711 
1712         for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
1713             aFullMark.SelectTable( j, true );
1714     }
1715 
1716     SCTAB nSelCount = aMark.GetSelectCount();
1717 
1718     // Adjust also related scenarios
1719 
1720     SCCOL nMergeTestStartCol = nStartCol;
1721     SCROW nMergeTestStartRow = nStartRow;
1722     SCCOL nMergeTestEndCol = nEndCol;
1723     SCROW nMergeTestEndRow = nEndRow;
1724 
1725     ScRange aExtendMergeRange( aTargetRange );
1726 
1727     if( aTargetRange.aStart == aTargetRange.aEnd && rDoc.HasAttrib(aTargetRange, HasAttrFlags::Merged) )
1728     {
1729         rDoc.ExtendMerge( aExtendMergeRange );
1730         rDoc.ExtendOverlapped( aExtendMergeRange );
1731         nMergeTestEndCol = aExtendMergeRange.aEnd.Col();
1732         nMergeTestEndRow = aExtendMergeRange.aEnd.Row();
1733         nPaintEndCol = nMergeTestEndCol;
1734         nPaintEndRow = nMergeTestEndRow;
1735     }
1736 
1737     if ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER )
1738     {
1739         nMergeTestStartCol = 0;
1740         nMergeTestEndCol = MAXCOL;
1741     }
1742     if ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER )
1743     {
1744         nMergeTestStartRow = 0;
1745         nMergeTestEndRow = MAXROW;
1746     }
1747     if ( eCmd == INS_CELLSDOWN )
1748         nMergeTestEndRow = MAXROW;
1749     if ( eCmd == INS_CELLSRIGHT )
1750         nMergeTestEndCol = MAXCOL;
1751 
1752     bool bNeedRefresh = false;
1753 
1754     SCCOL nEditTestEndCol = (eCmd==INS_INSCOLS_BEFORE || eCmd==INS_INSCOLS_AFTER) ? MAXCOL : nMergeTestEndCol;
1755     SCROW nEditTestEndRow = (eCmd==INS_INSROWS_BEFORE || eCmd==INS_INSROWS_AFTER) ? MAXROW : nMergeTestEndRow;
1756 
1757     ScEditableTester aTester;
1758 
1759     switch (eCmd)
1760     {
1761         case INS_INSCOLS_BEFORE:
1762             aTester = ScEditableTester(
1763                 rDoc, sc::ColRowEditAction::InsertColumnsBefore, nMergeTestStartCol, nMergeTestEndCol, aMark);
1764             break;
1765         case INS_INSCOLS_AFTER:
1766             aTester = ScEditableTester(
1767                 rDoc, sc::ColRowEditAction::InsertColumnsAfter, nMergeTestStartCol, nMergeTestEndCol, aMark);
1768             break;
1769         case INS_INSROWS_BEFORE:
1770             aTester = ScEditableTester(
1771                 rDoc, sc::ColRowEditAction::InsertRowsBefore, nMergeTestStartRow, nMergeTestEndRow, aMark);
1772             break;
1773         case INS_INSROWS_AFTER:
1774             aTester = ScEditableTester(
1775                 rDoc, sc::ColRowEditAction::InsertRowsAfter, nMergeTestStartRow, nMergeTestEndRow, aMark);
1776             break;
1777         default:
1778             aTester = ScEditableTester(
1779                 &rDoc, nMergeTestStartCol, nMergeTestStartRow, nEditTestEndCol, nEditTestEndRow, aMark);
1780     }
1781 
1782     if (!aTester.IsEditable())
1783     {
1784         if (!bApi)
1785             rDocShell.ErrorMessage(aTester.GetMessageId());
1786         return false;
1787     }
1788 
1789     // Check if this insertion is allowed with respect to pivot table.
1790     if (!canInsertCellsByPivot(aTargetRange, aMark, eCmd, &rDoc))
1791     {
1792         if (!bApi)
1793             rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE);
1794         return false;
1795     }
1796 
1797     WaitObject aWait( ScDocShell::GetActiveDialogParent() );      // important due to TrackFormulas at UpdateReference
1798 
1799     ScDocumentUniquePtr pRefUndoDoc;
1800     std::unique_ptr<ScRefUndoData> pUndoData;
1801     if ( bRecord )
1802     {
1803         pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
1804         pRefUndoDoc->InitUndo( &rDoc, 0, nTabCount-1 );
1805 
1806         // pRefUndoDoc is filled in InsertCol / InsertRow
1807 
1808         pUndoData.reset(new ScRefUndoData( &rDoc ));
1809 
1810         rDoc.BeginDrawUndo();
1811     }
1812 
1813     // #i8302 : we unmerge overwhelming ranges, before insertion all the actions are put in the same ListAction
1814     // the patch comes from mloiseleur and maoyg
1815     bool bInsertMerge = false;
1816     std::vector<ScRange> qIncreaseRange;
1817     OUString aUndo = ScResId( STR_UNDO_INSERTCELLS );
1818     if (bRecord)
1819     {
1820         ViewShellId nViewShellId(-1);
1821         if (pViewSh)
1822             nViewShellId = pViewSh->GetViewShellId();
1823         rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
1824     }
1825     std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge;
1826 
1827     for (const SCTAB i : aMark)
1828     {
1829         if (i >= nTabCount)
1830             break;
1831 
1832         if( rDoc.HasAttrib( nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
1833         {
1834             if (eCmd==INS_CELLSRIGHT)
1835                 bNeedRefresh = true;
1836 
1837             SCCOL nMergeStartCol = nMergeTestStartCol;
1838             SCROW nMergeStartRow = nMergeTestStartRow;
1839             SCCOL nMergeEndCol   = nMergeTestEndCol;
1840             SCROW nMergeEndRow   = nMergeTestEndRow;
1841 
1842             rDoc.ExtendMerge( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
1843             rDoc.ExtendOverlapped( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
1844 
1845             if(( eCmd == INS_CELLSDOWN && ( nMergeStartCol != nMergeTestStartCol || nMergeEndCol != nMergeTestEndCol )) ||
1846                 (eCmd == INS_CELLSRIGHT && ( nMergeStartRow != nMergeTestStartRow || nMergeEndRow != nMergeTestEndRow )) )
1847             {
1848                 if (!bApi)
1849                     rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);
1850                 rDocShell.GetUndoManager()->LeaveListAction();
1851                 return false;
1852             }
1853 
1854             SCCOL nTestCol = -1;
1855             SCROW nTestRow1 = -1;
1856             SCROW nTestRow2 = -1;
1857 
1858             ScDocAttrIterator aTestIter( &rDoc, i, nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow );
1859             ScRange aExtendRange( nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i );
1860             const ScPatternAttr* pPattern = nullptr;
1861             while ( ( pPattern = aTestIter.GetNext( nTestCol, nTestRow1, nTestRow2 ) ) != nullptr )
1862             {
1863                 const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
1864                 const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
1865                 ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver);
1866                 if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver)
1867                 {
1868                     ScRange aRange( nTestCol, nTestRow1, i );
1869                     rDoc.ExtendOverlapped(aRange);
1870                     rDoc.ExtendMerge(aRange, true);
1871 
1872                     if( nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor )
1873                     {
1874                         for( SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++ )
1875                         {
1876                             ScRange aTestRange( nTestCol, nTestRow, i );
1877                             rDoc.ExtendOverlapped( aTestRange );
1878                             rDoc.ExtendMerge( aTestRange, true);
1879                             ScRange aMergeRange( aTestRange.aStart.Col(),aTestRange.aStart.Row(), i );
1880                             if( !aExtendRange.In( aMergeRange ) )
1881                             {
1882                                 qIncreaseRange.push_back( aTestRange );
1883                                 bInsertMerge = true;
1884                             }
1885                         }
1886                     }
1887                     else
1888                     {
1889                         ScRange aMergeRange( aRange.aStart.Col(),aRange.aStart.Row(), i );
1890                         if( !aExtendRange.In( aMergeRange ) )
1891                         {
1892                             qIncreaseRange.push_back( aRange );
1893                         }
1894                         bInsertMerge = true;
1895                     }
1896                 }
1897             }
1898 
1899             if( bInsertMerge )
1900             {
1901                 if( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER || eCmd == INS_CELLSDOWN )
1902                 {
1903                     nStartRow = aExtendMergeRange.aStart.Row();
1904                     nEndRow = aExtendMergeRange.aEnd.Row();
1905 
1906                     if( eCmd == INS_CELLSDOWN )
1907                         nEndCol = nMergeTestEndCol;
1908                     else
1909                     {
1910                         nStartCol = 0;
1911                         nEndCol = MAXCOL;
1912                     }
1913                 }
1914                 else if( eCmd == INS_CELLSRIGHT || eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER )
1915                 {
1916 
1917                     nStartCol = aExtendMergeRange.aStart.Col();
1918                     nEndCol = aExtendMergeRange.aEnd.Col();
1919                     if( eCmd == INS_CELLSRIGHT )
1920                     {
1921                         nEndRow = nMergeTestEndRow;
1922                     }
1923                     else
1924                     {
1925                         nStartRow = 0;
1926                         nEndRow = MAXROW;
1927                     }
1928                 }
1929 
1930                 if( !qIncreaseRange.empty() )
1931                 {
1932                     if (bRecord && !pUndoRemoveMerge)
1933                     {
1934                         ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1935                         pUndoDoc->InitUndo( &rDoc, *aMark.begin(), *aMark.rbegin());
1936                         pUndoRemoveMerge.reset( new ScUndoRemoveMerge( &rDocShell, rRange, std::move(pUndoDoc) ));
1937                     }
1938 
1939                     for( const ScRange& aRange : qIncreaseRange )
1940                     {
1941                         if( rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
1942                         {
1943                             UnmergeCells( aRange, bRecord, pUndoRemoveMerge.get() );
1944                         }
1945                     }
1946                 }
1947             }
1948             else
1949             {
1950                 if (!bApi)
1951                     rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);
1952                 rDocShell.GetUndoManager()->LeaveListAction();
1953                 return false;
1954             }
1955         }
1956     }
1957 
1958     if (bRecord && pUndoRemoveMerge)
1959     {
1960         rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndoRemoveMerge));
1961     }
1962 
1963     switch (eCmd)
1964     {
1965         case INS_CELLSDOWN:
1966             bSuccess = rDoc.InsertRow( nStartCol, 0, nEndCol, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &aFullMark );
1967             nPaintEndRow = MAXROW;
1968             break;
1969         case INS_INSROWS_BEFORE:
1970         case INS_INSROWS_AFTER:
1971             bSuccess = rDoc.InsertRow( 0, 0, MAXCOL, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &aFullMark );
1972             nPaintStartCol = 0;
1973             nPaintEndCol = MAXCOL;
1974             nPaintEndRow = MAXROW;
1975             nPaintFlags |= PaintPartFlags::Left;
1976             break;
1977         case INS_CELLSRIGHT:
1978             bSuccess = rDoc.InsertCol( nStartRow, 0, nEndRow, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &aFullMark );
1979             nPaintEndCol = MAXCOL;
1980             break;
1981         case INS_INSCOLS_BEFORE:
1982         case INS_INSCOLS_AFTER:
1983             bSuccess = rDoc.InsertCol( 0, 0, MAXROW, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &aFullMark );
1984             nPaintStartRow = 0;
1985             nPaintEndRow = MAXROW;
1986             nPaintEndCol = MAXCOL;
1987             nPaintFlags |= PaintPartFlags::Top;
1988             break;
1989         default:
1990             OSL_FAIL("Wrong code at inserting");
1991             bSuccess = false;
1992             break;
1993     }
1994 
1995     if ( bSuccess )
1996     {
1997         SCTAB  nUndoPos  = 0;
1998 
1999         if ( bRecord )
2000         {
2001             std::unique_ptr<SCTAB[]> pTabs(new SCTAB[nSelCount]);
2002             std::unique_ptr<SCTAB[]> pScenarios(new SCTAB[nSelCount]);
2003             nUndoPos    = 0;
2004             for (const auto& rTab : aMark)
2005             {
2006                 if (rTab >= nTabCount)
2007                     break;
2008 
2009                 SCTAB nCount = 0;
2010                 for( SCTAB j=rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2011                     nCount ++;
2012 
2013                 pScenarios[nUndoPos] = nCount;
2014                 pTabs[nUndoPos] = rTab;
2015                 nUndoPos ++;
2016             }
2017 
2018             if( !bInsertMerge )
2019             {
2020                 rDocShell.GetUndoManager()->LeaveListAction();
2021             }
2022 
2023             rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoInsertCells>(
2024                 &rDocShell, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ),
2025                 nUndoPos, std::move(pTabs), std::move(pScenarios), eCmd, std::move(pRefUndoDoc), std::move(pUndoData), bPartOfPaste ) );
2026         }
2027 
2028         // #i8302 : we remerge growing ranges, with the new part inserted
2029 
2030         while( !qIncreaseRange.empty() )
2031         {
2032             ScRange aRange = qIncreaseRange.back();
2033             if( !rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
2034             {
2035                 switch (eCmd)
2036                 {
2037                     case INS_CELLSDOWN:
2038                     case INS_INSROWS_BEFORE:
2039                     case INS_INSROWS_AFTER:
2040                         aRange.aEnd.IncRow(static_cast<SCCOL>(nEndRow-nStartRow+1));
2041                         break;
2042                     case INS_CELLSRIGHT:
2043                     case INS_INSCOLS_BEFORE:
2044                     case INS_INSCOLS_AFTER:
2045                         aRange.aEnd.IncCol(static_cast<SCCOL>(nEndCol-nStartCol+1));
2046                         break;
2047                     default:
2048                         break;
2049                 }
2050                 ScCellMergeOption aMergeOption(
2051                     aRange.aStart.Col(), aRange.aStart.Row(),
2052                     aRange.aEnd.Col(), aRange.aEnd.Row() );
2053                 aMergeOption.maTabs.insert(aRange.aStart.Tab());
2054                 MergeCells(aMergeOption, false, true, true);
2055             }
2056             qIncreaseRange.pop_back();
2057         }
2058 
2059         if( bInsertMerge )
2060             rDocShell.GetUndoManager()->LeaveListAction();
2061 
2062         for (const SCTAB i : aMark)
2063         {
2064             if (i >= nTabCount)
2065                 break;
2066 
2067             rDoc.SetDrawPageSize(i);
2068 
2069             if (bNeedRefresh)
2070                 rDoc.ExtendMerge( nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow, i, true );
2071             else
2072                 rDoc.RefreshAutoFilter( nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow, i );
2073 
2074             if ( eCmd == INS_INSROWS_BEFORE ||eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSROWS_AFTER ||eCmd == INS_INSCOLS_AFTER )
2075                 rDoc.UpdatePageBreaks( i );
2076 
2077             sal_uInt16 nExtFlags = 0;
2078             rDocShell.UpdatePaintExt( nExtFlags, nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i );
2079 
2080             SCTAB nScenarioCount = 0;
2081 
2082             for( SCTAB j = i+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2083                 nScenarioCount ++;
2084 
2085             bool bAdjusted = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER ) ?
2086                         AdjustRowHeight(ScRange(0, nStartRow, i, MAXCOL, nEndRow, i+nScenarioCount )) :
2087                         AdjustRowHeight(ScRange(0, nPaintStartRow, i, MAXCOL, nPaintEndRow, i+nScenarioCount ));
2088             if (bAdjusted)
2089             {
2090                 //  paint only what is not done by AdjustRowHeight
2091                 if (nPaintFlags & PaintPartFlags::Top)
2092                     rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i+nScenarioCount, PaintPartFlags::Top );
2093             }
2094             else
2095                 rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i+nScenarioCount, nPaintFlags, nExtFlags );
2096         }
2097     }
2098     else
2099     {
2100         if( bInsertMerge )
2101         {
2102             while( !qIncreaseRange.empty() )
2103             {
2104                 ScRange aRange = qIncreaseRange.back();
2105                 ScCellMergeOption aMergeOption(
2106                     aRange.aStart.Col(), aRange.aStart.Row(),
2107                     aRange.aEnd.Col(), aRange.aEnd.Row() );
2108                 MergeCells(aMergeOption, false, true, true);
2109                 qIncreaseRange.pop_back();
2110             }
2111 
2112             if( pViewSh )
2113             {
2114                 pViewSh->MarkRange( aTargetRange, false );
2115                 pViewSh->SetCursor( nCursorCol, nCursorRow );
2116             }
2117         }
2118 
2119         rDocShell.GetUndoManager()->LeaveListAction();
2120         rDocShell.GetUndoManager()->RemoveLastUndoAction();
2121 
2122         pRefUndoDoc.reset();
2123         if (!bApi)
2124             rDocShell.ErrorMessage(STR_INSERT_FULL);        // column/row full
2125     }
2126 
2127     // The cursor position needs to be modified earlier than updating
2128     // any enabled edit view which is triggered by SetDocumentModified below.
2129     if (bSuccess)
2130     {
2131         bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
2132         bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
2133 
2134         if (bInsertCols)
2135         {
2136             pViewSh->OnLOKInsertDeleteColumn(rRange.aStart.Col(), 1);
2137         }
2138 
2139         if (bInsertRows)
2140         {
2141             pViewSh->OnLOKInsertDeleteRow(rRange.aStart.Row(), 1);
2142         }
2143     }
2144 
2145     aModificator.SetDocumentModified();
2146 
2147     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
2148     return bSuccess;
2149 }
2150 
2151 bool ScDocFunc::DeleteCells( const ScRange& rRange, const ScMarkData* pTabMark, DelCellCmd eCmd,
2152                              bool bApi )
2153 {
2154     ScDocShellModificator aModificator( rDocShell );
2155 
2156     if (rDocShell.GetDocument().GetChangeTrack() &&
2157             ((eCmd == DelCellCmd::CellsUp   && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != MAXCOL)) ||
2158              (eCmd == DelCellCmd::CellsLeft && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != MAXROW))))
2159     {
2160         // We should not reach this via UI disabled slots.
2161         assert(bApi);
2162         SAL_WARN("sc.ui","ScDocFunc::DeleteCells - no change-tracking of partial cell shift");
2163         return false;
2164     }
2165 
2166     SCCOL nStartCol = rRange.aStart.Col();
2167     SCROW nStartRow = rRange.aStart.Row();
2168     SCTAB nStartTab = rRange.aStart.Tab();
2169     SCCOL nEndCol = rRange.aEnd.Col();
2170     SCROW nEndRow = rRange.aEnd.Row();
2171     SCTAB nEndTab = rRange.aEnd.Tab();
2172 
2173     if ( !ValidRow(nStartRow) || !ValidRow(nEndRow) )
2174     {
2175         OSL_FAIL("invalid row in DeleteCells");
2176         return false;
2177     }
2178 
2179     ScDocument& rDoc = rDocShell.GetDocument();
2180     SCTAB nTabCount = rDoc.GetTableCount();
2181     SCCOL nPaintStartCol = nStartCol;
2182     SCROW nPaintStartRow = nStartRow;
2183     SCCOL nPaintEndCol = nEndCol;
2184     SCROW nPaintEndRow = nEndRow;
2185     PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
2186 
2187     bool bRecord = true;
2188     if (!rDoc.IsUndoEnabled())
2189         bRecord = false;
2190 
2191     ScMarkData aMark;
2192     if (pTabMark)
2193         aMark = *pTabMark;
2194     else
2195     {
2196         SCTAB nCount = 0;
2197         for(SCTAB i=0; i<nTabCount; i++ )
2198         {
2199             if( !rDoc.IsScenario(i) )
2200             {
2201                 nCount++;
2202                 if( nCount == nEndTab+1 )
2203                 {
2204                     aMark.SelectTable(i, true);
2205                     break;
2206                 }
2207             }
2208         }
2209     }
2210 
2211     ScMarkData aFullMark( aMark );          // including scenario sheets
2212     for (const auto& rTab : aMark)
2213     {
2214         if (rTab >= nTabCount)
2215             break;
2216 
2217         for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2218             aFullMark.SelectTable( j, true );
2219     }
2220 
2221     SCTAB nSelCount = aMark.GetSelectCount();
2222 
2223     SCCOL nUndoStartCol = nStartCol;
2224     SCROW nUndoStartRow = nStartRow;
2225     SCCOL nUndoEndCol = nEndCol;
2226     SCROW nUndoEndRow = nEndRow;
2227 
2228     ScRange aExtendMergeRange( rRange );
2229 
2230     if( rRange.aStart == rRange.aEnd && rDoc.HasAttrib(rRange, HasAttrFlags::Merged) )
2231     {
2232         rDoc.ExtendMerge( aExtendMergeRange );
2233         rDoc.ExtendOverlapped( aExtendMergeRange );
2234         nUndoEndCol = aExtendMergeRange.aEnd.Col();
2235         nUndoEndRow = aExtendMergeRange.aEnd.Row();
2236         nPaintEndCol = nUndoEndCol;
2237         nPaintEndRow = nUndoEndRow;
2238     }
2239 
2240     if (eCmd==DelCellCmd::Rows)
2241     {
2242         nUndoStartCol = 0;
2243         nUndoEndCol = MAXCOL;
2244     }
2245     if (eCmd==DelCellCmd::Cols)
2246     {
2247         nUndoStartRow = 0;
2248         nUndoEndRow = MAXROW;
2249     }
2250                     // Test for cell protection
2251 
2252     SCCOL nEditTestEndX = nUndoEndCol;
2253     if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft )
2254         nEditTestEndX = MAXCOL;
2255     SCROW nEditTestEndY = nUndoEndRow;
2256     if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp )
2257         nEditTestEndY = MAXROW;
2258 
2259     ScEditableTester aTester;
2260 
2261     switch (eCmd)
2262     {
2263         case DelCellCmd::Cols:
2264             aTester = ScEditableTester(
2265                 rDoc, sc::ColRowEditAction::DeleteColumns, nUndoStartCol, nUndoEndCol, aMark);
2266             break;
2267         case DelCellCmd::Rows:
2268             aTester = ScEditableTester(
2269                 rDoc, sc::ColRowEditAction::DeleteRows, nUndoStartRow, nUndoEndRow, aMark);
2270             break;
2271         default:
2272             aTester = ScEditableTester(
2273                 &rDoc, nUndoStartCol, nUndoStartRow, nEditTestEndX, nEditTestEndY, aMark);
2274     }
2275 
2276     if (!aTester.IsEditable())
2277     {
2278         if (!bApi)
2279             rDocShell.ErrorMessage(aTester.GetMessageId());
2280         return false;
2281     }
2282 
2283     if (!canDeleteCellsByPivot(rRange, aMark, eCmd, &rDoc))
2284     {
2285         if (!bApi)
2286             rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE);
2287         return false;
2288     }
2289                     // Test for merged cells
2290 
2291     SCCOL nMergeTestEndCol = (eCmd==DelCellCmd::CellsLeft) ? MAXCOL : nUndoEndCol;
2292     SCROW nMergeTestEndRow = (eCmd==DelCellCmd::CellsUp)   ? MAXROW : nUndoEndRow;
2293     SCCOL nExtendStartCol = nUndoStartCol;
2294     SCROW nExtendStartRow = nUndoStartRow;
2295     bool bNeedRefresh = false;
2296 
2297     //Issue 8302 want to be able to insert into the middle of merged cells
2298     //the patch comes from maoyg
2299     ::std::vector<ScRange> qDecreaseRange;
2300     bool bDeletingMerge = false;
2301     OUString aUndo = ScResId( STR_UNDO_DELETECELLS );
2302     if (bRecord)
2303     {
2304         ViewShellId nViewShellId(-1);
2305         if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
2306             nViewShellId = pViewSh->GetViewShellId();
2307         rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
2308     }
2309     std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge;
2310 
2311     for (const SCTAB i : aMark)
2312     {
2313         if (i >= nTabCount)
2314             break;
2315 
2316         if ( rDoc.HasAttrib( nUndoStartCol, nUndoStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2317         {
2318             SCCOL nMergeStartCol = nUndoStartCol;
2319             SCROW nMergeStartRow = nUndoStartRow;
2320             SCCOL nMergeEndCol   = nMergeTestEndCol;
2321             SCROW nMergeEndRow   = nMergeTestEndRow;
2322 
2323             rDoc.ExtendMerge( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
2324             rDoc.ExtendOverlapped( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
2325             if( ( eCmd == DelCellCmd::CellsUp && ( nMergeStartCol != nUndoStartCol || nMergeEndCol != nMergeTestEndCol))||
2326                 ( eCmd == DelCellCmd::CellsLeft && ( nMergeStartRow != nUndoStartRow || nMergeEndRow != nMergeTestEndRow)))
2327             {
2328                 if (!bApi)
2329                     rDocShell.ErrorMessage(STR_MSSG_DELETECELLS_0);
2330                 rDocShell.GetUndoManager()->LeaveListAction();
2331                 return false;
2332             }
2333 
2334             nExtendStartCol = nMergeStartCol;
2335             nExtendStartRow = nMergeStartRow;
2336             SCCOL nTestCol = -1;
2337             SCROW nTestRow1 = -1;
2338             SCROW nTestRow2 = -1;
2339 
2340             ScDocAttrIterator aTestIter( &rDoc, i, nUndoStartCol, nUndoStartRow, nMergeTestEndCol, nMergeTestEndRow );
2341             ScRange aExtendRange( nUndoStartCol, nUndoStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i );
2342             const ScPatternAttr* pPattern = nullptr;
2343             while ( ( pPattern = aTestIter.GetNext( nTestCol, nTestRow1, nTestRow2 ) ) != nullptr )
2344             {
2345                 const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
2346                 const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
2347                 ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver);
2348                 if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver)
2349                 {
2350                     ScRange aRange( nTestCol, nTestRow1, i );
2351                     rDoc.ExtendOverlapped( aRange );
2352                     rDoc.ExtendMerge( aRange, true );
2353 
2354                     if( nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor )
2355                     {
2356                         for( SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++ )
2357                         {
2358                             ScRange aTestRange( nTestCol, nTestRow, i );
2359                             rDoc.ExtendOverlapped( aTestRange );
2360                             rDoc.ExtendMerge( aTestRange, true );
2361                             ScRange aMergeRange( aTestRange.aStart.Col(),aTestRange.aStart.Row(), i );
2362                             if( !aExtendRange.In( aMergeRange ) )
2363                             {
2364                                 qDecreaseRange.push_back( aTestRange );
2365                                 bDeletingMerge = true;
2366                             }
2367                         }
2368                     }
2369                     else
2370                     {
2371                         ScRange aMergeRange( aRange.aStart.Col(),aRange.aStart.Row(), i );
2372                         if( !aExtendRange.In( aMergeRange ) )
2373                         {
2374                             qDecreaseRange.push_back( aRange );
2375                         }
2376                         bDeletingMerge = true;
2377                     }
2378                 }
2379             }
2380 
2381             if( bDeletingMerge )
2382             {
2383 
2384                 if( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::CellsUp )
2385                 {
2386                     nStartRow = aExtendMergeRange.aStart.Row();
2387                     nEndRow = aExtendMergeRange.aEnd.Row();
2388                     bNeedRefresh = true;
2389 
2390                     if( eCmd == DelCellCmd::CellsUp )
2391                     {
2392                         nEndCol = aExtendMergeRange.aEnd.Col();
2393                     }
2394                     else
2395                     {
2396                         nStartCol = 0;
2397                         nEndCol = MAXCOL;
2398                     }
2399                 }
2400                 else if( eCmd == DelCellCmd::CellsLeft || eCmd == DelCellCmd::Cols )
2401                 {
2402 
2403                     nStartCol = aExtendMergeRange.aStart.Col();
2404                     nEndCol = aExtendMergeRange.aEnd.Col();
2405                     if( eCmd == DelCellCmd::CellsLeft )
2406                     {
2407                         nEndRow = aExtendMergeRange.aEnd.Row();
2408                         bNeedRefresh = true;
2409                     }
2410                     else
2411                     {
2412                         nStartRow = 0;
2413                         nEndRow = MAXROW;
2414                     }
2415                 }
2416 
2417                 if( !qDecreaseRange.empty() )
2418                 {
2419                     if (bRecord && !pUndoRemoveMerge)
2420                     {
2421                         ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
2422                         pUndoDoc->InitUndo( &rDoc, *aMark.begin(), *aMark.rbegin());
2423                         pUndoRemoveMerge.reset( new ScUndoRemoveMerge( &rDocShell, rRange, std::move(pUndoDoc) ));
2424                     }
2425 
2426                     for( const ScRange& aRange : qDecreaseRange )
2427                     {
2428                         if( rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
2429                         {
2430                             UnmergeCells( aRange, bRecord, pUndoRemoveMerge.get() );
2431                         }
2432                     }
2433                 }
2434             }
2435             else
2436             {
2437                 if (!bApi)
2438                     rDocShell.ErrorMessage(STR_MSSG_DELETECELLS_0);
2439                 rDocShell.GetUndoManager()->LeaveListAction();
2440                 return false;
2441             }
2442         }
2443     }
2444 
2445     if (bRecord && pUndoRemoveMerge)
2446     {
2447         rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndoRemoveMerge));
2448     }
2449 
2450     //     do it
2451 
2452     WaitObject aWait( ScDocShell::GetActiveDialogParent() );      // important because of TrackFormulas in UpdateReference
2453 
2454     ScDocumentUniquePtr pUndoDoc;
2455     std::unique_ptr<ScDocument> pRefUndoDoc;
2456     std::unique_ptr<ScRefUndoData> pUndoData;
2457     if ( bRecord )
2458     {
2459         // With the fix for #101329#, UpdateRef always puts cells into pRefUndoDoc at their old position,
2460         // so it's no longer necessary to copy more than the deleted range into pUndoDoc.
2461 
2462         pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2463         pUndoDoc->InitUndo( &rDoc, 0, nTabCount-1, (eCmd==DelCellCmd::Cols), (eCmd==DelCellCmd::Rows) );
2464         for (const auto& rTab : aMark)
2465         {
2466             if (rTab >= nTabCount)
2467                 break;
2468 
2469             SCTAB nScenarioCount = 0;
2470 
2471             for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2472                 nScenarioCount ++;
2473 
2474             rDoc.CopyToDocument( nUndoStartCol, nUndoStartRow, rTab, nUndoEndCol, nUndoEndRow, rTab+nScenarioCount,
2475                 InsertDeleteFlags::ALL | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
2476         }
2477 
2478         pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2479         pRefUndoDoc->InitUndo( &rDoc, 0, nTabCount-1 );
2480 
2481         pUndoData.reset(new ScRefUndoData( &rDoc ));
2482 
2483         rDoc.BeginDrawUndo();
2484     }
2485 
2486     sal_uInt16 nExtFlags = 0;
2487     for (const auto& rTab : aMark)
2488     {
2489         if (rTab >= nTabCount)
2490             break;
2491 
2492         rDocShell.UpdatePaintExt( nExtFlags, nStartCol, nStartRow, rTab, nEndCol, nEndRow, rTab );
2493     }
2494 
2495     bool bUndoOutline = false;
2496     switch (eCmd)
2497     {
2498         case DelCellCmd::CellsUp:
2499             rDoc.DeleteRow( nStartCol, 0, nEndCol, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), nullptr, &aFullMark );
2500             nPaintEndRow = MAXROW;
2501             break;
2502         case DelCellCmd::Rows:
2503             rDoc.DeleteRow( 0, 0, MAXCOL, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &bUndoOutline, &aFullMark );
2504             nPaintStartCol = 0;
2505             nPaintEndCol = MAXCOL;
2506             nPaintEndRow = MAXROW;
2507             nPaintFlags |= PaintPartFlags::Left;
2508             break;
2509         case DelCellCmd::CellsLeft:
2510             rDoc.DeleteCol( nStartRow, 0, nEndRow, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), nullptr, &aFullMark );
2511             nPaintEndCol = MAXCOL;
2512             break;
2513         case DelCellCmd::Cols:
2514             rDoc.DeleteCol( 0, 0, MAXROW, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &bUndoOutline, &aFullMark );
2515             nPaintStartRow = 0;
2516             nPaintEndRow = MAXROW;
2517             nPaintEndCol = MAXCOL;
2518             nPaintFlags |= PaintPartFlags::Top;
2519             break;
2520         default:
2521             OSL_FAIL("Wrong code at deleting");
2522             break;
2523     }
2524 
2525     //! Test if the size of outline has changed
2526 
2527     if ( bRecord )
2528     {
2529         for (const auto& rTab : aFullMark)
2530         {
2531             if (rTab >= nTabCount)
2532                 break;
2533 
2534             pRefUndoDoc->DeleteAreaTab(nUndoStartCol,nUndoStartRow,nUndoEndCol,nUndoEndRow, rTab, InsertDeleteFlags::ALL);
2535         }
2536 
2537             //  for all sheets, so that formulas can be copied
2538         pUndoDoc->AddUndoTab( 0, nTabCount-1 );
2539 
2540             //  copy with bColRowFlags=false (#54194#)
2541         pRefUndoDoc->CopyToDocument(0,0,0,MAXCOL,MAXROW,MAXTAB,InsertDeleteFlags::FORMULA,false,*pUndoDoc,nullptr,false);
2542         pRefUndoDoc.reset();
2543 
2544         std::unique_ptr<SCTAB[]> pTabs(      new SCTAB[nSelCount]);
2545         std::unique_ptr<SCTAB[]> pScenarios( new SCTAB[nSelCount]);
2546         SCTAB   nUndoPos  = 0;
2547 
2548         for (const auto& rTab : aMark)
2549         {
2550             if (rTab >= nTabCount)
2551                 break;
2552 
2553             SCTAB nCount = 0;
2554             for( SCTAB j=rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2555                 nCount ++;
2556 
2557             pScenarios[nUndoPos] = nCount;
2558             pTabs[nUndoPos] = rTab;
2559             nUndoPos ++;
2560         }
2561 
2562         if( !bDeletingMerge )
2563         {
2564             rDocShell.GetUndoManager()->LeaveListAction();
2565         }
2566 
2567         rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDeleteCells>(
2568             &rDocShell, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ),
2569             nUndoPos, std::move(pTabs), std::move(pScenarios),
2570             eCmd, std::move(pUndoDoc), std::move(pUndoData) ) );
2571     }
2572 
2573     // #i8302 want to be able to insert into the middle of merged cells
2574     // the patch comes from maoyg
2575 
2576     while( !qDecreaseRange.empty() )
2577     {
2578         ScRange aRange = qDecreaseRange.back();
2579 
2580         long nDecreaseRowCount = 0;
2581         long nDecreaseColCount = 0;
2582         if( eCmd == DelCellCmd::CellsUp || eCmd == DelCellCmd::Rows )
2583         {
2584             if( nStartRow >= aRange.aStart.Row() && nStartRow <= aRange.aEnd.Row() && nEndRow>= aRange.aStart.Row() && nEndRow <= aRange.aEnd.Row() )
2585                 nDecreaseRowCount = nEndRow-nStartRow+1;
2586             else if( nStartRow >= aRange.aStart.Row() && nStartRow <= aRange.aEnd.Row() && nEndRow >= aRange.aStart.Row() && nEndRow >= aRange.aEnd.Row() )
2587                 nDecreaseRowCount = aRange.aEnd.Row()-nStartRow+1;
2588             else if( nStartRow >= aRange.aStart.Row() && nStartRow >= aRange.aEnd.Row() && nEndRow>= aRange.aStart.Row() && nEndRow <= aRange.aEnd.Row() )
2589                 nDecreaseRowCount = aRange.aEnd.Row()-nEndRow+1;
2590         }
2591         else if( eCmd == DelCellCmd::CellsLeft || eCmd == DelCellCmd::Cols )
2592         {
2593             if( nStartCol >= aRange.aStart.Col() && nStartCol <= aRange.aEnd.Col() && nEndCol>= aRange.aStart.Col() && nEndCol <= aRange.aEnd.Col() )
2594                 nDecreaseColCount = nEndCol-nStartCol+1;
2595             else if( nStartCol >= aRange.aStart.Col() && nStartCol <= aRange.aEnd.Col() && nEndCol >= aRange.aStart.Col() && nEndCol >= aRange.aEnd.Col() )
2596                 nDecreaseColCount = aRange.aEnd.Col()-nStartCol+1;
2597             else if( nStartCol >= aRange.aStart.Col() && nStartCol >= aRange.aEnd.Col() && nEndCol>= aRange.aStart.Col() && nEndCol <= aRange.aEnd.Col() )
2598                 nDecreaseColCount = aRange.aEnd.Col()-nEndCol+1;
2599         }
2600 
2601         switch (eCmd)
2602         {
2603             case DelCellCmd::CellsUp:
2604             case DelCellCmd::Rows:
2605                 aRange.aEnd.SetRow(static_cast<SCCOL>( aRange.aEnd.Row()-nDecreaseRowCount));
2606                 break;
2607             case DelCellCmd::CellsLeft:
2608             case DelCellCmd::Cols:
2609                 aRange.aEnd.SetCol(static_cast<SCCOL>( aRange.aEnd.Col()-nDecreaseColCount));
2610                 break;
2611             default:
2612                 break;
2613         }
2614 
2615         if( !rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
2616         {
2617             ScCellMergeOption aMergeOption(aRange);
2618             MergeCells( aMergeOption, false, true, true );
2619         }
2620         qDecreaseRange.pop_back();
2621     }
2622 
2623     if( bDeletingMerge )
2624         rDocShell.GetUndoManager()->LeaveListAction();
2625 
2626     if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft )
2627         nMergeTestEndCol = MAXCOL;
2628     if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp )
2629         nMergeTestEndRow = MAXROW;
2630     if ( bNeedRefresh )
2631     {
2632         // #i51445# old merge flag attributes must be deleted also for single cells,
2633         // not only for whole columns/rows
2634 
2635         ScPatternAttr aPattern( rDoc.GetPool() );
2636         aPattern.GetItemSet().Put( ScMergeFlagAttr() );
2637 
2638         rDoc.ApplyPatternArea( nExtendStartCol, nExtendStartRow, nMergeTestEndCol, nMergeTestEndRow, aMark, aPattern );
2639 
2640         for (const auto& rTab : aMark)
2641         {
2642             if (rTab >= nTabCount)
2643                 break;
2644 
2645             SCTAB nScenarioCount = 0;
2646 
2647             for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2648                 nScenarioCount ++;
2649 
2650             ScRange aMergedRange( nExtendStartCol, nExtendStartRow, rTab, nMergeTestEndCol, nMergeTestEndRow, rTab+nScenarioCount );
2651             rDoc.ExtendMerge( aMergedRange, true );
2652         }
2653     }
2654 
2655     for (const auto& rTab : aMark)
2656     {
2657         if (rTab >= nTabCount)
2658             break;
2659 
2660         rDoc.RefreshAutoFilter( nExtendStartCol, nExtendStartRow, nMergeTestEndCol, nMergeTestEndRow, rTab );
2661     }
2662 
2663     for (const auto& rTab : aMark)
2664     {
2665         if (rTab >= nTabCount)
2666             break;
2667 
2668         rDoc.SetDrawPageSize(rTab);
2669 
2670         if ( eCmd == DelCellCmd::Cols || eCmd == DelCellCmd::Rows )
2671             rDoc.UpdatePageBreaks( rTab );
2672 
2673         rDocShell.UpdatePaintExt( nExtFlags, nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab );
2674 
2675         SCTAB nScenarioCount = 0;
2676 
2677         for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2678             nScenarioCount ++;
2679 
2680         //  delete entire rows: do not adjust
2681         if ( eCmd == DelCellCmd::Rows || !AdjustRowHeight(ScRange( 0, nPaintStartRow, rTab, MAXCOL, nPaintEndRow, rTab+nScenarioCount )) )
2682             rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, nPaintFlags,  nExtFlags );
2683         else
2684         {
2685             //  paint only what is not done by AdjustRowHeight
2686             if (nExtFlags & SC_PF_LINES)
2687                 lcl_PaintAbove( rDocShell, ScRange( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount) );
2688             if (nPaintFlags & PaintPartFlags::Top)
2689                 rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, PaintPartFlags::Top );
2690         }
2691     }
2692 
2693     // The cursor position needs to be modified earlier than updating
2694     // any enabled edit view which is triggered by SetDocumentModified below.
2695     ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
2696     if (pViewSh)
2697     {
2698         if (eCmd == DelCellCmd::Cols)
2699         {
2700             pViewSh->OnLOKInsertDeleteColumn(rRange.aStart.Col(), -1);
2701         }
2702         if (eCmd == DelCellCmd::Rows)
2703         {
2704             pViewSh->OnLOKInsertDeleteRow(rRange.aStart.Row(), -1);
2705         }
2706     }
2707 
2708     aModificator.SetDocumentModified();
2709 
2710     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
2711 
2712     return true;
2713 }
2714 
2715 bool ScDocFunc::MoveBlock( const ScRange& rSource, const ScAddress& rDestPos,
2716                                 bool bCut, bool bRecord, bool bPaint, bool bApi )
2717 {
2718     ScDocShellModificator aModificator( rDocShell );
2719 
2720     SCCOL nStartCol = rSource.aStart.Col();
2721     SCROW nStartRow = rSource.aStart.Row();
2722     SCTAB nStartTab = rSource.aStart.Tab();
2723     SCCOL nEndCol = rSource.aEnd.Col();
2724     SCROW nEndRow = rSource.aEnd.Row();
2725     SCTAB nEndTab = rSource.aEnd.Tab();
2726     SCCOL nDestCol = rDestPos.Col();
2727     SCROW nDestRow = rDestPos.Row();
2728     SCTAB nDestTab = rDestPos.Tab();
2729 
2730     if ( !ValidRow(nStartRow) || !ValidRow(nEndRow) || !ValidRow(nDestRow) )
2731     {
2732         OSL_FAIL("invalid row in MoveBlock");
2733         return false;
2734     }
2735 
2736     //  adjust related scenarios too - but only when moved within one sheet
2737     bool bScenariosAdded = false;
2738     ScDocument& rDoc = rDocShell.GetDocument();
2739     if (bRecord && !rDoc.IsUndoEnabled())
2740         bRecord = false;
2741 
2742     SCTAB nTabCount = rDoc.GetTableCount();
2743     if ( nDestTab == nStartTab && !rDoc.IsScenario(nEndTab) )
2744         while ( nEndTab+1 < nTabCount && rDoc.IsScenario(nEndTab+1) )
2745         {
2746             ++nEndTab;
2747             bScenariosAdded = true;
2748         }
2749 
2750     SCTAB nSrcTabCount = nEndTab-nStartTab+1;
2751     SCTAB nDestEndTab = nDestTab+nSrcTabCount-1;
2752     SCTAB nTab;
2753 
2754     ScDocumentUniquePtr pClipDoc(new ScDocument(SCDOCMODE_CLIP));
2755 
2756     ScMarkData aSourceMark;
2757     for (nTab=nStartTab; nTab<=nEndTab; nTab++)
2758         aSourceMark.SelectTable( nTab, true );      // select source
2759     aSourceMark.SetMarkArea( rSource );
2760 
2761     ScDocShellRef aDragShellRef;
2762     if ( rDoc.HasOLEObjectsInArea( rSource ) )
2763     {
2764         aDragShellRef = new ScDocShell;     // DocShell needs a Ref immediately
2765         aDragShellRef->DoInitNew();
2766     }
2767     ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() );
2768 
2769     ScClipParam aClipParam(ScRange(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nStartTab), bCut);
2770     rDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSourceMark, bScenariosAdded, true);
2771 
2772     ScDrawLayer::SetGlobalDrawPersist(nullptr);
2773 
2774     SCCOL nOldEndCol = nEndCol;
2775     SCROW nOldEndRow = nEndRow;
2776     bool bClipOver = false;
2777     for (nTab=nStartTab; nTab<=nEndTab; nTab++)
2778     {
2779         SCCOL nTmpEndCol = nOldEndCol;
2780         SCROW nTmpEndRow = nOldEndRow;
2781         if (rDoc.ExtendMerge( nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab ))
2782             bClipOver = true;
2783         if ( nTmpEndCol > nEndCol ) nEndCol = nTmpEndCol;
2784         if ( nTmpEndRow > nEndRow ) nEndRow = nTmpEndRow;
2785     }
2786 
2787     SCCOL nDestEndCol = nDestCol + ( nOldEndCol-nStartCol );
2788     SCROW nDestEndRow = nDestRow + ( nOldEndRow-nStartRow );
2789 
2790     SCCOL nUndoEndCol = nDestCol + ( nEndCol-nStartCol );       // extended in destination block
2791     SCROW nUndoEndRow = nDestRow + ( nEndRow-nStartRow );
2792 
2793     bool bIncludeFiltered = bCut;
2794     if ( !bIncludeFiltered )
2795     {
2796         //  adjust sizes to include only non-filtered rows
2797 
2798         SCCOL nClipX;
2799         SCROW nClipY;
2800         pClipDoc->GetClipArea( nClipX, nClipY, false );
2801         SCROW nUndoAdd = nUndoEndRow - nDestEndRow;
2802         nDestEndRow = nDestRow + nClipY;
2803         nUndoEndRow = nDestEndRow + nUndoAdd;
2804     }
2805 
2806     if (!ValidCol(nUndoEndCol) || !ValidRow(nUndoEndRow))
2807     {
2808         if (!bApi)
2809             rDocShell.ErrorMessage(STR_PASTE_FULL);
2810         return false;
2811     }
2812 
2813     //  Test for cell protection
2814 
2815     ScEditableTester aTester;
2816     for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
2817         aTester.TestBlock( &rDoc, nTab, nDestCol,nDestRow, nUndoEndCol,nUndoEndRow );
2818     if (bCut)
2819         for (nTab=nStartTab; nTab<=nEndTab; nTab++)
2820             aTester.TestBlock( &rDoc, nTab, nStartCol,nStartRow, nEndCol,nEndRow );
2821 
2822     if (!aTester.IsEditable())
2823     {
2824         if (!bApi)
2825             rDocShell.ErrorMessage(aTester.GetMessageId());
2826         return false;
2827     }
2828 
2829     //  Test for merged cells- when moving after delete
2830 
2831     if (bClipOver && !bCut)
2832         if (rDoc.HasAttrib( nDestCol,nDestRow,nDestTab, nUndoEndCol,nUndoEndRow,nDestEndTab,
2833                                 HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2834         {       // "Merge of already merged cells not possible"
2835             if (!bApi)
2836                 rDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0);
2837             return false;
2838         }
2839 
2840     //  Are there borders in the cells? (for painting)
2841 
2842     sal_uInt16 nSourceExt = 0;
2843     rDocShell.UpdatePaintExt( nSourceExt, nStartCol,nStartRow,nStartTab, nEndCol,nEndRow,nEndTab );
2844     sal_uInt16 nDestExt = 0;
2845     rDocShell.UpdatePaintExt( nDestExt, nDestCol,nDestRow,nDestTab, nDestEndCol,nDestEndRow,nDestEndTab );
2846 
2847     //  do it
2848 
2849     ScDocumentUniquePtr pUndoDoc;
2850 
2851     if (bRecord)
2852     {
2853         bool bWholeCols = ( nStartRow == 0 && nEndRow == MAXROW );
2854         bool bWholeRows = ( nStartCol == 0 && nEndCol == MAXCOL );
2855         InsertDeleteFlags nUndoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
2856 
2857         pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2858         pUndoDoc->InitUndo( &rDoc, nStartTab, nEndTab, bWholeCols, bWholeRows );
2859 
2860         if (bCut)
2861         {
2862             rDoc.CopyToDocument( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
2863                                     nUndoFlags, false, *pUndoDoc );
2864         }
2865 
2866         if ( nDestTab != nStartTab )
2867             pUndoDoc->AddUndoTab( nDestTab, nDestEndTab, bWholeCols, bWholeRows );
2868         rDoc.CopyToDocument( nDestCol, nDestRow, nDestTab,
2869                                     nDestEndCol, nDestEndRow, nDestEndTab,
2870                                     nUndoFlags, false, *pUndoDoc );
2871         rDoc.BeginDrawUndo();
2872     }
2873 
2874     bool bSourceHeight = false;     // adjust heights?
2875     if (bCut)
2876     {
2877         ScMarkData aDelMark;    // only for tables
2878         for (nTab=nStartTab; nTab<=nEndTab; nTab++)
2879         {
2880             rDoc.DeleteAreaTab( nStartCol,nStartRow, nOldEndCol,nOldEndRow, nTab, InsertDeleteFlags::ALL );
2881             aDelMark.SelectTable( nTab, true );
2882         }
2883         rDoc.DeleteObjectsInArea( nStartCol,nStartRow, nOldEndCol,nOldEndRow, aDelMark );
2884 
2885         //  Test for merged cells
2886 
2887         if (bClipOver)
2888             if (rDoc.HasAttrib( nDestCol,nDestRow,nDestTab,
2889                                     nUndoEndCol,nUndoEndRow,nDestEndTab,
2890                                     HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2891             {
2892                 rDoc.CopyFromClip( rSource, aSourceMark, InsertDeleteFlags::ALL, nullptr, pClipDoc.get() );
2893                 for (nTab=nStartTab; nTab<=nEndTab; nTab++)
2894                 {
2895                     SCCOL nTmpEndCol = nEndCol;
2896                     SCROW nTmpEndRow = nEndRow;
2897                     rDoc.ExtendMerge( nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab, true );
2898                 }
2899 
2900                 // Report error only after restoring content
2901                 if (!bApi)      // "Merge of already merged cells not possible"
2902                     rDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0);
2903 
2904                 return false;
2905             }
2906 
2907         bSourceHeight = AdjustRowHeight( rSource, false );
2908     }
2909 
2910     ScRange aPasteDest( nDestCol, nDestRow, nDestTab, nDestEndCol, nDestEndRow, nDestEndTab );
2911 
2912     ScMarkData aDestMark;
2913     for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
2914         aDestMark.SelectTable( nTab, true );        // select destination
2915     aDestMark.SetMarkArea( aPasteDest );
2916 
2917     /*  Do not copy drawing objects here. While pasting, the
2918         function ScDocument::UpdateReference() is called which calls
2919         ScDrawLayer::MoveCells() which may move away inserted objects to wrong
2920         positions (e.g. if source and destination range overlaps).*/
2921 
2922     rDoc.CopyFromClip(
2923         aPasteDest, aDestMark, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS,
2924         pUndoDoc.get(), pClipDoc.get(), true, false, bIncludeFiltered);
2925 
2926     // skipped rows and merged cells don't mix
2927     if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() )
2928         UnmergeCells( aPasteDest, false, nullptr );
2929 
2930     bool bDestHeight = AdjustRowHeight(
2931                             ScRange( 0,nDestRow,nDestTab, MAXCOL,nDestEndRow,nDestEndTab ),
2932                             false );
2933 
2934     /*  Paste drawing objects after adjusting formula references
2935         and row heights. There are no cell notes or drawing objects, if the
2936         clipdoc does not contain a drawing layer.*/
2937     if ( pClipDoc->GetDrawLayer() )
2938         rDoc.CopyFromClip( aPasteDest, aDestMark, InsertDeleteFlags::OBJECTS,
2939                            nullptr, pClipDoc.get(), true, false, bIncludeFiltered );
2940 
2941     if (bRecord)
2942     {
2943         ScRange aUndoRange(nStartCol, nStartRow, nStartTab, nOldEndCol, nOldEndRow, nEndTab);
2944         ScAddress aDestPos(nDestCol, nDestRow, nDestTab);
2945 
2946         rDocShell.GetUndoManager()->AddUndoAction(
2947             std::make_unique<ScUndoDragDrop>(
2948                 &rDocShell, aUndoRange, aDestPos, bCut, std::move(pUndoDoc), bScenariosAdded));
2949     }
2950 
2951     SCCOL nDestPaintEndCol = nDestEndCol;
2952     SCROW nDestPaintEndRow = nDestEndRow;
2953     for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
2954     {
2955         SCCOL nTmpEndCol = nDestEndCol;
2956         SCROW nTmpEndRow = nDestEndRow;
2957         rDoc.ExtendMerge( nDestCol, nDestRow, nTmpEndCol, nTmpEndRow, nTab, true );
2958         if (nTmpEndCol > nDestPaintEndCol) nDestPaintEndCol = nTmpEndCol;
2959         if (nTmpEndRow > nDestPaintEndRow) nDestPaintEndRow = nTmpEndRow;
2960     }
2961 
2962     if (bCut)
2963         for (nTab=nStartTab; nTab<=nEndTab; nTab++)
2964             rDoc.RefreshAutoFilter( nStartCol, nStartRow, nEndCol, nEndRow, nTab );
2965 
2966     if (bPaint)
2967     {
2968             //  destination range:
2969 
2970         SCCOL nPaintStartX = nDestCol;
2971         SCROW nPaintStartY = nDestRow;
2972         SCCOL nPaintEndX = nDestPaintEndCol;
2973         SCROW nPaintEndY = nDestPaintEndRow;
2974         PaintPartFlags nFlags = PaintPartFlags::Grid;
2975 
2976         if ( nStartRow==0 && nEndRow==MAXROW )      // copy widths too?
2977         {
2978             nPaintEndX = MAXCOL;
2979             nPaintStartY = 0;
2980             nPaintEndY = MAXROW;
2981             nFlags |= PaintPartFlags::Top;
2982         }
2983         if ( bDestHeight || ( nStartCol == 0 && nEndCol == MAXCOL ) )
2984         {
2985             nPaintEndY = MAXROW;
2986             nPaintStartX = 0;
2987             nPaintEndX = MAXCOL;
2988             nFlags |= PaintPartFlags::Left;
2989         }
2990         if ( bScenariosAdded )
2991         {
2992             nPaintStartX = 0;
2993             nPaintStartY = 0;
2994             nPaintEndX = MAXCOL;
2995             nPaintEndY = MAXROW;
2996         }
2997 
2998         rDocShell.PostPaint( nPaintStartX,nPaintStartY,nDestTab,
2999                             nPaintEndX,nPaintEndY,nDestEndTab, nFlags, nSourceExt | nDestExt );
3000 
3001         if ( bCut )
3002         {
3003                 //  source range:
3004 
3005             nPaintStartX = nStartCol;
3006             nPaintStartY = nStartRow;
3007             nPaintEndX = nEndCol;
3008             nPaintEndY = nEndRow;
3009             nFlags = PaintPartFlags::Grid;
3010 
3011             if ( bSourceHeight )
3012             {
3013                 nPaintEndY = MAXROW;
3014                 nPaintStartX = 0;
3015                 nPaintEndX = MAXCOL;
3016                 nFlags |= PaintPartFlags::Left;
3017             }
3018             if ( bScenariosAdded )
3019             {
3020                 nPaintStartX = 0;
3021                 nPaintStartY = 0;
3022                 nPaintEndX = MAXCOL;
3023                 nPaintEndY = MAXROW;
3024             }
3025 
3026             rDocShell.PostPaint( nPaintStartX,nPaintStartY,nStartTab,
3027                                 nPaintEndX,nPaintEndY,nEndTab, nFlags, nSourceExt );
3028         }
3029     }
3030 
3031     aModificator.SetDocumentModified();
3032 
3033     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
3034 
3035     return true;
3036 }
3037 
3038 static uno::Reference< uno::XInterface > GetDocModuleObject( const SfxObjectShell& rDocSh, const OUString& sCodeName )
3039 {
3040     uno::Reference< lang::XMultiServiceFactory> xSF(rDocSh.GetModel(), uno::UNO_QUERY);
3041     uno::Reference< container::XNameAccess > xVBACodeNamedObjectAccess;
3042     uno::Reference< uno::XInterface > xDocModuleApiObject;
3043     if ( xSF.is() )
3044     {
3045         xVBACodeNamedObjectAccess.set( xSF->createInstance("ooo.vba.VBAObjectModuleObjectProvider"), uno::UNO_QUERY );
3046         xDocModuleApiObject.set( xVBACodeNamedObjectAccess->getByName( sCodeName ), uno::UNO_QUERY );
3047     }
3048     return xDocModuleApiObject;
3049 
3050 }
3051 
3052 static script::ModuleInfo lcl_InitModuleInfo( const SfxObjectShell& rDocSh, const OUString& sModule )
3053 {
3054     script::ModuleInfo sModuleInfo;
3055     sModuleInfo.ModuleType = script::ModuleType::DOCUMENT;
3056     sModuleInfo.ModuleObject = GetDocModuleObject( rDocSh, sModule );
3057     return sModuleInfo;
3058 }
3059 
3060 void VBA_InsertModule( ScDocument& rDoc, SCTAB nTab, const OUString& sSource )
3061 {
3062     SfxObjectShell& rDocSh = *rDoc.GetDocumentShell();
3063     uno::Reference< script::XLibraryContainer > xLibContainer = rDocSh.GetBasicContainer();
3064     OSL_ENSURE( xLibContainer.is(), "No BasicContainer!" );
3065 
3066     uno::Reference< container::XNameContainer > xLib;
3067     if( xLibContainer.is() )
3068     {
3069         OUString aLibName( "Standard" );
3070         if ( rDocSh.GetBasicManager() && !rDocSh.GetBasicManager()->GetName().isEmpty() )
3071         {
3072             aLibName = rDocSh.GetBasicManager()->GetName();
3073         }
3074         uno::Any aLibAny = xLibContainer->getByName( aLibName );
3075         aLibAny >>= xLib;
3076     }
3077     if( xLib.is() )
3078     {
3079         // if the Module with codename exists then find a new name
3080         sal_Int32 nNum = 1;
3081         OUString genModuleName = "Sheet1";
3082         while( xLib->hasByName( genModuleName ) )
3083             genModuleName = "Sheet" + OUString::number( ++nNum );
3084 
3085         uno::Any aSourceAny;
3086         OUString sTmpSource = sSource;
3087         if ( sTmpSource.isEmpty() )
3088             sTmpSource = "Rem Attribute VBA_ModuleType=VBADocumentModule\nOption VBASupport 1\n";
3089         aSourceAny <<= sTmpSource;
3090         uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY );
3091         if ( xVBAModuleInfo.is() )
3092         {
3093             rDoc.SetCodeName( nTab, genModuleName );
3094             script::ModuleInfo sModuleInfo = lcl_InitModuleInfo(  rDocSh, genModuleName );
3095             xVBAModuleInfo->insertModuleInfo( genModuleName, sModuleInfo );
3096             xLib->insertByName( genModuleName, aSourceAny );
3097         }
3098 
3099     }
3100 }
3101 
3102 void VBA_DeleteModule( ScDocShell& rDocSh, const OUString& sModuleName )
3103 {
3104     uno::Reference< script::XLibraryContainer > xLibContainer = rDocSh.GetBasicContainer();
3105     OSL_ENSURE( xLibContainer.is(), "No BasicContainer!" );
3106 
3107     uno::Reference< container::XNameContainer > xLib;
3108     if( xLibContainer.is() )
3109     {
3110         OUString aLibName( "Standard" );
3111         if ( rDocSh.GetBasicManager() && !rDocSh.GetBasicManager()->GetName().isEmpty() )
3112         {
3113             aLibName = rDocSh.GetBasicManager()->GetName();
3114         }
3115         uno::Any aLibAny = xLibContainer->getByName( aLibName );
3116         aLibAny >>= xLib;
3117     }
3118     if( xLib.is() )
3119     {
3120         uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY );
3121         if( xLib->hasByName( sModuleName ) )
3122             xLib->removeByName( sModuleName );
3123         if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo(sModuleName) )
3124             xVBAModuleInfo->removeModuleInfo( sModuleName );
3125 
3126     }
3127 }
3128 
3129 bool ScDocFunc::InsertTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi )
3130 {
3131     bool bSuccess = false;
3132     WaitObject aWait( ScDocShell::GetActiveDialogParent() );
3133 
3134     ScDocShellModificator aModificator( rDocShell );
3135 
3136     ScDocument& rDoc = rDocShell.GetDocument();
3137 
3138     // Strange loop, also basic is loaded too early ( InsertTable )
3139     // is called via the xml import for sheets in described in ODF
3140     bool bInsertDocModule = false;
3141 
3142     if(  !rDocShell.GetDocument().IsImportingXML() )
3143     {
3144         bInsertDocModule = rDoc.IsInVBAMode();
3145     }
3146     if ( bInsertDocModule || ( bRecord && !rDoc.IsUndoEnabled() ) )
3147         bRecord = false;
3148 
3149     if (bRecord)
3150         rDoc.BeginDrawUndo();                          //  InsertTab generates SdrUndoNewPage
3151 
3152     SCTAB nTabCount = rDoc.GetTableCount();
3153     bool bAppend = ( nTab >= nTabCount );
3154     if ( bAppend )
3155         nTab = nTabCount;       // important for Undo
3156 
3157     if (rDoc.InsertTab( nTab, rName ))
3158     {
3159         if (bRecord)
3160             rDocShell.GetUndoManager()->AddUndoAction(
3161                         std::make_unique<ScUndoInsertTab>( &rDocShell, nTab, bAppend, rName));
3162         //  Update views:
3163         // Only insert vba modules if vba mode ( and not currently importing XML )
3164         if( bInsertDocModule )
3165         {
3166             VBA_InsertModule( rDoc, nTab, OUString() );
3167         }
3168         rDocShell.Broadcast( ScTablesHint( SC_TAB_INSERTED, nTab ) );
3169 
3170         rDocShell.PostPaintExtras();
3171         aModificator.SetDocumentModified();
3172         SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3173         bSuccess = true;
3174     }
3175     else if (!bApi)
3176         rDocShell.ErrorMessage(STR_TABINSERT_ERROR);
3177 
3178     return bSuccess;
3179 }
3180 
3181 bool ScDocFunc::DeleteTable( SCTAB nTab, bool bRecord )
3182 {
3183     WaitObject aWait( ScDocShell::GetActiveDialogParent() );
3184 
3185     ScDocShellModificator aModificator( rDocShell );
3186 
3187     bool bSuccess = false;
3188     ScDocument& rDoc = rDocShell.GetDocument();
3189     bool bVbaEnabled = rDoc.IsInVBAMode();
3190     if (bRecord && !rDoc.IsUndoEnabled())
3191         bRecord = false;
3192     if ( bVbaEnabled )
3193         bRecord = false;
3194     bool bWasLinked = rDoc.IsLinked(nTab);
3195     ScDocumentUniquePtr pUndoDoc;
3196     std::unique_ptr<ScRefUndoData> pUndoData;
3197     if (bRecord)
3198     {
3199         pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
3200         SCTAB nCount = rDoc.GetTableCount();
3201 
3202         pUndoDoc->InitUndo( &rDoc, nTab, nTab, true, true );     // only nTab with Flags
3203         pUndoDoc->AddUndoTab( 0, nCount-1 );                    // all sheets for references
3204 
3205         rDoc.CopyToDocument(0,0,nTab, MAXCOL,MAXROW,nTab, InsertDeleteFlags::ALL,false, *pUndoDoc );
3206         OUString aOldName;
3207         rDoc.GetName( nTab, aOldName );
3208         pUndoDoc->RenameTab( nTab, aOldName );
3209         if (bWasLinked)
3210             pUndoDoc->SetLink( nTab, rDoc.GetLinkMode(nTab), rDoc.GetLinkDoc(nTab),
3211                                 rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab),
3212                                 rDoc.GetLinkTab(nTab),
3213                                 rDoc.GetLinkRefreshDelay(nTab) );
3214 
3215         if ( rDoc.IsScenario(nTab) )
3216         {
3217             pUndoDoc->SetScenario( nTab, true );
3218             OUString aComment;
3219             Color  aColor;
3220             ScScenarioFlags nScenFlags;
3221             rDoc.GetScenarioData( nTab, aComment, aColor, nScenFlags );
3222             pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags );
3223             bool bActive = rDoc.IsActiveScenario( nTab );
3224             pUndoDoc->SetActiveScenario( nTab, bActive );
3225         }
3226         pUndoDoc->SetVisible( nTab, rDoc.IsVisible( nTab ) );
3227         pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) );
3228         auto pSheetEvents = rDoc.GetSheetEvents( nTab );
3229         pUndoDoc->SetSheetEvents( nTab, std::unique_ptr<ScSheetEvents>(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) );
3230 
3231         //  Drawing-Layer has to take care of its own undo!!!
3232         rDoc.BeginDrawUndo();                          //  DeleteTab generates SdrUndoDelPage
3233 
3234         pUndoData.reset(new ScRefUndoData( &rDoc ));
3235     }
3236 
3237     if (rDoc.DeleteTab(nTab))
3238     {
3239         if (bRecord)
3240         {
3241             vector<SCTAB> theTabs;
3242             theTabs.push_back(nTab);
3243             rDocShell.GetUndoManager()->AddUndoAction(
3244                         std::make_unique<ScUndoDeleteTab>( &rDocShell, theTabs, std::move(pUndoDoc), std::move(pUndoData) ));
3245         }
3246         //  Update views:
3247         if( bVbaEnabled )
3248         {
3249             OUString sCodeName;
3250             if( rDoc.GetCodeName( nTab, sCodeName ) )
3251             {
3252                 VBA_DeleteModule( rDocShell, sCodeName );
3253             }
3254         }
3255         rDocShell.Broadcast( ScTablesHint( SC_TAB_DELETED, nTab ) );
3256 
3257         if (bWasLinked)
3258         {
3259             rDocShell.UpdateLinks();                // update Link-Manager
3260             SfxBindings* pBindings = rDocShell.GetViewBindings();
3261             if (pBindings)
3262                 pBindings->Invalidate(SID_LINKS);
3263         }
3264 
3265         rDocShell.PostPaintExtras();
3266         aModificator.SetDocumentModified();
3267 
3268         SfxApplication* pSfxApp = SfxGetpApp();                                // Navigator
3269         pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3270         pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
3271         pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
3272 
3273         bSuccess = true;
3274     }
3275     return bSuccess;
3276 }
3277 
3278 void ScDocFunc::SetTableVisible( SCTAB nTab, bool bVisible, bool bApi )
3279 {
3280     ScDocument& rDoc = rDocShell.GetDocument();
3281     bool bUndo(rDoc.IsUndoEnabled());
3282     if ( rDoc.IsVisible( nTab ) == bVisible )
3283         return;                                // nothing to do - ok
3284 
3285     if ( !rDoc.IsDocEditable() )
3286     {
3287         if (!bApi)
3288             rDocShell.ErrorMessage(STR_PROTECTIONERR);
3289         return;
3290     }
3291 
3292     ScDocShellModificator aModificator( rDocShell );
3293 
3294     if ( !bVisible && !rDoc.IsImportingXML() )     // #i57869# allow hiding in any order for loading
3295     {
3296         // do not disable all sheets
3297 
3298         sal_uInt16 nVisCount = 0;
3299         SCTAB nCount = rDoc.GetTableCount();
3300         for (SCTAB i=0; i<nCount && nVisCount<2; i++)
3301             if (rDoc.IsVisible(i))
3302                 ++nVisCount;
3303 
3304         if (nVisCount <= 1)
3305         {
3306             if (!bApi)
3307                 rDocShell.ErrorMessage(STR_PROTECTIONERR);  //! separate error message?
3308             return;
3309         }
3310     }
3311 
3312     rDoc.SetVisible( nTab, bVisible );
3313     if (bUndo)
3314     {
3315         std::vector<SCTAB> undoTabs;
3316         undoTabs.push_back(nTab);
3317         rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( &rDocShell, undoTabs, bVisible ) );
3318     }
3319 
3320     //  update views
3321     if (!bVisible)
3322         rDocShell.Broadcast( ScTablesHint( SC_TAB_HIDDEN, nTab ) );
3323 
3324     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3325     rDocShell.PostPaint(0,0,0,MAXCOL,MAXROW,MAXTAB, PaintPartFlags::Extras);
3326     aModificator.SetDocumentModified();
3327 }
3328 
3329 bool ScDocFunc::SetLayoutRTL( SCTAB nTab, bool bRTL )
3330 {
3331     ScDocument& rDoc = rDocShell.GetDocument();
3332     bool bUndo(rDoc.IsUndoEnabled());
3333     if ( rDoc.IsLayoutRTL( nTab ) == bRTL )
3334         return true;                                // nothing to do - ok
3335 
3336     //! protection (sheet or document?)
3337 
3338     ScDocShellModificator aModificator( rDocShell );
3339 
3340     rDoc.SetLayoutRTL( nTab, bRTL );
3341 
3342     if (bUndo)
3343     {
3344         rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoLayoutRTL>( &rDocShell, nTab, bRTL ) );
3345     }
3346 
3347     rDocShell.PostPaint( 0,0,0,MAXCOL,MAXROW,MAXTAB, PaintPartFlags::All );
3348     aModificator.SetDocumentModified();
3349 
3350     SfxBindings* pBindings = rDocShell.GetViewBindings();
3351     if (pBindings)
3352     {
3353         pBindings->Invalidate( FID_TAB_RTL );
3354         pBindings->Invalidate( SID_ATTR_SIZE );
3355     }
3356 
3357     return true;
3358 }
3359 
3360 bool ScDocFunc::RenameTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi )
3361 {
3362     ScDocument& rDoc = rDocShell.GetDocument();
3363     if (bRecord && !rDoc.IsUndoEnabled())
3364         bRecord = false;
3365     if ( !rDoc.IsDocEditable() )
3366     {
3367         if (!bApi)
3368             rDocShell.ErrorMessage(STR_PROTECTIONERR);
3369         return false;
3370     }
3371 
3372     ScDocShellModificator aModificator( rDocShell );
3373 
3374     bool bSuccess = false;
3375     OUString sOldName;
3376     rDoc.GetName(nTab, sOldName);
3377     if (rDoc.RenameTab( nTab, rName ))
3378     {
3379         if (bRecord)
3380         {
3381             rDocShell.GetUndoManager()->AddUndoAction(
3382                             std::make_unique<ScUndoRenameTab>( &rDocShell, nTab, sOldName, rName));
3383         }
3384         rDocShell.PostPaintExtras();
3385         aModificator.SetDocumentModified();
3386         SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3387 
3388         bSuccess = true;
3389     }
3390     return bSuccess;
3391 }
3392 
3393 bool ScDocFunc::SetTabBgColor( SCTAB nTab, const Color& rColor, bool bRecord, bool bApi )
3394 {
3395 
3396     ScDocument& rDoc = rDocShell.GetDocument();
3397     if (bRecord && !rDoc.IsUndoEnabled())
3398         bRecord = false;
3399     if ( !rDoc.IsDocEditable() || rDoc.IsTabProtected(nTab) )
3400     {
3401         if (!bApi)
3402             rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Check to see what this string is...
3403         return false;
3404     }
3405 
3406     Color aOldTabBgColor = rDoc.GetTabBgColor(nTab);
3407 
3408     bool bSuccess = false;
3409     rDoc.SetTabBgColor(nTab, rColor);
3410     if ( rDoc.GetTabBgColor(nTab) == rColor)
3411         bSuccess = true;
3412     if (bSuccess)
3413     {
3414         if (bRecord)
3415         {
3416             rDocShell.GetUndoManager()->AddUndoAction(
3417                 std::make_unique<ScUndoTabColor>( &rDocShell, nTab, aOldTabBgColor, rColor));
3418         }
3419         rDocShell.PostPaintExtras();
3420         ScDocShellModificator aModificator( rDocShell );
3421         aModificator.SetDocumentModified();
3422         SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3423 
3424         bSuccess = true;
3425     }
3426     return bSuccess;
3427 }
3428 
3429 bool ScDocFunc::SetTabBgColor(
3430     ScUndoTabColorInfo::List& rUndoTabColorList, bool bApi )
3431 {
3432     ScDocument& rDoc = rDocShell.GetDocument();
3433     bool bRecord = true;
3434     if (!rDoc.IsUndoEnabled())
3435         bRecord = false;
3436 
3437     if ( !rDoc.IsDocEditable() )
3438     {
3439         if (!bApi)
3440             rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Get a better String Error...
3441         return false;
3442     }
3443 
3444     sal_uInt16 nTab;
3445     Color aNewTabBgColor;
3446     bool bSuccess = true;
3447     size_t nTabProtectCount = 0;
3448     size_t nTabListCount = rUndoTabColorList.size();
3449     for ( size_t i = 0; i < nTabListCount; ++i )
3450     {
3451         ScUndoTabColorInfo& rInfo = rUndoTabColorList[i];
3452         nTab = rInfo.mnTabId;
3453         if ( !rDoc.IsTabProtected(nTab) )
3454         {
3455             aNewTabBgColor = rInfo.maNewTabBgColor;
3456             rInfo.maOldTabBgColor = rDoc.GetTabBgColor(nTab);
3457             rDoc.SetTabBgColor(nTab, aNewTabBgColor);
3458             if ( rDoc.GetTabBgColor(nTab) != aNewTabBgColor)
3459             {
3460                 bSuccess = false;
3461                 break;
3462             }
3463         }
3464         else
3465         {
3466             nTabProtectCount++;
3467         }
3468     }
3469 
3470     if ( nTabProtectCount == nTabListCount )
3471     {
3472         if (!bApi)
3473             rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Get a better String Error...
3474         return false;
3475     }
3476 
3477     if (bSuccess)
3478     {
3479         if (bRecord)
3480         {
3481             rDocShell.GetUndoManager()->AddUndoAction(
3482                 std::make_unique<ScUndoTabColor>( &rDocShell, rUndoTabColorList));
3483         }
3484         rDocShell.PostPaintExtras();
3485         ScDocShellModificator aModificator( rDocShell );
3486         aModificator.SetDocumentModified();
3487     }
3488     return bSuccess;
3489 }
3490 
3491 //! SetWidthOrHeight - duplicated in ViewFunc !!!!!!
3492 //! Problems:
3493 //! - Optimal height of text cells is different for a printer and a screen
3494 //! - Optimal width needs a selection in order to take only selected cells into account
3495 
3496 static sal_uInt16 lcl_GetOptimalColWidth( ScDocShell& rDocShell, SCCOL nCol, SCTAB nTab )
3497 {
3498     ScSizeDeviceProvider aProv(&rDocShell);
3499     OutputDevice* pDev = aProv.GetDevice();         // has pixel MapMode
3500     double nPPTX = aProv.GetPPTX();
3501     double nPPTY = aProv.GetPPTY();
3502 
3503     ScDocument& rDoc = rDocShell.GetDocument();
3504     Fraction aOne(1,1);
3505     sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, pDev, nPPTX, nPPTY, aOne, aOne,
3506                                                 false/*bFormula*/ );
3507 
3508     return nTwips;
3509 }
3510 
3511 bool ScDocFunc::SetWidthOrHeight(
3512     bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, SCTAB nTab,
3513     ScSizeMode eMode, sal_uInt16 nSizeTwips, bool bRecord, bool bApi )
3514 {
3515     ScDocShellModificator aModificator( rDocShell );
3516 
3517     if (rRanges.empty())
3518         return true;
3519 
3520     ScDocument& rDoc = rDocShell.GetDocument();
3521     if ( bRecord && !rDoc.IsUndoEnabled() )
3522         bRecord = false;
3523 
3524     // import into read-only document is possible
3525     if ( !rDoc.IsChangeReadOnlyEnabled() && !rDocShell.IsEditable() )
3526     {
3527         if (!bApi)
3528             rDocShell.ErrorMessage(STR_PROTECTIONERR);      //! separate error message?
3529         return false;
3530     }
3531 
3532     SCCOLROW nStart = rRanges[0].mnStart;
3533     SCCOLROW nEnd = rRanges[0].mnEnd;
3534 
3535     if ( eMode == SC_SIZE_OPTIMAL )
3536     {
3537         //! Option "Show formulas" - but where to get them from?
3538     }
3539 
3540     ScDocumentUniquePtr pUndoDoc;
3541     std::unique_ptr<ScOutlineTable> pUndoTab;
3542     std::vector<sc::ColRowSpan> aUndoRanges;
3543 
3544     if ( bRecord )
3545     {
3546         rDoc.BeginDrawUndo();                          // Drawing Updates
3547 
3548         pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
3549         if (bWidth)
3550         {
3551             pUndoDoc->InitUndo( &rDoc, nTab, nTab, true );
3552             rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab, static_cast<SCCOL>(nEnd), MAXROW, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
3553         }
3554         else
3555         {
3556             pUndoDoc->InitUndo( &rDoc, nTab, nTab, false, true );
3557             rDoc.CopyToDocument( 0, static_cast<SCROW>(nStart), nTab, MAXCOL, static_cast<SCROW>(nEnd), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
3558         }
3559 
3560         aUndoRanges = rRanges;
3561 
3562         ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
3563         if (pTable)
3564             pUndoTab.reset(new ScOutlineTable( *pTable ));
3565     }
3566 
3567     bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT;
3568     bool bOutline = false;
3569 
3570     for (const sc::ColRowSpan& rRange : rRanges)
3571     {
3572         SCCOLROW nStartNo = rRange.mnStart;
3573         SCCOLROW nEndNo   = rRange.mnEnd;
3574 
3575         if ( !bWidth )                      // deal with heights always in blocks
3576         {
3577             if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
3578             {
3579                 bool bAll = ( eMode==SC_SIZE_OPTIMAL );
3580                 if (!bAll)
3581                 {
3582                     //  delete for all that have CRFlags::ManualSize enabled
3583                     //  then SetOptimalHeight with bShrink = FALSE
3584                     for (SCROW nRow=nStartNo; nRow<=nEndNo; nRow++)
3585                     {
3586                         CRFlags nOld = rDoc.GetRowFlags(nRow,nTab);
3587                         SCROW nLastRow = -1;
3588                         bool bHidden = rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow);
3589                         if ( !bHidden && ( nOld & CRFlags::ManualSize ) )
3590                             rDoc.SetRowFlags( nRow, nTab, nOld & ~CRFlags::ManualSize );
3591                     }
3592                 }
3593 
3594                 ScSizeDeviceProvider aProv( &rDocShell );
3595                 Fraction aOne(1,1);
3596                 sc::RowHeightContext aCxt(aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice());
3597                 aCxt.setForceAutoSize(bAll);
3598                 rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab);
3599 
3600                 if (bAll)
3601                     rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
3602 
3603                 //  Manual flag will be set already in SetOptimalHeight if bAll=true
3604                 //  (it is on when Extra-Height, otherwise off).
3605             }
3606             else if ( eMode==SC_SIZE_DIRECT || eMode==SC_SIZE_ORIGINAL )
3607             {
3608                 if (nSizeTwips)
3609                 {
3610                     rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips );
3611                     rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true );          // height was set manually
3612                 }
3613                 if ( eMode != SC_SIZE_ORIGINAL )
3614                     rDoc.ShowRows( nStartNo, nEndNo, nTab, nSizeTwips != 0 );
3615             }
3616             else if ( eMode==SC_SIZE_SHOW )
3617             {
3618                 rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
3619             }
3620         }
3621         else                                // Column widths
3622         {
3623             for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++)
3624             {
3625                 if ( eMode != SC_SIZE_VISOPT || !rDoc.ColHidden(nCol, nTab) )
3626                 {
3627                     sal_uInt16 nThisSize = nSizeTwips;
3628 
3629                     if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
3630                         nThisSize = nSizeTwips +
3631                                     lcl_GetOptimalColWidth( rDocShell, nCol, nTab );
3632                     if ( nThisSize )
3633                         rDoc.SetColWidth( nCol, nTab, nThisSize );
3634 
3635                     if ( eMode != SC_SIZE_ORIGINAL )
3636                         rDoc.ShowCol( nCol, nTab, bShow );
3637                 }
3638             }
3639         }
3640 
3641                             //  adjust outlines
3642 
3643         if ( eMode != SC_SIZE_ORIGINAL )
3644         {
3645             if (bWidth)
3646                 bOutline = bOutline || rDoc.UpdateOutlineCol(
3647                         static_cast<SCCOL>(nStartNo),
3648                         static_cast<SCCOL>(nEndNo), nTab, bShow );
3649             else
3650                 bOutline = bOutline || rDoc.UpdateOutlineRow(
3651                         static_cast<SCROW>(nStartNo),
3652                         static_cast<SCROW>(nEndNo), nTab, bShow );
3653         }
3654     }
3655     rDoc.SetDrawPageSize(nTab);
3656 
3657     if (!bOutline)
3658         pUndoTab.reset();
3659 
3660     if (bRecord)
3661     {
3662         ScMarkData aMark;
3663         aMark.SelectOneTable( nTab );
3664         rDocShell.GetUndoManager()->AddUndoAction(
3665             std::make_unique<ScUndoWidthOrHeight>(
3666                 &rDocShell, aMark, nStart, nTab, nEnd, nTab, std::move(pUndoDoc),
3667                 aUndoRanges, std::move(pUndoTab), eMode, nSizeTwips, bWidth));
3668     }
3669 
3670     rDoc.UpdatePageBreaks( nTab );
3671 
3672     ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
3673     if (pViewSh)
3674         pViewSh->OnLOKSetWidthOrHeight(nStart, bWidth);
3675 
3676     rDocShell.PostPaint(0,0,nTab,MAXCOL,MAXROW,nTab,PaintPartFlags::All);
3677     aModificator.SetDocumentModified();
3678 
3679     return false;
3680 }
3681 
3682 bool ScDocFunc::InsertPageBreak( bool bColumn, const ScAddress& rPos,
3683                                 bool bRecord, bool bSetModified )
3684 {
3685     ScDocShellModificator aModificator( rDocShell );
3686 
3687     ScDocument& rDoc = rDocShell.GetDocument();
3688     if (bRecord && !rDoc.IsUndoEnabled())
3689         bRecord = false;
3690     SCTAB nTab = rPos.Tab();
3691     SfxBindings* pBindings = rDocShell.GetViewBindings();
3692 
3693     SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(rPos.Col()) :
3694         static_cast<SCCOLROW>(rPos.Row());
3695     if (nPos == 0)
3696         return false;                   // first column / row
3697 
3698     ScBreakType nBreak = bColumn ?
3699         rDoc.HasColBreak(static_cast<SCCOL>(nPos), nTab) :
3700         rDoc.HasRowBreak(static_cast<SCROW>(nPos), nTab);
3701     if (nBreak & ScBreakType::Manual)
3702         return true;
3703 
3704     if (bRecord)
3705         rDocShell.GetUndoManager()->AddUndoAction(
3706             std::make_unique<ScUndoPageBreak>( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, true ) );
3707 
3708     if (bColumn)
3709         rDoc.SetColBreak(static_cast<SCCOL>(nPos), nTab, false, true);
3710     else
3711         rDoc.SetRowBreak(static_cast<SCROW>(nPos), nTab, false, true);
3712 
3713     rDoc.InvalidatePageBreaks(nTab);
3714     rDoc.UpdatePageBreaks( nTab );
3715 
3716     rDoc.SetStreamValid(nTab, false);
3717 
3718     if (bColumn)
3719     {
3720         rDocShell.PostPaint( static_cast<SCCOL>(nPos)-1, 0, nTab, MAXCOL, MAXROW, nTab, PaintPartFlags::Grid );
3721         if (pBindings)
3722         {
3723             pBindings->Invalidate( FID_INS_COLBRK );
3724             pBindings->Invalidate( FID_DEL_COLBRK );
3725         }
3726     }
3727     else
3728     {
3729         rDocShell.PostPaint( 0, static_cast<SCROW>(nPos)-1, nTab, MAXCOL, MAXROW, nTab, PaintPartFlags::Grid );
3730         if (pBindings)
3731         {
3732             pBindings->Invalidate( FID_INS_ROWBRK );
3733             pBindings->Invalidate( FID_DEL_ROWBRK );
3734         }
3735     }
3736     if (pBindings)
3737         pBindings->Invalidate( FID_DEL_MANUALBREAKS );
3738 
3739     if (bSetModified)
3740         aModificator.SetDocumentModified();
3741 
3742     return true;
3743 }
3744 
3745 bool ScDocFunc::RemovePageBreak( bool bColumn, const ScAddress& rPos,
3746                                 bool bRecord, bool bSetModified )
3747 {
3748     ScDocShellModificator aModificator( rDocShell );
3749 
3750     ScDocument& rDoc = rDocShell.GetDocument();
3751     if (bRecord && !rDoc.IsUndoEnabled())
3752         bRecord = false;
3753     SCTAB nTab = rPos.Tab();
3754     SfxBindings* pBindings = rDocShell.GetViewBindings();
3755 
3756     SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(rPos.Col()) :
3757         static_cast<SCCOLROW>(rPos.Row());
3758 
3759     ScBreakType nBreak;
3760     if (bColumn)
3761         nBreak = rDoc.HasColBreak(static_cast<SCCOL>(nPos), nTab);
3762     else
3763         nBreak = rDoc.HasRowBreak(static_cast<SCROW>(nPos), nTab);
3764     if (!(nBreak & ScBreakType::Manual))
3765         // There is no manual break.
3766         return false;
3767 
3768     if (bRecord)
3769         rDocShell.GetUndoManager()->AddUndoAction(
3770             std::make_unique<ScUndoPageBreak>( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, false ) );
3771 
3772     if (bColumn)
3773         rDoc.RemoveColBreak(static_cast<SCCOL>(nPos), nTab, false, true);
3774     else
3775         rDoc.RemoveRowBreak(static_cast<SCROW>(nPos), nTab, false, true);
3776 
3777     rDoc.UpdatePageBreaks( nTab );
3778 
3779     rDoc.SetStreamValid(nTab, false);
3780 
3781     if (bColumn)
3782     {
3783         rDocShell.PostPaint( static_cast<SCCOL>(nPos)-1, 0, nTab, MAXCOL, MAXROW, nTab, PaintPartFlags::Grid );
3784         if (pBindings)
3785         {
3786             pBindings->Invalidate( FID_INS_COLBRK );
3787             pBindings->Invalidate( FID_DEL_COLBRK );
3788         }
3789     }
3790     else
3791     {
3792         rDocShell.PostPaint( 0, nPos-1, nTab, MAXCOL, MAXROW, nTab, PaintPartFlags::Grid );
3793         if (pBindings)
3794         {
3795             pBindings->Invalidate( FID_INS_ROWBRK );
3796             pBindings->Invalidate( FID_DEL_ROWBRK );
3797         }
3798     }
3799     if (pBindings)
3800         pBindings->Invalidate( FID_DEL_MANUALBREAKS );
3801 
3802     if (bSetModified)
3803         aModificator.SetDocumentModified();
3804 
3805     return true;
3806 }
3807 
3808 void ScDocFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect )
3809 {
3810     ScDocument& rDoc = rDocShell.GetDocument();
3811 
3812     rDoc.SetTabProtection(nTab, &rProtect);
3813     if (rDoc.IsUndoEnabled())
3814     {
3815         ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
3816         OSL_ENSURE(pProtect, "ScDocFunc::Unprotect: ScTableProtection pointer is NULL!");
3817         if (pProtect)
3818         {
3819             ::std::unique_ptr<ScTableProtection> p(new ScTableProtection(*pProtect));
3820             p->setProtected(true); // just in case ...
3821             rDocShell.GetUndoManager()->AddUndoAction(
3822                 std::make_unique<ScUndoTabProtect>(&rDocShell, nTab, std::move(p)) );
3823 
3824             // ownership of unique_ptr now transferred to ScUndoTabProtect.
3825         }
3826     }
3827 
3828     rDocShell.PostPaintGridAll();
3829     ScDocShellModificator aModificator(rDocShell);
3830     aModificator.SetDocumentModified();
3831 }
3832 
3833 bool ScDocFunc::Protect( SCTAB nTab, const OUString& rPassword )
3834 {
3835     ScDocument& rDoc = rDocShell.GetDocument();
3836     if (nTab == TABLEID_DOC)
3837     {
3838         // document protection
3839         ScDocProtection aProtection;
3840         aProtection.setProtected(true);
3841         aProtection.setPassword(rPassword);
3842         rDoc.SetDocProtection(&aProtection);
3843         if (rDoc.IsUndoEnabled())
3844         {
3845             ScDocProtection* pProtect = rDoc.GetDocProtection();
3846             OSL_ENSURE(pProtect, "ScDocFunc::Unprotect: ScDocProtection pointer is NULL!");
3847             if (pProtect)
3848             {
3849                 ::std::unique_ptr<ScDocProtection> p(new ScDocProtection(*pProtect));
3850                 p->setProtected(true); // just in case ...
3851                 rDocShell.GetUndoManager()->AddUndoAction(
3852                     std::make_unique<ScUndoDocProtect>(&rDocShell, std::move(p)) );
3853                 // ownership of unique_ptr is transferred to ScUndoDocProtect.
3854             }
3855         }
3856     }
3857     else
3858     {
3859         // sheet protection
3860 
3861         const ScTableProtection* pOldProtection = rDoc.GetTabProtection(nTab);
3862         ::std::unique_ptr<ScTableProtection> pNewProtection(pOldProtection ? new ScTableProtection(*pOldProtection) : new ScTableProtection());
3863         pNewProtection->setProtected(true);
3864         pNewProtection->setPassword(rPassword);
3865         rDoc.SetTabProtection(nTab, pNewProtection.get());
3866         if (rDoc.IsUndoEnabled())
3867         {
3868             ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
3869             OSL_ENSURE(pProtect, "ScDocFunc::Unprotect: ScTableProtection pointer is NULL!");
3870             if (pProtect)
3871             {
3872                 ::std::unique_ptr<ScTableProtection> p(new ScTableProtection(*pProtect));
3873                 p->setProtected(true); // just in case ...
3874                 rDocShell.GetUndoManager()->AddUndoAction(
3875                     std::make_unique<ScUndoTabProtect>(&rDocShell, nTab, std::move(p)) );
3876                 // ownership of unique_ptr now transferred to ScUndoTabProtect.
3877             }
3878         }
3879     }
3880 
3881     rDocShell.PostPaintGridAll();
3882     ScDocShellModificator aModificator( rDocShell );
3883     aModificator.SetDocumentModified();
3884 
3885     return true;
3886 }
3887 
3888 bool ScDocFunc::Unprotect( SCTAB nTab, const OUString& rPassword, bool bApi )
3889 {
3890     ScDocument& rDoc = rDocShell.GetDocument();
3891 
3892     if (nTab == TABLEID_DOC)
3893     {
3894         // document protection
3895 
3896         ScDocProtection* pDocProtect = rDoc.GetDocProtection();
3897         if (!pDocProtect || !pDocProtect->isProtected())
3898             // already unprotected (should not happen)!
3899             return true;
3900 
3901         // save the protection state before unprotect (for undo).
3902         ::std::unique_ptr<ScDocProtection> pProtectCopy(new ScDocProtection(*pDocProtect));
3903 
3904         if (!pDocProtect->verifyPassword(rPassword))
3905         {
3906             if (!bApi)
3907             {
3908                 vcl::Window* pWin = ScDocShell::GetActiveDialogParent();
3909                 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
3910                                                               VclMessageType::Info, VclButtonsType::Ok,
3911                                                               ScResId(SCSTR_WRONGPASSWORD)));
3912                 xInfoBox->run();
3913             }
3914             return false;
3915         }
3916 
3917         rDoc.SetDocProtection(nullptr);
3918         if (rDoc.IsUndoEnabled())
3919         {
3920             pProtectCopy->setProtected(false);
3921             rDocShell.GetUndoManager()->AddUndoAction(
3922                 std::make_unique<ScUndoDocProtect>(&rDocShell, std::move(pProtectCopy)) );
3923             // ownership of unique_ptr now transferred to ScUndoDocProtect.
3924         }
3925     }
3926     else
3927     {
3928         // sheet protection
3929 
3930         ScTableProtection* pTabProtect = rDoc.GetTabProtection(nTab);
3931         if (!pTabProtect || !pTabProtect->isProtected())
3932             // already unprotected (should not happen)!
3933             return true;
3934 
3935         // save the protection state before unprotect (for undo).
3936         ::std::unique_ptr<ScTableProtection> pProtectCopy(new ScTableProtection(*pTabProtect));
3937         if (!pTabProtect->verifyPassword(rPassword))
3938         {
3939             if (!bApi)
3940             {
3941                 vcl::Window* pWin = ScDocShell::GetActiveDialogParent();
3942                 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
3943                                                               VclMessageType::Info, VclButtonsType::Ok,
3944                                                               ScResId(SCSTR_WRONGPASSWORD)));
3945                 xInfoBox->run();
3946             }
3947             return false;
3948         }
3949 
3950         ::std::unique_ptr<ScTableProtection> pNewProtection(new ScTableProtection(*pTabProtect));
3951         pNewProtection->setProtected(false);
3952         rDoc.SetTabProtection(nTab, pNewProtection.get());
3953         if (rDoc.IsUndoEnabled())
3954         {
3955             pProtectCopy->setProtected(false);
3956             rDocShell.GetUndoManager()->AddUndoAction(
3957                 std::make_unique<ScUndoTabProtect>(&rDocShell, nTab, std::move(pProtectCopy)) );
3958             // ownership of unique_ptr now transferred to ScUndoTabProtect.
3959         }
3960     }
3961 
3962     rDocShell.PostPaintGridAll();
3963     ScDocShellModificator aModificator( rDocShell );
3964     aModificator.SetDocumentModified();
3965 
3966     return true;
3967 }
3968 
3969 void ScDocFunc::ClearItems( const ScMarkData& rMark, const sal_uInt16* pWhich, bool bApi )
3970 {
3971     ScDocShellModificator aModificator( rDocShell );
3972 
3973     ScDocument& rDoc = rDocShell.GetDocument();
3974     bool bUndo (rDoc.IsUndoEnabled());
3975     ScEditableTester aTester( &rDoc, rMark );
3976     if (!aTester.IsEditable())
3977     {
3978         if (!bApi)
3979             rDocShell.ErrorMessage(aTester.GetMessageId());
3980         return;
3981     }
3982 
3983     //  #i12940# ClearItems is called (from setPropertyToDefault) directly with uno object's cached
3984     //  MarkData (GetMarkData), so rMark must be changed to multi selection for ClearSelectionItems
3985     //  here.
3986 
3987     ScRange aMarkRange;
3988     ScMarkData aMultiMark = rMark;
3989     aMultiMark.SetMarking(false);       // for MarkToMulti
3990     aMultiMark.MarkToMulti();
3991     aMultiMark.GetMultiMarkArea( aMarkRange );
3992 
3993     if (bUndo)
3994     {
3995         SCTAB nStartTab = aMarkRange.aStart.Tab();
3996         SCTAB nEndTab = aMarkRange.aEnd.Tab();
3997 
3998         ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
3999         pUndoDoc->InitUndo( &rDoc, nStartTab, nEndTab );
4000         rDoc.CopyToDocument( aMarkRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aMultiMark );
4001 
4002         rDocShell.GetUndoManager()->AddUndoAction(
4003             std::make_unique<ScUndoClearItems>( &rDocShell, aMultiMark, std::move(pUndoDoc), pWhich ) );
4004     }
4005 
4006     rDoc.ClearSelectionItems( pWhich, aMultiMark );
4007 
4008     rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
4009     aModificator.SetDocumentModified();
4010 
4011     //! Bindings-Invalidate etc.?
4012 }
4013 
4014 bool ScDocFunc::ChangeIndent( const ScMarkData& rMark, bool bIncrement, bool bApi )
4015 {
4016     ScDocShellModificator aModificator( rDocShell );
4017 
4018     ScDocument& rDoc = rDocShell.GetDocument();
4019     bool bUndo(rDoc.IsUndoEnabled());
4020     ScEditableTester aTester( &rDoc, rMark );
4021     if (!aTester.IsEditable())
4022     {
4023         if (!bApi)
4024             rDocShell.ErrorMessage(aTester.GetMessageId());
4025         return false;
4026     }
4027 
4028     ScRange aMarkRange;
4029     rMark.GetMultiMarkArea( aMarkRange );
4030 
4031     if (bUndo)
4032     {
4033         SCTAB nStartTab = aMarkRange.aStart.Tab();
4034         SCTAB nTabCount = rDoc.GetTableCount();
4035 
4036         ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
4037         pUndoDoc->InitUndo( &rDoc, nStartTab, nStartTab );
4038         for (const auto& rTab : rMark)
4039         {
4040             if (rTab >= nTabCount)
4041                 break;
4042 
4043             if (rTab != nStartTab)
4044                 pUndoDoc->AddUndoTab( rTab, rTab );
4045         }
4046 
4047         ScRange aCopyRange = aMarkRange;
4048         aCopyRange.aStart.SetTab(0);
4049         aCopyRange.aEnd.SetTab(nTabCount-1);
4050         rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &rMark );
4051 
4052         rDocShell.GetUndoManager()->AddUndoAction(
4053             std::make_unique<ScUndoIndent>( &rDocShell, rMark, std::move(pUndoDoc), bIncrement ) );
4054     }
4055 
4056     rDoc.ChangeSelectionIndent( bIncrement, rMark );
4057 
4058     rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
4059     aModificator.SetDocumentModified();
4060 
4061     SfxBindings* pBindings = rDocShell.GetViewBindings();
4062     if (pBindings)
4063     {
4064         pBindings->Invalidate( SID_ALIGNLEFT );         // ChangeIndent aligns left
4065         pBindings->Invalidate( SID_ALIGNRIGHT );
4066         pBindings->Invalidate( SID_ALIGNBLOCK );
4067         pBindings->Invalidate( SID_ALIGNCENTERHOR );
4068         pBindings->Invalidate( SID_ATTR_LRSPACE );
4069         pBindings->Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
4070         pBindings->Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
4071         pBindings->Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
4072         pBindings->Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
4073         // pseudo slots for Format menu
4074         pBindings->Invalidate( SID_ALIGN_ANY_HDEFAULT );
4075         pBindings->Invalidate( SID_ALIGN_ANY_LEFT );
4076         pBindings->Invalidate( SID_ALIGN_ANY_HCENTER );
4077         pBindings->Invalidate( SID_ALIGN_ANY_RIGHT );
4078         pBindings->Invalidate( SID_ALIGN_ANY_JUSTIFIED );
4079     }
4080 
4081     return true;
4082 }
4083 
4084 bool ScDocFunc::AutoFormat( const ScRange& rRange, const ScMarkData* pTabMark,
4085                             sal_uInt16 nFormatNo, bool bApi )
4086 {
4087     ScDocShellModificator aModificator( rDocShell );
4088 
4089     ScDocument& rDoc = rDocShell.GetDocument();
4090     SCCOL nStartCol = rRange.aStart.Col();
4091     SCROW nStartRow = rRange.aStart.Row();
4092     SCTAB nStartTab = rRange.aStart.Tab();
4093     SCCOL nEndCol = rRange.aEnd.Col();
4094     SCROW nEndRow = rRange.aEnd.Row();
4095     SCTAB nEndTab = rRange.aEnd.Tab();
4096 
4097     bool bRecord = true;
4098     if (!rDoc.IsUndoEnabled())
4099         bRecord = false;
4100     ScMarkData aMark;
4101     if (pTabMark)
4102         aMark = *pTabMark;
4103     else
4104     {
4105         for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4106             aMark.SelectTable( nTab, true );
4107     }
4108 
4109     ScAutoFormat* pAutoFormat = ScGlobal::GetOrCreateAutoFormat();
4110     ScEditableTester aTester( &rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
4111     if ( nFormatNo < pAutoFormat->size() && aTester.IsEditable() )
4112     {
4113         WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4114 
4115         bool bSize = pAutoFormat->findByIndex(nFormatNo)->GetIncludeWidthHeight();
4116 
4117         SCTAB nTabCount = rDoc.GetTableCount();
4118         ScDocumentUniquePtr pUndoDoc;
4119         if ( bRecord )
4120         {
4121             pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4122             pUndoDoc->InitUndo( &rDoc, nStartTab, nStartTab, bSize, bSize );
4123             for (const auto& rTab : aMark)
4124             {
4125                 if (rTab >= nTabCount)
4126                     break;
4127 
4128                 if (rTab != nStartTab)
4129                     pUndoDoc->AddUndoTab( rTab, rTab, bSize, bSize );
4130             }
4131 
4132             ScRange aCopyRange = rRange;
4133             aCopyRange.aStart.SetTab(0);
4134             aCopyRange.aStart.SetTab(nTabCount-1);
4135             rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc, &aMark );
4136             if (bSize)
4137             {
4138                 rDoc.CopyToDocument( nStartCol,0,0, nEndCol,MAXROW,nTabCount-1,
4139                                                             InsertDeleteFlags::NONE, false, *pUndoDoc, &aMark );
4140                 rDoc.CopyToDocument( 0,nStartRow,0, MAXCOL,nEndRow,nTabCount-1,
4141                                                             InsertDeleteFlags::NONE, false, *pUndoDoc, &aMark );
4142             }
4143             rDoc.BeginDrawUndo();
4144         }
4145 
4146         rDoc.AutoFormat( nStartCol, nStartRow, nEndCol, nEndRow, nFormatNo, aMark );
4147 
4148         if (bSize)
4149         {
4150             std::vector<sc::ColRowSpan> aCols(1, sc::ColRowSpan(nStartCol,nEndCol));
4151             std::vector<sc::ColRowSpan> aRows(1, sc::ColRowSpan(nStartRow,nEndRow));
4152 
4153             for (const auto& rTab : aMark)
4154             {
4155                 if (rTab >= nTabCount)
4156                     break;
4157 
4158                 SetWidthOrHeight(true, aCols, rTab, SC_SIZE_VISOPT, STD_EXTRA_WIDTH, false, true);
4159                 SetWidthOrHeight(false, aRows, rTab, SC_SIZE_VISOPT, 0, false, false);
4160                 rDocShell.PostPaint( 0,0,rTab, MAXCOL,MAXROW,rTab,
4161                                 PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top );
4162             }
4163         }
4164         else
4165         {
4166             for (const auto& rTab : aMark)
4167             {
4168                 if (rTab >= nTabCount)
4169                     break;
4170 
4171                 bool bAdj = AdjustRowHeight( ScRange(nStartCol, nStartRow, rTab,
4172                                                     nEndCol, nEndRow, rTab), false );
4173                 if (bAdj)
4174                     rDocShell.PostPaint( 0,nStartRow,rTab, MAXCOL,MAXROW,rTab,
4175                                         PaintPartFlags::Grid | PaintPartFlags::Left );
4176                 else
4177                     rDocShell.PostPaint( nStartCol, nStartRow, rTab,
4178                                         nEndCol, nEndRow, rTab, PaintPartFlags::Grid );
4179             }
4180         }
4181 
4182         if ( bRecord )      // only now is Draw-Undo available
4183         {
4184             rDocShell.GetUndoManager()->AddUndoAction(
4185                 std::make_unique<ScUndoAutoFormat>( &rDocShell, rRange, std::move(pUndoDoc), aMark, bSize, nFormatNo ) );
4186         }
4187 
4188         aModificator.SetDocumentModified();
4189     }
4190     else if (!bApi)
4191         rDocShell.ErrorMessage(aTester.GetMessageId());
4192 
4193     return false;
4194 }
4195 
4196 bool ScDocFunc::EnterMatrix( const ScRange& rRange, const ScMarkData* pTabMark,
4197         const ScTokenArray* pTokenArray, const OUString& rString, bool bApi, bool bEnglish,
4198         const OUString& rFormulaNmsp, const formula::FormulaGrammar::Grammar eGrammar )
4199 {
4200     if (ScViewData::SelectionFillDOOM( rRange ))
4201         return false;
4202 
4203     ScDocShellModificator aModificator( rDocShell );
4204 
4205     bool bSuccess = false;
4206     ScDocument& rDoc = rDocShell.GetDocument();
4207     SCCOL nStartCol = rRange.aStart.Col();
4208     SCROW nStartRow = rRange.aStart.Row();
4209     SCTAB nStartTab = rRange.aStart.Tab();
4210     SCCOL nEndCol = rRange.aEnd.Col();
4211     SCROW nEndRow = rRange.aEnd.Row();
4212     SCTAB nEndTab = rRange.aEnd.Tab();
4213 
4214     ScMarkData aMark;
4215     if (pTabMark)
4216         aMark = *pTabMark;
4217     else
4218     {
4219         for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4220             aMark.SelectTable( nTab, true );
4221     }
4222 
4223     ScEditableTester aTester( &rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
4224     if ( aTester.IsEditable() )
4225     {
4226         WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4227 
4228         ScDocumentUniquePtr pUndoDoc;
4229 
4230         const bool bUndo(rDoc.IsUndoEnabled());
4231         if (bUndo)
4232         {
4233             //! take selected sheets into account also when undoing
4234             pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4235             pUndoDoc->InitUndo( &rDoc, nStartTab, nEndTab );
4236             rDoc.CopyToDocument( rRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc );
4237         }
4238 
4239         // use TokenArray if given, string (and flags) otherwise
4240         if ( pTokenArray )
4241         {
4242             rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
4243                     aMark, EMPTY_OUSTRING, pTokenArray, eGrammar);
4244         }
4245         else if ( rDoc.IsImportingXML() )
4246         {
4247             ScTokenArray aCode;
4248             aCode.AssignXMLString( rString,
4249                     ((eGrammar == formula::FormulaGrammar::GRAM_EXTERNAL) ? rFormulaNmsp : OUString()));
4250             rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
4251                     aMark, EMPTY_OUSTRING, &aCode, eGrammar);
4252             rDoc.IncXMLImportedFormulaCount( rString.getLength() );
4253         }
4254         else if (bEnglish)
4255         {
4256             ScCompiler aComp( &rDoc, rRange.aStart, eGrammar);
4257             std::unique_ptr<ScTokenArray> pCode = aComp.CompileString( rString );
4258             rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
4259                     aMark, EMPTY_OUSTRING, pCode.get(), eGrammar);
4260         }
4261         else
4262             rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
4263                     aMark, rString, nullptr, eGrammar);
4264 
4265         if (bUndo)
4266         {
4267             //! take selected sheets into account also when undoing
4268             rDocShell.GetUndoManager()->AddUndoAction(
4269                 std::make_unique<ScUndoEnterMatrix>( &rDocShell, rRange, std::move(pUndoDoc), rString ) );
4270         }
4271 
4272         //  Err522 painting of DDE-Formulas will be intercepted during interpreting
4273         rDocShell.PostPaint( nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab, PaintPartFlags::Grid );
4274         aModificator.SetDocumentModified();
4275 
4276         bSuccess = true;
4277     }
4278     else if (!bApi)
4279         rDocShell.ErrorMessage(aTester.GetMessageId());
4280 
4281     return bSuccess;
4282 }
4283 
4284 bool ScDocFunc::TabOp( const ScRange& rRange, const ScMarkData* pTabMark,
4285                             const ScTabOpParam& rParam, bool bRecord, bool bApi )
4286 {
4287     ScDocShellModificator aModificator( rDocShell );
4288 
4289     bool bSuccess = false;
4290     ScDocument& rDoc = rDocShell.GetDocument();
4291     SCCOL nStartCol = rRange.aStart.Col();
4292     SCROW nStartRow = rRange.aStart.Row();
4293     SCTAB nStartTab = rRange.aStart.Tab();
4294     SCCOL nEndCol = rRange.aEnd.Col();
4295     SCROW nEndRow = rRange.aEnd.Row();
4296     SCTAB nEndTab = rRange.aEnd.Tab();
4297 
4298     if (bRecord && !rDoc.IsUndoEnabled())
4299         bRecord = false;
4300 
4301     ScMarkData aMark;
4302     if (pTabMark)
4303         aMark = *pTabMark;
4304     else
4305     {
4306         for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4307             aMark.SelectTable( nTab, true );
4308     }
4309 
4310     ScEditableTester aTester( &rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
4311     if ( aTester.IsEditable() )
4312     {
4313         WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4314         rDoc.SetDirty( rRange, false );
4315         if ( bRecord )
4316         {
4317             //! take selected sheets into account also when undoing
4318             ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
4319             pUndoDoc->InitUndo( &rDoc, nStartTab, nEndTab );
4320             rDoc.CopyToDocument( rRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc );
4321 
4322             rDocShell.GetUndoManager()->AddUndoAction(
4323                     std::make_unique<ScUndoTabOp>( &rDocShell,
4324                                      nStartCol, nStartRow, nStartTab,
4325                                      nEndCol, nEndRow, nEndTab, std::move(pUndoDoc),
4326                                      rParam.aRefFormulaCell,
4327                                      rParam.aRefFormulaEnd,
4328                                      rParam.aRefRowCell,
4329                                      rParam.aRefColCell,
4330                                      rParam.meMode) );
4331         }
4332         rDoc.InsertTableOp(rParam, nStartCol, nStartRow, nEndCol, nEndRow, aMark);
4333         rDocShell.PostPaintGridAll();
4334         aModificator.SetDocumentModified();
4335         bSuccess = true;
4336     }
4337     else if (!bApi)
4338         rDocShell.ErrorMessage(aTester.GetMessageId());
4339 
4340     return bSuccess;
4341 }
4342 
4343 static ScDirection DirFromFillDir( FillDir eDir )
4344 {
4345     if (eDir==FILL_TO_BOTTOM)
4346         return DIR_BOTTOM;
4347     else if (eDir==FILL_TO_RIGHT)
4348         return DIR_RIGHT;
4349     else if (eDir==FILL_TO_TOP)
4350         return DIR_TOP;
4351     else // if (eDir==FILL_TO_LEFT)
4352         return DIR_LEFT;
4353 }
4354 
4355 namespace {
4356 
4357 /**
4358  * Expand the fill range as necessary, to allow copying of adjacent cell(s)
4359  * even when those cells are not in the original range.
4360  */
4361 void adjustFillRangeForAdjacentCopy(ScRange& rRange, FillDir eDir)
4362 {
4363     switch (eDir)
4364     {
4365         case FILL_TO_BOTTOM:
4366         {
4367             if (rRange.aStart.Row() == 0)
4368                 return;
4369 
4370             if (rRange.aStart.Row() != rRange.aEnd.Row())
4371                 return;
4372 
4373             // Include the above row.
4374             ScAddress& s = rRange.aStart;
4375             s.SetRow(s.Row()-1);
4376         }
4377         break;
4378         case FILL_TO_TOP:
4379         {
4380             if (rRange.aStart.Row() == MAXROW)
4381                 return;
4382 
4383             if (rRange.aStart.Row() != rRange.aEnd.Row())
4384                 return;
4385 
4386             // Include the row below.
4387             ScAddress& e = rRange.aEnd;
4388             e.SetRow(e.Row()+1);
4389         }
4390         break;
4391         case FILL_TO_LEFT:
4392         {
4393             if (rRange.aStart.Col() == MAXCOL)
4394                 return;
4395 
4396             if (rRange.aStart.Col() != rRange.aEnd.Col())
4397                 return;
4398 
4399             // Include the column to the right.
4400             ScAddress& e = rRange.aEnd;
4401             e.SetCol(e.Col()+1);
4402         }
4403         break;
4404         case FILL_TO_RIGHT:
4405         {
4406             if (rRange.aStart.Col() == 0)
4407                 return;
4408 
4409             if (rRange.aStart.Col() != rRange.aEnd.Col())
4410                 return;
4411 
4412             // Include the column to the left.
4413             ScAddress& s = rRange.aStart;
4414             s.SetCol(s.Col()-1);
4415         }
4416         break;
4417         default:
4418             ;
4419     }
4420 }
4421 
4422 }
4423 
4424 bool ScDocFunc::FillSimple( const ScRange& rRange, const ScMarkData* pTabMark,
4425                             FillDir eDir, bool bApi )
4426 {
4427     ScDocShellModificator aModificator( rDocShell );
4428     ScDocument& rDoc = rDocShell.GetDocument();
4429 
4430     bool bSuccess = false;
4431     ScRange aRange = rRange;
4432     adjustFillRangeForAdjacentCopy(aRange, eDir);
4433 
4434     SCCOL nStartCol = aRange.aStart.Col();
4435     SCROW nStartRow = aRange.aStart.Row();
4436     SCTAB nStartTab = aRange.aStart.Tab();
4437     SCCOL nEndCol = aRange.aEnd.Col();
4438     SCROW nEndRow = aRange.aEnd.Row();
4439     SCTAB nEndTab = aRange.aEnd.Tab();
4440 
4441     bool bRecord = true;
4442     if (!rDoc.IsUndoEnabled())
4443         bRecord = false;
4444 
4445     ScMarkData aMark;
4446     if (pTabMark)
4447         aMark = *pTabMark;
4448     else
4449     {
4450         for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4451             aMark.SelectTable( nTab, true );
4452     }
4453 
4454     ScEditableTester aTester( &rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
4455     if ( aTester.IsEditable() )
4456     {
4457         WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4458 
4459         ScRange aSourceArea = aRange;
4460         ScRange aDestArea   = aRange;
4461 
4462         SCCOLROW nCount = 0;
4463         switch (eDir)
4464         {
4465             case FILL_TO_BOTTOM:
4466                 nCount = aSourceArea.aEnd.Row()-aSourceArea.aStart.Row();
4467                 aSourceArea.aEnd.SetRow( aSourceArea.aStart.Row() );
4468                 break;
4469             case FILL_TO_RIGHT:
4470                 nCount = aSourceArea.aEnd.Col()-aSourceArea.aStart.Col();
4471                 aSourceArea.aEnd.SetCol( aSourceArea.aStart.Col() );
4472                 break;
4473             case FILL_TO_TOP:
4474                 nCount = aSourceArea.aEnd.Row()-aSourceArea.aStart.Row();
4475                 aSourceArea.aStart.SetRow( aSourceArea.aEnd.Row() );
4476                 break;
4477             case FILL_TO_LEFT:
4478                 nCount = aSourceArea.aEnd.Col()-aSourceArea.aStart.Col();
4479                 aSourceArea.aStart.SetCol( aSourceArea.aEnd.Col() );
4480                 break;
4481         }
4482 
4483         ScDocumentUniquePtr pUndoDoc;
4484         if ( bRecord )
4485         {
4486             SCTAB nTabCount = rDoc.GetTableCount();
4487             SCTAB nDestStartTab = aDestArea.aStart.Tab();
4488 
4489             pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4490             pUndoDoc->InitUndo( &rDoc, nDestStartTab, nDestStartTab );
4491             for (const auto& rTab : aMark)
4492             {
4493                 if (rTab >= nTabCount)
4494                     break;
4495 
4496                 if (rTab != nDestStartTab)
4497                     pUndoDoc->AddUndoTab( rTab, rTab );
4498             }
4499 
4500             ScRange aCopyRange = aDestArea;
4501             aCopyRange.aStart.SetTab(0);
4502             aCopyRange.aEnd.SetTab(nTabCount-1);
4503             rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
4504         }
4505 
4506         sal_uLong nProgCount;
4507         if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
4508             nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
4509         else
4510             nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
4511         nProgCount *= nCount;
4512         ScProgress aProgress( rDoc.GetDocumentShell(),
4513                 ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
4514 
4515         rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
4516                 aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
4517                 aMark, nCount, eDir, FILL_SIMPLE );
4518         AdjustRowHeight(aRange);
4519 
4520         if ( bRecord )      // only now is Draw-Undo available
4521         {
4522             rDocShell.GetUndoManager()->AddUndoAction(
4523                 std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
4524                                     eDir, FILL_SIMPLE, FILL_DAY, MAXDOUBLE, 1.0, 1e307) );
4525         }
4526 
4527         rDocShell.PostPaintGridAll();
4528         aModificator.SetDocumentModified();
4529 
4530         bSuccess = true;
4531     }
4532     else if (!bApi)
4533         rDocShell.ErrorMessage(aTester.GetMessageId());
4534 
4535     return bSuccess;
4536 }
4537 
4538 bool ScDocFunc::FillSeries( const ScRange& rRange, const ScMarkData* pTabMark,
4539                             FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
4540                             double fStart, double fStep, double fMax,
4541                             bool bApi )
4542 {
4543     ScDocShellModificator aModificator( rDocShell );
4544 
4545     bool bSuccess = false;
4546     ScDocument& rDoc = rDocShell.GetDocument();
4547     SCCOL nStartCol = rRange.aStart.Col();
4548     SCROW nStartRow = rRange.aStart.Row();
4549     SCTAB nStartTab = rRange.aStart.Tab();
4550     SCCOL nEndCol = rRange.aEnd.Col();
4551     SCROW nEndRow = rRange.aEnd.Row();
4552     SCTAB nEndTab = rRange.aEnd.Tab();
4553 
4554     bool bRecord = true;
4555     if (!rDoc.IsUndoEnabled())
4556         bRecord = false;
4557 
4558     ScMarkData aMark;
4559     if (pTabMark)
4560         aMark = *pTabMark;
4561     else
4562     {
4563         for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4564             aMark.SelectTable( nTab, true );
4565     }
4566 
4567     ScEditableTester aTester( &rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
4568     if ( aTester.IsEditable() )
4569     {
4570         WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4571 
4572         ScRange aSourceArea = rRange;
4573         ScRange aDestArea   = rRange;
4574 
4575         SCSIZE nCount = rDoc.GetEmptyLinesInBlock(
4576                 aSourceArea.aStart.Col(), aSourceArea.aStart.Row(), aSourceArea.aStart.Tab(),
4577                 aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), aSourceArea.aEnd.Tab(),
4578                 DirFromFillDir(eDir) );
4579 
4580         //  keep at least one row/column as source range
4581         SCSIZE nTotLines = ( eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP ) ?
4582             static_cast<SCSIZE>( aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1 ) :
4583             static_cast<SCSIZE>( aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1 );
4584         if ( nCount >= nTotLines )
4585             nCount = nTotLines - 1;
4586 
4587         switch (eDir)
4588         {
4589             case FILL_TO_BOTTOM:
4590                 aSourceArea.aEnd.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aEnd.Row() - nCount ) );
4591                 break;
4592             case FILL_TO_RIGHT:
4593                 aSourceArea.aEnd.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aEnd.Col() - nCount ) );
4594                 break;
4595             case FILL_TO_TOP:
4596                 aSourceArea.aStart.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aStart.Row() + nCount ) );
4597                 break;
4598             case FILL_TO_LEFT:
4599                 aSourceArea.aStart.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aStart.Col() + nCount ) );
4600                 break;
4601         }
4602 
4603         ScDocumentUniquePtr pUndoDoc;
4604         if ( bRecord )
4605         {
4606             SCTAB nTabCount = rDoc.GetTableCount();
4607             SCTAB nDestStartTab = aDestArea.aStart.Tab();
4608 
4609             pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4610             pUndoDoc->InitUndo( &rDoc, nDestStartTab, nDestStartTab );
4611             for (const auto& rTab : aMark)
4612             {
4613                 if (rTab >= nTabCount)
4614                     break;
4615 
4616                 if (rTab != nDestStartTab)
4617                     pUndoDoc->AddUndoTab( rTab, rTab );
4618             }
4619 
4620             rDoc.CopyToDocument(
4621                 aDestArea.aStart.Col(), aDestArea.aStart.Row(), 0,
4622                 aDestArea.aEnd.Col(), aDestArea.aEnd.Row(), nTabCount-1,
4623                 InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
4624         }
4625 
4626         if (aDestArea.aStart.Col() <= aDestArea.aEnd.Col() &&
4627             aDestArea.aStart.Row() <= aDestArea.aEnd.Row())
4628         {
4629             if ( fStart != MAXDOUBLE )
4630             {
4631                 SCCOL nValX = (eDir == FILL_TO_LEFT) ? aDestArea.aEnd.Col() : aDestArea.aStart.Col();
4632                 SCROW nValY = (eDir == FILL_TO_TOP ) ? aDestArea.aEnd.Row() : aDestArea.aStart.Row();
4633                 SCTAB nTab = aDestArea.aStart.Tab();
4634                 rDoc.SetValue( nValX, nValY, nTab, fStart );
4635             }
4636 
4637             sal_uLong nProgCount;
4638             if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
4639                 nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
4640             else
4641                 nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
4642             nProgCount *= nCount;
4643             ScProgress aProgress( rDoc.GetDocumentShell(),
4644                     ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
4645 
4646             rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
4647                         aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
4648                         aMark, nCount, eDir, eCmd, eDateCmd, fStep, fMax );
4649             AdjustRowHeight(rRange);
4650 
4651             rDocShell.PostPaintGridAll();
4652             aModificator.SetDocumentModified();
4653         }
4654 
4655         if ( bRecord )      // only now is Draw-Undo available
4656         {
4657             rDocShell.GetUndoManager()->AddUndoAction(
4658                 std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
4659                                     eDir, eCmd, eDateCmd, fStart, fStep, fMax) );
4660         }
4661 
4662         bSuccess = true;
4663     }
4664     else if (!bApi)
4665         rDocShell.ErrorMessage(aTester.GetMessageId());
4666 
4667     return bSuccess;
4668 }
4669 
4670 bool ScDocFunc::FillAuto( ScRange& rRange, const ScMarkData* pTabMark,
4671                             FillDir eDir, sal_uLong nCount, bool bApi )
4672 {
4673     return FillAuto( rRange, pTabMark, eDir, FILL_AUTO, FILL_DAY, nCount, 1.0/*fStep*/, MAXDOUBLE/*fMax*/, true/*bRecord*/, bApi );
4674 }
4675 
4676 bool ScDocFunc::FillAuto( ScRange& rRange, const ScMarkData* pTabMark, FillDir eDir, FillCmd eCmd, FillDateCmd  eDateCmd, sal_uLong nCount, double fStep, double fMax,  bool bRecord, bool bApi )
4677 {
4678     ScDocShellModificator aModificator( rDocShell );
4679 
4680     ScDocument& rDoc = rDocShell.GetDocument();
4681     SCCOL nStartCol = rRange.aStart.Col();
4682     SCROW nStartRow = rRange.aStart.Row();
4683     SCTAB nStartTab = rRange.aStart.Tab();
4684     SCCOL nEndCol = rRange.aEnd.Col();
4685     SCROW nEndRow = rRange.aEnd.Row();
4686     SCTAB nEndTab = rRange.aEnd.Tab();
4687 
4688     if (bRecord && !rDoc.IsUndoEnabled())
4689         bRecord = false;
4690 
4691     ScMarkData aMark;
4692     if (pTabMark)
4693         aMark = *pTabMark;
4694     else
4695     {
4696         for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4697             aMark.SelectTable( nTab, true );
4698     }
4699 
4700     ScRange aSourceArea = rRange;
4701     ScRange aDestArea   = rRange;
4702 
4703     switch (eDir)
4704     {
4705         case FILL_TO_BOTTOM:
4706             aDestArea.aEnd.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aEnd.Row() + nCount ) );
4707             break;
4708         case FILL_TO_TOP:
4709             if (nCount > sal::static_int_cast<sal_uLong>( aSourceArea.aStart.Row() ))
4710             {
4711                 OSL_FAIL("FillAuto: Row < 0");
4712                 nCount = aSourceArea.aStart.Row();
4713             }
4714             aDestArea.aStart.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aStart.Row() - nCount ) );
4715             break;
4716         case FILL_TO_RIGHT:
4717             aDestArea.aEnd.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aEnd.Col() + nCount ) );
4718             break;
4719         case FILL_TO_LEFT:
4720             if (nCount > sal::static_int_cast<sal_uLong>( aSourceArea.aStart.Col() ))
4721             {
4722                 OSL_FAIL("FillAuto: Col < 0");
4723                 nCount = aSourceArea.aStart.Col();
4724             }
4725             aDestArea.aStart.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aStart.Col() - nCount ) );
4726             break;
4727         default:
4728             OSL_FAIL("Wrong direction with FillAuto");
4729             break;
4730     }
4731 
4732     //      Test for cell protection
4733     //!     Source range can be protected !!!
4734     //!     but can't contain matrix fragments !!!
4735 
4736     ScEditableTester aTester( &rDoc, aDestArea );
4737     if ( !aTester.IsEditable() )
4738     {
4739         if (!bApi)
4740             rDocShell.ErrorMessage(aTester.GetMessageId());
4741         return false;
4742     }
4743 
4744     if ( rDoc.HasSelectedBlockMatrixFragment( nStartCol, nStartRow,
4745             nEndCol, nEndRow, aMark ) )
4746     {
4747         if (!bApi)
4748             rDocShell.ErrorMessage(STR_MATRIXFRAGMENTERR);
4749         return false;
4750     }
4751 
4752     // FID_FILL_... slots should already had been disabled, check here for API
4753     // calls, no message.
4754     if (ScViewData::SelectionFillDOOM( aDestArea))
4755         return false;
4756 
4757     WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4758 
4759     ScDocumentUniquePtr pUndoDoc;
4760     if ( bRecord )
4761     {
4762         SCTAB nTabCount = rDoc.GetTableCount();
4763         SCTAB nDestStartTab = aDestArea.aStart.Tab();
4764 
4765         pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4766         pUndoDoc->InitUndo( &rDoc, nDestStartTab, nDestStartTab );
4767         for (const auto& rTab : aMark)
4768         {
4769             if (rTab >= nTabCount)
4770                 break;
4771 
4772             if (rTab != nDestStartTab)
4773                 pUndoDoc->AddUndoTab( rTab, rTab );
4774         }
4775 
4776         // do not clone note captions in undo document
4777         rDoc.CopyToDocument(
4778             aDestArea.aStart.Col(), aDestArea.aStart.Row(), 0,
4779             aDestArea.aEnd.Col(), aDestArea.aEnd.Row(), nTabCount-1,
4780             InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
4781     }
4782 
4783     sal_uLong nProgCount;
4784     if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
4785         nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
4786     else
4787         nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
4788     nProgCount *= nCount;
4789     ScProgress aProgress( rDoc.GetDocumentShell(),
4790             ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
4791 
4792     rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
4793             aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
4794             aMark, nCount, eDir, eCmd, eDateCmd, fStep, fMax );
4795 
4796     AdjustRowHeight(aDestArea);
4797 
4798     if ( bRecord )      // only now is Draw-Undo available
4799     {
4800         rDocShell.GetUndoManager()->AddUndoAction(
4801             std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
4802                                 eDir, eCmd, eDateCmd, MAXDOUBLE, fStep, fMax) );
4803     }
4804 
4805     rDocShell.PostPaintGridAll();
4806     aModificator.SetDocumentModified();
4807 
4808     rRange = aDestArea;         // return destination range (for marking)
4809     return true;
4810 }
4811 
4812 bool ScDocFunc::MergeCells( const ScCellMergeOption& rOption, bool bContents, bool bRecord, bool bApi, bool bEmptyMergedCells /*=false*/ )
4813 {
4814     using ::std::set;
4815 
4816     ScDocShellModificator aModificator( rDocShell );
4817 
4818     SCCOL nStartCol = rOption.mnStartCol;
4819     SCROW nStartRow = rOption.mnStartRow;
4820     SCCOL nEndCol = rOption.mnEndCol;
4821     SCROW nEndRow = rOption.mnEndRow;
4822     if ((nStartCol == nEndCol && nStartRow == nEndRow) || rOption.maTabs.empty())
4823     {
4824         // Nothing to do.  Bail out quickly
4825         return true;
4826     }
4827 
4828     ScDocument& rDoc = rDocShell.GetDocument();
4829     SCTAB nTab1 = *rOption.maTabs.begin(), nTab2 = *rOption.maTabs.rbegin();
4830 
4831     if (bRecord && !rDoc.IsUndoEnabled())
4832         bRecord = false;
4833 
4834     for (const auto& rTab : rOption.maTabs)
4835     {
4836         ScEditableTester aTester( &rDoc, rTab, nStartCol, nStartRow, nEndCol, nEndRow );
4837         if (!aTester.IsEditable())
4838         {
4839             if (!bApi)
4840                 rDocShell.ErrorMessage(aTester.GetMessageId());
4841             return false;
4842         }
4843 
4844         if ( rDoc.HasAttrib( nStartCol, nStartRow, rTab, nEndCol, nEndRow, rTab,
4845                                 HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
4846         {
4847             // "Merge of already merged cells not possible"
4848             if (!bApi)
4849                 rDocShell.ErrorMessage(STR_MSSG_MERGECELLS_0);
4850             return false;
4851         }
4852     }
4853 
4854     ScDocumentUniquePtr pUndoDoc;
4855     bool bNeedContentsUndo = false;
4856     for (const SCTAB nTab : rOption.maTabs)
4857     {
4858         bool bIsBlockEmpty = ( nStartRow == nEndRow )
4859                              ? rDoc.IsBlockEmpty( nTab, nStartCol+1,nStartRow, nEndCol,nEndRow, true )
4860                              : rDoc.IsBlockEmpty( nTab, nStartCol,nStartRow+1, nStartCol,nEndRow, true ) &&
4861                                rDoc.IsBlockEmpty( nTab, nStartCol+1,nStartRow, nEndCol,nEndRow, true );
4862         bool bNeedContents = bContents && !bIsBlockEmpty;
4863         bool bNeedEmpty = bEmptyMergedCells && !bIsBlockEmpty && !bNeedContents; // if DoMergeContents then cells are emptyed
4864 
4865         if (bRecord)
4866         {
4867             // test if the range contains other notes which also implies that we need an undo document
4868             bool bHasNotes = false;
4869             for( ScAddress aPos( nStartCol, nStartRow, nTab ); !bHasNotes && (aPos.Col() <= nEndCol); aPos.IncCol() )
4870                 for( aPos.SetRow( nStartRow ); !bHasNotes && (aPos.Row() <= nEndRow); aPos.IncRow() )
4871                     bHasNotes = ((aPos.Col() != nStartCol) || (aPos.Row() != nStartRow)) && (rDoc.HasNote(aPos));
4872 
4873             if (!pUndoDoc)
4874             {
4875                 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4876                 pUndoDoc->InitUndo(&rDoc, nTab1, nTab2);
4877             }
4878             // note captions are collected by drawing undo
4879             rDoc.CopyToDocument( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab,
4880                                   InsertDeleteFlags::ALL|InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
4881             if( bHasNotes )
4882                 rDoc.BeginDrawUndo();
4883         }
4884 
4885         if (bNeedContents)
4886             rDoc.DoMergeContents( nTab, nStartCol,nStartRow, nEndCol,nEndRow );
4887         else if ( bNeedEmpty )
4888             rDoc.DoEmptyBlock( nTab, nStartCol,nStartRow, nEndCol,nEndRow );
4889         rDoc.DoMerge( nTab, nStartCol,nStartRow, nEndCol,nEndRow );
4890 
4891         if (rOption.mbCenter)
4892         {
4893             rDoc.ApplyAttr( nStartCol, nStartRow, nTab, SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY ) );
4894             rDoc.ApplyAttr( nStartCol, nStartRow, nTab, SvxVerJustifyItem( SvxCellVerJustify::Center, ATTR_VER_JUSTIFY ) );
4895         }
4896 
4897         if ( !AdjustRowHeight( ScRange( 0,nStartRow,nTab, MAXCOL,nEndRow,nTab ) ) )
4898             rDocShell.PostPaint( nStartCol, nStartRow, nTab,
4899                                  nEndCol, nEndRow, nTab, PaintPartFlags::Grid );
4900         if (bNeedContents || rOption.mbCenter)
4901         {
4902             ScRange aRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab);
4903             rDoc.SetDirty(aRange, true);
4904         }
4905 
4906         bNeedContentsUndo |= bNeedContents;
4907     }
4908 
4909     if (pUndoDoc)
4910     {
4911         std::unique_ptr<SdrUndoGroup> pDrawUndo = rDoc.GetDrawLayer() ? rDoc.GetDrawLayer()->GetCalcUndo() : nullptr;
4912         rDocShell.GetUndoManager()->AddUndoAction(
4913             std::make_unique<ScUndoMerge>(&rDocShell, rOption, bNeedContentsUndo, std::move(pUndoDoc), std::move(pDrawUndo)) );
4914     }
4915 
4916     aModificator.SetDocumentModified();
4917 
4918     SfxBindings* pBindings = rDocShell.GetViewBindings();
4919     if (pBindings)
4920     {
4921         pBindings->Invalidate( FID_MERGE_ON );
4922         pBindings->Invalidate( FID_MERGE_OFF );
4923         pBindings->Invalidate( FID_MERGE_TOGGLE );
4924     }
4925 
4926     return true;
4927 }
4928 
4929 bool ScDocFunc::UnmergeCells( const ScRange& rRange, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge )
4930 {
4931     ScCellMergeOption aOption(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
4932     SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab();
4933     for (SCTAB i = nTab1; i <= nTab2; ++i)
4934         aOption.maTabs.insert(i);
4935 
4936     return UnmergeCells(aOption, bRecord, pUndoRemoveMerge);
4937 }
4938 
4939 bool ScDocFunc::UnmergeCells( const ScCellMergeOption& rOption, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge )
4940 {
4941     using ::std::set;
4942 
4943     if (rOption.maTabs.empty())
4944         // Nothing to unmerge.
4945         return true;
4946 
4947     ScDocShellModificator aModificator( rDocShell );
4948     ScDocument& rDoc = rDocShell.GetDocument();
4949 
4950     if (bRecord && !rDoc.IsUndoEnabled())
4951         bRecord = false;
4952 
4953     ScDocument* pUndoDoc = (pUndoRemoveMerge ? pUndoRemoveMerge->GetUndoDoc() : nullptr);
4954     assert( pUndoDoc || !pUndoRemoveMerge );
4955     for (const SCTAB nTab : rOption.maTabs)
4956     {
4957         ScRange aRange = rOption.getSingleRange(nTab);
4958         if ( !rDoc.HasAttrib(aRange, HasAttrFlags::Merged) )
4959             continue;
4960 
4961         ScRange aExtended = aRange;
4962         rDoc.ExtendMerge(aExtended);
4963         ScRange aRefresh = aExtended;
4964         rDoc.ExtendOverlapped(aRefresh);
4965 
4966         if (bRecord)
4967         {
4968             if (!pUndoDoc)
4969             {
4970                 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
4971                 pUndoDoc->InitUndo(&rDoc, *rOption.maTabs.begin(), *rOption.maTabs.rbegin());
4972             }
4973             rDoc.CopyToDocument(aExtended, InsertDeleteFlags::ATTRIB, false, *pUndoDoc);
4974         }
4975 
4976         const SfxPoolItem& rDefAttr = rDoc.GetPool()->GetDefaultItem( ATTR_MERGE );
4977         ScPatternAttr aPattern( rDoc.GetPool() );
4978         aPattern.GetItemSet().Put( rDefAttr );
4979         rDoc.ApplyPatternAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
4980                                    aRange.aEnd.Col(), aRange.aEnd.Row(), nTab,
4981                                    aPattern );
4982 
4983         rDoc.RemoveFlagsTab( aExtended.aStart.Col(), aExtended.aStart.Row(),
4984                               aExtended.aEnd.Col(), aExtended.aEnd.Row(), nTab,
4985                               ScMF::Hor | ScMF::Ver );
4986 
4987         rDoc.ExtendMerge( aRefresh, true );
4988 
4989         if ( !AdjustRowHeight( aExtended ) )
4990             rDocShell.PostPaint( aExtended, PaintPartFlags::Grid );
4991     }
4992 
4993     if (bRecord)
4994     {
4995         if (pUndoRemoveMerge)
4996         {
4997             // If pUndoRemoveMerge was passed, the caller is responsible for
4998             // adding it to Undo. Just add the current option.
4999             pUndoRemoveMerge->AddCellMergeOption( rOption);
5000         }
5001         else
5002         {
5003             rDocShell.GetUndoManager()->AddUndoAction(
5004                     std::make_unique<ScUndoRemoveMerge>( &rDocShell, rOption, ScDocumentUniquePtr(pUndoDoc) ) );
5005         }
5006     }
5007     aModificator.SetDocumentModified();
5008 
5009     return true;
5010 }
5011 
5012 void ScDocFunc::ModifyRangeNames( const ScRangeName& rNewRanges, SCTAB nTab )
5013 {
5014     SetNewRangeNames( std::unique_ptr<ScRangeName>(new ScRangeName(rNewRanges)), true, nTab );
5015 }
5016 
5017 void ScDocFunc::SetNewRangeNames( std::unique_ptr<ScRangeName> pNewRanges, bool bModifyDoc, SCTAB nTab )     // takes ownership of pNewRanges
5018 {
5019     ScDocShellModificator aModificator( rDocShell );
5020 
5021     OSL_ENSURE( pNewRanges, "pNewRanges is 0" );
5022     ScDocument& rDoc = rDocShell.GetDocument();
5023     bool bUndo(rDoc.IsUndoEnabled());
5024 
5025     if (bUndo)
5026     {
5027         ScRangeName* pOld;
5028         if (nTab >=0)
5029         {
5030             pOld = rDoc.GetRangeName(nTab);
5031         }
5032         else
5033         {
5034             pOld = rDoc.GetRangeName();
5035         }
5036         std::unique_ptr<ScRangeName> pUndoRanges(new ScRangeName(*pOld));
5037         std::unique_ptr<ScRangeName> pRedoRanges(new ScRangeName(*pNewRanges));
5038         rDocShell.GetUndoManager()->AddUndoAction(
5039             std::make_unique<ScUndoRangeNames>( &rDocShell, std::move(pUndoRanges), std::move(pRedoRanges), nTab ) );
5040     }
5041 
5042     // #i55926# While loading XML, formula cells only have a single string token,
5043     // so CompileNameFormula would never find any name (index) tokens, and would
5044     // unnecessarily loop through all cells.
5045     bool bCompile = ( !rDoc.IsImportingXML() && rDoc.GetNamedRangesLockCount() == 0 );
5046 
5047     if ( bCompile )
5048         rDoc.PreprocessRangeNameUpdate();
5049     if (nTab >= 0)
5050         rDoc.SetRangeName( nTab, std::move(pNewRanges) ); // takes ownership
5051     else
5052         rDoc.SetRangeName( std::move(pNewRanges) );       // takes ownership
5053     if ( bCompile )
5054         rDoc.CompileHybridFormula();
5055 
5056     if (bModifyDoc)
5057     {
5058         aModificator.SetDocumentModified();
5059         SfxGetpApp()->Broadcast( SfxHint(SfxHintId::ScAreasChanged) );
5060     }
5061 }
5062 
5063 void ScDocFunc::ModifyAllRangeNames(const std::map<OUString, std::unique_ptr<ScRangeName>>& rRangeMap)
5064 {
5065     ScDocShellModificator aModificator(rDocShell);
5066     ScDocument& rDoc = rDocShell.GetDocument();
5067 
5068     if (rDoc.IsUndoEnabled())
5069     {
5070         std::map<OUString, ScRangeName*> aOldRangeMap;
5071         rDoc.GetRangeNameMap(aOldRangeMap);
5072         rDocShell.GetUndoManager()->AddUndoAction(
5073                 std::make_unique<ScUndoAllRangeNames>(&rDocShell, aOldRangeMap, rRangeMap));
5074     }
5075 
5076     rDoc.PreprocessAllRangeNamesUpdate(rRangeMap);
5077     rDoc.SetAllRangeNames(rRangeMap);
5078     rDoc.CompileHybridFormula();
5079 
5080     aModificator.SetDocumentModified();
5081     SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
5082 }
5083 
5084 void ScDocFunc::CreateOneName( ScRangeName& rList,
5085                                 SCCOL nPosX, SCROW nPosY, SCTAB nTab,
5086                                 SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
5087                                 bool& rCancel, bool bApi )
5088 {
5089     if (rCancel)
5090         return;
5091 
5092     ScDocument& rDoc = rDocShell.GetDocument();
5093     if (!rDoc.HasValueData( nPosX, nPosY, nTab ))
5094     {
5095         OUString aName = rDoc.GetString(nPosX, nPosY, nTab);
5096         ScRangeData::MakeValidName(aName);
5097         if (!aName.isEmpty())
5098         {
5099             OUString aContent(ScRange( nX1, nY1, nTab, nX2, nY2, nTab ).Format(ScRefFlags::RANGE_ABS_3D, &rDoc));
5100 
5101             bool bInsert = false;
5102             ScRangeData* pOld = rList.findByUpperName(ScGlobal::pCharClass->uppercase(aName));
5103             if (pOld)
5104             {
5105                 OUString aOldStr;
5106                 pOld->GetSymbol( aOldStr );
5107                 if (aOldStr != aContent)
5108                 {
5109                     if (bApi)
5110                         bInsert = true;     // don't check via API
5111                     else
5112                     {
5113                         OUString aTemplate = ScResId( STR_CREATENAME_REPLACE );
5114                         OUString aMessage = aTemplate.getToken( 0, '#' ) + aName + aTemplate.getToken( 1, '#' );
5115 
5116                         vcl::Window* pWin = ScDocShell::GetActiveDialogParent();
5117                         std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr,
5118                                                                        VclMessageType::Question, VclButtonsType::YesNo,
5119                                                                        aMessage));
5120                         xQueryBox->add_button(Button::GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
5121                         xQueryBox->set_default_response(RET_YES);
5122 
5123                         short nResult = xQueryBox->run();
5124                         if ( nResult == RET_YES )
5125                         {
5126                             rList.erase(*pOld);
5127                             bInsert = true;
5128                         }
5129                         else if ( nResult == RET_CANCEL )
5130                             rCancel = true;
5131                     }
5132                 }
5133             }
5134             else
5135                 bInsert = true;
5136 
5137             if (bInsert)
5138             {
5139                 ScRangeData* pData = new ScRangeData( &rDoc, aName, aContent,
5140                         ScAddress( nPosX, nPosY, nTab));
5141                 if (!rList.insert(pData))
5142                 {
5143                     OSL_FAIL("nanu?");
5144                 }
5145             }
5146         }
5147     }
5148 }
5149 
5150 bool ScDocFunc::CreateNames( const ScRange& rRange, CreateNameFlags nFlags, bool bApi, SCTAB aTab )
5151 {
5152     if (nFlags == CreateNameFlags::NONE)
5153         return false;       // was nothing
5154 
5155     ScDocShellModificator aModificator( rDocShell );
5156 
5157     bool bDone = false;
5158     SCCOL nStartCol = rRange.aStart.Col();
5159     SCROW nStartRow = rRange.aStart.Row();
5160     SCCOL nEndCol = rRange.aEnd.Col();
5161     SCROW nEndRow = rRange.aEnd.Row();
5162     SCTAB nTab = rRange.aStart.Tab();
5163     OSL_ENSURE(rRange.aEnd.Tab() == nTab, "CreateNames: multiple tables not possible");
5164 
5165     bool bValid = true;
5166     if ( nFlags & ( CreateNameFlags::Top | CreateNameFlags::Bottom ) )
5167         if ( nStartRow == nEndRow )
5168             bValid = false;
5169     if ( nFlags & ( CreateNameFlags::Left | CreateNameFlags::Right ) )
5170         if ( nStartCol == nEndCol )
5171             bValid = false;
5172 
5173     if (bValid)
5174     {
5175         ScDocument& rDoc = rDocShell.GetDocument();
5176         ScRangeName* pNames;
5177         if (aTab >=0)
5178             pNames = rDoc.GetRangeName(nTab);
5179         else
5180             pNames = rDoc.GetRangeName();
5181 
5182         if (!pNames)
5183             return false;   // shouldn't happen
5184         ScRangeName aNewRanges( *pNames );
5185 
5186         bool bTop   ( nFlags & CreateNameFlags::Top );
5187         bool bLeft  ( nFlags & CreateNameFlags::Left );
5188         bool bBottom( nFlags & CreateNameFlags::Bottom );
5189         bool bRight ( nFlags & CreateNameFlags::Right );
5190 
5191         SCCOL nContX1 = nStartCol;
5192         SCROW nContY1 = nStartRow;
5193         SCCOL nContX2 = nEndCol;
5194         SCROW nContY2 = nEndRow;
5195 
5196         if ( bTop )
5197             ++nContY1;
5198         if ( bLeft )
5199             ++nContX1;
5200         if ( bBottom )
5201             --nContY2;
5202         if ( bRight )
5203             --nContX2;
5204 
5205         bool bCancel = false;
5206         SCCOL i;
5207         SCROW j;
5208 
5209         if ( bTop )
5210             for (i=nContX1; i<=nContX2; i++)
5211                 CreateOneName( aNewRanges, i,nStartRow,nTab, i,nContY1,i,nContY2, bCancel, bApi );
5212         if ( bLeft )
5213             for (j=nContY1; j<=nContY2; j++)
5214                 CreateOneName( aNewRanges, nStartCol,j,nTab, nContX1,j,nContX2,j, bCancel, bApi );
5215         if ( bBottom )
5216             for (i=nContX1; i<=nContX2; i++)
5217                 CreateOneName( aNewRanges, i,nEndRow,nTab, i,nContY1,i,nContY2, bCancel, bApi );
5218         if ( bRight )
5219             for (j=nContY1; j<=nContY2; j++)
5220                 CreateOneName( aNewRanges, nEndCol,j,nTab, nContX1,j,nContX2,j, bCancel, bApi );
5221 
5222         if ( bTop && bLeft )
5223             CreateOneName( aNewRanges, nStartCol,nStartRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
5224         if ( bTop && bRight )
5225             CreateOneName( aNewRanges, nEndCol,nStartRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
5226         if ( bBottom && bLeft )
5227             CreateOneName( aNewRanges, nStartCol,nEndRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
5228         if ( bBottom && bRight )
5229             CreateOneName( aNewRanges, nEndCol,nEndRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
5230 
5231         ModifyRangeNames( aNewRanges, aTab );
5232         bDone = true;
5233 
5234     }
5235 
5236     return bDone;
5237 }
5238 
5239 bool ScDocFunc::InsertNameList( const ScAddress& rStartPos, bool bApi )
5240 {
5241     ScDocShellModificator aModificator( rDocShell );
5242 
5243     bool bDone = false;
5244     ScDocument& rDoc = rDocShell.GetDocument();
5245     const bool bRecord = rDoc.IsUndoEnabled();
5246     SCTAB nTab = rStartPos.Tab();
5247 
5248     //local names have higher priority than global names
5249     ScRangeName* pLocalList = rDoc.GetRangeName(nTab);
5250     sal_uInt16 nValidCount = 0;
5251     for (const auto& rEntry : *pLocalList)
5252     {
5253         const ScRangeData& r = *rEntry.second;
5254         if (!r.HasType(ScRangeData::Type::Database))
5255             ++nValidCount;
5256     }
5257     ScRangeName* pList = rDoc.GetRangeName();
5258     for (const auto& rEntry : *pList)
5259     {
5260         const ScRangeData& r = *rEntry.second;
5261         if (!r.HasType(ScRangeData::Type::Database) && !pLocalList->findByUpperName(r.GetUpperName()))
5262             ++nValidCount;
5263     }
5264 
5265     if (nValidCount)
5266     {
5267         SCCOL nStartCol = rStartPos.Col();
5268         SCROW nStartRow = rStartPos.Row();
5269         SCCOL nEndCol = nStartCol + 1;
5270         SCROW nEndRow = nStartRow + static_cast<SCROW>(nValidCount) - 1;
5271 
5272         ScEditableTester aTester( &rDoc, nTab, nStartCol,nStartRow, nEndCol,nEndRow );
5273         if (aTester.IsEditable())
5274         {
5275             ScDocumentUniquePtr pUndoDoc;
5276 
5277             if (bRecord)
5278             {
5279                 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
5280                 pUndoDoc->InitUndo( &rDoc, nTab, nTab );
5281                 rDoc.CopyToDocument(nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
5282                                     InsertDeleteFlags::ALL, false, *pUndoDoc);
5283 
5284                 rDoc.BeginDrawUndo();      // because of adjusting heights
5285             }
5286 
5287             std::unique_ptr<ScRangeData*[]> ppSortArray(new ScRangeData* [ nValidCount ]);
5288             sal_uInt16 j = 0;
5289             for (const auto& rEntry : *pLocalList)
5290             {
5291                 ScRangeData& r = *rEntry.second;
5292                 if (!r.HasType(ScRangeData::Type::Database))
5293                     ppSortArray[j++] = &r;
5294             }
5295             for (const auto& [rName, rxData] : *pList)
5296             {
5297                 ScRangeData& r = *rxData;
5298                 if (!r.HasType(ScRangeData::Type::Database) && !pLocalList->findByUpperName(rName))
5299                     ppSortArray[j++] = &r;
5300             }
5301             qsort( static_cast<void*>(ppSortArray.get()), nValidCount, sizeof(ScRangeData*),
5302                 &ScRangeData_QsortNameCompare );
5303             OUString aName;
5304             OUStringBuffer aContent;
5305             OUString aFormula;
5306             SCROW nOutRow = nStartRow;
5307             for (j=0; j<nValidCount; j++)
5308             {
5309                 ScRangeData* pData = ppSortArray[j];
5310                 pData->GetName(aName);
5311                 // adjust relative references to the left column in Excel-compliant way:
5312                 pData->UpdateSymbol(aContent, ScAddress( nStartCol, nOutRow, nTab ));
5313                 aFormula = "=" + aContent.toString();
5314                 ScSetStringParam aParam;
5315                 aParam.setTextInput();
5316                 rDoc.SetString(ScAddress(nStartCol,nOutRow,nTab), aName, &aParam);
5317                 rDoc.SetString(ScAddress(nEndCol,nOutRow,nTab), aFormula, &aParam);
5318                 ++nOutRow;
5319             }
5320 
5321             ppSortArray.reset();
5322 
5323             if (bRecord)
5324             {
5325                 ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO ));
5326                 pRedoDoc->InitUndo( &rDoc, nTab, nTab );
5327                 rDoc.CopyToDocument(nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
5328                                     InsertDeleteFlags::ALL, false, *pRedoDoc);
5329 
5330                 rDocShell.GetUndoManager()->AddUndoAction(
5331                     std::make_unique<ScUndoListNames>( &rDocShell,
5332                                 ScRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab ),
5333                                 std::move(pUndoDoc), std::move(pRedoDoc) ) );
5334             }
5335 
5336             if (!AdjustRowHeight(ScRange(0,nStartRow,nTab,MAXCOL,nEndRow,nTab)))
5337                 rDocShell.PostPaint( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab, PaintPartFlags::Grid );
5338 
5339             aModificator.SetDocumentModified();
5340             bDone = true;
5341         }
5342         else if (!bApi)
5343             rDocShell.ErrorMessage(aTester.GetMessageId());
5344     }
5345     return bDone;
5346 }
5347 
5348 void ScDocFunc::ResizeMatrix( const ScRange& rOldRange, const ScAddress& rNewEnd )
5349 {
5350     ScDocument& rDoc = rDocShell.GetDocument();
5351     SCCOL nStartCol = rOldRange.aStart.Col();
5352     SCROW nStartRow = rOldRange.aStart.Row();
5353     SCTAB nTab = rOldRange.aStart.Tab();
5354 
5355     OUString aFormula;
5356     rDoc.GetFormula( nStartCol, nStartRow, nTab, aFormula );
5357     if ( aFormula.startsWith("{") && aFormula.endsWith("}") )
5358     {
5359         OUString aUndo = ScResId( STR_UNDO_RESIZEMATRIX );
5360         bool bUndo(rDoc.IsUndoEnabled());
5361         if (bUndo)
5362         {
5363             ViewShellId nViewShellId(1);
5364             if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
5365                 nViewShellId = pViewSh->GetViewShellId();
5366             rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
5367         }
5368 
5369         aFormula = aFormula.copy(1, aFormula.getLength()-2);
5370 
5371         ScMarkData aMark;
5372         aMark.SetMarkArea( rOldRange );
5373         aMark.SelectTable( nTab, true );
5374         ScRange aNewRange( rOldRange.aStart, rNewEnd );
5375 
5376         if ( DeleteContents( aMark, InsertDeleteFlags::CONTENTS, true, false/*bApi*/ ) )
5377         {
5378             // GRAM_API for API compatibility.
5379             if (!EnterMatrix( aNewRange, &aMark, nullptr, aFormula, false/*bApi*/, false, EMPTY_OUSTRING, formula::FormulaGrammar::GRAM_API ))
5380             {
5381                 // try to restore the previous state
5382                 EnterMatrix( rOldRange, &aMark, nullptr, aFormula, false/*bApi*/, false, EMPTY_OUSTRING, formula::FormulaGrammar::GRAM_API );
5383             }
5384         }
5385 
5386         if (bUndo)
5387             rDocShell.GetUndoManager()->LeaveListAction();
5388     }
5389 }
5390 
5391 void ScDocFunc::InsertAreaLink( const OUString& rFile, const OUString& rFilter,
5392                                 const OUString& rOptions, const OUString& rSource,
5393                                 const ScRange& rDestRange, sal_uLong nRefresh,
5394                                 bool bFitBlock, bool bApi )
5395 {
5396     ScDocument& rDoc = rDocShell.GetDocument();
5397     bool bUndo (rDoc.IsUndoEnabled());
5398 
5399     sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
5400 
5401     //  #i52120# if other area links exist at the same start position,
5402     //  remove them first (file format specifies only one link definition
5403     //  for a cell)
5404 
5405     sal_uInt16 nLinkCount = pLinkManager->GetLinks().size();
5406     sal_uInt16 nRemoved = 0;
5407     sal_uInt16 nLinkPos = 0;
5408     while (nLinkPos<nLinkCount)
5409     {
5410         ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[nLinkPos].get();
5411         ScAreaLink* pLink = dynamic_cast<ScAreaLink*>(pBase);
5412         if (pLink && pLink->GetDestArea().aStart == rDestRange.aStart)
5413         {
5414             if ( bUndo )
5415             {
5416                 if ( !nRemoved )
5417                 {
5418                     // group all remove and the insert action
5419                     OUString aUndo = ScResId( STR_UNDO_INSERTAREALINK );
5420                     ViewShellId nViewShellId(-1);
5421                     if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
5422                         nViewShellId = pViewSh->GetViewShellId();
5423                     rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
5424                 }
5425 
5426                 ScAreaLink* pOldArea = static_cast<ScAreaLink*>(pBase);
5427                 rDocShell.GetUndoManager()->AddUndoAction(
5428                     std::make_unique<ScUndoRemoveAreaLink>( &rDocShell,
5429                         pOldArea->GetFile(), pOldArea->GetFilter(), pOldArea->GetOptions(),
5430                         pOldArea->GetSource(), pOldArea->GetDestArea(), pOldArea->GetRefreshDelay() ) );
5431             }
5432             pLinkManager->Remove( pBase );
5433             nLinkCount = pLinkManager->GetLinks().size();
5434             ++nRemoved;
5435         }
5436         else
5437             ++nLinkPos;
5438     }
5439 
5440     OUString aFilterName = rFilter;
5441     OUString aNewOptions = rOptions;
5442     if (aFilterName.isEmpty())
5443         ScDocumentLoader::GetFilterName( rFile, aFilterName, aNewOptions, true, !bApi );
5444 
5445     //  remove application prefix from filter name here, so the filter options
5446     //  aren't reset when the filter name is changed in ScAreaLink::DataChanged
5447     ScDocumentLoader::RemoveAppPrefix( aFilterName );
5448 
5449     ScAreaLink* pLink = new ScAreaLink( &rDocShell, rFile, aFilterName,
5450                                         aNewOptions, rSource, rDestRange, nRefresh );
5451     OUString aTmp = aFilterName;
5452     pLinkManager->InsertFileLink( *pLink, OBJECT_CLIENT_FILE, rFile, &aTmp, &rSource );
5453 
5454     //  Undo for an empty link
5455 
5456     if (bUndo)
5457     {
5458         rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoInsertAreaLink>( &rDocShell,
5459                                                     rFile, aFilterName, aNewOptions,
5460                                                     rSource, rDestRange, nRefresh ) );
5461         if ( nRemoved )
5462             rDocShell.GetUndoManager()->LeaveListAction();  // undo for link update is still separate
5463     }
5464 
5465     //  Update has its own undo
5466     if (rDoc.IsExecuteLinkEnabled())
5467     {
5468         pLink->SetDoInsert(bFitBlock);  // if applicable, don't insert anything on first update
5469         pLink->Update();                // no SetInCreate -> carry out update
5470     }
5471     pLink->SetDoInsert(true);       // Default = true
5472 
5473     SfxBindings* pBindings = rDocShell.GetViewBindings();
5474     if (pBindings)
5475         pBindings->Invalidate( SID_LINKS );
5476 
5477     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );     // Navigator
5478 }
5479 
5480 void ScDocFunc::ReplaceConditionalFormat( sal_uLong nOldFormat, std::unique_ptr<ScConditionalFormat> pFormat, SCTAB nTab, const ScRangeList& rRanges )
5481 {
5482     ScDocShellModificator aModificator(rDocShell);
5483     ScDocument& rDoc = rDocShell.GetDocument();
5484     if(rDoc.IsTabProtected(nTab))
5485         return;
5486 
5487     bool bUndo = rDoc.IsUndoEnabled();
5488     ScDocumentUniquePtr pUndoDoc;
5489     ScRange aCombinedRange = rRanges.Combine();
5490     ScRange aCompleteRange;
5491     if(bUndo)
5492     {
5493         pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
5494         pUndoDoc->InitUndo( &rDoc, nTab, nTab );
5495 
5496         if(pFormat)
5497         {
5498             aCompleteRange = aCombinedRange;
5499         }
5500         if(nOldFormat)
5501         {
5502             ScConditionalFormat* pOldFormat = rDoc.GetCondFormList(nTab)->GetFormat(nOldFormat);
5503             if(pOldFormat)
5504                 aCompleteRange.ExtendTo(pOldFormat->GetRange().Combine());
5505         }
5506 
5507         rDoc.CopyToDocument(aCompleteRange.aStart.Col(),aCompleteRange.aStart.Row(),nTab,
5508                             aCompleteRange.aEnd.Col(),aCompleteRange.aEnd.Row(),nTab,
5509                             InsertDeleteFlags::ALL, false, *pUndoDoc);
5510     }
5511 
5512     std::unique_ptr<ScRange> pRepaintRange;
5513     if(nOldFormat)
5514     {
5515         ScConditionalFormat* pOldFormat = rDoc.GetCondFormList(nTab)->GetFormat(nOldFormat);
5516         if(pOldFormat)
5517         {
5518             pRepaintRange.reset(new ScRange( pOldFormat->GetRange().Combine() ));
5519             rDoc.RemoveCondFormatData(pOldFormat->GetRange(), nTab, pOldFormat->GetKey());
5520         }
5521 
5522         rDoc.DeleteConditionalFormat(nOldFormat, nTab);
5523         rDoc.SetStreamValid(nTab, false);
5524     }
5525     if(pFormat)
5526     {
5527         if(pRepaintRange)
5528             pRepaintRange->ExtendTo(aCombinedRange);
5529         else
5530             pRepaintRange.reset(new ScRange(aCombinedRange));
5531 
5532         sal_uLong nIndex = rDoc.AddCondFormat(std::move(pFormat), nTab);
5533 
5534         rDoc.AddCondFormatData(rRanges, nTab, nIndex);
5535         rDoc.SetStreamValid(nTab, false);
5536     }
5537 
5538     if(bUndo)
5539     {
5540         ScDocumentUniquePtr pRedoDoc(new ScDocument(SCDOCMODE_UNDO));
5541         pRedoDoc->InitUndo( &rDoc, nTab, nTab );
5542         rDoc.CopyToDocument(aCompleteRange.aStart.Col(),aCompleteRange.aStart.Row(),nTab,
5543                             aCompleteRange.aEnd.Col(),aCompleteRange.aEnd.Row(),nTab,
5544                             InsertDeleteFlags::ALL, false, *pRedoDoc);
5545         rDocShell.GetUndoManager()->AddUndoAction(
5546                 std::make_unique<ScUndoConditionalFormat>(&rDocShell, std::move(pUndoDoc), std::move(pRedoDoc), aCompleteRange));
5547     }
5548 
5549     if(pRepaintRange)
5550         rDocShell.PostPaint(*pRepaintRange, PaintPartFlags::Grid);
5551 
5552     aModificator.SetDocumentModified();
5553     SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
5554 }
5555 
5556 void ScDocFunc::SetConditionalFormatList( ScConditionalFormatList* pList, SCTAB nTab )
5557 {
5558     ScDocShellModificator aModificator(rDocShell);
5559     ScDocument& rDoc = rDocShell.GetDocument();
5560     if(rDoc.IsTabProtected(nTab))
5561         return;
5562 
5563     bool bUndo = rDoc.IsUndoEnabled();
5564     ScDocumentUniquePtr pUndoDoc;
5565     if (bUndo)
5566     {
5567         pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
5568         pUndoDoc->InitUndo( &rDoc, nTab, nTab );
5569 
5570         ScConditionalFormatList* pOld = rDoc.GetCondFormList(nTab);
5571 
5572         if (pOld)
5573             pUndoDoc->SetCondFormList(new ScConditionalFormatList(pUndoDoc.get(), *pOld), nTab);
5574         else
5575             pUndoDoc->SetCondFormList(nullptr, nTab);
5576 
5577     }
5578 
5579     // first remove all old entries
5580     ScConditionalFormatList* pOldList = rDoc.GetCondFormList(nTab);
5581     pOldList->RemoveFromDocument(&rDoc);
5582 
5583     // then set new entries
5584     pList->AddToDocument(&rDoc);
5585 
5586     rDoc.SetCondFormList(pList, nTab);
5587     rDocShell.PostPaintGridAll();
5588 
5589     if(bUndo)
5590     {
5591         ScDocumentUniquePtr pRedoDoc(new ScDocument(SCDOCMODE_UNDO));
5592         pRedoDoc->InitUndo( &rDoc, nTab, nTab );
5593         pRedoDoc->SetCondFormList(new ScConditionalFormatList(pRedoDoc.get(), *pList), nTab);
5594 
5595         rDocShell.GetUndoManager()->AddUndoAction(
5596                 std::make_unique<ScUndoConditionalFormatList>(&rDocShell, std::move(pUndoDoc), std::move(pRedoDoc), nTab));
5597     }
5598 
5599     rDoc.SetStreamValid(nTab, false);
5600     aModificator.SetDocumentModified();
5601     SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
5602 }
5603 
5604 void ScDocFunc::ConvertFormulaToValue( const ScRange& rRange, bool bInteraction )
5605 {
5606     ScDocShellModificator aModificator(rDocShell);
5607     ScDocument& rDoc = rDocShell.GetDocument();
5608     bool bRecord = true;
5609     if (!rDoc.IsUndoEnabled())
5610         bRecord = false;
5611 
5612     ScEditableTester aTester(&rDoc, rRange);
5613     if (!aTester.IsEditable())
5614     {
5615         if (bInteraction)
5616             rDocShell.ErrorMessage(aTester.GetMessageId());
5617         return;
5618     }
5619 
5620     sc::TableValues aUndoVals(rRange);
5621     sc::TableValues* pUndoVals = bRecord ? &aUndoVals : nullptr;
5622 
5623     rDoc.ConvertFormulaToValue(rRange, pUndoVals);
5624 
5625     if (bRecord && pUndoVals)
5626     {
5627         rDocShell.GetUndoManager()->AddUndoAction(
5628             std::make_unique<sc::UndoFormulaToValue>(&rDocShell, *pUndoVals));
5629     }
5630 
5631     rDocShell.PostPaint(rRange, PaintPartFlags::Grid);
5632     rDocShell.PostDataChanged();
5633     rDoc.BroadcastCells(rRange, SfxHintId::ScDataChanged);
5634     aModificator.SetDocumentModified();
5635 }
5636 
5637 void ScDocFunc::EnterListAction(const char* pNameResId)
5638 {
5639     OUString aUndo(ScResId(pNameResId));
5640     ViewShellId nViewShellId(-1);
5641     if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
5642         nViewShellId = pViewSh->GetViewShellId();
5643     rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
5644 }
5645 
5646 void ScDocFunc::EndListAction()
5647 {
5648     rDocShell.GetUndoManager()->LeaveListAction();
5649 }
5650 
5651 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
5652