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