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