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