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