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