xref: /core/sc/source/ui/view/viewfunc.cxx (revision ec1c4c49)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_features.h>
21 
22 #include <scitems.hxx>
23 
24 #include <sfx2/app.hxx>
25 #include <svx/algitem.hxx>
26 #include <editeng/boxitem.hxx>
27 #include <editeng/editobj.hxx>
28 #include <editeng/langitem.hxx>
29 #include <editeng/justifyitem.hxx>
30 #include <o3tl/unit_conversion.hxx>
31 #include <sfx2/bindings.hxx>
32 #include <svl/numformat.hxx>
33 #include <svl/zforlist.hxx>
34 #include <svl/zformat.hxx>
35 #include <vcl/svapp.hxx>
36 #include <vcl/weld.hxx>
37 #include <vcl/virdev.hxx>
38 #include <stdlib.h>
39 #include <unotools/charclass.hxx>
40 #include <vcl/uitest/logger.hxx>
41 #include <vcl/uitest/eventdescription.hxx>
42 #include <osl/diagnose.h>
43 
44 #include <viewfunc.hxx>
45 #include <tabvwsh.hxx>
46 #include <docsh.hxx>
47 #include <attrib.hxx>
48 #include <patattr.hxx>
49 #include <docpool.hxx>
50 #include <sc.hrc>
51 #include <strings.hrc>
52 #include <undocell.hxx>
53 #include <undoblk.hxx>
54 #include <refundo.hxx>
55 #include <olinetab.hxx>
56 #include <rangenam.hxx>
57 #include <globstr.hrc>
58 #include <global.hxx>
59 #include <stlsheet.hxx>
60 #include <editutil.hxx>
61 #include <formulacell.hxx>
62 #include <scresid.hxx>
63 #include <inputhdl.hxx>
64 #include <scmod.hxx>
65 #include <inputopt.hxx>
66 #include <compiler.hxx>
67 #include <docfunc.hxx>
68 #include <appoptio.hxx>
69 #include <sizedev.hxx>
70 #include <editable.hxx>
71 #include <scui_def.hxx>
72 #include <funcdesc.hxx>
73 #include <docuno.hxx>
74 #include <cellsuno.hxx>
75 #include <tokenarray.hxx>
76 #include <rowheightcontext.hxx>
77 #include <comphelper/lok.hxx>
78 #include <conditio.hxx>
79 #include <columnspanset.hxx>
80 
81 #include <memory>
82 
83 static void lcl_PostRepaintCondFormat( const ScConditionalFormat *pCondFmt, ScDocShell *pDocSh )
84 {
85     if( pCondFmt )
86     {
87         const ScRangeList& rRanges = pCondFmt->GetRange();
88 
89         pDocSh->PostPaint( rRanges, PaintPartFlags::All );
90     }
91 }
92 
93 ScViewFunc::ScViewFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
94     ScTabView( pParent, rDocSh, pViewShell ),
95     bFormatValid( false )
96 {
97 }
98 
99 ScViewFunc::~ScViewFunc()
100 {
101 }
102 
103 namespace {
104 
105 void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
106 {
107     EventDescription aDescription;
108     aDescription.aID = "grid_window";
109     aDescription.aAction = rAction;
110     aDescription.aParameters = std::move(aParameters);
111     aDescription.aParent = "MainWindow";
112     aDescription.aKeyWord = "ScGridWinUIObject";
113 
114     UITestLogger::getInstance().logEvent(aDescription);
115 }
116 
117 }
118 
119 void ScViewFunc::StartFormatArea()
120 {
121     //  anything to do?
122     if ( !SC_MOD()->GetInputOptions().GetExtendFormat() )
123         return;
124 
125     //  start only with single cell (marked or cursor position)
126     ScRange aMarkRange;
127     bool bOk = (GetViewData().GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE);
128     if ( bOk && aMarkRange.aStart != aMarkRange.aEnd )
129         bOk = false;
130 
131     if (bOk)
132     {
133         bFormatValid = true;
134         aFormatSource = aMarkRange.aStart;
135         aFormatArea = ScRange( aFormatSource );
136     }
137     else
138         bFormatValid = false;       // discard old range
139 }
140 
141 bool ScViewFunc::TestFormatArea( SCCOL nCol, SCROW nRow, SCTAB nTab, bool bAttrChanged )
142 {
143     //  anything to do?
144     if ( !SC_MOD()->GetInputOptions().GetExtendFormat() )
145         return false;
146 
147     //  Test: treat input with numberformat (bAttrChanged) always as new Attribute
148     //  (discard old Area ). If not wanted, discard if-statement
149     if ( bAttrChanged )
150     {
151         StartFormatArea();
152         return false;
153     }
154 
155     //! Test if cell empty ???
156 
157     bool bFound = false;
158     ScRange aNewRange = aFormatArea;
159     if ( bFormatValid && nTab == aFormatSource.Tab() )
160     {
161         if ( nRow >= aFormatArea.aStart.Row() && nRow <= aFormatArea.aEnd.Row() )
162         {
163             //  within range?
164             if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
165             {
166                 bFound = true;          // do not change range
167             }
168             //  left ?
169             if ( nCol+1 == aFormatArea.aStart.Col() )
170             {
171                 bFound = true;
172                 aNewRange.aStart.SetCol( nCol );
173             }
174             //  right ?
175             if ( nCol == aFormatArea.aEnd.Col()+1 )
176             {
177                 bFound = true;
178                 aNewRange.aEnd.SetCol( nCol );
179             }
180         }
181         if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
182         {
183             //  top ?
184             if ( nRow+1 == aFormatArea.aStart.Row() )
185             {
186                 bFound = true;
187                 aNewRange.aStart.SetRow( nRow );
188             }
189             //  bottom ?
190             if ( nRow == aFormatArea.aEnd.Row()+1 )
191             {
192                 bFound = true;
193                 aNewRange.aEnd.SetRow( nRow );
194             }
195         }
196     }
197 
198     if (bFound)
199         aFormatArea = aNewRange;    // extend
200     else
201         bFormatValid = false;       // outside of range -> break
202 
203     return bFound;
204 }
205 
206 void ScViewFunc::DoAutoAttributes( SCCOL nCol, SCROW nRow, SCTAB nTab,
207                                    bool bAttrChanged )
208 {
209     ScDocShell* pDocSh = GetViewData().GetDocShell();
210     ScDocument& rDoc = pDocSh->GetDocument();
211 
212     const ScPatternAttr* pSource = rDoc.GetPattern(
213                             aFormatSource.Col(), aFormatSource.Row(), nTab );
214     if ( !pSource->GetItem(ATTR_MERGE).IsMerged() )
215     {
216         ScRange aRange( nCol, nRow, nTab, nCol, nRow, nTab );
217         ScMarkData aMark(rDoc.GetSheetLimits());
218         aMark.SetMarkArea( aRange );
219 
220         ScDocFunc &rFunc = GetViewData().GetDocFunc();
221 
222         // pOldPattern is only valid until call to ApplyAttributes!
223         const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
224         const ScStyleSheet* pSrcStyle = pSource->GetStyleSheet();
225         if ( pSrcStyle && pSrcStyle != pOldPattern->GetStyleSheet() )
226             rFunc.ApplyStyle( aMark, pSrcStyle->GetName(), false );
227 
228         rFunc.ApplyAttributes( aMark, *pSource, false );
229     }
230 
231     if ( bAttrChanged )                             // value entered with number format?
232         aFormatSource.Set( nCol, nRow, nTab );      // then set a new source
233 }
234 
235 //      additional routines
236 
237 sal_uInt16 ScViewFunc::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, bool bFormula )
238 {
239     ScDocShell* pDocSh = GetViewData().GetDocShell();
240     ScDocument& rDoc = pDocSh->GetDocument();
241     ScMarkData& rMark = GetViewData().GetMarkData();
242 
243     double nPPTX = GetViewData().GetPPTX();
244     double nPPTY = GetViewData().GetPPTY();
245     Fraction aZoomX = GetViewData().GetZoomX();
246     Fraction aZoomY = GetViewData().GetZoomY();
247 
248     ScSizeDeviceProvider aProv(pDocSh);
249     if (aProv.IsPrinter())
250     {
251         nPPTX = aProv.GetPPTX();
252         nPPTY = aProv.GetPPTY();
253         aZoomX = aZoomY = Fraction( 1, 1 );
254     }
255 
256     sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, aProv.GetDevice(),
257                                 nPPTX, nPPTY, aZoomX, aZoomY, bFormula, &rMark );
258     return nTwips;
259 }
260 
261 bool ScViewFunc::SelectionEditable( bool* pOnlyNotBecauseOfMatrix /* = NULL */ )
262 {
263     bool bRet;
264     ScDocument& rDoc = GetViewData().GetDocument();
265     ScMarkData& rMark = GetViewData().GetMarkData();
266     if (rMark.IsMarked() || rMark.IsMultiMarked())
267         bRet = rDoc.IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix );
268     else
269     {
270         SCCOL nCol = GetViewData().GetCurX();
271         SCROW nRow = GetViewData().GetCurY();
272         SCTAB nTab = GetViewData().GetTabNo();
273         bRet = rDoc.IsBlockEditable( nTab, nCol, nRow, nCol, nRow,
274             pOnlyNotBecauseOfMatrix );
275     }
276     return bRet;
277 }
278 
279 static bool lcl_FunctionKnown( sal_uInt16 nOpCode )
280 {
281     const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
282     if ( pFuncList )
283     {
284         sal_uLong nCount = pFuncList->GetCount();
285         for (sal_uLong i=0; i<nCount; i++)
286             if ( pFuncList->GetFunction(i)->nFIndex == nOpCode )
287                 return true;
288     }
289     return false;
290 }
291 
292 static bool lcl_AddFunction( ScAppOptions& rAppOpt, sal_uInt16 nOpCode )
293 {
294     sal_uInt16 nOldCount = rAppOpt.GetLRUFuncListCount();
295     sal_uInt16* pOldList = rAppOpt.GetLRUFuncList();
296     sal_uInt16 nPos;
297     for (nPos=0; nPos<nOldCount; nPos++)
298         if (pOldList[nPos] == nOpCode)          // is the function already in the list?
299         {
300             if ( nPos == 0 )
301                 return false;                   // already at the top -> no change
302 
303             //  count doesn't change, so the original array is modified
304 
305             for (sal_uInt16 nCopy=nPos; nCopy>0; nCopy--)
306                 pOldList[nCopy] = pOldList[nCopy-1];
307             pOldList[0] = nOpCode;
308 
309             return true;                        // list has changed
310         }
311 
312     if ( !lcl_FunctionKnown( nOpCode ) )
313         return false;                           // not in function list -> no change
314 
315     sal_uInt16 nNewCount = std::min( static_cast<sal_uInt16>(nOldCount + 1), sal_uInt16(LRU_MAX) );
316     sal_uInt16 nNewList[LRU_MAX];
317     nNewList[0] = nOpCode;
318     for (nPos=1; nPos<nNewCount; nPos++)
319         nNewList[nPos] = pOldList[nPos-1];
320     rAppOpt.SetLRUFuncList( nNewList, nNewCount );
321 
322     return true;                                // list has changed
323 }
324 
325 namespace HelperNotifyChanges
326 {
327     static void NotifyIfChangesListeners(const ScDocShell &rDocShell, ScMarkData& rMark, SCCOL nCol, SCROW nRow)
328     {
329         if (ScModelObj *pModelObj = getMustPropagateChangesModel(rDocShell))
330         {
331             ScRangeList aChangeRanges;
332             for (const auto& rTab : rMark)
333                 aChangeRanges.push_back( ScRange( nCol, nRow, rTab ) );
334 
335             HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "cell-change");
336         }
337     }
338 }
339 
340 //      actual functions
341 
342 //  input - undo OK
343 void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
344                             const OUString& rString,
345                             const EditTextObject* pData )
346 {
347     ScDocument& rDoc = GetViewData().GetDocument();
348     ScMarkData rMark(GetViewData().GetMarkData());
349     bool bRecord = rDoc.IsUndoEnabled();
350     SCTAB i;
351 
352     ScDocShell* pDocSh = GetViewData().GetDocShell();
353     ScDocFunc &rFunc = GetViewData().GetDocFunc();
354     ScDocShellModificator aModificator( *pDocSh );
355 
356     ScEditableTester aTester( rDoc, nCol,nRow, nCol,nRow, rMark );
357     if (!aTester.IsEditable())
358     {
359         ErrorMessage(aTester.GetMessageId());
360         PaintArea(nCol, nRow, nCol, nRow);        // possibly the edit-engine is still painted there
361         return;
362     }
363 
364     if ( bRecord )
365         rFunc.EnterListAction( STR_UNDO_ENTERDATA );
366 
367     bool bFormula = false;
368 
369     // a single '=' character is handled as string (needed for special filters)
370     if ( rString.getLength() > 1 )
371     {
372         if ( rString[0] == '=' )
373         {
374             // handle as formula
375             bFormula = true;
376         }
377         else if ( rString[0] == '+' || rString[0] == '-' )
378         {
379             // if there is more than one leading '+' or '-' character, remove the additional ones
380             sal_Int32 nIndex = 1;
381             sal_Int32 nLen = rString.getLength();
382             while ( nIndex < nLen && ( rString[ nIndex ] == '+' || rString[ nIndex ] == '-' ) )
383             {
384                 ++nIndex;
385             }
386             OUString aString = rString.replaceAt( 1, nIndex - 1, u"" );
387 
388             // if the remaining part without the leading '+' or '-' character
389             // is non-empty and not a number, handle as formula
390             if ( aString.getLength() > 1 )
391             {
392                 sal_uInt32 nFormat = rDoc.GetNumberFormat( nCol, nRow, nTab );
393                 SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
394                 double fNumber = 0;
395                 if ( !pFormatter->IsNumberFormat( aString, nFormat, fNumber ) )
396                 {
397                     bFormula = true;
398                 }
399             }
400         }
401     }
402 
403     bool bNumFmtChanged = false;
404     if ( bFormula )
405     {   // formula, compile with autoCorrection
406         i = rMark.GetFirstSelected();
407         ScAddress aPos( nCol, nRow, i );
408         ScCompiler aComp( rDoc, aPos, rDoc.GetGrammar(), true, false );
409 //2do: enable/disable autoCorrection via calcoptions
410         aComp.SetAutoCorrection( true );
411         if ( rString[0] == '+' || rString[0] == '-' )
412         {
413             aComp.SetExtendedErrorDetection( ScCompiler::EXTENDED_ERROR_DETECTION_NAME_BREAK );
414         }
415         OUString aFormula( rString );
416         std::unique_ptr< ScTokenArray > pArr;
417         bool bAgain;
418         do
419         {
420             bAgain = false;
421             bool bAddEqual = false;
422             pArr = aComp.CompileString( aFormula );
423             bool bCorrected = aComp.IsCorrected();
424             std::unique_ptr< ScTokenArray > pArrFirst;
425             if ( bCorrected )
426             {   // try to parse with first parser-correction
427                 pArrFirst = std::move( pArr );
428                 pArr = aComp.CompileString( aComp.GetCorrectedFormula() );
429             }
430             if ( pArr->GetCodeError() == FormulaError::NONE )
431             {
432                 bAddEqual = true;
433                 aComp.CompileTokenArray();
434                 bCorrected |= aComp.IsCorrected();
435             }
436             if ( bCorrected )
437             {
438                 OUString aCorrectedFormula;
439                 if ( bAddEqual )
440                 {
441                     aCorrectedFormula = "=" + aComp.GetCorrectedFormula();
442                 }
443                 else
444                     aCorrectedFormula = aComp.GetCorrectedFormula();
445                 short nResult;
446                 if ( aCorrectedFormula.getLength() == 1 )
447                     nResult = RET_NO;   // empty formula, just '='
448                 else
449                 {
450                     OUString aMessage = ScResId( SCSTR_FORMULA_AUTOCORRECTION ) + aCorrectedFormula;
451 
452                     std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(),
453                                                                    VclMessageType::Question, VclButtonsType::YesNo,
454                                                                    aMessage));
455                     xQueryBox->set_default_response(RET_YES);
456                     nResult = xQueryBox->run();
457                 }
458                 if ( nResult == RET_YES )
459                 {
460                     aFormula = aCorrectedFormula;
461                     bAgain = true;
462                 }
463                 else
464                 {
465                     if ( pArrFirst )
466                         pArr = std::move( pArrFirst );
467                 }
468             }
469         } while ( bAgain );
470         // to be used in multiple tabs, the formula must be compiled anew
471         // via ScFormulaCell copy-ctor because of RangeNames,
472         // the same code-array for all cells is not possible.
473         // If the array has an error, (it) must be RPN-erased in the newly generated
474         // cells and the error be set explicitly, so that
475         // via FormulaCell copy-ctor and Interpreter it will be, when possible,
476         // ironed out again, too intelligent... e.g.: =1))
477         FormulaError nError = pArr->GetCodeError();
478         if ( nError == FormulaError::NONE )
479         {
480             //  update list of recent functions with all functions that
481             //  are not within parentheses
482 
483             ScModule* pScMod = SC_MOD();
484             ScAppOptions aAppOpt = pScMod->GetAppOptions();
485             bool bOptChanged = false;
486 
487             formula::FormulaToken** ppToken = pArr->GetArray();
488             sal_uInt16 nTokens = pArr->GetLen();
489             sal_uInt16 nLevel = 0;
490             for (sal_uInt16 nTP=0; nTP<nTokens; nTP++)
491             {
492                 formula::FormulaToken* pTok = ppToken[nTP];
493                 OpCode eOp = pTok->GetOpCode();
494                 if ( eOp == ocOpen )
495                     ++nLevel;
496                 else if ( eOp == ocClose && nLevel )
497                     --nLevel;
498                 if ( nLevel == 0 && pTok->IsFunction() &&
499                         lcl_AddFunction( aAppOpt, sal::static_int_cast<sal_uInt16>( eOp ) ) )
500                     bOptChanged = true;
501             }
502 
503             if ( bOptChanged )
504             {
505                 pScMod->SetAppOptions(aAppOpt);
506             }
507         }
508 
509         ScFormulaCell aCell(rDoc, aPos, std::move( pArr ), formula::FormulaGrammar::GRAM_DEFAULT, ScMatrixMode::NONE);
510 
511         SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
512         for (const auto& rTab : rMark)
513         {
514             i = rTab;
515             aPos.SetTab( i );
516             const sal_uInt32 nIndex = rDoc.GetAttr(
517                         nCol, nRow, i, ATTR_VALUE_FORMAT )->GetValue();
518             const SvNumFormatType nType = pFormatter->GetType( nIndex);
519             if (nType == SvNumFormatType::TEXT ||
520                     ((rString[0] == '+' || rString[0] == '-') && nError != FormulaError::NONE && rString == aFormula))
521             {
522                 if ( pData )
523                 {
524                     // A clone of pData will be stored in the cell.
525                     rFunc.SetEditCell(aPos, *pData, true);
526                 }
527                 else
528                     rFunc.SetStringCell(aPos, aFormula, true);
529             }
530             else
531             {
532                 ScFormulaCell* pCell = new ScFormulaCell( aCell, rDoc, aPos );
533                 if ( nError != FormulaError::NONE )
534                 {
535                     pCell->GetCode()->DelRPN();
536                     pCell->SetErrCode( nError );
537                     if(pCell->GetCode()->IsHyperLink())
538                         pCell->GetCode()->SetHyperLink(false);
539                 }
540                 if (nType == SvNumFormatType::LOGICAL)
541                 {
542                     // Reset to General so the actual format can be determined
543                     // after the cell has been interpreted. A sticky boolean
544                     // number format is highly likely unwanted... see tdf#75650.
545                     // General of same locale as current number format.
546                     const SvNumberformat* pEntry = pFormatter->GetEntry( nIndex);
547                     const LanguageType nLang = (pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge);
548                     const sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, nLang);
549                     ScPatternAttr aPattern( rDoc.GetPool());
550                     aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nFormat));
551                     ScMarkData aMark(rDoc.GetSheetLimits());
552                     aMark.SelectTable( i, true);
553                     aMark.SetMarkArea( ScRange( aPos));
554                     rFunc.ApplyAttributes( aMark, aPattern, false);
555                     bNumFmtChanged = true;
556                 }
557                 rFunc.SetFormulaCell(aPos, pCell, true);
558             }
559         }
560     }
561     else
562     {
563         for (const auto& rTab : rMark)
564         {
565             bool bNumFmtSet = false;
566             rFunc.SetNormalString( bNumFmtSet, ScAddress( nCol, nRow, rTab ), rString, false );
567             if (bNumFmtSet)
568             {
569                 /* FIXME: if set on any sheet results in changed only on
570                  * sheet nTab for TestFormatArea() and DoAutoAttributes() */
571                 bNumFmtChanged = true;
572             }
573         }
574     }
575 
576     bool bAutoFormat = TestFormatArea(nCol, nRow, nTab, bNumFmtChanged);
577 
578     if (bAutoFormat)
579         DoAutoAttributes(nCol, nRow, nTab, bNumFmtChanged);
580 
581     pDocSh->UpdateOle(GetViewData());
582 
583     HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, nCol, nRow);
584 
585     if ( bRecord )
586         rFunc.EndListAction();
587 
588     aModificator.SetDocumentModified();
589     lcl_PostRepaintCondFormat( rDoc.GetCondFormat( nCol, nRow, nTab ), pDocSh );
590 }
591 
592 // enter value in single cell (on nTab only)
593 
594 void ScViewFunc::EnterValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rValue )
595 {
596     ScDocument& rDoc = GetViewData().GetDocument();
597     ScDocShell* pDocSh = GetViewData().GetDocShell();
598 
599     if (!pDocSh)
600         return;
601 
602     bool bUndo(rDoc.IsUndoEnabled());
603     ScDocShellModificator aModificator( *pDocSh );
604 
605     ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
606     if (aTester.IsEditable())
607     {
608         ScAddress aPos( nCol, nRow, nTab );
609         ScCellValue aUndoCell;
610         if (bUndo)
611             aUndoCell.assign(rDoc, aPos);
612 
613         rDoc.SetValue( nCol, nRow, nTab, rValue );
614 
615         // because of ChangeTrack after change in document
616         if (bUndo)
617         {
618             pDocSh->GetUndoManager()->AddUndoAction(
619                 std::make_unique<ScUndoEnterValue>(pDocSh, aPos, aUndoCell, rValue));
620         }
621 
622         pDocSh->PostPaintCell( aPos );
623         pDocSh->UpdateOle(GetViewData());
624         aModificator.SetDocumentModified();
625     }
626     else
627         ErrorMessage(aTester.GetMessageId());
628 }
629 
630 void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
631                             const EditTextObject& rData, bool bTestSimple )
632 {
633     ScDocShell* pDocSh = GetViewData().GetDocShell();
634     ScMarkData& rMark = GetViewData().GetMarkData();
635     ScDocument& rDoc = pDocSh->GetDocument();
636     bool bRecord = rDoc.IsUndoEnabled();
637 
638     ScDocShellModificator aModificator( *pDocSh );
639 
640     ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
641     if (aTester.IsEditable())
642     {
643 
644         //      test for attribute
645 
646         bool bSimple = false;
647         bool bCommon = false;
648         std::unique_ptr<ScPatternAttr> pCellAttrs;
649         OUString aString;
650 
651         const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
652         ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), &rDoc );
653         aEngine.SetTextCurrentDefaults(rData);
654 
655         if (bTestSimple)                    // test, if simple string without attribute
656         {
657             ScEditAttrTester aAttrTester( &aEngine );
658             bSimple = !aAttrTester.NeedsObject();
659             bCommon = aAttrTester.NeedsCellAttr();
660 
661             // formulas have to be recognized even if they're formatted
662             // (but common attributes are still collected)
663 
664             if ( !bSimple && aEngine.GetParagraphCount() == 1 )
665             {
666                 OUString aParStr(aEngine.GetText( 0 ));
667                 if ( aParStr[0] == '=' )
668                     bSimple = true;
669             }
670 
671             if (bCommon)                // attribute for tab
672             {
673                 pCellAttrs.reset(new ScPatternAttr( *pOldPattern ));
674                 pCellAttrs->GetFromEditItemSet( &aAttrTester.GetAttribs() );
675                 //! remove common attributes from EditEngine?
676             }
677         }
678 
679         // #i97726# always get text for "repeat" of undo action
680         aString = ScEditUtil::GetMultilineString(aEngine);
681 
682         //      undo
683 
684         std::unique_ptr<EditTextObject> pUndoData;
685         ScUndoEnterData::ValuesType aOldValues;
686 
687         if (bRecord && !bSimple)
688         {
689             for (const auto& rTab : rMark)
690             {
691                 ScUndoEnterData::Value aOldValue;
692                 aOldValue.mnTab = rTab;
693                 aOldValue.maCell.assign(rDoc, ScAddress(nCol, nRow, rTab));
694                 aOldValues.push_back(aOldValue);
695             }
696 
697             pUndoData = rData.Clone();
698         }
699 
700         //      enter data
701 
702         if (bCommon)
703             rDoc.ApplyPattern(nCol,nRow,nTab,*pCellAttrs);         //! undo
704 
705         if (bSimple)
706         {
707             if (bCommon)
708                 AdjustRowHeight(nRow,nRow,true);
709 
710             EnterData(nCol,nRow,nTab,aString);
711         }
712         else
713         {
714             for (const auto& rTab : rMark)
715             {
716                 ScAddress aPos(nCol, nRow, rTab);
717                 rDoc.SetEditText(aPos, rData, rDoc.GetEditPool());
718             }
719 
720             if ( bRecord )
721             {   //  because of ChangeTrack current first
722                 pDocSh->GetUndoManager()->AddUndoAction(
723                     std::make_unique<ScUndoEnterData>(pDocSh, ScAddress(nCol,nRow,nTab), aOldValues, aString, std::move(pUndoData)));
724             }
725 
726             HideAllCursors();
727 
728             AdjustRowHeight(nRow,nRow,true);
729 
730             for (const auto& rTab : rMark)
731                 pDocSh->PostPaintCell( nCol, nRow, rTab );
732 
733             ShowAllCursors();
734 
735             pDocSh->UpdateOle(GetViewData());
736 
737             HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, nCol, nRow);
738 
739             aModificator.SetDocumentModified();
740         }
741         lcl_PostRepaintCondFormat( rDoc.GetCondFormat( nCol, nRow, nTab ), pDocSh );
742     }
743     else
744     {
745         ErrorMessage(aTester.GetMessageId());
746         PaintArea( nCol, nRow, nCol, nRow );        // possibly the edit-engine is still painted there
747     }
748 }
749 
750 void ScViewFunc::EnterDataAtCursor( const OUString& rString )
751 {
752     SCCOL nPosX = GetViewData().GetCurX();
753     SCROW nPosY = GetViewData().GetCurY();
754     SCTAB nTab = GetViewData().GetTabNo();
755 
756     EnterData( nPosX, nPosY, nTab, rString );
757 }
758 
759 void ScViewFunc::EnterMatrix( const OUString& rString, ::formula::FormulaGrammar::Grammar eGram )
760 {
761     ScViewData& rData = GetViewData();
762     const SCCOL nCol = rData.GetCurX();
763     const SCROW nRow = rData.GetCurY();
764     const ScMarkData& rMark = rData.GetMarkData();
765     if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
766     {
767         //  nothing marked -> temporarily calculate block
768         //  with size of result formula to get the size
769 
770         ScDocument& rDoc = rData.GetDocument();
771         SCTAB nTab = rData.GetTabNo();
772         ScFormulaCell aFormCell( rDoc, ScAddress(nCol,nRow,nTab), rString, eGram, ScMatrixMode::Formula );
773 
774         SCSIZE nSizeX;
775         SCSIZE nSizeY;
776         aFormCell.GetResultDimensions( nSizeX, nSizeY );
777         if ( nSizeX != 0 && nSizeY != 0 &&
778              nCol+nSizeX-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxCol()) &&
779              nRow+nSizeY-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxRow()) )
780         {
781             ScRange aResult( nCol, nRow, nTab,
782                              sal::static_int_cast<SCCOL>(nCol+nSizeX-1),
783                              sal::static_int_cast<SCROW>(nRow+nSizeY-1), nTab );
784             MarkRange( aResult, false );
785         }
786     }
787 
788     ScRange aRange;
789     if (rData.GetSimpleArea(aRange) == SC_MARK_SIMPLE)
790     {
791         ScDocShell* pDocSh = rData.GetDocShell();
792         bool bSuccess = pDocSh->GetDocFunc().EnterMatrix(
793             aRange, &rMark, nullptr, rString, false, false, OUString(), eGram );
794         if (bSuccess)
795             pDocSh->UpdateOle(GetViewData());
796         else
797             PaintArea(nCol, nRow, nCol, nRow);        // possibly the edit-engine is still painted there
798     }
799     else
800         ErrorMessage(STR_NOMULTISELECT);
801 }
802 
803 SvtScriptType ScViewFunc::GetSelectionScriptType()
804 {
805     SvtScriptType nScript = SvtScriptType::NONE;
806 
807     ScDocument& rDoc = GetViewData().GetDocument();
808     const ScMarkData& rMark = GetViewData().GetMarkData();
809     if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
810     {
811         // no selection -> cursor
812 
813         nScript = rDoc.GetScriptType( GetViewData().GetCurX(),
814                             GetViewData().GetCurY(), GetViewData().GetTabNo());
815     }
816     else
817     {
818         ScRangeList aRanges;
819         rMark.FillRangeListWithMarks( &aRanges, false );
820         nScript = rDoc.GetRangeScriptType(aRanges);
821     }
822 
823     if (nScript == SvtScriptType::NONE)
824         nScript = ScGlobal::GetDefaultScriptType();
825 
826     return nScript;
827 }
828 
829 const ScPatternAttr* ScViewFunc::GetSelectionPattern()
830 {
831     // Don't use UnmarkFiltered in slot state functions, for performance reasons.
832     // The displayed state is always that of the whole selection including filtered rows.
833 
834     const ScMarkData& rMark = GetViewData().GetMarkData();
835     ScDocument& rDoc = GetViewData().GetDocument();
836     if ( rMark.IsMarked() || rMark.IsMultiMarked() )
837     {
838         //  MarkToMulti is no longer necessary for rDoc.GetSelectionPattern
839         const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( rMark );
840         return pAttr;
841     }
842     else
843     {
844         SCCOL  nCol = GetViewData().GetCurX();
845         SCROW  nRow = GetViewData().GetCurY();
846         SCTAB  nTab = GetViewData().GetTabNo();
847 
848         ScMarkData aTempMark( rMark );      // copy sheet selection
849         aTempMark.SetMarkArea( ScRange( nCol, nRow, nTab ) );
850         const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aTempMark );
851         return pAttr;
852     }
853 }
854 
855 void ScViewFunc::GetSelectionFrame(
856     std::shared_ptr<SvxBoxItem>& rLineOuter,
857     std::shared_ptr<SvxBoxInfoItem>& rLineInner )
858 {
859     ScDocument& rDoc = GetViewData().GetDocument();
860     const ScMarkData& rMark = GetViewData().GetMarkData();
861 
862     if ( rMark.IsMarked() || rMark.IsMultiMarked() )
863     {
864         rDoc.GetSelectionFrame( rMark, *rLineOuter, *rLineInner );
865     }
866     else
867     {
868         const ScPatternAttr* pAttrs =
869                     rDoc.GetPattern( GetViewData().GetCurX(),
870                                       GetViewData().GetCurY(),
871                                       GetViewData().GetTabNo() );
872 
873         rLineOuter.reset(pAttrs->GetItem(ATTR_BORDER).Clone());
874         rLineInner.reset(pAttrs->GetItem(ATTR_BORDER_INNER).Clone());
875 
876         rLineInner->SetTable(false);
877         rLineInner->SetDist(true);
878         rLineInner->SetMinDist(false);
879     }
880 }
881 
882 //  apply attribute - undo OK
883 //
884 //  complete set ( ATTR_STARTINDEX, ATTR_ENDINDEX )
885 
886 void ScViewFunc::ApplyAttributes( const SfxItemSet* pDialogSet,
887                                   const SfxItemSet* pOldSet,
888                                   bool bAdjustBlockHeight)
889 {
890     // not editable because of matrix only? attribute OK nonetheless
891     bool bOnlyNotBecauseOfMatrix;
892     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
893     {
894         ErrorMessage(STR_PROTECTIONERR);
895         return;
896     }
897 
898     ScPatternAttr aOldAttrs(( SfxItemSet(*pOldSet) ));
899     ScPatternAttr aNewAttrs(( SfxItemSet(*pDialogSet) ));
900     aNewAttrs.DeleteUnchanged( &aOldAttrs );
901 
902     if ( pDialogSet->GetItemState( ATTR_VALUE_FORMAT ) == SfxItemState::SET )
903     {   // don't reset to default SYSTEM GENERAL if not intended
904         sal_uInt32 nOldFormat =
905             pOldSet->Get( ATTR_VALUE_FORMAT ).GetValue();
906         sal_uInt32 nNewFormat =
907             pDialogSet->Get( ATTR_VALUE_FORMAT ).GetValue();
908         if ( nNewFormat != nOldFormat )
909         {
910             SvNumberFormatter* pFormatter =
911                 GetViewData().GetDocument().GetFormatTable();
912             const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
913             LanguageType eOldLang =
914                 pOldEntry ? pOldEntry->GetLanguage() : LANGUAGE_DONTKNOW;
915             const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewFormat );
916             LanguageType eNewLang =
917                 pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW;
918             if ( eNewLang != eOldLang )
919             {
920                 aNewAttrs.GetItemSet().Put(
921                     SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
922 
923                 //  only the language has changed -> do not touch numberformat-attribute
924                 sal_uInt32 nNewMod = nNewFormat % SV_COUNTRY_LANGUAGE_OFFSET;
925                 if ( nNewMod == ( nOldFormat % SV_COUNTRY_LANGUAGE_OFFSET ) &&
926                      nNewMod <= SV_MAX_COUNT_STANDARD_FORMATS )
927                     aNewAttrs.GetItemSet().ClearItem( ATTR_VALUE_FORMAT );
928             }
929         }
930     }
931 
932     if (pDialogSet->HasItem(ATTR_FONT_LANGUAGE))
933         // font language has changed.  Redo the online spelling.
934         ResetAutoSpell();
935 
936     const SvxBoxItem&     rOldOuter = pOldSet->Get(ATTR_BORDER);
937     const SvxBoxItem&     rNewOuter = pDialogSet->Get(ATTR_BORDER);
938     const SvxBoxInfoItem& rOldInner = pOldSet->Get(ATTR_BORDER_INNER);
939     const SvxBoxInfoItem& rNewInner = pDialogSet->Get(ATTR_BORDER_INNER);
940     SfxItemSet&           rNewSet   = aNewAttrs.GetItemSet();
941     SfxItemPool*          pNewPool  = rNewSet.GetPool();
942 
943     pNewPool->Put(rNewOuter);        // don't delete yet
944     pNewPool->Put(rNewInner);
945     rNewSet.ClearItem( ATTR_BORDER );
946     rNewSet.ClearItem( ATTR_BORDER_INNER );
947 
948     /*
949      * establish whether border attribute is to be set:
950      * 1. new != old
951      * 2. is one of the borders not-DontCare (since 238.f: IsxxValid())
952      *
953      */
954 
955     bool bFrame =    (pDialogSet->GetItemState( ATTR_BORDER ) != SfxItemState::DEFAULT)
956                   || (pDialogSet->GetItemState( ATTR_BORDER_INNER ) != SfxItemState::DEFAULT);
957 
958     if (&rNewOuter == &rOldOuter && &rNewInner == &rOldInner)
959         bFrame = false;
960 
961     //  this should be intercepted by the pool: ?!??!??
962 
963     if (bFrame && rNewOuter == rOldOuter && rNewInner == rOldInner)
964         bFrame = false;
965 
966     bFrame =   bFrame
967             && (   rNewInner.IsValid(SvxBoxInfoItemValidFlags::LEFT)
968                 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::RIGHT)
969                 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::TOP)
970                 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::BOTTOM)
971                 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::HORI)
972                 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::VERT) );
973 
974     if (!bFrame)
975         ApplySelectionPattern( aNewAttrs );            // standard only
976     else
977     {
978         // if new items are default-items, overwrite the old items:
979 
980         bool bDefNewOuter = IsStaticDefaultItem(&rNewOuter);
981         bool bDefNewInner = IsStaticDefaultItem(&rNewInner);
982 
983         ApplyPatternLines( aNewAttrs,
984                            bDefNewOuter ? rOldOuter : rNewOuter,
985                            bDefNewInner ? &rOldInner : &rNewInner );
986     }
987 
988     pNewPool->Remove(rNewOuter);         // release
989     pNewPool->Remove(rNewInner);
990 
991     //  adjust height only if needed
992     if (bAdjustBlockHeight)
993         AdjustBlockHeight();
994 
995     // CellContentChanged is called in ApplySelectionPattern / ApplyPatternLines
996 }
997 
998 void ScViewFunc::ApplyAttr( const SfxPoolItem& rAttrItem, bool bAdjustBlockHeight )
999 {
1000     // not editable because of matrix only? attribute OK nonetheless
1001     bool bOnlyNotBecauseOfMatrix;
1002     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1003     {
1004         ErrorMessage(STR_PROTECTIONERR);
1005         return;
1006     }
1007 
1008     ScPatternAttr aNewAttrs(
1009         SfxItemSetFixed<ATTR_PATTERN_START, ATTR_PATTERN_END>( *GetViewData().GetDocument().GetPool() ) );
1010 
1011     aNewAttrs.GetItemSet().Put( rAttrItem );
1012     //  if justify is set (with Buttons), always indentation 0
1013     if ( rAttrItem.Which() == ATTR_HOR_JUSTIFY )
1014         aNewAttrs.GetItemSet().Put( ScIndentItem( 0 ) );
1015     ApplySelectionPattern( aNewAttrs );
1016 
1017     // Prevent useless compute
1018     if (bAdjustBlockHeight)
1019         AdjustBlockHeight();
1020 
1021     // CellContentChanged is called in ApplySelectionPattern
1022 }
1023 
1024 //  patterns and borders
1025 
1026 void ScViewFunc::ApplyPatternLines( const ScPatternAttr& rAttr, const SvxBoxItem& rNewOuter,
1027                                     const SvxBoxInfoItem* pNewInner )
1028 {
1029     ScDocument& rDoc = GetViewData().GetDocument();
1030     ScMarkData aFuncMark( GetViewData().GetMarkData() );       // local copy for UnmarkFiltered
1031     ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1032     bool bRecord = true;
1033     if (!rDoc.IsUndoEnabled())
1034         bRecord = false;
1035 
1036     bool bRemoveAdjCellBorder = rNewOuter.IsRemoveAdjacentCellBorder();
1037     ScRange aMarkRange, aMarkRangeWithEnvelope;
1038     aFuncMark.MarkToSimple();
1039     bool bMulti = aFuncMark.IsMultiMarked();
1040     if (bMulti)
1041         aFuncMark.GetMultiMarkArea( aMarkRange );
1042     else if (aFuncMark.IsMarked())
1043         aFuncMark.GetMarkArea( aMarkRange );
1044     else
1045     {
1046         aMarkRange = ScRange( GetViewData().GetCurX(),
1047                             GetViewData().GetCurY(), GetViewData().GetTabNo() );
1048         DoneBlockMode();
1049         InitOwnBlockMode();
1050         aFuncMark.SetMarkArea(aMarkRange);
1051         MarkDataChanged();
1052     }
1053     if( bRemoveAdjCellBorder )
1054         aFuncMark.GetSelectionCover( aMarkRangeWithEnvelope );
1055     else
1056         aMarkRangeWithEnvelope = aMarkRange;
1057 
1058     ScDocShell* pDocSh = GetViewData().GetDocShell();
1059 
1060     ScDocShellModificator aModificator( *pDocSh );
1061 
1062     if (bRecord)
1063     {
1064         ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1065         SCTAB nStartTab = aMarkRange.aStart.Tab();
1066         SCTAB nTabCount = rDoc.GetTableCount();
1067         bool bCopyOnlyMarked = false;
1068         if( !bRemoveAdjCellBorder )
1069             bCopyOnlyMarked = bMulti;
1070         pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
1071         for (const auto& rTab : aFuncMark)
1072             if (rTab != nStartTab)
1073                 pUndoDoc->AddUndoTab( rTab, rTab );
1074 
1075         ScRange aCopyRange = aMarkRangeWithEnvelope;
1076         aCopyRange.aStart.SetTab(0);
1077         aCopyRange.aEnd.SetTab(nTabCount-1);
1078         rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bCopyOnlyMarked, *pUndoDoc, &aFuncMark );
1079 
1080         pDocSh->GetUndoManager()->AddUndoAction(
1081             std::make_unique<ScUndoSelectionAttr>(
1082                 pDocSh, aFuncMark,
1083                 aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), aMarkRange.aStart.Tab(),
1084                 aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), aMarkRange.aEnd.Tab(),
1085                 std::move(pUndoDoc), bCopyOnlyMarked, &rAttr, &rNewOuter, pNewInner, &aMarkRangeWithEnvelope ) );
1086     }
1087 
1088     sal_uInt16 nExt = SC_PF_TESTMERGE;
1089     pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content before the change
1090 
1091     rDoc.ApplySelectionFrame(aFuncMark, rNewOuter, pNewInner);
1092 
1093     pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content after the change
1094 
1095     aFuncMark.MarkToMulti();
1096     rDoc.ApplySelectionPattern( rAttr, aFuncMark );
1097 
1098     pDocSh->PostPaint( aMarkRange, PaintPartFlags::Grid, nExt );
1099     pDocSh->UpdateOle(GetViewData());
1100     aModificator.SetDocumentModified();
1101     CellContentChanged();
1102 
1103     StartFormatArea();
1104 }
1105 
1106 //  pattern only
1107 
1108 void ScViewFunc::ApplySelectionPattern( const ScPatternAttr& rAttr, bool bCursorOnly )
1109 {
1110     ScViewData& rViewData   = GetViewData();
1111     ScDocShell* pDocSh      = rViewData.GetDocShell();
1112     ScDocument& rDoc        = pDocSh->GetDocument();
1113     ScMarkData aFuncMark( rViewData.GetMarkData() );       // local copy for UnmarkFiltered
1114     ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1115 
1116     bool bRecord = true;
1117     if (!rDoc.IsUndoEnabled())
1118         bRecord = false;
1119 
1120     //  State from old ItemSet doesn't matter for paint flags, as any change will be
1121     //  from SfxItemState::SET in the new ItemSet (default is ignored in ApplyPattern).
1122     //  New alignment is checked (check in PostPaint isn't enough) in case a right
1123     //  alignment is changed to left.
1124     const SfxItemSet& rNewSet = rAttr.GetItemSet();
1125     bool bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
1126                      rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
1127     bool bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
1128 
1129     sal_uInt16 nExtFlags = 0;
1130     if ( bSetLines )
1131         nExtFlags |= SC_PF_LINES;
1132     if ( bSetAlign )
1133         nExtFlags |= SC_PF_WHOLEROWS;
1134 
1135     ScDocShellModificator aModificator( *pDocSh );
1136 
1137     bool bMulti = aFuncMark.IsMultiMarked();
1138     aFuncMark.MarkToMulti();
1139     bool bOnlyTab = (!aFuncMark.IsMultiMarked() && !bCursorOnly && aFuncMark.GetSelectCount() > 1);
1140     if (bOnlyTab)
1141     {
1142         SCCOL nCol = rViewData.GetCurX();
1143         SCROW nRow = rViewData.GetCurY();
1144         SCTAB nTab = rViewData.GetTabNo();
1145         aFuncMark.SetMarkArea(ScRange(nCol,nRow,nTab));
1146         aFuncMark.MarkToMulti();
1147     }
1148 
1149     ScRangeList aChangeRanges;
1150 
1151     if (aFuncMark.IsMultiMarked() && !bCursorOnly)
1152     {
1153         ScRange aMarkRange;
1154         aFuncMark.GetMultiMarkArea( aMarkRange );
1155         SCTAB nTabCount = rDoc.GetTableCount();
1156         for (const auto& rTab : aFuncMark)
1157         {
1158             ScRange aChangeRange( aMarkRange );
1159             aChangeRange.aStart.SetTab( rTab );
1160             aChangeRange.aEnd.SetTab( rTab );
1161             aChangeRanges.push_back( aChangeRange );
1162         }
1163 
1164         SCCOL nStartCol = aMarkRange.aStart.Col();
1165         SCROW nStartRow = aMarkRange.aStart.Row();
1166         SCTAB nStartTab = aMarkRange.aStart.Tab();
1167         SCCOL nEndCol = aMarkRange.aEnd.Col();
1168         SCROW nEndRow = aMarkRange.aEnd.Row();
1169         SCTAB nEndTab = aMarkRange.aEnd.Tab();
1170 
1171         ScEditDataArray* pEditDataArray = nullptr;
1172         if (bRecord)
1173         {
1174             ScRange aCopyRange = aMarkRange;
1175             aCopyRange.aStart.SetTab(0);
1176             aCopyRange.aEnd.SetTab(nTabCount-1);
1177 
1178             ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1179             pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
1180             for (const auto& rTab : aFuncMark)
1181                 if (rTab != nStartTab)
1182                     pUndoDoc->AddUndoTab( rTab, rTab );
1183             rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &aFuncMark );
1184 
1185             aFuncMark.MarkToMulti();
1186 
1187             ScUndoSelectionAttr* pUndoAttr = new ScUndoSelectionAttr(
1188                 pDocSh, aFuncMark, nStartCol, nStartRow, nStartTab,
1189                 nEndCol, nEndRow, nEndTab, std::move(pUndoDoc), bMulti, &rAttr );
1190             pDocSh->GetUndoManager()->AddUndoAction(std::unique_ptr<ScUndoSelectionAttr>(pUndoAttr));
1191             pEditDataArray = pUndoAttr->GetDataArray();
1192         }
1193 
1194         rDoc.ApplySelectionPattern( rAttr, aFuncMark, pEditDataArray );
1195 
1196         pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
1197                            nEndCol,   nEndRow,   nEndTab,
1198                            PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
1199         pDocSh->UpdateOle(GetViewData());
1200         aModificator.SetDocumentModified();
1201         CellContentChanged();
1202     }
1203     else                            // single cell - simpler undo
1204     {
1205         SCCOL nCol = rViewData.GetCurX();
1206         SCROW nRow = rViewData.GetCurY();
1207         SCTAB nTab = rViewData.GetTabNo();
1208 
1209         std::unique_ptr<EditTextObject> pOldEditData;
1210         std::unique_ptr<EditTextObject> pNewEditData;
1211         ScAddress aPos(nCol, nRow, nTab);
1212         ScRefCellValue aCell(rDoc, aPos);
1213         if (aCell.meType == CELLTYPE_EDIT)
1214         {
1215             const EditTextObject* pEditObj = aCell.mpEditText;
1216             pOldEditData = pEditObj->Clone();
1217             rDoc.RemoveEditTextCharAttribs(aPos, rAttr);
1218             pEditObj = rDoc.GetEditText(aPos);
1219             pNewEditData = pEditObj->Clone();
1220         }
1221 
1222         aChangeRanges.push_back(aPos);
1223         std::optional<ScPatternAttr> pOldPat(*rDoc.GetPattern( nCol, nRow, nTab ));
1224 
1225         rDoc.ApplyPattern( nCol, nRow, nTab, rAttr );
1226 
1227         const ScPatternAttr* pNewPat = rDoc.GetPattern( nCol, nRow, nTab );
1228 
1229         if (bRecord)
1230         {
1231             std::unique_ptr<ScUndoCursorAttr> pUndo(new ScUndoCursorAttr(
1232                 pDocSh, nCol, nRow, nTab, &*pOldPat, pNewPat, &rAttr ));
1233             pUndo->SetEditData(std::move(pOldEditData), std::move(pNewEditData));
1234             pDocSh->GetUndoManager()->AddUndoAction(std::move(pUndo));
1235         }
1236         pOldPat.reset();     // is copied in undo (Pool)
1237 
1238         pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
1239         pDocSh->UpdateOle(GetViewData());
1240         aModificator.SetDocumentModified();
1241         CellContentChanged();
1242     }
1243 
1244     ScModelObj* pModelObj = HelperNotifyChanges::getMustPropagateChangesModel(*pDocSh);
1245     if (pModelObj)
1246     {
1247         css::uno::Sequence< css::beans::PropertyValue > aProperties;
1248         sal_Int32 nCount = 0;
1249         const SfxItemPropertyMap& rMap = ScCellObj::GetCellPropertyMap();
1250         for ( sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; ++nWhich )
1251         {
1252             const SfxPoolItem* pItem = nullptr;
1253             if ( rNewSet.GetItemState( nWhich, true, &pItem ) == SfxItemState::SET && pItem )
1254             {
1255                 for ( const auto pEntry : rMap.getPropertyEntries())
1256                 {
1257                     if ( pEntry->nWID == nWhich )
1258                     {
1259                         css::uno::Any aVal;
1260                         pItem->QueryValue( aVal, pEntry->nMemberId );
1261                         aProperties.realloc( nCount + 1 );
1262                         auto pProperties = aProperties.getArray();
1263                         pProperties[ nCount ].Name = pEntry->aName;
1264                         pProperties[ nCount ].Value = aVal;
1265                         ++nCount;
1266                     }
1267                 }
1268             }
1269         }
1270         HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "attribute", aProperties);
1271     }
1272 
1273     StartFormatArea();
1274 }
1275 
1276 void ScViewFunc::ApplyUserItemSet( const SfxItemSet& rItemSet )
1277 {
1278     //  ItemSet from UI, may have different pool
1279 
1280     bool bOnlyNotBecauseOfMatrix;
1281     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1282     {
1283         ErrorMessage(STR_PROTECTIONERR);
1284         return;
1285     }
1286 
1287     ScPatternAttr aNewAttrs( GetViewData().GetDocument().GetPool() );
1288     SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
1289     rNewSet.Put( rItemSet, false );
1290     ApplySelectionPattern( aNewAttrs );
1291 
1292     AdjustBlockHeight();
1293 }
1294 
1295 const SfxStyleSheet* ScViewFunc::GetStyleSheetFromMarked()
1296 {
1297     // Don't use UnmarkFiltered in slot state functions, for performance reasons.
1298     // The displayed state is always that of the whole selection including filtered rows.
1299 
1300     const ScStyleSheet* pSheet      = nullptr;
1301     ScViewData&         rViewData   = GetViewData();
1302     ScDocument&         rDoc        = rViewData.GetDocument();
1303     ScMarkData&         rMark       = rViewData.GetMarkData();
1304 
1305     if ( rMark.IsMarked() || rMark.IsMultiMarked() )
1306         pSheet = rDoc.GetSelectionStyle( rMark );                  // MarkToMulti isn't necessary
1307     else
1308         pSheet = rDoc.GetStyle( rViewData.GetCurX(),
1309                                 rViewData.GetCurY(),
1310                                 rViewData.GetTabNo() );
1311 
1312     return pSheet;
1313 }
1314 
1315 void ScViewFunc::SetStyleSheetToMarked( const SfxStyleSheet* pStyleSheet )
1316 {
1317     // not editable because of matrix only? attribute OK nonetheless
1318     bool bOnlyNotBecauseOfMatrix;
1319     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1320     {
1321         ErrorMessage(STR_PROTECTIONERR);
1322         return;
1323     }
1324 
1325     if ( !pStyleSheet) return;
1326 
1327     ScViewData& rViewData   = GetViewData();
1328     ScDocShell* pDocSh      = rViewData.GetDocShell();
1329     ScDocument& rDoc        = pDocSh->GetDocument();
1330     ScMarkData aFuncMark( rViewData.GetMarkData() );       // local copy for UnmarkFiltered
1331     ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1332     SCTAB nTabCount     = rDoc.GetTableCount();
1333     bool bRecord = true;
1334     if (!rDoc.IsUndoEnabled())
1335         bRecord = false;
1336 
1337     ScDocShellModificator aModificator( *pDocSh );
1338 
1339     if ( aFuncMark.IsMarked() || aFuncMark.IsMultiMarked() )
1340     {
1341         ScRange aMarkRange;
1342         aFuncMark.MarkToMulti();
1343         aFuncMark.GetMultiMarkArea( aMarkRange );
1344 
1345         if ( bRecord )
1346         {
1347             SCTAB nTab = rViewData.GetTabNo();
1348             ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1349             pUndoDoc->InitUndo( rDoc, nTab, nTab );
1350             for (const auto& rTab : aFuncMark)
1351                 if (rTab != nTab)
1352                     pUndoDoc->AddUndoTab( rTab, rTab );
1353 
1354             ScRange aCopyRange = aMarkRange;
1355             aCopyRange.aStart.SetTab(0);
1356             aCopyRange.aEnd.SetTab(nTabCount-1);
1357             rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aFuncMark );
1358             aFuncMark.MarkToMulti();
1359 
1360             OUString aName = pStyleSheet->GetName();
1361             pDocSh->GetUndoManager()->AddUndoAction(
1362                 std::make_unique<ScUndoSelectionStyle>( pDocSh, aFuncMark, aMarkRange, aName, std::move(pUndoDoc) ) );
1363         }
1364 
1365         rDoc.ApplySelectionStyle( static_cast<const ScStyleSheet&>(*pStyleSheet), aFuncMark );
1366 
1367         if (!AdjustBlockHeight())
1368             rViewData.GetDocShell()->PostPaint( aMarkRange, PaintPartFlags::Grid );
1369 
1370         aFuncMark.MarkToSimple();
1371     }
1372     else
1373     {
1374         SCCOL nCol = rViewData.GetCurX();
1375         SCROW nRow = rViewData.GetCurY();
1376         SCTAB nTab = rViewData.GetTabNo();
1377 
1378         if ( bRecord )
1379         {
1380             ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1381             pUndoDoc->InitUndo( rDoc, nTab, nTab );
1382             for (const auto& rTab : aFuncMark)
1383                 if (rTab != nTab)
1384                     pUndoDoc->AddUndoTab( rTab, rTab );
1385 
1386             ScRange aCopyRange( nCol, nRow, 0, nCol, nRow, nTabCount-1 );
1387             rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc );
1388 
1389             ScRange aMarkRange ( nCol, nRow, nTab );
1390             ScMarkData aUndoMark = aFuncMark;
1391             aUndoMark.SetMultiMarkArea( aMarkRange );
1392 
1393             OUString aName = pStyleSheet->GetName();
1394             pDocSh->GetUndoManager()->AddUndoAction(
1395                 std::make_unique<ScUndoSelectionStyle>( pDocSh, aUndoMark, aMarkRange, aName, std::move(pUndoDoc) ) );
1396         }
1397 
1398         for (const auto& rTab : aFuncMark)
1399             rDoc.ApplyStyle( nCol, nRow, rTab, static_cast<const ScStyleSheet&>(*pStyleSheet) );
1400 
1401         if (!AdjustBlockHeight())
1402             rViewData.GetDocShell()->PostPaintCell( nCol, nRow, nTab );
1403 
1404     }
1405 
1406     aModificator.SetDocumentModified();
1407 
1408     StartFormatArea();
1409 }
1410 
1411 void ScViewFunc::RemoveStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
1412 {
1413     if ( !pStyleSheet) return;
1414 
1415     ScViewData& rViewData   = GetViewData();
1416     ScDocument& rDoc        = rViewData.GetDocument();
1417     ScDocShell* pDocSh      = rViewData.GetDocShell();
1418 
1419     ScDocShellModificator aModificator( *pDocSh );
1420 
1421     ScopedVclPtrInstance< VirtualDevice > pVirtDev;
1422     pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
1423     rDoc.StyleSheetChanged( pStyleSheet, true, pVirtDev,
1424                                 rViewData.GetPPTX(),
1425                                 rViewData.GetPPTY(),
1426                                 rViewData.GetZoomX(),
1427                                 rViewData.GetZoomY() );
1428 
1429     pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
1430     aModificator.SetDocumentModified();
1431 
1432     ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
1433     if (pHdl)
1434         pHdl->ForgetLastPattern();
1435 }
1436 
1437 void ScViewFunc::UpdateStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
1438 {
1439     if ( !pStyleSheet) return;
1440 
1441     ScViewData& rViewData   = GetViewData();
1442     ScDocument& rDoc        = rViewData.GetDocument();
1443     ScDocShell* pDocSh      = rViewData.GetDocShell();
1444 
1445     ScDocShellModificator aModificator( *pDocSh );
1446 
1447     ScopedVclPtrInstance< VirtualDevice > pVirtDev;
1448     pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
1449     rDoc.StyleSheetChanged( pStyleSheet, false, pVirtDev,
1450                                 rViewData.GetPPTX(),
1451                                 rViewData.GetPPTY(),
1452                                 rViewData.GetZoomX(),
1453                                 rViewData.GetZoomY() );
1454 
1455     pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
1456     aModificator.SetDocumentModified();
1457 
1458     ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
1459     if (pHdl)
1460         pHdl->ForgetLastPattern();
1461 }
1462 
1463 
1464 void ScViewFunc::OnLOKInsertDeleteColumn(SCCOL nStartCol, tools::Long nOffset)
1465 {
1466     if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
1467         return;
1468 
1469     SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
1470     SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1471     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1472     while (pViewShell)
1473     {
1474         ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1475         if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1476         {
1477             if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
1478                 pPosHelper->invalidateByIndex(nStartCol);
1479 
1480             // if we remove a column the cursor position  and the current selection
1481             // in other views could need to be moved on the left by one column.
1482             if (pTabViewShell != this)
1483             {
1484                 if (pTabViewShell->getPart() == nCurrentTabIndex)
1485                 {
1486                     SCCOL nX = pTabViewShell->GetViewData().GetCurX();
1487                     if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
1488                     {
1489                         ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
1490                         SCROW nY = pTabViewShell->GetViewData().GetCurY();
1491                         pTabViewShell->SetCursor(nX + nOffset, nY);
1492                         if (pInputHdl && pInputHdl->IsInputMode())
1493                         {
1494                             pInputHdl->SetModified();
1495                         }
1496                     }
1497 
1498                     ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
1499                     aMultiMark.SetMarking( false );
1500                     aMultiMark.MarkToMulti();
1501                     if (aMultiMark.IsMultiMarked())
1502                     {
1503                         aMultiMark.ShiftCols(pTabViewShell->GetViewData().GetDocument(), nStartCol, nOffset);
1504                         pTabViewShell->SetMarkData(aMultiMark);
1505                     }
1506                 }
1507                 else
1508                 {
1509                     SCROW nX = pTabViewShell->GetViewData().GetCurXForTab(nCurrentTabIndex);
1510                     if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
1511                     {
1512                         pTabViewShell->GetViewData().SetCurXForTab(nX + nOffset, nCurrentTabIndex);
1513                     }
1514                 }
1515             }
1516         }
1517         pViewShell = SfxViewShell::GetNext(*pViewShell);
1518     }
1519 }
1520 
1521 void ScViewFunc::OnLOKInsertDeleteRow(SCROW nStartRow, tools::Long nOffset)
1522 {
1523     if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
1524         return;
1525 
1526     SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
1527     SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1528     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1529     while (pViewShell)
1530     {
1531         ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1532         if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1533         {
1534             if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
1535                 pPosHelper->invalidateByIndex(nStartRow);
1536 
1537             // if we remove a row the cursor position and the current selection
1538             // in other views could need to be moved up by one row.
1539             if (pTabViewShell != this)
1540             {
1541                 if (pTabViewShell->getPart() == nCurrentTabIndex)
1542                 {
1543                     SCROW nY = pTabViewShell->GetViewData().GetCurY();
1544                     if (nY > nStartRow || (nY == nStartRow && nOffset > 0))
1545                     {
1546                         ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
1547                         SCCOL nX = pTabViewShell->GetViewData().GetCurX();
1548                         pTabViewShell->SetCursor(nX, nY + nOffset);
1549                         if (pInputHdl && pInputHdl->IsInputMode())
1550                         {
1551                             pInputHdl->SetModified();
1552                         }
1553                     }
1554 
1555                     ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
1556                     aMultiMark.SetMarking( false );
1557                     aMultiMark.MarkToMulti();
1558                     if (aMultiMark.IsMultiMarked())
1559                     {
1560                         aMultiMark.ShiftRows(pTabViewShell->GetViewData().GetDocument(), nStartRow, nOffset);
1561                         pTabViewShell->SetMarkData(aMultiMark);
1562                     }
1563                 }
1564                 else
1565                 {
1566                     SCROW nY = pTabViewShell->GetViewData().GetCurYForTab(nCurrentTabIndex);
1567                     if (nY > nStartRow || (nY == nStartRow && nOffset > 0))
1568                     {
1569                         pTabViewShell->GetViewData().SetCurYForTab(nY + nOffset, nCurrentTabIndex);
1570                     }
1571                 }
1572             }
1573         }
1574         pViewShell = SfxViewShell::GetNext(*pViewShell);
1575     }
1576 }
1577 
1578 void ScViewFunc::OnLOKSetWidthOrHeight(SCCOLROW nStart, bool bWidth)
1579 {
1580     if (!comphelper::LibreOfficeKit::isActive())
1581         return;
1582 
1583     SCTAB nCurTab = GetViewData().GetTabNo();
1584     SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1585     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1586     while (pViewShell)
1587     {
1588         ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1589         if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1590         {
1591             if (bWidth)
1592             {
1593                 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurTab))
1594                     pPosHelper->invalidateByIndex(nStart);
1595             }
1596             else
1597             {
1598                 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurTab))
1599                     pPosHelper->invalidateByIndex(nStart);
1600             }
1601         }
1602         pViewShell = SfxViewShell::GetNext(*pViewShell);
1603     }
1604 }
1605 
1606 //  insert cells - undo OK
1607 
1608 bool ScViewFunc::InsertCells( InsCellCmd eCmd, bool bRecord, bool bPartOfPaste )
1609 {
1610     ScRange aRange;
1611     ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
1612     if (eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED)
1613     {
1614         ScDocShell* pDocSh = GetViewData().GetDocShell();
1615         const ScMarkData& rMark = GetViewData().GetMarkData();
1616         bool bSuccess = pDocSh->GetDocFunc().InsertCells( aRange, &rMark, eCmd, bRecord, false, bPartOfPaste );
1617         if (bSuccess)
1618         {
1619             ResetAutoSpellForContentChange();
1620             bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
1621             bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
1622 
1623             pDocSh->UpdateOle(GetViewData());
1624             CellContentChanged();
1625 
1626             if ( bInsertCols || bInsertRows )
1627             {
1628                 OUString aOperation = bInsertRows ?
1629                     OUString("insert-rows"):
1630                     OUString("insert-columns");
1631                 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
1632             }
1633 
1634             if (comphelper::LibreOfficeKit::isActive())
1635             {
1636                 if (bInsertCols)
1637                     ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
1638 
1639                 if (bInsertRows)
1640                     ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
1641 
1642                 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
1643                                                                     bInsertCols, bInsertRows, true /* bSizes*/,
1644                                                                     true /* bHidden */, true /* bFiltered */,
1645                                                                     true /* bGroups */, GetViewData().GetTabNo());
1646             }
1647         }
1648         OUString aStartAddress =  aRange.aStart.GetColRowString();
1649         OUString aEndAddress = aRange.aEnd.GetColRowString();
1650         collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "INSERT_CELLS");
1651         return bSuccess;
1652     }
1653     else
1654     {
1655         ErrorMessage(STR_NOMULTISELECT);
1656         return false;
1657     }
1658 }
1659 
1660 //  delete cells - undo OK
1661 
1662 void ScViewFunc::DeleteCells( DelCellCmd eCmd )
1663 {
1664     ScRange aRange;
1665     if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
1666     {
1667         ScDocShell* pDocSh = GetViewData().GetDocShell();
1668         const ScMarkData& rMark = GetViewData().GetMarkData();
1669 
1670 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1671         // #i94841# [Collaboration] if deleting rows is rejected, the content is sometimes wrong
1672         if ( pDocSh->IsDocShared() && ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols ) )
1673         {
1674             ScRange aDelRange( aRange.aStart );
1675             SCCOLROW nCount = 0;
1676             if ( eCmd == DelCellCmd::Rows )
1677             {
1678                 nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Row() - aRange.aStart.Row() + 1 );
1679             }
1680             else
1681             {
1682                 nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Col() - aRange.aStart.Col() + 1 );
1683             }
1684             while ( nCount > 0 )
1685             {
1686                 pDocSh->GetDocFunc().DeleteCells( aDelRange, &rMark, eCmd, false );
1687                 --nCount;
1688             }
1689         }
1690         else
1691 #endif
1692         {
1693             pDocSh->GetDocFunc().DeleteCells( aRange, &rMark, eCmd, false );
1694         }
1695 
1696         ResetAutoSpellForContentChange();
1697         pDocSh->UpdateOle(GetViewData());
1698         CellContentChanged();
1699 
1700         if ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols )
1701         {
1702             OUString aOperation = ( eCmd == DelCellCmd::Rows) ?
1703               OUString("delete-rows"):
1704               OUString("delete-columns");
1705             HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
1706         }
1707 
1708         //  put cursor directly behind deleted range
1709         SCCOL nCurX = GetViewData().GetCurX();
1710         SCROW nCurY = GetViewData().GetCurY();
1711         if ( eCmd==DelCellCmd::CellsLeft || eCmd==DelCellCmd::Cols )
1712             nCurX = aRange.aStart.Col();
1713         else
1714             nCurY = aRange.aStart.Row();
1715         SetCursor( nCurX, nCurY );
1716 
1717         if (comphelper::LibreOfficeKit::isActive())
1718         {
1719             bool bColsDeleted = (eCmd == DelCellCmd::Cols);
1720             bool bRowsDeleted = (eCmd == DelCellCmd::Rows);
1721             if (bColsDeleted)
1722                 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
1723 
1724             if (bRowsDeleted)
1725                 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
1726 
1727             ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
1728                                                                 bColsDeleted, bRowsDeleted, true /* bSizes*/,
1729                                                                 true /* bHidden */, true /* bFiltered */,
1730                                                                 true /* bGroups */, GetViewData().GetTabNo());
1731         }
1732     }
1733     else
1734     {
1735         if (eCmd == DelCellCmd::Cols)
1736             DeleteMulti( false );
1737         else if (eCmd == DelCellCmd::Rows)
1738             DeleteMulti( true );
1739         else
1740             ErrorMessage(STR_NOMULTISELECT);
1741     }
1742 
1743     OUString aStartAddress =  aRange.aStart.GetColRowString();
1744     OUString aEndAddress = aRange.aEnd.GetColRowString();
1745     collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "DELETE_CELLS");
1746 
1747     Unmark();
1748 }
1749 
1750 void ScViewFunc::DeleteMulti( bool bRows )
1751 {
1752     ScDocShell* pDocSh = GetViewData().GetDocShell();
1753     ScDocShellModificator aModificator( *pDocSh );
1754     SCTAB nTab = GetViewData().GetTabNo();
1755     ScDocument& rDoc = pDocSh->GetDocument();
1756     ScMarkData aFuncMark( GetViewData().GetMarkData() );       // local copy for UnmarkFiltered
1757     ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1758 
1759     bool bRecord = true;
1760     if (!rDoc.IsUndoEnabled())
1761         bRecord = false;
1762 
1763     std::vector<sc::ColRowSpan> aSpans;
1764     if (bRows)
1765         aSpans = aFuncMark.GetMarkedRowSpans();
1766     else
1767         aSpans = aFuncMark.GetMarkedColSpans();
1768 
1769     if (aSpans.empty())
1770     {
1771         SCCOLROW nCurPos = bRows ? GetViewData().GetCurY() : GetViewData().GetCurX();
1772         aSpans.emplace_back(nCurPos, nCurPos);
1773     }
1774 
1775     //  test if allowed
1776 
1777     TranslateId pErrorId;
1778     bool bNeedRefresh = false;
1779     for (size_t i = 0, n = aSpans.size(); i < n && !pErrorId; ++i)
1780     {
1781         SCCOLROW nStart = aSpans[i].mnStart;
1782         SCCOLROW nEnd = aSpans[i].mnEnd;
1783 
1784         SCCOL nStartCol, nEndCol;
1785         SCROW nStartRow, nEndRow;
1786         if ( bRows )
1787         {
1788             nStartCol = 0;
1789             nEndCol   = rDoc.MaxCol();
1790             nStartRow = static_cast<SCROW>(nStart);
1791             nEndRow   = static_cast<SCROW>(nEnd);
1792         }
1793         else
1794         {
1795             nStartCol = static_cast<SCCOL>(nStart);
1796             nEndCol   = static_cast<SCCOL>(nEnd);
1797             nStartRow = 0;
1798             nEndRow   = rDoc.MaxRow();
1799         }
1800 
1801         // cell protection (only needed for first range, as all following cells are moved)
1802         if (i == 0)
1803         {
1804             // test to the end of the sheet
1805             ScEditableTester aTester( rDoc, nTab, nStartCol, nStartRow, rDoc.MaxCol(), rDoc.MaxRow() );
1806             if (!aTester.IsEditable())
1807                 pErrorId = aTester.GetMessageId();
1808         }
1809 
1810         // merged cells
1811         SCCOL nMergeStartX = nStartCol;
1812         SCROW nMergeStartY = nStartRow;
1813         SCCOL nMergeEndX   = nEndCol;
1814         SCROW nMergeEndY   = nEndRow;
1815         rDoc.ExtendMerge( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
1816         rDoc.ExtendOverlapped( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
1817 
1818         if ( nMergeStartX != nStartCol || nMergeStartY != nStartRow )
1819         {
1820             // Disallow deleting parts of a merged cell.
1821             // Deleting the start is allowed (merge is removed), so the end doesn't have to be checked.
1822 
1823             pErrorId = STR_MSSG_DELETECELLS_0;
1824         }
1825         if ( nMergeEndX != nEndCol || nMergeEndY != nEndRow )
1826         {
1827             // detect if the start of a merged cell is deleted, so the merge flags can be refreshed
1828 
1829             bNeedRefresh = true;
1830         }
1831     }
1832 
1833     if (pErrorId)
1834     {
1835         ErrorMessage(pErrorId);
1836         return;
1837     }
1838 
1839     //  proceed
1840 
1841     weld::WaitObject aWait(GetViewData().GetDialogParent());      // important for TrackFormulas in UpdateReference
1842 
1843     ResetAutoSpellForContentChange();
1844 
1845     ScDocumentUniquePtr pUndoDoc;
1846     std::unique_ptr<ScRefUndoData> pUndoData;
1847     if (bRecord)
1848     {
1849         pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
1850         pUndoDoc->InitUndo( rDoc, nTab, nTab, !bRows, bRows );      // row height
1851 
1852         for (const sc::ColRowSpan & rSpan : aSpans)
1853         {
1854             SCCOLROW nStart = rSpan.mnStart;
1855             SCCOLROW nEnd = rSpan.mnEnd;
1856             if (bRows)
1857                 rDoc.CopyToDocument( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, InsertDeleteFlags::ALL,false,*pUndoDoc );
1858             else
1859                 rDoc.CopyToDocument( static_cast<SCCOL>(nStart),0,nTab,
1860                         static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
1861                         InsertDeleteFlags::ALL,false,*pUndoDoc );
1862         }
1863 
1864         //  all Formulas because of references
1865         SCTAB nTabCount = rDoc.GetTableCount();
1866         pUndoDoc->AddUndoTab( 0, nTabCount-1 );
1867         rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, InsertDeleteFlags::FORMULA,false,*pUndoDoc );
1868 
1869         pUndoData.reset(new ScRefUndoData( &rDoc ));
1870 
1871         rDoc.BeginDrawUndo();
1872     }
1873 
1874     std::vector<sc::ColRowSpan>::const_reverse_iterator ri = aSpans.rbegin(), riEnd = aSpans.rend();
1875     aFuncMark.SelectOneTable(nTab);
1876     for (; ri != riEnd; ++ri)
1877     {
1878         SCCOLROW nEnd = ri->mnEnd;
1879         SCCOLROW nStart = ri->mnStart;
1880 
1881         if (bRows)
1882         {
1883             rDoc.DeleteObjectsInArea(0, nStart, rDoc.MaxCol(), nEnd, aFuncMark, true);
1884             rDoc.DeleteRow(0, nTab, rDoc.MaxCol(), nTab, nStart, static_cast<SCSIZE>(nEnd - nStart + 1));
1885         }
1886         else
1887         {
1888             rDoc.DeleteObjectsInArea(nStart, 0, nEnd, rDoc.MaxRow(), aFuncMark, true);
1889             rDoc.DeleteCol(0, nTab, rDoc.MaxRow(), nTab, static_cast<SCCOL>(nStart), static_cast<SCSIZE>(nEnd - nStart + 1));
1890         }
1891     }
1892 
1893     if (bNeedRefresh)
1894     {
1895         SCCOLROW nFirstStart = aSpans[0].mnStart;
1896         SCCOL nStartCol = bRows ? 0 : static_cast<SCCOL>(nFirstStart);
1897         SCROW nStartRow = bRows ? static_cast<SCROW>(nFirstStart) : 0;
1898         SCCOL nEndCol = rDoc.MaxCol();
1899         SCROW nEndRow = rDoc.MaxRow();
1900 
1901         rDoc.RemoveFlagsTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, ScMF::Hor | ScMF::Ver );
1902         rDoc.ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab, true );
1903     }
1904 
1905     if (bRecord)
1906     {
1907         pDocSh->GetUndoManager()->AddUndoAction(
1908             std::make_unique<ScUndoDeleteMulti>(
1909                 pDocSh, bRows, bNeedRefresh, nTab, std::vector(aSpans), std::move(pUndoDoc), std::move(pUndoData)));
1910     }
1911 
1912     if (!AdjustRowHeight(0, rDoc.MaxRow(), true))
1913     {
1914         if (bRows)
1915         {
1916             pDocSh->PostPaint(
1917                 0, aSpans[0].mnStart, nTab,
1918                 rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Left));
1919         }
1920         else
1921         {
1922             pDocSh->PostPaint(
1923                 static_cast<SCCOL>(aSpans[0].mnStart), 0, nTab,
1924                 rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Top));
1925         }
1926     }
1927 
1928     aModificator.SetDocumentModified();
1929 
1930     CellContentChanged();
1931 
1932     //  put cursor directly behind the first deleted range
1933     SCCOL nCurX = GetViewData().GetCurX();
1934     SCROW nCurY = GetViewData().GetCurY();
1935     if ( bRows )
1936         nCurY = aSpans[0].mnStart;
1937     else
1938         nCurX = static_cast<SCCOL>(aSpans[0].mnStart);
1939     SetCursor( nCurX, nCurY );
1940 
1941     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
1942 }
1943 
1944 //  delete contents
1945 
1946 void ScViewFunc::DeleteContents( InsertDeleteFlags nFlags )
1947 {
1948     ScViewData& rViewData = GetViewData();
1949     rViewData.SetPasteMode( ScPasteFlags::NONE );
1950     rViewData.GetViewShell()->UpdateCopySourceOverlay();
1951 
1952     // not editable because of matrix only? attribute OK nonetheless
1953     bool bOnlyNotBecauseOfMatrix;
1954     bool bEditable = SelectionEditable( &bOnlyNotBecauseOfMatrix );
1955     if ( !bEditable )
1956     {
1957         if ( !(bOnlyNotBecauseOfMatrix &&
1958                 ((nFlags & (InsertDeleteFlags::ATTRIB | InsertDeleteFlags::EDITATTR)) == nFlags)) )
1959         {
1960             ErrorMessage(bOnlyNotBecauseOfMatrix ? STR_MATRIXFRAGMENTERR : STR_PROTECTIONERR);
1961             return;
1962         }
1963     }
1964 
1965     ScRange aMarkRange;
1966     bool bSimple = false;
1967 
1968     ScDocument& rDoc = GetViewData().GetDocument();
1969     ScDocShell* pDocSh = GetViewData().GetDocShell();
1970     ScMarkData aFuncMark( GetViewData().GetMarkData() );       // local copy for UnmarkFiltered
1971     ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1972 
1973     bool bRecord =true;
1974     if (!rDoc.IsUndoEnabled())
1975         bRecord = false;
1976 
1977     if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
1978     {
1979         aMarkRange.aStart.SetCol(GetViewData().GetCurX());
1980         aMarkRange.aStart.SetRow(GetViewData().GetCurY());
1981         aMarkRange.aStart.SetTab(GetViewData().GetTabNo());
1982         aMarkRange.aEnd = aMarkRange.aStart;
1983         if ( rDoc.HasAttrib( aMarkRange, HasAttrFlags::Merged ) )
1984         {
1985             aFuncMark.SetMarkArea( aMarkRange );
1986         }
1987         else
1988             bSimple = true;
1989     }
1990 
1991     HideAllCursors();   // for if summary is cancelled
1992 
1993     ScDocFunc& rDocFunc = pDocSh->GetDocFunc();
1994 
1995     // Can we really be sure that we can pass the bApi parameter as false to DeleteCell() and
1996     // DeleteContents() here? (Meaning that this is interactive use.) Is this never invoked from
1997     // scripting and whatnot?
1998     if (bSimple)
1999         rDocFunc.DeleteCell(aMarkRange.aStart, aFuncMark, nFlags, bRecord, /*bApi=*/ false);
2000     else
2001         rDocFunc.DeleteContents(aFuncMark, nFlags, bRecord, /*bApi=*/ false);
2002 
2003     pDocSh->UpdateOle(GetViewData());
2004 
2005     if (ScModelObj *pModelObj = HelperNotifyChanges::getMustPropagateChangesModel(*pDocSh))
2006     {
2007         ScRangeList aChangeRanges;
2008         if ( bSimple )
2009         {
2010             aChangeRanges.push_back( aMarkRange );
2011         }
2012         else
2013         {
2014             aFuncMark.FillRangeListWithMarks( &aChangeRanges, false );
2015         }
2016         HelperNotifyChanges::Notify(*pModelObj, aChangeRanges);
2017     }
2018 
2019     CellContentChanged();
2020     ShowAllCursors();
2021 
2022     if ( nFlags & InsertDeleteFlags::ATTRIB )
2023     {
2024         if ( nFlags & InsertDeleteFlags::CONTENTS )
2025             bFormatValid = false;
2026         else
2027             StartFormatArea();              // delete attribute is also attribute-change
2028     }
2029     OUString aStartAddress =  aMarkRange.aStart.GetColRowString();
2030     OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
2031     collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "DELETE");
2032 }
2033 
2034 //  column width/row height (via header) - undo OK
2035 
2036 void ScViewFunc::SetWidthOrHeight(
2037     bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, ScSizeMode eMode,
2038     sal_uInt16 nSizeTwips, bool bRecord, const ScMarkData* pMarkData )
2039 {
2040     if (rRanges.empty())
2041         return;
2042 
2043     // Use view's mark if none specified, but do not modify the original data,
2044     // i.e. no MarkToMulti() on that.
2045     ScMarkData aMarkData( pMarkData ? *pMarkData : GetViewData().GetMarkData());
2046 
2047     ScDocShell* pDocSh = GetViewData().GetDocShell();
2048     ScDocument& rDoc = pDocSh->GetDocument();
2049     SCCOL nCurX = GetViewData().GetCurX();
2050     SCROW nCurY = GetViewData().GetCurY();
2051     SCTAB nFirstTab = aMarkData.GetFirstSelected();
2052     SCTAB nCurTab = GetViewData().GetTabNo();
2053     if (bRecord && !rDoc.IsUndoEnabled())
2054         bRecord = false;
2055 
2056     ScDocShellModificator aModificator( *pDocSh );
2057 
2058     bool bAllowed = true;
2059     for (const SCTAB& nTab : aMarkData)
2060     {
2061         bAllowed = std::all_of(rRanges.begin(), rRanges.end(),
2062             [&bWidth, &rDoc, &nTab](const sc::ColRowSpan& rRange) {
2063                 bool bOnlyMatrix;
2064                 bool bIsBlockEditable;
2065                 if (bWidth)
2066                     bIsBlockEditable = rDoc.IsBlockEditable(nTab, rRange.mnStart, 0, rRange.mnEnd, rDoc.MaxRow(), &bOnlyMatrix);
2067                 else
2068                     bIsBlockEditable = rDoc.IsBlockEditable(nTab, 0, rRange.mnStart, rDoc.MaxCol(), rRange.mnEnd, &bOnlyMatrix);
2069                 return bIsBlockEditable || bOnlyMatrix;
2070             });
2071         if (!bAllowed)
2072             break;
2073     }
2074 
2075     // Allow users to resize cols/rows in readonly docs despite the r/o state.
2076     // It is frustrating to be unable to see content in mis-sized cells.
2077     if( !bAllowed && !pDocSh->IsReadOnly() )
2078     {
2079         ErrorMessage(STR_PROTECTIONERR);
2080         return;
2081     }
2082 
2083     SCCOLROW nStart = rRanges.front().mnStart;
2084     SCCOLROW nEnd = rRanges.back().mnEnd;
2085 
2086     OnLOKSetWidthOrHeight(nStart, bWidth);
2087 
2088     bool bFormula = false;
2089     if ( eMode == SC_SIZE_OPTIMAL )
2090     {
2091         const ScViewOptions& rOpts = GetViewData().GetOptions();
2092         bFormula = rOpts.GetOption( VOPT_FORMULAS );
2093     }
2094 
2095     ScDocumentUniquePtr pUndoDoc;
2096     std::unique_ptr<ScOutlineTable> pUndoTab;
2097     std::vector<sc::ColRowSpan> aUndoRanges;
2098 
2099     if ( bRecord )
2100     {
2101         rDoc.BeginDrawUndo();                          // Drawing Updates
2102 
2103         pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2104         for (const SCTAB& nTab : aMarkData)
2105         {
2106             if (bWidth)
2107             {
2108                 if ( nTab == nFirstTab )
2109                     pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
2110                 else
2111                     pUndoDoc->AddUndoTab( nTab, nTab, true );
2112                 rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab,
2113                         static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE,
2114                         false, *pUndoDoc );
2115             }
2116             else
2117             {
2118                 if ( nTab == nFirstTab )
2119                     pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
2120                 else
2121                     pUndoDoc->AddUndoTab( nTab, nTab, false, true );
2122                 rDoc.CopyToDocument( 0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
2123             }
2124         }
2125 
2126         aUndoRanges = rRanges;
2127 
2128         //! outlines from all tab?
2129         ScOutlineTable* pTable = rDoc.GetOutlineTable( nCurTab );
2130         if (pTable)
2131             pUndoTab.reset(new ScOutlineTable( *pTable ));
2132     }
2133 
2134     if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2135         aMarkData.MarkToMulti();
2136 
2137     bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT;
2138     bool bOutline = false;
2139 
2140     for (const SCTAB& nTab : aMarkData)
2141     {
2142         for (const sc::ColRowSpan & rRange : rRanges)
2143         {
2144             SCCOLROW nStartNo = rRange.mnStart;
2145             SCCOLROW nEndNo = rRange.mnEnd;
2146 
2147             if ( !bWidth )                      // height always blockwise
2148             {
2149                 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2150                 {
2151                     bool bAll = ( eMode==SC_SIZE_OPTIMAL );
2152                     if (!bAll)
2153                     {
2154                         //  delete CRFlags::ManualSize for all in range,
2155                         //  then SetOptimalHeight with bShrink = FALSE
2156                         for (SCROW nRow = nStartNo; nRow <= nEndNo; ++nRow)
2157                         {
2158                             SCROW nLastRow = nRow;
2159                             if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
2160                             {
2161                                 nRow = nLastRow;
2162                                 continue;
2163                             }
2164 
2165                             CRFlags nOld = rDoc.GetRowFlags(nRow, nTab);
2166                             if (nOld & CRFlags::ManualSize)
2167                                 rDoc.SetRowFlags(nRow, nTab, nOld & ~CRFlags::ManualSize);
2168                         }
2169                     }
2170 
2171                     double nPPTX = GetViewData().GetPPTX();
2172                     double nPPTY = GetViewData().GetPPTY();
2173                     Fraction aZoomX = GetViewData().GetZoomX();
2174                     Fraction aZoomY = GetViewData().GetZoomY();
2175 
2176                     ScSizeDeviceProvider aProv(pDocSh);
2177                     if (aProv.IsPrinter())
2178                     {
2179                         nPPTX = aProv.GetPPTX();
2180                         nPPTY = aProv.GetPPTY();
2181                         aZoomX = aZoomY = Fraction( 1, 1 );
2182                     }
2183 
2184                     sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
2185                     aCxt.setForceAutoSize(bAll);
2186                     aCxt.setExtraHeight(nSizeTwips);
2187                     rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, true);
2188                     if (bAll)
2189                         rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
2190 
2191                     //  Manual-Flag already (re)set in SetOptimalHeight in case of bAll=sal_True
2192                     //  (set for Extra-Height, else reset).
2193                 }
2194                 else if ( eMode==SC_SIZE_DIRECT )
2195                 {
2196                     if (nSizeTwips)
2197                     {
2198                         rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips );
2199                         rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true );          // height was set manually
2200                     }
2201 
2202                     rDoc.ShowRows( nStartNo, nEndNo, nTab, nSizeTwips != 0 );
2203 
2204                     if (!bShow && nStartNo <= nCurY && nCurY <= nEndNo && nTab == nCurTab)
2205                     {
2206                         nCurY = -1;
2207                     }
2208                 }
2209                 else if ( eMode==SC_SIZE_SHOW )
2210                 {
2211                     rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
2212                 }
2213             }
2214             else                                // column width
2215             {
2216                 for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++)
2217                 {
2218                     if ( eMode != SC_SIZE_VISOPT || !rDoc.ColHidden(nCol, nTab) )
2219                     {
2220                         sal_uInt16 nThisSize = nSizeTwips;
2221 
2222                         if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2223                             nThisSize = nSizeTwips + GetOptimalColWidth( nCol, nTab, bFormula );
2224                         if ( nThisSize )
2225                             rDoc.SetColWidth( nCol, nTab, nThisSize );
2226 
2227                         rDoc.ShowCol( nCol, nTab, bShow );
2228 
2229                         if (!bShow && nCol == nCurX && nTab == nCurTab)
2230                         {
2231                             nCurX = -1;
2232                         }
2233                     }
2234                 }
2235             }
2236 
2237             //  adjust outline
2238             if (bWidth)
2239             {
2240                 if ( rDoc.UpdateOutlineCol( static_cast<SCCOL>(nStartNo),
2241                             static_cast<SCCOL>(nEndNo), nTab, bShow ) )
2242                     bOutline = true;
2243             }
2244             else
2245             {
2246                 if ( rDoc.UpdateOutlineRow( nStartNo, nEndNo, nTab, bShow ) )
2247                     bOutline = true;
2248             }
2249         }
2250         rDoc.SetDrawPageSize(nTab);
2251     }
2252 
2253     if (!bOutline)
2254         pUndoTab.reset();
2255 
2256     if (bRecord)
2257     {
2258         pDocSh->GetUndoManager()->AddUndoAction(
2259             std::make_unique<ScUndoWidthOrHeight>(
2260                 pDocSh, aMarkData, nStart, nCurTab, nEnd, nCurTab,
2261                 std::move(pUndoDoc), std::move(aUndoRanges), std::move(pUndoTab), eMode, nSizeTwips, bWidth));
2262     }
2263 
2264     if (nCurX < 0)
2265     {
2266         MoveCursorRel( 1, 0, SC_FOLLOW_LINE, false );
2267     }
2268 
2269     if (nCurY < 0)
2270     {
2271         MoveCursorRel( 0, 1, SC_FOLLOW_LINE, false );
2272     }
2273 
2274     // fdo#36247 Ensure that the drawing layer's map mode scaling factors match
2275     // the new heights and widths.
2276     GetViewData().GetView()->RefreshZoom();
2277 
2278     for (const SCTAB& nTab : aMarkData)
2279         rDoc.UpdatePageBreaks( nTab );
2280 
2281     bool bAffectsVisibility = (eMode != SC_SIZE_ORIGINAL && eMode != SC_SIZE_VISOPT);
2282     ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
2283             bWidth /* bColumns */, !bWidth /* bRows */,
2284             true /* bSizes*/, bAffectsVisibility /* bHidden */, bAffectsVisibility /* bFiltered */,
2285             false /* bGroups */, nCurTab);
2286     GetViewData().GetView()->UpdateScrollBars(bWidth ? COLUMN_HEADER : ROW_HEADER);
2287 
2288     {
2289         for (const SCTAB& nTab : aMarkData)
2290         {
2291             if (bWidth)
2292             {
2293                 if (rDoc.HasAttrib( static_cast<SCCOL>(nStart),0,nTab,
2294                             static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
2295                             HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2296                     nStart = 0;
2297                 if (nStart > 0)             // go upwards because of Lines and cursor
2298                     --nStart;
2299                 pDocSh->PostPaint( static_cast<SCCOL>(nStart), 0, nTab,
2300                         rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Top );
2301             }
2302             else
2303             {
2304                 if (rDoc.HasAttrib( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2305                     nStart = 0;
2306                 if (nStart != 0)
2307                     --nStart;
2308                 pDocSh->PostPaint( 0, nStart, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Left );
2309             }
2310         }
2311 
2312         pDocSh->UpdateOle(GetViewData());
2313         if( !pDocSh->IsReadOnly() )
2314             aModificator.SetDocumentModified();
2315     }
2316 
2317     if ( !bWidth )
2318         return;
2319 
2320     ScModelObj* pModelObj = HelperNotifyChanges::getMustPropagateChangesModel(*pDocSh);
2321     if (!pModelObj)
2322         return;
2323 
2324     ScRangeList aChangeRanges;
2325     for (const SCTAB& nTab : aMarkData)
2326     {
2327         for (const sc::ColRowSpan & rRange : rRanges)
2328         {
2329             SCCOL nStartCol = rRange.mnStart;
2330             SCCOL nEndCol   = rRange.mnEnd;
2331             for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
2332             {
2333                 aChangeRanges.push_back( ScRange( nCol, 0, nTab ) );
2334             }
2335         }
2336     }
2337     HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "column-resize");
2338 }
2339 
2340 //  column width/row height (via marked range)
2341 
2342 void ScViewFunc::SetMarkedWidthOrHeight( bool bWidth, ScSizeMode eMode, sal_uInt16 nSizeTwips )
2343 {
2344     ScMarkData& rMark = GetViewData().GetMarkData();
2345 
2346     rMark.MarkToMulti();
2347     if (!rMark.IsMultiMarked())
2348     {
2349         SCCOL nCol = GetViewData().GetCurX();
2350         SCROW nRow = GetViewData().GetCurY();
2351         SCTAB nTab = GetViewData().GetTabNo();
2352         DoneBlockMode();
2353         InitOwnBlockMode();
2354         rMark.SetMultiMarkArea( ScRange( nCol,nRow,nTab ) );
2355         MarkDataChanged();
2356     }
2357 
2358     std::vector<sc::ColRowSpan> aRanges =
2359         bWidth ? rMark.GetMarkedColSpans() : rMark.GetMarkedRowSpans();
2360 
2361     SetWidthOrHeight(bWidth, aRanges, eMode, nSizeTwips);
2362 
2363     rMark.MarkToSimple();
2364 }
2365 
2366 void ScViewFunc::ModifyCellSize( ScDirection eDir, bool bOptimal )
2367 {
2368     //! step size adjustable
2369     //  step size is also minimum
2370     constexpr sal_uInt16 nStepX = STD_COL_WIDTH / 5;
2371     sal_uInt16 nStepY = ScGlobal::nStdRowHeight;
2372 
2373     ScModule* pScMod = SC_MOD();
2374     bool bAnyEdit = pScMod->IsInputMode();
2375     SCCOL nCol = GetViewData().GetCurX();
2376     SCROW nRow = GetViewData().GetCurY();
2377     SCTAB nTab = GetViewData().GetTabNo();
2378     ScDocShell* pDocSh = GetViewData().GetDocShell();
2379     ScDocument& rDoc = pDocSh->GetDocument();
2380 
2381     bool bAllowed, bOnlyMatrix;
2382     if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
2383         bAllowed = rDoc.IsBlockEditable( nTab, nCol,0, nCol,rDoc.MaxRow(), &bOnlyMatrix );
2384     else
2385         bAllowed = rDoc.IsBlockEditable( nTab, 0,nRow, rDoc.MaxCol(), nRow, &bOnlyMatrix );
2386     if ( !bAllowed && !bOnlyMatrix )
2387     {
2388         ErrorMessage(STR_PROTECTIONERR);
2389         return;
2390     }
2391 
2392     HideAllCursors();
2393 
2394     sal_uInt16 nWidth = rDoc.GetColWidth( nCol, nTab );
2395     sal_uInt16 nHeight = rDoc.GetRowHeight( nRow, nTab );
2396     std::vector<sc::ColRowSpan> aRange(1, sc::ColRowSpan(0,0));
2397     if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
2398     {
2399         if (bOptimal)               // width of this single cell
2400         {
2401             if ( bAnyEdit )
2402             {
2403                 //  when editing the actual entered width
2404                 ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
2405                 if (pHdl)
2406                 {
2407                     tools::Long nEdit = pHdl->GetTextSize().Width();       // in 0.01 mm
2408 
2409                     const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
2410                     const SvxMarginItem& rMItem = pPattern->GetItem(ATTR_MARGIN);
2411                     sal_uInt16 nMargin = rMItem.GetLeftMargin() + rMItem.GetRightMargin();
2412                     if ( pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Left )
2413                         nMargin = sal::static_int_cast<sal_uInt16>(
2414                             nMargin + pPattern->GetItem(ATTR_INDENT).GetValue() );
2415 
2416                     nWidth = std::round(o3tl::convert(nEdit * pDocSh->GetOutputFactor(),
2417                                                       o3tl::Length::mm100, o3tl::Length::twip))
2418                                 + nMargin + STD_EXTRA_WIDTH;
2419                 }
2420             }
2421             else
2422             {
2423                 double nPPTX = GetViewData().GetPPTX();
2424                 double nPPTY = GetViewData().GetPPTY();
2425                 Fraction aZoomX = GetViewData().GetZoomX();
2426                 Fraction aZoomY = GetViewData().GetZoomY();
2427 
2428                 ScSizeDeviceProvider aProv(pDocSh);
2429                 if (aProv.IsPrinter())
2430                 {
2431                     nPPTX = aProv.GetPPTX();
2432                     nPPTY = aProv.GetPPTY();
2433                     aZoomX = aZoomY = Fraction( 1, 1 );
2434                 }
2435 
2436                 tools::Long nPixel = rDoc.GetNeededSize( nCol, nRow, nTab, aProv.GetDevice(),
2437                                             nPPTX, nPPTY, aZoomX, aZoomY, true );
2438                 sal_uInt16 nTwips = static_cast<sal_uInt16>( nPixel / nPPTX );
2439                 if (nTwips != 0)
2440                     nWidth = nTwips + STD_EXTRA_WIDTH;
2441                 else
2442                     nWidth = STD_COL_WIDTH;
2443             }
2444         }
2445         else                        // increment / decrement
2446         {
2447             if ( eDir == DIR_RIGHT )
2448                 nWidth = sal::static_int_cast<sal_uInt16>( nWidth + nStepX );
2449             else if ( nWidth > nStepX )
2450                 nWidth = sal::static_int_cast<sal_uInt16>( nWidth - nStepX );
2451             if ( nWidth < nStepX ) nWidth = nStepX;
2452             if ( nWidth > MAX_COL_WIDTH ) nWidth = MAX_COL_WIDTH;
2453         }
2454         aRange[0].mnStart = nCol;
2455         aRange[0].mnEnd = nCol;
2456         SetWidthOrHeight(true, aRange, SC_SIZE_DIRECT, nWidth);
2457 
2458         //  adjust height of this row if width demands/allows this
2459 
2460         if (!bAnyEdit)
2461         {
2462             const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
2463             bool bNeedHeight =
2464                     pPattern->GetItem( ATTR_LINEBREAK ).GetValue() ||
2465                     pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block;
2466             if (bNeedHeight)
2467                 AdjustRowHeight( nRow, nRow, true );
2468         }
2469     }
2470     else
2471     {
2472         ScSizeMode eMode;
2473         if (bOptimal)
2474         {
2475             eMode = SC_SIZE_OPTIMAL;
2476             nHeight = 0;
2477         }
2478         else
2479         {
2480             eMode = SC_SIZE_DIRECT;
2481             if ( eDir == DIR_BOTTOM )
2482                 nHeight = sal::static_int_cast<sal_uInt16>( nHeight + nStepY );
2483             else if ( nHeight > nStepY )
2484                 nHeight = sal::static_int_cast<sal_uInt16>( nHeight - nStepY );
2485             if ( nHeight < nStepY ) nHeight = nStepY;
2486             if ( nHeight > MAX_ROW_HEIGHT ) nHeight = MAX_ROW_HEIGHT;
2487         }
2488         aRange[0].mnStart = nRow;
2489         aRange[0].mnEnd = nRow;
2490         SetWidthOrHeight(false, aRange, eMode, nHeight);
2491     }
2492 
2493     if ( bAnyEdit )
2494     {
2495         UpdateEditView();
2496         if ( rDoc.HasAttrib( nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::NeedHeight ) )
2497         {
2498             ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
2499             if (pHdl)
2500                 pHdl->SetModified();    // so that the height is adjusted with Enter
2501         }
2502     }
2503 
2504     ShowAllCursors();
2505 }
2506 
2507 void ScViewFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect )
2508 {
2509     if (nTab == TABLEID_DOC)
2510         return;
2511 
2512     ScMarkData& rMark = GetViewData().GetMarkData();
2513     ScDocShell* pDocSh = GetViewData().GetDocShell();
2514     ScDocument& rDoc = pDocSh->GetDocument();
2515     ScDocFunc &rFunc = pDocSh->GetDocFunc();
2516     bool bUndo(rDoc.IsUndoEnabled());
2517 
2518     //  modifying several tabs is handled here
2519 
2520     if (bUndo)
2521     {
2522         OUString aUndo = ScResId( STR_UNDO_PROTECT_TAB );
2523         pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2524     }
2525 
2526     for (const auto& rTab : rMark)
2527     {
2528         rFunc.ProtectSheet(rTab, rProtect);
2529     }
2530 
2531     if (bUndo)
2532         pDocSh->GetUndoManager()->LeaveListAction();
2533 
2534     UpdateLayerLocks();         //! broadcast to all views
2535 }
2536 
2537 void ScViewFunc::ProtectDoc( const OUString& rPassword )
2538 {
2539     ScDocShell* pDocSh = GetViewData().GetDocShell();
2540     ScDocFunc &rFunc = pDocSh->GetDocFunc();
2541 
2542     rFunc.Protect( TABLEID_DOC, rPassword );
2543 
2544     UpdateLayerLocks();         //! broadcast to all views
2545 }
2546 
2547 bool ScViewFunc::Unprotect( SCTAB nTab, const OUString& rPassword )
2548 {
2549     ScMarkData& rMark = GetViewData().GetMarkData();
2550     ScDocShell* pDocSh = GetViewData().GetDocShell();
2551     ScDocument& rDoc = pDocSh->GetDocument();
2552     ScDocFunc &rFunc = pDocSh->GetDocFunc();
2553     bool bChanged = false;
2554     bool bUndo (rDoc.IsUndoEnabled());
2555 
2556     if ( nTab == TABLEID_DOC || rMark.GetSelectCount() <= 1 )
2557     {
2558         bChanged = rFunc.Unprotect( nTab, rPassword, false );
2559         if (bChanged && nTab != TABLEID_DOC)
2560             SetTabProtectionSymbol(nTab, false);
2561     }
2562     else
2563     {
2564         //  modifying several tabs is handled here
2565 
2566         if (bUndo)
2567         {
2568             OUString aUndo = ScResId( STR_UNDO_UNPROTECT_TAB );
2569             pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2570         }
2571 
2572         for (const auto& rTab : rMark)
2573         {
2574             if ( rFunc.Unprotect( rTab, rPassword, false ) )
2575             {
2576                 bChanged = true;
2577                 SetTabProtectionSymbol( rTab, false);
2578             }
2579         }
2580 
2581         if (bUndo)
2582             pDocSh->GetUndoManager()->LeaveListAction();
2583     }
2584 
2585     if (bChanged)
2586         UpdateLayerLocks();     //! broadcast to all views
2587 
2588     return bChanged;
2589 }
2590 
2591 void ScViewFunc::SetNoteText( const ScAddress& rPos, const OUString& rNoteText )
2592 {
2593     GetViewData().GetDocShell()->GetDocFunc().SetNoteText( rPos, rNoteText, false );
2594 }
2595 
2596 void ScViewFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate )
2597 {
2598     GetViewData().GetDocShell()->GetDocFunc().ReplaceNote( rPos, rNoteText, pAuthor, pDate, false );
2599 }
2600 
2601 void ScViewFunc::SetNumberFormat( SvNumFormatType nFormatType, sal_uLong nAdd )
2602 {
2603     // not editable because of matrix only? attribute OK nonetheless
2604     bool bOnlyNotBecauseOfMatrix;
2605     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2606     {
2607         ErrorMessage(STR_PROTECTIONERR);
2608         return;
2609     }
2610 
2611     sal_uInt32          nNumberFormat = 0;
2612     ScViewData&         rViewData = GetViewData();
2613     ScDocument&         rDoc = rViewData.GetDocument();
2614     SvNumberFormatter*  pNumberFormatter = rDoc.GetFormatTable();
2615     LanguageType        eLanguage = ScGlobal::eLnge;
2616     ScPatternAttr       aNewAttrs( rDoc.GetPool() );
2617 
2618     //  always take language from cursor position, even if there is a selection
2619 
2620     sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat( rViewData.GetCurX(),
2621                                                             rViewData.GetCurY(),
2622                                                             rViewData.GetTabNo());
2623     const SvNumberformat* pEntry = pNumberFormatter->GetEntry( nCurrentNumberFormat );
2624     if (pEntry)
2625         eLanguage = pEntry->GetLanguage();      // else keep ScGlobal::eLnge
2626 
2627     nNumberFormat = pNumberFormatter->GetStandardFormat( nFormatType, eLanguage ) + nAdd;
2628 
2629     SfxItemSet& rSet = aNewAttrs.GetItemSet();
2630     rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
2631     //  ATTR_LANGUAGE_FORMAT not
2632     ApplySelectionPattern( aNewAttrs );
2633 }
2634 
2635 void ScViewFunc::SetNumFmtByStr( const OUString& rCode )
2636 {
2637     // not editable because of matrix only? attribute OK nonetheless
2638     bool bOnlyNotBecauseOfMatrix;
2639     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2640     {
2641         ErrorMessage(STR_PROTECTIONERR);
2642         return;
2643     }
2644 
2645     ScViewData&         rViewData = GetViewData();
2646     ScDocument&         rDoc = rViewData.GetDocument();
2647     SvNumberFormatter*  pFormatter = rDoc.GetFormatTable();
2648 
2649     //  language always from cursor position
2650 
2651     sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat( rViewData.GetCurX(), rViewData.GetCurY(),
2652                                                             rViewData.GetTabNo());
2653     const SvNumberformat* pEntry = pFormatter->GetEntry( nCurrentNumberFormat );
2654     LanguageType eLanguage = pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge;
2655 
2656     //  determine index for String
2657 
2658     bool bOk = true;
2659     sal_uInt32 nNumberFormat = pFormatter->GetEntryKey( rCode, eLanguage );
2660     if ( nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
2661     {
2662         //  enter new
2663 
2664         OUString    aFormat = rCode;    // will be changed
2665         sal_Int32   nErrPos = 0;
2666         SvNumFormatType nType   = SvNumFormatType::ALL;        //! ???
2667         bOk = pFormatter->PutEntry( aFormat, nErrPos, nType, nNumberFormat, eLanguage );
2668     }
2669 
2670     if ( bOk )          // valid format?
2671     {
2672         ScPatternAttr aNewAttrs( rDoc.GetPool() );
2673         SfxItemSet& rSet = aNewAttrs.GetItemSet();
2674         rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
2675         rSet.Put( SvxLanguageItem( eLanguage, ATTR_LANGUAGE_FORMAT ) );
2676         ApplySelectionPattern( aNewAttrs );
2677     }
2678 
2679     //! else return error / issue warning ???
2680 }
2681 
2682 void ScViewFunc::ChangeNumFmtDecimals( bool bIncrement )
2683 {
2684     // not editable because of matrix only? attribute OK nonetheless
2685     bool bOnlyNotBecauseOfMatrix;
2686     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2687     {
2688         ErrorMessage(STR_PROTECTIONERR);
2689         return;
2690     }
2691 
2692     ScDocument&         rDoc = GetViewData().GetDocument();
2693     SvNumberFormatter*  pFormatter = rDoc.GetFormatTable();
2694 
2695     SCCOL nCol = GetViewData().GetCurX();
2696     SCROW nRow = GetViewData().GetCurY();
2697     SCTAB nTab = GetViewData().GetTabNo();
2698 
2699     sal_uInt32 nOldFormat = rDoc.GetNumberFormat( nCol, nRow, nTab );
2700     const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
2701     if (!pOldEntry)
2702     {
2703         OSL_FAIL("numberformat not found !!!");
2704         return;
2705     }
2706 
2707     //  what have we got here?
2708 
2709     sal_uInt32 nNewFormat = nOldFormat;
2710     bool bError = false;
2711 
2712     LanguageType eLanguage = pOldEntry->GetLanguage();
2713     bool bThousand, bNegRed;
2714     sal_uInt16 nPrecision, nLeading;
2715     pOldEntry->GetFormatSpecialInfo( bThousand, bNegRed, nPrecision, nLeading );
2716 
2717     SvNumFormatType nOldType = pOldEntry->GetType();
2718     if ( SvNumFormatType::ALL == ( nOldType & (
2719                 SvNumFormatType::NUMBER |  SvNumFormatType::CURRENCY | SvNumFormatType::PERCENT | SvNumFormatType::SCIENTIFIC | SvNumFormatType::TIME ) ) )
2720     {
2721         //  date, fraction, logical, text can not be changed
2722         bError = true;
2723     }
2724 
2725     //! SvNumberformat has a Member bStandard, but doesn't disclose it
2726     bool bWasStandard = ( nOldFormat == pFormatter->GetStandardIndex( eLanguage ) );
2727     OUString sExponentialStandardFormat = "";
2728     if (bWasStandard)
2729     {
2730         //  with "Standard" the decimal places depend on cell content
2731         //  0 if empty or text -> no decimal places
2732         double nVal = rDoc.GetValue( ScAddress( nCol, nRow, nTab ) );
2733 
2734         //  the ways of the Numberformatters are unfathomable, so try:
2735         OUString aOut;
2736         const Color* pCol;
2737         const_cast<SvNumberformat*>(pOldEntry)->GetOutputString( nVal, aOut, &pCol );
2738 
2739         nPrecision = 0;
2740         // 'E' for exponential is fixed in Numberformatter
2741         sal_Int32 nIndexE = aOut.indexOf('E');
2742         if ( nIndexE >= 0 )
2743         {
2744           sExponentialStandardFormat = aOut.copy( nIndexE ).replace( '-', '+' );
2745           for ( sal_Int32 i=1 ; i<sExponentialStandardFormat.getLength() ; i++ )
2746           {
2747             if ( sExponentialStandardFormat[i] >= '1' && sExponentialStandardFormat[i] <= '9' )
2748               sExponentialStandardFormat = sExponentialStandardFormat.replaceAt( i, 1, u"0" );
2749           }
2750           aOut = aOut.copy( 0, nIndexE ); // remove exponential part
2751         }
2752         OUString aDecSep( pFormatter->GetFormatDecimalSep( nOldFormat ) );
2753         sal_Int32 nPos = aOut.indexOf( aDecSep );
2754         if ( nPos >= 0 )
2755             nPrecision = aOut.getLength() - nPos - aDecSep.getLength();
2756         // else keep 0
2757     }
2758     else
2759     {
2760         if ( (nOldType & SvNumFormatType::SCIENTIFIC) && !bThousand &&
2761              (pOldEntry->GetFormatIntegerDigits()%3 == 0) && pOldEntry->GetFormatIntegerDigits() > 0 )
2762             bThousand =  true;
2763     }
2764 
2765     if (!bError)
2766     {
2767         if (bIncrement)
2768         {
2769             if (nPrecision<20)
2770                 ++nPrecision;           // increment
2771             else
2772                 bError = true;          // 20 is maximum
2773         }
2774         else
2775         {
2776             if (nPrecision)
2777                 --nPrecision;           // decrement
2778             else
2779                 bError = true;          // 0 is minimum
2780         }
2781     }
2782 
2783     if (!bError)
2784     {
2785         OUString aNewPicture = pFormatter->GenerateFormat(nOldFormat, eLanguage,
2786                                                           bThousand, bNegRed,
2787                                                           nPrecision, nLeading)
2788                                 + sExponentialStandardFormat;
2789 
2790         nNewFormat = pFormatter->GetEntryKey( aNewPicture, eLanguage );
2791         if ( nNewFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
2792         {
2793             sal_Int32 nErrPos = 0;
2794             SvNumFormatType nNewType = SvNumFormatType::ALL;
2795             bool bOk = pFormatter->PutEntry( aNewPicture, nErrPos,
2796                                                 nNewType, nNewFormat, eLanguage );
2797             OSL_ENSURE( bOk, "incorrect numberformat generated" );
2798             if (!bOk)
2799                 bError = true;
2800         }
2801     }
2802 
2803     if (!bError)
2804     {
2805         ScPatternAttr aNewAttrs( rDoc.GetPool() );
2806         SfxItemSet& rSet = aNewAttrs.GetItemSet();
2807         rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
2808         //  ATTR_LANGUAGE_FORMAT not
2809         ApplySelectionPattern( aNewAttrs );
2810     }
2811 }
2812 
2813 void ScViewFunc::ChangeIndent( bool bIncrement )
2814 {
2815     ScViewData& rViewData = GetViewData();
2816     ScDocShell* pDocSh  = rViewData.GetDocShell();
2817     ScMarkData& rMark   = rViewData.GetMarkData();
2818 
2819     ScMarkData aWorkMark = rMark;
2820     ScViewUtil::UnmarkFiltered( aWorkMark, pDocSh->GetDocument() );
2821     aWorkMark.MarkToMulti();
2822     if (!aWorkMark.IsMultiMarked())
2823     {
2824         SCCOL nCol = rViewData.GetCurX();
2825         SCROW nRow = rViewData.GetCurY();
2826         SCTAB nTab = rViewData.GetTabNo();
2827         aWorkMark.SetMultiMarkArea( ScRange(nCol,nRow,nTab) );
2828     }
2829 
2830     bool bSuccess = pDocSh->GetDocFunc().ChangeIndent( aWorkMark, bIncrement, false );
2831     if (bSuccess)
2832     {
2833         pDocSh->UpdateOle(rViewData);
2834         StartFormatArea();
2835 
2836         // stuff for sidebar panels
2837         SfxBindings& rBindings = GetViewData().GetBindings();
2838         rBindings.Invalidate( SID_H_ALIGNCELL );
2839         rBindings.Invalidate( SID_ATTR_ALIGN_INDENT );
2840     }
2841 }
2842 
2843 bool ScViewFunc::InsertName( const OUString& rName, const OUString& rSymbol,
2844                                 const OUString& rType )
2845 {
2846     //  Type = P,R,C,F (and combinations)
2847     //! undo...
2848 
2849     bool bOk = false;
2850     ScDocShell* pDocSh = GetViewData().GetDocShell();
2851     ScDocument& rDoc = pDocSh->GetDocument();
2852     SCTAB nTab = GetViewData().GetTabNo();
2853     ScRangeName* pList = rDoc.GetRangeName();
2854 
2855     ScRangeData::Type nType = ScRangeData::Type::Name;
2856     auto pNewEntry = std::make_unique<ScRangeData>(
2857         rDoc, rName, rSymbol, ScAddress( GetViewData().GetCurX(),
2858         GetViewData().GetCurY(), nTab), nType );
2859     OUString aUpType = rType.toAsciiUpperCase();
2860     if ( aUpType.indexOf( 'P' ) != -1 )
2861         nType |= ScRangeData::Type::PrintArea;
2862     if ( aUpType.indexOf( 'R' ) != -1 )
2863         nType |= ScRangeData::Type::RowHeader;
2864     if ( aUpType.indexOf( 'C' ) != -1 )
2865         nType |= ScRangeData::Type::ColHeader;
2866     if ( aUpType.indexOf( 'F' ) != -1 )
2867         nType |= ScRangeData::Type::Criteria;
2868     pNewEntry->AddType(nType);
2869 
2870     if ( pNewEntry->GetErrCode() == FormulaError::NONE )     //  text valid?
2871     {
2872         ScDocShellModificator aModificator( *pDocSh );
2873 
2874         rDoc.PreprocessRangeNameUpdate();
2875 
2876         // input available yet? Then remove beforehand (=change)
2877         ScRangeData* pData = pList->findByUpperName(ScGlobal::getCharClass().uppercase(rName));
2878         if (pData)
2879         {                                   // take old Index
2880             pNewEntry->SetIndex(pData->GetIndex());
2881             pList->erase(*pData);
2882         }
2883 
2884         // don't delete, insert took ownership, even on failure!
2885         if ( pList->insert( pNewEntry.release() ) )
2886             bOk = true;
2887 
2888         rDoc.CompileHybridFormula();
2889 
2890         aModificator.SetDocumentModified();
2891         SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
2892     }
2893 
2894     return bOk;
2895 }
2896 
2897 void ScViewFunc::CreateNames( CreateNameFlags nFlags )
2898 {
2899     bool bDone = false;
2900     ScRange aRange;
2901     if ( GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE )
2902         bDone = GetViewData().GetDocShell()->GetDocFunc().CreateNames( aRange, nFlags, false );
2903 
2904     if (!bDone)
2905         ErrorMessage(STR_CREATENAME_MARKERR);
2906 }
2907 
2908 CreateNameFlags ScViewFunc::GetCreateNameFlags()
2909 {
2910     CreateNameFlags nFlags = CreateNameFlags::NONE;
2911 
2912     SCCOL nStartCol, nEndCol;
2913     SCROW nStartRow, nEndRow;
2914     SCTAB nDummy;
2915     if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nDummy,nEndCol,nEndRow,nDummy) == SC_MARK_SIMPLE)
2916     {
2917         ScDocument& rDoc = GetViewData().GetDocument();
2918         SCTAB nTab = GetViewData().GetTabNo();
2919         bool bOk;
2920         SCCOL i;
2921         SCROW j;
2922 
2923         bOk = true;
2924         SCCOL nFirstCol = nStartCol;
2925         SCCOL nLastCol  = nEndCol;
2926         if (nStartCol+1 < nEndCol) { ++nFirstCol; --nLastCol; }
2927         for (i=nFirstCol; i<=nLastCol && bOk; i++)
2928             if (!rDoc.HasStringData( i,nStartRow,nTab ))
2929                 bOk = false;
2930         if (bOk)
2931             nFlags |= CreateNameFlags::Top;
2932         else                            // Bottom only if not Top
2933         {
2934             bOk = true;
2935             for (i=nFirstCol; i<=nLastCol && bOk; i++)
2936                 if (!rDoc.HasStringData( i,nEndRow,nTab ))
2937                     bOk = false;
2938             if (bOk)
2939                 nFlags |= CreateNameFlags::Bottom;
2940         }
2941 
2942         bOk = true;
2943         SCROW nFirstRow = nStartRow;
2944         SCROW nLastRow  = nEndRow;
2945         if (nStartRow+1 < nEndRow) { ++nFirstRow; --nLastRow; }
2946         for (j=nFirstRow; j<=nLastRow && bOk; j++)
2947             if (!rDoc.HasStringData( nStartCol,j,nTab ))
2948                 bOk = false;
2949         if (bOk)
2950             nFlags |= CreateNameFlags::Left;
2951         else                            // Right only if not Left
2952         {
2953             bOk = true;
2954             for (j=nFirstRow; j<=nLastRow && bOk; j++)
2955                 if (!rDoc.HasStringData( nEndCol,j,nTab ))
2956                     bOk = false;
2957             if (bOk)
2958                 nFlags |= CreateNameFlags::Right;
2959         }
2960     }
2961 
2962     if (nStartCol == nEndCol)
2963         nFlags &= ~CreateNameFlags( CreateNameFlags::Left | CreateNameFlags::Right );
2964     if (nStartRow == nEndRow)
2965         nFlags &= ~CreateNameFlags( CreateNameFlags::Top | CreateNameFlags::Bottom );
2966 
2967     return nFlags;
2968 }
2969 
2970 void ScViewFunc::InsertNameList()
2971 {
2972     ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
2973     ScDocShell* pDocSh = GetViewData().GetDocShell();
2974     if ( pDocSh->GetDocFunc().InsertNameList( aPos, false ) )
2975         pDocSh->UpdateOle(GetViewData());
2976 }
2977 
2978 void ScViewFunc::UpdateSelectionArea( const ScMarkData& rSel, ScPatternAttr* pAttr  )
2979 {
2980     ScDocShell* pDocShell = GetViewData().GetDocShell();
2981     ScRange aMarkRange;
2982     if (rSel.IsMultiMarked() )
2983         rSel.GetMultiMarkArea( aMarkRange );
2984     else
2985         rSel.GetMarkArea( aMarkRange );
2986 
2987     bool bSetLines = false;
2988     bool bSetAlign = false;
2989     if ( pAttr )
2990     {
2991         const SfxItemSet& rNewSet = pAttr->GetItemSet();
2992         bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
2993         rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
2994         bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
2995     }
2996 
2997     sal_uInt16 nExtFlags = 0;
2998     if ( bSetLines )
2999         nExtFlags |= SC_PF_LINES;
3000     if ( bSetAlign )
3001         nExtFlags |= SC_PF_WHOLEROWS;
3002 
3003     SCCOL nStartCol = aMarkRange.aStart.Col();
3004     SCROW nStartRow = aMarkRange.aStart.Row();
3005     SCTAB nStartTab = aMarkRange.aStart.Tab();
3006     SCCOL nEndCol = aMarkRange.aEnd.Col();
3007     SCROW nEndRow = aMarkRange.aEnd.Row();
3008     SCTAB nEndTab = aMarkRange.aEnd.Tab();
3009     pDocShell->PostPaint( nStartCol, nStartRow, nStartTab,
3010         nEndCol,   nEndRow,   nEndTab,
3011         PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
3012     ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
3013     pTabViewShell->AdjustBlockHeight(false, const_cast<ScMarkData*>(&rSel));
3014 }
3015 
3016 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3017