tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / docshell / docfunc.cxx
blobe745b922315e6ddcef9b45a1dc6f0c822ff2d3ad
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <scitems.hxx>
22 #include <comphelper/lok.hxx>
23 #include <o3tl/safeint.hxx>
24 #include <o3tl/string_view.hxx>
25 #include <sfx2/app.hxx>
26 #include <editeng/editobj.hxx>
27 #include <editeng/justifyitem.hxx>
28 #include <sfx2/linkmgr.hxx>
29 #include <sfx2/bindings.hxx>
30 #include <utility>
31 #include <vcl/weld.hxx>
32 #include <vcl/stdtext.hxx>
33 #include <vcl/svapp.hxx>
34 #include <svx/svdocapt.hxx>
35 #include <sal/log.hxx>
36 #include <unotools/charclass.hxx>
37 #include <osl/diagnose.h>
39 #include <com/sun/star/container/XNameContainer.hpp>
40 #include <com/sun/star/script/ModuleType.hpp>
41 #include <com/sun/star/script/XLibraryContainer.hpp>
42 #include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
44 #include <docfunc.hxx>
46 #include <sc.hrc>
47 #include <strings.hrc>
49 #include <arealink.hxx>
50 #include <attrib.hxx>
51 #include <dociter.hxx>
52 #include <autoform.hxx>
53 #include <formulacell.hxx>
54 #include <cellmergeoption.hxx>
55 #include <detdata.hxx>
56 #include <detfunc.hxx>
57 #include <docpool.hxx>
58 #include <docsh.hxx>
59 #include <drwlayer.hxx>
60 #include <editutil.hxx>
61 #include <globstr.hrc>
62 #include <olinetab.hxx>
63 #include <patattr.hxx>
64 #include <rangenam.hxx>
65 #include <refundo.hxx>
66 #include <scresid.hxx>
67 #include <stlpool.hxx>
68 #include <stlsheet.hxx>
69 #include <tablink.hxx>
70 #include <tabvwsh.hxx>
71 #include <uiitems.hxx>
72 #include <undoblk.hxx>
73 #include <undocell.hxx>
74 #include <undodraw.hxx>
75 #include <undotab.hxx>
76 #include <sizedev.hxx>
77 #include <scmod.hxx>
78 #include <inputhdl.hxx>
79 #include <editable.hxx>
80 #include <compiler.hxx>
81 #include <scui_def.hxx>
82 #include <tabprotection.hxx>
83 #include <clipparam.hxx>
84 #include <externalrefmgr.hxx>
85 #include <undorangename.hxx>
86 #include <progress.hxx>
87 #include <dpobject.hxx>
88 #include <stringutil.hxx>
89 #include <cellvalue.hxx>
90 #include <tokenarray.hxx>
91 #include <rowheightcontext.hxx>
92 #include <cellvalues.hxx>
93 #include <undoconvert.hxx>
94 #include <docfuncutil.hxx>
95 #include <sheetevents.hxx>
96 #include <conditio.hxx>
97 #include <columnspanset.hxx>
98 #include <validat.hxx>
99 #include <SparklineGroup.hxx>
100 #include <SparklineAttributes.hxx>
101 #include <SparklineData.hxx>
102 #include <undo/UndoInsertSparkline.hxx>
103 #include <undo/UndoDeleteSparkline.hxx>
104 #include <undo/UndoDeleteSparklineGroup.hxx>
105 #include <undo/UndoEditSparklineGroup.hxx>
106 #include <undo/UndoUngroupSparklines.hxx>
107 #include <undo/UndoGroupSparklines.hxx>
108 #include <undo/UndoEditSparkline.hxx>
109 #include <config_features.h>
111 #include <memory>
112 #include <basic/basmgr.hxx>
113 #include <set>
114 #include <vector>
115 #include <sfx2/viewfrm.hxx>
117 using namespace com::sun::star;
118 using ::std::vector;
120 #define AUTOFORMAT_WARN_SIZE 0x10ffffUL
122 void ScDocFunc::NotifyDrawUndo( std::unique_ptr<SdrUndoAction> pUndoAction)
124 // #i101118# if drawing layer collects the undo actions, add it there
125 ScDrawLayer* pDrawLayer = rDocShell.GetDocument().GetDrawLayer();
126 if( pDrawLayer && pDrawLayer->IsRecording() )
127 pDrawLayer->AddCalcUndo( std::move(pUndoAction) );
128 else
129 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDraw>( std::move(pUndoAction), &rDocShell ) );
130 rDocShell.SetDrawModified();
132 // the affected sheet isn't known, so all stream positions are invalidated
133 ScDocument& rDoc = rDocShell.GetDocument();
134 SCTAB nTabCount = rDoc.GetTableCount();
135 for (SCTAB nTab=0; nTab<nTabCount; nTab++)
136 rDoc.SetStreamValid(nTab, false);
139 // paint row above the range (because of lines after AdjustRowHeight)
141 static void lcl_PaintAbove( ScDocShell& rDocShell, const ScRange& rRange )
143 SCROW nRow = rRange.aStart.Row();
144 if ( nRow > 0 )
146 SCTAB nTab = rRange.aStart.Tab(); //! all of them?
147 --nRow;
148 ScDocument& rDoc = rDocShell.GetDocument();
149 rDocShell.PostPaint( ScRange(0,nRow,nTab,rDoc.MaxCol(),nRow,nTab), PaintPartFlags::Grid );
153 bool ScDocFunc::AdjustRowHeight( const ScRange& rRange, bool bPaint, bool bApi )
155 ScDocument& rDoc = rDocShell.GetDocument();
156 SfxViewShell* pSomeViewForThisDoc = rDocShell.GetBestViewShell(false);
157 if ( rDoc.IsImportingXML() )
159 // for XML import, all row heights are updated together after importing
160 return false;
162 if ( rDoc.IsAdjustHeightLocked() )
164 return false;
167 SCTAB nTab = rRange.aStart.Tab();
168 SCROW nStartRow = rRange.aStart.Row();
169 SCROW nEndRow = rRange.aEnd.Row();
171 ScSizeDeviceProvider aProv( &rDocShell );
172 Fraction aOne(1,1);
174 sc::RowHeightContext aCxt(rDoc.MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice());
175 bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab, bApi);
176 // tdf#76183: recalculate objects' positions
177 if (bChanged)
179 if (comphelper::LibreOfficeKit::isActive())
181 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
182 while (pViewShell)
184 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
185 if (pTabViewShell && pSomeViewForThisDoc && pTabViewShell->GetDocId() == pSomeViewForThisDoc->GetDocId())
187 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nTab))
188 pPosHelper->invalidateByIndex(nStartRow);
190 pViewShell = SfxViewShell::GetNext(*pViewShell);
193 rDoc.SetDrawPageSize(nTab);
196 if ( bPaint && bChanged )
197 rDocShell.PostPaint(ScRange(0, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab),
198 PaintPartFlags::Grid | PaintPartFlags::Left);
200 if (comphelper::LibreOfficeKit::isActive())
202 ScTabViewShell::notifyAllViewsHeaderInvalidation(pSomeViewForThisDoc, ROW_HEADER, nTab);
203 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
204 pSomeViewForThisDoc, false /* bColumns */, true /* bRows */, true /* bSizes*/,
205 false /* bHidden */, false /* bFiltered */, false /* bGroups */, nTab);
208 return bChanged;
211 bool ScDocFunc::DetectiveAddPred(const ScAddress& rPos)
213 ScDocShellModificator aModificator( rDocShell );
215 rDocShell.MakeDrawLayer();
216 ScDocument& rDoc = rDocShell.GetDocument();
217 bool bUndo (rDoc.IsUndoEnabled());
218 ScDrawLayer* pModel = rDoc.GetDrawLayer();
219 SCCOL nCol = rPos.Col();
220 SCROW nRow = rPos.Row();
221 SCTAB nTab = rPos.Tab();
223 if (bUndo)
224 pModel->BeginCalcUndo(false);
225 bool bDone = ScDetectiveFunc(rDoc, nTab).ShowPred( nCol, nRow );
226 std::unique_ptr<SdrUndoGroup> pUndo;
227 if (bUndo)
228 pUndo = pModel->GetCalcUndo();
229 if (bDone)
231 ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDPRED );
232 rDoc.AddDetectiveOperation( aOperation );
233 if (bUndo)
235 rDocShell.GetUndoManager()->AddUndoAction(
236 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
238 aModificator.SetDocumentModified();
239 SfxBindings* pBindings = rDocShell.GetViewBindings();
240 if (pBindings)
241 pBindings->Invalidate( SID_DETECTIVE_REFRESH );
244 return bDone;
247 bool ScDocFunc::DetectiveDelPred(const ScAddress& rPos)
249 ScDocument& rDoc = rDocShell.GetDocument();
251 bool bUndo(rDoc.IsUndoEnabled());
252 ScDrawLayer* pModel = rDoc.GetDrawLayer();
253 if (!pModel)
254 return false;
256 ScDocShellModificator aModificator( rDocShell );
258 SCCOL nCol = rPos.Col();
259 SCROW nRow = rPos.Row();
260 SCTAB nTab = rPos.Tab();
262 if (bUndo)
263 pModel->BeginCalcUndo(false);
264 bool bDone = ScDetectiveFunc(rDoc, nTab).DeletePred( nCol, nRow );
265 std::unique_ptr<SdrUndoGroup> pUndo;
266 if (bUndo)
267 pUndo = pModel->GetCalcUndo();
268 if (bDone)
270 ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELPRED );
271 rDoc.AddDetectiveOperation( aOperation );
272 if (bUndo)
274 rDocShell.GetUndoManager()->AddUndoAction(
275 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
277 aModificator.SetDocumentModified();
278 SfxBindings* pBindings = rDocShell.GetViewBindings();
279 if (pBindings)
280 pBindings->Invalidate( SID_DETECTIVE_REFRESH );
283 return bDone;
286 bool ScDocFunc::DetectiveAddSucc(const ScAddress& rPos)
288 ScDocShellModificator aModificator( rDocShell );
290 rDocShell.MakeDrawLayer();
291 ScDocument& rDoc = rDocShell.GetDocument();
293 bool bUndo(rDoc.IsUndoEnabled());
294 ScDrawLayer* pModel = rDoc.GetDrawLayer();
295 SCCOL nCol = rPos.Col();
296 SCROW nRow = rPos.Row();
297 SCTAB nTab = rPos.Tab();
299 if (bUndo)
300 pModel->BeginCalcUndo(false);
301 bool bDone = ScDetectiveFunc(rDoc, nTab).ShowSucc( nCol, nRow );
302 std::unique_ptr<SdrUndoGroup> pUndo;
303 if (bUndo)
304 pUndo = pModel->GetCalcUndo();
305 if (bDone)
307 ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDSUCC );
308 rDoc.AddDetectiveOperation( aOperation );
309 if (bUndo)
311 rDocShell.GetUndoManager()->AddUndoAction(
312 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
314 aModificator.SetDocumentModified();
315 SfxBindings* pBindings = rDocShell.GetViewBindings();
316 if (pBindings)
317 pBindings->Invalidate( SID_DETECTIVE_REFRESH );
320 return bDone;
323 bool ScDocFunc::DetectiveDelSucc(const ScAddress& rPos)
325 ScDocument& rDoc = rDocShell.GetDocument();
327 bool bUndo (rDoc.IsUndoEnabled());
328 ScDrawLayer* pModel = rDoc.GetDrawLayer();
329 if (!pModel)
330 return false;
332 ScDocShellModificator aModificator( rDocShell );
334 SCCOL nCol = rPos.Col();
335 SCROW nRow = rPos.Row();
336 SCTAB nTab = rPos.Tab();
338 if (bUndo)
339 pModel->BeginCalcUndo(false);
340 bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteSucc( nCol, nRow );
341 std::unique_ptr<SdrUndoGroup> pUndo;
342 if (bUndo)
343 pUndo = pModel->GetCalcUndo();
344 if (bDone)
346 ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELSUCC );
347 rDoc.AddDetectiveOperation( aOperation );
348 if (bUndo)
350 rDocShell.GetUndoManager()->AddUndoAction(
351 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
353 aModificator.SetDocumentModified();
354 SfxBindings* pBindings = rDocShell.GetViewBindings();
355 if (pBindings)
356 pBindings->Invalidate( SID_DETECTIVE_REFRESH );
359 return bDone;
362 bool ScDocFunc::DetectiveAddError(const ScAddress& rPos)
364 ScDocShellModificator aModificator( rDocShell );
366 rDocShell.MakeDrawLayer();
367 ScDocument& rDoc = rDocShell.GetDocument();
369 bool bUndo (rDoc.IsUndoEnabled());
370 ScDrawLayer* pModel = rDoc.GetDrawLayer();
371 SCCOL nCol = rPos.Col();
372 SCROW nRow = rPos.Row();
373 SCTAB nTab = rPos.Tab();
375 if (bUndo)
376 pModel->BeginCalcUndo(false);
377 bool bDone = ScDetectiveFunc(rDoc, nTab).ShowError( nCol, nRow );
378 std::unique_ptr<SdrUndoGroup> pUndo;
379 if (bUndo)
380 pUndo = pModel->GetCalcUndo();
381 if (bDone)
383 ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDERROR );
384 rDoc.AddDetectiveOperation( aOperation );
385 if (bUndo)
387 rDocShell.GetUndoManager()->AddUndoAction(
388 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) );
390 aModificator.SetDocumentModified();
391 SfxBindings* pBindings = rDocShell.GetViewBindings();
392 if (pBindings)
393 pBindings->Invalidate( SID_DETECTIVE_REFRESH );
396 return bDone;
399 bool ScDocFunc::DetectiveMarkInvalid(SCTAB nTab)
401 ScDocShellModificator aModificator( rDocShell );
403 rDocShell.MakeDrawLayer();
404 ScDocument& rDoc = rDocShell.GetDocument();
406 bool bUndo (rDoc.IsUndoEnabled());
407 ScDrawLayer* pModel = rDoc.GetDrawLayer();
409 std::unique_ptr<weld::WaitObject> xWaitWin(new weld::WaitObject(ScDocShell::GetActiveDialogParent()));
410 if (bUndo)
411 pModel->BeginCalcUndo(false);
412 bool bOverflow;
413 bool bDone = ScDetectiveFunc(rDoc, nTab).MarkInvalid( bOverflow );
414 std::unique_ptr<SdrUndoGroup> pUndo;
415 if (bUndo)
416 pUndo = pModel->GetCalcUndo();
417 xWaitWin.reset();
418 if (bDone)
420 if (pUndo && bUndo)
422 pUndo->SetComment( ScResId( STR_UNDO_DETINVALID ) );
423 rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndo) );
425 aModificator.SetDocumentModified();
426 if ( bOverflow )
428 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
429 VclMessageType::Info, VclButtonsType::Ok,
430 ScResId(STR_DETINVALID_OVERFLOW)));
431 xInfoBox->run();
435 return bDone;
438 bool ScDocFunc::DetectiveDelAll(SCTAB nTab)
440 ScDocument& rDoc = rDocShell.GetDocument();
442 bool bUndo (rDoc.IsUndoEnabled());
443 ScDrawLayer* pModel = rDoc.GetDrawLayer();
444 if (!pModel)
445 return false;
447 ScDocShellModificator aModificator( rDocShell );
449 if (bUndo)
450 pModel->BeginCalcUndo(false);
451 bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Detective );
452 std::unique_ptr<SdrUndoGroup> pUndo;
453 if (bUndo)
454 pUndo = pModel->GetCalcUndo();
455 if (bDone)
457 ScDetOpList* pOldList = rDoc.GetDetOpList();
458 std::unique_ptr<ScDetOpList> pUndoList;
459 if (bUndo && pOldList)
460 pUndoList.reset(new ScDetOpList(*pOldList));
462 rDoc.ClearDetectiveOperations();
464 if (bUndo)
466 rDocShell.GetUndoManager()->AddUndoAction(
467 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), nullptr, std::move(pUndoList) ) );
469 aModificator.SetDocumentModified();
470 SfxBindings* pBindings = rDocShell.GetViewBindings();
471 if (pBindings)
472 pBindings->Invalidate( SID_DETECTIVE_REFRESH );
475 return bDone;
478 bool ScDocFunc::DetectiveRefresh( bool bAutomatic )
480 bool bDone = false;
481 ScDocument& rDoc = rDocShell.GetDocument();
483 ScDetOpList* pList = rDoc.GetDetOpList();
484 if ( pList && pList->Count() )
486 rDocShell.MakeDrawLayer();
487 ScDrawLayer* pModel = rDoc.GetDrawLayer();
488 const bool bUndo (rDoc.IsUndoEnabled());
489 if (bUndo)
490 pModel->BeginCalcUndo(false);
492 // Delete in all sheets
494 SCTAB nTabCount = rDoc.GetTableCount();
495 for (SCTAB nTab=0; nTab<nTabCount; nTab++)
496 ScDetectiveFunc( rDoc,nTab ).DeleteAll( ScDetectiveDelete::Arrows ); // don't remove circles
498 // repeat
500 size_t nCount = pList->Count();
501 for (size_t i=0; i < nCount; ++i)
503 const ScDetOpData& rData = pList->GetObject(i);
504 const ScAddress& aPos = rData.GetPos();
505 ScDetectiveFunc aFunc( rDoc, aPos.Tab() );
506 SCCOL nCol = aPos.Col();
507 SCROW nRow = aPos.Row();
508 switch (rData.GetOperation())
510 case SCDETOP_ADDSUCC:
511 aFunc.ShowSucc( nCol, nRow );
512 break;
513 case SCDETOP_DELSUCC:
514 aFunc.DeleteSucc( nCol, nRow );
515 break;
516 case SCDETOP_ADDPRED:
517 aFunc.ShowPred( nCol, nRow );
518 break;
519 case SCDETOP_DELPRED:
520 aFunc.DeletePred( nCol, nRow );
521 break;
522 case SCDETOP_ADDERROR:
523 aFunc.ShowError( nCol, nRow );
524 break;
525 default:
526 OSL_FAIL("wrong operation in DetectiveRefresh");
530 if (bUndo)
532 std::unique_ptr<SdrUndoGroup> pUndo = pModel->GetCalcUndo();
533 if (pUndo)
535 pUndo->SetComment( ScResId( STR_UNDO_DETREFRESH ) );
536 // associate with the last action
537 rDocShell.GetUndoManager()->AddUndoAction(
538 std::make_unique<ScUndoDraw>( std::move(pUndo), &rDocShell ),
539 bAutomatic );
542 rDocShell.SetDrawModified();
543 bDone = true;
545 return bDone;
548 static void lcl_collectAllPredOrSuccRanges(
549 const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens, ScDocShell& rDocShell,
550 bool bPred)
552 ScDocument& rDoc = rDocShell.GetDocument();
553 vector<ScTokenRef> aRefTokens;
554 if (rSrcRanges.empty())
555 return;
556 ScRange const & rFrontRange = rSrcRanges.front();
557 ScDetectiveFunc aDetFunc(rDoc, rFrontRange.aStart.Tab());
558 for (ScRange const & r : rSrcRanges)
560 if (bPred)
562 aDetFunc.GetAllPreds(
563 r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens);
565 else
567 aDetFunc.GetAllSuccs(
568 r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens);
571 rRefTokens.swap(aRefTokens);
574 void ScDocFunc::DetectiveCollectAllPreds(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens)
576 lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, true);
579 void ScDocFunc::DetectiveCollectAllSuccs(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens)
581 lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, false);
584 bool ScDocFunc::DeleteContents(
585 const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi )
587 ScDocShellModificator aModificator( rDocShell );
589 if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
591 OSL_FAIL("ScDocFunc::DeleteContents without markings");
592 return false;
595 ScDocument& rDoc = rDocShell.GetDocument();
597 if (bRecord && !rDoc.IsUndoEnabled())
598 bRecord = false;
600 ScEditableTester aTester( rDoc, rMark );
601 if (!aTester.IsEditable())
603 if (!bApi)
604 rDocShell.ErrorMessage(aTester.GetMessageId());
605 return false;
608 ScMarkData aMultiMark = rMark;
609 aMultiMark.SetMarking(false); // for MarkToMulti
611 ScDocumentUniquePtr pUndoDoc;
612 bool bMulti = aMultiMark.IsMultiMarked();
613 aMultiMark.MarkToMulti();
614 const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();
615 ScRange aExtendedRange(aMarkRange);
616 if ( rDoc.ExtendMerge( aExtendedRange, true ) )
617 bMulti = false;
619 // no objects on protected tabs
620 bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark);
622 sal_uInt16 nExtFlags = 0; // extra flags are needed only if attributes are deleted
623 if ( nFlags & InsertDeleteFlags::ATTRIB )
624 rDocShell.UpdatePaintExt( nExtFlags, aMarkRange );
626 // order of operations:
627 // 1) BeginDrawUndo
628 // 2) Delete objects (DrawUndo will be filled)
629 // 3) Copy content for undo and set up undo actions
630 // 4) Delete content
632 bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE);
633 if (bRecord && bDrawUndo)
634 rDoc.BeginDrawUndo();
636 if (bObjects)
638 if (bMulti)
639 rDoc.DeleteObjectsInSelection( aMultiMark );
640 else
641 rDoc.DeleteObjectsInArea( aMarkRange.aStart.Col(), aMarkRange.aStart.Row(),
642 aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(),
643 aMultiMark );
646 // To keep track of all non-empty cells within the deleted area.
647 std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans;
649 if ( bRecord )
651 pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, aMultiMark, aMarkRange, nFlags, bMulti);
652 pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, aMultiMark, aMarkRange);
655 rDoc.DeleteSelection( nFlags, aMultiMark );
657 // add undo action after drawing undo is complete (objects and note captions)
658 if( bRecord )
660 sc::DocFuncUtil::addDeleteContentsUndo(
661 rDocShell.GetUndoManager(), &rDocShell, aMultiMark, aExtendedRange,
662 std::move(pUndoDoc), nFlags, pDataSpans, bMulti, bDrawUndo);
665 if (!AdjustRowHeight( aExtendedRange, true, bApi ))
666 rDocShell.PostPaint( aExtendedRange, PaintPartFlags::Grid, nExtFlags );
667 else if (nExtFlags & SC_PF_LINES)
668 lcl_PaintAbove( rDocShell, aExtendedRange ); // for lines above the range
670 aModificator.SetDocumentModified();
672 return true;
675 tools::Long ScDocShell::GetTwipWidthHint(const ScAddress& rPos)
677 ScViewData* pViewData = GetViewData();
678 if (!pViewData)
679 return -1;
681 ScSizeDeviceProvider aProv(this);
682 Fraction aZoomX, aZoomY;
683 double nPPTX, nPPTY;
684 pViewData->setupSizeDeviceProviderForColWidth(aProv, aZoomX, aZoomY, nPPTX, nPPTY);
686 ScDocument& rDoc = GetDocument();
687 tools::Long nWidth = rDoc.GetNeededSize(rPos.Col(), rPos.Row(), rPos.Tab(), aProv.GetDevice(),
688 nPPTX, nPPTY, aZoomX, aZoomY, true /*bWidth*/);
690 return (nWidth + 2) / nPPTX; // same as ScColumn::GetOptimalColWidth
693 bool ScDocFunc::DeleteCell(
694 const ScAddress& rPos, const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi )
696 ScDocShellModificator aModificator(rDocShell);
698 ScDocument& rDoc = rDocShell.GetDocument();
700 if (bRecord && !rDoc.IsUndoEnabled())
701 bRecord = false;
703 ScEditableTester aTester(rDoc, rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark);
704 if (!aTester.IsEditable())
706 rDocShell.ErrorMessage(aTester.GetMessageId());
707 return false;
710 // no objects on protected tabs
711 bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark);
713 sal_uInt16 nExtFlags = 0; // extra flags are needed only if attributes are deleted
714 if (nFlags & InsertDeleteFlags::ATTRIB)
715 rDocShell.UpdatePaintExt(nExtFlags, ScRange(rPos));
717 // order of operations:
718 // 1) BeginDrawUndo
719 // 2) delete objects (DrawUndo is filled)
720 // 3) copy contents for undo
721 // 4) delete contents
722 // 5) add undo-action
724 bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE); // needed for shown notes
725 if (bDrawUndo && bRecord)
726 rDoc.BeginDrawUndo();
728 if (bObjects)
729 rDoc.DeleteObjectsInArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark);
731 // To keep track of all non-empty cells within the deleted area.
732 std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans;
734 ScDocumentUniquePtr pUndoDoc;
735 if (bRecord)
737 pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, rMark, ScRange(rPos), nFlags, false);
738 pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, rMark, ScRange(rPos));
741 tools::Long nBefore(rDocShell.GetTwipWidthHint(rPos));
742 rDoc.DeleteArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark, nFlags);
744 if (bRecord)
746 sc::DocFuncUtil::addDeleteContentsUndo(
747 rDocShell.GetUndoManager(), &rDocShell, rMark, ScRange(rPos), std::move(pUndoDoc),
748 nFlags, pDataSpans, false, bDrawUndo);
751 if (!AdjustRowHeight(ScRange(rPos), true, bApi))
752 rDocShell.PostPaint(
753 rPos.Col(), rPos.Row(), rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Tab(),
754 PaintPartFlags::Grid, nExtFlags, nBefore);
756 aModificator.SetDocumentModified();
758 return true;
761 bool ScDocFunc::TransliterateText( const ScMarkData& rMark, TransliterationFlags nType,
762 bool bApi )
764 ScDocShellModificator aModificator( rDocShell );
766 ScDocument& rDoc = rDocShell.GetDocument();
767 bool bRecord = true;
768 if (!rDoc.IsUndoEnabled())
769 bRecord = false;
771 ScEditableTester aTester( rDoc, rMark );
772 if (!aTester.IsEditable())
774 if (!bApi)
775 rDocShell.ErrorMessage(aTester.GetMessageId());
776 return false;
779 ScMarkData aMultiMark = rMark;
780 aMultiMark.SetMarking(false); // for MarkToMulti
781 aMultiMark.MarkToMulti();
782 const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();
784 if (bRecord)
786 SCTAB nStartTab = aMarkRange.aStart.Tab();
787 SCTAB nTabCount = rDoc.GetTableCount();
789 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
790 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
791 for (const auto& rTab : rMark)
793 if (rTab >= nTabCount)
794 break;
796 if (rTab != nStartTab)
797 pUndoDoc->AddUndoTab( rTab, rTab );
800 ScRange aCopyRange = aMarkRange;
801 aCopyRange.aStart.SetTab(0);
802 aCopyRange.aEnd.SetTab(nTabCount-1);
803 rDoc.CopyToDocument(aCopyRange, InsertDeleteFlags::CONTENTS, true, *pUndoDoc, &aMultiMark);
805 rDocShell.GetUndoManager()->AddUndoAction(
806 std::make_unique<ScUndoTransliterate>( &rDocShell, aMultiMark, std::move(pUndoDoc), nType ) );
809 rDoc.TransliterateText( aMultiMark, nType );
811 if (!AdjustRowHeight( aMarkRange, true, true ))
812 rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid );
814 aModificator.SetDocumentModified();
816 return true;
819 bool ScDocFunc::SetNormalString( bool& o_rbNumFmtSet, const ScAddress& rPos, const OUString& rText, bool bApi )
821 ScDocShellModificator aModificator( rDocShell );
822 ScDocument& rDoc = rDocShell.GetDocument();
824 bool bUndo(rDoc.IsUndoEnabled());
825 ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
826 if (!aTester.IsEditable())
828 if (!bApi)
829 rDocShell.ErrorMessage(aTester.GetMessageId());
830 return false;
833 bool bEditDeleted = (rDoc.GetCellType(rPos) == CELLTYPE_EDIT);
834 ScUndoEnterData::ValuesType aOldValues;
836 if (bUndo)
838 ScUndoEnterData::Value aOldValue;
840 aOldValue.mnTab = rPos.Tab();
841 aOldValue.maCell.assign(rDoc, rPos);
843 const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(),rPos.Row(),rPos.Tab() );
844 if ( const SfxUInt32Item* pItem = pPattern->GetItemSet().GetItemIfSet(
845 ATTR_VALUE_FORMAT,false) )
847 aOldValue.mbHasFormat = true;
848 aOldValue.mnFormat = pItem->GetValue();
850 else
851 aOldValue.mbHasFormat = false;
853 aOldValues.push_back(aOldValue);
856 tools::Long nBefore(rDocShell.GetTwipWidthHint(rPos));
857 o_rbNumFmtSet = rDoc.SetString( rPos.Col(), rPos.Row(), rPos.Tab(), rText );
858 tools::Long nAfter(rDocShell.GetTwipWidthHint(rPos));
860 if (bUndo)
862 // because of ChangeTracking, UndoAction can be created only after SetString was called
863 rDocShell.GetUndoManager()->AddUndoAction(
864 std::make_unique<ScUndoEnterData>(&rDocShell, rPos, aOldValues, rText, nullptr));
867 if ( bEditDeleted || rDoc.HasAttrib( ScRange(rPos), HasAttrFlags::NeedHeight ) )
868 AdjustRowHeight( ScRange(rPos), true, bApi );
870 rDocShell.PostPaintCell( rPos, std::max(nBefore, nAfter) );
871 aModificator.SetDocumentModified();
873 // notify input handler here the same way as in PutCell
874 if (bApi)
875 NotifyInputHandler( rPos );
877 const SfxUInt32Item* pItem = rDoc.GetAttr(rPos, ATTR_VALIDDATA);
878 const ScValidationData* pData = rDoc.GetValidationEntry(pItem->GetValue());
879 if (pData)
881 ScRefCellValue aCell(rDoc, rPos);
882 if (pData->IsDataValid(aCell, rPos))
883 ScDetectiveFunc(rDoc, rPos.Tab()).DeleteCirclesAt(rPos.Col(), rPos.Row());
886 return true;
889 bool ScDocFunc::SetValueCell( const ScAddress& rPos, double fVal, bool bInteraction )
891 ScDocShellModificator aModificator( rDocShell );
892 ScDocument& rDoc = rDocShell.GetDocument();
893 bool bUndo = rDoc.IsUndoEnabled();
895 bool bHeight = rDoc.HasAttrib(ScRange(rPos), HasAttrFlags::NeedHeight);
897 ScCellValue aOldVal;
898 if (bUndo)
899 aOldVal.assign(rDoc, rPos);
901 rDoc.SetValue(rPos, fVal);
903 if (bUndo)
905 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
906 ScCellValue aNewVal;
907 aNewVal.assign(rDoc, rPos);
908 pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
911 if (bHeight)
912 AdjustRowHeight(ScRange(rPos), true, !bInteraction);
914 rDocShell.PostPaintCell( rPos );
915 aModificator.SetDocumentModified();
917 // #103934#; notify editline and cell in edit mode
918 if (!bInteraction)
919 NotifyInputHandler( rPos );
921 return true;
924 void ScDocFunc::SetValueCells( const ScAddress& rPos, const std::vector<double>& aVals, bool bInteraction )
926 ScDocument& rDoc = rDocShell.GetDocument();
928 // Check for invalid range.
929 SCROW nLastRow = rPos.Row() + aVals.size() - 1;
930 if (nLastRow > rDoc.MaxRow())
931 // out of bound.
932 return;
934 ScRange aRange(rPos);
935 aRange.aEnd.SetRow(nLastRow);
937 ScDocShellModificator aModificator(rDocShell);
939 if (rDoc.IsUndoEnabled())
941 std::unique_ptr<sc::UndoSetCells> pUndoObj(new sc::UndoSetCells(&rDocShell, rPos));
942 rDoc.TransferCellValuesTo(rPos, aVals.size(), pUndoObj->GetOldValues());
943 pUndoObj->SetNewValues(aVals);
944 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
945 pUndoMgr->AddUndoAction(std::move(pUndoObj));
948 rDoc.SetValues(rPos, aVals);
950 rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
951 aModificator.SetDocumentModified();
953 // #103934#; notify editline and cell in edit mode
954 if (!bInteraction)
955 NotifyInputHandler(rPos);
958 bool ScDocFunc::SetStringCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction )
960 ScDocShellModificator aModificator( rDocShell );
961 ScDocument& rDoc = rDocShell.GetDocument();
962 bool bUndo = rDoc.IsUndoEnabled();
964 bool bHeight = rDoc.HasAttrib(ScRange(rPos), HasAttrFlags::NeedHeight);
966 ScCellValue aOldVal;
967 if (bUndo)
968 aOldVal.assign(rDoc, rPos);
970 ScSetStringParam aParam;
971 aParam.setTextInput();
972 rDoc.SetString(rPos, rStr, &aParam);
974 if (bUndo)
976 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
977 ScCellValue aNewVal;
978 aNewVal.assign(rDoc, rPos);
979 pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
982 if (bHeight)
983 AdjustRowHeight(ScRange(rPos), true, !bInteraction);
985 rDocShell.PostPaintCell( rPos );
986 aModificator.SetDocumentModified();
988 // #103934#; notify editline and cell in edit mode
989 if (!bInteraction)
990 NotifyInputHandler( rPos );
992 return true;
995 bool ScDocFunc::SetEditCell( const ScAddress& rPos, const EditTextObject& rStr, bool bInteraction )
997 ScDocShellModificator aModificator( rDocShell );
998 ScDocument& rDoc = rDocShell.GetDocument();
999 bool bUndo = rDoc.IsUndoEnabled();
1001 bool bHeight = rDoc.HasAttrib(ScRange(rPos), HasAttrFlags::NeedHeight);
1003 ScCellValue aOldVal;
1004 if (bUndo)
1005 aOldVal.assign(rDoc, rPos);
1007 rDoc.SetEditText(rPos, rStr.Clone());
1009 if (bUndo)
1011 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
1012 ScCellValue aNewVal;
1013 aNewVal.assign(rDoc, rPos);
1014 pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
1017 if (bHeight)
1018 AdjustRowHeight(ScRange(rPos), true, !bInteraction);
1020 rDocShell.PostPaintCell( rPos );
1021 aModificator.SetDocumentModified();
1023 // #103934#; notify editline and cell in edit mode
1024 if (!bInteraction)
1025 NotifyInputHandler( rPos );
1027 return true;
1030 bool ScDocFunc::SetStringOrEditCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction )
1032 ScDocument& rDoc = rDocShell.GetDocument();
1034 if (ScStringUtil::isMultiline(rStr))
1036 ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
1037 rEngine.SetTextCurrentDefaults(rStr);
1038 std::unique_ptr<EditTextObject> pEditText(rEngine.CreateTextObject());
1039 return SetEditCell(rPos, *pEditText, bInteraction);
1041 else
1042 return SetStringCell(rPos, rStr, bInteraction);
1045 bool ScDocFunc::SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell, bool bInteraction )
1047 std::unique_ptr<ScFormulaCell> xCell(pCell);
1049 ScDocShellModificator aModificator( rDocShell );
1050 ScDocument& rDoc = rDocShell.GetDocument();
1051 bool bUndo = rDoc.IsUndoEnabled();
1053 bool bHeight = rDoc.HasAttrib(ScRange(rPos), HasAttrFlags::NeedHeight);
1055 ScCellValue aOldVal;
1056 if (bUndo)
1057 aOldVal.assign(rDoc, rPos);
1059 pCell = rDoc.SetFormulaCell(rPos, xCell.release());
1061 // For performance reasons API calls may disable calculation while
1062 // operating and recalculate once when done. If through user interaction
1063 // and AutoCalc is disabled, calculate the formula (without its
1064 // dependencies) once so the result matches the current document's content.
1065 if (bInteraction && !rDoc.GetAutoCalc() && pCell)
1067 // calculate just the cell once and set Dirty again
1068 pCell->Interpret();
1069 pCell->SetDirtyVar();
1070 rDoc.PutInFormulaTree( pCell);
1073 if (bUndo)
1075 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
1076 ScCellValue aNewVal;
1077 aNewVal.assign(rDoc, rPos);
1078 pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal));
1081 if (bHeight)
1082 AdjustRowHeight(ScRange(rPos), true, !bInteraction);
1084 rDocShell.PostPaintCell( rPos );
1085 aModificator.SetDocumentModified();
1087 // #103934#; notify editline and cell in edit mode
1088 if (!bInteraction)
1089 NotifyInputHandler( rPos );
1091 return true;
1094 bool ScDocFunc::SetFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells, bool bInteraction )
1096 ScDocument& rDoc = rDocShell.GetDocument();
1098 const size_t nLength = rCells.size();
1099 if (rPos.Row() + nLength - 1 > o3tl::make_unsigned(rDoc.MaxRow()))
1100 // out of bound
1101 return false;
1103 ScRange aRange(rPos);
1104 aRange.aEnd.IncRow(nLength - 1);
1106 ScDocShellModificator aModificator( rDocShell );
1107 bool bUndo = rDoc.IsUndoEnabled();
1109 std::unique_ptr<sc::UndoSetCells> pUndoObj;
1110 if (bUndo)
1112 pUndoObj.reset(new sc::UndoSetCells(&rDocShell, rPos));
1113 rDoc.TransferCellValuesTo(rPos, nLength, pUndoObj->GetOldValues());
1116 rDoc.SetFormulaCells(rPos, rCells);
1118 // For performance reasons API calls may disable calculation while
1119 // operating and recalculate once when done. If through user interaction
1120 // and AutoCalc is disabled, calculate the formula (without its
1121 // dependencies) once so the result matches the current document's content.
1122 if (bInteraction && !rDoc.GetAutoCalc())
1124 for (auto* pCell : rCells)
1126 // calculate just the cell once and set Dirty again
1127 pCell->Interpret();
1128 pCell->SetDirtyVar();
1129 rDoc.PutInFormulaTree( pCell);
1133 if (bUndo)
1135 pUndoObj->SetNewValues(rCells);
1136 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager();
1137 pUndoMgr->AddUndoAction(std::move(pUndoObj));
1140 rDocShell.PostPaint(aRange, PaintPartFlags::Grid);
1141 aModificator.SetDocumentModified();
1143 // #103934#; notify editline and cell in edit mode
1144 if (!bInteraction)
1145 NotifyInputHandler( rPos );
1147 return true;
1150 void ScDocFunc::NotifyInputHandler( const ScAddress& rPos )
1152 ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell();
1153 if ( !(pViewSh && pViewSh->GetViewData().GetDocShell() == &rDocShell) )
1154 return;
1156 ScInputHandler* pInputHdl = ScModule::get()->GetInputHdl();
1157 if ( pInputHdl && pInputHdl->GetCursorPos() == rPos )
1159 bool bIsEditMode(pInputHdl->IsEditMode());
1161 // set modified if in editmode, because so the string is not set in the InputWindow like in the cell
1162 // (the cell shows the same like the InputWindow)
1163 if (bIsEditMode)
1164 pInputHdl->SetModified();
1165 pViewSh->UpdateInputHandler(false, !bIsEditMode);
1169 namespace {
1171 struct ScMyRememberItem
1173 sal_Int32 nIndex;
1174 SfxItemSet aItemSet;
1176 ScMyRememberItem(SfxItemSet _aItemSet, sal_Int32 nTempIndex) :
1177 nIndex(nTempIndex), aItemSet(std::move(_aItemSet)) {}
1182 void ScDocFunc::PutData( const ScAddress& rPos, ScEditEngineDefaulter& rEngine, bool bApi )
1184 // PutData calls PutCell or SetNormalString
1186 bool bRet = false;
1187 ScDocument& rDoc = rDocShell.GetDocument();
1188 ScEditAttrTester aTester( &rEngine );
1189 bool bEditCell = aTester.NeedsObject();
1190 if ( bEditCell )
1192 // #i61702# With bLoseContent set, the content of rEngine isn't restored
1193 // (used in loading XML, where after the removeActionLock call the API object's
1194 // EditEngine isn't accessed again.
1195 bool bLoseContent = rDoc.IsImportingXML();
1197 const bool bUpdateMode = rEngine.SetUpdateLayout(false);
1199 std::vector<std::unique_ptr<ScMyRememberItem>> aRememberItems;
1201 // All paragraph attributes must be removed before calling CreateTextObject,
1202 // not only alignment, so the object doesn't contain the cell attributes as
1203 // paragraph attributes. Before removing the attributes store them in a vector to
1204 // set them back to the EditEngine.
1205 sal_Int32 nCount = rEngine.GetParagraphCount();
1206 for (sal_Int32 i=0; i<nCount; i++)
1208 const SfxItemSet& rOld = rEngine.GetParaAttribs( i );
1209 if ( rOld.Count() )
1211 if ( !bLoseContent )
1213 aRememberItems.push_back(std::make_unique<ScMyRememberItem>(rEngine.GetParaAttribs(i), i));
1215 rEngine.SetParaAttribs( i, SfxItemSet( *rOld.GetPool(), rOld.GetRanges() ) );
1219 // A copy of pNewData will be stored in the cell.
1220 std::unique_ptr<EditTextObject> pNewData(rEngine.CreateTextObject());
1221 bRet = SetEditCell(rPos, *pNewData, !bApi);
1223 // Set the paragraph attributes back to the EditEngine.
1224 for (const auto& rxItem : aRememberItems)
1226 rEngine.SetParaAttribs(rxItem->nIndex, rxItem->aItemSet);
1229 // #i61702# if the content isn't accessed, there's no need to set the UpdateMode again
1230 if ( bUpdateMode && !bLoseContent )
1231 rEngine.SetUpdateLayout(true);
1233 else
1235 OUString aText = rEngine.GetText();
1236 if (aText.isEmpty())
1238 bool bNumFmtSet = false;
1239 bRet = SetNormalString( bNumFmtSet, rPos, aText, bApi );
1241 else
1242 bRet = SetStringCell(rPos, aText, !bApi);
1245 if ( !(bRet && aTester.NeedsCellAttr()) )
1246 return;
1248 const SfxItemSet& rEditAttr = aTester.GetAttribs();
1249 ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
1250 aPattern.GetFromEditItemSet( &rEditAttr );
1251 aPattern.DeleteUnchanged( rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ) );
1252 aPattern.GetItemSet().ClearItem( ATTR_HOR_JUSTIFY ); // wasn't removed above if no edit object
1253 if ( aPattern.GetItemSet().Count() > 0 )
1255 ScMarkData aMark(rDoc.GetSheetLimits());
1256 aMark.SelectTable( rPos.Tab(), true );
1257 aMark.SetMarkArea( ScRange( rPos ) );
1258 ApplyAttributes( aMark, aPattern, bApi );
1262 bool ScDocFunc::SetCellText(
1263 const ScAddress& rPos, const OUString& rText, bool bInterpret, bool bEnglish, bool bApi,
1264 const formula::FormulaGrammar::Grammar eGrammar )
1266 bool bSet = false;
1267 if ( bInterpret )
1269 if ( bEnglish )
1271 ScDocument& rDoc = rDocShell.GetDocument();
1273 ::std::optional<ScExternalRefManager::ApiGuard> pExtRefGuard;
1274 if (bApi)
1275 pExtRefGuard.emplace(rDoc);
1277 ScInputStringType aRes =
1278 ScStringUtil::parseInputString(rDoc.GetNonThreadedContext(), rText, LANGUAGE_ENGLISH_US);
1280 switch (aRes.meType)
1282 case ScInputStringType::Formula:
1283 bSet = SetFormulaCell(rPos, new ScFormulaCell(rDoc, rPos, aRes.maText, eGrammar), !bApi);
1284 break;
1285 case ScInputStringType::Number:
1286 bSet = SetValueCell(rPos, aRes.mfValue, !bApi);
1287 break;
1288 case ScInputStringType::Text:
1289 bSet = SetStringOrEditCell(rPos, aRes.maText, !bApi);
1290 break;
1291 default:
1295 // otherwise keep Null -> SetString with local formulas/number formats
1297 else if (!rText.isEmpty())
1299 bSet = SetStringOrEditCell(rPos, rText, !bApi);
1302 if (!bSet)
1304 bool bNumFmtSet = false;
1305 bSet = SetNormalString( bNumFmtSet, rPos, rText, bApi );
1307 return bSet;
1310 bool ScDocFunc::ShowNote( const ScAddress& rPos, bool bShow )
1312 ScDocument& rDoc = rDocShell.GetDocument();
1313 ScPostIt* pNote = rDoc.GetNote( rPos );
1314 if( !pNote || (bShow == pNote->IsCaptionShown()) ||
1315 (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) )
1316 return false;
1318 // move the caption to internal or hidden layer and create undo action
1319 pNote->ShowCaption( rPos, bShow );
1320 if( rDoc.IsUndoEnabled() )
1321 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideNote>( rDocShell, rPos, bShow ) );
1323 rDoc.SetStreamValid(rPos.Tab(), false);
1325 ScTabView::OnLOKNoteStateChanged(pNote);
1327 if (ScViewData* pViewData = ScDocShell::GetViewData())
1329 if (ScDrawView* pDrawView = pViewData->GetScDrawView())
1330 pDrawView->SyncForGrid( pNote->GetCaption());
1333 rDocShell.SetDocumentModified();
1335 return true;
1338 void ScDocFunc::SetNoteText( const ScAddress& rPos, const OUString& rText, bool bApi )
1340 ScDocShellModificator aModificator( rDocShell );
1342 ScDocument& rDoc = rDocShell.GetDocument();
1343 ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
1344 if (!aTester.IsEditable())
1346 if (!bApi)
1347 rDocShell.ErrorMessage(aTester.GetMessageId());
1348 return;
1351 OUString aNewText = convertLineEnd(rText, GetSystemLineEnd()); //! is this necessary ???
1353 if( ScPostIt* pNote = (!aNewText.isEmpty()) ? rDoc.GetOrCreateNote( rPos ) : rDoc.GetNote(rPos) )
1354 pNote->SetText( rPos, aNewText );
1356 //! Undo !!!
1358 rDoc.SetStreamValid(rPos.Tab(), false);
1360 rDocShell.PostPaintCell( rPos );
1361 aModificator.SetDocumentModified();
1364 void ScDocFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate, bool bApi )
1366 ScDocShellModificator aModificator( rDocShell );
1367 ScDocument& rDoc = rDocShell.GetDocument();
1368 ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() );
1369 if (aTester.IsEditable())
1371 ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
1372 SfxUndoManager* pUndoMgr = (pDrawLayer && rDoc.IsUndoEnabled()) ? rDocShell.GetUndoManager() : nullptr;
1374 ScNoteData aOldData;
1375 std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos );
1376 sal_uInt32 nNoteId = 0;
1377 if( pOldNote )
1379 nNoteId = pOldNote->GetId();
1380 // ensure existing caption object before draw undo tracking starts
1381 pOldNote->GetOrCreateCaption( rPos );
1382 // rescue note data for undo
1383 aOldData = pOldNote->GetNoteData();
1386 // collect drawing undo actions for deleting/inserting caption objects
1387 if( pUndoMgr )
1388 pDrawLayer->BeginCalcUndo(false);
1390 // delete the note (creates drawing undo action for the caption object)
1391 bool hadOldNote(pOldNote);
1392 pOldNote.reset();
1394 // create new note (creates drawing undo action for the new caption object)
1395 ScNoteData aNewData;
1396 ScPostIt* pNewNote = nullptr;
1397 if( (pNewNote = ScNoteUtil::CreateNoteFromString( rDoc, rPos, rNoteText, false, true, nNoteId )) )
1399 if( pAuthor ) pNewNote->SetAuthor( *pAuthor );
1400 if( pDate ) pNewNote->SetDate( *pDate );
1402 // rescue note data for undo
1403 aNewData = pNewNote->GetNoteData();
1406 // create the undo action
1407 if( pUndoMgr && (aOldData.mxCaption || aNewData.mxCaption) )
1408 pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( rDocShell, rPos, aOldData, aNewData, pDrawLayer->GetCalcUndo() ) );
1410 // repaint cell (to make note marker visible)
1411 rDocShell.PostPaintCell( rPos );
1413 rDoc.SetStreamValid(rPos.Tab(), false);
1415 aModificator.SetDocumentModified();
1417 // Let our LOK clients know about the new/modified note
1418 if (pNewNote)
1420 ScDocShell::LOKCommentNotify(hadOldNote ? LOKCommentNotificationType::Modify : LOKCommentNotificationType::Add,
1421 rDoc, rPos, pNewNote);
1424 else if (!bApi)
1426 rDocShell.ErrorMessage(aTester.GetMessageId());
1430 void ScDocFunc::ImportNote( const ScAddress& rPos,
1431 std::unique_ptr<GenerateNoteCaption> xGenerator,
1432 const tools::Rectangle& rCaptionRect, bool bShown )
1434 ScDocShellModificator aModificator( rDocShell );
1435 ScDocument& rDoc = rDocShell.GetDocument();
1437 std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos );
1438 SAL_WARN_IF(pOldNote, "sc.ui", "imported data has >1 notes on same cell? at pos " << rPos);
1440 // create new note
1441 ScNoteUtil::CreateNoteFromGenerator(rDoc, rPos, std::move(xGenerator),
1442 rCaptionRect, bShown);
1444 rDoc.SetStreamValid(rPos.Tab(), false);
1446 aModificator.SetDocumentModified();
1449 bool ScDocFunc::ApplyAttributes( const ScMarkData& rMark, const ScPatternAttr& rPattern,
1450 bool bApi )
1452 ScDocument& rDoc = rDocShell.GetDocument();
1453 bool bRecord = true;
1454 if ( !rDoc.IsUndoEnabled() )
1455 bRecord = false;
1457 bool bImportingXML = rDoc.IsImportingXML();
1458 // Cell formats can still be set if the range isn't editable only because of matrix formulas.
1459 // #i62483# When loading XML, the check can be skipped altogether.
1460 bool bOnlyNotBecauseOfMatrix;
1461 if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix )
1462 && !bOnlyNotBecauseOfMatrix )
1464 if (!bApi)
1465 rDocShell.ErrorMessage(STR_PROTECTIONERR);
1466 return false;
1469 ScDocShellModificator aModificator( rDocShell );
1471 //! Border
1473 ScRange aMultiRange;
1474 bool bMulti = rMark.IsMultiMarked();
1475 if ( bMulti )
1476 aMultiRange = rMark.GetMultiMarkArea();
1477 else
1478 aMultiRange = rMark.GetMarkArea();
1480 if ( bRecord )
1482 ScDocumentUniquePtr pUndoDoc( new ScDocument( SCDOCMODE_UNDO ));
1483 pUndoDoc->InitUndo( rDoc, aMultiRange.aStart.Tab(), aMultiRange.aEnd.Tab() );
1484 rDoc.CopyToDocument(aMultiRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark);
1486 rDocShell.GetUndoManager()->AddUndoAction(
1487 std::make_unique<ScUndoSelectionAttr>(
1488 &rDocShell, rMark,
1489 aMultiRange.aStart.Col(), aMultiRange.aStart.Row(), aMultiRange.aStart.Tab(),
1490 aMultiRange.aEnd.Col(), aMultiRange.aEnd.Row(), aMultiRange.aEnd.Tab(),
1491 std::move(pUndoDoc), bMulti, &rPattern ) );
1494 // While loading XML it is not necessary to ask HasAttrib. It needs too much time.
1495 sal_uInt16 nExtFlags = 0;
1496 if ( !bImportingXML )
1497 rDocShell.UpdatePaintExt( nExtFlags, aMultiRange ); // content before the change
1499 bool bChanged = false;
1500 rDoc.ApplySelectionPattern( rPattern, rMark, nullptr, &bChanged );
1502 if(bChanged)
1504 if ( !bImportingXML )
1505 rDocShell.UpdatePaintExt( nExtFlags, aMultiRange ); // content after the change
1507 if (!AdjustRowHeight( aMultiRange, true, bApi ))
1508 rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid, nExtFlags );
1509 else if (nExtFlags & SC_PF_LINES)
1510 lcl_PaintAbove( rDocShell, aMultiRange ); // because of lines above the range
1512 aModificator.SetDocumentModified();
1515 return true;
1518 bool ScDocFunc::ApplyStyle( const ScMarkData& rMark, const OUString& rStyleName,
1519 bool bApi )
1521 ScDocument& rDoc = rDocShell.GetDocument();
1522 bool bRecord = true;
1523 if ( !rDoc.IsUndoEnabled() )
1524 bRecord = false;
1526 bool bImportingXML = rDoc.IsImportingXML();
1527 // Cell formats can still be set if the range isn't editable only because of matrix formulas.
1528 // #i62483# When loading XML, the check can be skipped altogether.
1529 bool bOnlyNotBecauseOfMatrix;
1530 if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix )
1531 && !bOnlyNotBecauseOfMatrix )
1533 if (!bApi)
1534 rDocShell.ErrorMessage(STR_PROTECTIONERR);
1535 return false;
1538 ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>( rDoc.GetStyleSheetPool()->Find(
1539 rStyleName, SfxStyleFamily::Para ));
1540 if (!pStyleSheet)
1541 return false;
1543 ScDocShellModificator aModificator( rDocShell );
1545 ScRange aMultiRange;
1546 bool bMulti = rMark.IsMultiMarked();
1547 if ( bMulti )
1548 aMultiRange = rMark.GetMultiMarkArea();
1549 else
1550 aMultiRange = rMark.GetMarkArea();
1552 if ( bRecord )
1554 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1555 SCTAB nStartTab = aMultiRange.aStart.Tab();
1556 SCTAB nTabCount = rDoc.GetTableCount();
1557 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
1558 for (const auto& rTab : rMark)
1560 if (rTab >= nTabCount)
1561 break;
1563 if (rTab != nStartTab)
1564 pUndoDoc->AddUndoTab( rTab, rTab );
1567 ScRange aCopyRange = aMultiRange;
1568 aCopyRange.aStart.SetTab(0);
1569 aCopyRange.aEnd.SetTab(nTabCount-1);
1570 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark );
1572 rDocShell.GetUndoManager()->AddUndoAction(
1573 std::make_unique<ScUndoSelectionStyle>(
1574 &rDocShell, rMark, aMultiRange, rStyleName, std::move(pUndoDoc) ) );
1578 rDoc.ApplySelectionStyle( *pStyleSheet, rMark );
1580 if (!AdjustRowHeight( aMultiRange, true, bApi ))
1581 rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid );
1583 aModificator.SetDocumentModified();
1585 return true;
1588 namespace {
1591 * Check if this insertion attempt would end up cutting one or more pivot
1592 * tables in half, which is not desirable.
1594 * @return true if this insertion can be done safely without shearing any
1595 * existing pivot tables, false otherwise.
1597 bool canInsertCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, InsCellCmd eCmd, const ScDocument& rDoc)
1599 if (!rDoc.HasPivotTable())
1600 // This document has no pivot tables.
1601 return true;
1603 const ScDPCollection* pDPs = rDoc.GetDPCollection();
1605 ScRange aRange(rRange); // local copy
1606 switch (eCmd)
1608 case INS_INSROWS_BEFORE:
1610 aRange.aStart.SetCol(0);
1611 aRange.aEnd.SetCol(rDoc.MaxCol());
1612 [[fallthrough]];
1614 case INS_CELLSDOWN:
1616 auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
1617 return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); });
1618 if (bIntersects)
1619 // This column range cuts through at least one pivot table. Not good.
1620 return false;
1622 // Start row must be either at the top or above any pivot tables.
1623 if (aRange.aStart.Row() < 0)
1624 // I don't know how to handle this case.
1625 return false;
1627 if (aRange.aStart.Row() == 0)
1628 // First row is always allowed.
1629 return true;
1631 ScRange aTest(aRange);
1632 aTest.aStart.IncRow(-1); // Test one row up.
1633 aTest.aEnd.SetRow(aTest.aStart.Row());
1634 for (const auto& rTab : rMarkData)
1636 aTest.aStart.SetTab(rTab);
1637 aTest.aEnd.SetTab(rTab);
1638 if (pDPs->HasTable(aTest))
1639 return false;
1642 break;
1643 case INS_INSCOLS_BEFORE:
1645 aRange.aStart.SetRow(0);
1646 aRange.aEnd.SetRow(rDoc.MaxRow());
1647 [[fallthrough]];
1649 case INS_CELLSRIGHT:
1651 auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
1652 return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); });
1653 if (bIntersects)
1654 // This column range cuts through at least one pivot table. Not good.
1655 return false;
1657 // Start row must be either at the top or above any pivot tables.
1658 if (aRange.aStart.Col() < 0)
1659 // I don't know how to handle this case.
1660 return false;
1662 if (aRange.aStart.Col() == 0)
1663 // First row is always allowed.
1664 return true;
1666 ScRange aTest(aRange);
1667 aTest.aStart.IncCol(-1); // Test one column to the left.
1668 aTest.aEnd.SetCol(aTest.aStart.Col());
1669 for (const auto& rTab : rMarkData)
1671 aTest.aStart.SetTab(rTab);
1672 aTest.aEnd.SetTab(rTab);
1673 if (pDPs->HasTable(aTest))
1674 return false;
1677 break;
1678 default:
1681 return true;
1685 * Check if this deletion attempt would end up cutting one or more pivot
1686 * tables in half, which is not desirable.
1688 * @return true if this deletion can be done safely without shearing any
1689 * existing pivot tables, false otherwise.
1691 bool canDeleteCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, DelCellCmd eCmd, const ScDocument& rDoc)
1693 if (!rDoc.HasPivotTable())
1694 // This document has no pivot tables.
1695 return true;
1697 const ScDPCollection* pDPs = rDoc.GetDPCollection();
1699 ScRange aRange(rRange); // local copy
1701 switch (eCmd)
1703 case DelCellCmd::Rows:
1705 aRange.aStart.SetCol(0);
1706 aRange.aEnd.SetCol(rDoc.MaxCol());
1707 [[fallthrough]];
1709 case DelCellCmd::CellsUp:
1711 auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
1712 return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); });
1713 if (bIntersects)
1714 // This column range cuts through at least one pivot table. Not good.
1715 return false;
1717 ScRange aTest(aRange);
1718 for (const auto& rTab : rMarkData)
1720 aTest.aStart.SetTab(rTab);
1721 aTest.aEnd.SetTab(rTab);
1722 if (pDPs->HasTable(aTest))
1723 return false;
1726 break;
1727 case DelCellCmd::Cols:
1729 aRange.aStart.SetRow(0);
1730 aRange.aEnd.SetRow(rDoc.MaxRow());
1731 [[fallthrough]];
1733 case DelCellCmd::CellsLeft:
1735 auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) {
1736 return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); });
1737 if (bIntersects)
1738 // This column range cuts through at least one pivot table. Not good.
1739 return false;
1741 ScRange aTest(aRange);
1742 for (const auto& rTab : rMarkData)
1744 aTest.aStart.SetTab(rTab);
1745 aTest.aEnd.SetTab(rTab);
1746 if (pDPs->HasTable(aTest))
1747 return false;
1750 break;
1751 default:
1754 return true;
1759 bool ScDocFunc::InsertCells( const ScRange& rRange, const ScMarkData* pTabMark, InsCellCmd eCmd,
1760 bool bRecord, bool bApi, bool bPartOfPaste, size_t nInsertCount )
1762 ScDocShellModificator aModificator( rDocShell );
1763 ScDocument& rDoc = rDocShell.GetDocument();
1765 if (rDocShell.GetDocument().GetChangeTrack() &&
1766 ((eCmd == INS_CELLSDOWN && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != rDoc.MaxCol())) ||
1767 (eCmd == INS_CELLSRIGHT && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != rDoc.MaxRow()))))
1769 // We should not reach this via UI disabled slots.
1770 assert(bApi);
1771 SAL_WARN("sc.ui","ScDocFunc::InsertCells - no change-tracking of partial cell shift");
1772 return false;
1775 ScRange aTargetRange( rRange );
1777 // If insertion is for full cols/rows and after the current
1778 // selection, then shift the range accordingly
1779 if ( eCmd == INS_INSROWS_AFTER )
1781 ScRange aErrorRange( ScAddress::UNINITIALIZED );
1782 if (!aTargetRange.Move(0, rRange.aEnd.Row() - rRange.aStart.Row() + 1, 0, aErrorRange, rDoc))
1784 return false;
1787 if ( eCmd == INS_INSCOLS_AFTER )
1789 ScRange aErrorRange( ScAddress::UNINITIALIZED );
1790 if (!aTargetRange.Move(rRange.aEnd.Col() - rRange.aStart.Col() + 1, 0, 0, aErrorRange, rDoc))
1792 return false;
1796 SCCOL nStartCol = aTargetRange.aStart.Col();
1797 SCROW nStartRow = aTargetRange.aStart.Row();
1798 SCTAB nStartTab = aTargetRange.aStart.Tab();
1799 SCCOL nEndCol = aTargetRange.aEnd.Col() + nInsertCount;
1800 SCROW nEndRow = aTargetRange.aEnd.Row() + nInsertCount;
1801 SCTAB nEndTab = aTargetRange.aEnd.Tab();
1803 if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) )
1805 OSL_FAIL("invalid row in InsertCells");
1806 return false;
1809 SCTAB nTabCount = rDoc.GetTableCount();
1810 SCCOL nPaintStartCol = nStartCol;
1811 SCROW nPaintStartRow = nStartRow;
1812 SCCOL nPaintEndCol = nEndCol;
1813 SCROW nPaintEndRow = nEndRow;
1814 PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
1815 bool bSuccess;
1817 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell(); //preserve current cursor position
1818 SCCOL nCursorCol = 0;
1819 SCROW nCursorRow = 0;
1820 if( pViewSh )
1822 nCursorCol = pViewSh->GetViewData().GetCurX();
1823 nCursorRow = pViewSh->GetViewData().GetCurY();
1826 if (bRecord && !rDoc.IsUndoEnabled())
1827 bRecord = false;
1829 ScMarkData aMark(rDoc.GetSheetLimits());
1830 if (pTabMark)
1831 aMark = *pTabMark;
1832 else
1834 SCTAB nCount = 0;
1835 for( SCTAB i=0; i<nTabCount; i++ )
1837 if( !rDoc.IsScenario(i) )
1839 nCount++;
1840 if( nCount == nEndTab+1 )
1842 aMark.SelectTable( i, true );
1843 break;
1849 ScMarkData aFullMark( aMark ); // including scenario sheets
1850 for (const auto& rTab : aMark)
1852 if (rTab >= nTabCount)
1853 break;
1855 for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
1856 aFullMark.SelectTable( j, true );
1859 SCTAB nSelCount = aMark.GetSelectCount();
1861 // Adjust also related scenarios
1863 SCCOL nMergeTestStartCol = nStartCol;
1864 SCROW nMergeTestStartRow = nStartRow;
1865 SCCOL nMergeTestEndCol = nEndCol;
1866 SCROW nMergeTestEndRow = nEndRow;
1868 ScRange aExtendMergeRange( aTargetRange );
1870 if( aTargetRange.aStart == aTargetRange.aEnd && rDoc.HasAttrib(aTargetRange, HasAttrFlags::Merged) )
1872 rDoc.ExtendMerge( aExtendMergeRange );
1873 rDoc.ExtendOverlapped( aExtendMergeRange );
1874 nMergeTestEndCol = aExtendMergeRange.aEnd.Col();
1875 nMergeTestEndRow = aExtendMergeRange.aEnd.Row();
1876 nPaintEndCol = nMergeTestEndCol;
1877 nPaintEndRow = nMergeTestEndRow;
1880 if ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER )
1882 nMergeTestStartCol = 0;
1883 nMergeTestEndCol = rDoc.MaxCol();
1885 if ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER )
1887 nMergeTestStartRow = 0;
1888 nMergeTestEndRow = rDoc.MaxRow();
1890 if ( eCmd == INS_CELLSDOWN )
1891 nMergeTestEndRow = rDoc.MaxRow();
1892 if ( eCmd == INS_CELLSRIGHT )
1893 nMergeTestEndCol = rDoc.MaxCol();
1895 bool bNeedRefresh = false;
1897 SCCOL nEditTestEndCol = (eCmd==INS_INSCOLS_BEFORE || eCmd==INS_INSCOLS_AFTER) ? rDoc.MaxCol() : nMergeTestEndCol;
1898 SCROW nEditTestEndRow = (eCmd==INS_INSROWS_BEFORE || eCmd==INS_INSROWS_AFTER) ? rDoc.MaxRow() : nMergeTestEndRow;
1900 ScEditableTester aTester;
1902 switch (eCmd)
1904 case INS_INSCOLS_BEFORE:
1905 aTester = ScEditableTester(
1906 rDoc, sc::EditAction::InsertColumnsBefore, nMergeTestStartCol, 0, nMergeTestEndCol, rDoc.MaxRow(), aMark);
1907 break;
1908 case INS_INSCOLS_AFTER:
1909 aTester = ScEditableTester(
1910 rDoc, sc::EditAction::InsertColumnsAfter, nMergeTestStartCol, 0, nMergeTestEndCol, rDoc.MaxRow(), aMark);
1911 break;
1912 case INS_INSROWS_BEFORE:
1913 aTester = ScEditableTester(
1914 rDoc, sc::EditAction::InsertRowsBefore, 0, nMergeTestStartRow, rDoc.MaxCol(), nMergeTestEndRow, aMark);
1915 break;
1916 case INS_INSROWS_AFTER:
1917 aTester = ScEditableTester(
1918 rDoc, sc::EditAction::InsertRowsAfter, 0, nMergeTestStartRow, rDoc.MaxCol(), nMergeTestEndRow, aMark);
1919 break;
1920 default:
1921 aTester = ScEditableTester(
1922 rDoc, nMergeTestStartCol, nMergeTestStartRow, nEditTestEndCol, nEditTestEndRow, aMark);
1925 if (!aTester.IsEditable())
1927 if (!bApi)
1928 rDocShell.ErrorMessage(aTester.GetMessageId());
1929 return false;
1932 // Check if this insertion is allowed with respect to pivot table.
1933 if (!canInsertCellsByPivot(aTargetRange, aMark, eCmd, rDoc))
1935 if (!bApi)
1936 rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE);
1937 return false;
1940 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important due to TrackFormulas at UpdateReference
1942 ScDocumentUniquePtr pRefUndoDoc;
1943 std::unique_ptr<ScRefUndoData> pUndoData;
1944 if ( bRecord )
1946 pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
1947 pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 );
1949 // pRefUndoDoc is filled in InsertCol / InsertRow
1951 pUndoData.reset(new ScRefUndoData( &rDoc ));
1953 rDoc.BeginDrawUndo();
1956 // #i8302 : we unmerge overwhelming ranges, before insertion all the actions are put in the same ListAction
1957 // the patch comes from mloiseleur and maoyg
1958 bool bInsertMerge = false;
1959 std::vector<ScRange> qIncreaseRange;
1960 OUString aUndo = ScResId( STR_UNDO_INSERTCELLS );
1961 if (bRecord)
1963 ViewShellId nViewShellId(-1);
1964 if (pViewSh)
1965 nViewShellId = pViewSh->GetViewShellId();
1966 rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
1968 std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge;
1970 for (const SCTAB i : aMark)
1972 if (i >= nTabCount)
1973 break;
1975 if( rDoc.HasAttrib( nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
1977 if (eCmd==INS_CELLSRIGHT)
1978 bNeedRefresh = true;
1980 SCCOL nMergeStartCol = nMergeTestStartCol;
1981 SCROW nMergeStartRow = nMergeTestStartRow;
1982 SCCOL nMergeEndCol = nMergeTestEndCol;
1983 SCROW nMergeEndRow = nMergeTestEndRow;
1985 rDoc.ExtendMerge( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
1986 rDoc.ExtendOverlapped( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
1988 if(( eCmd == INS_CELLSDOWN && ( nMergeStartCol != nMergeTestStartCol || nMergeEndCol != nMergeTestEndCol )) ||
1989 (eCmd == INS_CELLSRIGHT && ( nMergeStartRow != nMergeTestStartRow || nMergeEndRow != nMergeTestEndRow )) )
1991 if (!bApi)
1992 rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);
1993 rDocShell.GetUndoManager()->LeaveListAction();
1994 return false;
1997 SCCOL nTestCol = -1;
1998 SCROW nTestRow1 = -1;
1999 SCROW nTestRow2 = -1;
2001 ScDocAttrIterator aTestIter( rDoc, i, nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow );
2002 ScRange aExtendRange( nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i );
2003 const ScPatternAttr* pPattern = nullptr;
2004 while ( ( pPattern = aTestIter.GetNext( nTestCol, nTestRow1, nTestRow2 ) ) != nullptr )
2006 const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
2007 const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
2008 ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver);
2009 if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver)
2011 ScRange aRange( nTestCol, nTestRow1, i );
2012 rDoc.ExtendOverlapped(aRange);
2013 rDoc.ExtendMerge(aRange, true);
2015 if( nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor )
2017 for( SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++ )
2019 ScRange aTestRange( nTestCol, nTestRow, i );
2020 rDoc.ExtendOverlapped( aTestRange );
2021 rDoc.ExtendMerge( aTestRange, true);
2022 ScRange aMergeRange( aTestRange.aStart.Col(),aTestRange.aStart.Row(), i );
2023 if( !aExtendRange.Contains( aMergeRange ) )
2025 qIncreaseRange.push_back( aTestRange );
2026 bInsertMerge = true;
2030 else
2032 ScRange aMergeRange( aRange.aStart.Col(),aRange.aStart.Row(), i );
2033 if( !aExtendRange.Contains( aMergeRange ) )
2035 qIncreaseRange.push_back( aRange );
2037 bInsertMerge = true;
2042 if( bInsertMerge )
2044 if( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER || eCmd == INS_CELLSDOWN )
2046 nStartRow = aExtendMergeRange.aStart.Row();
2047 nEndRow = aExtendMergeRange.aEnd.Row();
2049 if( eCmd == INS_CELLSDOWN )
2050 nEndCol = nMergeTestEndCol;
2051 else
2053 nStartCol = 0;
2054 nEndCol = rDoc.MaxCol();
2057 else if( eCmd == INS_CELLSRIGHT || eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER )
2060 nStartCol = aExtendMergeRange.aStart.Col();
2061 nEndCol = aExtendMergeRange.aEnd.Col();
2062 if( eCmd == INS_CELLSRIGHT )
2064 nEndRow = nMergeTestEndRow;
2066 else
2068 nStartRow = 0;
2069 nEndRow = rDoc.MaxRow();
2073 if( !qIncreaseRange.empty() )
2075 if (bRecord && !pUndoRemoveMerge)
2077 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
2078 pUndoDoc->InitUndo( rDoc, *aMark.begin(), *aMark.rbegin());
2079 pUndoRemoveMerge.reset( new ScUndoRemoveMerge( &rDocShell, rRange, std::move(pUndoDoc) ));
2082 for( const ScRange& aRange : qIncreaseRange )
2084 if( rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
2086 UnmergeCells( aRange, bRecord, pUndoRemoveMerge.get() );
2091 else
2093 if (!bApi)
2094 rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0);
2095 rDocShell.GetUndoManager()->LeaveListAction();
2096 return false;
2101 if (bRecord && pUndoRemoveMerge)
2103 rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndoRemoveMerge));
2106 switch (eCmd)
2108 case INS_CELLSDOWN:
2109 bSuccess = rDoc.InsertRow( nStartCol, 0, nEndCol, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &aFullMark );
2110 nPaintEndRow = rDoc.MaxRow();
2111 break;
2112 case INS_INSROWS_BEFORE:
2113 case INS_INSROWS_AFTER:
2114 bSuccess = rDoc.InsertRow( 0, 0, rDoc.MaxCol(), MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &aFullMark );
2115 nPaintStartCol = 0;
2116 nPaintEndCol = rDoc.MaxCol();
2117 nPaintEndRow = rDoc.MaxRow();
2118 nPaintFlags |= PaintPartFlags::Left;
2119 break;
2120 case INS_CELLSRIGHT:
2121 bSuccess = rDoc.InsertCol( nStartRow, 0, nEndRow, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &aFullMark );
2122 nPaintEndCol = rDoc.MaxCol();
2123 break;
2124 case INS_INSCOLS_BEFORE:
2125 case INS_INSCOLS_AFTER:
2126 bSuccess = rDoc.InsertCol( 0, 0, rDoc.MaxRow(), MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &aFullMark );
2127 nPaintStartRow = 0;
2128 nPaintEndRow = rDoc.MaxRow();
2129 nPaintEndCol = rDoc.MaxCol();
2130 nPaintFlags |= PaintPartFlags::Top;
2131 break;
2132 default:
2133 OSL_FAIL("Wrong code at inserting");
2134 bSuccess = false;
2135 break;
2138 if ( bSuccess )
2140 SCTAB nUndoPos = 0;
2142 if ( bRecord )
2144 std::unique_ptr<SCTAB[]> pTabs(new SCTAB[nSelCount]);
2145 std::unique_ptr<SCTAB[]> pScenarios(new SCTAB[nSelCount]);
2146 nUndoPos = 0;
2147 for (const auto& rTab : aMark)
2149 if (rTab >= nTabCount)
2150 break;
2152 SCTAB nCount = 0;
2153 for( SCTAB j=rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2154 nCount ++;
2156 pScenarios[nUndoPos] = nCount;
2157 pTabs[nUndoPos] = rTab;
2158 nUndoPos ++;
2161 if( !bInsertMerge )
2163 rDocShell.GetUndoManager()->LeaveListAction();
2166 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoInsertCells>(
2167 &rDocShell, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ),
2168 nUndoPos, std::move(pTabs), std::move(pScenarios), eCmd, std::move(pRefUndoDoc), std::move(pUndoData), bPartOfPaste ) );
2171 // #i8302 : we remerge growing ranges, with the new part inserted
2173 while( !qIncreaseRange.empty() )
2175 ScRange aRange = qIncreaseRange.back();
2176 if( !rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
2178 switch (eCmd)
2180 case INS_CELLSDOWN:
2181 case INS_INSROWS_BEFORE:
2182 case INS_INSROWS_AFTER:
2183 aRange.aEnd.IncRow(static_cast<SCCOL>(nEndRow-nStartRow+1));
2184 break;
2185 case INS_CELLSRIGHT:
2186 case INS_INSCOLS_BEFORE:
2187 case INS_INSCOLS_AFTER:
2188 aRange.aEnd.IncCol(static_cast<SCCOL>(nEndCol-nStartCol+1));
2189 break;
2190 default:
2191 break;
2193 ScCellMergeOption aMergeOption(
2194 aRange.aStart.Col(), aRange.aStart.Row(),
2195 aRange.aEnd.Col(), aRange.aEnd.Row() );
2196 aMergeOption.maTabs.insert(aRange.aStart.Tab());
2197 MergeCells(aMergeOption, false, true, true);
2199 qIncreaseRange.pop_back();
2202 if( bInsertMerge )
2203 rDocShell.GetUndoManager()->LeaveListAction();
2205 for (const SCTAB i : aMark)
2207 if (i >= nTabCount)
2208 break;
2210 rDoc.SetDrawPageSize(i);
2212 if (bNeedRefresh)
2213 rDoc.ExtendMerge( nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow, i, true );
2214 else
2215 rDoc.RefreshAutoFilter( nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow, i );
2217 if ( eCmd == INS_INSROWS_BEFORE ||eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSROWS_AFTER ||eCmd == INS_INSCOLS_AFTER )
2218 rDoc.UpdatePageBreaks( i );
2220 sal_uInt16 nExtFlags = 0;
2221 rDocShell.UpdatePaintExt( nExtFlags, nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i );
2223 SCTAB nScenarioCount = 0;
2225 for( SCTAB j = i+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2226 nScenarioCount ++;
2228 bool bAdjusted = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER ) ?
2229 AdjustRowHeight(ScRange(0, nStartRow, i, rDoc.MaxCol(), nEndRow, i+nScenarioCount ), true, bApi) :
2230 AdjustRowHeight(ScRange(0, nPaintStartRow, i, rDoc.MaxCol(), nPaintEndRow, i+nScenarioCount ), true, bApi);
2231 if (bAdjusted)
2233 // paint only what is not done by AdjustRowHeight
2234 if (nPaintFlags & PaintPartFlags::Top)
2235 rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i+nScenarioCount, PaintPartFlags::Top );
2237 else
2238 rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i+nScenarioCount, nPaintFlags, nExtFlags );
2241 else
2243 if( bInsertMerge )
2245 while( !qIncreaseRange.empty() )
2247 ScRange aRange = qIncreaseRange.back();
2248 ScCellMergeOption aMergeOption(
2249 aRange.aStart.Col(), aRange.aStart.Row(),
2250 aRange.aEnd.Col(), aRange.aEnd.Row() );
2251 MergeCells(aMergeOption, false, true, true);
2252 qIncreaseRange.pop_back();
2255 if( pViewSh )
2257 pViewSh->MarkRange( aTargetRange, false );
2258 pViewSh->SetCursor( nCursorCol, nCursorRow );
2262 rDocShell.GetUndoManager()->LeaveListAction();
2263 rDocShell.GetUndoManager()->RemoveLastUndoAction();
2265 pRefUndoDoc.reset();
2266 if (!bApi)
2267 rDocShell.ErrorMessage(STR_INSERT_FULL); // column/row full
2270 // The cursor position needs to be modified earlier than updating
2271 // any enabled edit view which is triggered by SetDocumentModified below.
2272 if (bSuccess)
2274 bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
2275 bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
2277 if (bInsertCols)
2279 pViewSh->OnLOKInsertDeleteColumn(rRange.aStart.Col() - (eCmd == INS_INSCOLS_BEFORE ? 1: 0), 1);
2282 if (bInsertRows)
2284 pViewSh->OnLOKInsertDeleteRow(rRange.aStart.Row() - (eCmd == INS_INSROWS_BEFORE ? 1: 0), 1);
2288 aModificator.SetDocumentModified();
2290 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
2291 return bSuccess;
2294 bool ScDocFunc::DeleteCells( const ScRange& rRange, const ScMarkData* pTabMark, DelCellCmd eCmd,
2295 bool bApi )
2297 ScDocShellModificator aModificator( rDocShell );
2298 ScDocument& rDoc = rDocShell.GetDocument();
2300 if (rDocShell.GetDocument().GetChangeTrack() &&
2301 ((eCmd == DelCellCmd::CellsUp && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != rDoc.MaxCol())) ||
2302 (eCmd == DelCellCmd::CellsLeft && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != rDoc.MaxRow()))))
2304 // We should not reach this via UI disabled slots.
2305 assert(bApi);
2306 SAL_WARN("sc.ui","ScDocFunc::DeleteCells - no change-tracking of partial cell shift");
2307 return false;
2310 SCCOL nStartCol = rRange.aStart.Col();
2311 SCROW nStartRow = rRange.aStart.Row();
2312 SCTAB nStartTab = rRange.aStart.Tab();
2313 SCCOL nEndCol = rRange.aEnd.Col();
2314 SCROW nEndRow = rRange.aEnd.Row();
2315 SCTAB nEndTab = rRange.aEnd.Tab();
2317 if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) )
2319 OSL_FAIL("invalid row in DeleteCells");
2320 return false;
2323 SCTAB nTabCount = rDoc.GetTableCount();
2324 SCCOL nPaintStartCol = nStartCol;
2325 SCROW nPaintStartRow = nStartRow;
2326 SCCOL nPaintEndCol = nEndCol;
2327 SCROW nPaintEndRow = nEndRow;
2328 PaintPartFlags nPaintFlags = PaintPartFlags::Grid;
2330 bool bRecord = true;
2331 if (!rDoc.IsUndoEnabled())
2332 bRecord = false;
2334 ScMarkData aMark(rDoc.GetSheetLimits());
2335 if (pTabMark)
2336 aMark = *pTabMark;
2337 else
2339 SCTAB nCount = 0;
2340 for(SCTAB i=0; i<nTabCount; i++ )
2342 if( !rDoc.IsScenario(i) )
2344 nCount++;
2345 if( nCount == nEndTab+1 )
2347 aMark.SelectTable(i, true);
2348 break;
2354 ScMarkData aFullMark( aMark ); // including scenario sheets
2355 for (const auto& rTab : aMark)
2357 if (rTab >= nTabCount)
2358 break;
2360 for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2361 aFullMark.SelectTable( j, true );
2364 SCTAB nSelCount = aMark.GetSelectCount();
2366 SCCOL nUndoStartCol = nStartCol;
2367 SCROW nUndoStartRow = nStartRow;
2368 SCCOL nUndoEndCol = nEndCol;
2369 SCROW nUndoEndRow = nEndRow;
2371 ScRange aExtendMergeRange( rRange );
2373 if( rRange.aStart == rRange.aEnd && rDoc.HasAttrib(rRange, HasAttrFlags::Merged) )
2375 rDoc.ExtendMerge( aExtendMergeRange );
2376 rDoc.ExtendOverlapped( aExtendMergeRange );
2377 nUndoEndCol = aExtendMergeRange.aEnd.Col();
2378 nUndoEndRow = aExtendMergeRange.aEnd.Row();
2379 nPaintEndCol = nUndoEndCol;
2380 nPaintEndRow = nUndoEndRow;
2383 if (eCmd==DelCellCmd::Rows)
2385 nUndoStartCol = 0;
2386 nUndoEndCol = rDoc.MaxCol();
2388 if (eCmd==DelCellCmd::Cols)
2390 nUndoStartRow = 0;
2391 nUndoEndRow = rDoc.MaxRow();
2393 // Test for cell protection
2395 SCCOL nEditTestEndX = nUndoEndCol;
2396 if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft )
2397 nEditTestEndX = rDoc.MaxCol();
2398 SCROW nEditTestEndY = nUndoEndRow;
2399 if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp )
2400 nEditTestEndY = rDoc.MaxRow();
2402 ScEditableTester aTester;
2404 switch (eCmd)
2406 case DelCellCmd::Cols:
2407 aTester = ScEditableTester(
2408 rDoc, sc::EditAction::DeleteColumns, nUndoStartCol, 0, nUndoEndCol, rDoc.MaxRow(), aMark);
2409 break;
2410 case DelCellCmd::Rows:
2411 aTester = ScEditableTester(
2412 rDoc, sc::EditAction::DeleteRows, 0, nUndoStartRow, rDoc.MaxCol(), nUndoEndRow, aMark);
2413 break;
2414 default:
2415 aTester = ScEditableTester(
2416 rDoc, nUndoStartCol, nUndoStartRow, nEditTestEndX, nEditTestEndY, aMark);
2419 if (!aTester.IsEditable())
2421 if (!bApi)
2422 rDocShell.ErrorMessage(aTester.GetMessageId());
2423 return false;
2426 if (!canDeleteCellsByPivot(rRange, aMark, eCmd, rDoc))
2428 if (!bApi)
2429 rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE);
2430 return false;
2432 // Test for merged cells
2434 SCCOL nMergeTestEndCol = (eCmd==DelCellCmd::CellsLeft) ? rDoc.MaxCol() : nUndoEndCol;
2435 SCROW nMergeTestEndRow = (eCmd==DelCellCmd::CellsUp) ? rDoc.MaxRow() : nUndoEndRow;
2436 SCCOL nExtendStartCol = nUndoStartCol;
2437 SCROW nExtendStartRow = nUndoStartRow;
2438 bool bNeedRefresh = false;
2440 //Issue 8302 want to be able to insert into the middle of merged cells
2441 //the patch comes from maoyg
2442 ::std::vector<ScRange> qDecreaseRange;
2443 bool bDeletingMerge = false;
2444 OUString aUndo = ScResId( STR_UNDO_DELETECELLS );
2445 if (bRecord)
2447 ViewShellId nViewShellId(-1);
2448 if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
2449 nViewShellId = pViewSh->GetViewShellId();
2450 rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
2452 std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge;
2454 for (const SCTAB i : aMark)
2456 if (i >= nTabCount)
2457 break;
2459 if ( rDoc.HasAttrib( nUndoStartCol, nUndoStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2461 SCCOL nMergeStartCol = nUndoStartCol;
2462 SCROW nMergeStartRow = nUndoStartRow;
2463 SCCOL nMergeEndCol = nMergeTestEndCol;
2464 SCROW nMergeEndRow = nMergeTestEndRow;
2466 rDoc.ExtendMerge( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
2467 rDoc.ExtendOverlapped( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i );
2468 if( ( eCmd == DelCellCmd::CellsUp && ( nMergeStartCol != nUndoStartCol || nMergeEndCol != nMergeTestEndCol))||
2469 ( eCmd == DelCellCmd::CellsLeft && ( nMergeStartRow != nUndoStartRow || nMergeEndRow != nMergeTestEndRow)))
2471 if (!bApi)
2472 rDocShell.ErrorMessage(STR_MSSG_DELETECELLS_0);
2473 rDocShell.GetUndoManager()->LeaveListAction();
2474 return false;
2477 nExtendStartCol = nMergeStartCol;
2478 nExtendStartRow = nMergeStartRow;
2479 SCCOL nTestCol = -1;
2480 SCROW nTestRow1 = -1;
2481 SCROW nTestRow2 = -1;
2483 ScDocAttrIterator aTestIter( rDoc, i, nUndoStartCol, nUndoStartRow, nMergeTestEndCol, nMergeTestEndRow );
2484 ScRange aExtendRange( nUndoStartCol, nUndoStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i );
2485 const ScPatternAttr* pPattern = nullptr;
2486 while ( ( pPattern = aTestIter.GetNext( nTestCol, nTestRow1, nTestRow2 ) ) != nullptr )
2488 const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE);
2489 const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG);
2490 ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver);
2491 if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver)
2493 ScRange aRange( nTestCol, nTestRow1, i );
2494 rDoc.ExtendOverlapped( aRange );
2495 rDoc.ExtendMerge( aRange, true );
2497 if( nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor )
2499 for( SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++ )
2501 ScRange aTestRange( nTestCol, nTestRow, i );
2502 rDoc.ExtendOverlapped( aTestRange );
2503 rDoc.ExtendMerge( aTestRange, true );
2504 ScRange aMergeRange( aTestRange.aStart.Col(),aTestRange.aStart.Row(), i );
2505 if( !aExtendRange.Contains( aMergeRange ) )
2507 qDecreaseRange.push_back( aTestRange );
2508 bDeletingMerge = true;
2512 else
2514 ScRange aMergeRange( aRange.aStart.Col(),aRange.aStart.Row(), i );
2515 if( !aExtendRange.Contains( aMergeRange ) )
2517 qDecreaseRange.push_back( aRange );
2519 bDeletingMerge = true;
2524 if( bDeletingMerge )
2527 if( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::CellsUp )
2529 nStartRow = aExtendMergeRange.aStart.Row();
2530 nEndRow = aExtendMergeRange.aEnd.Row();
2531 bNeedRefresh = true;
2533 if( eCmd == DelCellCmd::CellsUp )
2535 nEndCol = aExtendMergeRange.aEnd.Col();
2537 else
2539 nStartCol = 0;
2540 nEndCol = rDoc.MaxCol();
2543 else if( eCmd == DelCellCmd::CellsLeft || eCmd == DelCellCmd::Cols )
2546 nStartCol = aExtendMergeRange.aStart.Col();
2547 nEndCol = aExtendMergeRange.aEnd.Col();
2548 if( eCmd == DelCellCmd::CellsLeft )
2550 nEndRow = aExtendMergeRange.aEnd.Row();
2551 bNeedRefresh = true;
2553 else
2555 nStartRow = 0;
2556 nEndRow = rDoc.MaxRow();
2560 if( !qDecreaseRange.empty() )
2562 if (bRecord && !pUndoRemoveMerge)
2564 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
2565 pUndoDoc->InitUndo( rDoc, *aMark.begin(), *aMark.rbegin());
2566 pUndoRemoveMerge.reset( new ScUndoRemoveMerge( &rDocShell, rRange, std::move(pUndoDoc) ));
2569 for( const ScRange& aRange : qDecreaseRange )
2571 if( rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
2573 UnmergeCells( aRange, bRecord, pUndoRemoveMerge.get() );
2578 else
2580 if (!bApi)
2581 rDocShell.ErrorMessage(STR_MSSG_DELETECELLS_0);
2582 rDocShell.GetUndoManager()->LeaveListAction();
2583 return false;
2588 if (bRecord && pUndoRemoveMerge)
2590 rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndoRemoveMerge));
2593 // do it
2595 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference
2597 ScDocumentUniquePtr pUndoDoc;
2598 std::unique_ptr<ScDocument> pRefUndoDoc;
2599 std::unique_ptr<ScRefUndoData> pUndoData;
2600 if ( bRecord )
2602 // With the fix for #101329#, UpdateRef always puts cells into pRefUndoDoc at their old position,
2603 // so it's no longer necessary to copy more than the deleted range into pUndoDoc.
2605 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2606 pUndoDoc->InitUndo( rDoc, 0, nTabCount-1, (eCmd==DelCellCmd::Cols), (eCmd==DelCellCmd::Rows) );
2607 for (const auto& rTab : aMark)
2609 if (rTab >= nTabCount)
2610 break;
2612 SCTAB nScenarioCount = 0;
2614 for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2615 nScenarioCount ++;
2617 rDoc.CopyToDocument( nUndoStartCol, nUndoStartRow, rTab, nUndoEndCol, nUndoEndRow, rTab+nScenarioCount,
2618 InsertDeleteFlags::ALL | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
2621 pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2622 pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 );
2624 pUndoData.reset(new ScRefUndoData( &rDoc ));
2626 rDoc.BeginDrawUndo();
2629 sal_uInt16 nExtFlags = 0;
2630 for (const auto& rTab : aMark)
2632 if (rTab >= nTabCount)
2633 break;
2635 rDocShell.UpdatePaintExt( nExtFlags, nStartCol, nStartRow, rTab, nEndCol, nEndRow, rTab );
2638 switch (eCmd)
2640 case DelCellCmd::CellsUp:
2641 case DelCellCmd::CellsLeft:
2642 rDoc.DeleteObjectsInArea(nStartCol, nStartRow, nEndCol, nEndRow, aMark, true);
2643 break;
2644 case DelCellCmd::Rows:
2645 rDoc.DeleteObjectsInArea(0, nStartRow, rDoc.MaxCol(), nEndRow, aMark, true);
2646 break;
2647 case DelCellCmd::Cols:
2648 rDoc.DeleteObjectsInArea(nStartCol, 0, nEndCol, rDoc.MaxRow(), aMark, true);
2649 break;
2650 default:
2651 break;
2655 bool bUndoOutline = false;
2656 switch (eCmd)
2658 case DelCellCmd::CellsUp:
2659 rDoc.DeleteRow( nStartCol, 0, nEndCol, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), nullptr, &aFullMark );
2660 nPaintEndRow = rDoc.MaxRow();
2661 break;
2662 case DelCellCmd::Rows:
2663 rDoc.DeleteRow( 0, 0, rDoc.MaxCol(), MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &bUndoOutline, &aFullMark );
2664 nPaintStartCol = 0;
2665 nPaintEndCol = rDoc.MaxCol();
2666 nPaintEndRow = rDoc.MaxRow();
2667 nPaintFlags |= PaintPartFlags::Left;
2668 break;
2669 case DelCellCmd::CellsLeft:
2670 rDoc.DeleteCol( nStartRow, 0, nEndRow, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), nullptr, &aFullMark );
2671 nPaintEndCol = rDoc.MaxCol();
2672 break;
2673 case DelCellCmd::Cols:
2674 rDoc.DeleteCol( 0, 0, rDoc.MaxRow(), MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &bUndoOutline, &aFullMark );
2675 nPaintStartRow = 0;
2676 nPaintEndRow = rDoc.MaxRow();
2677 nPaintEndCol = rDoc.MaxCol();
2678 nPaintFlags |= PaintPartFlags::Top;
2679 break;
2680 default:
2681 OSL_FAIL("Wrong code at deleting");
2682 break;
2685 //! Test if the size of outline has changed
2687 if ( bRecord )
2689 for (const auto& rTab : aFullMark)
2691 if (rTab >= nTabCount)
2692 break;
2694 pRefUndoDoc->DeleteAreaTab(nUndoStartCol,nUndoStartRow,nUndoEndCol,nUndoEndRow, rTab, InsertDeleteFlags::ALL);
2697 // for all sheets, so that formulas can be copied
2698 pUndoDoc->AddUndoTab( 0, nTabCount-1 );
2700 // copy with bColRowFlags=false (#54194#)
2701 pRefUndoDoc->CopyToDocument(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB,InsertDeleteFlags::FORMULA,false,*pUndoDoc,nullptr,false);
2702 pRefUndoDoc.reset();
2704 std::unique_ptr<SCTAB[]> pTabs( new SCTAB[nSelCount]);
2705 std::unique_ptr<SCTAB[]> pScenarios( new SCTAB[nSelCount]);
2706 SCTAB nUndoPos = 0;
2708 for (const auto& rTab : aMark)
2710 if (rTab >= nTabCount)
2711 break;
2713 SCTAB nCount = 0;
2714 for( SCTAB j=rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2715 nCount ++;
2717 pScenarios[nUndoPos] = nCount;
2718 pTabs[nUndoPos] = rTab;
2719 nUndoPos ++;
2722 if( !bDeletingMerge )
2724 rDocShell.GetUndoManager()->LeaveListAction();
2727 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDeleteCells>(
2728 &rDocShell, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ),
2729 nUndoPos, std::move(pTabs), std::move(pScenarios),
2730 eCmd, std::move(pUndoDoc), std::move(pUndoData) ) );
2733 // #i8302 want to be able to insert into the middle of merged cells
2734 // the patch comes from maoyg
2736 while( !qDecreaseRange.empty() )
2738 ScRange aRange = qDecreaseRange.back();
2740 sal_Int32 nDecreaseRowCount = 0;
2741 sal_Int32 nDecreaseColCount = 0;
2742 if( eCmd == DelCellCmd::CellsUp || eCmd == DelCellCmd::Rows )
2744 if( nStartRow >= aRange.aStart.Row() && nStartRow <= aRange.aEnd.Row() && nEndRow>= aRange.aStart.Row() && nEndRow <= aRange.aEnd.Row() )
2745 nDecreaseRowCount = nEndRow-nStartRow+1;
2746 else if( nStartRow >= aRange.aStart.Row() && nStartRow <= aRange.aEnd.Row() && nEndRow >= aRange.aStart.Row() && nEndRow >= aRange.aEnd.Row() )
2747 nDecreaseRowCount = aRange.aEnd.Row()-nStartRow+1;
2748 else if( nStartRow >= aRange.aStart.Row() && nStartRow >= aRange.aEnd.Row() && nEndRow>= aRange.aStart.Row() && nEndRow <= aRange.aEnd.Row() )
2749 nDecreaseRowCount = aRange.aEnd.Row()-nEndRow+1;
2751 else if( eCmd == DelCellCmd::CellsLeft || eCmd == DelCellCmd::Cols )
2753 if( nStartCol >= aRange.aStart.Col() && nStartCol <= aRange.aEnd.Col() && nEndCol>= aRange.aStart.Col() && nEndCol <= aRange.aEnd.Col() )
2754 nDecreaseColCount = nEndCol-nStartCol+1;
2755 else if( nStartCol >= aRange.aStart.Col() && nStartCol <= aRange.aEnd.Col() && nEndCol >= aRange.aStart.Col() && nEndCol >= aRange.aEnd.Col() )
2756 nDecreaseColCount = aRange.aEnd.Col()-nStartCol+1;
2757 else if( nStartCol >= aRange.aStart.Col() && nStartCol >= aRange.aEnd.Col() && nEndCol>= aRange.aStart.Col() && nEndCol <= aRange.aEnd.Col() )
2758 nDecreaseColCount = aRange.aEnd.Col()-nEndCol+1;
2761 switch (eCmd)
2763 case DelCellCmd::CellsUp:
2764 case DelCellCmd::Rows:
2765 aRange.aEnd.SetRow(static_cast<SCCOL>( aRange.aEnd.Row()-nDecreaseRowCount));
2766 break;
2767 case DelCellCmd::CellsLeft:
2768 case DelCellCmd::Cols:
2769 aRange.aEnd.SetCol(static_cast<SCCOL>( aRange.aEnd.Col()-nDecreaseColCount));
2770 break;
2771 default:
2772 break;
2775 if( !rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) )
2777 ScCellMergeOption aMergeOption(aRange);
2778 MergeCells( aMergeOption, false, true, true );
2780 qDecreaseRange.pop_back();
2783 if( bDeletingMerge )
2784 rDocShell.GetUndoManager()->LeaveListAction();
2786 if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft )
2787 nMergeTestEndCol = rDoc.MaxCol();
2788 if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp )
2789 nMergeTestEndRow = rDoc.MaxRow();
2790 if ( bNeedRefresh )
2792 // #i51445# old merge flag attributes must be deleted also for single cells,
2793 // not only for whole columns/rows
2795 ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
2796 aPattern.GetItemSet().Put( ScMergeFlagAttr() );
2798 rDoc.ApplyPatternArea( nExtendStartCol, nExtendStartRow, nMergeTestEndCol, nMergeTestEndRow, aMark, aPattern );
2800 for (const auto& rTab : aMark)
2802 if (rTab >= nTabCount)
2803 break;
2805 SCTAB nScenarioCount = 0;
2807 for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2808 nScenarioCount ++;
2810 ScRange aMergedRange( nExtendStartCol, nExtendStartRow, rTab, nMergeTestEndCol, nMergeTestEndRow, rTab+nScenarioCount );
2811 rDoc.ExtendMerge( aMergedRange, true );
2815 for (const auto& rTab : aMark)
2817 if (rTab >= nTabCount)
2818 break;
2820 rDoc.RefreshAutoFilter( nExtendStartCol, nExtendStartRow, nMergeTestEndCol, nMergeTestEndRow, rTab );
2823 for (const auto& rTab : aMark)
2825 if (rTab >= nTabCount)
2826 break;
2828 rDoc.SetDrawPageSize(rTab);
2830 if ( eCmd == DelCellCmd::Cols || eCmd == DelCellCmd::Rows )
2831 rDoc.UpdatePageBreaks( rTab );
2833 rDocShell.UpdatePaintExt( nExtFlags, nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab );
2835 SCTAB nScenarioCount = 0;
2837 for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ )
2838 nScenarioCount ++;
2840 // delete entire rows: do not adjust
2841 if ( eCmd == DelCellCmd::Rows || !AdjustRowHeight(ScRange( 0, nPaintStartRow, rTab, rDoc.MaxCol(), nPaintEndRow, rTab+nScenarioCount ), true, bApi) )
2842 rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, nPaintFlags, nExtFlags );
2843 else
2845 // paint only what is not done by AdjustRowHeight
2846 if (nExtFlags & SC_PF_LINES)
2847 lcl_PaintAbove( rDocShell, ScRange( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount) );
2848 if (nPaintFlags & PaintPartFlags::Top)
2849 rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, PaintPartFlags::Top );
2853 // The cursor position needs to be modified earlier than updating
2854 // any enabled edit view which is triggered by SetDocumentModified below.
2855 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
2856 if (pViewSh)
2858 if (eCmd == DelCellCmd::Cols)
2860 pViewSh->OnLOKInsertDeleteColumn(rRange.aStart.Col(), -1 * (rRange.aEnd.Col() - rRange.aStart.Col() + 1));
2862 if (eCmd == DelCellCmd::Rows)
2864 pViewSh->OnLOKInsertDeleteRow(rRange.aStart.Row(), -1 * (rRange.aEnd.Row() - rRange.aStart.Row() + 1));
2868 aModificator.SetDocumentModified();
2870 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
2872 return true;
2875 bool ScDocFunc::MoveBlock( const ScRange& rSource, const ScAddress& rDestPos,
2876 bool bCut, bool bRecord, bool bPaint, bool bApi )
2878 ScDocShellModificator aModificator( rDocShell );
2880 SCCOL nStartCol = rSource.aStart.Col();
2881 SCROW nStartRow = rSource.aStart.Row();
2882 SCTAB nStartTab = rSource.aStart.Tab();
2883 SCCOL nEndCol = rSource.aEnd.Col();
2884 SCROW nEndRow = rSource.aEnd.Row();
2885 SCTAB nEndTab = rSource.aEnd.Tab();
2886 SCCOL nDestCol = rDestPos.Col();
2887 SCROW nDestRow = rDestPos.Row();
2888 SCTAB nDestTab = rDestPos.Tab();
2890 ScDocument& rDoc = rDocShell.GetDocument();
2891 if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) || !rDoc.ValidRow(nDestRow) )
2893 OSL_FAIL("invalid row in MoveBlock");
2894 return false;
2897 // adjust related scenarios too - but only when moved within one sheet
2898 bool bScenariosAdded = false;
2899 if (bRecord && !rDoc.IsUndoEnabled())
2900 bRecord = false;
2902 SCTAB nTabCount = rDoc.GetTableCount();
2903 if ( nDestTab == nStartTab && !rDoc.IsScenario(nEndTab) )
2904 while ( nEndTab+1 < nTabCount && rDoc.IsScenario(nEndTab+1) )
2906 ++nEndTab;
2907 bScenariosAdded = true;
2910 SCTAB nSrcTabCount = nEndTab-nStartTab+1;
2911 SCTAB nDestEndTab = nDestTab+nSrcTabCount-1;
2912 SCTAB nTab;
2914 ScDocumentUniquePtr pClipDoc(new ScDocument(SCDOCMODE_CLIP));
2916 ScMarkData aSourceMark(rDoc.GetSheetLimits());
2917 for (nTab=nStartTab; nTab<=nEndTab; nTab++)
2918 aSourceMark.SelectTable( nTab, true ); // select source
2919 aSourceMark.SetMarkArea( rSource );
2921 ScDocShellRef aDragShellRef;
2922 if ( rDoc.HasOLEObjectsInArea( rSource ) )
2924 aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately
2925 aDragShellRef->DoInitNew();
2927 ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() );
2929 ScClipParam aClipParam(ScRange(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nStartTab), bCut);
2930 rDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSourceMark, bScenariosAdded, true);
2932 ScDrawLayer::SetGlobalDrawPersist(nullptr);
2934 SCCOL nOldEndCol = nEndCol;
2935 SCROW nOldEndRow = nEndRow;
2936 bool bClipOver = false;
2937 for (nTab=nStartTab; nTab<=nEndTab; nTab++)
2939 SCCOL nTmpEndCol = nOldEndCol;
2940 SCROW nTmpEndRow = nOldEndRow;
2941 if (rDoc.ExtendMerge( nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab ))
2942 bClipOver = true;
2943 if ( nTmpEndCol > nEndCol ) nEndCol = nTmpEndCol;
2944 if ( nTmpEndRow > nEndRow ) nEndRow = nTmpEndRow;
2947 SCCOL nDestEndCol = nDestCol + ( nOldEndCol-nStartCol );
2948 SCROW nDestEndRow = nDestRow + ( nOldEndRow-nStartRow );
2950 SCCOL nUndoEndCol = nDestCol + ( nEndCol-nStartCol ); // extended in destination block
2951 SCROW nUndoEndRow = nDestRow + ( nEndRow-nStartRow );
2953 bool bIncludeFiltered = bCut;
2954 if ( !bIncludeFiltered )
2956 // adjust sizes to include only non-filtered rows
2958 SCCOL nClipX;
2959 SCROW nClipY;
2960 pClipDoc->GetClipArea( nClipX, nClipY, false );
2961 SCROW nUndoAdd = nUndoEndRow - nDestEndRow;
2962 nDestEndRow = nDestRow + nClipY;
2963 nUndoEndRow = nDestEndRow + nUndoAdd;
2966 if (!rDoc.ValidCol(nUndoEndCol) || !rDoc.ValidRow(nUndoEndRow))
2968 if (!bApi)
2969 rDocShell.ErrorMessage(STR_PASTE_FULL);
2970 return false;
2973 // Test for cell protection
2975 ScEditableTester aTester;
2976 for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
2977 aTester.TestBlock( rDoc, nTab, nDestCol,nDestRow, nUndoEndCol,nUndoEndRow );
2978 if (bCut)
2979 for (nTab=nStartTab; nTab<=nEndTab; nTab++)
2980 aTester.TestBlock( rDoc, nTab, nStartCol,nStartRow, nEndCol,nEndRow );
2982 if (!aTester.IsEditable())
2984 if (!bApi)
2985 rDocShell.ErrorMessage(aTester.GetMessageId());
2986 return false;
2989 // Test for merged cells- when moving after delete
2991 if (bClipOver && !bCut)
2992 if (rDoc.HasAttrib( nDestCol,nDestRow,nDestTab, nUndoEndCol,nUndoEndRow,nDestEndTab,
2993 HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2994 { // "Merge of already merged cells not possible"
2995 if (!bApi)
2996 rDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0);
2997 return false;
3000 // Are there borders in the cells? (for painting)
3002 sal_uInt16 nSourceExt = 0;
3003 rDocShell.UpdatePaintExt( nSourceExt, nStartCol,nStartRow,nStartTab, nEndCol,nEndRow,nEndTab );
3004 sal_uInt16 nDestExt = 0;
3005 rDocShell.UpdatePaintExt( nDestExt, nDestCol,nDestRow,nDestTab, nDestEndCol,nDestEndRow,nDestEndTab );
3007 // do it
3009 ScDocumentUniquePtr pUndoDoc;
3011 if (bRecord)
3013 bool bWholeCols = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() );
3014 bool bWholeRows = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() );
3015 InsertDeleteFlags nUndoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS;
3017 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
3018 pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab, bWholeCols, bWholeRows );
3020 if (bCut)
3022 rDoc.CopyToDocument( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab,
3023 nUndoFlags, false, *pUndoDoc );
3026 if ( nDestTab != nStartTab )
3027 pUndoDoc->AddUndoTab( nDestTab, nDestEndTab, bWholeCols, bWholeRows );
3028 rDoc.CopyToDocument( nDestCol, nDestRow, nDestTab,
3029 nDestEndCol, nDestEndRow, nDestEndTab,
3030 nUndoFlags, false, *pUndoDoc );
3031 rDoc.BeginDrawUndo();
3034 bool bSourceHeight = false; // adjust heights?
3035 if (bCut)
3037 ScMarkData aDelMark(rDoc.GetSheetLimits()); // only for tables
3038 for (nTab=nStartTab; nTab<=nEndTab; nTab++)
3040 rDoc.DeleteAreaTab( nStartCol,nStartRow, nOldEndCol,nOldEndRow, nTab, InsertDeleteFlags::ALL );
3041 aDelMark.SelectTable( nTab, true );
3043 rDoc.DeleteObjectsInArea( nStartCol,nStartRow, nOldEndCol,nOldEndRow, aDelMark );
3045 // Test for merged cells
3047 if (bClipOver)
3048 if (rDoc.HasAttrib( nDestCol,nDestRow,nDestTab,
3049 nUndoEndCol,nUndoEndRow,nDestEndTab,
3050 HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
3052 rDoc.CopyFromClip( rSource, aSourceMark, InsertDeleteFlags::ALL, nullptr, pClipDoc.get() );
3053 for (nTab=nStartTab; nTab<=nEndTab; nTab++)
3055 SCCOL nTmpEndCol = nEndCol;
3056 SCROW nTmpEndRow = nEndRow;
3057 rDoc.ExtendMerge( nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab, true );
3060 // Report error only after restoring content
3061 if (!bApi) // "Merge of already merged cells not possible"
3062 rDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0);
3064 return false;
3067 bSourceHeight = AdjustRowHeight( rSource, false, bApi );
3070 ScRange aPasteDest( nDestCol, nDestRow, nDestTab, nDestEndCol, nDestEndRow, nDestEndTab );
3072 ScMarkData aDestMark(rDoc.GetSheetLimits());
3073 for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
3074 aDestMark.SelectTable( nTab, true ); // select destination
3075 aDestMark.SetMarkArea( aPasteDest );
3077 /* Do not copy drawing objects here. While pasting, the
3078 function ScDocument::UpdateReference() is called which calls
3079 ScDrawLayer::MoveCells() which may move away inserted objects to wrong
3080 positions (e.g. if source and destination range overlaps).*/
3082 rDoc.CopyFromClip(
3083 aPasteDest, aDestMark, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS,
3084 pUndoDoc.get(), pClipDoc.get(), true, false, bIncludeFiltered);
3086 // skipped rows and merged cells don't mix
3087 if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() )
3088 UnmergeCells( aPasteDest, false, nullptr );
3090 bool bDestHeight = AdjustRowHeight(
3091 ScRange( 0,nDestRow,nDestTab, rDoc.MaxCol(),nDestEndRow,nDestEndTab ),
3092 false, bApi );
3094 /* Paste drawing objects after adjusting formula references
3095 and row heights. There are no cell notes or drawing objects, if the
3096 clipdoc does not contain a drawing layer.*/
3097 if ( pClipDoc->GetDrawLayer() )
3098 rDoc.CopyFromClip( aPasteDest, aDestMark, InsertDeleteFlags::OBJECTS,
3099 nullptr, pClipDoc.get(), true, false, bIncludeFiltered );
3101 if (bRecord)
3103 ScRange aUndoRange(nStartCol, nStartRow, nStartTab, nOldEndCol, nOldEndRow, nEndTab);
3104 ScAddress aDestPos(nDestCol, nDestRow, nDestTab);
3106 rDocShell.GetUndoManager()->AddUndoAction(
3107 std::make_unique<ScUndoDragDrop>(
3108 &rDocShell, aUndoRange, aDestPos, bCut, std::move(pUndoDoc), bScenariosAdded));
3111 SCCOL nDestPaintEndCol = nDestEndCol;
3112 SCROW nDestPaintEndRow = nDestEndRow;
3113 for (nTab=nDestTab; nTab<=nDestEndTab; nTab++)
3115 SCCOL nTmpEndCol = nDestEndCol;
3116 SCROW nTmpEndRow = nDestEndRow;
3117 rDoc.ExtendMerge( nDestCol, nDestRow, nTmpEndCol, nTmpEndRow, nTab, true );
3118 if (nTmpEndCol > nDestPaintEndCol) nDestPaintEndCol = nTmpEndCol;
3119 if (nTmpEndRow > nDestPaintEndRow) nDestPaintEndRow = nTmpEndRow;
3122 if (bCut)
3123 for (nTab=nStartTab; nTab<=nEndTab; nTab++)
3124 rDoc.RefreshAutoFilter( nStartCol, nStartRow, nEndCol, nEndRow, nTab );
3126 if (bPaint)
3128 // destination range:
3130 SCCOL nPaintStartX = nDestCol;
3131 SCROW nPaintStartY = nDestRow;
3132 SCCOL nPaintEndX = nDestPaintEndCol;
3133 SCROW nPaintEndY = nDestPaintEndRow;
3134 PaintPartFlags nFlags = PaintPartFlags::Grid;
3136 if ( nStartRow==0 && nEndRow==rDoc.MaxRow() ) // copy widths too?
3138 nPaintEndX = rDoc.MaxCol();
3139 nPaintStartY = 0;
3140 nPaintEndY = rDoc.MaxRow();
3141 nFlags |= PaintPartFlags::Top;
3143 if ( bDestHeight || ( nStartCol == 0 && nEndCol == rDoc.MaxCol() ) )
3145 nPaintEndY = rDoc.MaxRow();
3146 nPaintStartX = 0;
3147 nPaintEndX = rDoc.MaxCol();
3148 nFlags |= PaintPartFlags::Left;
3150 if ( bScenariosAdded )
3152 nPaintStartX = 0;
3153 nPaintStartY = 0;
3154 nPaintEndX = rDoc.MaxCol();
3155 nPaintEndY = rDoc.MaxRow();
3158 rDocShell.PostPaint( nPaintStartX,nPaintStartY,nDestTab,
3159 nPaintEndX,nPaintEndY,nDestEndTab, nFlags, nSourceExt | nDestExt );
3161 if ( bCut )
3163 // source range:
3165 nPaintStartX = nStartCol;
3166 nPaintStartY = nStartRow;
3167 nPaintEndX = nEndCol;
3168 nPaintEndY = nEndRow;
3169 nFlags = PaintPartFlags::Grid;
3171 if ( bSourceHeight )
3173 nPaintEndY = rDoc.MaxRow();
3174 nPaintStartX = 0;
3175 nPaintEndX = rDoc.MaxCol();
3176 nFlags |= PaintPartFlags::Left;
3178 if ( bScenariosAdded )
3180 nPaintStartX = 0;
3181 nPaintStartY = 0;
3182 nPaintEndX = rDoc.MaxCol();
3183 nPaintEndY = rDoc.MaxRow();
3186 rDocShell.PostPaint( nPaintStartX,nPaintStartY,nStartTab,
3187 nPaintEndX,nPaintEndY,nEndTab, nFlags, nSourceExt );
3191 aModificator.SetDocumentModified();
3193 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
3195 return true;
3198 static uno::Reference< uno::XInterface > GetDocModuleObject( const SfxObjectShell& rDocSh, const OUString& sCodeName )
3200 uno::Reference< lang::XMultiServiceFactory> xSF(rDocSh.GetModel(), uno::UNO_QUERY);
3201 uno::Reference< container::XNameAccess > xVBACodeNamedObjectAccess;
3202 uno::Reference< uno::XInterface > xDocModuleApiObject;
3203 if ( xSF.is() )
3205 xVBACodeNamedObjectAccess.set( xSF->createInstance(u"ooo.vba.VBAObjectModuleObjectProvider"_ustr), uno::UNO_QUERY );
3206 xDocModuleApiObject.set( xVBACodeNamedObjectAccess->getByName( sCodeName ), uno::UNO_QUERY );
3208 return xDocModuleApiObject;
3212 static script::ModuleInfo lcl_InitModuleInfo( const SfxObjectShell& rDocSh, const OUString& sModule )
3214 script::ModuleInfo sModuleInfo;
3215 sModuleInfo.ModuleType = script::ModuleType::DOCUMENT;
3216 sModuleInfo.ModuleObject = GetDocModuleObject( rDocSh, sModule );
3217 return sModuleInfo;
3220 void VBA_InsertModule( ScDocument& rDoc, SCTAB nTab, const OUString& sSource )
3222 ScDocShell& rDocSh = *rDoc.GetDocumentShell();
3223 uno::Reference< script::XLibraryContainer > xLibContainer = rDocSh.GetBasicContainer();
3224 OSL_ENSURE( xLibContainer.is(), "No BasicContainer!" );
3226 uno::Reference< container::XNameContainer > xLib;
3227 if( xLibContainer.is() )
3229 OUString aLibName( u"Standard"_ustr );
3230 #if HAVE_FEATURE_SCRIPTING
3231 if ( rDocSh.GetBasicManager() && !rDocSh.GetBasicManager()->GetName().isEmpty() )
3233 aLibName = rDocSh.GetBasicManager()->GetName();
3235 #endif
3236 uno::Any aLibAny = xLibContainer->getByName( aLibName );
3237 aLibAny >>= xLib;
3239 if( !xLib.is() )
3240 return;
3242 // if the Module with codename exists then find a new name
3243 sal_Int32 nNum = 1;
3244 OUString genModuleName = u"Sheet1"_ustr;
3245 while( xLib->hasByName( genModuleName ) )
3246 genModuleName = "Sheet" + OUString::number( ++nNum );
3248 uno::Any aSourceAny;
3249 OUString sTmpSource = sSource;
3250 if ( sTmpSource.isEmpty() )
3251 sTmpSource = "Rem Attribute VBA_ModuleType=VBADocumentModule\nOption VBASupport 1\n";
3252 aSourceAny <<= sTmpSource;
3253 uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY );
3254 if ( xVBAModuleInfo.is() )
3256 rDoc.SetCodeName( nTab, genModuleName );
3257 script::ModuleInfo sModuleInfo = lcl_InitModuleInfo( rDocSh, genModuleName );
3258 xVBAModuleInfo->insertModuleInfo( genModuleName, sModuleInfo );
3259 xLib->insertByName( genModuleName, aSourceAny );
3263 void VBA_DeleteModule( ScDocShell& rDocSh, const OUString& sModuleName )
3265 uno::Reference< script::XLibraryContainer > xLibContainer = rDocSh.GetBasicContainer();
3266 OSL_ENSURE( xLibContainer.is(), "No BasicContainer!" );
3268 uno::Reference< container::XNameContainer > xLib;
3269 if( xLibContainer.is() )
3271 OUString aLibName( u"Standard"_ustr );
3272 #if HAVE_FEATURE_SCRIPTING
3273 if ( rDocSh.GetBasicManager() && !rDocSh.GetBasicManager()->GetName().isEmpty() )
3275 aLibName = rDocSh.GetBasicManager()->GetName();
3277 #endif
3278 uno::Any aLibAny = xLibContainer->getByName( aLibName );
3279 aLibAny >>= xLib;
3281 if( xLib.is() )
3283 uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY );
3284 if( xLib->hasByName( sModuleName ) )
3285 xLib->removeByName( sModuleName );
3286 if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo(sModuleName) )
3287 xVBAModuleInfo->removeModuleInfo( sModuleName );
3292 bool ScDocFunc::InsertTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi )
3294 bool bSuccess = false;
3295 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
3297 ScDocShellModificator aModificator( rDocShell );
3299 ScDocument& rDoc = rDocShell.GetDocument();
3301 // Strange loop, also basic is loaded too early ( InsertTable )
3302 // is called via the xml import for sheets in described in ODF
3303 bool bInsertDocModule = false;
3305 if( !rDocShell.GetDocument().IsImportingXML() )
3307 bInsertDocModule = rDoc.IsInVBAMode();
3309 if ( bInsertDocModule || ( bRecord && !rDoc.IsUndoEnabled() ) )
3310 bRecord = false;
3312 if (bRecord)
3313 rDoc.BeginDrawUndo(); // InsertTab generates SdrUndoNewPage
3315 SCTAB nTabCount = rDoc.GetTableCount();
3316 bool bAppend = ( nTab >= nTabCount );
3317 if ( bAppend )
3318 nTab = nTabCount; // important for Undo
3320 if (rDoc.InsertTab( nTab, rName ))
3322 if (bRecord)
3323 rDocShell.GetUndoManager()->AddUndoAction(
3324 std::make_unique<ScUndoInsertTab>( &rDocShell, nTab, bAppend, rName));
3325 // Update views:
3326 // Only insert vba modules if vba mode ( and not currently importing XML )
3327 if( bInsertDocModule )
3329 VBA_InsertModule( rDoc, nTab, OUString() );
3331 rDocShell.Broadcast( ScTablesHint( SC_TAB_INSERTED, nTab ) );
3333 rDocShell.PostPaintExtras();
3334 aModificator.SetDocumentModified();
3335 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3336 bSuccess = true;
3338 else if (!bApi)
3339 rDocShell.ErrorMessage(STR_TABINSERT_ERROR);
3341 return bSuccess;
3344 bool ScDocFunc::DeleteTable( SCTAB nTab, bool bRecord )
3346 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
3348 ScDocShellModificator aModificator( rDocShell );
3350 bool bSuccess = false;
3351 ScDocument& rDoc = rDocShell.GetDocument();
3352 bool bVbaEnabled = rDoc.IsInVBAMode();
3353 if (bRecord && !rDoc.IsUndoEnabled())
3354 bRecord = false;
3355 if ( bVbaEnabled )
3356 bRecord = false;
3357 bool bWasLinked = rDoc.IsLinked(nTab);
3358 ScDocumentUniquePtr pUndoDoc;
3359 std::unique_ptr<ScRefUndoData> pUndoData;
3360 if (bRecord)
3362 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
3363 SCTAB nCount = rDoc.GetTableCount();
3365 pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true ); // only nTab with Flags
3366 pUndoDoc->AddUndoTab( 0, nCount-1 ); // all sheets for references
3368 rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, InsertDeleteFlags::ALL,false, *pUndoDoc );
3369 OUString aOldName;
3370 rDoc.GetName( nTab, aOldName );
3371 pUndoDoc->RenameTab( nTab, aOldName );
3372 if (bWasLinked)
3373 pUndoDoc->SetLink( nTab, rDoc.GetLinkMode(nTab), rDoc.GetLinkDoc(nTab),
3374 rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab),
3375 rDoc.GetLinkTab(nTab),
3376 rDoc.GetLinkRefreshDelay(nTab) );
3378 if ( rDoc.IsScenario(nTab) )
3380 pUndoDoc->SetScenario( nTab, true );
3381 OUString aComment;
3382 Color aColor;
3383 ScScenarioFlags nScenFlags;
3384 rDoc.GetScenarioData( nTab, aComment, aColor, nScenFlags );
3385 pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags );
3386 bool bActive = rDoc.IsActiveScenario( nTab );
3387 pUndoDoc->SetActiveScenario( nTab, bActive );
3389 pUndoDoc->SetVisible( nTab, rDoc.IsVisible( nTab ) );
3390 pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) );
3391 auto pSheetEvents = rDoc.GetSheetEvents( nTab );
3392 pUndoDoc->SetSheetEvents( nTab, std::unique_ptr<ScSheetEvents>(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) );
3394 // Drawing-Layer has to take care of its own undo!!!
3395 rDoc.BeginDrawUndo(); // DeleteTab generates SdrUndoDelPage
3397 pUndoData.reset(new ScRefUndoData( &rDoc ));
3400 if (rDoc.DeleteTab(nTab))
3402 if (bRecord)
3404 vector<SCTAB> theTabs;
3405 theTabs.push_back(nTab);
3406 rDocShell.GetUndoManager()->AddUndoAction(
3407 std::make_unique<ScUndoDeleteTab>( &rDocShell, theTabs, std::move(pUndoDoc), std::move(pUndoData) ));
3409 // Update views:
3410 if( bVbaEnabled )
3412 OUString sCodeName;
3413 if( rDoc.GetCodeName( nTab, sCodeName ) )
3415 VBA_DeleteModule( rDocShell, sCodeName );
3418 rDocShell.Broadcast( ScTablesHint( SC_TAB_DELETED, nTab ) );
3420 if (bWasLinked)
3422 rDocShell.UpdateLinks(); // update Link-Manager
3423 SfxBindings* pBindings = rDocShell.GetViewBindings();
3424 if (pBindings)
3425 pBindings->Invalidate(SID_LINKS);
3428 rDocShell.PostPaintExtras();
3429 aModificator.SetDocumentModified();
3431 SfxApplication* pSfxApp = SfxGetpApp(); // Navigator
3432 pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3433 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
3434 pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) );
3435 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
3437 bSuccess = true;
3439 return bSuccess;
3442 void ScDocFunc::SetTableVisible( SCTAB nTab, bool bVisible, bool bApi )
3444 ScDocument& rDoc = rDocShell.GetDocument();
3445 bool bUndo(rDoc.IsUndoEnabled());
3446 if ( rDoc.IsVisible( nTab ) == bVisible )
3447 return; // nothing to do - ok
3449 if ( !rDoc.IsDocEditable() )
3451 if (!bApi)
3452 rDocShell.ErrorMessage(STR_PROTECTIONERR);
3453 return;
3456 ScDocShellModificator aModificator( rDocShell );
3458 if ( !bVisible && !rDoc.IsImportingXML() ) // #i57869# allow hiding in any order for loading
3460 // do not disable all sheets
3462 sal_uInt16 nVisCount = 0;
3463 SCTAB nCount = rDoc.GetTableCount();
3464 for (SCTAB i=0; i<nCount && nVisCount<2; i++)
3465 if (rDoc.IsVisible(i))
3466 ++nVisCount;
3468 if (nVisCount <= 1)
3470 if (!bApi)
3471 rDocShell.ErrorMessage(STR_PROTECTIONERR); //! separate error message?
3472 return;
3476 rDoc.SetVisible( nTab, bVisible );
3477 if (bUndo)
3479 std::vector<SCTAB> undoTabs { nTab };
3480 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( &rDocShell, std::move(undoTabs), bVisible ) );
3483 // update views
3484 if (!bVisible)
3485 rDocShell.Broadcast( ScTablesHint( SC_TAB_HIDDEN, nTab ) );
3487 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3488 rDocShell.PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras);
3489 aModificator.SetDocumentModified();
3492 bool ScDocFunc::SetLayoutRTL( SCTAB nTab, bool bRTL )
3494 ScDocument& rDoc = rDocShell.GetDocument();
3495 bool bUndo(rDoc.IsUndoEnabled());
3496 if ( rDoc.IsLayoutRTL( nTab ) == bRTL )
3497 return true; // nothing to do - ok
3499 //! protection (sheet or document?)
3501 ScDocShellModificator aModificator( rDocShell );
3503 rDoc.SetLayoutRTL( nTab, bRTL, ScObjectHandling::MirrorRTLMode);
3505 if (bUndo)
3507 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoLayoutRTL>( &rDocShell, nTab, bRTL ) );
3510 rDocShell.PostPaint( 0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::All );
3511 aModificator.SetDocumentModified();
3513 SfxBindings* pBindings = rDocShell.GetViewBindings();
3514 if (pBindings)
3516 pBindings->Invalidate( FID_TAB_RTL );
3517 pBindings->Invalidate( SID_ATTR_SIZE );
3520 return true;
3523 bool ScDocFunc::RenameTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi )
3525 ScDocument& rDoc = rDocShell.GetDocument();
3526 if (bRecord && !rDoc.IsUndoEnabled())
3527 bRecord = false;
3528 if ( !rDoc.IsDocEditable() )
3530 if (!bApi)
3531 rDocShell.ErrorMessage(STR_PROTECTIONERR);
3532 return false;
3535 ScDocShellModificator aModificator( rDocShell );
3537 bool bSuccess = false;
3538 OUString sOldName;
3539 rDoc.GetName(nTab, sOldName);
3540 if (rDoc.RenameTab( nTab, rName ))
3542 if (bRecord)
3544 rDocShell.GetUndoManager()->AddUndoAction(
3545 std::make_unique<ScUndoRenameTab>( &rDocShell, nTab, sOldName, rName));
3547 rDocShell.PostPaintExtras();
3548 aModificator.SetDocumentModified();
3549 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3550 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
3552 bSuccess = true;
3554 return bSuccess;
3557 bool ScDocFunc::SetTabBgColor( SCTAB nTab, const Color& rColor, bool bRecord, bool bApi )
3560 ScDocument& rDoc = rDocShell.GetDocument();
3561 if (bRecord && !rDoc.IsUndoEnabled())
3562 bRecord = false;
3563 if ( !rDoc.IsDocEditable() || rDoc.IsTabProtected(nTab) )
3565 if (!bApi)
3566 rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Check to see what this string is...
3567 return false;
3570 Color aOldTabBgColor = rDoc.GetTabBgColor(nTab);
3572 bool bSuccess = false;
3573 rDoc.SetTabBgColor(nTab, rColor);
3574 if ( rDoc.GetTabBgColor(nTab) == rColor)
3575 bSuccess = true;
3576 if (bSuccess)
3578 if (bRecord)
3580 rDocShell.GetUndoManager()->AddUndoAction(
3581 std::make_unique<ScUndoTabColor>( &rDocShell, nTab, aOldTabBgColor, rColor));
3583 rDocShell.PostPaintExtras();
3584 ScDocShellModificator aModificator( rDocShell );
3585 aModificator.SetDocumentModified();
3586 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) );
3588 bSuccess = true;
3590 return bSuccess;
3593 bool ScDocFunc::SetTabBgColor(
3594 ScUndoTabColorInfo::List& rUndoTabColorList, bool bApi )
3596 ScDocument& rDoc = rDocShell.GetDocument();
3597 bool bRecord = true;
3598 if (!rDoc.IsUndoEnabled())
3599 bRecord = false;
3601 if ( !rDoc.IsDocEditable() )
3603 if (!bApi)
3604 rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Get a better String Error...
3605 return false;
3608 sal_uInt16 nTab;
3609 Color aNewTabBgColor;
3610 bool bSuccess = true;
3611 size_t nTabProtectCount = 0;
3612 size_t nTabListCount = rUndoTabColorList.size();
3613 for ( size_t i = 0; i < nTabListCount; ++i )
3615 ScUndoTabColorInfo& rInfo = rUndoTabColorList[i];
3616 nTab = rInfo.mnTabId;
3617 if ( !rDoc.IsTabProtected(nTab) )
3619 aNewTabBgColor = rInfo.maNewTabBgColor;
3620 rInfo.maOldTabBgColor = rDoc.GetTabBgColor(nTab);
3621 rDoc.SetTabBgColor(nTab, aNewTabBgColor);
3622 if ( rDoc.GetTabBgColor(nTab) != aNewTabBgColor)
3624 bSuccess = false;
3625 break;
3628 else
3630 nTabProtectCount++;
3634 if ( nTabProtectCount == nTabListCount )
3636 if (!bApi)
3637 rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Get a better String Error...
3638 return false;
3641 if (bSuccess)
3643 if (bRecord)
3645 rDocShell.GetUndoManager()->AddUndoAction(
3646 std::make_unique<ScUndoTabColor>( &rDocShell, std::vector(rUndoTabColorList)));
3648 rDocShell.PostPaintExtras();
3649 ScDocShellModificator aModificator( rDocShell );
3650 aModificator.SetDocumentModified();
3652 return bSuccess;
3655 //! SetWidthOrHeight - duplicated in ViewFunc !!!!!!
3656 //! Problems:
3657 //! - Optimal height of text cells is different for a printer and a screen
3658 //! - Optimal width needs a selection in order to take only selected cells into account
3660 static sal_uInt16 lcl_GetOptimalColWidth( ScDocShell& rDocShell, SCCOL nCol, SCTAB nTab )
3662 ScSizeDeviceProvider aProv(&rDocShell);
3663 OutputDevice* pDev = aProv.GetDevice(); // has pixel MapMode
3664 double nPPTX = aProv.GetPPTX();
3665 double nPPTY = aProv.GetPPTY();
3667 ScDocument& rDoc = rDocShell.GetDocument();
3668 Fraction aOne(1,1);
3669 sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, pDev, nPPTX, nPPTY, aOne, aOne,
3670 false/*bFormula*/ );
3672 return nTwips;
3675 bool ScDocFunc::SetWidthOrHeight(
3676 bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, SCTAB nTab,
3677 ScSizeMode eMode, sal_uInt16 nSizeTwips, bool bRecord, bool bApi )
3679 ScDocShellModificator aModificator( rDocShell );
3681 if (rRanges.empty())
3682 return true;
3684 ScDocument& rDoc = rDocShell.GetDocument();
3685 if ( bRecord && !rDoc.IsUndoEnabled() )
3686 bRecord = false;
3688 // import into read-only document is possible
3689 if ( !rDoc.IsChangeReadOnlyEnabled() && !rDocShell.IsEditable() )
3691 if (!bApi)
3692 rDocShell.ErrorMessage(STR_PROTECTIONERR); //! separate error message?
3693 return false;
3696 SCCOLROW nStart = rRanges[0].mnStart;
3697 SCCOLROW nEnd = rRanges[0].mnEnd;
3699 if ( eMode == SC_SIZE_OPTIMAL )
3701 //! Option "Show formulas" - but where to get them from?
3704 ScDocumentUniquePtr pUndoDoc;
3705 std::unique_ptr<ScOutlineTable> pUndoTab;
3706 std::vector<sc::ColRowSpan> aUndoRanges;
3708 if ( bRecord )
3710 rDoc.BeginDrawUndo(); // Drawing Updates
3712 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
3713 if (bWidth)
3715 pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
3716 rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab, static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
3718 else
3720 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
3721 rDoc.CopyToDocument( 0, static_cast<SCROW>(nStart), nTab, rDoc.MaxCol(), static_cast<SCROW>(nEnd), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
3724 aUndoRanges = rRanges;
3726 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab );
3727 if (pTable)
3728 pUndoTab.reset(new ScOutlineTable( *pTable ));
3731 bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT;
3732 bool bOutline = false;
3734 for (const sc::ColRowSpan& rRange : rRanges)
3736 SCCOLROW nStartNo = rRange.mnStart;
3737 SCCOLROW nEndNo = rRange.mnEnd;
3739 if ( !bWidth ) // deal with heights always in blocks
3741 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
3743 bool bAll = ( eMode==SC_SIZE_OPTIMAL );
3744 if (!bAll)
3746 // delete for all that have CRFlags::ManualSize enabled
3747 // then SetOptimalHeight with bShrink = FALSE
3748 for (SCROW nRow=nStartNo; nRow<=nEndNo; nRow++)
3750 CRFlags nOld = rDoc.GetRowFlags(nRow,nTab);
3751 SCROW nLastRow = -1;
3752 bool bHidden = rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow);
3753 if ( !bHidden && ( nOld & CRFlags::ManualSize ) )
3754 rDoc.SetRowFlags( nRow, nTab, nOld & ~CRFlags::ManualSize );
3758 ScSizeDeviceProvider aProv( &rDocShell );
3759 Fraction aOne(1,1);
3760 sc::RowHeightContext aCxt(rDoc.MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice());
3761 aCxt.setForceAutoSize(bAll);
3762 rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, bApi);
3764 if (bAll)
3765 rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
3767 // Manual flag will be set already in SetOptimalHeight if bAll=true
3768 // (it is on when Extra-Height, otherwise off).
3770 else if ( eMode==SC_SIZE_DIRECT || eMode==SC_SIZE_ORIGINAL )
3772 if (nSizeTwips)
3774 rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips );
3775 rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true ); // height was set manually
3777 if ( eMode != SC_SIZE_ORIGINAL )
3778 rDoc.ShowRows( nStartNo, nEndNo, nTab, nSizeTwips != 0 );
3780 else if ( eMode==SC_SIZE_SHOW )
3782 rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
3785 else // Column widths
3787 for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++)
3789 if ( eMode != SC_SIZE_VISOPT || !rDoc.ColHidden(nCol, nTab) )
3791 sal_uInt16 nThisSize = nSizeTwips;
3793 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
3794 nThisSize = nSizeTwips +
3795 lcl_GetOptimalColWidth( rDocShell, nCol, nTab );
3796 if ( nThisSize )
3797 rDoc.SetColWidth( nCol, nTab, nThisSize );
3799 if ( eMode != SC_SIZE_ORIGINAL )
3800 rDoc.ShowCol( nCol, nTab, bShow );
3805 // adjust outlines
3807 if ( eMode != SC_SIZE_ORIGINAL )
3809 if (bWidth)
3810 bOutline = bOutline || rDoc.UpdateOutlineCol(
3811 static_cast<SCCOL>(nStartNo),
3812 static_cast<SCCOL>(nEndNo), nTab, bShow );
3813 else
3814 bOutline = bOutline || rDoc.UpdateOutlineRow(
3815 static_cast<SCROW>(nStartNo),
3816 static_cast<SCROW>(nEndNo), nTab, bShow );
3819 rDoc.SetDrawPageSize(nTab);
3821 if (!bOutline)
3822 pUndoTab.reset();
3824 if (bRecord)
3826 ScMarkData aMark(rDoc.GetSheetLimits());
3827 aMark.SelectOneTable( nTab );
3828 rDocShell.GetUndoManager()->AddUndoAction(
3829 std::make_unique<ScUndoWidthOrHeight>(
3830 &rDocShell, aMark, nStart, nTab, nEnd, nTab, std::move(pUndoDoc),
3831 std::move(aUndoRanges), std::move(pUndoTab), eMode, nSizeTwips, bWidth));
3834 rDoc.UpdatePageBreaks( nTab );
3836 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
3837 if (pViewSh)
3838 pViewSh->OnLOKSetWidthOrHeight(nStart, bWidth);
3840 rDocShell.PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::All);
3841 aModificator.SetDocumentModified();
3843 return false;
3846 bool ScDocFunc::InsertPageBreak( bool bColumn, const ScAddress& rPos,
3847 bool bRecord, bool bSetModified )
3849 ScDocShellModificator aModificator( rDocShell );
3851 ScDocument& rDoc = rDocShell.GetDocument();
3852 if (bRecord && !rDoc.IsUndoEnabled())
3853 bRecord = false;
3854 SCTAB nTab = rPos.Tab();
3855 SfxBindings* pBindings = rDocShell.GetViewBindings();
3857 SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(rPos.Col()) :
3858 static_cast<SCCOLROW>(rPos.Row());
3859 if (nPos == 0)
3860 return false; // first column / row
3862 ScBreakType nBreak = bColumn ?
3863 rDoc.HasColBreak(static_cast<SCCOL>(nPos), nTab) :
3864 rDoc.HasRowBreak(static_cast<SCROW>(nPos), nTab);
3865 if (nBreak & ScBreakType::Manual)
3866 return true;
3868 if (bRecord)
3869 rDocShell.GetUndoManager()->AddUndoAction(
3870 std::make_unique<ScUndoPageBreak>( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, true ) );
3872 if (bColumn)
3873 rDoc.SetColBreak(static_cast<SCCOL>(nPos), nTab, false, true);
3874 else
3875 rDoc.SetRowBreak(static_cast<SCROW>(nPos), nTab, false, true);
3877 rDoc.InvalidatePageBreaks(nTab);
3878 rDoc.UpdatePageBreaks( nTab );
3880 rDoc.SetStreamValid(nTab, false);
3882 if (bColumn)
3884 rDocShell.PostPaint( static_cast<SCCOL>(nPos)-1, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
3885 if (pBindings)
3887 pBindings->Invalidate( FID_INS_COLBRK );
3888 pBindings->Invalidate( FID_DEL_COLBRK );
3891 else
3893 rDocShell.PostPaint( 0, static_cast<SCROW>(nPos)-1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
3894 if (pBindings)
3896 pBindings->Invalidate( FID_INS_ROWBRK );
3897 pBindings->Invalidate( FID_DEL_ROWBRK );
3900 if (pBindings)
3901 pBindings->Invalidate( FID_DEL_MANUALBREAKS );
3903 if (bSetModified)
3904 aModificator.SetDocumentModified();
3906 return true;
3909 bool ScDocFunc::RemovePageBreak( bool bColumn, const ScAddress& rPos,
3910 bool bRecord, bool bSetModified )
3912 ScDocShellModificator aModificator( rDocShell );
3914 ScDocument& rDoc = rDocShell.GetDocument();
3915 if (bRecord && !rDoc.IsUndoEnabled())
3916 bRecord = false;
3917 SCTAB nTab = rPos.Tab();
3918 SfxBindings* pBindings = rDocShell.GetViewBindings();
3920 SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(rPos.Col()) :
3921 static_cast<SCCOLROW>(rPos.Row());
3923 ScBreakType nBreak;
3924 if (bColumn)
3925 nBreak = rDoc.HasColBreak(static_cast<SCCOL>(nPos), nTab);
3926 else
3927 nBreak = rDoc.HasRowBreak(static_cast<SCROW>(nPos), nTab);
3928 if (!(nBreak & ScBreakType::Manual))
3929 // There is no manual break.
3930 return false;
3932 if (bRecord)
3933 rDocShell.GetUndoManager()->AddUndoAction(
3934 std::make_unique<ScUndoPageBreak>( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, false ) );
3936 if (bColumn)
3937 rDoc.RemoveColBreak(static_cast<SCCOL>(nPos), nTab, false, true);
3938 else
3939 rDoc.RemoveRowBreak(static_cast<SCROW>(nPos), nTab, false, true);
3941 rDoc.UpdatePageBreaks( nTab );
3943 rDoc.SetStreamValid(nTab, false);
3945 if (bColumn)
3947 rDocShell.PostPaint( static_cast<SCCOL>(nPos)-1, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
3948 if (pBindings)
3950 pBindings->Invalidate( FID_INS_COLBRK );
3951 pBindings->Invalidate( FID_DEL_COLBRK );
3954 else
3956 rDocShell.PostPaint( 0, nPos-1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid );
3957 if (pBindings)
3959 pBindings->Invalidate( FID_INS_ROWBRK );
3960 pBindings->Invalidate( FID_DEL_ROWBRK );
3963 if (pBindings)
3964 pBindings->Invalidate( FID_DEL_MANUALBREAKS );
3966 if (bSetModified)
3967 aModificator.SetDocumentModified();
3969 return true;
3972 void ScDocFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect )
3974 ScDocument& rDoc = rDocShell.GetDocument();
3976 std::unique_ptr<ScTableProtection> p;
3977 if (!rProtect.isProtected() && rDoc.IsUndoEnabled())
3979 // In case of unprotecting, use a copy of passed ScTableProtection object for undo
3980 p = std::make_unique<ScTableProtection>(rProtect);
3982 rDoc.SetTabProtection(nTab, &rProtect);
3983 if (rDoc.IsUndoEnabled())
3985 if (!p)
3987 // For protection case, use a copy of resulting ScTableProtection for undo
3988 const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab);
3989 p = std::make_unique<ScTableProtection>(*pProtect);
3991 rDocShell.GetUndoManager()->AddUndoAction(
3992 std::make_unique<ScUndoTabProtect>(&rDocShell, nTab, std::move(p)));
3993 // ownership of unique_ptr now transferred to ScUndoTabProtect.
3995 for (SfxViewFrame* fr = SfxViewFrame::GetFirst(&rDocShell); fr;
3996 fr = SfxViewFrame::GetNext(*fr, &rDocShell))
3997 if (ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(fr->GetViewShell()))
3998 pTabViewShell->SetTabProtectionSymbol(nTab, rProtect.isProtected());
3999 rDocShell.PostPaintGridAll();
4000 ScDocShellModificator aModificator(rDocShell);
4001 aModificator.SetDocumentModified();
4004 void ScDocFunc::ProtectDocument(const ScDocProtection& rProtect)
4006 ScDocument& rDoc = rDocShell.GetDocument();
4008 std::unique_ptr<ScDocProtection> p;
4009 if (!rProtect.isProtected() && rDoc.IsUndoEnabled())
4011 // In case of unprotecting, use a copy of passed ScTableProtection object for undo
4012 p = std::make_unique<ScDocProtection>(rProtect);
4014 rDoc.SetDocProtection(&rProtect);
4015 if (rDoc.IsUndoEnabled())
4017 if (!p)
4019 // For protection case, use a copy of resulting ScTableProtection for undo
4020 ScDocProtection* pProtect = rDoc.GetDocProtection();
4021 p = std::make_unique<ScDocProtection>(*pProtect);
4023 rDocShell.GetUndoManager()->AddUndoAction(
4024 std::make_unique<ScUndoDocProtect>(&rDocShell, std::move(p)));
4025 // ownership of unique_ptr now transferred to ScUndoTabProtect.
4028 rDocShell.PostPaintGridAll();
4029 ScDocShellModificator aModificator(rDocShell);
4030 aModificator.SetDocumentModified();
4033 bool ScDocFunc::Protect( SCTAB nTab, const OUString& rPassword )
4035 if (nTab == TABLEID_DOC)
4037 // document protection
4038 ScDocProtection aProtection;
4039 aProtection.setProtected(true);
4040 aProtection.setPassword(rPassword);
4041 ProtectDocument(aProtection);
4044 else
4046 // sheet protection
4048 const ScTableProtection* pOldProtection = rDocShell.GetDocument().GetTabProtection(nTab);
4049 ::std::unique_ptr<ScTableProtection> pNewProtection(pOldProtection ? new ScTableProtection(*pOldProtection) : new ScTableProtection());
4050 pNewProtection->setProtected(true);
4051 pNewProtection->setPassword(rPassword);
4052 ProtectSheet(nTab, *pNewProtection);
4054 return true;
4057 bool ScDocFunc::Unprotect( SCTAB nTab, const OUString& rPassword, bool bApi )
4059 ScDocument& rDoc = rDocShell.GetDocument();
4061 if (nTab == TABLEID_DOC)
4063 // document protection
4065 ScDocProtection* pDocProtect = rDoc.GetDocProtection();
4066 if (!pDocProtect || !pDocProtect->isProtected())
4067 // already unprotected (should not happen)!
4068 return true;
4070 if (!pDocProtect->verifyPassword(rPassword))
4072 if (!bApi)
4074 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
4075 VclMessageType::Info, VclButtonsType::Ok,
4076 ScResId(SCSTR_WRONGPASSWORD)));
4077 xInfoBox->run();
4079 return false;
4082 ScDocProtection aNewProtection(*pDocProtect);
4083 aNewProtection.setProtected(false);
4084 ProtectDocument(aNewProtection);
4087 else
4089 // sheet protection
4091 const ScTableProtection* pTabProtect = rDoc.GetTabProtection(nTab);
4092 if (!pTabProtect || !pTabProtect->isProtected())
4093 // already unprotected (should not happen)!
4094 return true;
4095 if (!pTabProtect->verifyPassword(rPassword))
4097 if (!bApi)
4099 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
4100 VclMessageType::Info, VclButtonsType::Ok,
4101 ScResId(SCSTR_WRONGPASSWORD)));
4102 xInfoBox->run();
4104 return false;
4107 ScTableProtection aNewProtection(*pTabProtect);
4108 aNewProtection.setProtected(false);
4109 ProtectSheet(nTab, aNewProtection);
4112 return true;
4115 void ScDocFunc::ClearItems( const ScMarkData& rMark, const sal_uInt16* pWhich, bool bApi )
4117 ScDocShellModificator aModificator( rDocShell );
4119 ScDocument& rDoc = rDocShell.GetDocument();
4120 bool bUndo (rDoc.IsUndoEnabled());
4121 ScEditableTester aTester( rDoc, rMark );
4122 if (!aTester.IsEditable())
4124 if (!bApi)
4125 rDocShell.ErrorMessage(aTester.GetMessageId());
4126 return;
4129 // #i12940# ClearItems is called (from setPropertyToDefault) directly with uno object's cached
4130 // MarkData (GetMarkData), so rMark must be changed to multi selection for ClearSelectionItems
4131 // here.
4133 ScMarkData aMultiMark = rMark;
4134 aMultiMark.SetMarking(false); // for MarkToMulti
4135 aMultiMark.MarkToMulti();
4136 const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea();
4138 if (bUndo)
4140 SCTAB nStartTab = aMarkRange.aStart.Tab();
4141 SCTAB nEndTab = aMarkRange.aEnd.Tab();
4143 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
4144 pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
4145 rDoc.CopyToDocument( aMarkRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aMultiMark );
4147 rDocShell.GetUndoManager()->AddUndoAction(
4148 std::make_unique<ScUndoClearItems>( &rDocShell, aMultiMark, std::move(pUndoDoc), pWhich ) );
4151 rDoc.ClearSelectionItems( pWhich, aMultiMark );
4153 rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
4154 aModificator.SetDocumentModified();
4156 //! Bindings-Invalidate etc.?
4159 bool ScDocFunc::ChangeIndent( const ScMarkData& rMark, bool bIncrement, bool bApi )
4161 ScDocShellModificator aModificator( rDocShell );
4163 ScDocument& rDoc = rDocShell.GetDocument();
4164 bool bUndo(rDoc.IsUndoEnabled());
4165 ScEditableTester aTester( rDoc, rMark );
4166 if (!aTester.IsEditable())
4168 if (!bApi)
4169 rDocShell.ErrorMessage(aTester.GetMessageId());
4170 return false;
4173 const ScRange& aMarkRange = rMark.GetMultiMarkArea();
4175 if (bUndo)
4177 SCTAB nStartTab = aMarkRange.aStart.Tab();
4178 SCTAB nTabCount = rDoc.GetTableCount();
4180 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
4181 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
4182 for (const auto& rTab : rMark)
4184 if (rTab >= nTabCount)
4185 break;
4187 if (rTab != nStartTab)
4188 pUndoDoc->AddUndoTab( rTab, rTab );
4191 ScRange aCopyRange = aMarkRange;
4192 aCopyRange.aStart.SetTab(0);
4193 aCopyRange.aEnd.SetTab(nTabCount-1);
4194 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &rMark );
4196 rDocShell.GetUndoManager()->AddUndoAction(
4197 std::make_unique<ScUndoIndent>( &rDocShell, rMark, std::move(pUndoDoc), bIncrement ) );
4200 rDoc.ChangeSelectionIndent( bIncrement, rMark );
4202 rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE );
4203 aModificator.SetDocumentModified();
4205 SfxBindings* pBindings = rDocShell.GetViewBindings();
4206 if (pBindings)
4208 pBindings->Invalidate( SID_ALIGNLEFT ); // ChangeIndent aligns left
4209 pBindings->Invalidate( SID_ALIGNRIGHT );
4210 pBindings->Invalidate( SID_ALIGNBLOCK );
4211 pBindings->Invalidate( SID_ALIGNCENTERHOR );
4212 pBindings->Invalidate( SID_ATTR_LRSPACE );
4213 pBindings->Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
4214 pBindings->Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
4215 pBindings->Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
4216 pBindings->Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
4217 // pseudo slots for Format menu
4218 pBindings->Invalidate( SID_ALIGN_ANY_HDEFAULT );
4219 pBindings->Invalidate( SID_ALIGN_ANY_LEFT );
4220 pBindings->Invalidate( SID_ALIGN_ANY_HCENTER );
4221 pBindings->Invalidate( SID_ALIGN_ANY_RIGHT );
4222 pBindings->Invalidate( SID_ALIGN_ANY_JUSTIFIED );
4225 return true;
4228 bool ScDocFunc::AutoFormat( const ScRange& rRange, const ScMarkData* pTabMark,
4229 sal_uInt16 nFormatNo, bool bApi )
4231 ScDocShellModificator aModificator( rDocShell );
4233 ScDocument& rDoc = rDocShell.GetDocument();
4234 SCCOL nStartCol = rRange.aStart.Col();
4235 SCROW nStartRow = rRange.aStart.Row();
4236 SCTAB nStartTab = rRange.aStart.Tab();
4237 SCCOL nEndCol = rRange.aEnd.Col();
4238 SCROW nEndRow = rRange.aEnd.Row();
4239 SCTAB nEndTab = rRange.aEnd.Tab();
4241 bool bRecord = true;
4242 if (!rDoc.IsUndoEnabled())
4243 bRecord = false;
4244 ScMarkData aMark(rDoc.GetSheetLimits());
4245 if (pTabMark)
4246 aMark = *pTabMark;
4247 else
4249 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4250 aMark.SelectTable( nTab, true );
4253 ScAutoFormat* pAutoFormat = ScGlobal::GetOrCreateAutoFormat();
4254 ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
4255 if ( nFormatNo < pAutoFormat->size() && aTester.IsEditable() )
4257 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4259 bool bSize = pAutoFormat->findByIndex(nFormatNo)->GetIncludeWidthHeight();
4260 if (sal_uInt64(nEndCol - nStartCol + 1) * sal_uInt64(nEndRow - nStartRow + 1) > AUTOFORMAT_WARN_SIZE)
4262 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
4263 VclMessageType::Warning, VclButtonsType::YesNo,
4264 ScResId(STR_AUTOFORMAT_WAIT_WARNING)));
4265 xQueryBox->set_default_response(RET_NO);
4266 if (xQueryBox->run() != RET_YES)
4267 return false;
4270 SCTAB nTabCount = rDoc.GetTableCount();
4271 ScDocumentUniquePtr pUndoDoc;
4272 if ( bRecord )
4274 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4275 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab, bSize, bSize );
4276 for (const auto& rTab : aMark)
4278 if (rTab >= nTabCount)
4279 break;
4281 if (rTab != nStartTab)
4282 pUndoDoc->AddUndoTab( rTab, rTab, bSize, bSize );
4285 ScRange aCopyRange = rRange;
4286 aCopyRange.aStart.SetTab(0);
4287 aCopyRange.aStart.SetTab(nTabCount-1);
4288 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc, &aMark );
4289 if (bSize)
4291 rDoc.CopyToDocument( nStartCol,0,0, nEndCol,rDoc.MaxRow(),nTabCount-1,
4292 InsertDeleteFlags::NONE, false, *pUndoDoc, &aMark );
4293 rDoc.CopyToDocument( 0,nStartRow,0, rDoc.MaxCol(),nEndRow,nTabCount-1,
4294 InsertDeleteFlags::NONE, false, *pUndoDoc, &aMark );
4296 rDoc.BeginDrawUndo();
4299 rDoc.AutoFormat( nStartCol, nStartRow, nEndCol, nEndRow, nFormatNo, aMark );
4301 if (bSize)
4303 std::vector<sc::ColRowSpan> aCols(1, sc::ColRowSpan(nStartCol,nEndCol));
4304 std::vector<sc::ColRowSpan> aRows(1, sc::ColRowSpan(nStartRow,nEndRow));
4306 for (const auto& rTab : aMark)
4308 if (rTab >= nTabCount)
4309 break;
4311 SetWidthOrHeight(true, aCols, rTab, SC_SIZE_VISOPT, STD_EXTRA_WIDTH, false, true);
4312 SetWidthOrHeight(false, aRows, rTab, SC_SIZE_VISOPT, 0, false, false);
4313 rDocShell.PostPaint( 0,0,rTab, rDoc.MaxCol(),rDoc.MaxRow(),rTab,
4314 PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top );
4317 else
4319 for (const auto& rTab : aMark)
4321 if (rTab >= nTabCount)
4322 break;
4324 bool bAdj = AdjustRowHeight( ScRange(nStartCol, nStartRow, rTab,
4325 nEndCol, nEndRow, rTab), false, bApi );
4326 if (bAdj)
4327 rDocShell.PostPaint( 0,nStartRow,rTab, rDoc.MaxCol(),rDoc.MaxRow(),rTab,
4328 PaintPartFlags::Grid | PaintPartFlags::Left );
4329 else
4330 rDocShell.PostPaint( nStartCol, nStartRow, rTab,
4331 nEndCol, nEndRow, rTab, PaintPartFlags::Grid );
4335 if ( bRecord ) // only now is Draw-Undo available
4337 rDocShell.GetUndoManager()->AddUndoAction(
4338 std::make_unique<ScUndoAutoFormat>( &rDocShell, rRange, std::move(pUndoDoc), aMark, bSize, nFormatNo ) );
4341 aModificator.SetDocumentModified();
4343 else if (!bApi)
4344 rDocShell.ErrorMessage(aTester.GetMessageId());
4346 return false;
4349 bool ScDocFunc::EnterMatrix( const ScRange& rRange, const ScMarkData* pTabMark,
4350 const ScTokenArray* pTokenArray, const OUString& rString, bool bApi, bool bEnglish,
4351 const OUString& rFormulaNmsp, const formula::FormulaGrammar::Grammar eGrammar )
4353 if (ScViewData::SelectionFillDOOM( rRange ))
4354 return false;
4356 ScDocShellModificator aModificator( rDocShell );
4358 bool bSuccess = false;
4359 ScDocument& rDoc = rDocShell.GetDocument();
4360 SCCOL nStartCol = rRange.aStart.Col();
4361 SCROW nStartRow = rRange.aStart.Row();
4362 SCTAB nStartTab = rRange.aStart.Tab();
4363 SCCOL nEndCol = rRange.aEnd.Col();
4364 SCROW nEndRow = rRange.aEnd.Row();
4365 SCTAB nEndTab = rRange.aEnd.Tab();
4367 ScMarkData aMark(rDoc.GetSheetLimits());
4368 if (pTabMark)
4369 aMark = *pTabMark;
4370 else
4372 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4373 aMark.SelectTable( nTab, true );
4376 ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
4377 if ( aTester.IsEditable() )
4379 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4381 ScDocumentUniquePtr pUndoDoc;
4383 const bool bUndo(rDoc.IsUndoEnabled());
4384 if (bUndo)
4386 //! take selected sheets into account also when undoing
4387 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4388 pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
4389 rDoc.CopyToDocument( rRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc );
4392 // use TokenArray if given, string (and flags) otherwise
4393 if ( pTokenArray )
4395 rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
4396 aMark, OUString(), pTokenArray, eGrammar);
4398 else if ( rDoc.IsImportingXML() )
4400 ScTokenArray aCode(rDoc);
4401 aCode.AssignXMLString( rString,
4402 ((eGrammar == formula::FormulaGrammar::GRAM_EXTERNAL) ? rFormulaNmsp : OUString()));
4403 rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
4404 aMark, OUString(), &aCode, eGrammar);
4405 rDoc.IncXMLImportedFormulaCount( rString.getLength() );
4407 else if (bEnglish)
4409 ScCompiler aComp( rDoc, rRange.aStart, eGrammar);
4410 std::unique_ptr<ScTokenArray> pCode = aComp.CompileString( rString );
4411 rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
4412 aMark, OUString(), pCode.get(), eGrammar);
4414 else
4415 rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow,
4416 aMark, rString, nullptr, eGrammar);
4418 if (bUndo)
4420 //! take selected sheets into account also when undoing
4421 rDocShell.GetUndoManager()->AddUndoAction(
4422 std::make_unique<ScUndoEnterMatrix>( &rDocShell, rRange, std::move(pUndoDoc), rString ) );
4425 // Err522 painting of DDE-Formulas will be intercepted during interpreting
4426 rDocShell.PostPaint( nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab, PaintPartFlags::Grid );
4427 aModificator.SetDocumentModified();
4429 bSuccess = true;
4431 else if (!bApi)
4432 rDocShell.ErrorMessage(aTester.GetMessageId());
4434 return bSuccess;
4437 bool ScDocFunc::TabOp( const ScRange& rRange, const ScMarkData* pTabMark,
4438 const ScTabOpParam& rParam, bool bRecord, bool bApi )
4440 ScDocShellModificator aModificator( rDocShell );
4442 bool bSuccess = false;
4443 ScDocument& rDoc = rDocShell.GetDocument();
4444 SCCOL nStartCol = rRange.aStart.Col();
4445 SCROW nStartRow = rRange.aStart.Row();
4446 SCTAB nStartTab = rRange.aStart.Tab();
4447 SCCOL nEndCol = rRange.aEnd.Col();
4448 SCROW nEndRow = rRange.aEnd.Row();
4449 SCTAB nEndTab = rRange.aEnd.Tab();
4451 if (bRecord && !rDoc.IsUndoEnabled())
4452 bRecord = false;
4454 ScMarkData aMark(rDoc.GetSheetLimits());
4455 if (pTabMark)
4456 aMark = *pTabMark;
4457 else
4459 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4460 aMark.SelectTable( nTab, true );
4463 ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
4464 if ( aTester.IsEditable() )
4466 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4467 rDoc.SetDirty( rRange, false );
4468 if ( bRecord )
4470 //! take selected sheets into account also when undoing
4471 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
4472 pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab );
4473 rDoc.CopyToDocument( rRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc );
4475 rDocShell.GetUndoManager()->AddUndoAction(
4476 std::make_unique<ScUndoTabOp>( &rDocShell,
4477 nStartCol, nStartRow, nStartTab,
4478 nEndCol, nEndRow, nEndTab, std::move(pUndoDoc),
4479 rParam.aRefFormulaCell,
4480 rParam.aRefFormulaEnd,
4481 rParam.aRefRowCell,
4482 rParam.aRefColCell,
4483 rParam.meMode) );
4485 rDoc.InsertTableOp(rParam, nStartCol, nStartRow, nEndCol, nEndRow, aMark);
4486 rDocShell.PostPaintGridAll();
4487 aModificator.SetDocumentModified();
4488 bSuccess = true;
4490 else if (!bApi)
4491 rDocShell.ErrorMessage(aTester.GetMessageId());
4493 return bSuccess;
4496 static ScDirection DirFromFillDir( FillDir eDir )
4498 if (eDir==FILL_TO_BOTTOM)
4499 return DIR_BOTTOM;
4500 else if (eDir==FILL_TO_RIGHT)
4501 return DIR_RIGHT;
4502 else if (eDir==FILL_TO_TOP)
4503 return DIR_TOP;
4504 else // if (eDir==FILL_TO_LEFT)
4505 return DIR_LEFT;
4508 namespace {
4511 * Expand the fill range as necessary, to allow copying of adjacent cell(s)
4512 * even when those cells are not in the original range.
4514 void adjustFillRangeForAdjacentCopy(const ScDocument &rDoc, ScRange& rRange, FillDir eDir)
4516 switch (eDir)
4518 case FILL_TO_BOTTOM:
4520 if (rRange.aStart.Row() == 0)
4521 return;
4523 if (rRange.aStart.Row() != rRange.aEnd.Row())
4524 return;
4526 // Include the above row.
4527 ScAddress& s = rRange.aStart;
4528 s.SetRow(s.Row()-1);
4530 break;
4531 case FILL_TO_TOP:
4533 if (rRange.aStart.Row() == rDoc.MaxRow())
4534 return;
4536 if (rRange.aStart.Row() != rRange.aEnd.Row())
4537 return;
4539 // Include the row below.
4540 ScAddress& e = rRange.aEnd;
4541 e.SetRow(e.Row()+1);
4543 break;
4544 case FILL_TO_LEFT:
4546 if (rRange.aStart.Col() == rDoc.MaxCol())
4547 return;
4549 if (rRange.aStart.Col() != rRange.aEnd.Col())
4550 return;
4552 // Include the column to the right.
4553 ScAddress& e = rRange.aEnd;
4554 e.SetCol(e.Col()+1);
4556 break;
4557 case FILL_TO_RIGHT:
4559 if (rRange.aStart.Col() == 0)
4560 return;
4562 if (rRange.aStart.Col() != rRange.aEnd.Col())
4563 return;
4565 // Include the column to the left.
4566 ScAddress& s = rRange.aStart;
4567 s.SetCol(s.Col()-1);
4569 break;
4570 default:
4577 bool ScDocFunc::FillSimple( const ScRange& rRange, const ScMarkData* pTabMark,
4578 FillDir eDir, bool bApi )
4580 ScDocShellModificator aModificator( rDocShell );
4581 ScDocument& rDoc = rDocShell.GetDocument();
4583 bool bSuccess = false;
4584 ScRange aRange = rRange;
4585 adjustFillRangeForAdjacentCopy(rDoc, aRange, eDir);
4587 SCCOL nStartCol = aRange.aStart.Col();
4588 SCROW nStartRow = aRange.aStart.Row();
4589 SCTAB nStartTab = aRange.aStart.Tab();
4590 SCCOL nEndCol = aRange.aEnd.Col();
4591 SCROW nEndRow = aRange.aEnd.Row();
4592 SCTAB nEndTab = aRange.aEnd.Tab();
4594 bool bRecord = true;
4595 if (!rDoc.IsUndoEnabled())
4596 bRecord = false;
4598 ScMarkData aMark(rDoc.GetSheetLimits());
4599 if (pTabMark)
4600 aMark = *pTabMark;
4601 else
4603 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4604 aMark.SelectTable( nTab, true );
4607 ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
4608 if ( aTester.IsEditable() )
4610 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4612 ScRange aSourceArea = aRange;
4613 ScRange aDestArea = aRange;
4615 SCCOLROW nCount = 0;
4616 switch (eDir)
4618 case FILL_TO_BOTTOM:
4619 nCount = aSourceArea.aEnd.Row()-aSourceArea.aStart.Row();
4620 aSourceArea.aEnd.SetRow( aSourceArea.aStart.Row() );
4621 break;
4622 case FILL_TO_RIGHT:
4623 nCount = aSourceArea.aEnd.Col()-aSourceArea.aStart.Col();
4624 aSourceArea.aEnd.SetCol( aSourceArea.aStart.Col() );
4625 break;
4626 case FILL_TO_TOP:
4627 nCount = aSourceArea.aEnd.Row()-aSourceArea.aStart.Row();
4628 aSourceArea.aStart.SetRow( aSourceArea.aEnd.Row() );
4629 break;
4630 case FILL_TO_LEFT:
4631 nCount = aSourceArea.aEnd.Col()-aSourceArea.aStart.Col();
4632 aSourceArea.aStart.SetCol( aSourceArea.aEnd.Col() );
4633 break;
4636 ScDocumentUniquePtr pUndoDoc;
4637 if ( bRecord )
4639 SCTAB nTabCount = rDoc.GetTableCount();
4640 SCTAB nDestStartTab = aDestArea.aStart.Tab();
4642 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4643 pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab );
4644 for (const auto& rTab : aMark)
4646 if (rTab >= nTabCount)
4647 break;
4649 if (rTab != nDestStartTab)
4650 pUndoDoc->AddUndoTab( rTab, rTab );
4653 ScRange aCopyRange = aDestArea;
4654 aCopyRange.aStart.SetTab(0);
4655 aCopyRange.aEnd.SetTab(nTabCount-1);
4656 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
4659 sal_uLong nProgCount;
4660 if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
4661 nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
4662 else
4663 nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
4664 nProgCount *= nCount;
4665 ScProgress aProgress( rDoc.GetDocumentShell(),
4666 ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
4668 rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
4669 aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
4670 aMark, nCount, eDir, FILL_SIMPLE );
4671 AdjustRowHeight(aRange, true, bApi);
4673 if ( bRecord ) // only now is Draw-Undo available
4675 rDocShell.GetUndoManager()->AddUndoAction(
4676 std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
4677 eDir, FILL_SIMPLE, FILL_DAY, MAXDOUBLE, 1.0, 1e307) );
4680 rDocShell.PostPaintGridAll();
4681 aModificator.SetDocumentModified();
4683 bSuccess = true;
4685 else if (!bApi)
4686 rDocShell.ErrorMessage(aTester.GetMessageId());
4688 return bSuccess;
4691 bool ScDocFunc::FillSeries( const ScRange& rRange, const ScMarkData* pTabMark,
4692 FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd,
4693 double fStart, double fStep, double fMax,
4694 bool bApi )
4696 ScDocShellModificator aModificator( rDocShell );
4698 bool bSuccess = false;
4699 ScDocument& rDoc = rDocShell.GetDocument();
4700 SCCOL nStartCol = rRange.aStart.Col();
4701 SCROW nStartRow = rRange.aStart.Row();
4702 SCTAB nStartTab = rRange.aStart.Tab();
4703 SCCOL nEndCol = rRange.aEnd.Col();
4704 SCROW nEndRow = rRange.aEnd.Row();
4705 SCTAB nEndTab = rRange.aEnd.Tab();
4707 bool bRecord = true;
4708 if (!rDoc.IsUndoEnabled())
4709 bRecord = false;
4711 ScMarkData aMark(rDoc.GetSheetLimits());
4712 if (pTabMark)
4713 aMark = *pTabMark;
4714 else
4716 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4717 aMark.SelectTable( nTab, true );
4720 ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark );
4721 if ( aTester.IsEditable() )
4723 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4725 ScRange aSourceArea = rRange;
4726 ScRange aDestArea = rRange;
4728 SCSIZE nCount = rDoc.GetEmptyLinesInBlock(
4729 aSourceArea.aStart.Col(), aSourceArea.aStart.Row(), aSourceArea.aStart.Tab(),
4730 aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), aSourceArea.aEnd.Tab(),
4731 DirFromFillDir(eDir) );
4733 // keep at least one row/column as source range
4734 SCSIZE nTotLines = ( eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP ) ?
4735 static_cast<SCSIZE>( aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1 ) :
4736 static_cast<SCSIZE>( aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1 );
4737 if ( nCount >= nTotLines )
4739 assert(nTotLines > 0 && "coverity 2023.12.2");
4740 nCount = nTotLines - 1;
4743 switch (eDir)
4745 case FILL_TO_BOTTOM:
4746 aSourceArea.aEnd.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aEnd.Row() - nCount ) );
4747 break;
4748 case FILL_TO_RIGHT:
4749 aSourceArea.aEnd.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aEnd.Col() - nCount ) );
4750 break;
4751 case FILL_TO_TOP:
4752 aSourceArea.aStart.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aStart.Row() + nCount ) );
4753 break;
4754 case FILL_TO_LEFT:
4755 aSourceArea.aStart.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aStart.Col() + nCount ) );
4756 break;
4759 ScDocumentUniquePtr pUndoDoc;
4760 if ( bRecord )
4762 SCTAB nTabCount = rDoc.GetTableCount();
4763 SCTAB nDestStartTab = aDestArea.aStart.Tab();
4765 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4766 pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab );
4767 for (const auto& rTab : aMark)
4769 if (rTab >= nTabCount)
4770 break;
4772 if (rTab != nDestStartTab)
4773 pUndoDoc->AddUndoTab( rTab, rTab );
4776 rDoc.CopyToDocument(
4777 aDestArea.aStart.Col(), aDestArea.aStart.Row(), 0,
4778 aDestArea.aEnd.Col(), aDestArea.aEnd.Row(), nTabCount-1,
4779 InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
4782 if (aDestArea.aStart.Col() <= aDestArea.aEnd.Col() &&
4783 aDestArea.aStart.Row() <= aDestArea.aEnd.Row())
4785 if ( fStart != MAXDOUBLE )
4787 SCCOL nValX = (eDir == FILL_TO_LEFT) ? aDestArea.aEnd.Col() : aDestArea.aStart.Col();
4788 SCROW nValY = (eDir == FILL_TO_TOP ) ? aDestArea.aEnd.Row() : aDestArea.aStart.Row();
4789 SCTAB nTab = aDestArea.aStart.Tab();
4790 rDoc.SetValue( nValX, nValY, nTab, fStart );
4793 sal_uLong nProgCount;
4794 if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
4795 nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
4796 else
4797 nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
4798 nProgCount *= nCount;
4799 ScProgress aProgress( rDoc.GetDocumentShell(),
4800 ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
4802 rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
4803 aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
4804 aMark, nCount, eDir, eCmd, eDateCmd, fStep, fMax );
4805 AdjustRowHeight(rRange, true, bApi);
4807 rDocShell.PostPaintGridAll();
4808 aModificator.SetDocumentModified();
4811 if ( bRecord ) // only now is Draw-Undo available
4813 rDocShell.GetUndoManager()->AddUndoAction(
4814 std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
4815 eDir, eCmd, eDateCmd, fStart, fStep, fMax) );
4818 bSuccess = true;
4820 else if (!bApi)
4821 rDocShell.ErrorMessage(aTester.GetMessageId());
4823 return bSuccess;
4826 bool ScDocFunc::FillAuto( ScRange& rRange, const ScMarkData* pTabMark,
4827 FillDir eDir, sal_uLong nCount, bool bApi )
4829 return FillAuto( rRange, pTabMark, eDir, FILL_AUTO, FILL_DAY, nCount, 1.0/*fStep*/, MAXDOUBLE/*fMax*/, true/*bRecord*/, bApi );
4832 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 )
4834 ScDocShellModificator aModificator( rDocShell );
4836 ScDocument& rDoc = rDocShell.GetDocument();
4837 SCCOL nStartCol = rRange.aStart.Col();
4838 SCROW nStartRow = rRange.aStart.Row();
4839 SCTAB nStartTab = rRange.aStart.Tab();
4840 SCCOL nEndCol = rRange.aEnd.Col();
4841 SCROW nEndRow = rRange.aEnd.Row();
4842 SCTAB nEndTab = rRange.aEnd.Tab();
4844 if (bRecord && !rDoc.IsUndoEnabled())
4845 bRecord = false;
4847 ScMarkData aMark(rDoc.GetSheetLimits());
4848 if (pTabMark)
4849 aMark = *pTabMark;
4850 else
4852 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++)
4853 aMark.SelectTable( nTab, true );
4856 ScRange aSourceArea = rRange;
4857 ScRange aDestArea = rRange;
4859 switch (eDir)
4861 case FILL_TO_BOTTOM:
4862 aDestArea.aEnd.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aEnd.Row() + nCount ) );
4863 break;
4864 case FILL_TO_TOP:
4865 if (nCount > sal::static_int_cast<sal_uLong>( aSourceArea.aStart.Row() ))
4867 OSL_FAIL("FillAuto: Row < 0");
4868 nCount = aSourceArea.aStart.Row();
4870 aDestArea.aStart.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aStart.Row() - nCount ) );
4871 break;
4872 case FILL_TO_RIGHT:
4873 aDestArea.aEnd.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aEnd.Col() + nCount ) );
4874 break;
4875 case FILL_TO_LEFT:
4876 if (nCount > sal::static_int_cast<sal_uLong>( aSourceArea.aStart.Col() ))
4878 OSL_FAIL("FillAuto: Col < 0");
4879 nCount = aSourceArea.aStart.Col();
4881 aDestArea.aStart.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aStart.Col() - nCount ) );
4882 break;
4883 default:
4884 OSL_FAIL("Wrong direction with FillAuto");
4885 break;
4888 // Test for cell protection
4889 //! Source range can be protected !!!
4890 //! but can't contain matrix fragments !!!
4892 ScEditableTester aTester( rDoc, aDestArea, sc::EditAction::Unknown );
4893 if ( !aTester.IsEditable() )
4895 if (!bApi)
4896 rDocShell.ErrorMessage(aTester.GetMessageId());
4897 return false;
4900 if ( rDoc.HasSelectedBlockMatrixFragment( nStartCol, nStartRow,
4901 nEndCol, nEndRow, aMark ) )
4903 if (!bApi)
4904 rDocShell.ErrorMessage(STR_MATRIXFRAGMENTERR);
4905 return false;
4908 // FID_FILL_... slots should already had been disabled, check here for API
4909 // calls, no message.
4910 if (ScViewData::SelectionFillDOOM( aDestArea))
4911 return false;
4913 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
4915 ScDocumentUniquePtr pUndoDoc;
4916 if ( bRecord )
4918 SCTAB nTabCount = rDoc.GetTableCount();
4919 SCTAB nDestStartTab = aDestArea.aStart.Tab();
4921 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
4922 pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab );
4923 for (const auto& rTab : aMark)
4925 if (rTab >= nTabCount)
4926 break;
4928 if (rTab != nDestStartTab)
4929 pUndoDoc->AddUndoTab( rTab, rTab );
4932 // do not clone note captions in undo document
4933 rDoc.CopyToDocument(
4934 aDestArea.aStart.Col(), aDestArea.aStart.Row(), 0,
4935 aDestArea.aEnd.Col(), aDestArea.aEnd.Row(), nTabCount-1,
4936 InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark );
4939 sal_uLong nProgCount;
4940 if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP)
4941 nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1;
4942 else
4943 nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1;
4944 nProgCount *= nCount;
4945 ScProgress aProgress( rDoc.GetDocumentShell(),
4946 ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
4948 rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(),
4949 aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress,
4950 aMark, nCount, eDir, eCmd, eDateCmd, fStep, fMax );
4952 AdjustRowHeight(aDestArea, true, bApi);
4954 if ( bRecord ) // only now is Draw-Undo available
4956 rDocShell.GetUndoManager()->AddUndoAction(
4957 std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark,
4958 eDir, eCmd, eDateCmd, MAXDOUBLE, fStep, fMax) );
4961 rDocShell.PostPaintGridAll();
4962 aModificator.SetDocumentModified();
4964 rRange = aDestArea; // return destination range (for marking)
4965 return true;
4968 bool ScDocFunc::MergeCells( const ScCellMergeOption& rOption, bool bContents, bool bRecord, bool bApi, bool bEmptyMergedCells /*=false*/ )
4970 using ::std::set;
4972 ScDocShellModificator aModificator( rDocShell );
4974 SCCOL nStartCol = rOption.mnStartCol;
4975 SCROW nStartRow = rOption.mnStartRow;
4976 SCCOL nEndCol = rOption.mnEndCol;
4977 SCROW nEndRow = rOption.mnEndRow;
4978 if ((nStartCol == nEndCol && nStartRow == nEndRow) || rOption.maTabs.empty())
4980 // Nothing to do. Bail out quickly
4981 return true;
4984 ScDocument& rDoc = rDocShell.GetDocument();
4985 SCTAB nTab1 = *rOption.maTabs.begin(), nTab2 = *rOption.maTabs.rbegin();
4987 if (bRecord && !rDoc.IsUndoEnabled())
4988 bRecord = false;
4990 for (const auto& rTab : rOption.maTabs)
4992 ScEditableTester aTester( rDoc, rTab, nStartCol, nStartRow, nEndCol, nEndRow );
4993 if (!aTester.IsEditable())
4995 if (!bApi)
4996 rDocShell.ErrorMessage(aTester.GetMessageId());
4997 return false;
5000 if ( rDoc.HasAttrib( nStartCol, nStartRow, rTab, nEndCol, nEndRow, rTab,
5001 HasAttrFlags::Merged | HasAttrFlags::Overlapped ) )
5003 // "Merge of already merged cells not possible"
5004 if (!bApi)
5005 rDocShell.ErrorMessage(STR_MSSG_MERGECELLS_0);
5006 return false;
5010 ScDocumentUniquePtr pUndoDoc;
5011 bool bNeedContentsUndo = false;
5012 for (const SCTAB nTab : rOption.maTabs)
5014 bool bIsBlockEmpty = ( nStartRow == nEndRow )
5015 ? rDoc.IsEmptyData( nStartCol+1,nStartRow, nEndCol,nEndRow, nTab )
5016 : rDoc.IsEmptyData( nStartCol,nStartRow+1, nStartCol,nEndRow, nTab ) &&
5017 rDoc.IsEmptyData( nStartCol+1,nStartRow, nEndCol,nEndRow, nTab );
5018 bool bNeedContents = bContents && !bIsBlockEmpty;
5019 bool bNeedEmpty = bEmptyMergedCells && !bIsBlockEmpty && !bNeedContents; // if DoMergeContents then cells are emptied
5021 if (bRecord)
5023 // test if the range contains other notes which also implies that we need an undo document
5024 bool bHasNotes = rDoc.HasNote(nTab, nStartCol, nStartRow, nEndCol, nEndRow);
5025 if (!pUndoDoc)
5027 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
5028 pUndoDoc->InitUndo(rDoc, nTab1, nTab2);
5030 // note captions are collected by drawing undo
5031 rDoc.CopyToDocument( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab,
5032 InsertDeleteFlags::ALL|InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc );
5033 if( bHasNotes )
5034 rDoc.BeginDrawUndo();
5037 if (bNeedContents)
5038 rDoc.DoMergeContents( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
5039 else if ( bNeedEmpty )
5040 rDoc.DoEmptyBlock( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
5041 rDoc.DoMerge( nStartCol,nStartRow, nEndCol,nEndRow, nTab );
5043 if (rOption.mbCenter)
5045 rDoc.ApplyAttr( nStartCol, nStartRow, nTab, SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY ) );
5046 rDoc.ApplyAttr( nStartCol, nStartRow, nTab, SvxVerJustifyItem( SvxCellVerJustify::Center, ATTR_VER_JUSTIFY ) );
5049 if ( !AdjustRowHeight( ScRange( 0,nStartRow,nTab, rDoc.MaxCol(),nEndRow,nTab ), true, bApi ) )
5050 rDocShell.PostPaint( nStartCol, nStartRow, nTab,
5051 nEndCol, nEndRow, nTab, PaintPartFlags::Grid );
5052 if (bNeedContents || rOption.mbCenter)
5054 ScRange aRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab);
5055 rDoc.SetDirty(aRange, true);
5058 bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Circles );
5059 if(bDone)
5060 DetectiveMarkInvalid(nTab);
5062 bNeedContentsUndo |= bNeedContents;
5065 if (pUndoDoc)
5067 std::unique_ptr<SdrUndoGroup> pDrawUndo = rDoc.GetDrawLayer() ? rDoc.GetDrawLayer()->GetCalcUndo() : nullptr;
5068 rDocShell.GetUndoManager()->AddUndoAction(
5069 std::make_unique<ScUndoMerge>(&rDocShell, rOption, bNeedContentsUndo, std::move(pUndoDoc), std::move(pDrawUndo)) );
5072 aModificator.SetDocumentModified();
5074 SfxBindings* pBindings = rDocShell.GetViewBindings();
5075 if (pBindings)
5077 pBindings->Invalidate( FID_MERGE_ON );
5078 pBindings->Invalidate( FID_MERGE_OFF );
5079 pBindings->Invalidate( FID_MERGE_TOGGLE );
5082 return true;
5085 bool ScDocFunc::UnmergeCells( const ScRange& rRange, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge )
5087 ScCellMergeOption aOption(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
5088 SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab();
5089 for (SCTAB i = nTab1; i <= nTab2; ++i)
5090 aOption.maTabs.insert(i);
5092 return UnmergeCells(aOption, bRecord, pUndoRemoveMerge);
5095 bool ScDocFunc::UnmergeCells( const ScCellMergeOption& rOption, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge )
5097 using ::std::set;
5099 if (rOption.maTabs.empty())
5100 // Nothing to unmerge.
5101 return true;
5103 ScDocShellModificator aModificator( rDocShell );
5104 ScDocument& rDoc = rDocShell.GetDocument();
5106 if (bRecord && !rDoc.IsUndoEnabled())
5107 bRecord = false;
5109 ScDocument* pUndoDoc = (pUndoRemoveMerge ? pUndoRemoveMerge->GetUndoDoc() : nullptr);
5110 assert( pUndoDoc || !pUndoRemoveMerge );
5111 for (const SCTAB nTab : rOption.maTabs)
5113 ScRange aRange = rOption.getSingleRange(nTab);
5114 if ( !rDoc.HasAttrib(aRange, HasAttrFlags::Merged) )
5115 continue;
5117 ScRange aExtended = aRange;
5118 rDoc.ExtendMerge(aExtended);
5119 ScRange aRefresh = aExtended;
5120 rDoc.ExtendOverlapped(aRefresh);
5122 if (bRecord)
5124 if (!pUndoDoc)
5126 pUndoDoc = new ScDocument( SCDOCMODE_UNDO );
5127 pUndoDoc->InitUndo(rDoc, *rOption.maTabs.begin(), *rOption.maTabs.rbegin());
5129 rDoc.CopyToDocument(aExtended, InsertDeleteFlags::ATTRIB, false, *pUndoDoc);
5132 const SfxPoolItem& rDefAttr = rDoc.GetPool()->GetUserOrPoolDefaultItem( ATTR_MERGE );
5133 ScPatternAttr aPattern(rDoc.getCellAttributeHelper());
5134 aPattern.GetItemSet().Put( rDefAttr );
5135 rDoc.ApplyPatternAreaTab( aRange.aStart.Col(), aRange.aStart.Row(),
5136 aRange.aEnd.Col(), aRange.aEnd.Row(), nTab,
5137 aPattern );
5139 rDoc.RemoveFlagsTab( aExtended.aStart.Col(), aExtended.aStart.Row(),
5140 aExtended.aEnd.Col(), aExtended.aEnd.Row(), nTab,
5141 ScMF::Hor | ScMF::Ver );
5143 rDoc.ExtendMerge( aRefresh, true );
5145 if ( !AdjustRowHeight( aExtended, true, true ) )
5146 rDocShell.PostPaint( aExtended, PaintPartFlags::Grid );
5148 bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Circles );
5149 if(bDone)
5150 DetectiveMarkInvalid(nTab);
5153 if (bRecord)
5155 if (pUndoRemoveMerge)
5157 // If pUndoRemoveMerge was passed, the caller is responsible for
5158 // adding it to Undo. Just add the current option.
5159 pUndoRemoveMerge->AddCellMergeOption( rOption);
5161 else
5163 rDocShell.GetUndoManager()->AddUndoAction(
5164 std::make_unique<ScUndoRemoveMerge>( &rDocShell, rOption, ScDocumentUniquePtr(pUndoDoc) ) );
5167 aModificator.SetDocumentModified();
5169 return true;
5172 void ScDocFunc::ModifyRangeNames( const ScRangeName& rNewRanges, SCTAB nTab )
5174 SetNewRangeNames( std::unique_ptr<ScRangeName>(new ScRangeName(rNewRanges)), true, nTab );
5177 void ScDocFunc::SetNewRangeNames( std::unique_ptr<ScRangeName> pNewRanges, bool bModifyDoc, SCTAB nTab ) // takes ownership of pNewRanges
5179 ScDocShellModificator aModificator( rDocShell );
5181 OSL_ENSURE( pNewRanges, "pNewRanges is 0" );
5182 ScDocument& rDoc = rDocShell.GetDocument();
5183 bool bUndo(rDoc.IsUndoEnabled());
5185 if (bUndo)
5187 ScRangeName* pOld;
5188 if (nTab >=0)
5190 pOld = rDoc.GetRangeName(nTab);
5192 else
5194 pOld = rDoc.GetRangeName();
5196 std::unique_ptr<ScRangeName> pUndoRanges(new ScRangeName(*pOld));
5197 std::unique_ptr<ScRangeName> pRedoRanges(new ScRangeName(*pNewRanges));
5198 rDocShell.GetUndoManager()->AddUndoAction(
5199 std::make_unique<ScUndoRangeNames>( &rDocShell, std::move(pUndoRanges), std::move(pRedoRanges), nTab ) );
5202 // #i55926# While loading XML, formula cells only have a single string token,
5203 // so CompileNameFormula would never find any name (index) tokens, and would
5204 // unnecessarily loop through all cells.
5205 bool bCompile = ( !rDoc.IsImportingXML() && rDoc.GetNamedRangesLockCount() == 0 );
5207 if ( bCompile )
5208 rDoc.PreprocessRangeNameUpdate();
5209 if (nTab >= 0)
5210 rDoc.SetRangeName( nTab, std::move(pNewRanges) ); // takes ownership
5211 else
5212 rDoc.SetRangeName( std::move(pNewRanges) ); // takes ownership
5213 if ( bCompile )
5214 rDoc.CompileHybridFormula();
5216 if (bModifyDoc)
5218 aModificator.SetDocumentModified();
5219 SfxGetpApp()->Broadcast( SfxHint(SfxHintId::ScAreasChanged) );
5223 void ScDocFunc::ModifyAllRangeNames(const std::map<OUString, ScRangeName>& rRangeMap)
5225 ScDocShellModificator aModificator(rDocShell);
5226 ScDocument& rDoc = rDocShell.GetDocument();
5228 if (rDoc.IsUndoEnabled())
5230 std::map<OUString, ScRangeName*> aOldRangeMap;
5231 rDoc.GetRangeNameMap(aOldRangeMap);
5232 rDocShell.GetUndoManager()->AddUndoAction(
5233 std::make_unique<ScUndoAllRangeNames>(&rDocShell, aOldRangeMap, rRangeMap));
5236 rDoc.PreprocessAllRangeNamesUpdate(rRangeMap);
5237 rDoc.SetAllRangeNames(rRangeMap);
5238 rDoc.CompileHybridFormula();
5240 aModificator.SetDocumentModified();
5241 SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
5244 void ScDocFunc::CreateOneName( ScRangeName& rList,
5245 SCCOL nPosX, SCROW nPosY, SCTAB nTab,
5246 SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2,
5247 bool& rCancel, bool bApi )
5249 if (rCancel)
5250 return;
5252 ScDocument& rDoc = rDocShell.GetDocument();
5253 if (rDoc.HasValueData( nPosX, nPosY, nTab ))
5254 return;
5256 OUString aName = rDoc.GetString(nPosX, nPosY, nTab);
5257 ScRangeData::MakeValidName(rDoc, aName);
5258 if (aName.isEmpty())
5259 return;
5261 OUString aContent( ScRange( nX1, nY1, nTab, nX2, nY2, nTab ).Format(
5262 rDoc, ScRefFlags::RANGE_ABS_3D, ScAddress::Details( rDoc.GetAddressConvention(), nPosY, nPosX)));
5264 bool bInsert = false;
5265 ScRangeData* pOld = rList.findByUpperName(ScGlobal::getCharClass().uppercase(aName));
5266 if (pOld)
5268 OUString aOldStr = pOld->GetSymbol();
5269 if (aOldStr != aContent)
5271 if (bApi)
5272 bInsert = true; // don't check via API
5273 else
5275 OUString aTemplate = ScResId( STR_CREATENAME_REPLACE );
5276 OUString aMessage = o3tl::getToken(aTemplate, 0, '#' ) + aName + o3tl::getToken(aTemplate, 1, '#' );
5278 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(),
5279 VclMessageType::Question, VclButtonsType::YesNo,
5280 aMessage));
5281 xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
5282 xQueryBox->set_default_response(RET_YES);
5284 short nResult = xQueryBox->run();
5285 if ( nResult == RET_YES )
5287 rList.erase(*pOld);
5288 bInsert = true;
5290 else if ( nResult == RET_CANCEL )
5291 rCancel = true;
5295 else
5296 bInsert = true;
5298 if (bInsert)
5300 ScRangeData* pData = new ScRangeData( rDoc, aName, aContent,
5301 ScAddress( nPosX, nPosY, nTab));
5302 if (!rList.insert(pData))
5304 OSL_FAIL("nanu?");
5309 bool ScDocFunc::CreateNames( const ScRange& rRange, CreateNameFlags nFlags, bool bApi, SCTAB aTab )
5311 if (nFlags == CreateNameFlags::NONE)
5312 return false; // was nothing
5314 ScDocShellModificator aModificator( rDocShell );
5316 bool bDone = false;
5317 SCCOL nStartCol = rRange.aStart.Col();
5318 SCROW nStartRow = rRange.aStart.Row();
5319 SCCOL nEndCol = rRange.aEnd.Col();
5320 SCROW nEndRow = rRange.aEnd.Row();
5321 SCTAB nTab = rRange.aStart.Tab();
5322 OSL_ENSURE(rRange.aEnd.Tab() == nTab, "CreateNames: multiple tables not possible");
5324 bool bValid = true;
5325 if ( nFlags & ( CreateNameFlags::Top | CreateNameFlags::Bottom ) )
5326 if ( nStartRow == nEndRow )
5327 bValid = false;
5328 if ( nFlags & ( CreateNameFlags::Left | CreateNameFlags::Right ) )
5329 if ( nStartCol == nEndCol )
5330 bValid = false;
5332 if (bValid)
5334 ScDocument& rDoc = rDocShell.GetDocument();
5335 ScRangeName* pNames;
5336 if (aTab >=0)
5337 pNames = rDoc.GetRangeName(nTab);
5338 else
5339 pNames = rDoc.GetRangeName();
5341 if (!pNames)
5342 return false; // shouldn't happen
5343 ScRangeName aNewRanges( *pNames );
5345 bool bTop ( nFlags & CreateNameFlags::Top );
5346 bool bLeft ( nFlags & CreateNameFlags::Left );
5347 bool bBottom( nFlags & CreateNameFlags::Bottom );
5348 bool bRight ( nFlags & CreateNameFlags::Right );
5350 SCCOL nContX1 = nStartCol;
5351 SCROW nContY1 = nStartRow;
5352 SCCOL nContX2 = nEndCol;
5353 SCROW nContY2 = nEndRow;
5355 if ( bTop )
5356 ++nContY1;
5357 if ( bLeft )
5358 ++nContX1;
5359 if ( bBottom )
5360 --nContY2;
5361 if ( bRight )
5362 --nContX2;
5364 bool bCancel = false;
5365 SCCOL i;
5366 SCROW j;
5368 if ( bTop )
5369 for (i=nContX1; i<=nContX2; i++)
5370 CreateOneName( aNewRanges, i,nStartRow,nTab, i,nContY1,i,nContY2, bCancel, bApi );
5371 if ( bLeft )
5372 for (j=nContY1; j<=nContY2; j++)
5373 CreateOneName( aNewRanges, nStartCol,j,nTab, nContX1,j,nContX2,j, bCancel, bApi );
5374 if ( bBottom )
5375 for (i=nContX1; i<=nContX2; i++)
5376 CreateOneName( aNewRanges, i,nEndRow,nTab, i,nContY1,i,nContY2, bCancel, bApi );
5377 if ( bRight )
5378 for (j=nContY1; j<=nContY2; j++)
5379 CreateOneName( aNewRanges, nEndCol,j,nTab, nContX1,j,nContX2,j, bCancel, bApi );
5381 if ( bTop && bLeft )
5382 CreateOneName( aNewRanges, nStartCol,nStartRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
5383 if ( bTop && bRight )
5384 CreateOneName( aNewRanges, nEndCol,nStartRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
5385 if ( bBottom && bLeft )
5386 CreateOneName( aNewRanges, nStartCol,nEndRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
5387 if ( bBottom && bRight )
5388 CreateOneName( aNewRanges, nEndCol,nEndRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi );
5390 ModifyRangeNames( aNewRanges, aTab );
5391 bDone = true;
5395 return bDone;
5398 bool ScDocFunc::InsertNameList( const ScAddress& rStartPos, bool bApi )
5400 ScDocShellModificator aModificator( rDocShell );
5402 bool bDone = false;
5403 ScDocument& rDoc = rDocShell.GetDocument();
5404 const bool bRecord = rDoc.IsUndoEnabled();
5405 SCTAB nTab = rStartPos.Tab();
5407 //local names have higher priority than global names
5408 ScRangeName* pLocalList = rDoc.GetRangeName(nTab);
5409 sal_uInt16 nValidCount = 0;
5410 for (const auto& rEntry : *pLocalList)
5412 const ScRangeData& r = *rEntry.second;
5413 if (!r.HasType(ScRangeData::Type::Database))
5414 ++nValidCount;
5416 ScRangeName* pList = rDoc.GetRangeName();
5417 for (const auto& rEntry : *pList)
5419 const ScRangeData& r = *rEntry.second;
5420 if (!r.HasType(ScRangeData::Type::Database) && !pLocalList->findByUpperName(r.GetUpperName()))
5421 ++nValidCount;
5424 if (nValidCount)
5426 SCCOL nStartCol = rStartPos.Col();
5427 SCROW nStartRow = rStartPos.Row();
5428 SCCOL nEndCol = nStartCol + 1;
5429 SCROW nEndRow = nStartRow + static_cast<SCROW>(nValidCount) - 1;
5431 ScEditableTester aTester( rDoc, nTab, nStartCol,nStartRow, nEndCol,nEndRow );
5432 if (aTester.IsEditable())
5434 ScDocumentUniquePtr pUndoDoc;
5436 if (bRecord)
5438 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
5439 pUndoDoc->InitUndo( rDoc, nTab, nTab );
5440 rDoc.CopyToDocument(nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
5441 InsertDeleteFlags::ALL, false, *pUndoDoc);
5443 rDoc.BeginDrawUndo(); // because of adjusting heights
5446 std::unique_ptr<ScRangeData*[]> ppSortArray(new ScRangeData* [ nValidCount ]);
5447 sal_uInt16 j = 0;
5448 for (const auto& rEntry : *pLocalList)
5450 ScRangeData& r = *rEntry.second;
5451 if (!r.HasType(ScRangeData::Type::Database))
5452 ppSortArray[j++] = &r;
5454 for (const auto& [rName, rxData] : *pList)
5456 ScRangeData& r = *rxData;
5457 if (!r.HasType(ScRangeData::Type::Database) && !pLocalList->findByUpperName(rName))
5458 ppSortArray[j++] = &r;
5460 qsort( static_cast<void*>(ppSortArray.get()), nValidCount, sizeof(ScRangeData*),
5461 &ScRangeData_QsortNameCompare );
5462 OUString aName;
5463 OUStringBuffer aContent;
5464 OUString aFormula;
5465 SCROW nOutRow = nStartRow;
5466 for (j=0; j<nValidCount; j++)
5468 ScRangeData* pData = ppSortArray[j];
5469 pData->GetName(aName);
5470 // adjust relative references to the left column in Excel-compliant way:
5471 pData->UpdateSymbol(aContent, ScAddress( nStartCol, nOutRow, nTab ));
5472 aFormula = "=" + aContent;
5473 ScSetStringParam aParam;
5474 aParam.setTextInput();
5475 rDoc.SetString(ScAddress(nStartCol,nOutRow,nTab), aName, &aParam);
5476 rDoc.SetString(ScAddress(nEndCol,nOutRow,nTab), aFormula, &aParam);
5477 ++nOutRow;
5480 ppSortArray.reset();
5482 if (bRecord)
5484 ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO ));
5485 pRedoDoc->InitUndo( rDoc, nTab, nTab );
5486 rDoc.CopyToDocument(nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab,
5487 InsertDeleteFlags::ALL, false, *pRedoDoc);
5489 rDocShell.GetUndoManager()->AddUndoAction(
5490 std::make_unique<ScUndoListNames>( &rDocShell,
5491 ScRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab ),
5492 std::move(pUndoDoc), std::move(pRedoDoc) ) );
5495 if (!AdjustRowHeight(ScRange(0,nStartRow,nTab,rDoc.MaxCol(),nEndRow,nTab), true, true))
5496 rDocShell.PostPaint( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab, PaintPartFlags::Grid );
5498 aModificator.SetDocumentModified();
5499 bDone = true;
5501 else if (!bApi)
5502 rDocShell.ErrorMessage(aTester.GetMessageId());
5504 return bDone;
5507 void ScDocFunc::ResizeMatrix( const ScRange& rOldRange, const ScAddress& rNewEnd )
5509 ScDocument& rDoc = rDocShell.GetDocument();
5510 SCCOL nStartCol = rOldRange.aStart.Col();
5511 SCROW nStartRow = rOldRange.aStart.Row();
5512 SCTAB nTab = rOldRange.aStart.Tab();
5514 OUString aFormula = rDoc.GetFormula( nStartCol, nStartRow, nTab );
5515 if ( !(aFormula.startsWith("{") && aFormula.endsWith("}")) )
5516 return;
5518 OUString aUndo = ScResId( STR_UNDO_RESIZEMATRIX );
5519 bool bUndo(rDoc.IsUndoEnabled());
5520 if (bUndo)
5522 ViewShellId nViewShellId(1);
5523 if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
5524 nViewShellId = pViewSh->GetViewShellId();
5525 rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
5528 aFormula = aFormula.copy(1, aFormula.getLength()-2);
5530 ScMarkData aMark(rDoc.GetSheetLimits());
5531 aMark.SetMarkArea( rOldRange );
5532 aMark.SelectTable( nTab, true );
5533 ScRange aNewRange( rOldRange.aStart, rNewEnd );
5535 if ( DeleteContents( aMark, InsertDeleteFlags::CONTENTS, true, false/*bApi*/ ) )
5537 // Formula string was obtained in document grammar.
5538 if (!EnterMatrix( aNewRange, &aMark, nullptr, aFormula, false/*bApi*/, false, OUString(), rDoc.GetGrammar() ))
5540 // try to restore the previous state
5541 EnterMatrix( rOldRange, &aMark, nullptr, aFormula, false/*bApi*/, false, OUString(), rDoc.GetGrammar() );
5545 if (bUndo)
5546 rDocShell.GetUndoManager()->LeaveListAction();
5549 void ScDocFunc::InsertAreaLink( const OUString& rFile, const OUString& rFilter,
5550 const OUString& rOptions, const OUString& rSource,
5551 const ScRange& rDestRange, sal_Int32 nRefreshDelaySeconds,
5552 bool bFitBlock, bool bApi )
5554 ScDocument& rDoc = rDocShell.GetDocument();
5555 bool bUndo (rDoc.IsUndoEnabled());
5557 sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager();
5559 // #i52120# if other area links exist at the same start position,
5560 // remove them first (file format specifies only one link definition
5561 // for a cell)
5563 sal_uInt16 nLinkCount = pLinkManager->GetLinks().size();
5564 sal_uInt16 nRemoved = 0;
5565 sal_uInt16 nLinkPos = 0;
5566 while (nLinkPos<nLinkCount)
5568 ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[nLinkPos].get();
5569 ScAreaLink* pLink = dynamic_cast<ScAreaLink*>(pBase);
5570 if (pLink && pLink->GetDestArea().aStart == rDestRange.aStart)
5572 if ( bUndo )
5574 if ( !nRemoved )
5576 // group all remove and the insert action
5577 OUString aUndo = ScResId( STR_UNDO_INSERTAREALINK );
5578 ViewShellId nViewShellId(-1);
5579 if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
5580 nViewShellId = pViewSh->GetViewShellId();
5581 rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
5584 ScAreaLink* pOldArea = static_cast<ScAreaLink*>(pBase);
5585 rDocShell.GetUndoManager()->AddUndoAction(
5586 std::make_unique<ScUndoRemoveAreaLink>( &rDocShell,
5587 pOldArea->GetFile(), pOldArea->GetFilter(), pOldArea->GetOptions(),
5588 pOldArea->GetSource(), pOldArea->GetDestArea(), pOldArea->GetRefreshDelaySeconds() ) );
5590 pLinkManager->Remove( pBase );
5591 nLinkCount = pLinkManager->GetLinks().size();
5592 ++nRemoved;
5594 else
5595 ++nLinkPos;
5598 OUString aFilterName = rFilter;
5599 OUString aNewOptions = rOptions;
5600 if (aFilterName.isEmpty())
5601 ScDocumentLoader::GetFilterName( rFile, aFilterName, aNewOptions, true, !bApi );
5603 // remove application prefix from filter name here, so the filter options
5604 // aren't reset when the filter name is changed in ScAreaLink::DataChanged
5605 ScDocumentLoader::RemoveAppPrefix( aFilterName );
5607 ScAreaLink* pLink = new ScAreaLink( &rDocShell, rFile, aFilterName,
5608 aNewOptions, rSource, rDestRange, nRefreshDelaySeconds );
5609 OUString aTmp = aFilterName;
5610 pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, rFile, &aTmp, &rSource );
5612 // Undo for an empty link
5614 if (bUndo)
5616 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoInsertAreaLink>( &rDocShell,
5617 rFile, aFilterName, aNewOptions,
5618 rSource, rDestRange, nRefreshDelaySeconds ) );
5619 if ( nRemoved )
5620 rDocShell.GetUndoManager()->LeaveListAction(); // undo for link update is still separate
5623 // Update has its own undo
5624 if (rDoc.IsExecuteLinkEnabled())
5626 pLink->SetDoInsert(bFitBlock); // if applicable, don't insert anything on first update
5627 pLink->Update(); // no SetInCreate -> carry out update
5629 pLink->SetDoInsert(true); // Default = true
5631 SfxBindings* pBindings = rDocShell.GetViewBindings();
5632 if (pBindings)
5633 pBindings->Invalidate( SID_LINKS );
5635 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); // Navigator
5638 void ScDocFunc::ReplaceConditionalFormat( sal_uLong nOldFormat, std::unique_ptr<ScConditionalFormat> pFormat, SCTAB nTab, const ScRangeList& rRanges )
5640 ScDocShellModificator aModificator(rDocShell);
5641 ScDocument& rDoc = rDocShell.GetDocument();
5642 if(rDoc.IsTabProtected(nTab))
5643 return;
5645 ScRange aCombinedRange = rRanges.Combine();
5646 std::unique_ptr<ScUndoConditionalFormat> pUndo;
5647 if (rDoc.IsUndoEnabled())
5648 pUndo.reset(new ScUndoConditionalFormat(&rDocShell, nTab));
5650 std::unique_ptr<ScRange> pRepaintRange;
5651 if(nOldFormat)
5653 ScConditionalFormat* pOldFormat = rDoc.GetCondFormList(nTab)->GetFormat(nOldFormat);
5654 if(pOldFormat)
5656 pRepaintRange.reset(new ScRange( pOldFormat->GetRange().Combine() ));
5657 rDoc.RemoveCondFormatData(pOldFormat->GetRange(), nTab, pOldFormat->GetKey());
5660 rDoc.DeleteConditionalFormat(nOldFormat, nTab);
5661 rDoc.SetStreamValid(nTab, false);
5663 if(pFormat)
5665 if(pRepaintRange)
5666 pRepaintRange->ExtendTo(aCombinedRange);
5667 else
5668 pRepaintRange.reset(new ScRange(aCombinedRange));
5670 sal_uInt32 nIndex = rDoc.AddCondFormat(std::move(pFormat), nTab);
5672 rDoc.AddCondFormatData(rRanges, nTab, nIndex);
5673 rDoc.SetStreamValid(nTab, false);
5676 if (pUndo)
5678 pUndo->setRedoData();
5679 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
5682 if(pRepaintRange)
5683 rDocShell.PostPaint(*pRepaintRange, PaintPartFlags::Grid, SC_PF_TESTMERGE);
5685 aModificator.SetDocumentModified();
5686 SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
5689 void ScDocFunc::SetConditionalFormatList( ScConditionalFormatList* pList, SCTAB nTab )
5691 ScDocShellModificator aModificator(rDocShell);
5692 ScDocument& rDoc = rDocShell.GetDocument();
5693 if(rDoc.IsTabProtected(nTab))
5694 return;
5696 bool bUndo = rDoc.IsUndoEnabled();
5697 ScDocumentUniquePtr pUndoDoc;
5698 if (bUndo)
5700 pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
5701 pUndoDoc->InitUndo( rDoc, nTab, nTab );
5703 ScConditionalFormatList* pOld = rDoc.GetCondFormList(nTab);
5705 if (pOld)
5706 pUndoDoc->SetCondFormList(new ScConditionalFormatList(*pUndoDoc, *pOld), nTab);
5707 else
5708 pUndoDoc->SetCondFormList(nullptr, nTab);
5712 // first remove all old entries
5713 ScConditionalFormatList* pOldList = rDoc.GetCondFormList(nTab);
5714 pOldList->RemoveFromDocument(rDoc);
5716 // then set new entries
5717 pList->AddToDocument(rDoc);
5719 rDoc.SetCondFormList(pList, nTab);
5720 rDocShell.PostPaintGridAll();
5722 if(bUndo)
5724 ScDocumentUniquePtr pRedoDoc(new ScDocument(SCDOCMODE_UNDO));
5725 pRedoDoc->InitUndo( rDoc, nTab, nTab );
5726 pRedoDoc->SetCondFormList(new ScConditionalFormatList(*pRedoDoc, *pList), nTab);
5728 rDocShell.GetUndoManager()->AddUndoAction(
5729 std::make_unique<ScUndoConditionalFormatList>(&rDocShell, std::move(pUndoDoc), std::move(pRedoDoc), nTab));
5732 rDoc.SetStreamValid(nTab, false);
5733 aModificator.SetDocumentModified();
5734 SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged));
5737 void ScDocFunc::ConvertFormulaToValue( const ScRange& rRange, bool bInteraction )
5739 ScDocShellModificator aModificator(rDocShell);
5740 ScDocument& rDoc = rDocShell.GetDocument();
5741 bool bRecord = true;
5742 if (!rDoc.IsUndoEnabled())
5743 bRecord = false;
5745 ScEditableTester aTester(rDoc, rRange, sc::EditAction::Unknown);
5746 if (!aTester.IsEditable())
5748 if (bInteraction)
5749 rDocShell.ErrorMessage(aTester.GetMessageId());
5750 return;
5753 sc::TableValues aUndoVals(rRange);
5754 sc::TableValues* pUndoVals = bRecord ? &aUndoVals : nullptr;
5756 rDoc.ConvertFormulaToValue(rRange, pUndoVals);
5758 if (bRecord && pUndoVals)
5760 rDocShell.GetUndoManager()->AddUndoAction(
5761 std::make_unique<sc::UndoFormulaToValue>(&rDocShell, *pUndoVals));
5764 rDocShell.PostPaint(rRange, PaintPartFlags::Grid);
5765 rDocShell.PostDataChanged();
5766 rDoc.BroadcastCells(rRange, SfxHintId::ScDataChanged);
5767 aModificator.SetDocumentModified();
5770 void ScDocFunc::EnterListAction(TranslateId pNameResId)
5772 OUString aUndo(ScResId(pNameResId));
5773 ViewShellId nViewShellId(-1);
5774 if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell())
5775 nViewShellId = pViewSh->GetViewShellId();
5776 rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId );
5779 void ScDocFunc::EndListAction()
5781 rDocShell.GetUndoManager()->LeaveListAction();
5784 bool ScDocFunc::InsertSparklines(ScRange const& rDataRange, ScRange const& rSparklineRange,
5785 const std::shared_ptr<sc::SparklineGroup>& pSparklineGroup)
5787 std::vector<sc::SparklineData> aSparklineDataVector;
5789 if (rSparklineRange.aStart.Col() == rSparklineRange.aEnd.Col())
5791 sal_Int32 nOutputRowSize = rSparklineRange.aEnd.Row() - rSparklineRange.aStart.Row();
5793 auto eInputOrientation = sc::calculateOrientation(nOutputRowSize, rDataRange);
5795 if (eInputOrientation == sc::RangeOrientation::Unknown)
5796 return false;
5798 sal_Int32 nIndex = 0;
5800 for (ScAddress aAddress = rSparklineRange.aStart; aAddress.Row() <= rSparklineRange.aEnd.Row();
5801 aAddress.IncRow())
5803 ScRange aInputRangeSlice = rDataRange;
5804 if (eInputOrientation == sc::RangeOrientation::Row)
5806 aInputRangeSlice.aStart.SetRow(rDataRange.aStart.Row() + nIndex);
5807 aInputRangeSlice.aEnd.SetRow(rDataRange.aStart.Row() + nIndex);
5809 else
5811 aInputRangeSlice.aStart.SetCol(rDataRange.aStart.Col() + nIndex);
5812 aInputRangeSlice.aEnd.SetCol(rDataRange.aStart.Col() + nIndex);
5815 aSparklineDataVector.emplace_back(aAddress, aInputRangeSlice);
5817 nIndex++;
5820 else if (rSparklineRange.aStart.Row() == rSparklineRange.aEnd.Row())
5822 sal_Int32 nOutputColSize = rSparklineRange.aEnd.Col() - rSparklineRange.aStart.Col();
5824 auto eInputOrientation = sc::calculateOrientation(nOutputColSize, rDataRange);
5826 if (eInputOrientation == sc::RangeOrientation::Unknown)
5827 return false;
5829 sal_Int32 nIndex = 0;
5831 for (ScAddress aAddress = rSparklineRange.aStart; aAddress.Col() <= rSparklineRange.aEnd.Col();
5832 aAddress.IncCol())
5834 ScRange aInputRangeSlice = rDataRange;
5835 if (eInputOrientation == sc::RangeOrientation::Row)
5837 aInputRangeSlice.aStart.SetRow(rDataRange.aStart.Row() + nIndex);
5838 aInputRangeSlice.aEnd.SetRow(rDataRange.aStart.Row() + nIndex);
5840 else
5842 aInputRangeSlice.aStart.SetCol(rDataRange.aStart.Col() + nIndex);
5843 aInputRangeSlice.aEnd.SetCol(rDataRange.aStart.Col() + nIndex);
5846 aSparklineDataVector.emplace_back(aAddress, aInputRangeSlice);
5848 nIndex++;
5852 if (aSparklineDataVector.empty())
5853 return false;
5855 auto pUndoInsertSparkline = std::make_unique<sc::UndoInsertSparkline>(rDocShell, aSparklineDataVector, pSparklineGroup);
5856 // insert the sparkline by "redoing"
5857 pUndoInsertSparkline->Redo();
5858 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoInsertSparkline));
5860 return true;
5863 bool ScDocFunc::DeleteSparkline(ScAddress const& rAddress)
5865 auto& rDocument = rDocShell.GetDocument();
5867 if (!rDocument.HasSparkline(rAddress))
5868 return false;
5870 auto pUndoDeleteSparkline = std::make_unique<sc::UndoDeleteSparkline>(rDocShell, rAddress);
5871 // delete sparkline by "redoing"
5872 pUndoDeleteSparkline->Redo();
5873 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoDeleteSparkline));
5875 return true;
5878 bool ScDocFunc::DeleteSparklineGroup(std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup, SCTAB nTab)
5880 if (!pSparklineGroup)
5881 return false;
5883 auto& rDocument = rDocShell.GetDocument();
5885 if (!rDocument.HasTable(nTab))
5886 return false;
5888 auto pUndo = std::make_unique<sc::UndoDeleteSparklineGroup>(rDocShell, pSparklineGroup, nTab);
5889 // delete sparkline group by "redoing"
5890 pUndo->Redo();
5891 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
5892 return true;
5895 bool ScDocFunc::ChangeSparklineGroupAttributes(std::shared_ptr<sc::SparklineGroup> const& pExistingSparklineGroup,
5896 sc::SparklineAttributes const& rNewAttributes)
5898 auto pUndo = std::make_unique<sc::UndoEditSparklneGroup>(rDocShell, pExistingSparklineGroup, rNewAttributes);
5899 // change sparkline group attributes by "redoing"
5900 pUndo->Redo();
5901 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
5902 return true;
5905 bool ScDocFunc::GroupSparklines(ScRange const& rRange, std::shared_ptr<sc::SparklineGroup> const& rpGroup)
5907 auto pUndo = std::make_unique<sc::UndoGroupSparklines>(rDocShell, rRange, rpGroup);
5908 // group sparklines by "redoing"
5909 pUndo->Redo();
5910 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
5911 return true;
5914 bool ScDocFunc::UngroupSparklines(ScRange const& rRange)
5916 auto pUndo = std::make_unique<sc::UndoUngroupSparklines>(rDocShell, rRange);
5917 // ungroup sparklines by "redoing"
5918 pUndo->Redo();
5919 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
5920 return true;
5923 bool ScDocFunc::ChangeSparkline(std::shared_ptr<sc::Sparkline> const& rpSparkline, SCTAB nTab, ScRangeList const& rDataRange)
5925 auto pUndo = std::make_unique<sc::UndoEditSparkline>(rDocShell, rpSparkline, nTab, rDataRange);
5926 // change sparkline by "redoing"
5927 pUndo->Redo();
5928 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo));
5929 return true;
5932 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */