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