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/zforlist.hxx>
33 #include <svl/zformat.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/weld.hxx>
36 #include <vcl/virdev.hxx>
37 #include <stdlib.h>
38 #include <unotools/charclass.hxx>
39 #include <vcl/uitest/logger.hxx>
40 #include <vcl/uitest/eventdescription.hxx>
41 #include <osl/diagnose.h>
42 
43 #include <viewfunc.hxx>
44 #include <tabvwsh.hxx>
45 #include <docsh.hxx>
46 #include <attrib.hxx>
47 #include <patattr.hxx>
48 #include <docpool.hxx>
49 #include <sc.hrc>
50 #include <strings.hrc>
51 #include <undocell.hxx>
52 #include <undoblk.hxx>
53 #include <refundo.hxx>
54 #include <olinetab.hxx>
55 #include <rangenam.hxx>
56 #include <globstr.hrc>
57 #include <global.hxx>
58 #include <stlsheet.hxx>
59 #include <editutil.hxx>
60 #include <formulacell.hxx>
61 #include <scresid.hxx>
62 #include <inputhdl.hxx>
63 #include <scmod.hxx>
64 #include <inputopt.hxx>
65 #include <compiler.hxx>
66 #include <docfunc.hxx>
67 #include <appoptio.hxx>
68 #include <sizedev.hxx>
69 #include <editable.hxx>
70 #include <scui_def.hxx>
71 #include <funcdesc.hxx>
72 #include <docuno.hxx>
73 #include <cellsuno.hxx>
74 #include <tokenarray.hxx>
75 #include <rowheightcontext.hxx>
76 #include <comphelper/lok.hxx>
77 #include <conditio.hxx>
78 #include <columnspanset.hxx>
79 
80 #include <memory>
81 
lcl_PostRepaintCondFormat(const ScConditionalFormat * pCondFmt,ScDocShell * pDocSh)82 static void lcl_PostRepaintCondFormat( const ScConditionalFormat *pCondFmt, ScDocShell *pDocSh )
83 {
84     if( pCondFmt )
85     {
86         const ScRangeList& rRanges = pCondFmt->GetRange();
87 
88         pDocSh->PostPaint( rRanges, PaintPartFlags::All );
89     }
90 }
91 
ScViewFunc(vcl::Window * pParent,ScDocShell & rDocSh,ScTabViewShell * pViewShell)92 ScViewFunc::ScViewFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
93     ScTabView( pParent, rDocSh, pViewShell ),
94     bFormatValid( false )
95 {
96 }
97 
~ScViewFunc()98 ScViewFunc::~ScViewFunc()
99 {
100 }
101 
102 namespace {
103 
collectUIInformation(const std::map<OUString,OUString> & aParameters,const OUString & rAction)104 void collectUIInformation(const std::map<OUString, OUString>& aParameters, const OUString& rAction)
105 {
106     EventDescription aDescription;
107     aDescription.aID = "grid_window";
108     aDescription.aAction = rAction;
109     aDescription.aParameters = aParameters;
110     aDescription.aParent = "MainWindow";
111     aDescription.aKeyWord = "ScGridWinUIObject";
112 
113     UITestLogger::getInstance().logEvent(aDescription);
114 }
115 
116 }
117 
StartFormatArea()118 void ScViewFunc::StartFormatArea()
119 {
120     //  anything to do?
121     if ( !SC_MOD()->GetInputOptions().GetExtendFormat() )
122         return;
123 
124     //  start only with single cell (marked or cursor position)
125     ScRange aMarkRange;
126     bool bOk = (GetViewData().GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE);
127     if ( bOk && aMarkRange.aStart != aMarkRange.aEnd )
128         bOk = false;
129 
130     if (bOk)
131     {
132         bFormatValid = true;
133         aFormatSource = aMarkRange.aStart;
134         aFormatArea = ScRange( aFormatSource );
135     }
136     else
137         bFormatValid = false;       // discard old range
138 }
139 
TestFormatArea(SCCOL nCol,SCROW nRow,SCTAB nTab,bool bAttrChanged)140 bool ScViewFunc::TestFormatArea( SCCOL nCol, SCROW nRow, SCTAB nTab, bool bAttrChanged )
141 {
142     //  anything to do?
143     if ( !SC_MOD()->GetInputOptions().GetExtendFormat() )
144         return false;
145 
146     //  Test: treat input with numberformat (bAttrChanged) always as new Attribute
147     //  (discard old Area ). If not wanted, discard if-statement
148     if ( bAttrChanged )
149     {
150         StartFormatArea();
151         return false;
152     }
153 
154     //! Test if cell empty ???
155 
156     bool bFound = false;
157     ScRange aNewRange = aFormatArea;
158     if ( bFormatValid && nTab == aFormatSource.Tab() )
159     {
160         if ( nRow >= aFormatArea.aStart.Row() && nRow <= aFormatArea.aEnd.Row() )
161         {
162             //  within range?
163             if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
164             {
165                 bFound = true;          // do not change range
166             }
167             //  left ?
168             if ( nCol+1 == aFormatArea.aStart.Col() )
169             {
170                 bFound = true;
171                 aNewRange.aStart.SetCol( nCol );
172             }
173             //  right ?
174             if ( nCol == aFormatArea.aEnd.Col()+1 )
175             {
176                 bFound = true;
177                 aNewRange.aEnd.SetCol( nCol );
178             }
179         }
180         if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
181         {
182             //  top ?
183             if ( nRow+1 == aFormatArea.aStart.Row() )
184             {
185                 bFound = true;
186                 aNewRange.aStart.SetRow( nRow );
187             }
188             //  bottom ?
189             if ( nRow == aFormatArea.aEnd.Row()+1 )
190             {
191                 bFound = true;
192                 aNewRange.aEnd.SetRow( nRow );
193             }
194         }
195     }
196 
197     if (bFound)
198         aFormatArea = aNewRange;    // extend
199     else
200         bFormatValid = false;       // outside of range -> break
201 
202     return bFound;
203 }
204 
DoAutoAttributes(SCCOL nCol,SCROW nRow,SCTAB nTab,bool bAttrChanged)205 void ScViewFunc::DoAutoAttributes( SCCOL nCol, SCROW nRow, SCTAB nTab,
206                                    bool bAttrChanged )
207 {
208     ScDocShell* pDocSh = GetViewData().GetDocShell();
209     ScDocument& rDoc = pDocSh->GetDocument();
210 
211     const ScPatternAttr* pSource = rDoc.GetPattern(
212                             aFormatSource.Col(), aFormatSource.Row(), nTab );
213     if ( !pSource->GetItem(ATTR_MERGE).IsMerged() )
214     {
215         ScRange aRange( nCol, nRow, nTab, nCol, nRow, nTab );
216         ScMarkData aMark(rDoc.GetSheetLimits());
217         aMark.SetMarkArea( aRange );
218 
219         ScDocFunc &rFunc = GetViewData().GetDocFunc();
220 
221         // pOldPattern is only valid until call to ApplyAttributes!
222         const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
223         const ScStyleSheet* pSrcStyle = pSource->GetStyleSheet();
224         if ( pSrcStyle && pSrcStyle != pOldPattern->GetStyleSheet() )
225             rFunc.ApplyStyle( aMark, pSrcStyle->GetName(), false );
226 
227         rFunc.ApplyAttributes( aMark, *pSource, false );
228     }
229 
230     if ( bAttrChanged )                             // value entered with number format?
231         aFormatSource.Set( nCol, nRow, nTab );      // then set a new source
232 }
233 
234 //      additional routines
235 
GetOptimalColWidth(SCCOL nCol,SCTAB nTab,bool bFormula)236 sal_uInt16 ScViewFunc::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, bool bFormula )
237 {
238     ScDocShell* pDocSh = GetViewData().GetDocShell();
239     ScDocument& rDoc = pDocSh->GetDocument();
240     ScMarkData& rMark = GetViewData().GetMarkData();
241 
242     double nPPTX = GetViewData().GetPPTX();
243     double nPPTY = GetViewData().GetPPTY();
244     Fraction aZoomX = GetViewData().GetZoomX();
245     Fraction aZoomY = GetViewData().GetZoomY();
246 
247     ScSizeDeviceProvider aProv(pDocSh);
248     if (aProv.IsPrinter())
249     {
250         nPPTX = aProv.GetPPTX();
251         nPPTY = aProv.GetPPTY();
252         aZoomX = aZoomY = Fraction( 1, 1 );
253     }
254 
255     sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, aProv.GetDevice(),
256                                 nPPTX, nPPTY, aZoomX, aZoomY, bFormula, &rMark );
257     return nTwips;
258 }
259 
SelectionEditable(bool * pOnlyNotBecauseOfMatrix)260 bool ScViewFunc::SelectionEditable( bool* pOnlyNotBecauseOfMatrix /* = NULL */ )
261 {
262     bool bRet;
263     ScDocument& rDoc = GetViewData().GetDocument();
264     ScMarkData& rMark = GetViewData().GetMarkData();
265     if (rMark.IsMarked() || rMark.IsMultiMarked())
266         bRet = rDoc.IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix );
267     else
268     {
269         SCCOL nCol = GetViewData().GetCurX();
270         SCROW nRow = GetViewData().GetCurY();
271         SCTAB nTab = GetViewData().GetTabNo();
272         bRet = rDoc.IsBlockEditable( nTab, nCol, nRow, nCol, nRow,
273             pOnlyNotBecauseOfMatrix );
274     }
275     return bRet;
276 }
277 
lcl_FunctionKnown(sal_uInt16 nOpCode)278 static bool lcl_FunctionKnown( sal_uInt16 nOpCode )
279 {
280     const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
281     if ( pFuncList )
282     {
283         sal_uLong nCount = pFuncList->GetCount();
284         for (sal_uLong i=0; i<nCount; i++)
285             if ( pFuncList->GetFunction(i)->nFIndex == nOpCode )
286                 return true;
287     }
288     return false;
289 }
290 
lcl_AddFunction(ScAppOptions & rAppOpt,sal_uInt16 nOpCode)291 static bool lcl_AddFunction( ScAppOptions& rAppOpt, sal_uInt16 nOpCode )
292 {
293     sal_uInt16 nOldCount = rAppOpt.GetLRUFuncListCount();
294     sal_uInt16* pOldList = rAppOpt.GetLRUFuncList();
295     sal_uInt16 nPos;
296     for (nPos=0; nPos<nOldCount; nPos++)
297         if (pOldList[nPos] == nOpCode)          // is the function already in the list?
298         {
299             if ( nPos == 0 )
300                 return false;                   // already at the top -> no change
301 
302             //  count doesn't change, so the original array is modified
303 
304             for (sal_uInt16 nCopy=nPos; nCopy>0; nCopy--)
305                 pOldList[nCopy] = pOldList[nCopy-1];
306             pOldList[0] = nOpCode;
307 
308             return true;                        // list has changed
309         }
310 
311     if ( !lcl_FunctionKnown( nOpCode ) )
312         return false;                           // not in function list -> no change
313 
314     sal_uInt16 nNewCount = std::min( static_cast<sal_uInt16>(nOldCount + 1), sal_uInt16(LRU_MAX) );
315     sal_uInt16 nNewList[LRU_MAX];
316     nNewList[0] = nOpCode;
317     for (nPos=1; nPos<nNewCount; nPos++)
318         nNewList[nPos] = pOldList[nPos-1];
319     rAppOpt.SetLRUFuncList( nNewList, nNewCount );
320 
321     return true;                                // list has changed
322 }
323 
324 namespace HelperNotifyChanges
325 {
NotifyIfChangesListeners(const ScDocShell & rDocShell,ScMarkData & rMark,SCCOL nCol,SCROW nRow)326     static void NotifyIfChangesListeners(const ScDocShell &rDocShell, ScMarkData& rMark, SCCOL nCol, SCROW nRow)
327     {
328         if (ScModelObj *pModelObj = getMustPropagateChangesModel(rDocShell))
329         {
330             ScRangeList aChangeRanges;
331             for (const auto& rTab : rMark)
332                 aChangeRanges.push_back( ScRange( nCol, nRow, rTab ) );
333 
334             HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "cell-change");
335         }
336     }
337 }
338 
339 //      actual functions
340 
341 //  input - undo OK
EnterData(SCCOL nCol,SCROW nRow,SCTAB nTab,const OUString & rString,const EditTextObject * pData)342 void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
343                             const OUString& rString,
344                             const EditTextObject* pData )
345 {
346     ScDocument& rDoc = GetViewData().GetDocument();
347     ScMarkData rMark(GetViewData().GetMarkData());
348     bool bRecord = rDoc.IsUndoEnabled();
349     SCTAB i;
350 
351     ScDocShell* pDocSh = GetViewData().GetDocShell();
352     ScDocFunc &rFunc = GetViewData().GetDocFunc();
353     ScDocShellModificator aModificator( *pDocSh );
354 
355     ScEditableTester aTester( rDoc, nCol,nRow, nCol,nRow, rMark );
356     if (!aTester.IsEditable())
357     {
358         ErrorMessage(aTester.GetMessageId());
359         PaintArea(nCol, nRow, nCol, nRow);        // possibly the edit-engine is still painted there
360         return;
361     }
362 
363     if ( bRecord )
364         rFunc.EnterListAction( STR_UNDO_ENTERDATA );
365 
366     bool bFormula = false;
367 
368     // a single '=' character is handled as string (needed for special filters)
369     if ( rString.getLength() > 1 )
370     {
371         if ( rString[0] == '=' )
372         {
373             // handle as formula
374             bFormula = true;
375         }
376         else if ( rString[0] == '+' || rString[0] == '-' )
377         {
378             // if there is more than one leading '+' or '-' character, remove the additional ones
379             sal_Int32 nIndex = 1;
380             sal_Int32 nLen = rString.getLength();
381             while ( nIndex < nLen && ( rString[ nIndex ] == '+' || rString[ nIndex ] == '-' ) )
382             {
383                 ++nIndex;
384             }
385             OUString aString = rString.replaceAt( 1, nIndex - 1, "" );
386 
387             // if the remaining part without the leading '+' or '-' character
388             // is non-empty and not a number, handle as formula
389             if ( aString.getLength() > 1 )
390             {
391                 sal_uInt32 nFormat = 0;
392                 rDoc.GetNumberFormat( nCol, nRow, nTab, nFormat );
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 
EnterValue(SCCOL nCol,SCROW nRow,SCTAB nTab,const double & rValue)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 
EnterData(SCCOL nCol,SCROW nRow,SCTAB nTab,const EditTextObject & rData,bool bTestSimple)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::GetSpaceDelimitedString(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 
EnterDataAtCursor(const OUString & rString)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 
EnterMatrix(const OUString & rString,::formula::FormulaGrammar::Grammar eGram)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, EMPTY_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 
GetSelectionScriptType()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 
GetSelectionPattern()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 
GetSelectionFrame(std::shared_ptr<SvxBoxItem> & rLineOuter,std::shared_ptr<SvxBoxInfoItem> & rLineInner)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 
ApplyAttributes(const SfxItemSet * pDialogSet,const SfxItemSet * pOldSet,bool bAdjustBlockHeight)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( std::make_unique<SfxItemSet>(*pOldSet) );
899     ScPatternAttr aNewAttrs( std::make_unique<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 
ApplyAttr(const SfxPoolItem & rAttrItem,bool bAdjustBlockHeight)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( std::make_unique<SfxItemSet>( *GetViewData().GetDocument().GetPool(),
1009                                             svl::Items<ATTR_PATTERN_START, ATTR_PATTERN_END>{} ) );
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 
ApplyPatternLines(const ScPatternAttr & rAttr,const SvxBoxItem & rNewOuter,const SvxBoxInfoItem * pNewInner)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 
ApplySelectionPattern(const ScPatternAttr & rAttr,bool bCursorOnly)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                         aProperties[ nCount ].Name = pEntry->aName;
1263                         aProperties[ nCount ].Value = aVal;
1264                         ++nCount;
1265                     }
1266                 }
1267             }
1268         }
1269         HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "attribute", aProperties);
1270     }
1271 
1272     StartFormatArea();
1273 }
1274 
ApplyUserItemSet(const SfxItemSet & rItemSet)1275 void ScViewFunc::ApplyUserItemSet( const SfxItemSet& rItemSet )
1276 {
1277     //  ItemSet from UI, may have different pool
1278 
1279     bool bOnlyNotBecauseOfMatrix;
1280     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1281     {
1282         ErrorMessage(STR_PROTECTIONERR);
1283         return;
1284     }
1285 
1286     ScPatternAttr aNewAttrs( GetViewData().GetDocument().GetPool() );
1287     SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
1288     rNewSet.Put( rItemSet, false );
1289     ApplySelectionPattern( aNewAttrs );
1290 
1291     AdjustBlockHeight();
1292 }
1293 
GetStyleSheetFromMarked()1294 const SfxStyleSheet* ScViewFunc::GetStyleSheetFromMarked()
1295 {
1296     // Don't use UnmarkFiltered in slot state functions, for performance reasons.
1297     // The displayed state is always that of the whole selection including filtered rows.
1298 
1299     const ScStyleSheet* pSheet      = nullptr;
1300     ScViewData&         rViewData   = GetViewData();
1301     ScDocument&         rDoc        = rViewData.GetDocument();
1302     ScMarkData&         rMark       = rViewData.GetMarkData();
1303 
1304     if ( rMark.IsMarked() || rMark.IsMultiMarked() )
1305         pSheet = rDoc.GetSelectionStyle( rMark );                  // MarkToMulti isn't necessary
1306     else
1307         pSheet = rDoc.GetStyle( rViewData.GetCurX(),
1308                                 rViewData.GetCurY(),
1309                                 rViewData.GetTabNo() );
1310 
1311     return pSheet;
1312 }
1313 
SetStyleSheetToMarked(const SfxStyleSheet * pStyleSheet)1314 void ScViewFunc::SetStyleSheetToMarked( const SfxStyleSheet* pStyleSheet )
1315 {
1316     // not editable because of matrix only? attribute OK nonetheless
1317     bool bOnlyNotBecauseOfMatrix;
1318     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1319     {
1320         ErrorMessage(STR_PROTECTIONERR);
1321         return;
1322     }
1323 
1324     if ( !pStyleSheet) return;
1325 
1326     ScViewData& rViewData   = GetViewData();
1327     ScDocShell* pDocSh      = rViewData.GetDocShell();
1328     ScDocument& rDoc        = pDocSh->GetDocument();
1329     ScMarkData aFuncMark( rViewData.GetMarkData() );       // local copy for UnmarkFiltered
1330     ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1331     SCTAB nTabCount     = rDoc.GetTableCount();
1332     bool bRecord = true;
1333     if (!rDoc.IsUndoEnabled())
1334         bRecord = false;
1335 
1336     ScDocShellModificator aModificator( *pDocSh );
1337 
1338     if ( aFuncMark.IsMarked() || aFuncMark.IsMultiMarked() )
1339     {
1340         ScRange aMarkRange;
1341         aFuncMark.MarkToMulti();
1342         aFuncMark.GetMultiMarkArea( aMarkRange );
1343 
1344         if ( bRecord )
1345         {
1346             SCTAB nTab = rViewData.GetTabNo();
1347             ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1348             pUndoDoc->InitUndo( rDoc, nTab, nTab );
1349             for (const auto& rTab : aFuncMark)
1350                 if (rTab != nTab)
1351                     pUndoDoc->AddUndoTab( rTab, rTab );
1352 
1353             ScRange aCopyRange = aMarkRange;
1354             aCopyRange.aStart.SetTab(0);
1355             aCopyRange.aEnd.SetTab(nTabCount-1);
1356             rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aFuncMark );
1357             aFuncMark.MarkToMulti();
1358 
1359             OUString aName = pStyleSheet->GetName();
1360             pDocSh->GetUndoManager()->AddUndoAction(
1361                 std::make_unique<ScUndoSelectionStyle>( pDocSh, aFuncMark, aMarkRange, aName, std::move(pUndoDoc) ) );
1362         }
1363 
1364         rDoc.ApplySelectionStyle( static_cast<const ScStyleSheet&>(*pStyleSheet), aFuncMark );
1365 
1366         if (!AdjustBlockHeight())
1367             rViewData.GetDocShell()->PostPaint( aMarkRange, PaintPartFlags::Grid );
1368 
1369         aFuncMark.MarkToSimple();
1370     }
1371     else
1372     {
1373         SCCOL nCol = rViewData.GetCurX();
1374         SCROW nRow = rViewData.GetCurY();
1375         SCTAB nTab = rViewData.GetTabNo();
1376 
1377         if ( bRecord )
1378         {
1379             ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1380             pUndoDoc->InitUndo( rDoc, nTab, nTab );
1381             for (const auto& rTab : aFuncMark)
1382                 if (rTab != nTab)
1383                     pUndoDoc->AddUndoTab( rTab, rTab );
1384 
1385             ScRange aCopyRange( nCol, nRow, 0, nCol, nRow, nTabCount-1 );
1386             rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc );
1387 
1388             ScRange aMarkRange ( nCol, nRow, nTab );
1389             ScMarkData aUndoMark = aFuncMark;
1390             aUndoMark.SetMultiMarkArea( aMarkRange );
1391 
1392             OUString aName = pStyleSheet->GetName();
1393             pDocSh->GetUndoManager()->AddUndoAction(
1394                 std::make_unique<ScUndoSelectionStyle>( pDocSh, aUndoMark, aMarkRange, aName, std::move(pUndoDoc) ) );
1395         }
1396 
1397         for (const auto& rTab : aFuncMark)
1398             rDoc.ApplyStyle( nCol, nRow, rTab, static_cast<const ScStyleSheet&>(*pStyleSheet) );
1399 
1400         if (!AdjustBlockHeight())
1401             rViewData.GetDocShell()->PostPaintCell( nCol, nRow, nTab );
1402 
1403     }
1404 
1405     aModificator.SetDocumentModified();
1406 
1407     StartFormatArea();
1408 }
1409 
RemoveStyleSheetInUse(const SfxStyleSheetBase * pStyleSheet)1410 void ScViewFunc::RemoveStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
1411 {
1412     if ( !pStyleSheet) return;
1413 
1414     ScViewData& rViewData   = GetViewData();
1415     ScDocument& rDoc        = rViewData.GetDocument();
1416     ScDocShell* pDocSh      = rViewData.GetDocShell();
1417 
1418     ScDocShellModificator aModificator( *pDocSh );
1419 
1420     ScopedVclPtrInstance< VirtualDevice > pVirtDev;
1421     pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
1422     rDoc.StyleSheetChanged( pStyleSheet, true, pVirtDev,
1423                                 rViewData.GetPPTX(),
1424                                 rViewData.GetPPTY(),
1425                                 rViewData.GetZoomX(),
1426                                 rViewData.GetZoomY() );
1427 
1428     pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
1429     aModificator.SetDocumentModified();
1430 
1431     ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
1432     if (pHdl)
1433         pHdl->ForgetLastPattern();
1434 }
1435 
UpdateStyleSheetInUse(const SfxStyleSheetBase * pStyleSheet)1436 void ScViewFunc::UpdateStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
1437 {
1438     if ( !pStyleSheet) return;
1439 
1440     ScViewData& rViewData   = GetViewData();
1441     ScDocument& rDoc        = rViewData.GetDocument();
1442     ScDocShell* pDocSh      = rViewData.GetDocShell();
1443 
1444     ScDocShellModificator aModificator( *pDocSh );
1445 
1446     ScopedVclPtrInstance< VirtualDevice > pVirtDev;
1447     pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
1448     rDoc.StyleSheetChanged( pStyleSheet, false, pVirtDev,
1449                                 rViewData.GetPPTX(),
1450                                 rViewData.GetPPTY(),
1451                                 rViewData.GetZoomX(),
1452                                 rViewData.GetZoomY() );
1453 
1454     pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
1455     aModificator.SetDocumentModified();
1456 
1457     ScInputHandler* pHdl = SC_MOD()->GetInputHdl();
1458     if (pHdl)
1459         pHdl->ForgetLastPattern();
1460 }
1461 
1462 
OnLOKInsertDeleteColumn(SCCOL nStartCol,tools::Long nOffset)1463 void ScViewFunc::OnLOKInsertDeleteColumn(SCCOL nStartCol, tools::Long nOffset)
1464 {
1465     if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
1466         return;
1467 
1468     SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
1469     SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1470     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1471     while (pViewShell)
1472     {
1473         ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1474         if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1475         {
1476             pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex)->invalidateByIndex(nStartCol);
1477 
1478             // if we remove a column the cursor position  and the current selection
1479             // in other views could need to be moved on the left by one column.
1480             if (pTabViewShell != this)
1481             {
1482                 if (pTabViewShell->getPart() == nCurrentTabIndex)
1483                 {
1484                     SCCOL nX = pTabViewShell->GetViewData().GetCurX();
1485                     if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
1486                     {
1487                         ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
1488                         SCROW nY = pTabViewShell->GetViewData().GetCurY();
1489                         pTabViewShell->SetCursor(nX + nOffset, nY);
1490                         if (pInputHdl && pInputHdl->IsInputMode())
1491                         {
1492                             pInputHdl->SetModified();
1493                         }
1494                     }
1495 
1496                     ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
1497                     aMultiMark.SetMarking( false );
1498                     aMultiMark.MarkToMulti();
1499                     if (aMultiMark.IsMultiMarked())
1500                     {
1501                         aMultiMark.ShiftCols(pTabViewShell->GetViewData().GetDocument(), nStartCol, nOffset);
1502                         pTabViewShell->SetMarkData(aMultiMark);
1503                     }
1504                 }
1505                 else
1506                 {
1507                     SCROW nX = pTabViewShell->GetViewData().GetCurXForTab(nCurrentTabIndex);
1508                     if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
1509                     {
1510                         pTabViewShell->GetViewData().SetCurXForTab(nX + nOffset, nCurrentTabIndex);
1511                     }
1512                 }
1513             }
1514         }
1515         pViewShell = SfxViewShell::GetNext(*pViewShell);
1516     }
1517 }
1518 
OnLOKInsertDeleteRow(SCROW nStartRow,tools::Long nOffset)1519 void ScViewFunc::OnLOKInsertDeleteRow(SCROW nStartRow, tools::Long nOffset)
1520 {
1521     if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
1522         return;
1523 
1524     SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
1525     SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1526     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1527     while (pViewShell)
1528     {
1529         ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1530         if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1531         {
1532             pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex)->invalidateByIndex(nStartRow);
1533 
1534             // if we remove a row the cursor position and the current selection
1535             // in other views could need to be moved up by one row.
1536             if (pTabViewShell != this)
1537             {
1538                 if (pTabViewShell->getPart() == nCurrentTabIndex)
1539                 {
1540                     SCROW nY = pTabViewShell->GetViewData().GetCurY();
1541                     if (nY > nStartRow || (nY == nStartRow && nOffset > 0))
1542                     {
1543                         ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
1544                         SCCOL nX = pTabViewShell->GetViewData().GetCurX();
1545                         pTabViewShell->SetCursor(nX, nY + nOffset);
1546                         if (pInputHdl && pInputHdl->IsInputMode())
1547                         {
1548                             pInputHdl->SetModified();
1549                         }
1550                     }
1551 
1552                     ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
1553                     aMultiMark.SetMarking( false );
1554                     aMultiMark.MarkToMulti();
1555                     if (aMultiMark.IsMultiMarked())
1556                     {
1557                         aMultiMark.ShiftRows(pTabViewShell->GetViewData().GetDocument(), nStartRow, nOffset);
1558                         pTabViewShell->SetMarkData(aMultiMark);
1559                     }
1560                 }
1561                 else
1562                 {
1563                     SCROW nY = pTabViewShell->GetViewData().GetCurYForTab(nCurrentTabIndex);
1564                     if (nY > nStartRow || (nY == nStartRow && nOffset > 0))
1565                     {
1566                         pTabViewShell->GetViewData().SetCurYForTab(nY + nOffset, nCurrentTabIndex);
1567                     }
1568                 }
1569             }
1570         }
1571         pViewShell = SfxViewShell::GetNext(*pViewShell);
1572     }
1573 }
1574 
OnLOKSetWidthOrHeight(SCCOLROW nStart,bool bWidth)1575 void ScViewFunc::OnLOKSetWidthOrHeight(SCCOLROW nStart, bool bWidth)
1576 {
1577     if (!comphelper::LibreOfficeKit::isActive())
1578         return;
1579 
1580     SCTAB nCurTab = GetViewData().GetTabNo();
1581     SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1582     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1583     while (pViewShell)
1584     {
1585         ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1586         if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1587         {
1588             if (bWidth)
1589                 pTabViewShell->GetViewData().GetLOKWidthHelper(nCurTab)->invalidateByIndex(nStart);
1590             else
1591                 pTabViewShell->GetViewData().GetLOKHeightHelper(nCurTab)->invalidateByIndex(nStart);
1592         }
1593         pViewShell = SfxViewShell::GetNext(*pViewShell);
1594     }
1595 }
1596 
1597 //  insert cells - undo OK
1598 
InsertCells(InsCellCmd eCmd,bool bRecord,bool bPartOfPaste)1599 bool ScViewFunc::InsertCells( InsCellCmd eCmd, bool bRecord, bool bPartOfPaste )
1600 {
1601     ScRange aRange;
1602     ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
1603     if (eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED)
1604     {
1605         ScDocShell* pDocSh = GetViewData().GetDocShell();
1606         const ScMarkData& rMark = GetViewData().GetMarkData();
1607         bool bSuccess = pDocSh->GetDocFunc().InsertCells( aRange, &rMark, eCmd, bRecord, false, bPartOfPaste );
1608         if (bSuccess)
1609         {
1610             ResetAutoSpellForContentChange();
1611             bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
1612             bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
1613 
1614             pDocSh->UpdateOle(GetViewData());
1615             CellContentChanged();
1616 
1617             if ( bInsertCols || bInsertRows )
1618             {
1619                 OUString aOperation = bInsertRows ?
1620                     OUString("insert-rows"):
1621                     OUString("insert-columns");
1622                 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
1623             }
1624 
1625             if (comphelper::LibreOfficeKit::isActive())
1626             {
1627                 if (bInsertCols)
1628                     ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
1629 
1630                 if (bInsertRows)
1631                     ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
1632 
1633                 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
1634                                                                     bInsertCols, bInsertRows, true /* bSizes*/,
1635                                                                     true /* bHidden */, true /* bFiltered */,
1636                                                                     true /* bGroups */, GetViewData().GetTabNo());
1637             }
1638         }
1639         OUString aStartAddress =  aRange.aStart.GetColRowString();
1640         OUString aEndAddress = aRange.aEnd.GetColRowString();
1641         collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "INSERT_CELLS");
1642         return bSuccess;
1643     }
1644     else
1645     {
1646         ErrorMessage(STR_NOMULTISELECT);
1647         return false;
1648     }
1649 }
1650 
1651 //  delete cells - undo OK
1652 
DeleteCells(DelCellCmd eCmd)1653 void ScViewFunc::DeleteCells( DelCellCmd eCmd )
1654 {
1655     ScRange aRange;
1656     if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
1657     {
1658         ScDocShell* pDocSh = GetViewData().GetDocShell();
1659         const ScMarkData& rMark = GetViewData().GetMarkData();
1660 
1661 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1662         // #i94841# [Collaboration] if deleting rows is rejected, the content is sometimes wrong
1663         if ( pDocSh->IsDocShared() && ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols ) )
1664         {
1665             ScRange aDelRange( aRange.aStart );
1666             SCCOLROW nCount = 0;
1667             if ( eCmd == DelCellCmd::Rows )
1668             {
1669                 nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Row() - aRange.aStart.Row() + 1 );
1670             }
1671             else
1672             {
1673                 nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Col() - aRange.aStart.Col() + 1 );
1674             }
1675             while ( nCount > 0 )
1676             {
1677                 pDocSh->GetDocFunc().DeleteCells( aDelRange, &rMark, eCmd, false );
1678                 --nCount;
1679             }
1680         }
1681         else
1682 #endif
1683         {
1684             pDocSh->GetDocFunc().DeleteCells( aRange, &rMark, eCmd, false );
1685         }
1686 
1687         ResetAutoSpellForContentChange();
1688         pDocSh->UpdateOle(GetViewData());
1689         CellContentChanged();
1690 
1691         if ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols )
1692         {
1693             OUString aOperation = ( eCmd == DelCellCmd::Rows) ?
1694               OUString("delete-rows"):
1695               OUString("delete-columns");
1696             HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
1697         }
1698 
1699         //  put cursor directly behind deleted range
1700         SCCOL nCurX = GetViewData().GetCurX();
1701         SCROW nCurY = GetViewData().GetCurY();
1702         if ( eCmd==DelCellCmd::CellsLeft || eCmd==DelCellCmd::Cols )
1703             nCurX = aRange.aStart.Col();
1704         else
1705             nCurY = aRange.aStart.Row();
1706         SetCursor( nCurX, nCurY );
1707 
1708         if (comphelper::LibreOfficeKit::isActive())
1709         {
1710             bool bColsDeleted = (eCmd == DelCellCmd::Cols);
1711             bool bRowsDeleted = (eCmd == DelCellCmd::Rows);
1712             if (bColsDeleted)
1713                 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
1714 
1715             if (bRowsDeleted)
1716                 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
1717 
1718             ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
1719                                                                 bColsDeleted, bRowsDeleted, true /* bSizes*/,
1720                                                                 true /* bHidden */, true /* bFiltered */,
1721                                                                 true /* bGroups */, GetViewData().GetTabNo());
1722         }
1723     }
1724     else
1725     {
1726         if (eCmd == DelCellCmd::Cols)
1727             DeleteMulti( false );
1728         else if (eCmd == DelCellCmd::Rows)
1729             DeleteMulti( true );
1730         else
1731             ErrorMessage(STR_NOMULTISELECT);
1732     }
1733 
1734     OUString aStartAddress =  aRange.aStart.GetColRowString();
1735     OUString aEndAddress = aRange.aEnd.GetColRowString();
1736     collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "DELETE_CELLS");
1737 
1738     Unmark();
1739 }
1740 
DeleteMulti(bool bRows)1741 void ScViewFunc::DeleteMulti( bool bRows )
1742 {
1743     ScDocShell* pDocSh = GetViewData().GetDocShell();
1744     ScDocShellModificator aModificator( *pDocSh );
1745     SCTAB nTab = GetViewData().GetTabNo();
1746     ScDocument& rDoc = pDocSh->GetDocument();
1747     ScMarkData aFuncMark( GetViewData().GetMarkData() );       // local copy for UnmarkFiltered
1748     ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1749 
1750     bool bRecord = true;
1751     if (!rDoc.IsUndoEnabled())
1752         bRecord = false;
1753 
1754     std::vector<sc::ColRowSpan> aSpans;
1755     if (bRows)
1756         aSpans = aFuncMark.GetMarkedRowSpans();
1757     else
1758         aSpans = aFuncMark.GetMarkedColSpans();
1759 
1760     if (aSpans.empty())
1761     {
1762         SCCOLROW nCurPos = bRows ? GetViewData().GetCurY() : GetViewData().GetCurX();
1763         aSpans.emplace_back(nCurPos, nCurPos);
1764     }
1765 
1766     //  test if allowed
1767 
1768     const char* pErrorId = nullptr;
1769     bool bNeedRefresh = false;
1770     for (size_t i = 0, n = aSpans.size(); i < n && !pErrorId; ++i)
1771     {
1772         SCCOLROW nStart = aSpans[i].mnStart;
1773         SCCOLROW nEnd = aSpans[i].mnEnd;
1774 
1775         SCCOL nStartCol, nEndCol;
1776         SCROW nStartRow, nEndRow;
1777         if ( bRows )
1778         {
1779             nStartCol = 0;
1780             nEndCol   = rDoc.MaxCol();
1781             nStartRow = static_cast<SCROW>(nStart);
1782             nEndRow   = static_cast<SCROW>(nEnd);
1783         }
1784         else
1785         {
1786             nStartCol = static_cast<SCCOL>(nStart);
1787             nEndCol   = static_cast<SCCOL>(nEnd);
1788             nStartRow = 0;
1789             nEndRow   = rDoc.MaxRow();
1790         }
1791 
1792         // cell protection (only needed for first range, as all following cells are moved)
1793         if (i == 0)
1794         {
1795             // test to the end of the sheet
1796             ScEditableTester aTester( rDoc, nTab, nStartCol, nStartRow, rDoc.MaxCol(), rDoc.MaxRow() );
1797             if (!aTester.IsEditable())
1798                 pErrorId = aTester.GetMessageId();
1799         }
1800 
1801         // merged cells
1802         SCCOL nMergeStartX = nStartCol;
1803         SCROW nMergeStartY = nStartRow;
1804         SCCOL nMergeEndX   = nEndCol;
1805         SCROW nMergeEndY   = nEndRow;
1806         rDoc.ExtendMerge( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
1807         rDoc.ExtendOverlapped( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
1808 
1809         if ( nMergeStartX != nStartCol || nMergeStartY != nStartRow )
1810         {
1811             // Disallow deleting parts of a merged cell.
1812             // Deleting the start is allowed (merge is removed), so the end doesn't have to be checked.
1813 
1814             pErrorId = STR_MSSG_DELETECELLS_0;
1815         }
1816         if ( nMergeEndX != nEndCol || nMergeEndY != nEndRow )
1817         {
1818             // detect if the start of a merged cell is deleted, so the merge flags can be refreshed
1819 
1820             bNeedRefresh = true;
1821         }
1822     }
1823 
1824     if (pErrorId)
1825     {
1826         ErrorMessage(pErrorId);
1827         return;
1828     }
1829 
1830     //  proceed
1831 
1832     weld::WaitObject aWait(GetViewData().GetDialogParent());      // important for TrackFormulas in UpdateReference
1833 
1834     ResetAutoSpellForContentChange();
1835 
1836     ScDocumentUniquePtr pUndoDoc;
1837     std::unique_ptr<ScRefUndoData> pUndoData;
1838     if (bRecord)
1839     {
1840         pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
1841         pUndoDoc->InitUndo( rDoc, nTab, nTab, !bRows, bRows );      // row height
1842 
1843         for (const sc::ColRowSpan & rSpan : aSpans)
1844         {
1845             SCCOLROW nStart = rSpan.mnStart;
1846             SCCOLROW nEnd = rSpan.mnEnd;
1847             if (bRows)
1848                 rDoc.CopyToDocument( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, InsertDeleteFlags::ALL,false,*pUndoDoc );
1849             else
1850                 rDoc.CopyToDocument( static_cast<SCCOL>(nStart),0,nTab,
1851                         static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
1852                         InsertDeleteFlags::ALL,false,*pUndoDoc );
1853         }
1854 
1855         //  all Formulas because of references
1856         SCTAB nTabCount = rDoc.GetTableCount();
1857         pUndoDoc->AddUndoTab( 0, nTabCount-1 );
1858         rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, InsertDeleteFlags::FORMULA,false,*pUndoDoc );
1859 
1860         pUndoData.reset(new ScRefUndoData( &rDoc ));
1861 
1862         rDoc.BeginDrawUndo();
1863     }
1864 
1865     std::vector<sc::ColRowSpan>::const_reverse_iterator ri = aSpans.rbegin(), riEnd = aSpans.rend();
1866     aFuncMark.SelectOneTable(nTab);
1867     for (; ri != riEnd; ++ri)
1868     {
1869         SCCOLROW nEnd = ri->mnEnd;
1870         SCCOLROW nStart = ri->mnStart;
1871 
1872         if (bRows)
1873         {
1874             rDoc.DeleteObjectsInArea(0, nStart, rDoc.MaxCol(), nEnd, aFuncMark, true);
1875             rDoc.DeleteRow(0, nTab, rDoc.MaxCol(), nTab, nStart, static_cast<SCSIZE>(nEnd - nStart + 1));
1876         }
1877         else
1878         {
1879             rDoc.DeleteObjectsInArea(nStart, 0, nEnd, rDoc.MaxRow(), aFuncMark, true);
1880             rDoc.DeleteCol(0, nTab, rDoc.MaxRow(), nTab, static_cast<SCCOL>(nStart), static_cast<SCSIZE>(nEnd - nStart + 1));
1881         }
1882     }
1883 
1884     if (bNeedRefresh)
1885     {
1886         SCCOLROW nFirstStart = aSpans[0].mnStart;
1887         SCCOL nStartCol = bRows ? 0 : static_cast<SCCOL>(nFirstStart);
1888         SCROW nStartRow = bRows ? static_cast<SCROW>(nFirstStart) : 0;
1889         SCCOL nEndCol = rDoc.MaxCol();
1890         SCROW nEndRow = rDoc.MaxRow();
1891 
1892         rDoc.RemoveFlagsTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, ScMF::Hor | ScMF::Ver );
1893         rDoc.ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab, true );
1894     }
1895 
1896     if (bRecord)
1897     {
1898         pDocSh->GetUndoManager()->AddUndoAction(
1899             std::make_unique<ScUndoDeleteMulti>(
1900                 pDocSh, bRows, bNeedRefresh, nTab, aSpans, std::move(pUndoDoc), std::move(pUndoData)));
1901     }
1902 
1903     if (!AdjustRowHeight(0, rDoc.MaxRow(), true))
1904     {
1905         if (bRows)
1906         {
1907             pDocSh->PostPaint(
1908                 0, aSpans[0].mnStart, nTab,
1909                 rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Left));
1910         }
1911         else
1912         {
1913             pDocSh->PostPaint(
1914                 static_cast<SCCOL>(aSpans[0].mnStart), 0, nTab,
1915                 rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Top));
1916         }
1917     }
1918 
1919     aModificator.SetDocumentModified();
1920 
1921     CellContentChanged();
1922 
1923     //  put cursor directly behind the first deleted range
1924     SCCOL nCurX = GetViewData().GetCurX();
1925     SCROW nCurY = GetViewData().GetCurY();
1926     if ( bRows )
1927         nCurY = aSpans[0].mnStart;
1928     else
1929         nCurX = static_cast<SCCOL>(aSpans[0].mnStart);
1930     SetCursor( nCurX, nCurY );
1931 
1932     SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
1933 }
1934 
1935 //  delete contents
1936 
DeleteContents(InsertDeleteFlags nFlags)1937 void ScViewFunc::DeleteContents( InsertDeleteFlags nFlags )
1938 {
1939     ScViewData& rViewData = GetViewData();
1940     rViewData.SetPasteMode( ScPasteFlags::NONE );
1941     rViewData.GetViewShell()->UpdateCopySourceOverlay();
1942 
1943     // not editable because of matrix only? attribute OK nonetheless
1944     bool bOnlyNotBecauseOfMatrix;
1945     bool bEditable = SelectionEditable( &bOnlyNotBecauseOfMatrix );
1946     if ( !bEditable )
1947     {
1948         if ( !(bOnlyNotBecauseOfMatrix &&
1949                 ((nFlags & (InsertDeleteFlags::ATTRIB | InsertDeleteFlags::EDITATTR)) == nFlags)) )
1950         {
1951             ErrorMessage(bOnlyNotBecauseOfMatrix ? STR_MATRIXFRAGMENTERR : STR_PROTECTIONERR);
1952             return;
1953         }
1954     }
1955 
1956     ScRange aMarkRange;
1957     bool bSimple = false;
1958 
1959     ScDocument& rDoc = GetViewData().GetDocument();
1960     ScDocShell* pDocSh = GetViewData().GetDocShell();
1961     ScMarkData aFuncMark( GetViewData().GetMarkData() );       // local copy for UnmarkFiltered
1962     ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1963 
1964     bool bRecord =true;
1965     if (!rDoc.IsUndoEnabled())
1966         bRecord = false;
1967 
1968     if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
1969     {
1970         aMarkRange.aStart.SetCol(GetViewData().GetCurX());
1971         aMarkRange.aStart.SetRow(GetViewData().GetCurY());
1972         aMarkRange.aStart.SetTab(GetViewData().GetTabNo());
1973         aMarkRange.aEnd = aMarkRange.aStart;
1974         if ( rDoc.HasAttrib( aMarkRange, HasAttrFlags::Merged ) )
1975         {
1976             aFuncMark.SetMarkArea( aMarkRange );
1977         }
1978         else
1979             bSimple = true;
1980     }
1981 
1982     HideAllCursors();   // for if summary is cancelled
1983 
1984     ScDocFunc& rDocFunc = pDocSh->GetDocFunc();
1985 
1986     // Can we really be sure that we can pass the bApi parameter as false to DeleteCell() and
1987     // DeleteContents() here? (Meaning that this is interactive use.) Is this never invoked from
1988     // scripting and whatnot?
1989     if (bSimple)
1990         rDocFunc.DeleteCell(aMarkRange.aStart, aFuncMark, nFlags, bRecord, /*bApi=*/ false);
1991     else
1992         rDocFunc.DeleteContents(aFuncMark, nFlags, bRecord, /*bApi=*/ false);
1993 
1994     pDocSh->UpdateOle(GetViewData());
1995 
1996     if (ScModelObj *pModelObj = HelperNotifyChanges::getMustPropagateChangesModel(*pDocSh))
1997     {
1998         ScRangeList aChangeRanges;
1999         if ( bSimple )
2000         {
2001             aChangeRanges.push_back( aMarkRange );
2002         }
2003         else
2004         {
2005             aFuncMark.FillRangeListWithMarks( &aChangeRanges, false );
2006         }
2007         HelperNotifyChanges::Notify(*pModelObj, aChangeRanges);
2008     }
2009 
2010     CellContentChanged();
2011     ShowAllCursors();
2012 
2013     if ( nFlags & InsertDeleteFlags::ATTRIB )
2014     {
2015         if ( nFlags & InsertDeleteFlags::CONTENTS )
2016             bFormatValid = false;
2017         else
2018             StartFormatArea();              // delete attribute is also attribute-change
2019     }
2020     OUString aStartAddress =  aMarkRange.aStart.GetColRowString();
2021     OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
2022     collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "DELETE");
2023 }
2024 
2025 //  column width/row height (via header) - undo OK
2026 
SetWidthOrHeight(bool bWidth,const std::vector<sc::ColRowSpan> & rRanges,ScSizeMode eMode,sal_uInt16 nSizeTwips,bool bRecord,const ScMarkData * pMarkData)2027 void ScViewFunc::SetWidthOrHeight(
2028     bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, ScSizeMode eMode,
2029     sal_uInt16 nSizeTwips, bool bRecord, const ScMarkData* pMarkData )
2030 {
2031     if (rRanges.empty())
2032         return;
2033 
2034     // Use view's mark if none specified, but do not modify the original data,
2035     // i.e. no MarkToMulti() on that.
2036     ScMarkData aMarkData( pMarkData ? *pMarkData : GetViewData().GetMarkData());
2037 
2038     ScDocShell* pDocSh = GetViewData().GetDocShell();
2039     ScDocument& rDoc = pDocSh->GetDocument();
2040     SCCOL nCurX = GetViewData().GetCurX();
2041     SCROW nCurY = GetViewData().GetCurY();
2042     SCTAB nFirstTab = aMarkData.GetFirstSelected();
2043     SCTAB nCurTab = GetViewData().GetTabNo();
2044     if (bRecord && !rDoc.IsUndoEnabled())
2045         bRecord = false;
2046 
2047     ScDocShellModificator aModificator( *pDocSh );
2048 
2049     bool bAllowed = true;
2050     for (const SCTAB& nTab : aMarkData)
2051     {
2052         bAllowed = std::all_of(rRanges.begin(), rRanges.end(),
2053             [&bWidth, &rDoc, &nTab](const sc::ColRowSpan& rRange) {
2054                 bool bOnlyMatrix;
2055                 bool bIsBlockEditable;
2056                 if (bWidth)
2057                     bIsBlockEditable = rDoc.IsBlockEditable(nTab, rRange.mnStart, 0, rRange.mnEnd, rDoc.MaxRow(), &bOnlyMatrix);
2058                 else
2059                     bIsBlockEditable = rDoc.IsBlockEditable(nTab, 0, rRange.mnStart, rDoc.MaxCol(), rRange.mnEnd, &bOnlyMatrix);
2060                 return bIsBlockEditable || bOnlyMatrix;
2061             });
2062         if (!bAllowed)
2063             break;
2064     }
2065 
2066     // Allow users to resize cols/rows in readonly docs despite the r/o state.
2067     // It is frustrating to be unable to see content in mis-sized cells.
2068     if( !bAllowed && !pDocSh->IsReadOnly() )
2069     {
2070         ErrorMessage(STR_PROTECTIONERR);
2071         return;
2072     }
2073 
2074     SCCOLROW nStart = rRanges.front().mnStart;
2075     SCCOLROW nEnd = rRanges.back().mnEnd;
2076 
2077     OnLOKSetWidthOrHeight(nStart, bWidth);
2078 
2079     bool bFormula = false;
2080     if ( eMode == SC_SIZE_OPTIMAL )
2081     {
2082         const ScViewOptions& rOpts = GetViewData().GetOptions();
2083         bFormula = rOpts.GetOption( VOPT_FORMULAS );
2084     }
2085 
2086     ScDocumentUniquePtr pUndoDoc;
2087     std::unique_ptr<ScOutlineTable> pUndoTab;
2088     std::vector<sc::ColRowSpan> aUndoRanges;
2089 
2090     if ( bRecord )
2091     {
2092         rDoc.BeginDrawUndo();                          // Drawing Updates
2093 
2094         pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2095         for (const SCTAB& nTab : aMarkData)
2096         {
2097             if (bWidth)
2098             {
2099                 if ( nTab == nFirstTab )
2100                     pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
2101                 else
2102                     pUndoDoc->AddUndoTab( nTab, nTab, true );
2103                 rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab,
2104                         static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE,
2105                         false, *pUndoDoc );
2106             }
2107             else
2108             {
2109                 if ( nTab == nFirstTab )
2110                     pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
2111                 else
2112                     pUndoDoc->AddUndoTab( nTab, nTab, false, true );
2113                 rDoc.CopyToDocument( 0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
2114             }
2115         }
2116 
2117         aUndoRanges = rRanges;
2118 
2119         //! outlines from all tab?
2120         ScOutlineTable* pTable = rDoc.GetOutlineTable( nCurTab );
2121         if (pTable)
2122             pUndoTab.reset(new ScOutlineTable( *pTable ));
2123     }
2124 
2125     if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2126         aMarkData.MarkToMulti();
2127 
2128     bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT;
2129     bool bOutline = false;
2130 
2131     for (const SCTAB& nTab : aMarkData)
2132     {
2133         for (const sc::ColRowSpan & rRange : rRanges)
2134         {
2135             SCCOLROW nStartNo = rRange.mnStart;
2136             SCCOLROW nEndNo = rRange.mnEnd;
2137 
2138             if ( !bWidth )                      // height always blockwise
2139             {
2140                 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2141                 {
2142                     bool bAll = ( eMode==SC_SIZE_OPTIMAL );
2143                     if (!bAll)
2144                     {
2145                         //  delete CRFlags::ManualSize for all in range,
2146                         //  then SetOptimalHeight with bShrink = FALSE
2147                         for (SCROW nRow = nStartNo; nRow <= nEndNo; ++nRow)
2148                         {
2149                             SCROW nLastRow = nRow;
2150                             if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
2151                             {
2152                                 nRow = nLastRow;
2153                                 continue;
2154                             }
2155 
2156                             CRFlags nOld = rDoc.GetRowFlags(nRow, nTab);
2157                             if (nOld & CRFlags::ManualSize)
2158                                 rDoc.SetRowFlags(nRow, nTab, nOld & ~CRFlags::ManualSize);
2159                         }
2160                     }
2161 
2162                     double nPPTX = GetViewData().GetPPTX();
2163                     double nPPTY = GetViewData().GetPPTY();
2164                     Fraction aZoomX = GetViewData().GetZoomX();
2165                     Fraction aZoomY = GetViewData().GetZoomY();
2166 
2167                     ScSizeDeviceProvider aProv(pDocSh);
2168                     if (aProv.IsPrinter())
2169                     {
2170                         nPPTX = aProv.GetPPTX();
2171                         nPPTY = aProv.GetPPTY();
2172                         aZoomX = aZoomY = Fraction( 1, 1 );
2173                     }
2174 
2175                     sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
2176                     aCxt.setForceAutoSize(bAll);
2177                     aCxt.setExtraHeight(nSizeTwips);
2178                     rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, true);
2179                     if (bAll)
2180                         rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
2181 
2182                     //  Manual-Flag already (re)set in SetOptimalHeight in case of bAll=sal_True
2183                     //  (set for Extra-Height, else reset).
2184                 }
2185                 else if ( eMode==SC_SIZE_DIRECT )
2186                 {
2187                     if (nSizeTwips)
2188                     {
2189                         rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips );
2190                         rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true );          // height was set manually
2191                     }
2192 
2193                     rDoc.ShowRows( nStartNo, nEndNo, nTab, nSizeTwips != 0 );
2194 
2195                     if (!bShow && nStartNo <= nCurY && nCurY <= nEndNo && nTab == nCurTab)
2196                     {
2197                         nCurY = -1;
2198                     }
2199                 }
2200                 else if ( eMode==SC_SIZE_SHOW )
2201                 {
2202                     rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
2203                 }
2204             }
2205             else                                // column width
2206             {
2207                 for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++)
2208                 {
2209                     if ( eMode != SC_SIZE_VISOPT || !rDoc.ColHidden(nCol, nTab) )
2210                     {
2211                         sal_uInt16 nThisSize = nSizeTwips;
2212 
2213                         if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2214                             nThisSize = nSizeTwips + GetOptimalColWidth( nCol, nTab, bFormula );
2215                         if ( nThisSize )
2216                             rDoc.SetColWidth( nCol, nTab, nThisSize );
2217 
2218                         rDoc.ShowCol( nCol, nTab, bShow );
2219 
2220                         if (!bShow && nCol == nCurX && nTab == nCurTab)
2221                         {
2222                             nCurX = -1;
2223                         }
2224                     }
2225                 }
2226             }
2227 
2228             //  adjust outline
2229             if (bWidth)
2230             {
2231                 if ( rDoc.UpdateOutlineCol( static_cast<SCCOL>(nStartNo),
2232                             static_cast<SCCOL>(nEndNo), nTab, bShow ) )
2233                     bOutline = true;
2234             }
2235             else
2236             {
2237                 if ( rDoc.UpdateOutlineRow( nStartNo, nEndNo, nTab, bShow ) )
2238                     bOutline = true;
2239             }
2240         }
2241         rDoc.SetDrawPageSize(nTab);
2242     }
2243 
2244     if (!bOutline)
2245         pUndoTab.reset();
2246 
2247     if (bRecord)
2248     {
2249         pDocSh->GetUndoManager()->AddUndoAction(
2250             std::make_unique<ScUndoWidthOrHeight>(
2251                 pDocSh, aMarkData, nStart, nCurTab, nEnd, nCurTab,
2252                 std::move(pUndoDoc), aUndoRanges, std::move(pUndoTab), eMode, nSizeTwips, bWidth));
2253     }
2254 
2255     if (nCurX < 0)
2256     {
2257         MoveCursorRel( 1, 0, SC_FOLLOW_LINE, false );
2258     }
2259 
2260     if (nCurY < 0)
2261     {
2262         MoveCursorRel( 0, 1, SC_FOLLOW_LINE, false );
2263     }
2264 
2265     // fdo#36247 Ensure that the drawing layer's map mode scaling factors match
2266     // the new heights and widths.
2267     GetViewData().GetView()->RefreshZoom();
2268 
2269     for (const SCTAB& nTab : aMarkData)
2270         rDoc.UpdatePageBreaks( nTab );
2271 
2272     bool bAffectsVisibility = (eMode != SC_SIZE_ORIGINAL && eMode != SC_SIZE_VISOPT);
2273     ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
2274             bWidth /* bColumns */, !bWidth /* bRows */,
2275             true /* bSizes*/, bAffectsVisibility /* bHidden */, bAffectsVisibility /* bFiltered */,
2276             false /* bGroups */, nCurTab);
2277     GetViewData().GetView()->UpdateScrollBars(bWidth ? COLUMN_HEADER : ROW_HEADER);
2278 
2279     {
2280         for (const SCTAB& nTab : aMarkData)
2281         {
2282             if (bWidth)
2283             {
2284                 if (rDoc.HasAttrib( static_cast<SCCOL>(nStart),0,nTab,
2285                             static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
2286                             HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2287                     nStart = 0;
2288                 if (nStart > 0)             // go upwards because of Lines and cursor
2289                     --nStart;
2290                 pDocSh->PostPaint( static_cast<SCCOL>(nStart), 0, nTab,
2291                         rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Top );
2292             }
2293             else
2294             {
2295                 if (rDoc.HasAttrib( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2296                     nStart = 0;
2297                 if (nStart != 0)
2298                     --nStart;
2299                 pDocSh->PostPaint( 0, nStart, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Left );
2300             }
2301         }
2302 
2303         pDocSh->UpdateOle(GetViewData());
2304         if( !pDocSh->IsReadOnly() )
2305             aModificator.SetDocumentModified();
2306     }
2307 
2308     if ( !bWidth )
2309         return;
2310 
2311     ScModelObj* pModelObj = HelperNotifyChanges::getMustPropagateChangesModel(*pDocSh);
2312     if (!pModelObj)
2313         return;
2314 
2315     ScRangeList aChangeRanges;
2316     for (const SCTAB& nTab : aMarkData)
2317     {
2318         for (const sc::ColRowSpan & rRange : rRanges)
2319         {
2320             SCCOL nStartCol = rRange.mnStart;
2321             SCCOL nEndCol   = rRange.mnEnd;
2322             for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
2323             {
2324                 aChangeRanges.push_back( ScRange( nCol, 0, nTab ) );
2325             }
2326         }
2327     }
2328     HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "column-resize");
2329 }
2330 
2331 //  column width/row height (via marked range)
2332 
SetMarkedWidthOrHeight(bool bWidth,ScSizeMode eMode,sal_uInt16 nSizeTwips)2333 void ScViewFunc::SetMarkedWidthOrHeight( bool bWidth, ScSizeMode eMode, sal_uInt16 nSizeTwips )
2334 {
2335     ScMarkData& rMark = GetViewData().GetMarkData();
2336 
2337     rMark.MarkToMulti();
2338     if (!rMark.IsMultiMarked())
2339     {
2340         SCCOL nCol = GetViewData().GetCurX();
2341         SCROW nRow = GetViewData().GetCurY();
2342         SCTAB nTab = GetViewData().GetTabNo();
2343         DoneBlockMode();
2344         InitOwnBlockMode();
2345         rMark.SetMultiMarkArea( ScRange( nCol,nRow,nTab ) );
2346         MarkDataChanged();
2347     }
2348 
2349     std::vector<sc::ColRowSpan> aRanges =
2350         bWidth ? rMark.GetMarkedColSpans() : rMark.GetMarkedRowSpans();
2351 
2352     SetWidthOrHeight(bWidth, aRanges, eMode, nSizeTwips);
2353 
2354     rMark.MarkToSimple();
2355 }
2356 
ModifyCellSize(ScDirection eDir,bool bOptimal)2357 void ScViewFunc::ModifyCellSize( ScDirection eDir, bool bOptimal )
2358 {
2359     //! step size adjustable
2360     //  step size is also minimum
2361     constexpr sal_uInt16 nStepX = STD_COL_WIDTH / 5;
2362     sal_uInt16 nStepY = ScGlobal::nStdRowHeight;
2363 
2364     ScModule* pScMod = SC_MOD();
2365     bool bAnyEdit = pScMod->IsInputMode();
2366     SCCOL nCol = GetViewData().GetCurX();
2367     SCROW nRow = GetViewData().GetCurY();
2368     SCTAB nTab = GetViewData().GetTabNo();
2369     ScDocShell* pDocSh = GetViewData().GetDocShell();
2370     ScDocument& rDoc = pDocSh->GetDocument();
2371 
2372     bool bAllowed, bOnlyMatrix;
2373     if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
2374         bAllowed = rDoc.IsBlockEditable( nTab, nCol,0, nCol,rDoc.MaxRow(), &bOnlyMatrix );
2375     else
2376         bAllowed = rDoc.IsBlockEditable( nTab, 0,nRow, rDoc.MaxCol(), nRow, &bOnlyMatrix );
2377     if ( !bAllowed && !bOnlyMatrix )
2378     {
2379         ErrorMessage(STR_PROTECTIONERR);
2380         return;
2381     }
2382 
2383     HideAllCursors();
2384 
2385     sal_uInt16 nWidth = rDoc.GetColWidth( nCol, nTab );
2386     sal_uInt16 nHeight = rDoc.GetRowHeight( nRow, nTab );
2387     std::vector<sc::ColRowSpan> aRange(1, sc::ColRowSpan(0,0));
2388     if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
2389     {
2390         if (bOptimal)               // width of this single cell
2391         {
2392             if ( bAnyEdit )
2393             {
2394                 //  when editing the actual entered width
2395                 ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
2396                 if (pHdl)
2397                 {
2398                     tools::Long nEdit = pHdl->GetTextSize().Width();       // in 0.01 mm
2399 
2400                     const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
2401                     const SvxMarginItem& rMItem = pPattern->GetItem(ATTR_MARGIN);
2402                     sal_uInt16 nMargin = rMItem.GetLeftMargin() + rMItem.GetRightMargin();
2403                     if ( pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Left )
2404                         nMargin = sal::static_int_cast<sal_uInt16>(
2405                             nMargin + pPattern->GetItem(ATTR_INDENT).GetValue() );
2406 
2407                     nWidth = std::round(o3tl::convert(nEdit * pDocSh->GetOutputFactor(),
2408                                                       o3tl::Length::mm100, o3tl::Length::twip))
2409                                 + nMargin + STD_EXTRA_WIDTH;
2410                 }
2411             }
2412             else
2413             {
2414                 double nPPTX = GetViewData().GetPPTX();
2415                 double nPPTY = GetViewData().GetPPTY();
2416                 Fraction aZoomX = GetViewData().GetZoomX();
2417                 Fraction aZoomY = GetViewData().GetZoomY();
2418 
2419                 ScSizeDeviceProvider aProv(pDocSh);
2420                 if (aProv.IsPrinter())
2421                 {
2422                     nPPTX = aProv.GetPPTX();
2423                     nPPTY = aProv.GetPPTY();
2424                     aZoomX = aZoomY = Fraction( 1, 1 );
2425                 }
2426 
2427                 tools::Long nPixel = rDoc.GetNeededSize( nCol, nRow, nTab, aProv.GetDevice(),
2428                                             nPPTX, nPPTY, aZoomX, aZoomY, true );
2429                 sal_uInt16 nTwips = static_cast<sal_uInt16>( nPixel / nPPTX );
2430                 if (nTwips != 0)
2431                     nWidth = nTwips + STD_EXTRA_WIDTH;
2432                 else
2433                     nWidth = STD_COL_WIDTH;
2434             }
2435         }
2436         else                        // increment / decrement
2437         {
2438             if ( eDir == DIR_RIGHT )
2439                 nWidth = sal::static_int_cast<sal_uInt16>( nWidth + nStepX );
2440             else if ( nWidth > nStepX )
2441                 nWidth = sal::static_int_cast<sal_uInt16>( nWidth - nStepX );
2442             if ( nWidth < nStepX ) nWidth = nStepX;
2443             if ( nWidth > MAX_COL_WIDTH ) nWidth = MAX_COL_WIDTH;
2444         }
2445         aRange[0].mnStart = nCol;
2446         aRange[0].mnEnd = nCol;
2447         SetWidthOrHeight(true, aRange, SC_SIZE_DIRECT, nWidth);
2448 
2449         //  adjust height of this row if width demands/allows this
2450 
2451         if (!bAnyEdit)
2452         {
2453             const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
2454             bool bNeedHeight =
2455                     pPattern->GetItem( ATTR_LINEBREAK ).GetValue() ||
2456                     pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block;
2457             if (bNeedHeight)
2458                 AdjustRowHeight( nRow, nRow, true );
2459         }
2460     }
2461     else
2462     {
2463         ScSizeMode eMode;
2464         if (bOptimal)
2465         {
2466             eMode = SC_SIZE_OPTIMAL;
2467             nHeight = 0;
2468         }
2469         else
2470         {
2471             eMode = SC_SIZE_DIRECT;
2472             if ( eDir == DIR_BOTTOM )
2473                 nHeight = sal::static_int_cast<sal_uInt16>( nHeight + nStepY );
2474             else if ( nHeight > nStepY )
2475                 nHeight = sal::static_int_cast<sal_uInt16>( nHeight - nStepY );
2476             if ( nHeight < nStepY ) nHeight = nStepY;
2477             if ( nHeight > MAX_ROW_HEIGHT ) nHeight = MAX_ROW_HEIGHT;
2478         }
2479         aRange[0].mnStart = nRow;
2480         aRange[0].mnEnd = nRow;
2481         SetWidthOrHeight(false, aRange, eMode, nHeight);
2482     }
2483 
2484     if ( bAnyEdit )
2485     {
2486         UpdateEditView();
2487         if ( rDoc.HasAttrib( nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::NeedHeight ) )
2488         {
2489             ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
2490             if (pHdl)
2491                 pHdl->SetModified();    // so that the height is adjusted with Enter
2492         }
2493     }
2494 
2495     ShowAllCursors();
2496 }
2497 
ProtectSheet(SCTAB nTab,const ScTableProtection & rProtect)2498 void ScViewFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect )
2499 {
2500     if (nTab == TABLEID_DOC)
2501         return;
2502 
2503     ScMarkData& rMark = GetViewData().GetMarkData();
2504     ScDocShell* pDocSh = GetViewData().GetDocShell();
2505     ScDocument& rDoc = pDocSh->GetDocument();
2506     ScDocFunc &rFunc = pDocSh->GetDocFunc();
2507     bool bUndo(rDoc.IsUndoEnabled());
2508 
2509     //  modifying several tabs is handled here
2510 
2511     if (bUndo)
2512     {
2513         OUString aUndo = ScResId( STR_UNDO_PROTECT_TAB );
2514         pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2515     }
2516 
2517     for (const auto& rTab : rMark)
2518     {
2519         rFunc.ProtectSheet(rTab, rProtect);
2520     }
2521 
2522     if (bUndo)
2523         pDocSh->GetUndoManager()->LeaveListAction();
2524 
2525     UpdateLayerLocks();         //! broadcast to all views
2526 }
2527 
ProtectDoc(const OUString & rPassword)2528 void ScViewFunc::ProtectDoc( const OUString& rPassword )
2529 {
2530     ScDocShell* pDocSh = GetViewData().GetDocShell();
2531     ScDocFunc &rFunc = pDocSh->GetDocFunc();
2532 
2533     rFunc.Protect( TABLEID_DOC, rPassword );
2534 
2535     UpdateLayerLocks();         //! broadcast to all views
2536 }
2537 
Unprotect(SCTAB nTab,const OUString & rPassword)2538 bool ScViewFunc::Unprotect( SCTAB nTab, const OUString& rPassword )
2539 {
2540     ScMarkData& rMark = GetViewData().GetMarkData();
2541     ScDocShell* pDocSh = GetViewData().GetDocShell();
2542     ScDocument& rDoc = pDocSh->GetDocument();
2543     ScDocFunc &rFunc = pDocSh->GetDocFunc();
2544     bool bChanged = false;
2545     bool bUndo (rDoc.IsUndoEnabled());
2546 
2547     if ( nTab == TABLEID_DOC || rMark.GetSelectCount() <= 1 )
2548     {
2549         bChanged = rFunc.Unprotect( nTab, rPassword, false );
2550         if (bChanged && nTab != TABLEID_DOC)
2551             SetTabProtectionSymbol(nTab, false);
2552     }
2553     else
2554     {
2555         //  modifying several tabs is handled here
2556 
2557         if (bUndo)
2558         {
2559             OUString aUndo = ScResId( STR_UNDO_UNPROTECT_TAB );
2560             pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2561         }
2562 
2563         for (const auto& rTab : rMark)
2564         {
2565             if ( rFunc.Unprotect( rTab, rPassword, false ) )
2566             {
2567                 bChanged = true;
2568                 SetTabProtectionSymbol( rTab, false);
2569             }
2570         }
2571 
2572         if (bUndo)
2573             pDocSh->GetUndoManager()->LeaveListAction();
2574     }
2575 
2576     if (bChanged)
2577         UpdateLayerLocks();     //! broadcast to all views
2578 
2579     return bChanged;
2580 }
2581 
SetNoteText(const ScAddress & rPos,const OUString & rNoteText)2582 void ScViewFunc::SetNoteText( const ScAddress& rPos, const OUString& rNoteText )
2583 {
2584     GetViewData().GetDocShell()->GetDocFunc().SetNoteText( rPos, rNoteText, false );
2585 }
2586 
ReplaceNote(const ScAddress & rPos,const OUString & rNoteText,const OUString * pAuthor,const OUString * pDate)2587 void ScViewFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate )
2588 {
2589     GetViewData().GetDocShell()->GetDocFunc().ReplaceNote( rPos, rNoteText, pAuthor, pDate, false );
2590 }
2591 
SetNumberFormat(SvNumFormatType nFormatType,sal_uLong nAdd)2592 void ScViewFunc::SetNumberFormat( SvNumFormatType nFormatType, sal_uLong nAdd )
2593 {
2594     // not editable because of matrix only? attribute OK nonetheless
2595     bool bOnlyNotBecauseOfMatrix;
2596     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2597     {
2598         ErrorMessage(STR_PROTECTIONERR);
2599         return;
2600     }
2601 
2602     sal_uInt32          nNumberFormat = 0;
2603     ScViewData&         rViewData = GetViewData();
2604     ScDocument&         rDoc = rViewData.GetDocument();
2605     SvNumberFormatter*  pNumberFormatter = rDoc.GetFormatTable();
2606     LanguageType        eLanguage = ScGlobal::eLnge;
2607     ScPatternAttr       aNewAttrs( rDoc.GetPool() );
2608 
2609     //  always take language from cursor position, even if there is a selection
2610 
2611     sal_uInt32 nCurrentNumberFormat;
2612     rDoc.GetNumberFormat( rViewData.GetCurX(),
2613                           rViewData.GetCurY(),
2614                           rViewData.GetTabNo(),
2615                           nCurrentNumberFormat );
2616     const SvNumberformat* pEntry = pNumberFormatter->GetEntry( nCurrentNumberFormat );
2617     if (pEntry)
2618         eLanguage = pEntry->GetLanguage();      // else keep ScGlobal::eLnge
2619 
2620     nNumberFormat = pNumberFormatter->GetStandardFormat( nFormatType, eLanguage ) + nAdd;
2621 
2622     SfxItemSet& rSet = aNewAttrs.GetItemSet();
2623     rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
2624     //  ATTR_LANGUAGE_FORMAT not
2625     ApplySelectionPattern( aNewAttrs );
2626 }
2627 
SetNumFmtByStr(const OUString & rCode)2628 void ScViewFunc::SetNumFmtByStr( const OUString& rCode )
2629 {
2630     // not editable because of matrix only? attribute OK nonetheless
2631     bool bOnlyNotBecauseOfMatrix;
2632     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2633     {
2634         ErrorMessage(STR_PROTECTIONERR);
2635         return;
2636     }
2637 
2638     ScViewData&         rViewData = GetViewData();
2639     ScDocument&         rDoc = rViewData.GetDocument();
2640     SvNumberFormatter*  pFormatter = rDoc.GetFormatTable();
2641 
2642     //  language always from cursor position
2643 
2644     sal_uInt32 nCurrentNumberFormat;
2645     rDoc.GetNumberFormat( rViewData.GetCurX(), rViewData.GetCurY(),
2646                            rViewData.GetTabNo(), nCurrentNumberFormat );
2647     const SvNumberformat* pEntry = pFormatter->GetEntry( nCurrentNumberFormat );
2648     LanguageType eLanguage = pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge;
2649 
2650     //  determine index for String
2651 
2652     bool bOk = true;
2653     sal_uInt32 nNumberFormat = pFormatter->GetEntryKey( rCode, eLanguage );
2654     if ( nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
2655     {
2656         //  enter new
2657 
2658         OUString    aFormat = rCode;    // will be changed
2659         sal_Int32   nErrPos = 0;
2660         SvNumFormatType nType   = SvNumFormatType::ALL;        //! ???
2661         bOk = pFormatter->PutEntry( aFormat, nErrPos, nType, nNumberFormat, eLanguage );
2662     }
2663 
2664     if ( bOk )          // valid format?
2665     {
2666         ScPatternAttr aNewAttrs( rDoc.GetPool() );
2667         SfxItemSet& rSet = aNewAttrs.GetItemSet();
2668         rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
2669         rSet.Put( SvxLanguageItem( eLanguage, ATTR_LANGUAGE_FORMAT ) );
2670         ApplySelectionPattern( aNewAttrs );
2671     }
2672 
2673     //! else return error / issue warning ???
2674 }
2675 
ChangeNumFmtDecimals(bool bIncrement)2676 void ScViewFunc::ChangeNumFmtDecimals( bool bIncrement )
2677 {
2678     // not editable because of matrix only? attribute OK nonetheless
2679     bool bOnlyNotBecauseOfMatrix;
2680     if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2681     {
2682         ErrorMessage(STR_PROTECTIONERR);
2683         return;
2684     }
2685 
2686     ScDocument&         rDoc = GetViewData().GetDocument();
2687     SvNumberFormatter*  pFormatter = rDoc.GetFormatTable();
2688 
2689     SCCOL nCol = GetViewData().GetCurX();
2690     SCROW nRow = GetViewData().GetCurY();
2691     SCTAB nTab = GetViewData().GetTabNo();
2692 
2693     sal_uInt32 nOldFormat;
2694     rDoc.GetNumberFormat( nCol, nRow, nTab, nOldFormat );
2695     const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
2696     if (!pOldEntry)
2697     {
2698         OSL_FAIL("numberformat not found !!!");
2699         return;
2700     }
2701 
2702     //  what have we got here?
2703 
2704     sal_uInt32 nNewFormat = nOldFormat;
2705     bool bError = false;
2706 
2707     LanguageType eLanguage = pOldEntry->GetLanguage();
2708     bool bThousand, bNegRed;
2709     sal_uInt16 nPrecision, nLeading;
2710     pOldEntry->GetFormatSpecialInfo( bThousand, bNegRed, nPrecision, nLeading );
2711 
2712     SvNumFormatType nOldType = pOldEntry->GetType();
2713     if ( SvNumFormatType::ALL == ( nOldType & (
2714                 SvNumFormatType::NUMBER |  SvNumFormatType::CURRENCY | SvNumFormatType::PERCENT | SvNumFormatType::SCIENTIFIC | SvNumFormatType::TIME ) ) )
2715     {
2716         //  date, fraction, logical, text can not be changed
2717         bError = true;
2718     }
2719 
2720     //! SvNumberformat has a Member bStandard, but doesn't disclose it
2721     bool bWasStandard = ( nOldFormat == pFormatter->GetStandardIndex( eLanguage ) );
2722     OUString sExponentialStandardFormat = "";
2723     if (bWasStandard)
2724     {
2725         //  with "Standard" the decimal places depend on cell content
2726         //  0 if empty or text -> no decimal places
2727         double nVal = rDoc.GetValue( ScAddress( nCol, nRow, nTab ) );
2728 
2729         //  the ways of the Numberformatters are unfathomable, so try:
2730         OUString aOut;
2731         const Color* pCol;
2732         const_cast<SvNumberformat*>(pOldEntry)->GetOutputString( nVal, aOut, &pCol );
2733 
2734         nPrecision = 0;
2735         // 'E' for exponential is fixed in Numberformatter
2736         sal_Int32 nIndexE = aOut.indexOf('E');
2737         if ( nIndexE >= 0 )
2738         {
2739           sExponentialStandardFormat = aOut.copy( nIndexE ).replace( '-', '+' );
2740           for ( sal_Int32 i=1 ; i<sExponentialStandardFormat.getLength() ; i++ )
2741           {
2742             if ( sExponentialStandardFormat[i] >= '1' && sExponentialStandardFormat[i] <= '9' )
2743               sExponentialStandardFormat = sExponentialStandardFormat.replaceAt( i, 1, "0" );
2744           }
2745           aOut = aOut.copy( 0, nIndexE ); // remove exponential part
2746         }
2747         OUString aDecSep( pFormatter->GetFormatDecimalSep( nOldFormat ) );
2748         sal_Int32 nPos = aOut.indexOf( aDecSep );
2749         if ( nPos >= 0 )
2750             nPrecision = aOut.getLength() - nPos - aDecSep.getLength();
2751         // else keep 0
2752     }
2753     else
2754     {
2755         if ( (nOldType & SvNumFormatType::SCIENTIFIC) && !bThousand &&
2756              (pOldEntry->GetFormatIntegerDigits()%3 == 0) && pOldEntry->GetFormatIntegerDigits() > 0 )
2757             bThousand =  true;
2758     }
2759 
2760     if (!bError)
2761     {
2762         if (bIncrement)
2763         {
2764             if (nPrecision<20)
2765                 ++nPrecision;           // increment
2766             else
2767                 bError = true;          // 20 is maximum
2768         }
2769         else
2770         {
2771             if (nPrecision)
2772                 --nPrecision;           // decrement
2773             else
2774                 bError = true;          // 0 is minimum
2775         }
2776     }
2777 
2778     if (!bError)
2779     {
2780         OUString aNewPicture = pFormatter->GenerateFormat(nOldFormat, eLanguage,
2781                                                           bThousand, bNegRed,
2782                                                           nPrecision, nLeading)
2783                                 + sExponentialStandardFormat;
2784 
2785         nNewFormat = pFormatter->GetEntryKey( aNewPicture, eLanguage );
2786         if ( nNewFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
2787         {
2788             sal_Int32 nErrPos = 0;
2789             SvNumFormatType nNewType = SvNumFormatType::ALL;
2790             bool bOk = pFormatter->PutEntry( aNewPicture, nErrPos,
2791                                                 nNewType, nNewFormat, eLanguage );
2792             OSL_ENSURE( bOk, "incorrect numberformat generated" );
2793             if (!bOk)
2794                 bError = true;
2795         }
2796     }
2797 
2798     if (!bError)
2799     {
2800         ScPatternAttr aNewAttrs( rDoc.GetPool() );
2801         SfxItemSet& rSet = aNewAttrs.GetItemSet();
2802         rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
2803         //  ATTR_LANGUAGE_FORMAT not
2804         ApplySelectionPattern( aNewAttrs );
2805     }
2806 }
2807 
ChangeIndent(bool bIncrement)2808 void ScViewFunc::ChangeIndent( bool bIncrement )
2809 {
2810     ScViewData& rViewData = GetViewData();
2811     ScDocShell* pDocSh  = rViewData.GetDocShell();
2812     ScMarkData& rMark   = rViewData.GetMarkData();
2813 
2814     ScMarkData aWorkMark = rMark;
2815     ScViewUtil::UnmarkFiltered( aWorkMark, pDocSh->GetDocument() );
2816     aWorkMark.MarkToMulti();
2817     if (!aWorkMark.IsMultiMarked())
2818     {
2819         SCCOL nCol = rViewData.GetCurX();
2820         SCROW nRow = rViewData.GetCurY();
2821         SCTAB nTab = rViewData.GetTabNo();
2822         aWorkMark.SetMultiMarkArea( ScRange(nCol,nRow,nTab) );
2823     }
2824 
2825     bool bSuccess = pDocSh->GetDocFunc().ChangeIndent( aWorkMark, bIncrement, false );
2826     if (bSuccess)
2827     {
2828         pDocSh->UpdateOle(rViewData);
2829         StartFormatArea();
2830 
2831         // stuff for sidebar panels
2832         SfxBindings& rBindings = GetViewData().GetBindings();
2833         rBindings.Invalidate( SID_H_ALIGNCELL );
2834         rBindings.Invalidate( SID_ATTR_ALIGN_INDENT );
2835     }
2836 }
2837 
InsertName(const OUString & rName,const OUString & rSymbol,const OUString & rType)2838 bool ScViewFunc::InsertName( const OUString& rName, const OUString& rSymbol,
2839                                 const OUString& rType )
2840 {
2841     //  Type = P,R,C,F (and combinations)
2842     //! undo...
2843 
2844     bool bOk = false;
2845     ScDocShell* pDocSh = GetViewData().GetDocShell();
2846     ScDocument& rDoc = pDocSh->GetDocument();
2847     SCTAB nTab = GetViewData().GetTabNo();
2848     ScRangeName* pList = rDoc.GetRangeName();
2849 
2850     ScRangeData::Type nType = ScRangeData::Type::Name;
2851     auto pNewEntry = std::make_unique<ScRangeData>(
2852         rDoc, rName, rSymbol, ScAddress( GetViewData().GetCurX(),
2853         GetViewData().GetCurY(), nTab), nType );
2854     OUString aUpType = rType.toAsciiUpperCase();
2855     if ( aUpType.indexOf( 'P' ) != -1 )
2856         nType |= ScRangeData::Type::PrintArea;
2857     if ( aUpType.indexOf( 'R' ) != -1 )
2858         nType |= ScRangeData::Type::RowHeader;
2859     if ( aUpType.indexOf( 'C' ) != -1 )
2860         nType |= ScRangeData::Type::ColHeader;
2861     if ( aUpType.indexOf( 'F' ) != -1 )
2862         nType |= ScRangeData::Type::Criteria;
2863     pNewEntry->AddType(nType);
2864 
2865     if ( pNewEntry->GetErrCode() == FormulaError::NONE )     //  text valid?
2866     {
2867         ScDocShellModificator aModificator( *pDocSh );
2868 
2869         rDoc.PreprocessRangeNameUpdate();
2870 
2871         // input available yet? Then remove beforehand (=change)
2872         ScRangeData* pData = pList->findByUpperName(ScGlobal::getCharClassPtr()->uppercase(rName));
2873         if (pData)
2874         {                                   // take old Index
2875             pNewEntry->SetIndex(pData->GetIndex());
2876             pList->erase(*pData);
2877         }
2878 
2879         // don't delete, insert took ownership, even on failure!
2880         if ( pList->insert( pNewEntry.release() ) )
2881             bOk = true;
2882 
2883         rDoc.CompileHybridFormula();
2884 
2885         aModificator.SetDocumentModified();
2886         SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
2887     }
2888 
2889     return bOk;
2890 }
2891 
CreateNames(CreateNameFlags nFlags)2892 void ScViewFunc::CreateNames( CreateNameFlags nFlags )
2893 {
2894     bool bDone = false;
2895     ScRange aRange;
2896     if ( GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE )
2897         bDone = GetViewData().GetDocShell()->GetDocFunc().CreateNames( aRange, nFlags, false );
2898 
2899     if (!bDone)
2900         ErrorMessage(STR_CREATENAME_MARKERR);
2901 }
2902 
GetCreateNameFlags()2903 CreateNameFlags ScViewFunc::GetCreateNameFlags()
2904 {
2905     CreateNameFlags nFlags = CreateNameFlags::NONE;
2906 
2907     SCCOL nStartCol, nEndCol;
2908     SCROW nStartRow, nEndRow;
2909     SCTAB nDummy;
2910     if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nDummy,nEndCol,nEndRow,nDummy) == SC_MARK_SIMPLE)
2911     {
2912         ScDocument& rDoc = GetViewData().GetDocument();
2913         SCTAB nTab = GetViewData().GetTabNo();
2914         bool bOk;
2915         SCCOL i;
2916         SCROW j;
2917 
2918         bOk = true;
2919         SCCOL nFirstCol = nStartCol;
2920         SCCOL nLastCol  = nEndCol;
2921         if (nStartCol+1 < nEndCol) { ++nFirstCol; --nLastCol; }
2922         for (i=nFirstCol; i<=nLastCol && bOk; i++)
2923             if (!rDoc.HasStringData( i,nStartRow,nTab ))
2924                 bOk = false;
2925         if (bOk)
2926             nFlags |= CreateNameFlags::Top;
2927         else                            // Bottom only if not Top
2928         {
2929             bOk = true;
2930             for (i=nFirstCol; i<=nLastCol && bOk; i++)
2931                 if (!rDoc.HasStringData( i,nEndRow,nTab ))
2932                     bOk = false;
2933             if (bOk)
2934                 nFlags |= CreateNameFlags::Bottom;
2935         }
2936 
2937         bOk = true;
2938         SCROW nFirstRow = nStartRow;
2939         SCROW nLastRow  = nEndRow;
2940         if (nStartRow+1 < nEndRow) { ++nFirstRow; --nLastRow; }
2941         for (j=nFirstRow; j<=nLastRow && bOk; j++)
2942             if (!rDoc.HasStringData( nStartCol,j,nTab ))
2943                 bOk = false;
2944         if (bOk)
2945             nFlags |= CreateNameFlags::Left;
2946         else                            // Right only if not Left
2947         {
2948             bOk = true;
2949             for (j=nFirstRow; j<=nLastRow && bOk; j++)
2950                 if (!rDoc.HasStringData( nEndCol,j,nTab ))
2951                     bOk = false;
2952             if (bOk)
2953                 nFlags |= CreateNameFlags::Right;
2954         }
2955     }
2956 
2957     if (nStartCol == nEndCol)
2958         nFlags &= ~CreateNameFlags( CreateNameFlags::Left | CreateNameFlags::Right );
2959     if (nStartRow == nEndRow)
2960         nFlags &= ~CreateNameFlags( CreateNameFlags::Top | CreateNameFlags::Bottom );
2961 
2962     return nFlags;
2963 }
2964 
InsertNameList()2965 void ScViewFunc::InsertNameList()
2966 {
2967     ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
2968     ScDocShell* pDocSh = GetViewData().GetDocShell();
2969     if ( pDocSh->GetDocFunc().InsertNameList( aPos, false ) )
2970         pDocSh->UpdateOle(GetViewData());
2971 }
2972 
UpdateSelectionArea(const ScMarkData & rSel,ScPatternAttr * pAttr)2973 void ScViewFunc::UpdateSelectionArea( const ScMarkData& rSel, ScPatternAttr* pAttr  )
2974 {
2975     ScDocShell* pDocShell = GetViewData().GetDocShell();
2976     ScRange aMarkRange;
2977     if (rSel.IsMultiMarked() )
2978         rSel.GetMultiMarkArea( aMarkRange );
2979     else
2980         rSel.GetMarkArea( aMarkRange );
2981 
2982     bool bSetLines = false;
2983     bool bSetAlign = false;
2984     if ( pAttr )
2985     {
2986         const SfxItemSet& rNewSet = pAttr->GetItemSet();
2987         bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
2988         rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
2989         bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
2990     }
2991 
2992     sal_uInt16 nExtFlags = 0;
2993     if ( bSetLines )
2994         nExtFlags |= SC_PF_LINES;
2995     if ( bSetAlign )
2996         nExtFlags |= SC_PF_WHOLEROWS;
2997 
2998     SCCOL nStartCol = aMarkRange.aStart.Col();
2999     SCROW nStartRow = aMarkRange.aStart.Row();
3000     SCTAB nStartTab = aMarkRange.aStart.Tab();
3001     SCCOL nEndCol = aMarkRange.aEnd.Col();
3002     SCROW nEndRow = aMarkRange.aEnd.Row();
3003     SCTAB nEndTab = aMarkRange.aEnd.Tab();
3004     pDocShell->PostPaint( nStartCol, nStartRow, nStartTab,
3005         nEndCol,   nEndRow,   nEndTab,
3006         PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
3007     ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
3008     pTabViewShell->AdjustBlockHeight(false, const_cast<ScMarkData*>(&rSel));
3009 }
3010 
3011 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
3012