tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / ui / view / viewfunc.cxx
blobfff197680f860aac0a878120f620da38a165443b
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.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <address.hxx>
21 #include <config_features.h>
23 #include <scitems.hxx>
25 #include <sfx2/app.hxx>
26 #include <svx/algitem.hxx>
27 #include <editeng/boxitem.hxx>
28 #include <editeng/editobj.hxx>
29 #include <editeng/langitem.hxx>
30 #include <editeng/justifyitem.hxx>
31 #include <o3tl/unit_conversion.hxx>
32 #include <sfx2/bindings.hxx>
33 #include <svl/numformat.hxx>
34 #include <svl/zforlist.hxx>
35 #include <svl/zformat.hxx>
36 #include <vcl/weld.hxx>
37 #include <vcl/virdev.hxx>
38 #include <stdlib.h>
39 #include <unotools/charclass.hxx>
40 #include <vcl/uitest/logger.hxx>
41 #include <vcl/uitest/eventdescription.hxx>
42 #include <osl/diagnose.h>
44 #include <viewfunc.hxx>
45 #include <tabvwsh.hxx>
46 #include <docsh.hxx>
47 #include <attrib.hxx>
48 #include <patattr.hxx>
49 #include <sc.hrc>
50 #include <undocell.hxx>
51 #include <undoblk.hxx>
52 #include <refundo.hxx>
53 #include <olinetab.hxx>
54 #include <rangenam.hxx>
55 #include <globstr.hrc>
56 #include <global.hxx>
57 #include <stlsheet.hxx>
58 #include <editutil.hxx>
59 #include <formulacell.hxx>
60 #include <scresid.hxx>
61 #include <inputhdl.hxx>
62 #include <scmod.hxx>
63 #include <inputopt.hxx>
64 #include <compiler.hxx>
65 #include <docfunc.hxx>
66 #include <appoptio.hxx>
67 #include <sizedev.hxx>
68 #include <editable.hxx>
69 #include <scui_def.hxx>
70 #include <funcdesc.hxx>
71 #include <docuno.hxx>
72 #include <cellsuno.hxx>
73 #include <tokenarray.hxx>
74 #include <rowheightcontext.hxx>
75 #include <comphelper/lok.hxx>
76 #include <conditio.hxx>
77 #include <columnspanset.hxx>
78 #include <stringutil.hxx>
79 #include <SparklineList.hxx>
81 #include <memory>
83 static void ShowFilteredRows(ScDocument& rDoc, SCTAB nTab, SCCOLROW nStartNo, SCCOLROW nEndNo,
84 bool bShow)
86 SCROW nFirstRow = nStartNo;
87 SCROW nLastRow = nStartNo;
90 if (!rDoc.RowFiltered(nFirstRow, nTab, nullptr, &nLastRow))
91 rDoc.ShowRows(nFirstRow, nLastRow < nEndNo ? nLastRow : nEndNo, nTab, bShow);
92 nFirstRow = nLastRow + 1;
93 } while (nFirstRow <= nEndNo);
96 static void lcl_PostRepaintCondFormat( const ScConditionalFormat *pCondFmt, ScDocShell *pDocSh )
98 if( pCondFmt )
100 const ScRangeList& rRanges = pCondFmt->GetRange();
102 pDocSh->PostPaint( rRanges, PaintPartFlags::All );
106 static void lcl_PostRepaintSparkLine(sc::SparklineList* pSparklineList, const ScRange& rRange,
107 ScDocShell* pDocSh)
109 if (pSparklineList)
111 for (auto& rSparkLineGroup : pSparklineList->getSparklineGroups())
113 for (auto& rSparkline : pSparklineList->getSparklinesFor(rSparkLineGroup))
115 if (rSparkline->getInputRange().Contains(rRange))
117 pDocSh->PostPaint(
118 ScRange(rSparkline->getColumn(), rSparkline->getRow(), rRange.aStart.Tab()),
119 PaintPartFlags::All, SC_PF_TESTMERGE);
126 ScViewFunc::ScViewFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
127 ScTabView( pParent, rDocSh, pViewShell ),
128 bFormatValid( false )
132 ScViewFunc::~ScViewFunc()
136 namespace {
138 struct FormulaProcessingContext
140 std::shared_ptr<ScAddress> aPos;
141 std::shared_ptr<ScCompiler> aComp;
142 std::shared_ptr<ScDocShellModificator> aModificator;
143 std::shared_ptr<ScTokenArray> pArr;
144 std::shared_ptr<ScTokenArray> pArrFirst;
146 std::shared_ptr<EditTextObject> xTextObject;
147 ScMarkData aMark;
148 ScViewFunc& rViewFunc;
150 OUString aCorrectedFormula;
151 OUString aFormula;
152 OUString aString;
154 SCCOL nCol;
155 SCROW nRow;
156 SCTAB nTab;
158 bool bMatrixExpand;
159 bool bNumFmtChanged;
160 bool bRecord;
162 ScViewData& GetViewData() const
164 return rViewFunc.GetViewData();
167 ScDocFunc& GetDocFunc() const
169 return GetViewData().GetDocFunc();
172 ScDocument& GetDoc() const
174 return GetViewData().GetDocument();
178 void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
180 EventDescription aDescription;
181 aDescription.aID = "grid_window";
182 aDescription.aAction = rAction;
183 aDescription.aParameters = std::move(aParameters);
184 aDescription.aParent = "MainWindow";
185 aDescription.aKeyWord = "ScGridWinUIObject";
187 UITestLogger::getInstance().logEvent(aDescription);
192 void ScViewFunc::StartFormatArea()
194 // anything to do?
195 if (!ScModule::get()->GetInputOptions().GetExtendFormat())
196 return;
198 // start only with single cell (marked or cursor position)
199 ScRange aMarkRange;
200 bool bOk = (GetViewData().GetSimpleArea( aMarkRange ) == SC_MARK_SIMPLE);
201 if ( bOk && aMarkRange.aStart != aMarkRange.aEnd )
202 bOk = false;
204 if (bOk)
206 bFormatValid = true;
207 aFormatSource = aMarkRange.aStart;
208 aFormatArea = ScRange( aFormatSource );
210 else
211 bFormatValid = false; // discard old range
214 bool ScViewFunc::TestFormatArea( SCCOL nCol, SCROW nRow, SCTAB nTab, bool bAttrChanged )
216 // anything to do?
217 if (!ScModule::get()->GetInputOptions().GetExtendFormat())
218 return false;
220 // Test: treat input with numberformat (bAttrChanged) always as new Attribute
221 // (discard old Area ). If not wanted, discard if-statement
222 if ( bAttrChanged )
224 StartFormatArea();
225 return false;
228 //! Test if cell empty ???
230 bool bFound = false;
231 ScRange aNewRange = aFormatArea;
232 if ( bFormatValid && nTab == aFormatSource.Tab() )
234 if ( nRow >= aFormatArea.aStart.Row() && nRow <= aFormatArea.aEnd.Row() )
236 // within range?
237 if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
239 bFound = true; // do not change range
241 // left ?
242 if ( nCol+1 == aFormatArea.aStart.Col() )
244 bFound = true;
245 aNewRange.aStart.SetCol( nCol );
247 // right ?
248 if ( nCol == aFormatArea.aEnd.Col()+1 )
250 bFound = true;
251 aNewRange.aEnd.SetCol( nCol );
254 if ( nCol >= aFormatArea.aStart.Col() && nCol <= aFormatArea.aEnd.Col() )
256 // top ?
257 if ( nRow+1 == aFormatArea.aStart.Row() )
259 bFound = true;
260 aNewRange.aStart.SetRow( nRow );
262 // bottom ?
263 if ( nRow == aFormatArea.aEnd.Row()+1 )
265 bFound = true;
266 aNewRange.aEnd.SetRow( nRow );
271 if (bFound)
272 aFormatArea = aNewRange; // extend
273 else
274 bFormatValid = false; // outside of range -> break
276 return bFound;
279 void ScViewFunc::DoAutoAttributes( SCCOL nCol, SCROW nRow, SCTAB nTab,
280 bool bAttrChanged )
282 ScDocShell* pDocSh = GetViewData().GetDocShell();
283 ScDocument& rDoc = pDocSh->GetDocument();
285 const ScPatternAttr* pSource = rDoc.GetPattern(
286 aFormatSource.Col(), aFormatSource.Row(), nTab );
287 if ( !pSource->GetItem(ATTR_MERGE).IsMerged() )
289 ScRange aRange( nCol, nRow, nTab, nCol, nRow, nTab );
290 ScMarkData aMark(rDoc.GetSheetLimits());
291 aMark.SetMarkArea( aRange );
293 ScDocFunc &rFunc = GetViewData().GetDocFunc();
295 // pOldPattern is only valid until call to ApplyAttributes!
296 const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
297 const ScStyleSheet* pSrcStyle = pSource->GetStyleSheet();
298 if ( pSrcStyle && pSrcStyle != pOldPattern->GetStyleSheet() )
299 rFunc.ApplyStyle( aMark, pSrcStyle->GetName(), false );
301 rFunc.ApplyAttributes( aMark, *pSource, false );
304 if ( bAttrChanged ) // value entered with number format?
305 aFormatSource.Set( nCol, nRow, nTab ); // then set a new source
308 // additional routines
310 void ScViewData::setupSizeDeviceProviderForColWidth(ScSizeDeviceProvider& rProv, Fraction& rZoomX, Fraction& rZoomY, double& rPPTX, double &rPPTY)
312 if (rProv.IsPrinter())
314 rPPTX = rProv.GetPPTX();
315 rPPTY = rProv.GetPPTY();
316 rZoomX = rZoomY = Fraction(1, 1);
318 else
320 rPPTX = GetPPTX();
321 rPPTY = GetPPTY();
322 rZoomX = GetZoomX();
323 rZoomY = GetZoomY();
327 sal_uInt16 ScViewFunc::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, bool bFormula )
329 ScDocShell* pDocSh = GetViewData().GetDocShell();
330 ScDocument& rDoc = pDocSh->GetDocument();
331 ScMarkData& rMark = GetViewData().GetMarkData();
333 ScSizeDeviceProvider aProv(pDocSh);
335 Fraction aZoomX, aZoomY;
336 double nPPTX, nPPTY;
337 GetViewData().setupSizeDeviceProviderForColWidth(aProv, aZoomX, aZoomY, nPPTX, nPPTY);
339 sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, aProv.GetDevice(),
340 nPPTX, nPPTY, aZoomX, aZoomY, bFormula, &rMark );
341 return nTwips;
344 bool ScViewFunc::SelectionEditable( bool* pOnlyNotBecauseOfMatrix /* = NULL */ )
346 bool bRet;
347 ScDocument& rDoc = GetViewData().GetDocument();
348 ScMarkData& rMark = GetViewData().GetMarkData();
349 if (rMark.IsMarked() || rMark.IsMultiMarked())
350 bRet = rDoc.IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix );
351 else
353 SCCOL nCol = GetViewData().GetCurX();
354 SCROW nRow = GetViewData().GetCurY();
355 SCTAB nTab = GetViewData().GetTabNo();
356 bRet = rDoc.IsBlockEditable( nTab, nCol, nRow, nCol, nRow,
357 pOnlyNotBecauseOfMatrix );
359 return bRet;
362 static bool lcl_FunctionKnown( sal_uInt16 nOpCode )
364 const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList();
365 if ( pFuncList )
367 sal_uLong nCount = pFuncList->GetCount();
368 for (sal_uLong i=0; i<nCount; i++)
369 if ( pFuncList->GetFunction(i)->nFIndex == nOpCode )
370 return true;
372 return false;
375 static bool lcl_AddFunction( ScAppOptions& rAppOpt, sal_uInt16 nOpCode )
377 sal_uInt16 nOldCount = rAppOpt.GetLRUFuncListCount();
378 sal_uInt16* pOldList = rAppOpt.GetLRUFuncList();
379 sal_uInt16 nPos;
380 for (nPos=0; nPos<nOldCount; nPos++)
381 if (pOldList[nPos] == nOpCode) // is the function already in the list?
383 if ( nPos == 0 )
384 return false; // already at the top -> no change
386 // count doesn't change, so the original array is modified
388 for (sal_uInt16 nCopy=nPos; nCopy>0; nCopy--)
389 pOldList[nCopy] = pOldList[nCopy-1];
390 pOldList[0] = nOpCode;
392 return true; // list has changed
395 if ( !lcl_FunctionKnown( nOpCode ) )
396 return false; // not in function list -> no change
398 sal_uInt16 nNewCount = std::min( static_cast<sal_uInt16>(nOldCount + 1), sal_uInt16(LRU_MAX) );
399 sal_uInt16 nNewList[LRU_MAX];
400 nNewList[0] = nOpCode;
401 for (nPos=1; nPos<nNewCount; nPos++)
402 nNewList[nPos] = pOldList[nPos-1];
403 rAppOpt.SetLRUFuncList( nNewList, nNewCount );
405 return true; // list has changed
408 namespace HelperNotifyChanges
410 static void NotifyIfChangesListeners(const ScDocShell &rDocShell, const ScMarkData& rMark,
411 SCCOL nCol, SCROW nRow, const OUString& rType = u"cell-change"_ustr)
413 ScModelObj* pModelObj = rDocShell.GetModel();
415 ScRangeList aChangeRanges;
416 for (const auto& rTab : rMark)
417 aChangeRanges.push_back( ScRange( nCol, nRow, rTab ) );
419 if (getMustPropagateChangesModel(pModelObj))
420 Notify(*pModelObj, aChangeRanges, rType);
421 else
423 Notify(*pModelObj, aChangeRanges, isDataAreaInvalidateType(rType)
424 ? u"data-area-invalidate"_ustr : u"data-area-extend"_ustr);
429 namespace
431 class AutoCorrectQuery : public weld::MessageDialogController
433 private:
434 std::unique_ptr<weld::TextView> m_xError;
435 public:
436 AutoCorrectQuery(weld::Window* pParent, const OUString& rFormula)
437 : weld::MessageDialogController(pParent, u"modules/scalc/ui/warnautocorrect.ui"_ustr, u"WarnAutoCorrect"_ustr, u"grid"_ustr)
438 , m_xError(m_xBuilder->weld_text_view(u"error"_ustr))
440 m_xDialog->set_default_response(RET_YES);
442 const int nMaxWidth = m_xError->get_approximate_digit_width() * 65;
443 const int nMaxHeight = m_xError->get_height_rows(6);
444 m_xError->set_size_request(nMaxWidth, nMaxHeight);
446 m_xError->set_text(rFormula);
450 namespace
452 void runAutoCorrectQueryAsync(const std::shared_ptr<FormulaProcessingContext>& context);
454 void performAutoFormatAndUpdate(std::u16string_view rString, const ScMarkData& rMark, SCCOL nCol,
455 SCROW nRow, SCTAB nTab, bool bNumFmtChanged, bool bRecord,
456 const std::shared_ptr<ScDocShellModificator>& pModificator,
457 ScViewFunc& rViewFunc)
459 bool bAutoFormat = rViewFunc.TestFormatArea(nCol, nRow, nTab, bNumFmtChanged);
461 if (bAutoFormat)
462 rViewFunc.DoAutoAttributes(nCol, nRow, nTab, bNumFmtChanged);
464 ScViewData& rViewData = rViewFunc.GetViewData();
465 ScDocShell* pDocSh = rViewData.GetDocShell();
466 pDocSh->UpdateOle(rViewData);
468 const OUString aType(rString.empty() ? u"delete-content" : u"cell-change");
469 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, nCol, nRow, aType);
471 if (bRecord)
473 ScDocFunc &rFunc = rViewData.GetDocFunc();
474 rFunc.EndListAction();
477 pModificator->SetDocumentModified();
478 ScDocument& rDoc = rViewData.GetDocument();
479 lcl_PostRepaintCondFormat(rDoc.GetCondFormat(nCol, nRow, nTab), pDocSh);
480 lcl_PostRepaintSparkLine(rDoc.GetSparklineList(nTab), ScRange(nCol, nRow, nTab), pDocSh);
483 void finalizeFormulaProcessing(const std::shared_ptr<FormulaProcessingContext>& context)
485 // to be used in multiple tabs, the formula must be compiled anew
486 // via ScFormulaCell copy-ctor because of RangeNames,
487 // the same code-array for all cells is not possible.
488 // If the array has an error, (it) must be RPN-erased in the newly generated
489 // cells and the error be set explicitly, so that
490 // via FormulaCell copy-ctor and Interpreter it will be, when possible,
491 // ironed out again, too intelligent... e.g.: =1))
492 FormulaError nError = context->pArr->GetCodeError();
493 if ( nError == FormulaError::NONE )
495 // update list of recent functions with all functions that
496 // are not within parentheses
498 ScModule* pScMod = ScModule::get();
499 ScAppOptions aAppOpt = pScMod->GetAppOptions();
500 bool bOptChanged = false;
502 formula::FormulaToken** ppToken = context->pArr->GetArray();
503 sal_uInt16 nTokens = context->pArr->GetLen();
504 sal_uInt16 nLevel = 0;
505 for (sal_uInt16 nTP=0; nTP<nTokens; nTP++)
507 formula::FormulaToken* pTok = ppToken[nTP];
508 OpCode eOp = pTok->GetOpCode();
509 if ( eOp == ocOpen )
510 ++nLevel;
511 else if ( eOp == ocClose && nLevel )
512 --nLevel;
513 if ( nLevel == 0 && pTok->IsFunction() &&
514 lcl_AddFunction( aAppOpt, sal::static_int_cast<sal_uInt16>( eOp ) ) )
515 bOptChanged = true;
518 if ( bOptChanged )
520 pScMod->SetAppOptions(aAppOpt);
523 if (context->bMatrixExpand)
525 // If the outer function/operator returns an array/matrix then
526 // enter a matrix formula. ScViewFunc::EnterMatrix() takes care
527 // of selection/mark of the result dimensions or preselected
528 // mark. If the user wanted less or a single cell then should
529 // mark such prior to entering the formula.
530 const formula::FormulaToken* pToken = context->pArr->LastRPNToken();
531 if (pToken && (formula::FormulaCompiler::IsMatrixFunction( pToken->GetOpCode())
532 || pToken->IsInForceArray()))
534 // Discard this (still empty here) Undo action,
535 // EnterMatrix() will create its own.
536 if (context->bRecord)
537 context->GetDocFunc().EndListAction();
539 // Use corrected formula string.
540 context->rViewFunc.EnterMatrix( context->aFormula, context->GetDoc().GetGrammar());
542 return;
547 ScFormulaCell aCell(context->GetDoc(), *context->aPos, std::move(*context->pArr), formula::FormulaGrammar::GRAM_DEFAULT, ScMatrixMode::NONE);
549 SCTAB i;
550 SvNumberFormatter* pFormatter = context->GetDoc().GetFormatTable();
551 for (const auto& rTab : context->aMark)
553 i = rTab;
554 context->aPos->SetTab( i );
555 const sal_uInt32 nIndex = context->GetDoc().GetAttr(
556 context->nCol, context->nRow, i, ATTR_VALUE_FORMAT )->GetValue();
557 const SvNumFormatType nType = pFormatter->GetType( nIndex);
558 if (nType == SvNumFormatType::TEXT ||
559 ((context->aString[0] == '+' || context->aString[0] == '-') && nError != FormulaError::NONE && context->aString == context->aFormula))
561 if ( context->xTextObject )
563 // A clone of context->xTextObject will be stored in the cell.
564 context->GetDocFunc().SetEditCell(*(context->aPos), *context->xTextObject, true);
566 else
567 context->GetDocFunc().SetStringCell(*(context->aPos), context->aFormula, true);
569 else
571 ScFormulaCell* pCell = new ScFormulaCell( aCell, context->GetDoc(), *(context->aPos) );
572 if ( nError != FormulaError::NONE )
574 pCell->GetCode()->DelRPN();
575 pCell->SetErrCode( nError );
576 if(pCell->GetCode()->IsHyperLink())
577 pCell->GetCode()->SetHyperLink(false);
579 if (nType == SvNumFormatType::LOGICAL)
581 // Reset to General so the actual format can be determined
582 // after the cell has been interpreted. A sticky boolean
583 // number format is highly likely unwanted... see tdf#75650.
584 // General of same locale as current number format.
585 const SvNumberformat* pEntry = pFormatter->GetEntry( nIndex);
586 const LanguageType nLang = (pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge);
587 const sal_uInt32 nFormat = pFormatter->GetStandardFormat( SvNumFormatType::NUMBER, nLang);
588 ScPatternAttr aPattern(context->GetDoc().getCellAttributeHelper());
589 aPattern.GetItemSet().Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nFormat));
590 ScMarkData aMark(context->GetDoc().GetSheetLimits());
591 aMark.SelectTable( i, true);
592 aMark.SetMarkArea( ScRange( *(context->aPos)));
593 context->GetDocFunc().ApplyAttributes( aMark, aPattern, false);
594 context->bNumFmtChanged = true;
596 context->GetDocFunc().SetFormulaCell(*(context->aPos), pCell, true);
600 performAutoFormatAndUpdate(context->aString, context->aMark, context->nCol,
601 context->nRow, context->nTab, context->bNumFmtChanged,
602 context->bRecord, context->aModificator, context->rViewFunc);
605 void parseAndCorrectFormula(std::shared_ptr<FormulaProcessingContext> context)
607 bool bAddEqual = false;
608 context->pArr = context->aComp->CompileString(context->aFormula);
609 bool bCorrected = context->aComp->IsCorrected();
611 if (bCorrected) {
612 context->pArrFirst = context->pArr;
613 context->pArr = context->aComp->CompileString(context->aComp->GetCorrectedFormula());
616 if (context->pArr->GetCodeError() == FormulaError::NONE) {
617 bAddEqual = true;
618 context->aComp->CompileTokenArray();
619 bCorrected |= context->aComp->IsCorrected();
622 if (bCorrected) {
623 context->aCorrectedFormula = bAddEqual ? "=" + context->aComp->GetCorrectedFormula()
624 : context->aComp->GetCorrectedFormula();
625 if (context->aCorrectedFormula.getLength() == 1) {
626 // empty formula, just '='
627 if (context->pArrFirst)
628 context->pArr = context->pArrFirst;
630 else
632 runAutoCorrectQueryAsync(context);
633 return;
636 finalizeFormulaProcessing(context);
639 void runAutoCorrectQueryAsync(const std::shared_ptr<FormulaProcessingContext>& context)
641 auto aQueryBox = std::make_shared<AutoCorrectQuery>(context->GetViewData().GetDialogParent(), context->aCorrectedFormula);
642 weld::DialogController::runAsync(aQueryBox, [context] (int nResult)
644 if (nResult == RET_YES) {
645 context->aFormula = context->aCorrectedFormula;
646 parseAndCorrectFormula(context);
647 } else {
648 if (context->pArrFirst)
649 context->pArr = context->pArrFirst;
651 finalizeFormulaProcessing(context);
657 // actual functions
659 // input - undo OK
660 void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
661 const OUString& rString,
662 const EditTextObject* pData,
663 bool bMatrixExpand )
665 ScDocument& rDoc = GetViewData().GetDocument();
666 ScMarkData aMark(GetViewData().GetMarkData());
667 bool bRecord = rDoc.IsUndoEnabled();
668 SCTAB i;
670 ScDocShell* pDocSh = GetViewData().GetDocShell();
671 ScDocFunc &rFunc = GetViewData().GetDocFunc();
672 std::shared_ptr<ScDocShellModificator> xModificator = std::make_shared<ScDocShellModificator>(*pDocSh);
674 ScEditableTester aTester( rDoc, nCol,nRow, nCol,nRow, aMark );
675 if (!aTester.IsEditable())
677 ErrorMessage(aTester.GetMessageId());
678 PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
679 return;
682 if ( bRecord )
683 rFunc.EnterListAction( STR_UNDO_ENTERDATA );
685 bool bFormula = false;
687 // do not check formula if it is a text cell
688 sal_uInt32 format = rDoc.GetNumberFormat( nCol, nRow, nTab );
689 SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
690 // a single '=' character is handled as string (needed for special filters)
691 if ( pFormatter->GetType(format) != SvNumFormatType::TEXT && rString.getLength() > 1 )
693 if ( rString[0] == '=' )
695 // handle as formula
696 bFormula = true;
698 else if ( rString[0] == '+' || rString[0] == '-' )
700 // if there is more than one leading '+' or '-' character, remove the additional ones
701 sal_Int32 nIndex = 1;
702 sal_Int32 nLen = rString.getLength();
703 while ( nIndex < nLen && ( rString[ nIndex ] == '+' || rString[ nIndex ] == '-' ) )
705 ++nIndex;
707 OUString aString = rString.replaceAt( 1, nIndex - 1, u"" );
709 // if the remaining part without the leading '+' or '-' character
710 // is non-empty and not a number, handle as formula
711 if ( aString.getLength() > 1 )
713 double fNumber = 0;
714 if ( !pFormatter->IsNumberFormat( aString, format, fNumber ) )
716 bFormula = true;
722 bool bNumFmtChanged = false;
723 if ( bFormula )
724 { // formula, compile with autoCorrection
725 i = aMark.GetFirstSelected();
726 auto xPosPtr = std::make_shared<ScAddress>(nCol, nRow, i);
727 auto xCompPtr = std::make_shared<ScCompiler>(rDoc, *xPosPtr, rDoc.GetGrammar(), true, false);
728 std::unique_ptr<EditTextObject> xTextObject(pData ? pData->Clone() : nullptr);
730 //2do: enable/disable autoCorrection via calcoptions
731 xCompPtr->SetAutoCorrection( true );
732 if ( rString[0] == '+' || rString[0] == '-' )
734 xCompPtr->SetExtendedErrorDetection( ScCompiler::EXTENDED_ERROR_DETECTION_NAME_BREAK );
737 OUString aFormula( rString );
739 FormulaProcessingContext context_instance{
740 std::move(xPosPtr), std::move(xCompPtr), std::move(xModificator), nullptr,
741 nullptr, std::move(xTextObject), std::move(aMark), *this,
742 OUString(), aFormula, rString, nCol,
743 nRow, nTab, bMatrixExpand, bNumFmtChanged,
744 bRecord
747 parseAndCorrectFormula(std::make_shared<FormulaProcessingContext>(context_instance));
749 else
751 ScFieldEditEngine& rEngine = rDoc.GetEditEngine();
752 for (const auto& rTab : aMark)
754 bool bNumFmtSet = false;
755 const ScAddress aScAddress(nCol, nRow, rTab);
757 // tdf#104902 - handle embedded newline
758 if (ScStringUtil::isMultiline(rString))
760 rEngine.SetTextCurrentDefaults(rString);
761 rDoc.SetEditText(aScAddress, rEngine.CreateTextObject());
762 pDocSh->AdjustRowHeight(nRow, nRow, rTab);
764 else
766 rFunc.SetNormalString(bNumFmtSet, aScAddress, rString, false);
769 if (bNumFmtSet)
771 /* FIXME: if set on any sheet results in changed only on
772 * sheet nTab for TestFormatArea() and DoAutoAttributes() */
773 bNumFmtChanged = true;
776 performAutoFormatAndUpdate(rString, aMark, nCol, nRow, nTab, bNumFmtChanged, bRecord, xModificator, *this);
780 // enter value in single cell (on nTab only)
782 void ScViewFunc::EnterValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rValue )
784 ScDocument& rDoc = GetViewData().GetDocument();
785 ScDocShell* pDocSh = GetViewData().GetDocShell();
787 if (!pDocSh)
788 return;
790 bool bUndo(rDoc.IsUndoEnabled());
791 ScDocShellModificator aModificator( *pDocSh );
793 ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
794 if (aTester.IsEditable())
796 ScAddress aPos( nCol, nRow, nTab );
797 ScCellValue aUndoCell;
798 if (bUndo)
799 aUndoCell.assign(rDoc, aPos);
801 rDoc.SetValue( nCol, nRow, nTab, rValue );
803 // because of ChangeTrack after change in document
804 if (bUndo)
806 pDocSh->GetUndoManager()->AddUndoAction(
807 std::make_unique<ScUndoEnterValue>(pDocSh, aPos, aUndoCell, rValue));
810 pDocSh->PostPaintCell( aPos );
811 pDocSh->UpdateOle(GetViewData());
812 aModificator.SetDocumentModified();
814 else
815 ErrorMessage(aTester.GetMessageId());
818 void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab,
819 const EditTextObject& rData, bool bTestSimple )
821 ScDocShell* pDocSh = GetViewData().GetDocShell();
822 ScMarkData& rMark = GetViewData().GetMarkData();
823 ScDocument& rDoc = pDocSh->GetDocument();
824 bool bRecord = rDoc.IsUndoEnabled();
826 ScDocShellModificator aModificator( *pDocSh );
828 ScEditableTester aTester( rDoc, nTab, nCol,nRow, nCol,nRow );
829 if (aTester.IsEditable())
832 // test for attribute
834 bool bSimple = false;
835 bool bCommon = false;
836 std::unique_ptr<ScPatternAttr> pCellAttrs;
837 OUString aString;
839 const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab );
840 ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), &rDoc );
841 aEngine.SetTextCurrentDefaults(rData);
843 if (bTestSimple) // test, if simple string without attribute
845 ScEditAttrTester aAttrTester( &aEngine );
846 bSimple = !aAttrTester.NeedsObject();
847 bCommon = aAttrTester.NeedsCellAttr();
849 // formulas have to be recognized even if they're formatted
850 // (but common attributes are still collected)
852 if (!bSimple)
854 OUString aParStr(aEngine.GetText( 0 ));
855 if ( aParStr[0] == '=' )
856 bSimple = true;
859 if (bCommon) // attribute for tab
861 pCellAttrs.reset(new ScPatternAttr( *pOldPattern ));
862 pCellAttrs->GetFromEditItemSet( &aAttrTester.GetAttribs() );
863 //! remove common attributes from EditEngine?
867 // #i97726# always get text for "repeat" of undo action
868 aString = ScEditUtil::GetMultilineString(aEngine);
870 // undo
872 std::unique_ptr<EditTextObject> pUndoData;
873 ScUndoEnterData::ValuesType aOldValues;
875 if (bRecord && !bSimple)
877 for (const auto& rTab : rMark)
879 ScUndoEnterData::Value aOldValue;
880 aOldValue.mnTab = rTab;
881 aOldValue.maCell.assign(rDoc, ScAddress(nCol, nRow, rTab));
882 aOldValues.push_back(aOldValue);
885 pUndoData = rData.Clone();
888 // enter data
890 if (bCommon)
891 rDoc.ApplyPattern(nCol,nRow,nTab,*pCellAttrs); //! undo
893 if (bSimple)
895 if (bCommon)
896 AdjustRowHeight(nRow,nRow,true);
898 EnterData( nCol, nRow, nTab, aString, nullptr, true /*bMatrixExpand*/);
900 else
902 for (const auto& rTab : rMark)
904 ScAddress aPos(nCol, nRow, rTab);
905 rDoc.SetEditText(aPos, rData, rDoc.GetEditPool());
908 if ( bRecord )
909 { // because of ChangeTrack current first
910 pDocSh->GetUndoManager()->AddUndoAction(
911 std::make_unique<ScUndoEnterData>(pDocSh, ScAddress(nCol,nRow,nTab), aOldValues, aString, std::move(pUndoData)));
914 HideAllCursors();
916 AdjustRowHeight(nRow,nRow,true);
918 for (const auto& rTab : rMark)
919 pDocSh->PostPaintCell( nCol, nRow, rTab );
921 ShowAllCursors();
923 pDocSh->UpdateOle(GetViewData());
925 bool bIsEmpty = rData.GetParagraphCount() == 0
926 || (rData.GetParagraphCount() == 1 && !rData.HasText(0));
927 const OUString aType(bIsEmpty ? u"delete-content" : u"cell-change");
928 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, rMark, nCol, nRow, aType);
930 aModificator.SetDocumentModified();
932 lcl_PostRepaintCondFormat( rDoc.GetCondFormat( nCol, nRow, nTab ), pDocSh );
934 else
936 ErrorMessage(aTester.GetMessageId());
937 PaintArea( nCol, nRow, nCol, nRow ); // possibly the edit-engine is still painted there
941 void ScViewFunc::EnterDataAtCursor( const OUString& rString )
943 SCCOL nPosX = GetViewData().GetCurX();
944 SCROW nPosY = GetViewData().GetCurY();
945 SCTAB nTab = GetViewData().GetTabNo();
947 EnterData( nPosX, nPosY, nTab, rString );
948 // tdf#154174: update repeated data in the cell
949 GetViewData().GetViewShell()->UpdateInputHandler();
952 void ScViewFunc::EnterMatrix( const OUString& rString, ::formula::FormulaGrammar::Grammar eGram )
954 ScViewData& rData = GetViewData();
955 const SCCOL nCol = rData.GetCurX();
956 const SCROW nRow = rData.GetCurY();
957 const ScMarkData& rMark = rData.GetMarkData();
958 if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
960 // nothing marked -> temporarily calculate block
961 // with size of result formula to get the size
963 ScDocument& rDoc = rData.GetDocument();
964 SCTAB nTab = rData.GetTabNo();
965 ScFormulaCell aFormCell( rDoc, ScAddress(nCol,nRow,nTab), rString, eGram, ScMatrixMode::Formula );
967 SCSIZE nSizeX;
968 SCSIZE nSizeY;
969 aFormCell.GetResultDimensions( nSizeX, nSizeY );
970 if ( nSizeX != 0 && nSizeY != 0 &&
971 nCol+nSizeX-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxCol()) &&
972 nRow+nSizeY-1 <= sal::static_int_cast<SCSIZE>(rDoc.MaxRow()) )
974 ScRange aResult( nCol, nRow, nTab,
975 sal::static_int_cast<SCCOL>(nCol+nSizeX-1),
976 sal::static_int_cast<SCROW>(nRow+nSizeY-1), nTab );
977 MarkRange( aResult, false );
981 ScRange aRange;
982 if (rData.GetSimpleArea(aRange) == SC_MARK_SIMPLE)
984 ScDocShell* pDocSh = rData.GetDocShell();
985 bool bSuccess = pDocSh->GetDocFunc().EnterMatrix(
986 aRange, &rMark, nullptr, rString, false, false, OUString(), eGram );
987 if (bSuccess)
988 pDocSh->UpdateOle(GetViewData());
989 else
990 PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there
992 else
993 ErrorMessage(STR_NOMULTISELECT);
996 SvtScriptType ScViewFunc::GetSelectionScriptType()
998 SvtScriptType nScript = SvtScriptType::NONE;
1000 ScDocument& rDoc = GetViewData().GetDocument();
1001 const ScMarkData& rMark = GetViewData().GetMarkData();
1002 if ( !rMark.IsMarked() && !rMark.IsMultiMarked() )
1004 // no selection -> cursor
1006 nScript = rDoc.GetScriptType( GetViewData().GetCurX(),
1007 GetViewData().GetCurY(), GetViewData().GetTabNo());
1009 else
1011 ScRangeList aRanges;
1012 rMark.FillRangeListWithMarks( &aRanges, false );
1013 nScript = rDoc.GetRangeScriptType(aRanges);
1016 if (nScript == SvtScriptType::NONE)
1017 nScript = ScGlobal::GetDefaultScriptType();
1019 return nScript;
1022 static void ShrinkToDataArea(ScMarkData& rFuncMark, ScDocument& rDoc);
1024 const ScPatternAttr* ScViewFunc::GetSelectionPattern()
1026 // Don't use UnmarkFiltered in slot state functions, for performance reasons.
1027 // The displayed state is always that of the whole selection including filtered rows.
1029 ScMarkData aMark = GetViewData().GetMarkData();
1030 ScDocument& rDoc = GetViewData().GetDocument();
1032 // tdf#155368 if the selection is the whole sheet, we need to shrink the mark area, otherwise
1033 // we will not return a consistent result
1034 // (consistent compared to what happens in ScViewFunc::ApplySelectionPattern)
1035 ShrinkToDataArea( aMark, rDoc );
1037 if ( aMark.IsMarked() || aMark.IsMultiMarked() )
1039 // MarkToMulti is no longer necessary for rDoc.GetSelectionPattern
1040 const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aMark );
1041 return pAttr;
1043 else
1045 SCCOL nCol = GetViewData().GetCurX();
1046 SCROW nRow = GetViewData().GetCurY();
1047 SCTAB nTab = GetViewData().GetTabNo();
1049 // copy sheet selection
1050 aMark.SetMarkArea( ScRange( nCol, nRow, nTab ) );
1051 const ScPatternAttr* pAttr = rDoc.GetSelectionPattern( aMark );
1052 return pAttr;
1056 void ScViewFunc::GetSelectionFrame(
1057 std::shared_ptr<SvxBoxItem>& rLineOuter,
1058 std::shared_ptr<SvxBoxInfoItem>& rLineInner )
1060 ScDocument& rDoc = GetViewData().GetDocument();
1061 const ScMarkData& rMark = GetViewData().GetMarkData();
1063 if ( rMark.IsMarked() || rMark.IsMultiMarked() )
1065 rDoc.GetSelectionFrame( rMark, *rLineOuter, *rLineInner );
1067 else
1069 const ScPatternAttr* pAttrs =
1070 rDoc.GetPattern( GetViewData().GetCurX(),
1071 GetViewData().GetCurY(),
1072 GetViewData().GetTabNo() );
1074 rLineOuter.reset(pAttrs->GetItem(ATTR_BORDER).Clone());
1075 rLineInner.reset(pAttrs->GetItem(ATTR_BORDER_INNER).Clone());
1077 rLineInner->SetTable(false);
1078 rLineInner->SetDist(true);
1079 rLineInner->SetMinDist(false);
1083 // apply attribute - undo OK
1085 // complete set ( ATTR_STARTINDEX, ATTR_ENDINDEX )
1087 void ScViewFunc::ApplyAttributes( const SfxItemSet& rDialogSet,
1088 const SfxItemSet& rOldSet,
1089 bool bAdjustBlockHeight)
1091 // not editable because of matrix only? attribute OK nonetheless
1092 bool bOnlyNotBecauseOfMatrix;
1093 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1095 ErrorMessage(STR_PROTECTIONERR);
1096 return;
1099 ScDocument& rDoc = GetViewData().GetDocument();
1100 ScPatternAttr aOldAttrs(rDoc.getCellAttributeHelper(), &rOldSet);
1101 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper(), &rDialogSet);
1102 aNewAttrs.DeleteUnchanged( &aOldAttrs );
1104 if ( rDialogSet.GetItemState( ATTR_VALUE_FORMAT ) == SfxItemState::SET )
1105 { // don't reset to default SYSTEM GENERAL if not intended
1106 sal_uInt32 nOldFormat =
1107 rOldSet.Get( ATTR_VALUE_FORMAT ).GetValue();
1108 sal_uInt32 nNewFormat =
1109 rDialogSet.Get( ATTR_VALUE_FORMAT ).GetValue();
1110 if ( nNewFormat != nOldFormat )
1112 SvNumberFormatter* pFormatter =
1113 GetViewData().GetDocument().GetFormatTable();
1114 const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
1115 LanguageType eOldLang =
1116 pOldEntry ? pOldEntry->GetLanguage() : LANGUAGE_DONTKNOW;
1117 const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewFormat );
1118 LanguageType eNewLang =
1119 pNewEntry ? pNewEntry->GetLanguage() : LANGUAGE_DONTKNOW;
1120 if ( eNewLang != eOldLang )
1122 aNewAttrs.GetItemSet().Put(
1123 SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) );
1125 // only the language has changed -> do not touch numberformat-attribute
1126 sal_uInt32 nNewMod = nNewFormat % SV_COUNTRY_LANGUAGE_OFFSET;
1127 if ( nNewMod == ( nOldFormat % SV_COUNTRY_LANGUAGE_OFFSET ) &&
1128 nNewMod <= SV_MAX_COUNT_STANDARD_FORMATS )
1129 aNewAttrs.GetItemSet().ClearItem( ATTR_VALUE_FORMAT );
1134 if (rDialogSet.HasItem(ATTR_FONT_LANGUAGE))
1135 // font language has changed. Redo the online spelling.
1136 ResetAutoSpell();
1138 const SvxBoxItem& rOldOuter = rOldSet.Get(ATTR_BORDER);
1139 const SvxBoxItem& rNewOuter = rDialogSet.Get(ATTR_BORDER);
1140 const SvxBoxInfoItem& rOldInner = rOldSet.Get(ATTR_BORDER_INNER);
1141 const SvxBoxInfoItem& rNewInner = rDialogSet.Get(ATTR_BORDER_INNER);
1142 SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
1144 // protect referenced Items from disappearing (was: don't delete yet)
1145 const SfxPoolItemHolder aHoldOuter(*rDialogSet.GetPool() , &rNewOuter);
1146 const SfxPoolItemHolder aHoldInner(*rDialogSet.GetPool() , &rNewInner);
1147 (void)aHoldOuter;
1148 (void)aHoldInner;
1150 rNewSet.ClearItem( ATTR_BORDER );
1151 rNewSet.ClearItem( ATTR_BORDER_INNER );
1154 * establish whether border attribute is to be set:
1155 * 1. new != old
1156 * 2. is one of the borders not-DontCare (since 238.f: IsxxValid())
1160 bool bFrame = (rDialogSet.GetItemState( ATTR_BORDER ) != SfxItemState::DEFAULT)
1161 || (rDialogSet.GetItemState( ATTR_BORDER_INNER ) != SfxItemState::DEFAULT);
1163 if (SfxPoolItem::areSame(rNewOuter, rOldOuter) && SfxPoolItem::areSame(rNewInner, rOldInner))
1164 bFrame = false;
1166 // this should be intercepted by the pool: ?!??!??
1168 if (bFrame && rNewOuter == rOldOuter && rNewInner == rOldInner)
1169 bFrame = false;
1171 bFrame = bFrame
1172 && ( rNewInner.IsValid(SvxBoxInfoItemValidFlags::LEFT)
1173 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::RIGHT)
1174 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::TOP)
1175 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::BOTTOM)
1176 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::HORI)
1177 || rNewInner.IsValid(SvxBoxInfoItemValidFlags::VERT) );
1179 if (!bFrame)
1180 ApplySelectionPattern( aNewAttrs ); // standard only
1181 else
1183 // if new items are default-items, overwrite the old items:
1185 bool bDefNewOuter = IsStaticDefaultItem(&rNewOuter);
1186 bool bDefNewInner = IsStaticDefaultItem(&rNewInner);
1188 ApplyPatternLines( aNewAttrs,
1189 bDefNewOuter ? rOldOuter : rNewOuter,
1190 bDefNewInner ? &rOldInner : &rNewInner );
1193 // adjust height only if needed
1194 if (bAdjustBlockHeight)
1195 AdjustBlockHeight();
1197 // CellContentChanged is called in ApplySelectionPattern / ApplyPatternLines
1200 void ScViewFunc::ApplyAttr( const SfxPoolItem& rAttrItem, bool bAdjustBlockHeight )
1202 // not editable because of matrix only? attribute OK nonetheless
1203 bool bOnlyNotBecauseOfMatrix;
1204 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1206 ErrorMessage(STR_PROTECTIONERR);
1207 return;
1210 ScDocument& rDoc = GetViewData().GetDocument();
1211 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper());
1213 aNewAttrs.GetItemSet().Put( rAttrItem );
1214 // if justify is set (with Buttons), always indentation 0
1215 if ( rAttrItem.Which() == ATTR_HOR_JUSTIFY )
1216 aNewAttrs.GetItemSet().Put( ScIndentItem( 0 ) );
1217 ApplySelectionPattern( aNewAttrs );
1219 // Prevent useless compute
1220 if (bAdjustBlockHeight)
1221 AdjustBlockHeight();
1223 // CellContentChanged is called in ApplySelectionPattern
1226 // patterns and borders
1228 void ScViewFunc::ApplyPatternLines( const ScPatternAttr& rAttr, const SvxBoxItem& rNewOuter,
1229 const SvxBoxInfoItem* pNewInner )
1231 ScDocument& rDoc = GetViewData().GetDocument();
1232 ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
1233 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1234 bool bRecord = true;
1235 if (!rDoc.IsUndoEnabled())
1236 bRecord = false;
1238 bool bRemoveAdjCellBorder = rNewOuter.IsRemoveAdjacentCellBorder();
1239 ScRange aMarkRange, aMarkRangeWithEnvelope;
1240 aFuncMark.MarkToSimple();
1241 bool bMulti = aFuncMark.IsMultiMarked();
1242 if (bMulti)
1243 aMarkRange = aFuncMark.GetMultiMarkArea();
1244 else if (aFuncMark.IsMarked())
1245 aMarkRange = aFuncMark.GetMarkArea();
1246 else
1248 aMarkRange = ScRange( GetViewData().GetCurX(),
1249 GetViewData().GetCurY(), GetViewData().GetTabNo() );
1250 DoneBlockMode();
1251 InitOwnBlockMode( aMarkRange );
1252 aFuncMark.SetMarkArea(aMarkRange);
1253 MarkDataChanged();
1255 if( bRemoveAdjCellBorder )
1256 aFuncMark.GetSelectionCover( aMarkRangeWithEnvelope );
1257 else
1258 aMarkRangeWithEnvelope = aMarkRange;
1260 ScDocShell* pDocSh = GetViewData().GetDocShell();
1262 ScDocShellModificator aModificator( *pDocSh );
1264 if (bRecord)
1266 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1267 SCTAB nStartTab = aMarkRange.aStart.Tab();
1268 SCTAB nTabCount = rDoc.GetTableCount();
1269 bool bCopyOnlyMarked = false;
1270 if( !bRemoveAdjCellBorder )
1271 bCopyOnlyMarked = bMulti;
1272 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
1273 for (const auto& rTab : aFuncMark)
1274 if (rTab != nStartTab)
1275 pUndoDoc->AddUndoTab( rTab, rTab );
1277 ScRange aCopyRange = aMarkRangeWithEnvelope;
1278 aCopyRange.aStart.SetTab(0);
1279 aCopyRange.aEnd.SetTab(nTabCount-1);
1280 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bCopyOnlyMarked, *pUndoDoc, &aFuncMark );
1282 pDocSh->GetUndoManager()->AddUndoAction(
1283 std::make_unique<ScUndoSelectionAttr>(
1284 pDocSh, aFuncMark,
1285 aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), aMarkRange.aStart.Tab(),
1286 aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), aMarkRange.aEnd.Tab(),
1287 std::move(pUndoDoc), bCopyOnlyMarked, &rAttr, &rNewOuter, pNewInner, &aMarkRangeWithEnvelope ) );
1290 sal_uInt16 nExt = SC_PF_TESTMERGE;
1291 pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content before the change
1293 rDoc.ApplySelectionFrame(aFuncMark, rNewOuter, pNewInner);
1295 pDocSh->UpdatePaintExt( nExt, aMarkRangeWithEnvelope ); // content after the change
1297 aFuncMark.MarkToMulti();
1298 rDoc.ApplySelectionPattern( rAttr, aFuncMark );
1300 pDocSh->PostPaint( aMarkRange, PaintPartFlags::Grid, nExt );
1301 pDocSh->UpdateOle(GetViewData());
1302 aModificator.SetDocumentModified();
1303 CellContentChanged();
1305 StartFormatArea();
1308 // tdf#147842 if the marked area is the entire sheet, then shrink it to the data area.
1309 // Otherwise ctrl-A, perform-action, will take a very long time as it tries to modify
1310 // cells that we are not using.
1311 static void ShrinkToDataArea(ScMarkData& rFuncMark, ScDocument& rDoc)
1313 // do not make it marked if it is not already marked
1314 if (!rFuncMark.IsMarked())
1315 return;
1316 if (rFuncMark.IsMultiMarked())
1317 return;
1318 ScRange aMarkArea = rFuncMark.GetMarkArea();
1319 const ScSheetLimits& rLimits = rDoc.GetSheetLimits();
1320 if (aMarkArea.aStart.Row() != 0 || aMarkArea.aStart.Col() != 0)
1321 return;
1322 if (aMarkArea.aEnd.Row() != rLimits.MaxRow() || aMarkArea.aEnd.Col() != rLimits.MaxCol())
1323 return;
1324 if (aMarkArea.aStart.Tab() != aMarkArea.aEnd.Tab())
1325 return;
1326 SCCOL nStartCol = aMarkArea.aStart.Col();
1327 SCROW nStartRow = aMarkArea.aStart.Row();
1328 SCCOL nEndCol = aMarkArea.aEnd.Col();
1329 SCROW nEndRow = aMarkArea.aEnd.Row();
1330 rDoc.ShrinkToDataArea(aMarkArea.aStart.Tab(), nStartCol, nStartRow, nEndCol, nEndRow);
1331 aMarkArea.aStart.SetCol(nStartCol);
1332 aMarkArea.aStart.SetRow(nStartRow);
1333 aMarkArea.aEnd.SetCol(nEndCol);
1334 aMarkArea.aEnd.SetRow(nEndRow);
1335 rFuncMark.ResetMark();
1336 rFuncMark.SetMarkArea(aMarkArea);
1339 // pattern only
1341 void ScViewFunc::ApplySelectionPattern( const ScPatternAttr& rAttr, bool bCursorOnly )
1343 ScViewData& rViewData = GetViewData();
1344 ScDocShell* pDocSh = rViewData.GetDocShell();
1345 ScDocument& rDoc = pDocSh->GetDocument();
1346 ScMarkData aFuncMark( rViewData.GetMarkData() ); // local copy for UnmarkFiltered
1347 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1349 bool bRecord = true;
1350 if (!rDoc.IsUndoEnabled())
1351 bRecord = false;
1353 // State from old ItemSet doesn't matter for paint flags, as any change will be
1354 // from SfxItemState::SET in the new ItemSet (default is ignored in ApplyPattern).
1355 // New alignment is checked (check in PostPaint isn't enough) in case a right
1356 // alignment is changed to left.
1357 const SfxItemSet& rNewSet = rAttr.GetItemSet();
1358 bool bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
1359 rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
1360 bool bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
1362 sal_uInt16 nExtFlags = 0;
1363 if ( bSetLines )
1364 nExtFlags |= SC_PF_LINES;
1365 if ( bSetAlign )
1366 nExtFlags |= SC_PF_WHOLEROWS;
1368 ScDocShellModificator aModificator( *pDocSh );
1370 bool bMulti = aFuncMark.IsMultiMarked();
1371 aFuncMark.MarkToMulti();
1372 bool bOnlyTab = (!aFuncMark.IsMultiMarked() && !bCursorOnly && aFuncMark.GetSelectCount() > 1);
1373 if (bOnlyTab)
1375 SCCOL nCol = rViewData.GetCurX();
1376 SCROW nRow = rViewData.GetCurY();
1377 SCTAB nTab = rViewData.GetTabNo();
1378 aFuncMark.SetMarkArea(ScRange(nCol,nRow,nTab));
1379 aFuncMark.MarkToMulti();
1382 ScRangeList aChangeRanges;
1384 if (aFuncMark.IsMultiMarked() && !bCursorOnly)
1386 const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
1387 SCTAB nTabCount = rDoc.GetTableCount();
1388 for (const auto& rTab : aFuncMark)
1390 ScRange aChangeRange( aMarkRange );
1391 aChangeRange.aStart.SetTab( rTab );
1392 aChangeRange.aEnd.SetTab( rTab );
1393 aChangeRanges.push_back( aChangeRange );
1396 SCCOL nStartCol = aMarkRange.aStart.Col();
1397 SCROW nStartRow = aMarkRange.aStart.Row();
1398 SCTAB nStartTab = aMarkRange.aStart.Tab();
1399 SCCOL nEndCol = aMarkRange.aEnd.Col();
1400 SCROW nEndRow = aMarkRange.aEnd.Row();
1401 SCTAB nEndTab = aMarkRange.aEnd.Tab();
1403 ScEditDataArray* pEditDataArray = nullptr;
1404 if (bRecord)
1406 ScRange aCopyRange = aMarkRange;
1407 aCopyRange.aStart.SetTab(0);
1408 aCopyRange.aEnd.SetTab(nTabCount-1);
1410 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1411 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab );
1412 for (const auto& rTab : aFuncMark)
1413 if (rTab != nStartTab)
1414 pUndoDoc->AddUndoTab( rTab, rTab );
1415 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &aFuncMark );
1417 aFuncMark.MarkToMulti();
1419 ScUndoSelectionAttr* pUndoAttr = new ScUndoSelectionAttr(
1420 pDocSh, aFuncMark, nStartCol, nStartRow, nStartTab,
1421 nEndCol, nEndRow, nEndTab, std::move(pUndoDoc), bMulti, &rAttr );
1422 pDocSh->GetUndoManager()->AddUndoAction(std::unique_ptr<ScUndoSelectionAttr>(pUndoAttr));
1423 pEditDataArray = pUndoAttr->GetDataArray();
1426 rDoc.ApplySelectionPattern( rAttr, aFuncMark, pEditDataArray );
1428 pDocSh->PostPaint( nStartCol, nStartRow, nStartTab,
1429 nEndCol, nEndRow, nEndTab,
1430 PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
1431 pDocSh->UpdateOle(GetViewData());
1432 aModificator.SetDocumentModified();
1433 CellContentChanged();
1435 else // single cell - simpler undo
1437 SCCOL nCol = rViewData.GetCurX();
1438 SCROW nRow = rViewData.GetCurY();
1439 SCTAB nTab = rViewData.GetTabNo();
1441 std::unique_ptr<EditTextObject> pOldEditData;
1442 std::unique_ptr<EditTextObject> pNewEditData;
1443 ScAddress aPos(nCol, nRow, nTab);
1444 ScRefCellValue aCell(rDoc, aPos);
1445 if (aCell.getType() == CELLTYPE_EDIT)
1447 const EditTextObject* pEditObj = aCell.getEditText();
1448 pOldEditData = pEditObj->Clone();
1449 rDoc.RemoveEditTextCharAttribs(aPos, rAttr);
1450 pEditObj = rDoc.GetEditText(aPos);
1451 pNewEditData = pEditObj->Clone();
1454 aChangeRanges.push_back(ScRange(aPos));
1455 std::optional<ScPatternAttr> pOldPat(*rDoc.GetPattern( nCol, nRow, nTab ));
1457 rDoc.ApplyPattern( nCol, nRow, nTab, rAttr );
1459 const ScPatternAttr* pNewPat = rDoc.GetPattern( nCol, nRow, nTab );
1461 if (bRecord)
1463 std::unique_ptr<ScUndoCursorAttr> pUndo(new ScUndoCursorAttr(
1464 pDocSh, nCol, nRow, nTab, &*pOldPat, pNewPat, &rAttr ));
1465 pUndo->SetEditData(std::move(pOldEditData), std::move(pNewEditData));
1466 pDocSh->GetUndoManager()->AddUndoAction(std::move(pUndo));
1468 pOldPat.reset(); // is copied in undo (Pool)
1470 pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
1471 pDocSh->UpdateOle(GetViewData());
1472 aModificator.SetDocumentModified();
1473 CellContentChanged();
1476 ScModelObj* pModelObj = pDocSh->GetModel();
1478 if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
1480 css::uno::Sequence< css::beans::PropertyValue > aProperties;
1481 sal_Int32 nCount = 0;
1482 const SfxItemPropertyMap& rMap = ScCellObj::GetCellPropertyMap();
1483 for ( sal_uInt16 nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; ++nWhich )
1485 const SfxPoolItem* pItem = nullptr;
1486 if ( rNewSet.GetItemState( nWhich, true, &pItem ) == SfxItemState::SET && pItem )
1488 for ( const auto & rPair : rMap.getPropertyEntries())
1490 const SfxItemPropertyMapEntry* pEntry = rPair.second;
1491 if ( pEntry->nWID == nWhich )
1493 css::uno::Any aVal;
1494 pItem->QueryValue( aVal, pEntry->nMemberId );
1495 aProperties.realloc( nCount + 1 );
1496 auto pProperties = aProperties.getArray();
1497 pProperties[ nCount ].Name = pEntry->aName;
1498 pProperties[ nCount ].Value = std::move(aVal);
1499 ++nCount;
1504 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, u"attribute"_ustr, aProperties);
1507 StartFormatArea();
1510 void ScViewFunc::ApplyUserItemSet( const SfxItemSet& rItemSet )
1512 // ItemSet from UI, may have different pool
1514 bool bOnlyNotBecauseOfMatrix;
1515 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1517 ErrorMessage(STR_PROTECTIONERR);
1518 return;
1521 ScPatternAttr aNewAttrs(GetViewData().GetDocument().getCellAttributeHelper());
1522 SfxItemSet& rNewSet = aNewAttrs.GetItemSet();
1523 rNewSet.Put( rItemSet, false );
1524 ApplySelectionPattern( aNewAttrs );
1526 AdjustBlockHeight();
1529 const SfxStyleSheet* ScViewFunc::GetStyleSheetFromMarked()
1531 // Don't use UnmarkFiltered in slot state functions, for performance reasons.
1532 // The displayed state is always that of the whole selection including filtered rows.
1534 const ScStyleSheet* pSheet = nullptr;
1535 ScViewData& rViewData = GetViewData();
1536 ScDocument& rDoc = rViewData.GetDocument();
1537 ScMarkData& rMark = rViewData.GetMarkData();
1539 if ( rMark.IsMarked() || rMark.IsMultiMarked() )
1540 pSheet = rDoc.GetSelectionStyle( rMark ); // MarkToMulti isn't necessary
1541 else
1542 pSheet = rDoc.GetStyle( rViewData.GetCurX(),
1543 rViewData.GetCurY(),
1544 rViewData.GetTabNo() );
1546 return pSheet;
1549 void ScViewFunc::SetStyleSheetToMarked( const SfxStyleSheet* pStyleSheet )
1551 // not editable because of matrix only? attribute OK nonetheless
1552 bool bOnlyNotBecauseOfMatrix;
1553 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
1555 ErrorMessage(STR_PROTECTIONERR);
1556 return;
1559 if ( !pStyleSheet) return;
1561 ScViewData& rViewData = GetViewData();
1562 ScDocShell* pDocSh = rViewData.GetDocShell();
1563 ScDocument& rDoc = pDocSh->GetDocument();
1564 ScMarkData aFuncMark( rViewData.GetMarkData() ); // local copy for UnmarkFiltered
1565 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
1566 SCTAB nTabCount = rDoc.GetTableCount();
1567 bool bRecord = true;
1568 if (!rDoc.IsUndoEnabled())
1569 bRecord = false;
1571 ScDocShellModificator aModificator( *pDocSh );
1573 if ( aFuncMark.IsMarked() || aFuncMark.IsMultiMarked() )
1575 aFuncMark.MarkToMulti();
1576 const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea();
1578 if ( bRecord )
1580 SCTAB nTab = rViewData.GetTabNo();
1581 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1582 pUndoDoc->InitUndo( rDoc, nTab, nTab );
1583 for (const auto& rTab : aFuncMark)
1584 if (rTab != nTab)
1585 pUndoDoc->AddUndoTab( rTab, rTab );
1587 ScRange aCopyRange = aMarkRange;
1588 aCopyRange.aStart.SetTab(0);
1589 aCopyRange.aEnd.SetTab(nTabCount-1);
1590 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aFuncMark );
1591 aFuncMark.MarkToMulti();
1593 OUString aName = pStyleSheet->GetName();
1594 pDocSh->GetUndoManager()->AddUndoAction(
1595 std::make_unique<ScUndoSelectionStyle>( pDocSh, aFuncMark, aMarkRange, aName, std::move(pUndoDoc) ) );
1598 rDoc.ApplySelectionStyle( static_cast<const ScStyleSheet&>(*pStyleSheet), aFuncMark );
1600 if (!AdjustBlockHeight())
1601 rViewData.GetDocShell()->PostPaint( aMarkRange, PaintPartFlags::Grid );
1603 aFuncMark.MarkToSimple();
1605 else
1607 SCCOL nCol = rViewData.GetCurX();
1608 SCROW nRow = rViewData.GetCurY();
1609 SCTAB nTab = rViewData.GetTabNo();
1611 if ( bRecord )
1613 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO ));
1614 pUndoDoc->InitUndo( rDoc, nTab, nTab );
1615 for (const auto& rTab : aFuncMark)
1616 if (rTab != nTab)
1617 pUndoDoc->AddUndoTab( rTab, rTab );
1619 ScRange aCopyRange( nCol, nRow, 0, nCol, nRow, nTabCount-1 );
1620 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc );
1622 ScRange aMarkRange ( nCol, nRow, nTab );
1623 ScMarkData aUndoMark = aFuncMark;
1624 aUndoMark.SetMultiMarkArea( aMarkRange );
1626 OUString aName = pStyleSheet->GetName();
1627 pDocSh->GetUndoManager()->AddUndoAction(
1628 std::make_unique<ScUndoSelectionStyle>( pDocSh, aUndoMark, aMarkRange, aName, std::move(pUndoDoc) ) );
1631 for (const auto& rTab : aFuncMark)
1632 rDoc.ApplyStyle( nCol, nRow, rTab, static_cast<const ScStyleSheet&>(*pStyleSheet) );
1634 if (!AdjustBlockHeight())
1635 rViewData.GetDocShell()->PostPaintCell( nCol, nRow, nTab );
1639 aModificator.SetDocumentModified();
1641 StartFormatArea();
1644 void ScViewFunc::RemoveStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
1646 if ( !pStyleSheet) return;
1648 ScViewData& rViewData = GetViewData();
1649 ScDocument& rDoc = rViewData.GetDocument();
1650 ScDocShell* pDocSh = rViewData.GetDocShell();
1652 ScDocShellModificator aModificator( *pDocSh );
1654 ScopedVclPtrInstance< VirtualDevice > pVirtDev;
1655 pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
1656 rDoc.StyleSheetChanged( pStyleSheet, true, pVirtDev,
1657 rViewData.GetPPTX(),
1658 rViewData.GetPPTY(),
1659 rViewData.GetZoomX(),
1660 rViewData.GetZoomY() );
1662 pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
1663 aModificator.SetDocumentModified();
1665 ScInputHandler* pHdl = ScModule::get()->GetInputHdl();
1666 if (pHdl)
1667 pHdl->ForgetLastPattern();
1670 void ScViewFunc::UpdateStyleSheetInUse( const SfxStyleSheetBase* pStyleSheet )
1672 if ( !pStyleSheet) return;
1674 ScViewData& rViewData = GetViewData();
1675 ScDocument& rDoc = rViewData.GetDocument();
1676 ScDocShell* pDocSh = rViewData.GetDocShell();
1678 ScDocShellModificator aModificator( *pDocSh );
1680 ScopedVclPtrInstance< VirtualDevice > pVirtDev;
1681 pVirtDev->SetMapMode(MapMode(MapUnit::MapPixel));
1682 rDoc.StyleSheetChanged( pStyleSheet, false, pVirtDev,
1683 rViewData.GetPPTX(),
1684 rViewData.GetPPTY(),
1685 rViewData.GetZoomX(),
1686 rViewData.GetZoomY() );
1688 pDocSh->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid|PaintPartFlags::Left );
1689 aModificator.SetDocumentModified();
1691 ScInputHandler* pHdl = ScModule::get()->GetInputHdl();
1692 if (pHdl)
1693 pHdl->ForgetLastPattern();
1697 void ScViewFunc::OnLOKInsertDeleteColumn(SCCOL nStartCol, tools::Long nOffset)
1699 if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
1700 return;
1702 SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
1703 SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1704 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1705 while (pViewShell)
1707 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1708 if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1710 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurrentTabIndex))
1711 pPosHelper->invalidateByIndex(nStartCol);
1713 // if we remove a column the cursor position and the current selection
1714 // in other views could need to be moved on the left by one column.
1715 if (pTabViewShell != this)
1717 if (pTabViewShell->getPart() == nCurrentTabIndex)
1719 SCCOL nX = pTabViewShell->GetViewData().GetCurX();
1720 if (nX > nStartCol)
1722 tools::Long offset = nOffset;
1723 if (nOffset + nStartCol > nX)
1724 offset = nX - nStartCol;
1725 else if (nOffset < 0 && nStartCol - nOffset > nX)
1726 offset = -1 * (nX - nStartCol);
1728 ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
1729 SCROW nY = pTabViewShell->GetViewData().GetCurY();
1730 pTabViewShell->SetCursor(nX + offset, nY);
1731 if (pInputHdl && pInputHdl->IsInputMode())
1733 pInputHdl->SetModified();
1737 ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
1738 aMultiMark.SetMarking( false );
1740 if (aMultiMark.IsMultiMarked() || aMultiMark.IsMarked())
1742 aMultiMark.ShiftCols(pTabViewShell->GetViewData().GetDocument(), nStartCol, nOffset);
1743 pTabViewShell->SetMarkData(aMultiMark);
1746 else
1748 SCROW nX = pTabViewShell->GetViewData().GetCurXForTab(nCurrentTabIndex);
1749 if (nX > nStartCol || (nX == nStartCol && nOffset > 0))
1751 pTabViewShell->GetViewData().SetCurXForTab(nX + nOffset, nCurrentTabIndex);
1756 pViewShell = SfxViewShell::GetNext(*pViewShell);
1760 void ScViewFunc::OnLOKInsertDeleteRow(SCROW nStartRow, tools::Long nOffset)
1762 if (!comphelper::LibreOfficeKit::isActive() || nOffset == 0)
1763 return;
1765 SCTAB nCurrentTabIndex = GetViewData().GetTabNo();
1766 SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1767 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1768 while (pViewShell)
1770 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1771 if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1773 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurrentTabIndex))
1774 pPosHelper->invalidateByIndex(nStartRow);
1776 // if we remove a row the cursor position and the current selection
1777 // in other views could need to be moved up by one row.
1778 if (pTabViewShell != this)
1780 if (pTabViewShell->getPart() == nCurrentTabIndex)
1782 SCROW nY = pTabViewShell->GetViewData().GetCurY();
1783 if (nY > nStartRow)
1785 tools::Long offset = nOffset;
1786 if (nOffset + nStartRow > nY)
1787 offset = nY - nStartRow;
1788 else if (nOffset < 0 && nStartRow - nOffset > nY)
1789 offset = -1 * (nY - nStartRow);
1791 ScInputHandler* pInputHdl = pTabViewShell->GetInputHandler();
1792 SCCOL nX = pTabViewShell->GetViewData().GetCurX();
1793 pTabViewShell->SetCursor(nX, nY + offset);
1794 if (pInputHdl && pInputHdl->IsInputMode())
1796 pInputHdl->SetModified();
1800 ScMarkData aMultiMark( pTabViewShell->GetViewData().GetMarkData() );
1801 aMultiMark.SetMarking( false );
1803 if (aMultiMark.IsMultiMarked() || aMultiMark.IsMarked())
1805 aMultiMark.ShiftRows(pTabViewShell->GetViewData().GetDocument(), nStartRow, nOffset);
1806 pTabViewShell->SetMarkData(aMultiMark);
1809 else
1811 SCROW nY = pTabViewShell->GetViewData().GetCurYForTab(nCurrentTabIndex);
1812 if (nY > nStartRow || (nY == nStartRow && nOffset > 0))
1814 pTabViewShell->GetViewData().SetCurYForTab(nY + nOffset, nCurrentTabIndex);
1819 pViewShell = SfxViewShell::GetNext(*pViewShell);
1823 void ScViewFunc::OnLOKSetWidthOrHeight(SCCOLROW nStart, bool bWidth)
1825 if (!comphelper::LibreOfficeKit::isActive())
1826 return;
1828 SCTAB nCurTab = GetViewData().GetTabNo();
1829 SfxViewShell* pCurrentViewShell = GetViewData().GetViewShell();
1830 SfxViewShell* pViewShell = SfxViewShell::GetFirst();
1831 while (pViewShell)
1833 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell);
1834 if (pTabViewShell && pTabViewShell->GetDocId() == pCurrentViewShell->GetDocId())
1836 if (bWidth)
1838 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKWidthHelper(nCurTab))
1839 pPosHelper->invalidateByIndex(nStart);
1841 else
1843 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nCurTab))
1844 pPosHelper->invalidateByIndex(nStart);
1847 pViewShell = SfxViewShell::GetNext(*pViewShell);
1851 // insert cells - undo OK
1853 bool ScViewFunc::InsertCells( InsCellCmd eCmd, bool bRecord, bool bPartOfPaste, size_t nCount )
1855 ScRange aRange;
1856 ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
1857 if (eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED)
1859 ScDocShell* pDocSh = GetViewData().GetDocShell();
1860 const ScMarkData& rMark = GetViewData().GetMarkData();
1861 bool bSuccess = pDocSh->GetDocFunc().InsertCells( aRange, &rMark, eCmd, bRecord, false, bPartOfPaste, nCount );
1862 if (bSuccess)
1864 ResetAutoSpellForContentChange();
1865 bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER);
1866 bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER );
1868 pDocSh->UpdateOle(GetViewData());
1869 CellContentChanged();
1871 if ( bInsertCols || bInsertRows )
1873 OUString aOperation = bInsertRows ?
1874 u"insert-rows"_ustr:
1875 u"insert-columns"_ustr;
1876 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
1879 if (comphelper::LibreOfficeKit::isActive())
1881 if (bInsertCols)
1882 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
1884 if (bInsertRows)
1885 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
1887 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
1888 bInsertCols, bInsertRows, true /* bSizes*/,
1889 true /* bHidden */, true /* bFiltered */,
1890 true /* bGroups */, GetViewData().GetTabNo());
1893 else
1895 ErrorMessage(STR_ERR_INSERT_CELLS);
1898 OUString aStartAddress = aRange.aStart.GetColRowString();
1899 OUString aEndAddress = aRange.aEnd.GetColRowString();
1900 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, u"INSERT_CELLS"_ustr);
1901 return bSuccess;
1903 else
1905 ErrorMessage(STR_NOMULTISELECT);
1906 return false;
1910 // delete cells - undo OK
1912 void ScViewFunc::DeleteCells( DelCellCmd eCmd )
1914 ScRange aRange;
1915 if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE )
1917 ScDocShell* pDocSh = GetViewData().GetDocShell();
1918 const ScMarkData& rMark = GetViewData().GetMarkData();
1920 #if HAVE_FEATURE_MULTIUSER_ENVIRONMENT
1921 // #i94841# [Collaboration] if deleting rows is rejected, the content is sometimes wrong
1922 if ( pDocSh->IsDocShared() && ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols ) )
1924 ScRange aDelRange( aRange.aStart );
1925 SCCOLROW nCount = 0;
1926 if ( eCmd == DelCellCmd::Rows )
1928 nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Row() - aRange.aStart.Row() + 1 );
1930 else
1932 nCount = sal::static_int_cast< SCCOLROW >( aRange.aEnd.Col() - aRange.aStart.Col() + 1 );
1934 while ( nCount > 0 )
1936 pDocSh->GetDocFunc().DeleteCells( aDelRange, &rMark, eCmd, false );
1937 --nCount;
1940 else
1941 #endif
1943 pDocSh->GetDocFunc().DeleteCells( aRange, &rMark, eCmd, false );
1946 ResetAutoSpellForContentChange();
1947 pDocSh->UpdateOle(GetViewData());
1948 CellContentChanged();
1950 if ( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::Cols )
1952 OUString aOperation = ( eCmd == DelCellCmd::Rows) ?
1953 u"delete-rows"_ustr:
1954 u"delete-columns"_ustr;
1955 HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange, aOperation);
1958 // put cursor directly behind deleted range
1959 SCCOL nCurX = GetViewData().GetCurX();
1960 SCROW nCurY = GetViewData().GetCurY();
1961 if ( eCmd==DelCellCmd::CellsLeft || eCmd==DelCellCmd::Cols )
1962 nCurX = aRange.aStart.Col();
1963 else
1964 nCurY = aRange.aStart.Row();
1965 SetCursor( nCurX, nCurY );
1967 if (comphelper::LibreOfficeKit::isActive())
1969 bool bColsDeleted = (eCmd == DelCellCmd::Cols);
1970 bool bRowsDeleted = (eCmd == DelCellCmd::Rows);
1971 if (bColsDeleted)
1972 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), COLUMN_HEADER, GetViewData().GetTabNo());
1974 if (bRowsDeleted)
1975 ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo());
1977 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
1978 bColsDeleted, bRowsDeleted, true /* bSizes*/,
1979 true /* bHidden */, true /* bFiltered */,
1980 true /* bGroups */, GetViewData().GetTabNo());
1983 else
1985 if (eCmd == DelCellCmd::Cols)
1986 DeleteMulti( false );
1987 else if (eCmd == DelCellCmd::Rows)
1988 DeleteMulti( true );
1989 else
1990 ErrorMessage(STR_NOMULTISELECT);
1993 OUString aStartAddress = aRange.aStart.GetColRowString();
1994 OUString aEndAddress = aRange.aEnd.GetColRowString();
1995 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, u"DELETE_CELLS"_ustr);
1997 Unmark();
2000 void ScViewFunc::DeleteMulti( bool bRows )
2002 ScDocShell* pDocSh = GetViewData().GetDocShell();
2003 ScDocShellModificator aModificator( *pDocSh );
2004 SCTAB nTab = GetViewData().GetTabNo();
2005 ScDocument& rDoc = pDocSh->GetDocument();
2006 ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
2007 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
2009 bool bRecord = true;
2010 if (!rDoc.IsUndoEnabled())
2011 bRecord = false;
2013 std::vector<sc::ColRowSpan> aSpans;
2014 if (bRows)
2015 aSpans = aFuncMark.GetMarkedRowSpans();
2016 else
2017 aSpans = aFuncMark.GetMarkedColSpans();
2019 if (aSpans.empty())
2021 SCCOLROW nCurPos = bRows ? GetViewData().GetCurY() : GetViewData().GetCurX();
2022 aSpans.emplace_back(nCurPos, nCurPos);
2025 // test if allowed
2027 TranslateId pErrorId;
2028 bool bNeedRefresh = false;
2029 for (size_t i = 0, n = aSpans.size(); i < n && !pErrorId; ++i)
2031 SCCOLROW nStart = aSpans[i].mnStart;
2032 SCCOLROW nEnd = aSpans[i].mnEnd;
2034 SCCOL nStartCol, nEndCol;
2035 SCROW nStartRow, nEndRow;
2036 if ( bRows )
2038 nStartCol = 0;
2039 nEndCol = rDoc.MaxCol();
2040 nStartRow = static_cast<SCROW>(nStart);
2041 nEndRow = static_cast<SCROW>(nEnd);
2043 else
2045 nStartCol = static_cast<SCCOL>(nStart);
2046 nEndCol = static_cast<SCCOL>(nEnd);
2047 nStartRow = 0;
2048 nEndRow = rDoc.MaxRow();
2051 // cell protection (only needed for first range, as all following cells are moved)
2052 if (i == 0)
2054 // test to the end of the sheet
2055 ScEditableTester aTester( rDoc, nTab, nStartCol, nStartRow, rDoc.MaxCol(), rDoc.MaxRow() );
2056 if (!aTester.IsEditable())
2057 pErrorId = aTester.GetMessageId();
2060 // merged cells
2061 SCCOL nMergeStartX = nStartCol;
2062 SCROW nMergeStartY = nStartRow;
2063 SCCOL nMergeEndX = nEndCol;
2064 SCROW nMergeEndY = nEndRow;
2065 rDoc.ExtendMerge( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
2066 rDoc.ExtendOverlapped( nMergeStartX, nMergeStartY, nMergeEndX, nMergeEndY, nTab );
2068 if ( nMergeStartX != nStartCol || nMergeStartY != nStartRow )
2070 // Disallow deleting parts of a merged cell.
2071 // Deleting the start is allowed (merge is removed), so the end doesn't have to be checked.
2073 pErrorId = STR_MSSG_DELETECELLS_0;
2075 if ( nMergeEndX != nEndCol || nMergeEndY != nEndRow )
2077 // detect if the start of a merged cell is deleted, so the merge flags can be refreshed
2079 bNeedRefresh = true;
2083 if (pErrorId)
2085 ErrorMessage(pErrorId);
2086 return;
2089 // proceed
2091 weld::WaitObject aWait(GetViewData().GetDialogParent()); // important for TrackFormulas in UpdateReference
2093 ResetAutoSpellForContentChange();
2095 ScDocumentUniquePtr pUndoDoc;
2096 std::unique_ptr<ScRefUndoData> pUndoData;
2097 if (bRecord)
2099 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2100 pUndoDoc->InitUndo( rDoc, nTab, nTab, !bRows, bRows ); // row height
2102 for (const sc::ColRowSpan & rSpan : aSpans)
2104 SCCOLROW nStart = rSpan.mnStart;
2105 SCCOLROW nEnd = rSpan.mnEnd;
2106 if (bRows)
2107 rDoc.CopyToDocument( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, InsertDeleteFlags::ALL,false,*pUndoDoc );
2108 else
2109 rDoc.CopyToDocument( static_cast<SCCOL>(nStart),0,nTab,
2110 static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
2111 InsertDeleteFlags::ALL,false,*pUndoDoc );
2114 // all Formulas because of references
2115 SCTAB nTabCount = rDoc.GetTableCount();
2116 pUndoDoc->AddUndoTab( 0, nTabCount-1 );
2117 rDoc.CopyToDocument( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, InsertDeleteFlags::FORMULA,false,*pUndoDoc );
2119 pUndoData.reset(new ScRefUndoData( &rDoc ));
2121 rDoc.BeginDrawUndo();
2124 std::vector<sc::ColRowSpan>::const_reverse_iterator ri = aSpans.rbegin(), riEnd = aSpans.rend();
2125 aFuncMark.SelectOneTable(nTab);
2126 for (; ri != riEnd; ++ri)
2128 SCCOLROW nEnd = ri->mnEnd;
2129 SCCOLROW nStart = ri->mnStart;
2131 if (bRows)
2133 rDoc.DeleteObjectsInArea(0, nStart, rDoc.MaxCol(), nEnd, aFuncMark, true);
2134 rDoc.DeleteRow(0, nTab, rDoc.MaxCol(), nTab, nStart, static_cast<SCSIZE>(nEnd - nStart + 1));
2136 else
2138 rDoc.DeleteObjectsInArea(nStart, 0, nEnd, rDoc.MaxRow(), aFuncMark, true);
2139 rDoc.DeleteCol(0, nTab, rDoc.MaxRow(), nTab, static_cast<SCCOL>(nStart), static_cast<SCSIZE>(nEnd - nStart + 1));
2143 if (bNeedRefresh)
2145 SCCOLROW nFirstStart = aSpans[0].mnStart;
2146 SCCOL nStartCol = bRows ? 0 : static_cast<SCCOL>(nFirstStart);
2147 SCROW nStartRow = bRows ? static_cast<SCROW>(nFirstStart) : 0;
2148 SCCOL nEndCol = rDoc.MaxCol();
2149 SCROW nEndRow = rDoc.MaxRow();
2151 rDoc.RemoveFlagsTab( nStartCol, nStartRow, nEndCol, nEndRow, nTab, ScMF::Hor | ScMF::Ver );
2152 rDoc.ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab, true );
2155 if (bRecord)
2157 pDocSh->GetUndoManager()->AddUndoAction(
2158 std::make_unique<ScUndoDeleteMulti>(
2159 pDocSh, bRows, bNeedRefresh, nTab, std::vector(aSpans), std::move(pUndoDoc), std::move(pUndoData)));
2162 if (!AdjustRowHeight(0, rDoc.MaxRow(), true))
2164 if (bRows)
2166 pDocSh->PostPaint(
2167 0, aSpans[0].mnStart, nTab,
2168 rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Left));
2170 else
2172 pDocSh->PostPaint(
2173 static_cast<SCCOL>(aSpans[0].mnStart), 0, nTab,
2174 rDoc.MaxCol(), rDoc.MaxRow(), nTab, (PaintPartFlags::Grid | PaintPartFlags::Top));
2178 aModificator.SetDocumentModified();
2180 CellContentChanged();
2182 // put cursor directly behind the first deleted range
2183 SCCOL nCurX = GetViewData().GetCurX();
2184 SCROW nCurY = GetViewData().GetCurY();
2185 if ( bRows )
2186 nCurY = aSpans[0].mnStart;
2187 else
2188 nCurX = static_cast<SCCOL>(aSpans[0].mnStart);
2189 SetCursor( nCurX, nCurY );
2191 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) );
2194 // delete contents
2196 void ScViewFunc::DeleteContents( InsertDeleteFlags nFlags )
2198 ScViewData& rViewData = GetViewData();
2199 rViewData.SetPasteMode( ScPasteFlags::NONE );
2200 rViewData.GetViewShell()->UpdateCopySourceOverlay();
2202 // not editable because of matrix only? attribute OK nonetheless
2203 bool bOnlyNotBecauseOfMatrix;
2204 bool bEditable = SelectionEditable( &bOnlyNotBecauseOfMatrix );
2205 if ( !bEditable )
2207 if ( !(bOnlyNotBecauseOfMatrix &&
2208 ((nFlags & (InsertDeleteFlags::ATTRIB | InsertDeleteFlags::EDITATTR)) == nFlags)) )
2210 ErrorMessage(bOnlyNotBecauseOfMatrix ? STR_MATRIXFRAGMENTERR : STR_PROTECTIONERR);
2211 return;
2215 ScRange aMarkRange;
2216 bool bSimple = false;
2218 ScDocument& rDoc = GetViewData().GetDocument();
2219 ScDocShell* pDocSh = GetViewData().GetDocShell();
2220 ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered
2221 ScViewUtil::UnmarkFiltered( aFuncMark, rDoc );
2223 bool bRecord =true;
2224 if (!rDoc.IsUndoEnabled())
2225 bRecord = false;
2227 if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() )
2229 aMarkRange.aStart.SetCol(GetViewData().GetCurX());
2230 aMarkRange.aStart.SetRow(GetViewData().GetCurY());
2231 aMarkRange.aStart.SetTab(GetViewData().GetTabNo());
2232 aMarkRange.aEnd = aMarkRange.aStart;
2233 if ( rDoc.HasAttrib( aMarkRange, HasAttrFlags::Merged ) )
2235 aFuncMark.SetMarkArea( aMarkRange );
2237 else
2238 bSimple = true;
2241 HideAllCursors(); // for if summary is cancelled
2243 ScDocFunc& rDocFunc = pDocSh->GetDocFunc();
2245 // Can we really be sure that we can pass the bApi parameter as false to DeleteCell() and
2246 // DeleteContents() here? (Meaning that this is interactive use.) Is this never invoked from
2247 // scripting and whatnot?
2248 if (bSimple)
2249 rDocFunc.DeleteCell(aMarkRange.aStart, aFuncMark, nFlags, bRecord, /*bApi=*/ false);
2250 else
2251 rDocFunc.DeleteContents(aFuncMark, nFlags, bRecord, /*bApi=*/ false);
2253 pDocSh->UpdateOle(GetViewData());
2255 if (ScModelObj* pModelObj = pDocSh->GetModel())
2257 ScRangeList aChangeRanges;
2258 if ( bSimple )
2260 aChangeRanges.push_back( aMarkRange );
2262 else
2264 aFuncMark.FillRangeListWithMarks( &aChangeRanges, false );
2267 if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
2268 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, u"delete-content"_ustr);
2269 else if (pModelObj)
2270 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, u"data-area-invalidate"_ustr);
2273 CellContentChanged();
2274 ShowAllCursors();
2276 if ( nFlags & InsertDeleteFlags::ATTRIB )
2278 if ( nFlags & InsertDeleteFlags::CONTENTS )
2279 bFormatValid = false;
2280 else
2281 StartFormatArea(); // delete attribute is also attribute-change
2283 OUString aStartAddress = aMarkRange.aStart.GetColRowString();
2284 OUString aEndAddress = aMarkRange.aEnd.GetColRowString();
2285 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, u"DELETE"_ustr);
2288 // column width/row height (via header) - undo OK
2290 void ScViewFunc::SetWidthOrHeight(
2291 bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, ScSizeMode eMode,
2292 sal_uInt16 nSizeTwips, bool bRecord, const ScMarkData* pMarkData )
2294 if (rRanges.empty())
2295 return;
2297 // Use view's mark if none specified, but do not modify the original data,
2298 // i.e. no MarkToMulti() on that.
2299 ScMarkData aMarkData( pMarkData ? *pMarkData : GetViewData().GetMarkData());
2301 ScDocShell* pDocSh = GetViewData().GetDocShell();
2302 ScDocument& rDoc = pDocSh->GetDocument();
2303 SCCOL nCurX = GetViewData().GetCurX();
2304 SCROW nCurY = GetViewData().GetCurY();
2305 SCTAB nFirstTab = aMarkData.GetFirstSelected();
2306 SCTAB nCurTab = GetViewData().GetTabNo();
2307 if (bRecord && !rDoc.IsUndoEnabled())
2308 bRecord = false;
2310 ScDocShellModificator aModificator( *pDocSh );
2312 bool bAllowed = true;
2313 for (const SCTAB& nTab : aMarkData)
2315 bAllowed = std::all_of(rRanges.begin(), rRanges.end(),
2316 [&bWidth, &rDoc, &nTab](const sc::ColRowSpan& rRange) {
2317 bool bOnlyMatrix;
2318 bool bIsBlockEditable;
2319 if (bWidth)
2320 bIsBlockEditable = rDoc.IsBlockEditable(nTab, rRange.mnStart, 0, rRange.mnEnd, rDoc.MaxRow(), &bOnlyMatrix);
2321 else
2322 bIsBlockEditable = rDoc.IsBlockEditable(nTab, 0, rRange.mnStart, rDoc.MaxCol(), rRange.mnEnd, &bOnlyMatrix);
2323 return bIsBlockEditable || bOnlyMatrix;
2325 if (!bAllowed)
2326 break;
2329 // Allow users to resize cols/rows in readonly docs despite the r/o state.
2330 // It is frustrating to be unable to see content in mis-sized cells.
2331 if( !bAllowed && !pDocSh->IsReadOnly() )
2333 ErrorMessage(STR_PROTECTIONERR);
2334 return;
2337 SCCOLROW nStart = rRanges.front().mnStart;
2338 SCCOLROW nEnd = rRanges.back().mnEnd;
2340 OnLOKSetWidthOrHeight(nStart, bWidth);
2342 bool bFormula = false;
2343 if ( eMode == SC_SIZE_OPTIMAL )
2345 const ScViewOptions& rOpts = GetViewData().GetOptions();
2346 bFormula = rOpts.GetOption( VOPT_FORMULAS );
2349 ScDocumentUniquePtr pUndoDoc;
2350 std::unique_ptr<ScOutlineTable> pUndoTab;
2351 std::vector<sc::ColRowSpan> aUndoRanges;
2353 if ( bRecord )
2355 rDoc.BeginDrawUndo(); // Drawing Updates
2357 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
2358 for (const SCTAB& nTab : aMarkData)
2360 if (bWidth)
2362 if ( nTab == nFirstTab )
2363 pUndoDoc->InitUndo( rDoc, nTab, nTab, true );
2364 else
2365 pUndoDoc->AddUndoTab( nTab, nTab, true );
2366 rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab,
2367 static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE,
2368 false, *pUndoDoc );
2370 else
2372 if ( nTab == nFirstTab )
2373 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
2374 else
2375 pUndoDoc->AddUndoTab( nTab, nTab, false, true );
2376 rDoc.CopyToDocument( 0, nStart, nTab, rDoc.MaxCol(), nEnd, nTab, InsertDeleteFlags::NONE, false, *pUndoDoc );
2380 aUndoRanges = rRanges;
2382 //! outlines from all tab?
2383 ScOutlineTable* pTable = rDoc.GetOutlineTable( nCurTab );
2384 if (pTable)
2385 pUndoTab.reset(new ScOutlineTable( *pTable ));
2388 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2389 aMarkData.MarkToMulti();
2391 bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT;
2392 bool bOutline = false;
2394 for (const SCTAB& nTab : aMarkData)
2396 for (const sc::ColRowSpan & rRange : rRanges)
2398 SCCOLROW nStartNo = rRange.mnStart;
2399 SCCOLROW nEndNo = rRange.mnEnd;
2401 if ( !bWidth ) // height always blockwise
2403 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2405 bool bAll = ( eMode==SC_SIZE_OPTIMAL );
2406 bool bFiltered = false;
2407 if (!bAll)
2409 // delete CRFlags::ManualSize for all in range,
2410 // then SetOptimalHeight with bShrink = FALSE
2411 for (SCROW nRow = nStartNo; nRow <= nEndNo; ++nRow)
2413 SCROW nLastRow = nRow;
2414 if (rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow))
2416 nRow = nLastRow;
2417 continue;
2420 CRFlags nOld = rDoc.GetRowFlags(nRow, nTab);
2421 if (nOld & CRFlags::ManualSize)
2422 rDoc.SetRowFlags(nRow, nTab, nOld & ~CRFlags::ManualSize);
2425 else
2427 SCROW nLastRow = nStartNo;
2428 if (rDoc.RowFiltered(nStartNo, nTab, nullptr, &nLastRow)
2429 || nLastRow < nEndNo)
2430 bFiltered = true;
2434 double nPPTX = GetViewData().GetPPTX();
2435 double nPPTY = GetViewData().GetPPTY();
2436 Fraction aZoomX = GetViewData().GetZoomX();
2437 Fraction aZoomY = GetViewData().GetZoomY();
2439 ScSizeDeviceProvider aProv(pDocSh);
2440 if (aProv.IsPrinter())
2442 nPPTX = aProv.GetPPTX();
2443 nPPTY = aProv.GetPPTY();
2444 aZoomX = aZoomY = Fraction( 1, 1 );
2447 sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice());
2448 aCxt.setForceAutoSize(bAll);
2449 aCxt.setExtraHeight(nSizeTwips);
2450 rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, true);
2452 if (bFiltered)
2453 ShowFilteredRows(rDoc, nTab, nStartNo, nEndNo, bShow);
2455 // Manual-Flag already (re)set in SetOptimalHeight in case of bAll=sal_True
2456 // (set for Extra-Height, else reset).
2458 else if ( eMode==SC_SIZE_DIRECT )
2460 if (nSizeTwips)
2462 rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips );
2463 rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true ); // height was set manually
2466 // tdf#36383 - Skip consecutive rows hidden by AutoFilter
2467 ShowFilteredRows(rDoc, nTab, nStartNo, nEndNo, nSizeTwips != 0);
2469 if (!bShow && nStartNo <= nCurY && nCurY <= nEndNo && nTab == nCurTab)
2471 nCurY = -1;
2474 else if ( eMode==SC_SIZE_SHOW )
2476 rDoc.ShowRows( nStartNo, nEndNo, nTab, true );
2479 else // column width
2481 for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++)
2483 const bool bIsColHidden = rDoc.ColHidden(nCol, nTab);
2484 if ( eMode != SC_SIZE_VISOPT || !bIsColHidden )
2486 sal_uInt16 nThisSize = nSizeTwips;
2488 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT )
2489 nThisSize = nSizeTwips + GetOptimalColWidth( nCol, nTab, bFormula );
2490 if ( nThisSize )
2491 rDoc.SetColWidth( nCol, nTab, nThisSize );
2493 // tdf#131073 - Don't show hidden cols after setting optimal col width
2494 if (eMode == SC_SIZE_OPTIMAL)
2495 rDoc.ShowCol(nCol, nTab, !bIsColHidden);
2496 else
2497 rDoc.ShowCol( nCol, nTab, bShow );
2499 if (!bShow && nCol == nCurX && nTab == nCurTab)
2501 nCurX = -1;
2507 // adjust outline
2508 if (bWidth)
2510 if ( rDoc.UpdateOutlineCol( static_cast<SCCOL>(nStartNo),
2511 static_cast<SCCOL>(nEndNo), nTab, bShow ) )
2512 bOutline = true;
2514 else
2516 if ( rDoc.UpdateOutlineRow( nStartNo, nEndNo, nTab, bShow ) )
2517 bOutline = true;
2520 rDoc.SetDrawPageSize(nTab);
2523 if (!bOutline)
2524 pUndoTab.reset();
2526 if (bRecord)
2528 pDocSh->GetUndoManager()->AddUndoAction(
2529 std::make_unique<ScUndoWidthOrHeight>(
2530 pDocSh, aMarkData, nStart, nCurTab, nEnd, nCurTab,
2531 std::move(pUndoDoc), std::move(aUndoRanges), std::move(pUndoTab), eMode, nSizeTwips, bWidth));
2534 if (nCurX < 0)
2536 MoveCursorRel( 1, 0, SC_FOLLOW_LINE, false );
2539 if (nCurY < 0)
2541 MoveCursorRel( 0, 1, SC_FOLLOW_LINE, false );
2544 // fdo#36247 Ensure that the drawing layer's map mode scaling factors match
2545 // the new heights and widths.
2546 GetViewData().GetView()->RefreshZoom();
2548 for (const SCTAB& nTab : aMarkData)
2549 rDoc.UpdatePageBreaks( nTab );
2551 bool bAffectsVisibility = (eMode != SC_SIZE_ORIGINAL && eMode != SC_SIZE_VISOPT);
2552 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(GetViewData().GetViewShell(),
2553 bWidth /* bColumns */, !bWidth /* bRows */,
2554 true /* bSizes*/, bAffectsVisibility /* bHidden */, bAffectsVisibility /* bFiltered */,
2555 false /* bGroups */, nCurTab);
2556 GetViewData().GetView()->UpdateScrollBars(bWidth ? COLUMN_HEADER : ROW_HEADER);
2559 for (const SCTAB& nTab : aMarkData)
2561 if (bWidth)
2563 if (rDoc.HasAttrib( static_cast<SCCOL>(nStart),0,nTab,
2564 static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab,
2565 HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2566 nStart = 0;
2567 if (nStart > 0) // go upwards because of Lines and cursor
2568 --nStart;
2569 pDocSh->PostPaint( static_cast<SCCOL>(nStart), 0, nTab,
2570 rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Top );
2572 else
2574 if (rDoc.HasAttrib( 0,nStart,nTab, rDoc.MaxCol(), nEnd,nTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
2575 nStart = 0;
2576 if (nStart != 0)
2577 --nStart;
2578 pDocSh->PostPaint( 0, nStart, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Left );
2582 pDocSh->UpdateOle(GetViewData());
2583 if( !pDocSh->IsReadOnly() )
2584 aModificator.SetDocumentModified();
2587 if ( !bWidth )
2588 return;
2590 ScModelObj* pModelObj = pDocSh->GetModel();
2592 if (!HelperNotifyChanges::getMustPropagateChangesModel(pModelObj))
2593 return;
2595 ScRangeList aChangeRanges;
2596 for (const SCTAB& nTab : aMarkData)
2598 for (const sc::ColRowSpan & rRange : rRanges)
2600 SCCOL nStartCol = rRange.mnStart;
2601 SCCOL nEndCol = rRange.mnEnd;
2602 for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
2604 aChangeRanges.push_back( ScRange( nCol, 0, nTab ) );
2608 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, u"column-resize"_ustr);
2611 // column width/row height (via marked range)
2613 void ScViewFunc::SetMarkedWidthOrHeight( bool bWidth, ScSizeMode eMode, sal_uInt16 nSizeTwips )
2615 ScMarkData& rMark = GetViewData().GetMarkData();
2617 rMark.MarkToMulti();
2618 if (!rMark.IsMultiMarked())
2620 SCCOL nCol = GetViewData().GetCurX();
2621 SCROW nRow = GetViewData().GetCurY();
2622 SCTAB nTab = GetViewData().GetTabNo();
2623 const ScRange aMarkRange( nCol, nRow, nTab);
2624 DoneBlockMode();
2625 InitOwnBlockMode( aMarkRange );
2626 rMark.SetMultiMarkArea( aMarkRange );
2627 MarkDataChanged();
2630 std::vector<sc::ColRowSpan> aRanges =
2631 bWidth ? rMark.GetMarkedColSpans() : rMark.GetMarkedRowSpans();
2633 SetWidthOrHeight(bWidth, aRanges, eMode, nSizeTwips);
2635 rMark.MarkToSimple();
2638 void ScViewFunc::ModifyCellSize( ScDirection eDir, bool bOptimal )
2640 ScModule* pScMod = ScModule::get();
2641 bool bAnyEdit = pScMod->IsInputMode();
2642 SCCOL nCol = GetViewData().GetCurX();
2643 SCROW nRow = GetViewData().GetCurY();
2644 SCTAB nTab = GetViewData().GetTabNo();
2645 ScDocShell* pDocSh = GetViewData().GetDocShell();
2646 ScDocument& rDoc = pDocSh->GetDocument();
2648 bool bAllowed, bOnlyMatrix;
2649 if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
2650 bAllowed = rDoc.IsBlockEditable( nTab, nCol,0, nCol,rDoc.MaxRow(), &bOnlyMatrix );
2651 else
2652 bAllowed = rDoc.IsBlockEditable( nTab, 0,nRow, rDoc.MaxCol(), nRow, &bOnlyMatrix );
2653 if ( !bAllowed && !bOnlyMatrix )
2655 ErrorMessage(STR_PROTECTIONERR);
2656 return;
2659 HideAllCursors();
2661 //! step size adjustable
2662 // step size is also minimum
2663 constexpr sal_uInt16 nStepX = STD_COL_WIDTH / 5;
2664 const sal_uInt16 nStepY = rDoc.GetSheetOptimalMinRowHeight(nTab);
2666 sal_uInt16 nWidth = rDoc.GetColWidth( nCol, nTab );
2667 sal_uInt16 nHeight = rDoc.GetRowHeight( nRow, nTab );
2668 std::vector<sc::ColRowSpan> aRange(1, sc::ColRowSpan(0,0));
2669 if ( eDir == DIR_LEFT || eDir == DIR_RIGHT )
2671 if (bOptimal) // width of this single cell
2673 if ( bAnyEdit )
2675 // when editing the actual entered width
2676 ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
2677 if (pHdl)
2679 tools::Long nEdit = pHdl->GetTextSize().Width(); // in 0.01 mm
2681 const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
2682 const SvxMarginItem& rMItem = pPattern->GetItem(ATTR_MARGIN);
2683 sal_uInt16 nMargin = rMItem.GetLeftMargin() + rMItem.GetRightMargin();
2684 if ( pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Left )
2685 nMargin = sal::static_int_cast<sal_uInt16>(
2686 nMargin + pPattern->GetItem(ATTR_INDENT).GetValue() );
2688 nWidth = std::round(o3tl::convert(nEdit * pDocSh->GetOutputFactor(),
2689 o3tl::Length::mm100, o3tl::Length::twip))
2690 + nMargin + STD_EXTRA_WIDTH;
2693 else
2695 double nPPTX = GetViewData().GetPPTX();
2696 double nPPTY = GetViewData().GetPPTY();
2697 Fraction aZoomX = GetViewData().GetZoomX();
2698 Fraction aZoomY = GetViewData().GetZoomY();
2700 ScSizeDeviceProvider aProv(pDocSh);
2701 if (aProv.IsPrinter())
2703 nPPTX = aProv.GetPPTX();
2704 nPPTY = aProv.GetPPTY();
2705 aZoomX = aZoomY = Fraction( 1, 1 );
2708 tools::Long nPixel = rDoc.GetNeededSize( nCol, nRow, nTab, aProv.GetDevice(),
2709 nPPTX, nPPTY, aZoomX, aZoomY, true );
2710 sal_uInt16 nTwips = static_cast<sal_uInt16>( nPixel / nPPTX );
2711 if (nTwips != 0)
2712 nWidth = nTwips + STD_EXTRA_WIDTH;
2713 else
2714 nWidth = STD_COL_WIDTH;
2717 else // increment / decrement
2719 if ( eDir == DIR_RIGHT )
2720 nWidth = sal::static_int_cast<sal_uInt16>( nWidth + nStepX );
2721 else if ( nWidth > nStepX )
2722 nWidth = sal::static_int_cast<sal_uInt16>( nWidth - nStepX );
2723 if ( nWidth < nStepX ) nWidth = nStepX;
2724 if ( nWidth > MAX_COL_WIDTH ) nWidth = MAX_COL_WIDTH;
2726 aRange[0].mnStart = nCol;
2727 aRange[0].mnEnd = nCol;
2728 SetWidthOrHeight(true, aRange, SC_SIZE_DIRECT, nWidth);
2730 // adjust height of this row if width demands/allows this
2732 if (!bAnyEdit)
2734 const ScPatternAttr* pPattern = rDoc.GetPattern( nCol, nRow, nTab );
2735 bool bNeedHeight =
2736 pPattern->GetItem( ATTR_LINEBREAK ).GetValue() ||
2737 pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block;
2738 if (bNeedHeight)
2739 AdjustRowHeight( nRow, nRow, true );
2742 else
2744 ScSizeMode eMode;
2745 if (bOptimal)
2747 eMode = SC_SIZE_OPTIMAL;
2748 nHeight = 0;
2750 else
2752 eMode = SC_SIZE_DIRECT;
2753 if ( eDir == DIR_BOTTOM )
2754 nHeight = sal::static_int_cast<sal_uInt16>( nHeight + nStepY );
2755 else if ( nHeight > nStepY )
2756 nHeight = sal::static_int_cast<sal_uInt16>( nHeight - nStepY );
2757 if ( nHeight < nStepY ) nHeight = nStepY;
2758 if ( nHeight > MAX_ROW_HEIGHT ) nHeight = MAX_ROW_HEIGHT;
2760 aRange[0].mnStart = nRow;
2761 aRange[0].mnEnd = nRow;
2762 SetWidthOrHeight(false, aRange, eMode, nHeight);
2765 if ( bAnyEdit )
2767 UpdateEditView();
2768 if ( rDoc.HasAttrib( nCol, nRow, nTab, nCol, nRow, nTab, HasAttrFlags::NeedHeight ) )
2770 ScInputHandler* pHdl = pScMod->GetInputHdl( GetViewData().GetViewShell() );
2771 if (pHdl)
2772 pHdl->SetModified(); // so that the height is adjusted with Enter
2776 ShowAllCursors();
2779 void ScViewFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect )
2781 if (nTab == TABLEID_DOC)
2782 return;
2784 ScMarkData& rMark = GetViewData().GetMarkData();
2785 ScDocShell* pDocSh = GetViewData().GetDocShell();
2786 ScDocument& rDoc = pDocSh->GetDocument();
2787 ScDocFunc &rFunc = pDocSh->GetDocFunc();
2788 bool bUndo(rDoc.IsUndoEnabled());
2790 // modifying several tabs is handled here
2792 if (bUndo)
2794 OUString aUndo = ScResId( STR_UNDO_PROTECT_TAB );
2795 pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2798 for (const auto& rTab : rMark)
2800 rFunc.ProtectSheet(rTab, rProtect);
2803 if (bUndo)
2804 pDocSh->GetUndoManager()->LeaveListAction();
2806 UpdateLayerLocks(); //! broadcast to all views
2809 void ScViewFunc::ProtectDoc( const OUString& rPassword )
2811 ScDocShell* pDocSh = GetViewData().GetDocShell();
2812 ScDocFunc &rFunc = pDocSh->GetDocFunc();
2814 rFunc.Protect( TABLEID_DOC, rPassword );
2816 UpdateLayerLocks(); //! broadcast to all views
2819 bool ScViewFunc::Unprotect( SCTAB nTab, const OUString& rPassword )
2821 ScMarkData& rMark = GetViewData().GetMarkData();
2822 ScDocShell* pDocSh = GetViewData().GetDocShell();
2823 ScDocument& rDoc = pDocSh->GetDocument();
2824 ScDocFunc &rFunc = pDocSh->GetDocFunc();
2825 bool bChanged = false;
2826 bool bUndo (rDoc.IsUndoEnabled());
2828 if ( nTab == TABLEID_DOC || rMark.GetSelectCount() <= 1 )
2830 bChanged = rFunc.Unprotect( nTab, rPassword, false );
2831 if (bChanged && nTab != TABLEID_DOC)
2832 SetTabProtectionSymbol(nTab, false);
2834 else
2836 // modifying several tabs is handled here
2838 if (bUndo)
2840 OUString aUndo = ScResId( STR_UNDO_UNPROTECT_TAB );
2841 pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() );
2844 for (const auto& rTab : rMark)
2846 if ( rFunc.Unprotect( rTab, rPassword, false ) )
2848 bChanged = true;
2849 SetTabProtectionSymbol( rTab, false);
2853 if (bUndo)
2854 pDocSh->GetUndoManager()->LeaveListAction();
2857 if (bChanged)
2858 UpdateLayerLocks(); //! broadcast to all views
2860 return bChanged;
2863 void ScViewFunc::SetNoteText( const ScAddress& rPos, const OUString& rNoteText )
2865 GetViewData().GetDocShell()->GetDocFunc().SetNoteText( rPos, rNoteText, false );
2868 void ScViewFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate )
2870 GetViewData().GetDocShell()->GetDocFunc().ReplaceNote( rPos, rNoteText, pAuthor, pDate, false );
2873 void ScViewFunc::SetNumberFormat( SvNumFormatType nFormatType, sal_uLong nAdd )
2875 // not editable because of matrix only? attribute OK nonetheless
2876 bool bOnlyNotBecauseOfMatrix;
2877 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2879 ErrorMessage(STR_PROTECTIONERR);
2880 return;
2883 sal_uInt32 nNumberFormat = 0;
2884 ScViewData& rViewData = GetViewData();
2885 ScDocument& rDoc = rViewData.GetDocument();
2886 SvNumberFormatter* pNumberFormatter = rDoc.GetFormatTable();
2887 LanguageType eLanguage = ScGlobal::eLnge;
2888 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper());
2890 // always take language from cursor position, even if there is a selection
2892 sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat( rViewData.GetCurX(),
2893 rViewData.GetCurY(),
2894 rViewData.GetTabNo());
2895 const SvNumberformat* pEntry = pNumberFormatter->GetEntry( nCurrentNumberFormat );
2896 if (pEntry)
2897 eLanguage = pEntry->GetLanguage(); // else keep ScGlobal::eLnge
2899 nNumberFormat = pNumberFormatter->GetStandardFormat( nFormatType, eLanguage ) + nAdd;
2901 SfxItemSet& rSet = aNewAttrs.GetItemSet();
2902 rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
2903 // ATTR_LANGUAGE_FORMAT not
2904 ApplySelectionPattern( aNewAttrs );
2907 void ScViewFunc::SetNumFmtByStr( const OUString& rCode )
2909 // not editable because of matrix only? attribute OK nonetheless
2910 bool bOnlyNotBecauseOfMatrix;
2911 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2913 ErrorMessage(STR_PROTECTIONERR);
2914 return;
2917 ScViewData& rViewData = GetViewData();
2918 ScDocument& rDoc = rViewData.GetDocument();
2919 SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
2921 // language always from cursor position
2923 sal_uInt32 nCurrentNumberFormat = rDoc.GetNumberFormat( rViewData.GetCurX(), rViewData.GetCurY(),
2924 rViewData.GetTabNo());
2925 const SvNumberformat* pEntry = pFormatter->GetEntry( nCurrentNumberFormat );
2926 LanguageType eLanguage = pEntry ? pEntry->GetLanguage() : ScGlobal::eLnge;
2928 // determine index for String
2930 bool bOk = true;
2931 sal_uInt32 nNumberFormat = pFormatter->GetEntryKey( rCode, eLanguage );
2932 if ( nNumberFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
2934 // enter new
2936 OUString aFormat = rCode; // will be changed
2937 sal_Int32 nErrPos = 0;
2938 SvNumFormatType nType = SvNumFormatType::ALL; //! ???
2939 bOk = pFormatter->PutEntry( aFormat, nErrPos, nType, nNumberFormat, eLanguage );
2942 if ( bOk ) // valid format?
2944 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper());
2945 SfxItemSet& rSet = aNewAttrs.GetItemSet();
2946 rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumberFormat ) );
2947 rSet.Put( SvxLanguageItem( eLanguage, ATTR_LANGUAGE_FORMAT ) );
2948 ApplySelectionPattern( aNewAttrs );
2951 //! else return error / issue warning ???
2954 void ScViewFunc::ChangeNumFmtDecimals( bool bIncrement )
2956 // not editable because of matrix only? attribute OK nonetheless
2957 bool bOnlyNotBecauseOfMatrix;
2958 if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix )
2960 ErrorMessage(STR_PROTECTIONERR);
2961 return;
2964 ScDocument& rDoc = GetViewData().GetDocument();
2965 SvNumberFormatter* pFormatter = rDoc.GetFormatTable();
2967 SCCOL nCol = GetViewData().GetCurX();
2968 SCROW nRow = GetViewData().GetCurY();
2969 SCTAB nTab = GetViewData().GetTabNo();
2971 sal_uInt32 nOldFormat = rDoc.GetNumberFormat( nCol, nRow, nTab );
2972 const SvNumberformat* pOldEntry = pFormatter->GetEntry( nOldFormat );
2973 if (!pOldEntry)
2975 OSL_FAIL("numberformat not found !!!");
2976 return;
2979 // what have we got here?
2981 sal_uInt32 nNewFormat = nOldFormat;
2982 bool bError = false;
2984 LanguageType eLanguage = pOldEntry->GetLanguage();
2985 bool bThousand, bNegRed;
2986 sal_uInt16 nPrecision, nLeading;
2987 pOldEntry->GetFormatSpecialInfo( bThousand, bNegRed, nPrecision, nLeading );
2989 SvNumFormatType nOldType = pOldEntry->GetType();
2990 if ( SvNumFormatType::ALL == ( nOldType & (
2991 SvNumFormatType::NUMBER | SvNumFormatType::CURRENCY | SvNumFormatType::PERCENT | SvNumFormatType::SCIENTIFIC | SvNumFormatType::TIME ) ) )
2993 // date, fraction, logical, text can not be changed
2994 bError = true;
2997 //! SvNumberformat has a Member bStandard, but doesn't disclose it
2998 bool bWasStandard = ( nOldFormat == pFormatter->GetStandardIndex( eLanguage ) );
2999 OUString sExponentialStandardFormat = u""_ustr;
3000 if (bWasStandard)
3002 // with "Standard" the decimal places depend on cell content
3003 // 0 if empty or text -> no decimal places
3004 double nVal = rDoc.GetValue( ScAddress( nCol, nRow, nTab ) );
3006 // the ways of the Numberformatters are unfathomable, so try:
3007 OUString aOut;
3008 const Color* pCol;
3009 pOldEntry->GetOutputString( nVal, aOut, &pCol, pFormatter->GetNatNum(), pFormatter->GetROLanguageData() );
3011 nPrecision = 0;
3012 // 'E' for exponential is fixed in Numberformatter
3013 sal_Int32 nIndexE = aOut.indexOf('E');
3014 if ( nIndexE >= 0 )
3016 sExponentialStandardFormat = aOut.copy( nIndexE ).replace( '-', '+' );
3017 for ( sal_Int32 i=1 ; i<sExponentialStandardFormat.getLength() ; i++ )
3019 if ( sExponentialStandardFormat[i] >= '1' && sExponentialStandardFormat[i] <= '9' )
3020 sExponentialStandardFormat = sExponentialStandardFormat.replaceAt( i, 1, u"0" );
3022 aOut = aOut.copy( 0, nIndexE ); // remove exponential part
3024 OUString aDecSep( pFormatter->GetFormatDecimalSep( nOldFormat ) );
3025 sal_Int32 nPos = aOut.indexOf( aDecSep );
3026 if ( nPos >= 0 )
3027 nPrecision = aOut.getLength() - nPos - aDecSep.getLength();
3028 // else keep 0
3030 else
3032 if ( (nOldType & SvNumFormatType::SCIENTIFIC) && !bThousand &&
3033 (pOldEntry->GetFormatIntegerDigits()%3 == 0) && pOldEntry->GetFormatIntegerDigits() > 0 )
3034 bThousand = true;
3037 if (!bError)
3039 if (bIncrement)
3041 if (nPrecision<20)
3042 ++nPrecision; // increment
3043 else
3044 bError = true; // 20 is maximum
3046 else
3048 if (nPrecision)
3049 --nPrecision; // decrement
3050 else
3051 bError = true; // 0 is minimum
3055 if (!bError)
3057 OUString aNewPicture = pFormatter->GenerateFormat(nOldFormat, eLanguage,
3058 bThousand, bNegRed,
3059 nPrecision, nLeading)
3060 + sExponentialStandardFormat;
3062 nNewFormat = pFormatter->GetEntryKey( aNewPicture, eLanguage );
3063 if ( nNewFormat == NUMBERFORMAT_ENTRY_NOT_FOUND )
3065 sal_Int32 nErrPos = 0;
3066 SvNumFormatType nNewType = SvNumFormatType::ALL;
3067 bool bOk = pFormatter->PutEntry( aNewPicture, nErrPos,
3068 nNewType, nNewFormat, eLanguage );
3069 OSL_ENSURE( bOk, "incorrect numberformat generated" );
3070 if (!bOk)
3071 bError = true;
3075 if (!bError)
3077 ScPatternAttr aNewAttrs(rDoc.getCellAttributeHelper());
3078 SfxItemSet& rSet = aNewAttrs.GetItemSet();
3079 rSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNewFormat ) );
3080 // ATTR_LANGUAGE_FORMAT not
3081 ApplySelectionPattern( aNewAttrs );
3085 void ScViewFunc::ChangeIndent( bool bIncrement )
3087 ScViewData& rViewData = GetViewData();
3088 ScDocShell* pDocSh = rViewData.GetDocShell();
3089 ScMarkData& rMark = rViewData.GetMarkData();
3091 ScMarkData aWorkMark = rMark;
3092 ScViewUtil::UnmarkFiltered( aWorkMark, pDocSh->GetDocument() );
3093 aWorkMark.MarkToMulti();
3094 if (!aWorkMark.IsMultiMarked())
3096 SCCOL nCol = rViewData.GetCurX();
3097 SCROW nRow = rViewData.GetCurY();
3098 SCTAB nTab = rViewData.GetTabNo();
3099 aWorkMark.SetMultiMarkArea( ScRange(nCol,nRow,nTab) );
3102 bool bSuccess = pDocSh->GetDocFunc().ChangeIndent( aWorkMark, bIncrement, false );
3103 if (bSuccess)
3105 pDocSh->UpdateOle(rViewData);
3106 StartFormatArea();
3108 // stuff for sidebar panels
3109 SfxBindings& rBindings = GetViewData().GetBindings();
3110 rBindings.Invalidate( SID_H_ALIGNCELL );
3111 rBindings.Invalidate( SID_ATTR_ALIGN_INDENT );
3115 bool ScViewFunc::InsertName( const OUString& rName, const OUString& rSymbol,
3116 const OUString& rType )
3118 // Type = P,R,C,F (and combinations)
3119 //! undo...
3121 bool bOk = false;
3122 ScDocShell* pDocSh = GetViewData().GetDocShell();
3123 ScDocument& rDoc = pDocSh->GetDocument();
3124 SCTAB nTab = GetViewData().GetTabNo();
3125 ScRangeName* pList = rDoc.GetRangeName();
3127 ScRangeData::Type nType = ScRangeData::Type::Name;
3128 auto pNewEntry = std::make_unique<ScRangeData>(
3129 rDoc, rName, rSymbol, ScAddress( GetViewData().GetCurX(),
3130 GetViewData().GetCurY(), nTab), nType );
3131 OUString aUpType = rType.toAsciiUpperCase();
3132 if ( aUpType.indexOf( 'P' ) != -1 )
3133 nType |= ScRangeData::Type::PrintArea;
3134 if ( aUpType.indexOf( 'R' ) != -1 )
3135 nType |= ScRangeData::Type::RowHeader;
3136 if ( aUpType.indexOf( 'C' ) != -1 )
3137 nType |= ScRangeData::Type::ColHeader;
3138 if ( aUpType.indexOf( 'F' ) != -1 )
3139 nType |= ScRangeData::Type::Criteria;
3140 pNewEntry->AddType(nType);
3142 if ( pNewEntry->GetErrCode() == FormulaError::NONE ) // text valid?
3144 ScDocShellModificator aModificator( *pDocSh );
3146 rDoc.PreprocessRangeNameUpdate();
3148 // input available yet? Then remove beforehand (=change)
3149 ScRangeData* pData = pList->findByUpperName(ScGlobal::getCharClass().uppercase(rName));
3150 if (pData)
3151 { // take old Index
3152 pNewEntry->SetIndex(pData->GetIndex());
3153 pList->erase(*pData);
3156 // don't delete, insert took ownership, even on failure!
3157 if ( pList->insert( pNewEntry.release() ) )
3158 bOk = true;
3160 rDoc.CompileHybridFormula();
3162 aModificator.SetDocumentModified();
3163 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) );
3166 return bOk;
3169 void ScViewFunc::CreateNames( CreateNameFlags nFlags )
3171 bool bDone = false;
3172 ScRange aRange;
3173 if ( GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE )
3174 bDone = GetViewData().GetDocShell()->GetDocFunc().CreateNames( aRange, nFlags, false );
3176 if (!bDone)
3177 ErrorMessage(STR_CREATENAME_MARKERR);
3180 CreateNameFlags ScViewFunc::GetCreateNameFlags()
3182 CreateNameFlags nFlags = CreateNameFlags::NONE;
3184 SCCOL nStartCol, nEndCol;
3185 SCROW nStartRow, nEndRow;
3186 SCTAB nDummy;
3187 if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nDummy,nEndCol,nEndRow,nDummy) == SC_MARK_SIMPLE)
3189 ScDocument& rDoc = GetViewData().GetDocument();
3190 SCTAB nTab = GetViewData().GetTabNo();
3191 bool bOk;
3192 SCCOL i;
3193 SCROW j;
3195 bOk = true;
3196 SCCOL nFirstCol = nStartCol;
3197 SCCOL nLastCol = nEndCol;
3198 if (nStartCol+1 < nEndCol) { ++nFirstCol; --nLastCol; }
3199 for (i=nFirstCol; i<=nLastCol && bOk; i++)
3200 if (!rDoc.HasStringData( i,nStartRow,nTab ))
3201 bOk = false;
3202 if (bOk)
3203 nFlags |= CreateNameFlags::Top;
3204 else // Bottom only if not Top
3206 bOk = true;
3207 for (i=nFirstCol; i<=nLastCol && bOk; i++)
3208 if (!rDoc.HasStringData( i,nEndRow,nTab ))
3209 bOk = false;
3210 if (bOk)
3211 nFlags |= CreateNameFlags::Bottom;
3214 bOk = true;
3215 SCROW nFirstRow = nStartRow;
3216 SCROW nLastRow = nEndRow;
3217 if (nStartRow+1 < nEndRow) { ++nFirstRow; --nLastRow; }
3218 for (j=nFirstRow; j<=nLastRow && bOk; j++)
3219 if (!rDoc.HasStringData( nStartCol,j,nTab ))
3220 bOk = false;
3221 if (bOk)
3222 nFlags |= CreateNameFlags::Left;
3223 else // Right only if not Left
3225 bOk = true;
3226 for (j=nFirstRow; j<=nLastRow && bOk; j++)
3227 if (!rDoc.HasStringData( nEndCol,j,nTab ))
3228 bOk = false;
3229 if (bOk)
3230 nFlags |= CreateNameFlags::Right;
3234 if (nStartCol == nEndCol)
3235 nFlags &= ~CreateNameFlags( CreateNameFlags::Left | CreateNameFlags::Right );
3236 if (nStartRow == nEndRow)
3237 nFlags &= ~CreateNameFlags( CreateNameFlags::Top | CreateNameFlags::Bottom );
3239 return nFlags;
3242 void ScViewFunc::InsertNameList()
3244 ScAddress aPos( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() );
3245 ScDocShell* pDocSh = GetViewData().GetDocShell();
3246 if ( pDocSh->GetDocFunc().InsertNameList( aPos, false ) )
3247 pDocSh->UpdateOle(GetViewData());
3250 void ScViewFunc::UpdateSelectionArea( const ScMarkData& rSel, ScPatternAttr* pAttr )
3252 ScDocShell* pDocShell = GetViewData().GetDocShell();
3253 ScRange aMarkRange;
3254 if (rSel.IsMultiMarked() )
3255 aMarkRange = rSel.GetMultiMarkArea();
3256 else
3257 aMarkRange = rSel.GetMarkArea();
3259 bool bSetLines = false;
3260 bool bSetAlign = false;
3261 if ( pAttr )
3263 const SfxItemSet& rNewSet = pAttr->GetItemSet();
3264 bSetLines = rNewSet.GetItemState( ATTR_BORDER ) == SfxItemState::SET ||
3265 rNewSet.GetItemState( ATTR_SHADOW ) == SfxItemState::SET;
3266 bSetAlign = rNewSet.GetItemState( ATTR_HOR_JUSTIFY ) == SfxItemState::SET;
3269 sal_uInt16 nExtFlags = 0;
3270 if ( bSetLines )
3271 nExtFlags |= SC_PF_LINES;
3272 if ( bSetAlign )
3273 nExtFlags |= SC_PF_WHOLEROWS;
3275 SCCOL nStartCol = aMarkRange.aStart.Col();
3276 SCROW nStartRow = aMarkRange.aStart.Row();
3277 SCTAB nStartTab = aMarkRange.aStart.Tab();
3278 SCCOL nEndCol = aMarkRange.aEnd.Col();
3279 SCROW nEndRow = aMarkRange.aEnd.Row();
3280 SCTAB nEndTab = aMarkRange.aEnd.Tab();
3281 pDocShell->PostPaint( nStartCol, nStartRow, nStartTab,
3282 nEndCol, nEndRow, nEndTab,
3283 PaintPartFlags::Grid, nExtFlags | SC_PF_TESTMERGE );
3284 ScTabViewShell* pTabViewShell = GetViewData().GetViewShell();
3285 pTabViewShell->AdjustBlockHeight(false, const_cast<ScMarkData*>(&rSel));
3288 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */