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