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