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