1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 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 <iterator> 21 #include <memory> 22 #include <string_view> 23 24 #include <inputhdl.hxx> 25 #include <scitems.hxx> 26 #include <editeng/eeitem.hxx> 27 28 #include <sfx2/app.hxx> 29 #include <editeng/acorrcfg.hxx> 30 #include <formula/errorcodes.hxx> 31 #include <editeng/adjustitem.hxx> 32 #include <editeng/brushitem.hxx> 33 #include <svtools/colorcfg.hxx> 34 #include <editeng/colritem.hxx> 35 #include <editeng/editobj.hxx> 36 #include <editeng/editstat.hxx> 37 #include <editeng/editview.hxx> 38 #include <editeng/langitem.hxx> 39 #include <editeng/svxacorr.hxx> 40 #include <editeng/unolingu.hxx> 41 #include <editeng/wghtitem.hxx> 42 #include <editeng/justifyitem.hxx> 43 #include <editeng/misspellrange.hxx> 44 #include <sfx2/bindings.hxx> 45 #include <sfx2/viewfrm.hxx> 46 #include <sfx2/docfile.hxx> 47 #include <sfx2/printer.hxx> 48 #include <svl/numformat.hxx> 49 #include <svl/zforlist.hxx> 50 #include <unotools/localedatawrapper.hxx> 51 #include <unotools/charclass.hxx> 52 #include <utility> 53 #include <vcl/help.hxx> 54 #include <vcl/jsdialog/executor.hxx> 55 #include <vcl/commandevent.hxx> 56 #include <vcl/cursor.hxx> 57 #include <vcl/settings.hxx> 58 #include <vcl/svapp.hxx> 59 #include <tools/urlobj.hxx> 60 #include <formula/formulahelper.hxx> 61 #include <formula/funcvarargs.h> 62 #include <LibreOfficeKit/LibreOfficeKitEnums.h> 63 #include <comphelper/lok.hxx> 64 #include <osl/diagnose.h> 65 66 #include <attrib.hxx> 67 #include <inputwin.hxx> 68 #include <tabvwsh.hxx> 69 #include <docsh.hxx> 70 #include <scmod.hxx> 71 #include <formulaopt.hxx> 72 #include <uiitems.hxx> 73 #include <global.hxx> 74 #include <sc.hrc> 75 #include <globstr.hrc> 76 #include <scresid.hxx> 77 #include <patattr.hxx> 78 #include <viewdata.hxx> 79 #include <document.hxx> 80 #include <docpool.hxx> 81 #include <editutil.hxx> 82 #include <appoptio.hxx> 83 #include <docoptio.hxx> 84 #include <validat.hxx> 85 #include <rfindlst.hxx> 86 #include <inputopt.hxx> 87 #include <simpleformulacalc.hxx> 88 #include <compiler.hxx> 89 #include <editable.hxx> 90 #include <funcdesc.hxx> 91 #include <markdata.hxx> 92 #include <tokenarray.hxx> 93 #include <gridwin.hxx> 94 #include <output.hxx> 95 #include <fillinfo.hxx> 96 97 // Maximum Ranges in RangeFinder 98 #define RANGEFIND_MAX 128 99 100 using namespace formula; 101 102 namespace { 103 104 ScTypedCaseStrSet::const_iterator findText( 105 const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos, 106 const OUString& rStart, OUString& rResult, bool bBack) 107 { 108 auto lIsMatch = [&rStart](const ScTypedStrData& rData) { 109 return (rData.GetStringType() != ScTypedStrData::Value) && ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()); }; 110 111 if (bBack) // Backwards 112 { 113 ScTypedCaseStrSet::const_reverse_iterator it = rDataSet.rbegin(), itEnd = rDataSet.rend(); 114 if (itPos != rDataSet.end()) 115 { 116 size_t nPos = std::distance(rDataSet.begin(), itPos); 117 size_t nRPos = rDataSet.size() - 1 - nPos; 118 std::advance(it, nRPos); 119 ++it; 120 } 121 122 it = std::find_if(it, itEnd, lIsMatch); 123 if (it != itEnd) 124 { 125 rResult = it->GetString(); 126 return (++it).base(); // convert the reverse iterator back to iterator. 127 } 128 } 129 else // Forwards 130 { 131 ScTypedCaseStrSet::const_iterator it = rDataSet.begin(), itEnd = rDataSet.end(); 132 if (itPos != itEnd) 133 { 134 it = std::next(itPos); 135 } 136 137 it = std::find_if(it, itEnd, lIsMatch); 138 if (it != itEnd) 139 { 140 rResult = it->GetString(); 141 return it; 142 } 143 } 144 145 return rDataSet.end(); // no matching text found 146 } 147 148 OUString getExactMatch(const ScTypedCaseStrSet& rDataSet, const OUString& rString) 149 { 150 auto it = std::find_if(rDataSet.begin(), rDataSet.end(), 151 [&rString](const ScTypedStrData& rData) { 152 return (rData.GetStringType() != ScTypedStrData::Value) 153 && ScGlobal::GetTransliteration().isEqual(rData.GetString(), rString); 154 }); 155 if (it != rDataSet.end()) 156 return it->GetString(); 157 return rString; 158 } 159 160 // This assumes that rResults is a sorted ring w.r.t ScTypedStrData::LessCaseInsensitive() or 161 // in the reverse direction, whose origin is specified by nRingOrigin. 162 sal_Int32 getLongestCommonPrefixLength(const std::vector<OUString>& rResults, std::u16string_view aUserEntry, sal_Int32 nRingOrigin) 163 { 164 sal_Int32 nResults = rResults.size(); 165 if (!nResults) 166 return 0; 167 168 if (nResults == 1) 169 return rResults[0].getLength(); 170 171 sal_Int32 nMinLen = aUserEntry.size(); 172 sal_Int32 nLastIdx = nRingOrigin ? nRingOrigin - 1 : nResults - 1; 173 const OUString& rFirst = rResults[nRingOrigin]; 174 const OUString& rLast = rResults[nLastIdx]; 175 const sal_Int32 nMaxLen = std::min(rFirst.getLength(), rLast.getLength()); 176 177 for (sal_Int32 nLen = nMaxLen; nLen > nMinLen; --nLen) 178 { 179 if (ScGlobal::GetTransliteration().isMatch(rFirst.copy(0, nLen), rLast)) 180 return nLen; 181 } 182 183 return nMinLen; 184 } 185 186 ScTypedCaseStrSet::const_iterator findTextAll( 187 const ScTypedCaseStrSet& rDataSet, ScTypedCaseStrSet::const_iterator const & itPos, 188 const OUString& rStart, ::std::vector< OUString > &rResultVec, bool bBack, sal_Int32* pLongestPrefixLen = nullptr) 189 { 190 rResultVec.clear(); // clear contents 191 192 if (!rDataSet.size()) 193 return rDataSet.end(); 194 195 sal_Int32 nRingOrigin = 0; 196 size_t nCount = 0; 197 ScTypedCaseStrSet::const_iterator retit; 198 if ( bBack ) // Backwards 199 { 200 ScTypedCaseStrSet::const_reverse_iterator it, itEnd; 201 if ( itPos == rDataSet.end() ) 202 { 203 it = rDataSet.rend(); 204 --it; 205 itEnd = it; 206 } 207 else 208 { 209 it = rDataSet.rbegin(); 210 size_t nPos = std::distance(rDataSet.begin(), itPos); 211 size_t nRPos = rDataSet.size() - 1 - nPos; // if itPos == rDataSet.end(), then nRPos = -1 212 std::advance(it, nRPos); 213 if ( it == rDataSet.rend() ) 214 it = rDataSet.rbegin(); 215 itEnd = it; 216 } 217 bool bFirstTime = true; 218 219 while ( it != itEnd || bFirstTime ) 220 { 221 ++it; 222 if ( it == rDataSet.rend() ) // go to the first if reach the end 223 { 224 it = rDataSet.rbegin(); 225 nRingOrigin = nCount; 226 } 227 228 if ( bFirstTime ) 229 bFirstTime = false; 230 const ScTypedStrData& rData = *it; 231 if ( rData.GetStringType() == ScTypedStrData::Value ) 232 // skip values 233 continue; 234 235 if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) ) 236 // not a match 237 continue; 238 239 rResultVec.push_back(rData.GetString()); // set the match data 240 if ( nCount == 0 ) // convert the reverse iterator back to iterator. 241 { 242 // actually we want to do "retit = it;". 243 retit = rDataSet.begin(); 244 size_t nRPos = std::distance(rDataSet.rbegin(), it); 245 size_t nPos = rDataSet.size() - 1 - nRPos; 246 std::advance(retit, nPos); 247 } 248 ++nCount; 249 } 250 } 251 else // Forwards 252 { 253 ScTypedCaseStrSet::const_iterator it, itEnd; 254 it = itPos; 255 if ( it == rDataSet.end() ) 256 it = --rDataSet.end(); 257 itEnd = it; 258 bool bFirstTime = true; 259 260 while ( it != itEnd || bFirstTime ) 261 { 262 ++it; 263 if ( it == rDataSet.end() ) // go to the first if reach the end 264 { 265 it = rDataSet.begin(); 266 nRingOrigin = nCount; 267 } 268 269 if ( bFirstTime ) 270 bFirstTime = false; 271 const ScTypedStrData& rData = *it; 272 if ( rData.GetStringType() == ScTypedStrData::Value ) 273 // skip values 274 continue; 275 276 if ( !ScGlobal::GetTransliteration().isMatch(rStart, rData.GetString()) ) 277 // not a match 278 continue; 279 280 rResultVec.push_back(rData.GetString()); // set the match data 281 if ( nCount == 0 ) 282 retit = it; // remember first match iterator 283 ++nCount; 284 } 285 } 286 287 if (pLongestPrefixLen) 288 { 289 if (nRingOrigin >= static_cast<sal_Int32>(nCount)) 290 { 291 // All matches were picked when rDataSet was read in one direction. 292 nRingOrigin = 0; 293 } 294 // rResultsVec is a sorted ring with nRingOrigin "origin". 295 // The direction of sorting is not important for getLongestCommonPrefixLength. 296 *pLongestPrefixLen = getLongestCommonPrefixLength(rResultVec, rStart, nRingOrigin); 297 } 298 299 if ( nCount > 0 ) // at least one function has matched 300 return retit; 301 return rDataSet.end(); // no matching text found 302 } 303 304 } 305 306 void ScInputHandler::SendReferenceMarks( const SfxViewShell* pViewShell, 307 const std::vector<ReferenceMark>& rReferenceMarks ) 308 { 309 if ( !pViewShell ) 310 return; 311 312 bool bSend = false; 313 314 std::stringstream ss; 315 316 ss << "{ \"marks\": [ "; 317 318 for ( size_t i = 0; i < rReferenceMarks.size(); i++ ) 319 { 320 if ( rReferenceMarks[i].Is() ) 321 { 322 if ( bSend ) 323 ss << ", "; 324 325 ss << "{ \"rectangle\": \"" 326 << rReferenceMarks[i].nX << ", " 327 << rReferenceMarks[i].nY << ", " 328 << rReferenceMarks[i].nWidth << ", " 329 << rReferenceMarks[i].nHeight << "\", " 330 "\"color\": \"" << rReferenceMarks[i].aColor.AsRGBHexString() << "\", " 331 "\"part\": \"" << rReferenceMarks[i].nTab << "\" } "; 332 333 bSend = true; 334 } 335 } 336 337 ss << " ] }"; 338 339 OString aPayload( ss.str() ); 340 pViewShell->libreOfficeKitViewCallback( 341 LOK_CALLBACK_REFERENCE_MARKS, aPayload ); 342 } 343 344 static inline void incPos( const sal_Unicode c, sal_Int32& rPos, ESelection& rSel ) 345 { 346 ++rPos; 347 if (c == '\n') 348 { 349 ++rSel.nEndPara; 350 rSel.nEndPos = 0; 351 } 352 else 353 { 354 ++rSel.nEndPos; 355 } 356 } 357 358 void ScInputHandler::InitRangeFinder( const OUString& rFormula ) 359 { 360 DeleteRangeFinder(); 361 if ( !pActiveViewSh || !SC_MOD()->GetInputOptions().GetRangeFinder() ) 362 return; 363 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell(); 364 ScDocument& rDoc = pDocSh->GetDocument(); 365 const sal_Unicode cSheetSep = rDoc.GetSheetSeparator(); 366 367 OUString aDelimiters = ScEditUtil::ModifyDelimiters(" !~%\"\t\n"); 368 // delimiters (in addition to ScEditUtil): only characters that are 369 // allowed in formulas next to references and the quotation mark (so 370 // string constants can be skipped) 371 372 sal_Int32 nColon = aDelimiters.indexOf( ':' ); 373 if ( nColon != -1 ) 374 aDelimiters = aDelimiters.replaceAt( nColon, 1, u""); // Delimiter without colon 375 sal_Int32 nDot = aDelimiters.indexOf(cSheetSep); 376 if ( nDot != -1 ) 377 aDelimiters = aDelimiters.replaceAt( nDot, 1 , u""); // Delimiter without dot 378 379 const sal_Unicode* pChar = rFormula.getStr(); 380 sal_Int32 nLen = rFormula.getLength(); 381 sal_Int32 nPos = 0; 382 sal_Int32 nStart = 0; 383 ESelection aSel; 384 sal_uInt16 nCount = 0; 385 ScRange aRange; 386 while ( nPos < nLen && nCount < RANGEFIND_MAX ) 387 { 388 // Skip separator 389 while ( nPos<nLen && ScGlobal::UnicodeStrChr( aDelimiters.getStr(), pChar[nPos] ) ) 390 { 391 if ( pChar[nPos] == '"' ) // String 392 { 393 incPos( pChar[nPos], nPos, aSel); 394 while (nPos<nLen && pChar[nPos] != '"') // Skip until end 395 incPos( pChar[nPos], nPos, aSel); 396 } 397 incPos( pChar[nPos], nPos, aSel); // Separator or closing quote 398 } 399 400 // Text between separators. We only consider within one line/paragraph. 401 aSel.nStartPara = aSel.nEndPara; 402 aSel.nStartPos = aSel.nEndPos; 403 nStart = nPos; 404 handle_r1c1: 405 { 406 bool bSingleQuoted = false; 407 while (nPos < nLen) 408 { 409 // tdf#114113: handle addresses with quoted sheet names like "'Sheet 1'.A1" 410 // Literal single quotes in sheet names are masked by another single quote 411 if (pChar[nPos] == '\'') 412 { 413 bSingleQuoted = !bSingleQuoted; 414 } 415 else if (!bSingleQuoted) // Get everything in single quotes, including separators 416 { 417 if (ScGlobal::UnicodeStrChr(aDelimiters.getStr(), pChar[nPos])) 418 break; 419 } 420 incPos( pChar[nPos], nPos, aSel); 421 } 422 } 423 424 // for R1C1 '-' in R[-]... or C[-]... are not delimiters 425 // Nothing heroic here to ensure that there are '[]' around a negative 426 // integer. we need to clean up this code. 427 if( nPos < nLen && nPos > 0 && 428 '-' == pChar[nPos] && '[' == pChar[nPos-1] && 429 formula::FormulaGrammar::CONV_XL_R1C1 == rDoc.GetAddressConvention() ) 430 { 431 incPos( pChar[nPos], nPos, aSel); 432 goto handle_r1c1; 433 } 434 435 if ( nPos > nStart ) 436 { 437 OUString aTest = rFormula.copy( nStart, nPos-nStart ); 438 const ScAddress::Details aAddrDetails( rDoc, aCursorPos ); 439 ScRefFlags nFlags = aRange.ParseAny( aTest, rDoc, aAddrDetails ); 440 if ( nFlags & ScRefFlags::VALID ) 441 { 442 // Set tables if not specified 443 if ( (nFlags & ScRefFlags::TAB_3D) == ScRefFlags::ZERO) 444 aRange.aStart.SetTab( pActiveViewSh->GetViewData().GetTabNo() ); 445 if ( (nFlags & ScRefFlags::TAB2_3D) == ScRefFlags::ZERO) 446 aRange.aEnd.SetTab( aRange.aStart.Tab() ); 447 448 if ( ( nFlags & (ScRefFlags::COL2_VALID|ScRefFlags::ROW2_VALID|ScRefFlags::TAB2_VALID) ) == 449 ScRefFlags::ZERO ) 450 { 451 // #i73766# if a single ref was parsed, set the same "abs" flags for ref2, 452 // so Format doesn't output a double ref because of different flags. 453 ScRefFlags nAbsFlags = nFlags & (ScRefFlags::COL_ABS|ScRefFlags::ROW_ABS|ScRefFlags::TAB_ABS); 454 applyStartToEndFlags(nFlags, nAbsFlags); 455 } 456 457 if (!nCount) 458 { 459 mpEditEngine->SetUpdateLayout( false ); 460 pRangeFindList.reset(new ScRangeFindList( pDocSh->GetTitle() )); 461 } 462 463 Color nColor = pRangeFindList->Insert( ScRangeFindData( aRange, nFlags, aSel)); 464 465 SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() ); 466 aSet.Put( SvxColorItem( nColor, EE_CHAR_COLOR ) ); 467 mpEditEngine->QuickSetAttribs( aSet, aSel ); 468 ++nCount; 469 } 470 } 471 472 // Do not skip last separator; could be a quote (?) 473 } 474 475 UpdateLokReferenceMarks(); 476 477 if (nCount) 478 { 479 mpEditEngine->SetUpdateLayout( true ); 480 481 pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) ); 482 } 483 } 484 485 ReferenceMark ScInputHandler::GetReferenceMark( const ScViewData& rViewData, ScDocShell* pDocSh, 486 tools::Long nX1, tools::Long nX2, tools::Long nY1, tools::Long nY2, 487 tools::Long nTab, const Color& rColor ) 488 { 489 ScSplitPos eWhich = rViewData.GetActivePart(); 490 491 // This method is LOK specific. 492 if (comphelper::LibreOfficeKit::isCompatFlagSet( 493 comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs)) 494 { 495 SCCOL nCol1 = nX1, nCol2 = nX2; 496 SCROW nRow1 = nY1, nRow2 = nY2; 497 ScDocument& rDoc = pDocSh->GetDocument(); 498 499 PutInOrder(nCol1, nCol2); 500 PutInOrder(nRow1, nRow2); 501 502 if (nCol1 == nCol2 && nRow1 == nRow2) 503 rDoc.ExtendMerge(nCol1, nRow1, nCol2, nRow2, nTab); 504 else if (rDoc.HasAttrib(nCol2, nRow2, nTab, HasAttrFlags::Merged)) 505 rDoc.ExtendMerge(nCol2, nRow2, nCol2, nRow2, nTab); 506 507 Point aTopLeft = rViewData.GetPrintTwipsPos(nCol1, nRow1); 508 Point aBottomRight = rViewData.GetPrintTwipsPos(nCol2 + 1, nRow2 + 1); 509 tools::Long nSizeX = aBottomRight.X() - aTopLeft.X() - 1; 510 tools::Long nSizeY = aBottomRight.Y() - aTopLeft.Y() - 1; 511 512 return ReferenceMark(aTopLeft.X(), aTopLeft.Y(), nSizeX, nSizeY, nTab, rColor); 513 } 514 515 Point aScrPos = rViewData.GetScrPos( nX1, nY1, eWhich ); 516 tools::Long nScrX = aScrPos.X(); 517 tools::Long nScrY = aScrPos.Y(); 518 519 double nPPTX = rViewData.GetPPTX(); 520 double nPPTY = rViewData.GetPPTY(); 521 522 Fraction aZoomX = rViewData.GetZoomX(); 523 Fraction aZoomY = rViewData.GetZoomY(); 524 525 ScTableInfo aTabInfo; 526 pDocSh->GetDocument().FillInfo( aTabInfo, nX1, nY1, nX2, nY2, 527 nTab, nPPTX, nPPTY, false, false ); 528 529 ScOutputData aOutputData( nullptr, OUTTYPE_WINDOW, aTabInfo, 530 &( pDocSh->GetDocument() ), nTab, 531 nScrX, nScrY, 532 nX1, nY1, nX2, nY2, 533 nPPTX, nPPTY, 534 &aZoomX, &aZoomY ); 535 536 return aOutputData.FillReferenceMark( nX1, nY1, nX2, nY2, 537 rColor ); 538 } 539 540 void ScInputHandler::UpdateLokReferenceMarks() 541 { 542 if ( !comphelper::LibreOfficeKit::isActive()) 543 return; 544 545 ScTabViewShell* pShell = pActiveViewSh ? pActiveViewSh 546 : dynamic_cast<ScTabViewShell*>(SfxViewShell::Current()); 547 548 if (!pShell) 549 return; 550 551 ScViewData& rViewData = pShell->GetViewData(); 552 ScDocShell* pDocSh = rViewData.GetDocShell(); 553 ScRangeFindList* pRangeFinder = GetRangeFindList(); 554 555 if ( !pRangeFinder && !rViewData.IsRefMode() ) 556 return; 557 558 sal_uInt16 nAdditionalMarks = 0; 559 std::vector<ReferenceMark> aReferenceMarks( 1 ); 560 561 if ( rViewData.IsRefMode() ) 562 { 563 nAdditionalMarks = 1; 564 565 const svtools::ColorConfig& rColorCfg = SC_MOD()->GetColorConfig(); 566 Color aRefColor( rColorCfg.GetColorValue( svtools::CALCREFERENCE ).nColor ); 567 tools::Long nX1 = rViewData.GetRefStartX(); 568 tools::Long nX2 = rViewData.GetRefEndX(); 569 tools::Long nY1 = rViewData.GetRefStartY(); 570 tools::Long nY2 = rViewData.GetRefEndY(); 571 tools::Long nTab = rViewData.GetRefStartZ(); 572 573 if (rViewData.GetRefEndZ() == rViewData.GetTabNo()) 574 nTab = rViewData.GetRefEndZ(); 575 576 PutInOrder(nX1, nX2); 577 PutInOrder(nY1, nY2); 578 579 aReferenceMarks[0] = ScInputHandler::GetReferenceMark( rViewData, pDocSh, 580 nX1, nX2, nY1, nY2, 581 nTab, aRefColor ); 582 } 583 584 sal_uInt16 nCount = pRangeFinder ? 585 ( static_cast<sal_uInt16>( pRangeFinder->Count() ) + nAdditionalMarks ) : nAdditionalMarks; 586 aReferenceMarks.resize( nCount ); 587 588 if ( nCount && pRangeFinder && !pRangeFinder->IsHidden() && 589 pRangeFinder->GetDocName() == pDocSh->GetTitle() ) 590 { 591 for (sal_uInt16 i = 0; i < nCount - nAdditionalMarks; i++) 592 { 593 ScRangeFindData& rData = pRangeFinder->GetObject( i ); 594 ScRange aRef = rData.aRef; 595 aRef.PutInOrder(); 596 597 tools::Long nX1 = aRef.aStart.Col(); 598 tools::Long nX2 = aRef.aEnd.Col(); 599 tools::Long nY1 = aRef.aStart.Row(); 600 tools::Long nY2 = aRef.aEnd.Row(); 601 tools::Long nTab = aRef.aStart.Tab(); 602 603 aReferenceMarks[i + nAdditionalMarks] = ScInputHandler::GetReferenceMark( rViewData, pDocSh, 604 nX1, nX2, nY1, nY2, 605 nTab, rData.nColor ); 606 607 ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks ); 608 } 609 } 610 else if ( nCount ) 611 { 612 ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks ); 613 } 614 else 615 { 616 // Clear 617 aReferenceMarks.clear(); 618 ScInputHandler::SendReferenceMarks( pShell, aReferenceMarks ); 619 } 620 } 621 622 void ScInputHandler::SetDocumentDisposing( bool b ) 623 { 624 mbDocumentDisposing = b; 625 } 626 627 static void lcl_Replace( EditView* pView, const OUString& rNewStr, const ESelection& rOldSel ) 628 { 629 if ( !pView ) 630 return; 631 632 ESelection aOldSel = pView->GetSelection(); 633 if (aOldSel.HasRange()) 634 pView->SetSelection( ESelection( aOldSel.nEndPara, aOldSel.nEndPos, 635 aOldSel.nEndPara, aOldSel.nEndPos ) ); 636 637 EditEngine* pEngine = pView->GetEditEngine(); 638 pEngine->QuickInsertText( rNewStr, rOldSel ); 639 640 // Dummy InsertText for Update and Paint 641 // To do that we need to cancel the selection from above (before QuickInsertText) 642 pView->InsertText( OUString() ); 643 644 const sal_Int32 nPara = pEngine->GetParagraphCount() - 1; 645 const sal_Int32 nLen = pEngine->GetTextLen(nPara); 646 ESelection aSel( nPara, nLen, nPara, nLen ); 647 pView->SetSelection( aSel ); // Set cursor to the end 648 } 649 650 void ScInputHandler::UpdateRange( sal_uInt16 nIndex, const ScRange& rNew ) 651 { 652 ScTabViewShell* pDocView = pRefViewSh ? pRefViewSh : pActiveViewSh; 653 if ( pDocView && pRangeFindList && nIndex < pRangeFindList->Count() ) 654 { 655 ScRangeFindData& rData = pRangeFindList->GetObject( nIndex ); 656 Color nNewColor = pRangeFindList->FindColor( rNew, nIndex ); 657 658 ScRange aJustified = rNew; 659 aJustified.PutInOrder(); // Always display Ref in the Formula the right way 660 ScDocument& rDoc = pDocView->GetViewData().GetDocument(); 661 const ScAddress::Details aAddrDetails( rDoc, aCursorPos ); 662 OUString aNewStr(aJustified.Format(rDoc, rData.nFlags, aAddrDetails)); 663 SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() ); 664 665 DataChanging(); 666 667 lcl_Replace( pTopView, aNewStr, rData.maSel ); 668 lcl_Replace( pTableView, aNewStr, rData.maSel ); 669 670 // We are within one paragraph. 671 const sal_Int32 nDiff = aNewStr.getLength() - (rData.maSel.nEndPos - rData.maSel.nStartPos); 672 rData.maSel.nEndPos += nDiff; 673 674 aSet.Put( SvxColorItem( nNewColor, EE_CHAR_COLOR ) ); 675 mpEditEngine->QuickSetAttribs( aSet, rData.maSel ); 676 677 bInRangeUpdate = true; 678 DataChanged(); 679 bInRangeUpdate = false; 680 681 rData.aRef = rNew; 682 rData.nColor = nNewColor; 683 684 if (nDiff) 685 { 686 const size_t nCount = pRangeFindList->Count(); 687 for (size_t i = nIndex + 1; i < nCount; ++i) 688 { 689 ScRangeFindData& rNext = pRangeFindList->GetObject( i ); 690 if (rNext.maSel.nStartPara != rData.maSel.nStartPara) 691 break; 692 693 rNext.maSel.nStartPos += nDiff; 694 rNext.maSel.nEndPos += nDiff; 695 } 696 } 697 698 EditView* pActiveView = pTopView ? pTopView : pTableView; 699 pActiveView->ShowCursor( false ); 700 } 701 else 702 { 703 OSL_FAIL("UpdateRange: we're missing something"); 704 } 705 } 706 707 void ScInputHandler::DeleteRangeFinder() 708 { 709 ScTabViewShell* pPaintView = pRefViewSh ? pRefViewSh : pActiveViewSh; 710 if ( pRangeFindList && pPaintView ) 711 { 712 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell(); 713 pRangeFindList->SetHidden(true); 714 pDocSh->Broadcast( SfxHint( SfxHintId::ScShowRangeFinder ) ); // Steal 715 pRangeFindList.reset(); 716 } 717 } 718 719 static OUString GetEditText(const EditEngine* pEng) 720 { 721 return ScEditUtil::GetMultilineString(*pEng); 722 } 723 724 static void lcl_RemoveTabs(OUString& rStr) 725 { 726 rStr = rStr.replace('\t', ' '); 727 } 728 729 static void lcl_RemoveLineEnd(OUString& rStr) 730 { 731 rStr = convertLineEnd(rStr, LINEEND_LF); 732 rStr = rStr.replace('\n', ' '); 733 } 734 735 static sal_Int32 lcl_MatchParenthesis( const OUString& rStr, sal_Int32 nPos ) 736 { 737 int nDir; 738 sal_Unicode c1, c2 = 0; 739 c1 = rStr[nPos]; 740 switch ( c1 ) 741 { 742 case '(' : 743 c2 = ')'; 744 nDir = 1; 745 break; 746 case ')' : 747 c2 = '('; 748 nDir = -1; 749 break; 750 case '<' : 751 c2 = '>'; 752 nDir = 1; 753 break; 754 case '>' : 755 c2 = '<'; 756 nDir = -1; 757 break; 758 case '{' : 759 c2 = '}'; 760 nDir = 1; 761 break; 762 case '}' : 763 c2 = '{'; 764 nDir = -1; 765 break; 766 case '[' : 767 c2 = ']'; 768 nDir = 1; 769 break; 770 case ']' : 771 c2 = '['; 772 nDir = -1; 773 break; 774 default: 775 nDir = 0; 776 } 777 if ( !nDir ) 778 return -1; 779 sal_Int32 nLen = rStr.getLength(); 780 const sal_Unicode* p0 = rStr.getStr(); 781 const sal_Unicode* p; 782 const sal_Unicode* p1; 783 sal_uInt16 nQuotes = 0; 784 if ( nPos < nLen / 2 ) 785 { 786 p = p0; 787 p1 = p0 + nPos; 788 } 789 else 790 { 791 p = p0 + nPos; 792 p1 = p0 + nLen; 793 } 794 while ( p < p1 ) 795 { 796 if ( *p++ == '\"' ) 797 nQuotes++; 798 } 799 // Odd number of quotes that we find ourselves in a string 800 bool bLookInString = ((nQuotes % 2) != 0); 801 bool bInString = bLookInString; 802 p = p0 + nPos; 803 p1 = (nDir < 0 ? p0 : p0 + nLen) ; 804 sal_uInt16 nLevel = 1; 805 while ( p != p1 && nLevel ) 806 { 807 p += nDir; 808 if ( *p == '\"' ) 809 { 810 bInString = !bInString; 811 if ( bLookInString && !bInString ) 812 p = p1; // That's it then 813 } 814 else if ( bInString == bLookInString ) 815 { 816 if ( *p == c1 ) 817 nLevel++; 818 else if ( *p == c2 ) 819 nLevel--; 820 } 821 } 822 if ( nLevel ) 823 return -1; 824 return static_cast<sal_Int32>(p - p0); 825 } 826 827 ScInputHandler::ScInputHandler() 828 : pInputWin( nullptr ), 829 pTableView( nullptr ), 830 pTopView( nullptr ), 831 pTipVisibleParent( nullptr ), 832 nTipVisible( nullptr ), 833 pTipVisibleSecParent( nullptr ), 834 nTipVisibleSec( nullptr ), 835 nFormSelStart( 0 ), 836 nFormSelEnd( 0 ), 837 nCellPercentFormatDecSep( 0 ), 838 nAutoPar( 0 ), 839 eMode( SC_INPUT_NONE ), 840 bUseTab( false ), 841 bTextValid( true ), 842 bModified( false ), 843 bSelIsRef( false ), 844 bFormulaMode( false ), 845 bInRangeUpdate( false ), 846 bParenthesisShown( false ), 847 bCreatingFuncView( false ), 848 bInEnterHandler( false ), 849 bCommandErrorShown( false ), 850 bInOwnChange( false ), 851 bProtected( false ), 852 bLastIsSymbol( false ), 853 mbDocumentDisposing(false), 854 mbPartialPrefix(false), 855 mbEditingExistingContent(false), 856 nValidation( 0 ), 857 eAttrAdjust( SvxCellHorJustify::Standard ), 858 aScaleX( 1,1 ), 859 aScaleY( 1,1 ), 860 pRefViewSh( nullptr ), 861 pLastPattern( nullptr ) 862 { 863 // The InputHandler is constructed with the view, so SfxViewShell::Current 864 // doesn't have the right view yet. pActiveViewSh is updated in NotifyChange. 865 pActiveViewSh = nullptr; 866 867 // Bindings (only still used for Invalidate) are retrieved if needed on demand 868 869 pDelayTimer.reset( new Timer( "ScInputHandlerDelay timer" ) ); 870 pDelayTimer->SetTimeout( 500 ); // 500 ms delay 871 pDelayTimer->SetInvokeHandler( LINK( this, ScInputHandler, DelayTimer ) ); 872 } 873 874 ScInputHandler::~ScInputHandler() 875 { 876 // If this is the application InputHandler, the dtor is called after SfxApplication::Main, 877 // thus we can't rely on any Sfx functions 878 if (!mbDocumentDisposing) // inplace 879 EnterHandler(); // Finish input 880 881 if (SC_MOD()->GetRefInputHdl() == this) 882 SC_MOD()->SetRefInputHdl(nullptr); 883 884 if ( pInputWin && pInputWin->GetInputHandler() == this ) 885 pInputWin->SetInputHandler( nullptr ); 886 } 887 888 void ScInputHandler::SetRefScale( const Fraction& rX, const Fraction& rY ) 889 { 890 if ( rX != aScaleX || rY != aScaleY ) 891 { 892 aScaleX = rX; 893 aScaleY = rY; 894 if (mpEditEngine) 895 { 896 MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY ); 897 mpEditEngine->SetRefMapMode( aMode ); 898 } 899 } 900 } 901 902 void ScInputHandler::UpdateRefDevice() 903 { 904 if (!mpEditEngine) 905 return; 906 907 bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg(); 908 bool bInPlace = pActiveViewSh && pActiveViewSh->GetViewFrame().GetFrame().IsInPlace(); 909 EEControlBits nCtrl = mpEditEngine->GetControlWord(); 910 if ( bTextWysiwyg || bInPlace ) 911 nCtrl |= EEControlBits::FORMAT100; // EditEngine default: always format for 100% 912 else 913 nCtrl &= ~EEControlBits::FORMAT100; // when formatting for screen, use the actual MapMode 914 mpEditEngine->SetControlWord( nCtrl ); 915 if ( bTextWysiwyg && pActiveViewSh ) 916 mpEditEngine->SetRefDevice( pActiveViewSh->GetViewData().GetDocument().GetPrinter() ); 917 else 918 mpEditEngine->SetRefDevice( nullptr ); 919 920 MapMode aMode( MapUnit::Map100thMM, Point(), aScaleX, aScaleY ); 921 mpEditEngine->SetRefMapMode( aMode ); 922 923 // SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev, 924 // so the DigitLanguage can be safely modified (might use an own VDev instead of NULL). 925 if ( !( bTextWysiwyg && pActiveViewSh ) ) 926 { 927 mpEditEngine->GetRefDevice()->SetDigitLanguage( ScModule::GetOptDigitLanguage() ); 928 } 929 } 930 931 void ScInputHandler::ImplCreateEditEngine() 932 { 933 if ( mpEditEngine ) 934 return; 935 936 // we cannot create a properly initialised EditEngine until we have a document 937 assert( pActiveViewSh ); 938 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); 939 mpEditEngine = std::make_unique<ScFieldEditEngine>(&rDoc, rDoc.GetEnginePool(), rDoc.GetEditPool()); 940 mpEditEngine->SetWordDelimiters( ScEditUtil::ModifyDelimiters( mpEditEngine->GetWordDelimiters() ) ); 941 UpdateRefDevice(); // also sets MapMode 942 mpEditEngine->SetPaperSize( Size( 1000000, 1000000 ) ); 943 pEditDefaults.reset( new SfxItemSet( mpEditEngine->GetEmptyItemSet() ) ); 944 945 mpEditEngine->SetControlWord( mpEditEngine->GetControlWord() | EEControlBits::AUTOCORRECT ); 946 mpEditEngine->SetReplaceLeadingSingleQuotationMark( false ); 947 mpEditEngine->SetModifyHdl( LINK( this, ScInputHandler, ModifyHdl ) ); 948 } 949 950 void ScInputHandler::UpdateAutoCorrFlag() 951 { 952 EEControlBits nCntrl = mpEditEngine->GetControlWord(); 953 EEControlBits nOld = nCntrl; 954 955 // Don't use pLastPattern here (may be invalid because of AutoStyle) 956 bool bDisable = bLastIsSymbol || bFormulaMode; 957 if ( bDisable ) 958 nCntrl &= ~EEControlBits::AUTOCORRECT; 959 else 960 nCntrl |= EEControlBits::AUTOCORRECT; 961 962 if ( nCntrl != nOld ) 963 mpEditEngine->SetControlWord(nCntrl); 964 } 965 966 void ScInputHandler::UpdateSpellSettings( bool bFromStartTab ) 967 { 968 if ( !pActiveViewSh ) 969 return; 970 971 ScViewData& rViewData = pActiveViewSh->GetViewData(); 972 bool bOnlineSpell = rViewData.GetDocument().GetDocOptions().IsAutoSpell(); 973 974 // SetDefaultLanguage is independent of the language attributes, 975 // ScGlobal::GetEditDefaultLanguage is always used. 976 // It must be set every time in case the office language was changed. 977 978 mpEditEngine->SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() ); 979 980 // if called for changed options, update flags only if already editing 981 // if called from StartTable, always update flags 982 983 if ( bFromStartTab || eMode != SC_INPUT_NONE ) 984 { 985 EEControlBits nCntrl = mpEditEngine->GetControlWord(); 986 EEControlBits nOld = nCntrl; 987 if( bOnlineSpell ) 988 nCntrl |= EEControlBits::ONLINESPELLING; 989 else 990 nCntrl &= ~EEControlBits::ONLINESPELLING; 991 // No AutoCorrect for Symbol Font (EditEngine does no evaluate Default) 992 if ( pLastPattern && pLastPattern->IsSymbolFont() ) 993 nCntrl &= ~EEControlBits::AUTOCORRECT; 994 else 995 nCntrl |= EEControlBits::AUTOCORRECT; 996 if ( nCntrl != nOld ) 997 mpEditEngine->SetControlWord(nCntrl); 998 999 ScDocument& rDoc = rViewData.GetDocument(); 1000 rDoc.ApplyAsianEditSettings( *mpEditEngine ); 1001 mpEditEngine->SetDefaultHorizontalTextDirection( 1002 rDoc.GetEditTextDirection( rViewData.GetTabNo() ) ); 1003 mpEditEngine->SetFirstWordCapitalization( false ); 1004 } 1005 1006 // Language is set separately, so the speller is needed only if online spelling is active 1007 if ( bOnlineSpell ) { 1008 css::uno::Reference<css::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() ); 1009 mpEditEngine->SetSpeller( xXSpellChecker1 ); 1010 } 1011 1012 bool bHyphen = pLastPattern && pLastPattern->GetItem(ATTR_HYPHENATE).GetValue(); 1013 if ( bHyphen ) { 1014 css::uno::Reference<css::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() ); 1015 mpEditEngine->SetHyphenator( xXHyphenator ); 1016 } 1017 } 1018 1019 // Function/Range names etc. as Tip help 1020 1021 // The other types are defined in ScDocument::GetFormulaEntries 1022 void ScInputHandler::GetFormulaData() 1023 { 1024 if ( !pActiveViewSh ) 1025 return; 1026 1027 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); 1028 1029 if ( pFormulaData ) 1030 pFormulaData->clear(); 1031 else 1032 { 1033 pFormulaData.reset( new ScTypedCaseStrSet ); 1034 } 1035 1036 if( pFormulaDataPara ) 1037 pFormulaDataPara->clear(); 1038 else 1039 pFormulaDataPara.reset( new ScTypedCaseStrSet ); 1040 1041 const OUString aParenthesesReplacement( cParenthesesReplacement); 1042 const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); 1043 const sal_uInt32 nListCount = pFuncList->GetCount(); 1044 const InputHandlerFunctionNames& rFunctionNames = ScGlobal::GetInputHandlerFunctionNames(); 1045 *pFormulaData = rFunctionNames.maFunctionData; 1046 *pFormulaDataPara = rFunctionNames.maFunctionDataPara; 1047 maFormulaChar = rFunctionNames.maFunctionChar; 1048 1049 // Increase suggestion priority of MRU formulas 1050 const ScAppOptions& rOpt = SC_MOD()->GetAppOptions(); 1051 const sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount(); 1052 const sal_uInt16* pMRUList = rOpt.GetLRUFuncList(); 1053 for (sal_uInt16 i = 0; i < nMRUCount; i++) 1054 { 1055 const sal_uInt16 nId = pMRUList[i]; 1056 for (sal_uInt32 j = 0; j < nListCount; j++) 1057 { 1058 const ScFuncDesc* pDesc = pFuncList->GetFunction(j); 1059 if (pDesc->nFIndex == nId && pDesc->mxFuncName) 1060 { 1061 const OUString aEntry = *pDesc->mxFuncName + aParenthesesReplacement;; 1062 const ScTypedStrData aData(aEntry, 0.0, 0.0, ScTypedStrData::Standard); 1063 auto it = pFormulaData->find(aData); 1064 if (it != pFormulaData->end()) 1065 pFormulaData->erase(it); 1066 pFormulaData->insert(ScTypedStrData(aEntry, 0.0, 0.0, ScTypedStrData::MRU)); 1067 break; // Stop searching 1068 } 1069 } 1070 } 1071 miAutoPosFormula = pFormulaData->end(); 1072 1073 // tdf#142031 - collect all the characters for the formula suggestion auto input 1074 ScTypedCaseStrSet aStrSet; 1075 rDoc.GetFormulaEntries( aStrSet ); 1076 for (auto iter = aStrSet.begin(); iter != aStrSet.end(); ++iter) 1077 { 1078 const OUString aFuncName = ScGlobal::getCharClass().uppercase((*iter).GetString()); 1079 // fdo#75264 fill maFormulaChar with all characters used in formula names 1080 for (sal_Int32 j = 0; j < aFuncName.getLength(); j++) 1081 maFormulaChar.insert(aFuncName[j]); 1082 } 1083 pFormulaData->insert(aStrSet.begin(), aStrSet.end()); 1084 pFormulaDataPara->insert(aStrSet.begin(), aStrSet.end()); 1085 } 1086 1087 IMPL_LINK( ScInputHandler, ShowHideTipVisibleParentListener, VclWindowEvent&, rEvent, void ) 1088 { 1089 if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide 1090 || rEvent.GetId() == VclEventId::WindowLoseFocus || rEvent.GetId() == VclEventId::ControlLoseFocus) 1091 HideTip(); 1092 } 1093 1094 IMPL_LINK( ScInputHandler, ShowHideTipVisibleSecParentListener, VclWindowEvent&, rEvent, void ) 1095 { 1096 if (rEvent.GetId() == VclEventId::ObjectDying || rEvent.GetId() == VclEventId::WindowHide 1097 || rEvent.GetId() == VclEventId::WindowLoseFocus || rEvent.GetId() == VclEventId::ControlLoseFocus) 1098 HideTipBelow(); 1099 } 1100 1101 void ScInputHandler::HideTip() 1102 { 1103 if ( nTipVisible ) 1104 { 1105 pTipVisibleParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) ); 1106 Help::HidePopover(pTipVisibleParent, nTipVisible ); 1107 nTipVisible = nullptr; 1108 pTipVisibleParent = nullptr; 1109 } 1110 aManualTip.clear(); 1111 } 1112 void ScInputHandler::HideTipBelow() 1113 { 1114 if ( nTipVisibleSec ) 1115 { 1116 pTipVisibleSecParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) ); 1117 Help::HidePopover(pTipVisibleSecParent, nTipVisibleSec); 1118 nTipVisibleSec = nullptr; 1119 pTipVisibleSecParent = nullptr; 1120 } 1121 aManualTip.clear(); 1122 } 1123 1124 namespace 1125 { 1126 1127 bool lcl_hasSingleToken(std::u16string_view s, sal_Unicode c) 1128 { 1129 return !s.empty() && s.find(c) == std::u16string_view::npos; 1130 } 1131 1132 } 1133 1134 void ScInputHandler::ShowArgumentsTip( OUString& rSelText ) 1135 { 1136 if (comphelper::LibreOfficeKit::isActive()) 1137 { 1138 return; 1139 } 1140 1141 if ( !pActiveViewSh ) 1142 return; 1143 1144 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell(); 1145 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); 1146 const sal_Unicode cSheetSep = pDocSh->GetDocument().GetSheetSeparator(); 1147 FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr()); 1148 bool bFound = false; 1149 while( !bFound ) 1150 { 1151 rSelText += ")"; 1152 sal_Int32 nLeftParentPos = lcl_MatchParenthesis( rSelText, rSelText.getLength()-1 ); 1153 if( nLeftParentPos != -1 ) 1154 { 1155 sal_Int32 nNextFStart = aHelper.GetFunctionStart( rSelText, nLeftParentPos, true); 1156 const IFunctionDescription* ppFDesc; 1157 ::std::vector< OUString> aArgs; 1158 if( aHelper.GetNextFunc( rSelText, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) ) 1159 { 1160 if( !ppFDesc->getFunctionName().isEmpty() ) 1161 { 1162 sal_Int32 nArgPos = aHelper.GetArgStart( rSelText, nNextFStart, 0 ); 1163 sal_uInt16 nArgs = static_cast<sal_uInt16>(ppFDesc->getParameterCount()); 1164 OUString aFuncName( ppFDesc->getFunctionName() + "("); 1165 OUString aNew; 1166 ScTypedCaseStrSet::const_iterator it = 1167 findText(*pFormulaDataPara, pFormulaDataPara->end(), aFuncName, aNew, false); 1168 if (it != pFormulaDataPara->end()) 1169 { 1170 bool bFlag = false; 1171 sal_uInt16 nActive = 0; 1172 for( sal_uInt16 i=0; i < nArgs; i++ ) 1173 { 1174 sal_Int32 nLength = aArgs[i].getLength(); 1175 if( nArgPos <= rSelText.getLength()-1 ) 1176 { 1177 nActive = i+1; 1178 bFlag = true; 1179 } 1180 nArgPos+=nLength+1; 1181 } 1182 if( bFlag ) 1183 { 1184 sal_Int32 nStartPosition = 0; 1185 sal_Int32 nEndPosition = 0; 1186 1187 if( lcl_hasSingleToken(aNew, cSep) ) 1188 { 1189 for (sal_Int32 i = 0; i < aNew.getLength(); ++i) 1190 { 1191 sal_Unicode cNext = aNew[i]; 1192 if( cNext == '(' ) 1193 { 1194 nStartPosition = i+1; 1195 } 1196 } 1197 } 1198 else if( lcl_hasSingleToken(aNew, cSheetSep) ) 1199 { 1200 sal_uInt16 nCount = 0; 1201 for (sal_Int32 i = 0; i < aNew.getLength(); ++i) 1202 { 1203 sal_Unicode cNext = aNew[i]; 1204 if( cNext == '(' ) 1205 { 1206 nStartPosition = i+1; 1207 } 1208 else if( cNext == cSep ) 1209 { 1210 nCount ++; 1211 nEndPosition = i; 1212 if( nCount == nActive ) 1213 { 1214 break; 1215 } 1216 nStartPosition = nEndPosition+1; 1217 } 1218 } 1219 } 1220 else 1221 { 1222 sal_uInt16 nCount = 0; 1223 for (sal_Int32 i = 0; i < aNew.getLength(); ++i) 1224 { 1225 sal_Unicode cNext = aNew[i]; 1226 if( cNext == '(' ) 1227 { 1228 nStartPosition = i+1; 1229 } 1230 else if( cNext == cSep ) 1231 { 1232 nCount ++; 1233 nEndPosition = i; 1234 if( nCount == nActive ) 1235 { 1236 break; 1237 } 1238 nStartPosition = nEndPosition+1; 1239 } 1240 else if( cNext == cSheetSep ) 1241 { 1242 continue; 1243 } 1244 } 1245 } 1246 1247 if (nStartPosition > 0) 1248 { 1249 nArgs = ppFDesc->getParameterCount(); 1250 sal_Int16 nVarArgsSet = 0; 1251 if ( nArgs >= PAIRED_VAR_ARGS ) 1252 { 1253 nVarArgsSet = 2; 1254 nArgs -= PAIRED_VAR_ARGS - nVarArgsSet; 1255 } 1256 else if ( nArgs >= VAR_ARGS ) 1257 { 1258 nVarArgsSet = 1; 1259 nArgs -= VAR_ARGS - nVarArgsSet; 1260 } 1261 if ( nVarArgsSet > 0 && nActive > nArgs ) 1262 nActive = nArgs - (nActive - nArgs) % nVarArgsSet; 1263 aNew = OUString::Concat(aNew.subView(0, nStartPosition)) + 1264 u"\x25BA" + 1265 aNew.subView(nStartPosition) + 1266 " : " + 1267 ppFDesc->getParameterDescription(nActive-1); 1268 if (eMode != SC_INPUT_TOP) 1269 { 1270 ShowTipBelow( aNew ); 1271 } 1272 else 1273 { 1274 ShowTip(aNew); 1275 } 1276 bFound = true; 1277 } 1278 } 1279 else 1280 { 1281 ShowTipBelow( aNew ); 1282 bFound = true; 1283 } 1284 } 1285 } 1286 } 1287 } 1288 else 1289 { 1290 break; 1291 } 1292 } 1293 } 1294 1295 void ScInputHandler::ShowTipCursor() 1296 { 1297 HideTip(); 1298 HideTipBelow(); 1299 EditView* pActiveView = pTopView ? pTopView : pTableView; 1300 1301 /* TODO: MLFORMULA: this should work also with multi-line formulas. */ 1302 if ( !(bFormulaMode && pActiveView && pFormulaDataPara && mpEditEngine->GetParagraphCount() == 1) ) 1303 return; 1304 1305 OUString aParagraph = mpEditEngine->GetText( 0 ); 1306 ESelection aSel = pActiveView->GetSelection(); 1307 aSel.Adjust(); 1308 1309 if ( aParagraph.getLength() < aSel.nEndPos ) 1310 return; 1311 1312 if ( aSel.nEndPos > 0 ) 1313 { 1314 OUString aSelText( aParagraph.copy( 0, aSel.nEndPos )); 1315 1316 ShowArgumentsTip( aSelText ); 1317 } 1318 } 1319 1320 void ScInputHandler::ShowTip( const OUString& rText ) 1321 { 1322 // aManualTip needs to be set afterwards from outside 1323 1324 HideTip(); 1325 HideTipBelow(); 1326 1327 EditView* pActiveView = pTopView ? pTopView : pTableView; 1328 if (!pActiveView) 1329 return; 1330 1331 Point aPos; 1332 if (pInputWin && pInputWin->GetEditView() == pActiveView) 1333 { 1334 pTipVisibleParent = pInputWin->GetEditWindow(); 1335 aPos = pInputWin->GetCursorScreenPixelPos(); 1336 } 1337 else 1338 { 1339 pTipVisibleParent = pActiveView->GetWindow(); 1340 if (vcl::Cursor* pCur = pActiveView->GetCursor()) 1341 aPos = pTipVisibleParent->LogicToPixel( pCur->GetPos() ); 1342 aPos = pTipVisibleParent->OutputToScreenPixel( aPos ); 1343 } 1344 1345 tools::Rectangle aRect( aPos, aPos ); 1346 QuickHelpFlags const nAlign = QuickHelpFlags::Left|QuickHelpFlags::Bottom; 1347 nTipVisible = Help::ShowPopover(pTipVisibleParent, aRect, rText, nAlign); 1348 pTipVisibleParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) ); 1349 } 1350 1351 void ScInputHandler::ShowTipBelow( const OUString& rText ) 1352 { 1353 HideTipBelow(); 1354 1355 EditView* pActiveView = pTopView ? pTopView : pTableView; 1356 if ( !pActiveView ) 1357 return; 1358 1359 Point aPos; 1360 if (pInputWin && pInputWin->GetEditView() == pActiveView) 1361 { 1362 pTipVisibleSecParent = pInputWin->GetEditWindow(); 1363 aPos = pInputWin->GetCursorScreenPixelPos(true); 1364 } 1365 else 1366 { 1367 pTipVisibleSecParent = pActiveView->GetWindow(); 1368 if (vcl::Cursor* pCur = pActiveView->GetCursor()) 1369 { 1370 Point aLogicPos = pCur->GetPos(); 1371 aLogicPos.AdjustY(pCur->GetHeight() ); 1372 aPos = pTipVisibleSecParent->LogicToPixel( aLogicPos ); 1373 } 1374 aPos = pTipVisibleSecParent->OutputToScreenPixel( aPos ); 1375 } 1376 1377 tools::Rectangle aRect( aPos, aPos ); 1378 QuickHelpFlags const nAlign = QuickHelpFlags::Left | QuickHelpFlags::Top | QuickHelpFlags::NoEvadePointer; 1379 nTipVisibleSec = Help::ShowPopover(pTipVisibleSecParent, aRect, rText, nAlign); 1380 pTipVisibleSecParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) ); 1381 } 1382 1383 bool ScInputHandler::GetFuncName( OUString& aStart, OUString& aResult ) 1384 { 1385 if ( aStart.isEmpty() ) 1386 return false; 1387 1388 aStart = ScGlobal::getCharClass().uppercase( aStart ); 1389 sal_Int32 nPos = aStart.getLength() - 1; 1390 sal_Unicode c = aStart[ nPos ]; 1391 // fdo#75264 use maFormulaChar to check if characters are used in function names 1392 ::std::set< sal_Unicode >::const_iterator p = maFormulaChar.find( c ); 1393 if ( p == maFormulaChar.end() ) 1394 return false; // last character is not part of any function name, quit 1395 1396 ::std::vector<sal_Unicode> aTemp { c }; 1397 for(sal_Int32 i = nPos - 1; i >= 0; --i) 1398 { 1399 c = aStart[ i ]; 1400 p = maFormulaChar.find( c ); 1401 1402 if (p == maFormulaChar.end()) 1403 break; 1404 1405 aTemp.push_back( c ); 1406 } 1407 1408 ::std::vector<sal_Unicode>::reverse_iterator rIt = aTemp.rbegin(); 1409 aResult = OUString( *rIt++ ); 1410 while ( rIt != aTemp.rend() ) 1411 aResult += OUStringChar( *rIt++ ); 1412 1413 return true; 1414 } 1415 1416 namespace { 1417 /// Rid ourselves of unwanted " quoted json characters. 1418 OString escapeJSON(const OUString &aStr) 1419 { 1420 OUString aEscaped = aStr; 1421 aEscaped = aEscaped.replaceAll("\n", " "); 1422 aEscaped = aEscaped.replaceAll("\"", "'"); 1423 return OUStringToOString(aEscaped, RTL_TEXTENCODING_UTF8); 1424 } 1425 } 1426 1427 void ScInputHandler::ShowFuncList( const ::std::vector< OUString > & rFuncStrVec ) 1428 { 1429 const SfxViewShell* pViewShell = SfxViewShell::Current(); 1430 if (comphelper::LibreOfficeKit::isActive()) 1431 { 1432 if (rFuncStrVec.size() && pViewShell && pViewShell->isLOKMobilePhone()) 1433 { 1434 auto aPos = pFormulaData->begin(); 1435 sal_uInt32 nCurIndex = std::distance(aPos, miAutoPosFormula); 1436 const sal_uInt32 nSize = pFormulaData->size(); 1437 1438 OUString aFuncNameStr; 1439 OUString aDescFuncNameStr; 1440 OStringBuffer aPayload("[ "); 1441 for (const OUString& rFunc : rFuncStrVec) 1442 { 1443 if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement ) 1444 { 1445 aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1); 1446 } 1447 else 1448 { 1449 aFuncNameStr = rFunc; 1450 } 1451 1452 FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr()); 1453 aDescFuncNameStr = aFuncNameStr + "()"; 1454 sal_Int32 nNextFStart = 0; 1455 const IFunctionDescription* ppFDesc; 1456 ::std::vector< OUString > aArgs; 1457 OUString eqPlusFuncName = "=" + aDescFuncNameStr; 1458 if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) ) 1459 { 1460 if ( !ppFDesc->getFunctionName().isEmpty() ) 1461 { 1462 aPayload.append("{" 1463 "\"index\": " 1464 + OString::number(static_cast<sal_Int64>(nCurIndex)) 1465 + ", " 1466 "\"signature\": \"" 1467 + escapeJSON(ppFDesc->getSignature()) 1468 + "\", " 1469 "\"description\": \"" 1470 + escapeJSON(ppFDesc->getDescription()) 1471 + "\"}, "); 1472 } 1473 } 1474 ++nCurIndex; 1475 if (nCurIndex == nSize) 1476 nCurIndex = 0; 1477 } 1478 sal_Int32 nLen = aPayload.getLength(); 1479 aPayload[nLen - 2] = ' '; 1480 aPayload[nLen - 1] = ']'; 1481 1482 OString s = aPayload.makeStringAndClear(); 1483 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CALC_FUNCTION_LIST, s); 1484 } 1485 // not tunnel tooltips in the lok case 1486 return; 1487 } 1488 1489 OUStringBuffer aTipStr; 1490 OUString aFuncNameStr; 1491 OUString aDescFuncNameStr; 1492 ::std::vector<OUString>::const_iterator itStr = rFuncStrVec.begin(); 1493 sal_Int32 nMaxFindNumber = 3; 1494 sal_Int32 nRemainFindNumber = nMaxFindNumber; 1495 for ( ; itStr != rFuncStrVec.end(); ++itStr ) 1496 { 1497 const OUString& rFunc = *itStr; 1498 if ( rFunc[rFunc.getLength()-1] == cParenthesesReplacement ) 1499 { 1500 aFuncNameStr = rFunc.copy(0, rFunc.getLength()-1); 1501 } 1502 else 1503 { 1504 aFuncNameStr = rFunc; 1505 } 1506 if ( itStr == rFuncStrVec.begin() ) 1507 { 1508 aTipStr = "["; 1509 aDescFuncNameStr = aFuncNameStr + "()"; 1510 } 1511 else 1512 { 1513 aTipStr.append(", "); 1514 } 1515 aTipStr.append(aFuncNameStr); 1516 if ( itStr == rFuncStrVec.begin() ) 1517 aTipStr.append("]"); 1518 if ( --nRemainFindNumber <= 0 ) 1519 break; 1520 } 1521 sal_Int32 nRemainNumber = rFuncStrVec.size() - nMaxFindNumber; 1522 if ( nRemainFindNumber == 0 && nRemainNumber > 0 ) 1523 { 1524 OUString aMessage( ScResId( STR_FUNCTIONS_FOUND ) ); 1525 aMessage = aMessage.replaceFirst("%2", OUString::number(nRemainNumber)); 1526 aMessage = aMessage.replaceFirst("%1", aTipStr); 1527 aTipStr = aMessage; 1528 } 1529 FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr()); 1530 sal_Int32 nNextFStart = 0; 1531 const IFunctionDescription* ppFDesc; 1532 ::std::vector< OUString > aArgs; 1533 OUString eqPlusFuncName = "=" + aDescFuncNameStr; 1534 if ( aHelper.GetNextFunc( eqPlusFuncName, false, nNextFStart, nullptr, &ppFDesc, &aArgs ) ) 1535 { 1536 if ( !ppFDesc->getFunctionName().isEmpty() ) 1537 { 1538 aTipStr.append(" : " + ppFDesc->getDescription()); 1539 } 1540 } 1541 ShowTip( aTipStr.makeStringAndClear() ); 1542 } 1543 1544 void ScInputHandler::UseFormulaData() 1545 { 1546 EditView* pActiveView = pTopView ? pTopView : pTableView; 1547 1548 /* TODO: MLFORMULA: this should work also with multi-line formulas. */ 1549 if ( !(pActiveView && pFormulaData && mpEditEngine->GetParagraphCount() == 1) ) 1550 return; 1551 1552 OUString aParagraph = mpEditEngine->GetText( 0 ); 1553 ESelection aSel = pActiveView->GetSelection(); 1554 aSel.Adjust(); 1555 1556 // Due to differences between table and input cell (e.g clipboard with line breaks), 1557 // the selection may not be in line with the EditEngine anymore. 1558 // Just return without any indication as to why. 1559 if ( aSel.nEndPos > aParagraph.getLength() ) 1560 return; 1561 1562 if ( aParagraph.getLength() > aSel.nEndPos && 1563 ( ScGlobal::getCharClass().isLetterNumeric( aParagraph, aSel.nEndPos ) || 1564 aParagraph[ aSel.nEndPos ] == '_' || 1565 aParagraph[ aSel.nEndPos ] == '.' || 1566 aParagraph[ aSel.nEndPos ] == '$' ) ) 1567 return; 1568 1569 // Is the cursor at the end of a word? 1570 if ( aSel.nEndPos <= 0 ) 1571 return; 1572 1573 OUString aSelText( aParagraph.copy( 0, aSel.nEndPos )); 1574 1575 OUString aText; 1576 if ( GetFuncName( aSelText, aText ) ) 1577 { 1578 // function name is incomplete: 1579 // show matching functions name as tip above cell 1580 ::std::vector<OUString> aNewVec; 1581 miAutoPosFormula = pFormulaData->end(); 1582 miAutoPosFormula = findTextAll(*pFormulaData, miAutoPosFormula, aText, aNewVec, false); 1583 if (miAutoPosFormula != pFormulaData->end()) 1584 { 1585 // check if partial function name is not between quotes 1586 sal_Unicode cBetweenQuotes = 0; 1587 for ( int n = 0; n < aSelText.getLength(); n++ ) 1588 { 1589 if (cBetweenQuotes) 1590 { 1591 if (aSelText[n] == cBetweenQuotes) 1592 cBetweenQuotes = 0; 1593 } 1594 else if ( aSelText[ n ] == '"' ) 1595 cBetweenQuotes = '"'; 1596 else if ( aSelText[ n ] == '\'' ) 1597 cBetweenQuotes = '\''; 1598 } 1599 if ( cBetweenQuotes ) 1600 return; // we're between quotes 1601 1602 ShowFuncList(aNewVec); 1603 aAutoSearch = aText; 1604 } 1605 return; 1606 } 1607 1608 // function name is complete: 1609 // show tip below the cell with function name and arguments of function 1610 ShowArgumentsTip( aSelText ); 1611 } 1612 1613 void ScInputHandler::NextFormulaEntry( bool bBack ) 1614 { 1615 EditView* pActiveView = pTopView ? pTopView : pTableView; 1616 if ( pActiveView && pFormulaData ) 1617 { 1618 ::std::vector<OUString> aNewVec; 1619 ScTypedCaseStrSet::const_iterator itNew = findTextAll(*pFormulaData, miAutoPosFormula, aAutoSearch, aNewVec, bBack); 1620 if (itNew != pFormulaData->end()) 1621 { 1622 miAutoPosFormula = itNew; 1623 ShowFuncList( aNewVec ); 1624 } 1625 } 1626 1627 // For Tab we always call HideCursor first 1628 if (pActiveView) 1629 pActiveView->ShowCursor(); 1630 } 1631 1632 namespace { 1633 1634 void completeFunction( EditView* pView, const OUString& rInsert, bool& rParInserted ) 1635 { 1636 if (!pView) 1637 return; 1638 1639 ESelection aSel = pView->GetSelection(); 1640 1641 bool bNoInitialLetter = false; 1642 OUString aOld = pView->GetEditEngine()->GetText(0); 1643 // in case we want just insert a function and not completing 1644 if ( comphelper::LibreOfficeKit::isActive() ) 1645 { 1646 ESelection aSelRange = aSel; 1647 --aSelRange.nStartPos; 1648 --aSelRange.nEndPos; 1649 pView->SetSelection(aSelRange); 1650 pView->SelectCurrentWord(); 1651 1652 if ( aOld == "=" ) 1653 { 1654 bNoInitialLetter = true; 1655 aSelRange.nStartPos = 1; 1656 aSelRange.nEndPos = 1; 1657 pView->SetSelection(aSelRange); 1658 } 1659 else if ( pView->GetSelected().startsWith("()") ) 1660 { 1661 bNoInitialLetter = true; 1662 ++aSelRange.nStartPos; 1663 ++aSelRange.nEndPos; 1664 pView->SetSelection(aSelRange); 1665 } 1666 } 1667 1668 if(!bNoInitialLetter) 1669 { 1670 const sal_Int32 nMinLen = std::max(aSel.nEndPos - aSel.nStartPos, sal_Int32(1)); 1671 // Since transliteration service is used to test for match, the replaced string could be 1672 // longer than rInsert, so in order to find longest match before the cursor, test whole 1673 // string from start to current cursor position (don't limit to length of rInsert) 1674 // Disclaimer: I really don't know if a match longer than rInsert is actually possible, 1675 // so the above is based on assumptions how "transliteration" might possibly work. If 1676 // it's in fact impossible, an optimization would be useful to limit aSel.nStartPos to 1677 // std::max(sal_Int32(0), aSel.nEndPos - rInsert.getLength()). 1678 aSel.nStartPos = 0; 1679 pView->SetSelection(aSel); 1680 const OUString aAll = pView->GetSelected(); 1681 OUString aMatch; 1682 for (sal_Int32 n = aAll.getLength(); n >= nMinLen && aMatch.isEmpty(); --n) 1683 { 1684 const OUString aTest = aAll.copy(aAll.getLength() - n); // n trailing chars 1685 if (ScGlobal::GetTransliteration().isMatch(aTest, rInsert)) 1686 aMatch = aTest; // Found => break the loop 1687 } 1688 1689 aSel.nStartPos = aSel.nEndPos - aMatch.getLength(); 1690 pView->SetSelection(aSel); 1691 } 1692 1693 OUString aInsStr = rInsert; 1694 sal_Int32 nInsLen = aInsStr.getLength(); 1695 bool bDoParen = ( nInsLen > 1 && aInsStr[nInsLen-2] == '(' 1696 && aInsStr[nInsLen-1] == ')' ); 1697 if ( bDoParen ) 1698 { 1699 // Do not insert parentheses after function names if there already are some 1700 // (e.g. if the function name was edited). 1701 ESelection aWordSel = pView->GetSelection(); 1702 1703 // aWordSel.EndPos points one behind string if word at end 1704 if (aWordSel.nEndPos < aOld.getLength()) 1705 { 1706 sal_Unicode cNext = aOld[aWordSel.nEndPos]; 1707 if ( cNext == '(' ) 1708 { 1709 bDoParen = false; 1710 aInsStr = aInsStr.copy( 0, nInsLen - 2 ); // Skip parentheses 1711 } 1712 } 1713 } 1714 1715 pView->InsertText( aInsStr ); 1716 1717 if ( bDoParen ) // Put cursor between parentheses 1718 { 1719 aSel = pView->GetSelection(); 1720 --aSel.nStartPos; 1721 --aSel.nEndPos; 1722 pView->SetSelection(aSel); 1723 1724 rParInserted = true; 1725 } 1726 } 1727 1728 } 1729 1730 void ScInputHandler::PasteFunctionData() 1731 { 1732 if (pFormulaData && miAutoPosFormula != pFormulaData->end()) 1733 { 1734 const ScTypedStrData& rData = *miAutoPosFormula; 1735 OUString aInsert = rData.GetString(); 1736 if (aInsert[aInsert.getLength()-1] == cParenthesesReplacement) 1737 aInsert = OUString::Concat(aInsert.subView( 0, aInsert.getLength()-1)) + "()"; 1738 bool bParInserted = false; 1739 1740 DataChanging(); // Cannot be new 1741 completeFunction( pTopView, aInsert, bParInserted ); 1742 completeFunction( pTableView, aInsert, bParInserted ); 1743 DataChanged(); 1744 ShowTipCursor(); 1745 1746 if (bParInserted) 1747 AutoParAdded(); 1748 } 1749 1750 HideTip(); 1751 1752 EditView* pActiveView = pTopView ? pTopView : pTableView; 1753 if (comphelper::LibreOfficeKit::isActive() && pTopView && pInputWin) 1754 pInputWin->TextGrabFocus(); 1755 if (pActiveView) 1756 pActiveView->ShowCursor(); 1757 } 1758 1759 void ScInputHandler::LOKPasteFunctionData(const OUString& rFunctionName) 1760 { 1761 // in case we have no top view try to create it 1762 if (!pTopView && pInputWin) 1763 { 1764 ScInputMode eCurMode = eMode; 1765 SetMode(SC_INPUT_TOP); 1766 if (!pTopView) 1767 SetMode(eCurMode); 1768 } 1769 1770 EditView* pEditView = pTopView ? pTopView : pTableView; 1771 1772 if (!pActiveViewSh || !pEditView) 1773 return; 1774 1775 bool bEdit = false; 1776 OUString aFormula; 1777 const EditEngine* pEditEngine = pEditView->GetEditEngine(); 1778 if (pEditEngine) 1779 { 1780 aFormula = pEditEngine->GetText(0); 1781 /* TODO: LOK: are you sure you want '+' and '-' let start formulas with 1782 * function names? That was meant for "data typist" numeric keyboard 1783 * input. */ 1784 bEdit = aFormula.getLength() > 1 && (aFormula[0] == '=' || aFormula[0] == '+' || aFormula[0] == '-'); 1785 } 1786 1787 if ( !bEdit ) 1788 { 1789 OUString aNewFormula('='); 1790 if ( aFormula.startsWith("=") ) 1791 aNewFormula = aFormula; 1792 1793 InputReplaceSelection( aNewFormula ); 1794 } 1795 1796 if (pFormulaData) 1797 { 1798 OUString aNew; 1799 ScTypedCaseStrSet::const_iterator aPos = findText(*pFormulaData, pFormulaData->begin(), rFunctionName, aNew, /* backward = */false); 1800 1801 if (aPos != pFormulaData->end()) 1802 { 1803 miAutoPosFormula = aPos; 1804 PasteFunctionData(); 1805 } 1806 } 1807 } 1808 1809 void ScInputHandler::LOKSendFormulabarUpdate(EditView* pActiveView, 1810 const SfxViewShell* pActiveViewSh, 1811 const OUString& rText, 1812 const ESelection& rSelection) 1813 { 1814 OUString aSelection; 1815 if (pActiveView) 1816 { 1817 aSelection = OUString::number(pActiveView->GetPosWithField(0, rSelection.nStartPos)) + ";" + 1818 OUString::number(pActiveView->GetPosWithField(0, rSelection.nEndPos)) + ";" + 1819 OUString::number(rSelection.nStartPara) + ";" + OUString::number(rSelection.nEndPara); 1820 } 1821 else 1822 { 1823 aSelection = OUString::number(rSelection.nStartPos) + ";" + OUString::number(rSelection.nEndPos) + ";" + 1824 OUString::number(rSelection.nStartPara) + ";" + OUString::number(rSelection.nEndPara); 1825 } 1826 1827 std::unique_ptr<jsdialog::ActionDataMap> pData = std::make_unique<jsdialog::ActionDataMap>(); 1828 (*pData)["action_type"] = "setText"; 1829 (*pData)["text"] = rText; 1830 (*pData)["selection"] = aSelection; 1831 1832 sal_uInt64 nCurrentShellId = reinterpret_cast<sal_uInt64>(pActiveViewSh); 1833 OUString sWindowId = OUString::number(nCurrentShellId) + "formulabar"; 1834 jsdialog::SendAction(sWindowId, "sc_input_window", std::move(pData)); 1835 } 1836 1837 // Calculate selection and display as tip help 1838 static OUString lcl_Calculate( const OUString& rFormula, ScDocument& rDoc, const ScAddress &rPos ) 1839 { 1840 //TODO: Merge with ScFormulaDlg::CalcValue and move into Document! 1841 // Quotation marks for Strings are only inserted here. 1842 1843 if(rFormula.isEmpty()) 1844 return OUString(); 1845 1846 std::optional<ScSimpleFormulaCalculator> pCalc( std::in_place, rDoc, rPos, rFormula, false ); 1847 1848 // FIXME: HACK! In order to not get a #REF! for ColRowNames, if a name is actually inserted as a Range 1849 // into the whole Formula, but is interpreted as a single cell reference when displaying it on its own 1850 bool bColRowName = pCalc->HasColRowName(); 1851 if ( bColRowName ) 1852 { 1853 // ColRowName in RPN code? 1854 if ( pCalc->GetCode()->GetCodeLen() <= 1 ) 1855 { // ==1: Single one is as a Parameter always a Range 1856 // ==0: It might be one, if ... 1857 OUString aBraced = "(" + rFormula + ")"; 1858 pCalc.emplace( rDoc, rPos, aBraced, false ); 1859 } 1860 else 1861 bColRowName = false; 1862 } 1863 1864 FormulaError nErrCode = pCalc->GetErrCode(); 1865 if ( nErrCode != FormulaError::NONE ) 1866 return ScGlobal::GetErrorString(nErrCode); 1867 1868 SvNumberFormatter& aFormatter = *rDoc.GetFormatTable(); 1869 OUString aValue; 1870 if ( pCalc->IsValue() ) 1871 { 1872 double n = pCalc->GetValue(); 1873 sal_uInt32 nFormat = aFormatter.GetStandardFormat( n, 0, 1874 pCalc->GetFormatType(), ScGlobal::eLnge ); 1875 aFormatter.GetInputLineString( n, nFormat, aValue ); 1876 //! display OutputString but insert InputLineString 1877 } 1878 else 1879 { 1880 OUString aStr = pCalc->GetString().getString(); 1881 sal_uInt32 nFormat = aFormatter.GetStandardFormat( 1882 pCalc->GetFormatType(), ScGlobal::eLnge); 1883 { 1884 const Color* pColor; 1885 aFormatter.GetOutputString( aStr, nFormat, 1886 aValue, &pColor ); 1887 } 1888 1889 aValue = "\"" + aValue + "\""; 1890 //! Escape quotation marks in String?? 1891 } 1892 1893 ScRange aTestRange; 1894 if ( bColRowName || (aTestRange.Parse(rFormula, rDoc) & ScRefFlags::VALID) ) 1895 aValue += " ..."; 1896 1897 return aValue; 1898 } 1899 1900 void ScInputHandler::FormulaPreview() 1901 { 1902 OUString aValue; 1903 EditView* pActiveView = pTopView ? pTopView : pTableView; 1904 if ( pActiveView && pActiveViewSh ) 1905 { 1906 OUString aPart = pActiveView->GetSelected(); 1907 if (aPart.isEmpty()) 1908 aPart = mpEditEngine->GetText(0); 1909 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); 1910 aValue = lcl_Calculate( aPart, rDoc, aCursorPos ); 1911 } 1912 1913 if (!aValue.isEmpty()) 1914 { 1915 ShowTip( aValue ); // Display as QuickHelp 1916 aManualTip = aValue; // Set after ShowTip 1917 if (pFormulaData) 1918 miAutoPosFormula = pFormulaData->end(); 1919 if (pColumnData) 1920 miAutoPosColumn = pColumnData->end(); 1921 } 1922 } 1923 1924 void ScInputHandler::PasteManualTip() 1925 { 1926 // Three dots at the end -> Range reference -> do not insert 1927 // FIXME: Once we have matrix constants, we can change this 1928 sal_Int32 nTipLen = aManualTip.getLength(); 1929 sal_uInt32 const nTipLen2(sal::static_int_cast<sal_uInt32>(nTipLen)); 1930 if ( nTipLen && ( nTipLen < 3 || aManualTip.subView( nTipLen2-3 ) != u"..." ) ) 1931 { 1932 DataChanging(); // Cannot be new 1933 1934 OUString aInsert = aManualTip; 1935 EditView* pActiveView = pTopView ? pTopView : pTableView; 1936 if (!pActiveView->HasSelection()) 1937 { 1938 // Nothing selected -> select everything 1939 sal_Int32 nOldLen = mpEditEngine->GetTextLen(0); 1940 ESelection aAllSel( 0, 0, 0, nOldLen ); 1941 if ( pTopView ) 1942 pTopView->SetSelection( aAllSel ); 1943 if ( pTableView ) 1944 pTableView->SetSelection( aAllSel ); 1945 } 1946 1947 ESelection aSel = pActiveView->GetSelection(); 1948 aSel.Adjust(); 1949 OSL_ENSURE( !aSel.nStartPara && !aSel.nEndPara, "Too many paragraphs in Formula" ); 1950 if ( !aSel.nStartPos ) // Selection from the start? 1951 { 1952 if ( aSel.nEndPos == mpEditEngine->GetTextLen(0) ) 1953 { 1954 // Everything selected -> skip quotation marks 1955 if ( aInsert[0] == '"' ) 1956 aInsert = aInsert.copy(1); 1957 sal_Int32 nInsLen = aInsert.getLength(); 1958 if ( aInsert.endsWith("\"") ) 1959 aInsert = aInsert.copy( 0, nInsLen-1 ); 1960 } 1961 else if ( aSel.nEndPos ) 1962 { 1963 // Not everything selected -> do not overwrite equality sign 1964 //FIXME: Even double equality signs?? 1965 aSel.nStartPos = 1; 1966 if ( pTopView ) 1967 pTopView->SetSelection( aSel ); 1968 if ( pTableView ) 1969 pTableView->SetSelection( aSel ); 1970 } 1971 } 1972 if ( pTopView ) 1973 pTopView->InsertText( aInsert, true ); 1974 if ( pTableView ) 1975 pTableView->InsertText( aInsert, true ); 1976 1977 DataChanged(); 1978 } 1979 1980 HideTip(); 1981 } 1982 1983 void ScInputHandler::ResetAutoPar() 1984 { 1985 nAutoPar = 0; 1986 } 1987 1988 void ScInputHandler::AutoParAdded() 1989 { 1990 ++nAutoPar; // Closing parenthesis can be overwritten 1991 } 1992 1993 bool ScInputHandler::CursorAtClosingPar() 1994 { 1995 // Test if the cursor is before a closing parenthesis 1996 // Selection from SetReference has been removed before 1997 EditView* pActiveView = pTopView ? pTopView : pTableView; 1998 if ( pActiveView && !pActiveView->HasSelection() && bFormulaMode ) 1999 { 2000 ESelection aSel = pActiveView->GetSelection(); 2001 sal_Int32 nPos = aSel.nStartPos; 2002 OUString aFormula = mpEditEngine->GetText(0); 2003 if ( nPos < aFormula.getLength() && aFormula[nPos] == ')' ) 2004 return true; 2005 } 2006 return false; 2007 } 2008 2009 void ScInputHandler::SkipClosingPar() 2010 { 2011 // this is called when a ')' is typed and the cursor is before a ')' 2012 // that can be overwritten -> just set the cursor behind the ')' 2013 2014 EditView* pActiveView = pTopView ? pTopView : pTableView; 2015 if (pActiveView) 2016 { 2017 ESelection aSel = pActiveView->GetSelection(); 2018 ++aSel.nStartPos; 2019 ++aSel.nEndPos; 2020 2021 // this is in a formula (only one paragraph), so the selection 2022 // can be used directly for the TopView 2023 2024 if ( pTopView ) 2025 pTopView->SetSelection( aSel ); 2026 if ( pTableView ) 2027 pTableView->SetSelection( aSel ); 2028 } 2029 2030 OSL_ENSURE(nAutoPar, "SkipClosingPar: count is wrong"); 2031 --nAutoPar; 2032 } 2033 2034 // Auto input 2035 2036 void ScInputHandler::GetColData() 2037 { 2038 if ( !pActiveViewSh ) 2039 return; 2040 2041 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); 2042 2043 if ( pColumnData ) 2044 pColumnData->clear(); 2045 else 2046 pColumnData.reset( new ScTypedCaseStrSet ); 2047 2048 std::vector<ScTypedStrData> aEntries; 2049 rDoc.GetDataEntries( 2050 aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), aEntries); 2051 if (!aEntries.empty()) 2052 pColumnData->insert(aEntries.begin(), aEntries.end()); 2053 2054 miAutoPosColumn = pColumnData->end(); 2055 } 2056 2057 void ScInputHandler::UseColData() // When typing 2058 { 2059 EditView* pActiveView = pTopView ? pTopView : pTableView; 2060 if ( !(pActiveView && pColumnData) ) 2061 return; 2062 2063 // Only change when cursor is at the end 2064 ESelection aSel = pActiveView->GetSelection(); 2065 aSel.Adjust(); 2066 2067 sal_Int32 nParCnt = mpEditEngine->GetParagraphCount(); 2068 if ( aSel.nEndPara+1 != nParCnt ) 2069 return; 2070 2071 sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.nEndPara ); 2072 if ( aSel.nEndPos != nParLen ) 2073 return; 2074 2075 OUString aText = GetEditText(mpEditEngine.get()); 2076 if (aText.isEmpty()) 2077 return; 2078 2079 std::vector< OUString > aResultVec; 2080 OUString aNew; 2081 sal_Int32 nLongestPrefixLen = 0; 2082 miAutoPosColumn = pColumnData->end(); 2083 mbPartialPrefix = false; 2084 miAutoPosColumn = findTextAll(*pColumnData, miAutoPosColumn, aText, aResultVec, false, &nLongestPrefixLen); 2085 2086 if (nLongestPrefixLen <= 0 || aResultVec.empty()) 2087 return; 2088 2089 if (aResultVec.size() > 1) 2090 { 2091 mbPartialPrefix = true; 2092 bUseTab = true; // Allow Ctrl (+ Shift + ) + TAB cycling. 2093 miAutoPosColumn = pColumnData->end(); 2094 2095 // Display the rest of longest common prefix as suggestion. 2096 aNew = aResultVec[0].copy(0, nLongestPrefixLen); 2097 } 2098 else 2099 { 2100 aNew = aResultVec[0]; 2101 } 2102 2103 // Strings can contain line endings (e.g. due to dBase import), 2104 // which would result in multiple paragraphs here, which is not desirable. 2105 //! Then GetExactMatch doesn't work either 2106 lcl_RemoveLineEnd( aNew ); 2107 2108 // Keep paragraph, just append the rest 2109 //! Exact replacement in EnterHandler !!! 2110 // One Space between paragraphs: 2111 sal_Int32 nEdLen = mpEditEngine->GetTextLen() + nParCnt - 1; 2112 OUString aIns = aNew.copy(nEdLen); 2113 2114 // Selection must be "backwards", so the cursor stays behind the last 2115 // typed character 2116 ESelection aSelection( aSel.nEndPara, aSel.nEndPos + aIns.getLength(), 2117 aSel.nEndPara, aSel.nEndPos ); 2118 2119 // When editing in input line, apply to both edit views 2120 if ( pTableView ) 2121 { 2122 pTableView->InsertText( aIns ); 2123 pTableView->SetSelection( aSelection ); 2124 } 2125 if ( pTopView ) 2126 { 2127 pTopView->InsertText( aIns ); 2128 pTopView->SetSelection( aSelection ); 2129 } 2130 2131 aAutoSearch = aText; // To keep searching - nAutoPos is set 2132 } 2133 2134 void ScInputHandler::NextAutoEntry( bool bBack ) 2135 { 2136 EditView* pActiveView = pTopView ? pTopView : pTableView; 2137 if ( pActiveView && pColumnData ) 2138 { 2139 if (!aAutoSearch.isEmpty()) 2140 { 2141 // Is the selection still valid (could be changed via the mouse)? 2142 ESelection aSel = pActiveView->GetSelection(); 2143 aSel.Adjust(); 2144 sal_Int32 nParCnt = mpEditEngine->GetParagraphCount(); 2145 if ( aSel.nEndPara+1 == nParCnt && aSel.nStartPara == aSel.nEndPara ) 2146 { 2147 OUString aText = GetEditText(mpEditEngine.get()); 2148 sal_Int32 nSelLen = aSel.nEndPos - aSel.nStartPos; 2149 sal_Int32 nParLen = mpEditEngine->GetTextLen( aSel.nEndPara ); 2150 if ( aSel.nEndPos == nParLen && aText.getLength() == aAutoSearch.getLength() + nSelLen ) 2151 { 2152 OUString aNew; 2153 ScTypedCaseStrSet::const_iterator itNew = 2154 findText(*pColumnData, miAutoPosColumn, aAutoSearch, aNew, bBack); 2155 2156 if (itNew != pColumnData->end()) 2157 { 2158 // match found! 2159 miAutoPosColumn = itNew; 2160 bInOwnChange = true; // disable ModifyHdl (reset below) 2161 mbPartialPrefix = false; 2162 2163 lcl_RemoveLineEnd( aNew ); 2164 OUString aIns = aNew.copy(aAutoSearch.getLength()); 2165 2166 // when editing in input line, apply to both edit views 2167 if ( pTableView ) 2168 { 2169 pTableView->DeleteSelected(); 2170 pTableView->InsertText( aIns ); 2171 pTableView->SetSelection( ESelection( 2172 aSel.nEndPara, aSel.nStartPos + aIns.getLength(), 2173 aSel.nEndPara, aSel.nStartPos ) ); 2174 } 2175 if ( pTopView ) 2176 { 2177 pTopView->DeleteSelected(); 2178 pTopView->InsertText( aIns ); 2179 pTopView->SetSelection( ESelection( 2180 aSel.nEndPara, aSel.nStartPos + aIns.getLength(), 2181 aSel.nEndPara, aSel.nStartPos ) ); 2182 } 2183 2184 bInOwnChange = false; 2185 } 2186 } 2187 } 2188 } 2189 } 2190 2191 // For Tab, HideCursor was always called first 2192 if (pActiveView) 2193 pActiveView->ShowCursor(); 2194 } 2195 2196 // Highlight parentheses 2197 void ScInputHandler::UpdateParenthesis() 2198 { 2199 // Find parentheses 2200 //TODO: Can we disable parentheses highlighting per parentheses? 2201 bool bFound = false; 2202 if ( bFormulaMode && eMode != SC_INPUT_TOP ) 2203 { 2204 if ( pTableView && !pTableView->HasSelection() ) // Selection is always at the bottom 2205 { 2206 ESelection aSel = pTableView->GetSelection(); 2207 if (aSel.nStartPos) 2208 { 2209 // Examine character left to the cursor 2210 sal_Int32 nPos = aSel.nStartPos - 1; 2211 OUString aFormula = mpEditEngine->GetText(aSel.nStartPara); 2212 sal_Unicode c = aFormula[nPos]; 2213 if ( c == '(' || c == ')' ) 2214 { 2215 // Note this matches only within one paragraph. 2216 sal_Int32 nOther = lcl_MatchParenthesis( aFormula, nPos ); 2217 if ( nOther != -1 ) 2218 { 2219 SfxItemSet aSet( mpEditEngine->GetEmptyItemSet() ); 2220 aSet.Put( SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT ) ); 2221 2222 //! Distinguish if cell is already highlighted!!!! 2223 if (bParenthesisShown) 2224 { 2225 // Remove old highlighting 2226 sal_Int32 nCount = mpEditEngine->GetParagraphCount(); 2227 for (sal_Int32 i=0; i<nCount; i++) 2228 mpEditEngine->RemoveCharAttribs( i, EE_CHAR_WEIGHT ); 2229 } 2230 2231 ESelection aSelThis( aSel.nStartPara, nPos, aSel.nStartPara, nPos+1); 2232 mpEditEngine->QuickSetAttribs( aSet, aSelThis ); 2233 ESelection aSelOther( aSel.nStartPara, nOther, aSel.nStartPara, nOther+1); 2234 mpEditEngine->QuickSetAttribs( aSet, aSelOther ); 2235 2236 // Dummy InsertText for Update and Paint (selection is empty) 2237 pTableView->InsertText( OUString() ); 2238 2239 bFound = true; 2240 } 2241 } 2242 } 2243 2244 // mark parenthesis right of cursor if it will be overwritten (nAutoPar) 2245 // with different color (COL_LIGHTBLUE) ?? 2246 } 2247 } 2248 2249 // Remove old highlighting, if no new one is set 2250 if ( bParenthesisShown && !bFound && pTableView ) 2251 { 2252 sal_Int32 nCount = mpEditEngine->GetParagraphCount(); 2253 for (sal_Int32 i=0; i<nCount; i++) 2254 pTableView->RemoveCharAttribs( i, EE_CHAR_WEIGHT ); 2255 } 2256 2257 bParenthesisShown = bFound; 2258 } 2259 2260 void ScInputHandler::ViewShellGone(const ScTabViewShell* pViewSh) // Executed synchronously! 2261 { 2262 if ( pViewSh == pActiveViewSh ) 2263 { 2264 pLastState.reset(); 2265 pLastPattern = nullptr; 2266 } 2267 2268 if ( pViewSh == pRefViewSh ) 2269 { 2270 //! The input from the EnterHandler does not arrive anymore 2271 // We end the EditMode anyways 2272 EnterHandler(); 2273 bFormulaMode = false; 2274 pRefViewSh = nullptr; 2275 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); 2276 SC_MOD()->SetRefInputHdl(nullptr); 2277 if (pInputWin) 2278 pInputWin->SetFormulaMode(false); 2279 UpdateAutoCorrFlag(); 2280 } 2281 2282 pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); 2283 2284 if ( pActiveViewSh && pActiveViewSh == pViewSh ) 2285 { 2286 OSL_FAIL("pActiveViewSh is gone"); 2287 pActiveViewSh = nullptr; 2288 } 2289 2290 if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() ) 2291 UpdateRefDevice(); // Don't keep old document's printer as RefDevice 2292 } 2293 2294 void ScInputHandler::UpdateActiveView() 2295 { 2296 ImplCreateEditEngine(); 2297 2298 // #i20588# Don't rely on focus to find the active edit view. Instead, the 2299 // active pane at the start of editing is now stored (GetEditActivePart). 2300 // GetActiveWin (the currently active pane) fails for ref input across the 2301 // panes of a split view. 2302 2303 vcl::Window* pShellWin = pActiveViewSh ? 2304 pActiveViewSh->GetWindowByPos( pActiveViewSh->GetViewData().GetEditActivePart() ) : 2305 nullptr; 2306 2307 sal_uInt16 nCount = mpEditEngine->GetViewCount(); 2308 if (nCount > 0) 2309 { 2310 pTableView = mpEditEngine->GetView(); 2311 for (sal_uInt16 i=1; i<nCount; i++) 2312 { 2313 EditView* pThis = mpEditEngine->GetView(i); 2314 vcl::Window* pWin = pThis->GetWindow(); 2315 if ( pWin==pShellWin ) 2316 pTableView = pThis; 2317 } 2318 } 2319 else 2320 pTableView = nullptr; 2321 2322 // setup the pTableView editeng for tiled rendering to get cursor and selections 2323 if (pTableView && pActiveViewSh) 2324 { 2325 if (comphelper::LibreOfficeKit::isActive()) 2326 { 2327 pTableView->RegisterViewShell(pActiveViewSh); 2328 } 2329 } 2330 2331 if (pInputWin && (eMode == SC_INPUT_TOP || eMode == SC_INPUT_TABLE)) 2332 { 2333 // tdf#71409: Always create the edit engine instance for the input 2334 // window, in order to properly manage accessibility events. 2335 pTopView = pInputWin->GetEditView(); 2336 if (eMode != SC_INPUT_TOP) 2337 pTopView = nullptr; 2338 } 2339 else 2340 pTopView = nullptr; 2341 } 2342 2343 void ScInputHandler::SetInputWindow( ScInputWindow* pNew ) 2344 { 2345 pInputWin = pNew; 2346 } 2347 2348 void ScInputHandler::StopInputWinEngine( bool bAll ) 2349 { 2350 if (pInputWin && !pInputWin->isDisposed()) 2351 pInputWin->StopEditEngine( bAll ); 2352 2353 pTopView = nullptr; // invalid now 2354 } 2355 2356 EditView* ScInputHandler::GetActiveView() 2357 { 2358 UpdateActiveView(); 2359 return pTopView ? pTopView : pTableView; 2360 } 2361 2362 void ScInputHandler::ForgetLastPattern() 2363 { 2364 pLastPattern = nullptr; 2365 if ( !pLastState && pActiveViewSh ) 2366 pActiveViewSh->UpdateInputHandler( true ); // Get status again 2367 else 2368 NotifyChange( pLastState.get(), true ); 2369 } 2370 2371 void ScInputHandler::UpdateAdjust( sal_Unicode cTyped ) 2372 { 2373 SvxAdjust eSvxAdjust; 2374 switch (eAttrAdjust) 2375 { 2376 case SvxCellHorJustify::Standard: 2377 { 2378 bool bNumber = false; 2379 if (cTyped) // Restarted 2380 bNumber = (cTyped>='0' && cTyped<='9'); // Only ciphers are numbers 2381 else if ( pActiveViewSh ) 2382 { 2383 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); 2384 bNumber = ( rDoc.GetCellType( aCursorPos ) == CELLTYPE_VALUE ); 2385 } 2386 eSvxAdjust = bNumber ? SvxAdjust::Right : SvxAdjust::Left; 2387 } 2388 break; 2389 case SvxCellHorJustify::Block: 2390 eSvxAdjust = SvxAdjust::Block; 2391 break; 2392 case SvxCellHorJustify::Center: 2393 eSvxAdjust = SvxAdjust::Center; 2394 break; 2395 case SvxCellHorJustify::Right: 2396 eSvxAdjust = SvxAdjust::Right; 2397 break; 2398 default: // SvxCellHorJustify::Left 2399 eSvxAdjust = SvxAdjust::Left; 2400 break; 2401 } 2402 2403 bool bAsianVertical = pLastPattern && 2404 pLastPattern->GetItem( ATTR_STACKED ).GetValue() && 2405 pLastPattern->GetItem( ATTR_VERTICAL_ASIAN ).GetValue(); 2406 if ( bAsianVertical ) 2407 { 2408 // Always edit at top of cell -> LEFT when editing vertically 2409 eSvxAdjust = SvxAdjust::Left; 2410 } 2411 2412 pEditDefaults->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) ); 2413 mpEditEngine->SetDefaults( *pEditDefaults ); 2414 2415 if ( pActiveViewSh ) 2416 { 2417 pActiveViewSh->GetViewData().SetEditAdjust( eSvxAdjust ); 2418 } 2419 mpEditEngine->SetVertical( bAsianVertical ); 2420 } 2421 2422 void ScInputHandler::RemoveAdjust() 2423 { 2424 // Delete hard alignment attributes 2425 bool bUndo = mpEditEngine->IsUndoEnabled(); 2426 if ( bUndo ) 2427 mpEditEngine->EnableUndo( false ); 2428 2429 // Non-default paragraph attributes (e.g. from clipboard) 2430 // must be turned into character attributes 2431 mpEditEngine->RemoveParaAttribs(); 2432 2433 if ( bUndo ) 2434 mpEditEngine->EnableUndo( true ); 2435 2436 } 2437 2438 void ScInputHandler::RemoveRangeFinder() 2439 { 2440 // Delete pRangeFindList and colors 2441 mpEditEngine->SetUpdateLayout(false); 2442 sal_Int32 nCount = mpEditEngine->GetParagraphCount(); // Could just have been inserted 2443 for (sal_Int32 i=0; i<nCount; i++) 2444 mpEditEngine->RemoveCharAttribs( i, EE_CHAR_COLOR ); 2445 mpEditEngine->SetUpdateLayout(true); 2446 2447 EditView* pActiveView = pTopView ? pTopView : pTableView; 2448 pActiveView->ShowCursor( false ); 2449 2450 DeleteRangeFinder(); // Deletes the list and the labels on the table 2451 } 2452 2453 bool ScInputHandler::StartTable( sal_Unicode cTyped, bool bFromCommand, bool bInputActivated, 2454 ScEditEngineDefaulter* pTopEngine ) 2455 { 2456 bool bNewTable = false; 2457 2458 if (bModified) 2459 return false; 2460 2461 if (pActiveViewSh) 2462 { 2463 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocShell()->GetDocument(); 2464 2465 if (!rDoc.ValidCol(aCursorPos.Col())) 2466 return false; 2467 2468 ImplCreateEditEngine(); 2469 UpdateActiveView(); 2470 SyncViews(); 2471 2472 2473 const ScMarkData& rMark = pActiveViewSh->GetViewData().GetMarkData(); 2474 ScEditableTester aTester; 2475 if ( rMark.IsMarked() || rMark.IsMultiMarked() ) 2476 aTester.TestSelection( rDoc, rMark ); 2477 else 2478 aTester.TestSelectedBlock( 2479 rDoc, aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Col(), aCursorPos.Row(), rMark ); 2480 2481 bool bStartInputMode = true; 2482 2483 if (!aTester.IsEditable()) 2484 { 2485 bProtected = true; 2486 // We allow read-only input mode activation regardless 2487 // whether it's part of an array or not or whether explicit cell 2488 // activation is requested (double-click or F2) or a click in input 2489 // line. 2490 bool bShowError = (!bInputActivated || !aTester.GetMessageId() || aTester.GetMessageId() != STR_PROTECTIONERR) && 2491 !pActiveViewSh->GetViewData().GetDocShell()->IsReadOnly(); 2492 if (bShowError) 2493 { 2494 eMode = SC_INPUT_NONE; 2495 StopInputWinEngine( true ); 2496 UpdateFormulaMode(); 2497 if ( pActiveViewSh && ( !bFromCommand || !bCommandErrorShown ) ) 2498 { 2499 // Prevent repeated error messages for the same cell from command events 2500 // (for keyboard events, multiple messages are wanted). 2501 // Set the flag before showing the error message because the command handler 2502 // for the next IME command may be called when showing the dialog. 2503 if ( bFromCommand ) 2504 bCommandErrorShown = true; 2505 2506 pActiveViewSh->GetActiveWin()->GrabFocus(); 2507 pActiveViewSh->ErrorMessage(aTester.GetMessageId()); 2508 } 2509 bStartInputMode = false; 2510 } 2511 } 2512 2513 if (bStartInputMode) 2514 { 2515 // UpdateMode is enabled again in ScViewData::SetEditEngine (and not needed otherwise) 2516 mpEditEngine->SetUpdateLayout( false ); 2517 2518 // Take over attributes in EditEngine 2519 const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), 2520 aCursorPos.Row(), 2521 aCursorPos.Tab() ); 2522 if (pPattern != pLastPattern) 2523 { 2524 // Percent format? 2525 const SfxItemSet& rAttrSet = pPattern->GetItemSet(); 2526 2527 if ( const SfxUInt32Item* pItem = rAttrSet.GetItemIfSet( ATTR_VALUE_FORMAT ) ) 2528 { 2529 sal_uInt32 nFormat = pItem->GetValue(); 2530 if (SvNumFormatType::PERCENT == rDoc.GetFormatTable()->GetType( nFormat )) 2531 nCellPercentFormatDecSep = rDoc.GetFormatTable()->GetFormatDecimalSep( nFormat).toChar(); 2532 else 2533 nCellPercentFormatDecSep = 0; 2534 } 2535 else 2536 nCellPercentFormatDecSep = 0; // Default: no percent 2537 2538 // Validity specified? 2539 if ( const SfxUInt32Item* pItem = rAttrSet.GetItemIfSet( ATTR_VALIDDATA ) ) 2540 nValidation = pItem->GetValue(); 2541 else 2542 nValidation = 0; 2543 2544 // EditEngine Defaults 2545 // In no case SetParaAttribs, because the EditEngine might already 2546 // be filled (for Edit cells). 2547 // SetParaAttribs would change the content. 2548 2549 //! The SetDefaults is now (since MUST/src602 2550 //! EditEngine changes) implemented as a SetParaAttribs. 2551 //! Any problems? 2552 2553 pPattern->FillEditItemSet( pEditDefaults.get() ); 2554 mpEditEngine->SetDefaults( *pEditDefaults ); 2555 pLastPattern = pPattern; 2556 bLastIsSymbol = pPattern->IsSymbolFont(); 2557 2558 // Background color must be known for automatic font color. 2559 // For transparent cell background, the document background color must be used. 2560 2561 Color aBackCol = pPattern->GetItem( ATTR_BACKGROUND ).GetColor(); 2562 ScModule* pScMod = SC_MOD(); 2563 if ( aBackCol.IsTransparent() || 2564 Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) 2565 aBackCol = pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor; 2566 mpEditEngine->SetBackgroundColor( aBackCol ); 2567 2568 // Adjustment 2569 eAttrAdjust = pPattern->GetItem(ATTR_HOR_JUSTIFY).GetValue(); 2570 if ( eAttrAdjust == SvxCellHorJustify::Repeat && 2571 pPattern->GetItem(ATTR_LINEBREAK).GetValue() ) 2572 { 2573 // #i31843# "repeat" with "line breaks" is treated as default alignment 2574 eAttrAdjust = SvxCellHorJustify::Standard; 2575 } 2576 } 2577 2578 if (pTopEngine) 2579 { 2580 // Necessary to sync SvxAutoCorrect behavior. This has to be 2581 // done before InitRangeFinder() below. 2582 MergeLanguageAttributes( *pTopEngine); 2583 } 2584 2585 // UpdateSpellSettings enables online spelling if needed 2586 // -> also call if attributes are unchanged 2587 UpdateSpellSettings( true ); // uses pLastPattern 2588 2589 // Fill EditEngine 2590 OUString aStr; 2591 if (bTextValid) 2592 { 2593 mpEditEngine->SetTextCurrentDefaults(aCurrentText); 2594 aStr = aCurrentText; 2595 bTextValid = false; 2596 aCurrentText.clear(); 2597 } 2598 else 2599 aStr = GetEditText(mpEditEngine.get()); 2600 2601 // cTyped!=0 is overtyping, not editing. 2602 mbEditingExistingContent = !cTyped && !aStr.isEmpty(); 2603 2604 if (aStr.startsWith("{=") && aStr.endsWith("}") ) // Matrix formula? 2605 { 2606 aStr = aStr.copy(1, aStr.getLength() -2); 2607 mpEditEngine->SetTextCurrentDefaults(aStr); 2608 if ( pInputWin ) 2609 pInputWin->SetTextString(aStr); 2610 } 2611 2612 UpdateAdjust( cTyped ); 2613 2614 if ( SC_MOD()->GetAppOptions().GetAutoComplete() ) 2615 GetColData(); 2616 2617 if (!cTyped && !bCreatingFuncView && StartsLikeFormula(aStr)) 2618 InitRangeFinder(aStr); // Formula is being edited -> RangeFinder 2619 2620 bNewTable = true; // -> PostEditView Call 2621 } 2622 } 2623 2624 if (!bProtected && pInputWin) 2625 pInputWin->SetOkCancelMode(); 2626 2627 return bNewTable; 2628 } 2629 2630 void ScInputHandler::MergeLanguageAttributes( ScEditEngineDefaulter& rDestEngine ) const 2631 { 2632 const SfxItemSet& rSrcSet = mpEditEngine->GetDefaults(); 2633 rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE )); 2634 rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CJK )); 2635 rDestEngine.SetDefaultItem( rSrcSet.Get( EE_CHAR_LANGUAGE_CTL )); 2636 } 2637 2638 static void lcl_SetTopSelection( EditView* pEditView, ESelection& rSel ) 2639 { 2640 OSL_ENSURE( rSel.nStartPara==0 && rSel.nEndPara==0, "SetTopSelection: Para != 0" ); 2641 2642 EditEngine* pEngine = pEditView->GetEditEngine(); 2643 sal_Int32 nCount = pEngine->GetParagraphCount(); 2644 if (nCount > 1) 2645 { 2646 sal_Int32 nParLen = pEngine->GetTextLen(rSel.nStartPara); 2647 while (rSel.nStartPos > nParLen && rSel.nStartPara+1 < nCount) 2648 { 2649 rSel.nStartPos -= nParLen + 1; // Including space from line break 2650 nParLen = pEngine->GetTextLen(++rSel.nStartPara); 2651 } 2652 2653 nParLen = pEngine->GetTextLen(rSel.nEndPara); 2654 while (rSel.nEndPos > nParLen && rSel.nEndPara+1 < nCount) 2655 { 2656 rSel.nEndPos -= nParLen + 1; // Including space from line break 2657 nParLen = pEngine->GetTextLen(++rSel.nEndPara); 2658 } 2659 } 2660 2661 ESelection aSel = pEditView->GetSelection(); 2662 2663 if ( rSel.nStartPara != aSel.nStartPara || rSel.nEndPara != aSel.nEndPara 2664 || rSel.nStartPos != aSel.nStartPos || rSel.nEndPos != aSel.nEndPos ) 2665 pEditView->SetSelection( rSel ); 2666 } 2667 2668 void ScInputHandler::SyncViews( const EditView* pSourceView ) 2669 { 2670 if (pSourceView) 2671 { 2672 bool bSelectionForTopView = false; 2673 if (pTopView && pTopView != pSourceView) 2674 bSelectionForTopView = true; 2675 bool bSelectionForTableView = false; 2676 if (pTableView && pTableView != pSourceView) 2677 bSelectionForTableView = true; 2678 if (bSelectionForTopView || bSelectionForTableView) 2679 { 2680 ESelection aSel(pSourceView->GetSelection()); 2681 if (bSelectionForTopView) 2682 pTopView->SetSelection(aSel); 2683 if (bSelectionForTableView) 2684 lcl_SetTopSelection(pTableView, aSel); 2685 } 2686 } 2687 // Only sync selection from topView if we are actually editing there 2688 else if (pTopView && pTableView) 2689 { 2690 ESelection aSel(pTopView->GetSelection()); 2691 lcl_SetTopSelection( pTableView, aSel ); 2692 } 2693 } 2694 2695 IMPL_LINK_NOARG(ScInputHandler, ModifyHdl, LinkParamNone*, void) 2696 { 2697 if ( !bInOwnChange && ( eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE ) && 2698 mpEditEngine && mpEditEngine->IsUpdateLayout() && pInputWin ) 2699 { 2700 // Update input line from ModifyHdl for changes that are not 2701 // wrapped by DataChanging/DataChanged calls (like Drag&Drop) 2702 OUString aText(ScEditUtil::GetMultilineString(*mpEditEngine)); 2703 lcl_RemoveTabs(aText); 2704 pInputWin->SetTextString(aText); 2705 } 2706 } 2707 2708 /** 2709 * @return true means new view created 2710 */ 2711 bool ScInputHandler::DataChanging( sal_Unicode cTyped, bool bFromCommand ) 2712 { 2713 if (pActiveViewSh) 2714 pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE ); 2715 bInOwnChange = true; // disable ModifyHdl (reset in DataChanged) 2716 2717 if ( eMode == SC_INPUT_NONE ) 2718 return StartTable( cTyped, bFromCommand, false, nullptr ); 2719 else 2720 return false; 2721 } 2722 2723 void ScInputHandler::DataChanged( bool bFromTopNotify, bool bSetModified ) 2724 { 2725 ImplCreateEditEngine(); 2726 2727 if (eMode==SC_INPUT_NONE) 2728 eMode = SC_INPUT_TYPE; 2729 2730 if ( eMode == SC_INPUT_TOP && pTopView && !bFromTopNotify ) 2731 { 2732 // table EditEngine is formatted below, input line needs formatting after paste 2733 // #i20282# not when called from the input line's modify handler 2734 pTopView->GetEditEngine()->QuickFormatDoc( true ); 2735 2736 // #i23720# QuickFormatDoc hides the cursor, but can't show it again because it 2737 // can't safely access the EditEngine's current view, so the cursor has to be 2738 // shown again here. 2739 pTopView->ShowCursor(); 2740 } 2741 2742 if (bSetModified) 2743 bModified = true; 2744 bSelIsRef = false; 2745 2746 if ( pRangeFindList && !bInRangeUpdate ) 2747 RemoveRangeFinder(); // Delete attributes and labels 2748 2749 UpdateParenthesis(); // Highlight parentheses anew 2750 2751 if (eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE) 2752 { 2753 OUString aText; 2754 if (pInputWin) 2755 aText = ScEditUtil::GetMultilineString(*mpEditEngine); 2756 else 2757 aText = GetEditText(mpEditEngine.get()); 2758 lcl_RemoveTabs(aText); 2759 2760 if ( pInputWin ) 2761 pInputWin->SetTextString( aText ); 2762 2763 if (comphelper::LibreOfficeKit::isActive()) 2764 { 2765 if (pActiveViewSh) 2766 { 2767 // TODO: deprecated? 2768 pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aText.toUtf8()); 2769 } 2770 } 2771 } 2772 2773 // If the cursor is before the end of a paragraph, parts are being pushed to 2774 // the right (independently from the eMode) -> Adapt View! 2775 // If the cursor is at the end, the StatusHandler of the ViewData is sufficient. 2776 // 2777 // First make sure the status handler is called now if the cursor 2778 // is outside the visible area 2779 mpEditEngine->QuickFormatDoc(); 2780 2781 EditView* pActiveView = pTopView ? pTopView : pTableView; 2782 ESelection aSel; 2783 if (pActiveView && pActiveViewSh) 2784 { 2785 ScViewData& rViewData = pActiveViewSh->GetViewData(); 2786 2787 bool bNeedGrow = ( rViewData.GetEditAdjust() != SvxAdjust::Left ); // Always right-aligned 2788 if (!bNeedGrow) 2789 { 2790 // Cursor before the end? 2791 aSel = pActiveView->GetSelection(); 2792 aSel.Adjust(); 2793 bNeedGrow = ( aSel.nEndPos != mpEditEngine->GetTextLen(aSel.nEndPara) ); 2794 } 2795 if (!bNeedGrow) 2796 { 2797 bNeedGrow = rViewData.GetDocument().IsLayoutRTL( rViewData.GetTabNo() ); 2798 } 2799 if (bNeedGrow) 2800 { 2801 // Adjust inplace view 2802 rViewData.EditGrowY(); 2803 rViewData.EditGrowX(); 2804 } 2805 } 2806 2807 if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh && pInputWin) 2808 { 2809 UpdateActiveView(); 2810 if (pActiveView) 2811 aSel = pActiveView->GetSelection(); 2812 2813 ScInputHandler::LOKSendFormulabarUpdate(pActiveView, pActiveViewSh, 2814 ScEditUtil::GetMultilineString(*mpEditEngine), 2815 aSel); 2816 } 2817 2818 UpdateFormulaMode(); 2819 bTextValid = false; // Changes only in the EditEngine 2820 bInOwnChange = false; 2821 } 2822 2823 bool ScInputHandler::StartsLikeFormula( std::u16string_view rStr ) const 2824 { 2825 // For new input '+' and '-' may start the dreaded "lazy data typist" 2826 // formula input, editing existing formula content can only start with '='. 2827 return !rStr.empty() && (rStr[0] == '=' || (!mbEditingExistingContent && (rStr[0] == '+' || rStr[0] == '-'))); 2828 } 2829 2830 void ScInputHandler::UpdateFormulaMode() 2831 { 2832 SfxApplication* pSfxApp = SfxGetpApp(); 2833 2834 bool bIsFormula = !bProtected; 2835 if (bIsFormula) 2836 { 2837 const OUString& rText = mpEditEngine->GetText(0); 2838 bIsFormula = StartsLikeFormula(rText); 2839 } 2840 2841 if ( bIsFormula ) 2842 { 2843 if (!bFormulaMode) 2844 { 2845 bFormulaMode = true; 2846 pRefViewSh = pActiveViewSh; 2847 pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); 2848 ScModule* pMod = SC_MOD(); 2849 pMod->SetRefInputHdl(this); 2850 if (pInputWin) 2851 pInputWin->SetFormulaMode(true); 2852 2853 // in LOK, we always need to perform the GetFormulaData() call so 2854 // that the formula insertion works 2855 if (comphelper::LibreOfficeKit::isActive() || pMod->GetAppOptions().GetAutoComplete()) 2856 GetFormulaData(); 2857 2858 UpdateParenthesis(); 2859 UpdateAutoCorrFlag(); 2860 } 2861 } 2862 else // Deactivate 2863 { 2864 if (bFormulaMode) 2865 { 2866 ShowRefFrame(); 2867 bFormulaMode = false; 2868 pRefViewSh = nullptr; 2869 pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); 2870 SC_MOD()->SetRefInputHdl(nullptr); 2871 if (pInputWin) 2872 pInputWin->SetFormulaMode(false); 2873 UpdateAutoCorrFlag(); 2874 } 2875 } 2876 } 2877 2878 void ScInputHandler::ShowRefFrame() 2879 { 2880 // Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat 2881 // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh. 2882 // A local variable is used instead. 2883 ScTabViewShell* pVisibleSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); 2884 if ( !(pRefViewSh && pRefViewSh != pVisibleSh) ) 2885 return; 2886 2887 bool bFound = false; 2888 SfxViewFrame& rRefFrame = pRefViewSh->GetViewFrame(); 2889 SfxViewFrame* pOneFrame = SfxViewFrame::GetFirst(); 2890 while ( pOneFrame && !bFound ) 2891 { 2892 if ( pOneFrame == &rRefFrame ) 2893 bFound = true; 2894 pOneFrame = SfxViewFrame::GetNext( *pOneFrame ); 2895 } 2896 2897 if (bFound) 2898 { 2899 // We count on Activate working synchronously here 2900 // (pActiveViewSh is set while doing so) 2901 pRefViewSh->SetActive(); // Appear and SetViewFrame 2902 2903 // pLastState is set correctly in the NotifyChange from the Activate 2904 } 2905 else 2906 { 2907 OSL_FAIL("ViewFrame for reference input is not here anymore"); 2908 } 2909 } 2910 2911 void ScInputHandler::RemoveSelection() 2912 { 2913 EditView* pActiveView = pTopView ? pTopView : pTableView; 2914 if (!pActiveView) 2915 return; 2916 2917 ESelection aSel = pActiveView->GetSelection(); 2918 aSel.nStartPara = aSel.nEndPara; 2919 aSel.nStartPos = aSel.nEndPos; 2920 if (pTableView) 2921 pTableView->SetSelection( aSel ); 2922 if (pTopView) 2923 pTopView->SetSelection( aSel ); 2924 } 2925 2926 void ScInputHandler::InvalidateAttribs() 2927 { 2928 SfxViewFrame* pViewFrm = SfxViewFrame::Current(); 2929 if (!pViewFrm) 2930 return; 2931 2932 SfxBindings& rBindings = pViewFrm->GetBindings(); 2933 2934 rBindings.Invalidate( SID_ATTR_CHAR_FONT ); 2935 rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); 2936 rBindings.Invalidate( SID_ATTR_CHAR_COLOR ); 2937 2938 rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT ); 2939 rBindings.Invalidate( SID_ATTR_CHAR_POSTURE ); 2940 rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE ); 2941 rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE ); 2942 rBindings.Invalidate( SID_ULINE_VAL_NONE ); 2943 rBindings.Invalidate( SID_ULINE_VAL_SINGLE ); 2944 rBindings.Invalidate( SID_ULINE_VAL_DOUBLE ); 2945 rBindings.Invalidate( SID_ULINE_VAL_DOTTED ); 2946 2947 rBindings.Invalidate( SID_HYPERLINK_GETLINK ); 2948 2949 rBindings.Invalidate( SID_ATTR_CHAR_KERNING ); 2950 rBindings.Invalidate( SID_SET_SUPER_SCRIPT ); 2951 rBindings.Invalidate( SID_SET_SUB_SCRIPT ); 2952 rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT ); 2953 rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED ); 2954 2955 rBindings.Invalidate( SID_SAVEDOC ); 2956 rBindings.Invalidate( SID_DOC_MODIFIED ); 2957 } 2958 2959 // --------------- public methods -------------------------------------------- 2960 2961 void ScInputHandler::SetMode( ScInputMode eNewMode, const OUString* pInitText, ScEditEngineDefaulter* pTopEngine ) 2962 { 2963 if ( eMode == eNewMode ) 2964 return; 2965 2966 ImplCreateEditEngine(); 2967 2968 if (bProtected) 2969 { 2970 eMode = SC_INPUT_NONE; 2971 StopInputWinEngine( true ); 2972 if (pActiveViewSh) 2973 pActiveViewSh->GetActiveWin()->GrabFocus(); 2974 return; 2975 } 2976 2977 if (eNewMode != SC_INPUT_NONE && pActiveViewSh) 2978 // Disable paste mode when edit mode starts. 2979 pActiveViewSh->GetViewData().SetPasteMode( ScPasteFlags::NONE ); 2980 2981 bInOwnChange = true; // disable ModifyHdl (reset below) 2982 2983 ScInputMode eOldMode = eMode; 2984 eMode = eNewMode; 2985 if (eOldMode == SC_INPUT_TOP && eNewMode != eOldMode) 2986 StopInputWinEngine( false ); 2987 2988 if (eMode==SC_INPUT_TOP || eMode==SC_INPUT_TABLE) 2989 { 2990 if (eOldMode == SC_INPUT_NONE) // not if switching between modes 2991 { 2992 if (StartTable(0, false, eMode == SC_INPUT_TABLE, pTopEngine)) 2993 { 2994 if (pActiveViewSh) 2995 pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos ); 2996 } 2997 } 2998 2999 if (pInitText) 3000 { 3001 mpEditEngine->SetTextCurrentDefaults(*pInitText); 3002 bModified = true; 3003 } 3004 3005 sal_Int32 nPara = mpEditEngine->GetParagraphCount()-1; 3006 sal_Int32 nLen = mpEditEngine->GetText(nPara).getLength(); 3007 sal_uInt16 nCount = mpEditEngine->GetViewCount(); 3008 3009 for (sal_uInt16 i=0; i<nCount; i++) 3010 { 3011 if ( eMode == SC_INPUT_TABLE && eOldMode == SC_INPUT_TOP ) 3012 { 3013 // Keep Selection 3014 } 3015 else 3016 { 3017 mpEditEngine->GetView(i)-> 3018 SetSelection( ESelection( nPara, nLen, nPara, nLen ) ); 3019 } 3020 mpEditEngine->GetView(i)->ShowCursor(false); 3021 } 3022 } 3023 3024 UpdateActiveView(); 3025 if (eMode==SC_INPUT_TABLE || eMode==SC_INPUT_TYPE) 3026 { 3027 if (pTableView) 3028 pTableView->SetEditEngineUpdateLayout(true); 3029 } 3030 else 3031 { 3032 if (pTopView) 3033 pTopView->SetEditEngineUpdateLayout(true); 3034 } 3035 3036 if (eNewMode != eOldMode) 3037 UpdateFormulaMode(); 3038 3039 bInOwnChange = false; 3040 } 3041 3042 /** 3043 * @return true if rString only contains digits (no autocorrect then) 3044 */ 3045 static bool lcl_IsNumber(std::u16string_view aString) 3046 { 3047 size_t nLen = aString.size(); 3048 for (size_t i=0; i<nLen; i++) 3049 { 3050 sal_Unicode c = aString[i]; 3051 if ( c < '0' || c > '9' ) 3052 return false; 3053 } 3054 return true; 3055 } 3056 3057 static void lcl_SelectionToEnd( EditView* pView ) 3058 { 3059 if ( pView ) 3060 { 3061 EditEngine* pEngine = pView->GetEditEngine(); 3062 sal_Int32 nParCnt = pEngine->GetParagraphCount(); 3063 if ( nParCnt == 0 ) 3064 nParCnt = 1; 3065 ESelection aSel( nParCnt-1, pEngine->GetTextLen(nParCnt-1) ); // empty selection, cursor at the end 3066 pView->SetSelection( aSel ); 3067 } 3068 } 3069 3070 void ScInputHandler::EnterHandler( ScEnterMode nBlockMode, bool bBeforeSavingInLOK ) 3071 { 3072 if (!mbDocumentDisposing && comphelper::LibreOfficeKit::isActive() 3073 && pActiveViewSh != SfxViewShell::Current()) 3074 return; 3075 3076 if (!pActiveViewSh) 3077 return; 3078 3079 // Macro calls for validity can cause a lot of problems, so inhibit 3080 // nested calls of EnterHandler(). 3081 if (bInEnterHandler) return; 3082 bInEnterHandler = true; 3083 bInOwnChange = true; // disable ModifyHdl (reset below) 3084 mbPartialPrefix = false; 3085 3086 ImplCreateEditEngine(); 3087 3088 bool bMatrix = ( nBlockMode == ScEnterMode::MATRIX ); 3089 3090 SfxApplication* pSfxApp = SfxGetpApp(); 3091 std::unique_ptr<EditTextObject> pObject; 3092 std::unique_ptr<ScPatternAttr> pCellAttrs; 3093 bool bForget = false; // Remove due to validity? 3094 3095 OUString aString = GetEditText(mpEditEngine.get()); 3096 OUString aPreAutoCorrectString(aString); 3097 EditView* pActiveView = pTopView ? pTopView : pTableView; 3098 if (bModified && pActiveView && !aString.isEmpty() && !lcl_IsNumber(aString)) 3099 { 3100 if (pColumnData && miAutoPosColumn != pColumnData->end()) 3101 { 3102 // #i47125# If AutoInput appended something, do the final AutoCorrect 3103 // with the cursor at the end of the input. 3104 lcl_SelectionToEnd(pTopView); 3105 lcl_SelectionToEnd(pTableView); 3106 } 3107 3108 vcl::Window* pFrameWin = pActiveViewSh->GetFrameWin(); 3109 3110 if (pTopView) 3111 pTopView->CompleteAutoCorrect(); // CompleteAutoCorrect for both Views 3112 if (pTableView) 3113 pTableView->CompleteAutoCorrect(pFrameWin); 3114 aString = GetEditText(mpEditEngine.get()); 3115 } 3116 lcl_RemoveTabs(aString); 3117 lcl_RemoveTabs(aPreAutoCorrectString); 3118 3119 // Test if valid (always with simple string) 3120 if (bModified && nValidation) 3121 { 3122 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument(); 3123 const ScValidationData* pData = rDoc.GetValidationEntry( nValidation ); 3124 if (pData && pData->HasErrMsg()) 3125 { 3126 // #i67990# don't use pLastPattern in EnterHandler 3127 const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); 3128 3129 bool bOk; 3130 3131 if (pData->GetDataMode() == SC_VALID_CUSTOM) 3132 { 3133 bOk = pData->IsDataValidCustom( aString, *pPattern, aCursorPos, ScValidationData::CustomValidationPrivateAccess() ); 3134 } 3135 else 3136 { 3137 bOk = pData->IsDataValid( aString, *pPattern, aCursorPos ); 3138 } 3139 3140 if (!bOk) 3141 { 3142 pActiveViewSh->StopMarking(); // (the InfoBox consumes the MouseButtonUp) 3143 3144 // tdf#125917 Release the grab that a current mouse-down event being handled 3145 // by ScTabView has put on the mouse via its SelectionEngine. 3146 // Otherwise the warning box cannot interact with the mouse 3147 if (ScTabView* pView = pActiveViewSh->GetViewData().GetView()) 3148 { 3149 if (ScViewSelectionEngine* pSelEngine = pView->GetSelEngine()) 3150 pSelEngine->ReleaseMouse(); 3151 } 3152 3153 if (bBeforeSavingInLOK) 3154 { 3155 // Invalid entry but not applied to the document model. 3156 // Exit to complete the "save", leaving the edit view as it is 3157 // for the user to continue after save. 3158 bInOwnChange = false; 3159 bInEnterHandler = false; 3160 return; 3161 } 3162 3163 if (pData->DoError(pActiveViewSh->GetFrameWeld(), aString, aCursorPos)) 3164 bForget = true; // Do not take over input 3165 } 3166 } 3167 } 3168 3169 // Check for input into DataPilot table 3170 if ( bModified && !bForget ) 3171 { 3172 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument(); 3173 ScDPObject* pDPObj = rDoc.GetDPAtCursor( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); 3174 if ( pDPObj ) 3175 { 3176 // Any input within the DataPilot table is either a valid renaming 3177 // or an invalid action - normal cell input is always aborted 3178 pActiveViewSh->DataPilotInput( aCursorPos, aString ); 3179 bForget = true; 3180 } 3181 } 3182 3183 std::vector<editeng::MisspellRanges> aMisspellRanges; 3184 // UpdateLayout must be true during CompleteOnlineSpelling 3185 const bool bUpdateLayout = mpEditEngine->SetUpdateLayout( true ); 3186 mpEditEngine->CompleteOnlineSpelling(); 3187 bool bSpellErrors = !bFormulaMode && mpEditEngine->HasOnlineSpellErrors(); 3188 if ( bSpellErrors ) 3189 { 3190 // #i3820# If the spell checker flags numerical input as error, 3191 // it still has to be treated as number, not EditEngine object. 3192 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument(); 3193 // #i67990# don't use pLastPattern in EnterHandler 3194 const ScPatternAttr* pPattern = rDoc.GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); 3195 if (pPattern) 3196 { 3197 SvNumberFormatter* pFormatter = rDoc.GetFormatTable(); 3198 // without conditional format, as in ScColumn::SetString 3199 sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter ); 3200 double nVal; 3201 if ( pFormatter->IsNumberFormat( aString, nFormat, nVal ) ) 3202 { 3203 bSpellErrors = false; // ignore the spelling errors 3204 } 3205 } 3206 } 3207 3208 // After RemoveAdjust, the EditView must not be repainted (has wrong font size etc). 3209 // SetUpdateLayout must come after CompleteOnlineSpelling. 3210 // The view is hidden in any case below (Broadcast). 3211 mpEditEngine->SetUpdateLayout( false ); 3212 3213 if ( bModified && !bForget ) // What is being entered (text/object)? 3214 { 3215 sal_Int32 nParCnt = mpEditEngine->GetParagraphCount(); 3216 if ( nParCnt == 0 ) 3217 nParCnt = 1; 3218 3219 bool bUniformAttribs = true; 3220 SfxItemSet aPara1Attribs = mpEditEngine->GetAttribs(0, 0, mpEditEngine->GetTextLen(0)); 3221 for (sal_Int32 nPara = 1; nPara < nParCnt; ++nPara) 3222 { 3223 SfxItemSet aPara2Attribs = mpEditEngine->GetAttribs(nPara, 0, mpEditEngine->GetTextLen(nPara)); 3224 if (!(aPara1Attribs == aPara2Attribs)) 3225 { 3226 // Paragraph format different from that of the 1st paragraph. 3227 bUniformAttribs = false; 3228 break; 3229 } 3230 } 3231 3232 ESelection aSel( 0, 0, nParCnt-1, mpEditEngine->GetTextLen(nParCnt-1) ); 3233 SfxItemSet aOldAttribs = mpEditEngine->GetAttribs( aSel ); 3234 const SfxPoolItem* pItem = nullptr; 3235 3236 // Find common (cell) attributes before RemoveAdjust 3237 if ( bUniformAttribs ) 3238 { 3239 std::optional<SfxItemSet> pCommonAttrs; 3240 for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END; nId++) 3241 { 3242 SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem ); 3243 if ( eState == SfxItemState::SET && 3244 nId != EE_CHAR_ESCAPEMENT && nId != EE_CHAR_PAIRKERNING && 3245 nId != EE_CHAR_KERNING && nId != EE_CHAR_XMLATTRIBS && 3246 *pItem != pEditDefaults->Get(nId) ) 3247 { 3248 if ( !pCommonAttrs ) 3249 pCommonAttrs.emplace( mpEditEngine->GetEmptyItemSet() ); 3250 pCommonAttrs->Put( *pItem ); 3251 } 3252 } 3253 3254 if ( pCommonAttrs ) 3255 { 3256 ScDocument& rDoc = pActiveViewSh->GetViewData().GetDocument(); 3257 pCellAttrs = std::make_unique<ScPatternAttr>(rDoc.GetPool()); 3258 pCellAttrs->GetFromEditItemSet( &*pCommonAttrs ); 3259 } 3260 } 3261 3262 // Clear ParaAttribs (including adjustment) 3263 RemoveAdjust(); 3264 3265 bool bAttrib = false; // Formatting present? 3266 3267 // check if EditObject is needed 3268 if (nParCnt > 1) 3269 bAttrib = true; 3270 else 3271 { 3272 for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && !bAttrib; nId++) 3273 { 3274 SfxItemState eState = aOldAttribs.GetItemState( nId, false, &pItem ); 3275 if (eState == SfxItemState::DONTCARE) 3276 bAttrib = true; 3277 else if (eState == SfxItemState::SET) 3278 { 3279 // Keep same items in EditEngine as in ScEditAttrTester 3280 if ( nId == EE_CHAR_ESCAPEMENT || nId == EE_CHAR_PAIRKERNING || 3281 nId == EE_CHAR_KERNING || nId == EE_CHAR_XMLATTRIBS ) 3282 { 3283 if ( *pItem != pEditDefaults->Get(nId) ) 3284 bAttrib = true; 3285 } 3286 } 3287 } 3288 3289 // Contains fields? 3290 SfxItemState eFieldState = aOldAttribs.GetItemState( EE_FEATURE_FIELD, false ); 3291 if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET ) 3292 bAttrib = true; 3293 3294 // Not converted characters? 3295 SfxItemState eConvState = aOldAttribs.GetItemState( EE_FEATURE_NOTCONV, false ); 3296 if ( eConvState == SfxItemState::DONTCARE || eConvState == SfxItemState::SET ) 3297 bAttrib = true; 3298 3299 // Always recognize formulas as formulas 3300 // We still need the preceding test due to cell attributes 3301 } 3302 3303 if (bSpellErrors) 3304 mpEditEngine->GetAllMisspellRanges(aMisspellRanges); 3305 3306 if (bMatrix) 3307 bAttrib = false; 3308 3309 if (bAttrib) 3310 { 3311 mpEditEngine->ClearSpellErrors(); 3312 pObject = mpEditEngine->CreateTextObject(); 3313 } 3314 else if (SC_MOD()->GetAppOptions().GetAutoComplete()) // Adjust Upper/Lower case 3315 { 3316 // Perform case-matching only when the typed text is partial. 3317 if (pColumnData && aAutoSearch.getLength() < aString.getLength()) 3318 aString = getExactMatch(*pColumnData, aString); 3319 } 3320 } 3321 3322 // Don't rely on ShowRefFrame switching the active view synchronously 3323 // execute the function directly on the correct view's bindings instead 3324 // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call 3325 ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh; 3326 3327 if (bFormulaMode) 3328 { 3329 ShowRefFrame(); 3330 3331 if (pExecuteSh) 3332 { 3333 pExecuteSh->SetTabNo(aCursorPos.Tab()); 3334 pExecuteSh->ActiveGrabFocus(); 3335 } 3336 3337 bFormulaMode = false; 3338 pSfxApp->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); 3339 SC_MOD()->SetRefInputHdl(nullptr); 3340 if (pInputWin) 3341 pInputWin->SetFormulaMode(false); 3342 UpdateAutoCorrFlag(); 3343 } 3344 pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot 3345 DeleteRangeFinder(); 3346 ResetAutoPar(); 3347 3348 bool bOldMod = bModified; 3349 3350 bModified = false; 3351 bSelIsRef = false; 3352 eMode = SC_INPUT_NONE; 3353 StopInputWinEngine(true); 3354 3355 // Text input (through number formats) or ApplySelectionPattern modify 3356 // the cell's attributes, so pLastPattern is no longer valid 3357 pLastPattern = nullptr; 3358 3359 if (bOldMod && !bProtected && !bForget) 3360 { 3361 bool bInsertPreCorrectedString = true; 3362 // No typographic quotes in formulas 3363 if (aString.startsWith("=")) 3364 { 3365 SvxAutoCorrect* pAuto = SvxAutoCorrCfg::Get().GetAutoCorrect(); 3366 if ( pAuto ) 3367 { 3368 bInsertPreCorrectedString = false; 3369 OUString aReplace(pAuto->GetStartDoubleQuote()); 3370 if( aReplace.isEmpty() ) 3371 aReplace = ScGlobal::getLocaleData().getDoubleQuotationMarkStart(); 3372 if( aReplace != "\"" ) 3373 aString = aString.replaceAll( aReplace, "\"" ); 3374 3375 aReplace = OUString(pAuto->GetEndDoubleQuote()); 3376 if( aReplace.isEmpty() ) 3377 aReplace = ScGlobal::getLocaleData().getDoubleQuotationMarkEnd(); 3378 if( aReplace != "\"" ) 3379 aString = aString.replaceAll( aReplace, "\"" ); 3380 3381 aReplace = OUString(pAuto->GetStartSingleQuote()); 3382 if( aReplace.isEmpty() ) 3383 aReplace = ScGlobal::getLocaleData().getQuotationMarkStart(); 3384 if( aReplace != "'" ) 3385 aString = aString.replaceAll( aReplace, "'" ); 3386 3387 aReplace = OUString(pAuto->GetEndSingleQuote()); 3388 if( aReplace.isEmpty() ) 3389 aReplace = ScGlobal::getLocaleData().getQuotationMarkEnd(); 3390 if( aReplace != "'" ) 3391 aString = aString.replaceAll( aReplace, "'"); 3392 } 3393 } 3394 3395 pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditViewNoPaint ) ); 3396 3397 if ( pExecuteSh ) 3398 { 3399 SfxBindings& rBindings = pExecuteSh->GetViewFrame().GetBindings(); 3400 3401 sal_uInt16 nId = FID_INPUTLINE_ENTER; 3402 if ( nBlockMode == ScEnterMode::BLOCK ) 3403 nId = FID_INPUTLINE_BLOCK; 3404 else if ( nBlockMode == ScEnterMode::MATRIX ) 3405 nId = FID_INPUTLINE_MATRIX; 3406 3407 const SfxPoolItem* aArgs[2]; 3408 aArgs[1] = nullptr; 3409 3410 if ( bInsertPreCorrectedString && aString != aPreAutoCorrectString ) 3411 { 3412 ScInputStatusItem aItem(FID_INPUTLINE_STATUS, 3413 aCursorPos, aCursorPos, aCursorPos, 3414 aPreAutoCorrectString, pObject.get()); 3415 aArgs[0] = &aItem; 3416 rBindings.Execute(nId, aArgs); 3417 } 3418 3419 ScInputStatusItem aItemCorrected(FID_INPUTLINE_STATUS, 3420 aCursorPos, aCursorPos, aCursorPos, 3421 aString, pObject.get()); 3422 if ( !aMisspellRanges.empty() ) 3423 aItemCorrected.SetMisspellRanges(&aMisspellRanges); 3424 3425 aArgs[0] = &aItemCorrected; 3426 rBindings.Execute(nId, aArgs); 3427 } 3428 3429 pLastState.reset(); // pLastState still contains the old text 3430 } 3431 else 3432 pSfxApp->Broadcast( SfxHint( SfxHintId::ScKillEditView ) ); 3433 3434 if ( bOldMod && pExecuteSh && pCellAttrs && !bForget ) 3435 { 3436 // Combine with input? 3437 pExecuteSh->ApplySelectionPattern( *pCellAttrs, true ); 3438 pExecuteSh->AdjustBlockHeight(); 3439 } 3440 3441 HideTip(); 3442 HideTipBelow(); 3443 3444 nFormSelStart = nFormSelEnd = 0; 3445 aFormText.clear(); 3446 3447 mbEditingExistingContent = false; 3448 bInOwnChange = false; 3449 bInEnterHandler = false; 3450 if (bUpdateLayout) 3451 mpEditEngine->SetUpdateLayout( true ); 3452 } 3453 3454 void ScInputHandler::CancelHandler() 3455 { 3456 bInOwnChange = true; // Also without FormulaMode due to FunctionsAutoPilot 3457 3458 ImplCreateEditEngine(); 3459 3460 bModified = false; 3461 mbPartialPrefix = false; 3462 mbEditingExistingContent = false; 3463 3464 // Don't rely on ShowRefFrame switching the active view synchronously 3465 // execute the function directly on the correct view's bindings instead 3466 // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call 3467 ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh; 3468 3469 if (bFormulaMode) 3470 { 3471 ShowRefFrame(); 3472 if (pExecuteSh) 3473 { 3474 pExecuteSh->SetTabNo(aCursorPos.Tab()); 3475 pExecuteSh->ActiveGrabFocus(); 3476 } 3477 bFormulaMode = false; 3478 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScRefModeChanged ) ); 3479 SC_MOD()->SetRefInputHdl(nullptr); 3480 if (pInputWin) 3481 pInputWin->SetFormulaMode(false); 3482 UpdateAutoCorrFlag(); 3483 } 3484 pRefViewSh = nullptr; // Also without FormulaMode due to FunctionsAutoPilot 3485 DeleteRangeFinder(); 3486 ResetAutoPar(); 3487 3488 eMode = SC_INPUT_NONE; 3489 StopInputWinEngine( true ); 3490 SCCOL nMaxCol(MAXCOL); 3491 if (pExecuteSh) 3492 { 3493 pExecuteSh->StopEditShell(); 3494 nMaxCol = pExecuteSh->GetViewData().GetDocument().MaxCol(); 3495 } 3496 3497 aCursorPos.Set(nMaxCol+1,0,0); // Invalid flag 3498 mpEditEngine->SetTextCurrentDefaults(OUString()); 3499 3500 if ( !pLastState && pExecuteSh ) 3501 pExecuteSh->UpdateInputHandler( true ); // Update status again 3502 else 3503 NotifyChange( pLastState.get(), true ); 3504 3505 nFormSelStart = nFormSelEnd = 0; 3506 aFormText.clear(); 3507 3508 bInOwnChange = false; 3509 3510 if ( comphelper::LibreOfficeKit::isActive() && pExecuteSh ) 3511 { 3512 // Clear 3513 std::vector<ReferenceMark> aReferenceMarks; 3514 ScInputHandler::SendReferenceMarks( pActiveViewSh, aReferenceMarks ); 3515 } 3516 } 3517 3518 bool ScInputHandler::IsModalMode( const SfxObjectShell* pDocSh ) 3519 { 3520 // References to unnamed document; that doesn't work 3521 return bFormulaMode && pRefViewSh 3522 && pRefViewSh->GetViewData().GetDocument().GetDocumentShell() != pDocSh 3523 && !pDocSh->HasName(); 3524 } 3525 3526 void ScInputHandler::AddRefEntry() 3527 { 3528 const sal_Unicode cSep = ScCompiler::GetNativeSymbolChar(ocSep); 3529 UpdateActiveView(); 3530 if (!pTableView && !pTopView) 3531 return; // E.g. FillMode 3532 3533 DataChanging(); // Cannot be new 3534 3535 RemoveSelection(); 3536 OUString aText = GetEditText(mpEditEngine.get()); 3537 sal_Unicode cLastChar = 0; 3538 sal_Int32 nPos = aText.getLength() - 1; 3539 while (nPos >= 0) //checking space 3540 { 3541 cLastChar = aText[nPos]; 3542 if (cLastChar != ' ') 3543 break; 3544 --nPos; 3545 } 3546 3547 bool bAppendSeparator = (cLastChar != '(' && cLastChar != cSep && cLastChar != '='); 3548 if (bAppendSeparator) 3549 { 3550 if (pTableView) 3551 pTableView->InsertText( OUString(cSep) ); 3552 if (pTopView) 3553 pTopView->InsertText( OUString(cSep) ); 3554 } 3555 3556 DataChanged(); 3557 } 3558 3559 void ScInputHandler::SetReference( const ScRange& rRef, const ScDocument& rDoc ) 3560 { 3561 HideTip(); 3562 3563 const ScDocument* pThisDoc = nullptr; 3564 if (pRefViewSh) 3565 pThisDoc = &pRefViewSh->GetViewData().GetDocument(); 3566 bool bOtherDoc = (pThisDoc != &rDoc); 3567 if (bOtherDoc && !rDoc.GetDocumentShell()->HasName()) 3568 { 3569 // References to unnamed document; that doesn't work 3570 // SetReference should not be called, then 3571 return; 3572 } 3573 if (!pThisDoc) 3574 pThisDoc = &rDoc; 3575 3576 UpdateActiveView(); 3577 if (!pTableView && !pTopView) 3578 return; // E.g. FillMode 3579 3580 // Never overwrite the "="! 3581 EditView* pActiveView = pTopView ? pTopView : pTableView; 3582 ESelection aSel = pActiveView->GetSelection(); 3583 aSel.Adjust(); 3584 if ( aSel.nStartPara == 0 && aSel.nStartPos == 0 ) 3585 return; 3586 3587 DataChanging(); // Cannot be new 3588 3589 // Turn around selection if backwards. 3590 if (pTableView) 3591 { 3592 ESelection aTabSel = pTableView->GetSelection(); 3593 if (aTabSel.nStartPos > aTabSel.nEndPos && aTabSel.nStartPara == aTabSel.nEndPara) 3594 { 3595 aTabSel.Adjust(); 3596 pTableView->SetSelection(aTabSel); 3597 } 3598 } 3599 if (pTopView) 3600 { 3601 ESelection aTopSel = pTopView->GetSelection(); 3602 if (aTopSel.nStartPos > aTopSel.nEndPos && aTopSel.nStartPara == aTopSel.nEndPara) 3603 { 3604 aTopSel.Adjust(); 3605 pTopView->SetSelection(aTopSel); 3606 } 3607 } 3608 3609 // Create string from reference, in the syntax of the document being edited. 3610 OUString aRefStr; 3611 const ScAddress::Details aAddrDetails( *pThisDoc, aCursorPos ); 3612 if (bOtherDoc) 3613 { 3614 // Reference to other document 3615 OSL_ENSURE(rRef.aStart.Tab()==rRef.aEnd.Tab(), "nStartTab!=nEndTab"); 3616 3617 // Always 3D and absolute. 3618 OUString aTmp(rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails)); 3619 3620 ScDocShell* pObjSh = rDoc.GetDocumentShell(); 3621 // #i75893# convert escaped URL of the document to something user friendly 3622 OUString aFileName = pObjSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ); 3623 3624 switch(aAddrDetails.eConv) 3625 { 3626 case formula::FormulaGrammar::CONV_XL_A1 : 3627 case formula::FormulaGrammar::CONV_XL_OOX : 3628 case formula::FormulaGrammar::CONV_XL_R1C1 : 3629 aRefStr = "[\'" + aFileName + "']"; 3630 break; 3631 case formula::FormulaGrammar::CONV_OOO : 3632 default: 3633 aRefStr = "\'" + aFileName + "'#"; 3634 break; 3635 } 3636 aRefStr += aTmp; 3637 } 3638 else 3639 { 3640 if ( rRef.aStart.Tab() != aCursorPos.Tab() || 3641 rRef.aStart.Tab() != rRef.aEnd.Tab() ) 3642 // pointer-selected => absolute sheet reference 3643 aRefStr = rRef.Format(rDoc, ScRefFlags::VALID | ScRefFlags::TAB_ABS_3D, aAddrDetails); 3644 else 3645 aRefStr = rRef.Format(rDoc, ScRefFlags::VALID, aAddrDetails); 3646 } 3647 bool bLOKShowSelect = true; 3648 if(comphelper::LibreOfficeKit::isActive() && pRefViewSh->GetViewData().GetRefTabNo() != pRefViewSh->GetViewData().GetTabNo()) 3649 bLOKShowSelect = false; 3650 3651 if (pTableView || pTopView) 3652 { 3653 if (pTableView) 3654 pTableView->InsertText( aRefStr, true, bLOKShowSelect ); 3655 if (pTopView) 3656 pTopView->InsertText( aRefStr, true, bLOKShowSelect ); 3657 3658 DataChanged(); 3659 } 3660 3661 bSelIsRef = true; 3662 } 3663 3664 void ScInputHandler::InsertFunction( const OUString& rFuncName, bool bAddPar ) 3665 { 3666 if ( eMode == SC_INPUT_NONE ) 3667 { 3668 OSL_FAIL("InsertFunction, not during input mode"); 3669 return; 3670 } 3671 3672 UpdateActiveView(); 3673 if (!pTableView && !pTopView) 3674 return; // E.g. FillMode 3675 3676 DataChanging(); // Cannot be new 3677 3678 OUString aText = rFuncName; 3679 if (bAddPar) 3680 aText += "()"; 3681 3682 if (pTableView) 3683 { 3684 pTableView->InsertText( aText ); 3685 if (bAddPar) 3686 { 3687 ESelection aSel = pTableView->GetSelection(); 3688 --aSel.nStartPos; 3689 --aSel.nEndPos; 3690 pTableView->SetSelection(aSel); 3691 } 3692 } 3693 if (pTopView) 3694 { 3695 pTopView->InsertText( aText ); 3696 if (bAddPar) 3697 { 3698 ESelection aSel = pTopView->GetSelection(); 3699 --aSel.nStartPos; 3700 --aSel.nEndPos; 3701 pTopView->SetSelection(aSel); 3702 } 3703 } 3704 3705 DataChanged(); 3706 3707 if (bAddPar) 3708 AutoParAdded(); 3709 } 3710 3711 void ScInputHandler::ClearText() 3712 { 3713 if ( eMode == SC_INPUT_NONE ) 3714 { 3715 OSL_FAIL("ClearText, not during input mode"); 3716 return; 3717 } 3718 3719 UpdateActiveView(); 3720 if (!pTableView && !pTopView) 3721 return; // E.g. FillMode 3722 3723 DataChanging(); // Cannot be new 3724 3725 if (pTableView) 3726 { 3727 pTableView->GetEditEngine()->SetText( "" ); 3728 pTableView->SetSelection( ESelection(0,0, 0,0) ); 3729 } 3730 if (pTopView) 3731 { 3732 pTopView->GetEditEngine()->SetText( "" ); 3733 pTopView->SetSelection( ESelection(0,0, 0,0) ); 3734 } 3735 3736 DataChanged(); 3737 } 3738 3739 bool ScInputHandler::KeyInput( const KeyEvent& rKEvt, bool bStartEdit /* = false */ ) 3740 { 3741 vcl::KeyCode aCode = rKEvt.GetKeyCode(); 3742 sal_uInt16 nModi = aCode.GetModifier(); 3743 bool bShift = aCode.IsShift(); 3744 bool bControl = aCode.IsMod1(); 3745 bool bAlt = aCode.IsMod2(); 3746 sal_uInt16 nCode = aCode.GetCode(); 3747 sal_Unicode nChar = rKEvt.GetCharCode(); 3748 3749 if (bAlt && !bControl && nCode != KEY_RETURN) 3750 // Alt-Return and Alt-Ctrl-* are accepted. Everything else with ALT are not. 3751 return false; 3752 3753 // There is a partial autocomplete suggestion. 3754 // Allow its completion with right arrow key (without modifiers). 3755 if (mbPartialPrefix && nCode == KEY_RIGHT && !bControl && !bShift && !bAlt && 3756 (pTopView || pTableView)) 3757 { 3758 if (pTopView) 3759 pTopView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH)); 3760 if (pTableView) 3761 pTableView->PostKeyEvent(KeyEvent(0, css::awt::Key::MOVE_TO_END_OF_PARAGRAPH)); 3762 3763 mbPartialPrefix = false; 3764 3765 // Indicate that this event has been consumed and ScTabViewShell should not act on this. 3766 return true; 3767 } 3768 3769 if (!bControl && nCode == KEY_TAB) 3770 { 3771 // Normal TAB moves the cursor right. 3772 EnterHandler(); 3773 3774 if (pActiveViewSh) 3775 pActiveViewSh->FindNextUnprot( bShift, true ); 3776 return true; 3777 } 3778 3779 bool bInputLine = ( eMode==SC_INPUT_TOP ); 3780 3781 bool bUsed = false; 3782 bool bSkip = false; 3783 bool bDoEnter = false; 3784 3785 switch ( nCode ) 3786 { 3787 case KEY_RETURN: 3788 // New line when in the input line and Shift/Ctrl-Enter is pressed, 3789 // or when in a cell and Ctrl-Enter is pressed. 3790 if ((pInputWin && bInputLine && bControl != bShift) || (!bInputLine && bControl && !bShift)) 3791 { 3792 bDoEnter = true; 3793 } 3794 else if (nModi == 0 && nTipVisible && pFormulaData && miAutoPosFormula != pFormulaData->end()) 3795 { 3796 PasteFunctionData(); 3797 bUsed = true; 3798 } 3799 else if ( nModi == 0 && nTipVisible && !aManualTip.isEmpty() ) 3800 { 3801 PasteManualTip(); 3802 bUsed = true; 3803 } 3804 else 3805 { 3806 ScEnterMode nMode = ScEnterMode::NORMAL; 3807 if ( bShift && bControl ) 3808 nMode = ScEnterMode::MATRIX; 3809 else if ( bAlt ) 3810 nMode = ScEnterMode::BLOCK; 3811 EnterHandler( nMode ); 3812 3813 if (pActiveViewSh) 3814 pActiveViewSh->MoveCursorEnter( bShift && !bControl ); 3815 3816 bUsed = true; 3817 } 3818 break; 3819 case KEY_TAB: 3820 if (bControl && !bAlt) 3821 { 3822 if (pFormulaData && nTipVisible && miAutoPosFormula != pFormulaData->end()) 3823 { 3824 // Iterate 3825 NextFormulaEntry( bShift ); 3826 bUsed = true; 3827 } 3828 else if (pColumnData && bUseTab) 3829 { 3830 // Iterate through AutoInput entries 3831 NextAutoEntry( bShift ); 3832 bUsed = true; 3833 } 3834 } 3835 break; 3836 case KEY_ESCAPE: 3837 if ( nTipVisible ) 3838 { 3839 HideTip(); 3840 bUsed = true; 3841 } 3842 else if( nTipVisibleSec ) 3843 { 3844 HideTipBelow(); 3845 bUsed = true; 3846 } 3847 else if (eMode != SC_INPUT_NONE) 3848 { 3849 CancelHandler(); 3850 bUsed = true; 3851 } 3852 else 3853 bSkip = true; 3854 break; 3855 case KEY_F2: 3856 if ( !bShift && !bControl && !bAlt && eMode == SC_INPUT_TABLE ) 3857 { 3858 eMode = SC_INPUT_TYPE; 3859 bUsed = true; 3860 } 3861 break; 3862 } 3863 3864 // Only execute cursor keys if already in EditMode 3865 // E.g. due to Shift-Ctrl-PageDn (not defined as an accelerator) 3866 bool bCursorKey = EditEngine::DoesKeyMoveCursor(rKEvt); 3867 bool bInsKey = ( nCode == KEY_INSERT && !nModi ); // Treat Insert like Cursorkeys 3868 if ( !bUsed && !bSkip && ( bDoEnter || EditEngine::DoesKeyChangeText(rKEvt) || 3869 ( eMode != SC_INPUT_NONE && ( bCursorKey || bInsKey ) ) ) ) 3870 { 3871 HideTip(); 3872 HideTipBelow(); 3873 3874 if (bSelIsRef) 3875 { 3876 RemoveSelection(); 3877 bSelIsRef = false; 3878 } 3879 3880 UpdateActiveView(); 3881 bool bNewView = DataChanging( nChar ); 3882 3883 if (bProtected) // Protected cell? 3884 bUsed = true; // Don't forward KeyEvent 3885 else // Changes allowed 3886 { 3887 if (bNewView ) // Create anew 3888 { 3889 if (pActiveViewSh) 3890 pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos ); 3891 UpdateActiveView(); 3892 if (eMode==SC_INPUT_NONE) 3893 if (pTableView || pTopView) 3894 { 3895 OUString aStrLoP; 3896 3897 if (bStartEdit && nCellPercentFormatDecSep != 0 && 3898 ((nChar >= '0' && nChar <= '9') || nChar == '-' || nChar == nCellPercentFormatDecSep)) 3899 { 3900 aStrLoP = "%"; 3901 } 3902 3903 if (pTableView) 3904 { 3905 pTableView->GetEditEngine()->SetText( aStrLoP ); 3906 if ( !aStrLoP.isEmpty() ) 3907 pTableView->SetSelection( ESelection(0,0, 0,0) ); // before the '%' 3908 3909 // Don't call SetSelection if the string is empty anyway, 3910 // to avoid breaking the bInitial handling in ScViewData::EditGrowY 3911 } 3912 if (pTopView) 3913 { 3914 pTopView->GetEditEngine()->SetText( aStrLoP ); 3915 if ( !aStrLoP.isEmpty() ) 3916 pTopView->SetSelection( ESelection(0,0, 0,0) ); // before the '%' 3917 } 3918 } 3919 SyncViews(); 3920 } 3921 3922 if (pTableView || pTopView) 3923 { 3924 if (bDoEnter) 3925 { 3926 if (pTableView) 3927 if( pTableView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) ) 3928 bUsed = true; 3929 if (pTopView) 3930 if( pTopView->PostKeyEvent( KeyEvent( '\r', vcl::KeyCode(KEY_RETURN) ) ) ) 3931 bUsed = true; 3932 } 3933 else if ( nAutoPar && nChar == ')' && CursorAtClosingPar() ) 3934 { 3935 SkipClosingPar(); 3936 bUsed = true; 3937 } 3938 else 3939 { 3940 if (pTableView) 3941 { 3942 if (pTopView) 3943 pTableView->SetControlWord(pTableView->GetControlWord() | EVControlBits::SINGLELINEPASTE); 3944 3945 vcl::Window* pFrameWin = pActiveViewSh ? pActiveViewSh->GetFrameWin() : nullptr; 3946 if ( pTableView->PostKeyEvent( rKEvt, pFrameWin ) ) 3947 bUsed = true; 3948 3949 pTableView->SetControlWord(pTableView->GetControlWord() & ~EVControlBits::SINGLELINEPASTE); 3950 } 3951 if (pTopView) 3952 { 3953 if ( bUsed && rKEvt.GetKeyCode().GetFunction() == KeyFuncType::CUT ) 3954 pTopView->DeleteSelected(); 3955 else if ( pTopView->PostKeyEvent( rKEvt ) ) 3956 bUsed = true; 3957 } 3958 } 3959 3960 // AutoInput: 3961 if ( bUsed && SC_MOD()->GetAppOptions().GetAutoComplete() ) 3962 { 3963 bUseTab = false; 3964 if (pFormulaData) 3965 miAutoPosFormula = pFormulaData->end(); // do not search further 3966 if (pColumnData) 3967 miAutoPosColumn = pColumnData->end(); 3968 3969 KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction(); 3970 if ( nChar && nChar != 8 && nChar != 127 && // no 'backspace', no 'delete' 3971 KeyFuncType::CUT != eFunc) // and no 'CTRL-X' 3972 { 3973 if (bFormulaMode) 3974 UseFormulaData(); 3975 else 3976 UseColData(); 3977 } 3978 } 3979 3980 // When the selection is changed manually or an opening parenthesis 3981 // is typed, stop overwriting parentheses 3982 if ( bUsed && nChar == '(' ) 3983 ResetAutoPar(); 3984 3985 if ( KEY_INSERT == nCode ) 3986 { 3987 SfxViewFrame* pViewFrm = SfxViewFrame::Current(); 3988 if (pViewFrm) 3989 pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT ); 3990 } 3991 if( bUsed && bFormulaMode && ( bCursorKey || bInsKey || nCode == KEY_DELETE || nCode == KEY_BACKSPACE ) ) 3992 { 3993 ShowTipCursor(); 3994 } 3995 if( bUsed && bFormulaMode && nCode == KEY_BACKSPACE ) 3996 { 3997 UseFormulaData(); 3998 } 3999 4000 } 4001 4002 // #i114511# don't count cursor keys as modification 4003 bool bSetModified = !bCursorKey; 4004 DataChanged(false, bSetModified); // also calls UpdateParenthesis() 4005 4006 // In the LOK case, we want to set the document modified state 4007 // right away at the start of the edit, so that the content is 4008 // saved even when the user leaves the document before hitting 4009 // Enter 4010 if (comphelper::LibreOfficeKit::isActive() && bSetModified && pActiveViewSh && !pActiveViewSh->GetViewData().GetDocShell()->IsModified()) 4011 pActiveViewSh->GetViewData().GetDocShell()->SetModified(); 4012 4013 InvalidateAttribs(); //! in DataChanged? 4014 } 4015 } 4016 4017 if (pTopView && eMode != SC_INPUT_NONE) 4018 SyncViews(); 4019 4020 return bUsed; 4021 } 4022 4023 OUString ScInputHandler::GetSurroundingText() 4024 { 4025 if (eMode != SC_INPUT_NONE) 4026 { 4027 UpdateActiveView(); 4028 if (pTableView || pTopView) 4029 { 4030 if (pTableView) 4031 return pTableView->GetSurroundingText(); 4032 else if (pTopView) // call only once 4033 return pTopView->GetSurroundingText(); 4034 } 4035 } 4036 return OUString(); 4037 } 4038 4039 Selection ScInputHandler::GetSurroundingTextSelection() 4040 { 4041 if (eMode != SC_INPUT_NONE) 4042 { 4043 UpdateActiveView(); 4044 if (pTableView || pTopView) 4045 { 4046 if (pTableView) 4047 return pTableView->GetSurroundingTextSelection(); 4048 else if (pTopView) // call only once 4049 return pTopView->GetSurroundingTextSelection(); 4050 } 4051 } 4052 return Selection(0, 0); 4053 } 4054 4055 bool ScInputHandler::DeleteSurroundingText(const Selection& rSelection) 4056 { 4057 if (eMode != SC_INPUT_NONE) 4058 { 4059 UpdateActiveView(); 4060 if (pTableView || pTopView) 4061 { 4062 if (pTableView) 4063 return pTableView->DeleteSurroundingText(rSelection); 4064 else if (pTopView) // call only once 4065 return pTopView->DeleteSurroundingText(rSelection); 4066 } 4067 } 4068 return false; 4069 } 4070 4071 void ScInputHandler::InputCommand( const CommandEvent& rCEvt ) 4072 { 4073 if ( rCEvt.GetCommand() == CommandEventId::CursorPos ) 4074 { 4075 // For CommandEventId::CursorPos, do as little as possible, because 4076 // with remote VCL, even a ShowCursor will generate another event. 4077 if ( eMode != SC_INPUT_NONE ) 4078 { 4079 UpdateActiveView(); 4080 if (pTableView || pTopView) 4081 { 4082 if (pTableView) 4083 pTableView->Command( rCEvt ); 4084 else if (pTopView) // call only once 4085 pTopView->Command( rCEvt ); 4086 } 4087 } 4088 } 4089 else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition ) 4090 { 4091 if ( eMode != SC_INPUT_NONE ) 4092 { 4093 UpdateActiveView(); 4094 if (pTableView || pTopView) 4095 { 4096 if (pTableView) 4097 pTableView->Command( rCEvt ); 4098 else if (pTopView) // call only once 4099 pTopView->Command( rCEvt ); 4100 } 4101 } 4102 } 4103 else 4104 { 4105 HideTip(); 4106 HideTipBelow(); 4107 4108 if ( bSelIsRef ) 4109 { 4110 RemoveSelection(); 4111 bSelIsRef = false; 4112 } 4113 4114 UpdateActiveView(); 4115 bool bNewView = DataChanging( 0, true ); 4116 4117 if (!bProtected) // changes allowed 4118 { 4119 if (bNewView) // create new edit view 4120 { 4121 if (pActiveViewSh) 4122 pActiveViewSh->GetViewData().GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos ); 4123 UpdateActiveView(); 4124 if (eMode==SC_INPUT_NONE) 4125 if (pTableView || pTopView) 4126 { 4127 if (pTableView) 4128 { 4129 pTableView->GetEditEngine()->SetText( "" ); 4130 pTableView->SetSelection( ESelection(0,0, 0,0) ); 4131 } 4132 if (pTopView) 4133 { 4134 pTopView->GetEditEngine()->SetText( "" ); 4135 pTopView->SetSelection( ESelection(0,0, 0,0) ); 4136 } 4137 } 4138 SyncViews(); 4139 } 4140 4141 if (pTableView || pTopView) 4142 { 4143 if (pTableView) 4144 pTableView->Command( rCEvt ); 4145 if (pTopView) 4146 pTopView->Command( rCEvt ); 4147 4148 if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput ) 4149 { 4150 // AutoInput after ext text input 4151 4152 if (pFormulaData) 4153 miAutoPosFormula = pFormulaData->end(); 4154 if (pColumnData) 4155 miAutoPosColumn = pColumnData->end(); 4156 4157 if (bFormulaMode) 4158 UseFormulaData(); 4159 else 4160 UseColData(); 4161 } 4162 } 4163 4164 DataChanged(); // calls UpdateParenthesis() 4165 InvalidateAttribs(); //! in DataChanged ? 4166 } 4167 4168 if (pTopView && eMode != SC_INPUT_NONE) 4169 SyncViews(); 4170 } 4171 } 4172 4173 void ScInputHandler::NotifyChange( const ScInputHdlState* pState, 4174 bool bForce, ScTabViewShell* pSourceSh, 4175 bool bStopEditing) 4176 { 4177 // If the call originates from a macro call in the EnterHandler, 4178 // return immediately and don't mess up the status 4179 if (bInEnterHandler) 4180 return; 4181 4182 bool bRepeat = (pState == pLastState.get()); 4183 if (!bRepeat && pState && pLastState) 4184 bRepeat = (*pState == *pLastState); 4185 if (bRepeat && !bForce) 4186 return; 4187 4188 bInOwnChange = true; // disable ModifyHdl (reset below) 4189 4190 if ( pState && !pLastState ) // Enable again 4191 bForce = true; 4192 4193 bool bHadObject = pLastState && pLastState->GetEditData(); 4194 4195 //! Before EditEngine gets eventually created (so it gets the right pools) 4196 if ( pSourceSh ) 4197 pActiveViewSh = pSourceSh; 4198 else 4199 pActiveViewSh = dynamic_cast<ScTabViewShell*>( SfxViewShell::Current() ); 4200 4201 if (pActiveViewSh) 4202 ImplCreateEditEngine(); 4203 4204 if ( pState != pLastState.get() ) 4205 { 4206 pLastState.reset( pState ? new ScInputHdlState( *pState ) : nullptr); 4207 } 4208 4209 if ( pState && pActiveViewSh ) 4210 { 4211 ScModule* pScMod = SC_MOD(); 4212 4213 ScTabViewShell* pScTabViewShell = dynamic_cast<ScTabViewShell*>(pScMod->GetViewShell()); 4214 4215 // Also take foreign reference input into account here (e.g. FunctionsAutoPilot), 4216 // FormEditData, if we're switching from Help to Calc: 4217 if ( !bFormulaMode && !pScMod->IsFormulaMode() && 4218 ( !pScTabViewShell || !pScTabViewShell->GetFormEditData() ) ) 4219 { 4220 bool bIgnore = false; 4221 if ( bModified ) 4222 { 4223 if (pState->GetPos() != aCursorPos) 4224 { 4225 if (!bProtected) 4226 EnterHandler(); 4227 } 4228 else 4229 bIgnore = true; 4230 } 4231 4232 if ( !bIgnore ) 4233 { 4234 const ScAddress& rSPos = pState->GetStartPos(); 4235 const ScAddress& rEPos = pState->GetEndPos(); 4236 const EditTextObject* pData = pState->GetEditData(); 4237 OUString aString = pState->GetString(); 4238 bool bTxtMod = false; 4239 ScDocShell* pDocSh = pActiveViewSh->GetViewData().GetDocShell(); 4240 ScDocument& rDoc = pDocSh->GetDocument(); 4241 4242 aCursorPos = pState->GetPos(); 4243 4244 if ( pData ) 4245 bTxtMod = true; 4246 else if ( bHadObject ) 4247 bTxtMod = true; 4248 else if ( bTextValid ) 4249 bTxtMod = ( aString != aCurrentText ); 4250 else 4251 bTxtMod = ( aString != GetEditText(mpEditEngine.get()) ); 4252 4253 if ( bTxtMod || bForce ) 4254 { 4255 if (pData) 4256 { 4257 mpEditEngine->SetTextCurrentDefaults( *pData ); 4258 if (pInputWin) 4259 aString = ScEditUtil::GetMultilineString(*mpEditEngine); 4260 else 4261 aString = GetEditText(mpEditEngine.get()); 4262 lcl_RemoveTabs(aString); 4263 bTextValid = false; 4264 aCurrentText.clear(); 4265 } 4266 else 4267 { 4268 aCurrentText = aString; 4269 bTextValid = true; //! To begin with remember as a string 4270 } 4271 4272 if ( pInputWin ) 4273 pInputWin->SetTextString(aString); 4274 4275 if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh) 4276 { 4277 UpdateActiveView(); 4278 EditView* pActiveView = pTopView ? pTopView : pTableView; 4279 ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection(); 4280 4281 // if we switched content completely - don't send huge numbers 4282 if (aSel.nStartPara == EE_PARA_NOT_FOUND) 4283 aSel.nStartPara = 0; 4284 4285 if (aSel.nEndPara == EE_PARA_NOT_FOUND) 4286 aSel.nEndPara = 0; 4287 4288 ScInputHandler::LOKSendFormulabarUpdate(pActiveView, pActiveViewSh, aString, aSel); 4289 // TODO: deprecated? 4290 pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_FORMULA, aString.toUtf8()); 4291 } 4292 } 4293 4294 if ( pInputWin || comphelper::LibreOfficeKit::isActive()) // Named range input 4295 { 4296 OUString aPosStr; 4297 bool bSheetLocal = false; 4298 const ScAddress::Details aAddrDetails( rDoc, aCursorPos ); 4299 4300 // Is the range a name? 4301 //! Find by Timer? 4302 if ( pActiveViewSh ) 4303 pActiveViewSh->GetViewData().GetDocument(). 4304 GetRangeAtBlock( ScRange( rSPos, rEPos ), aPosStr, &bSheetLocal); 4305 4306 if ( aPosStr.isEmpty() ) // Not a name -> format 4307 { 4308 ScRefFlags nFlags = ScRefFlags::ZERO; 4309 if( aAddrDetails.eConv == formula::FormulaGrammar::CONV_XL_R1C1 ) 4310 nFlags |= ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS; 4311 if ( rSPos != rEPos ) 4312 { 4313 ScRange r(rSPos, rEPos); 4314 applyStartToEndFlags(nFlags); 4315 aPosStr = r.Format(rDoc, ScRefFlags::VALID | nFlags, aAddrDetails); 4316 } 4317 else 4318 aPosStr = aCursorPos.Format(ScRefFlags::VALID | nFlags, &rDoc, aAddrDetails); 4319 } 4320 else if (bSheetLocal) 4321 { 4322 OUString aName; 4323 if (rDoc.GetName( rSPos.Tab(), aName)) 4324 aPosStr = ScPosWnd::createLocalRangeName( aPosStr, aName); 4325 } 4326 4327 if (pInputWin) 4328 { 4329 // Disable the accessible VALUE_CHANGE event 4330 bool bIsSuppressed = pInputWin->IsAccessibilityEventsSuppressed(false); 4331 pInputWin->SetAccessibilityEventsSuppressed(true); 4332 pInputWin->SetPosString(aPosStr); 4333 pInputWin->SetAccessibilityEventsSuppressed(bIsSuppressed); 4334 pInputWin->SetSumAssignMode(); 4335 } 4336 4337 if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh) 4338 pActiveViewSh->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_ADDRESS, aPosStr.toUtf8()); 4339 } 4340 4341 if (bStopEditing) { 4342 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScKillEditView ) ); 4343 4344 // As long as the content is not edited, turn off online spelling. 4345 // Online spelling is turned back on in StartTable, after setting 4346 // the right language from cell attributes. 4347 4348 EEControlBits nCntrl = mpEditEngine->GetControlWord(); 4349 if ( nCntrl & EEControlBits::ONLINESPELLING ) 4350 mpEditEngine->SetControlWord( nCntrl & ~EEControlBits::ONLINESPELLING ); 4351 } 4352 4353 bModified = false; 4354 bSelIsRef = false; 4355 bProtected = false; 4356 bCommandErrorShown = false; 4357 } 4358 } 4359 4360 if ( pInputWin) 4361 { 4362 // Do not enable if RefDialog is open 4363 if(!pScMod->IsFormulaMode()&& !pScMod->IsRefDialogOpen()) 4364 { 4365 if ( !pInputWin->IsEnabled()) 4366 { 4367 pDelayTimer->Stop(); 4368 pInputWin->Enable(); 4369 } 4370 } 4371 else if(pScMod->IsRefDialogOpen()) 4372 { // Because every document has its own InputWin, 4373 // we should start Timer again, because the input line may 4374 // still be active 4375 if ( !pDelayTimer->IsActive() ) 4376 pDelayTimer->Start(); 4377 } 4378 } 4379 } 4380 else // !pState || !pActiveViewSh 4381 { 4382 if ( !pDelayTimer->IsActive() ) 4383 pDelayTimer->Start(); 4384 } 4385 4386 HideTip(); 4387 HideTipBelow(); 4388 bInOwnChange = false; 4389 } 4390 4391 void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust ) 4392 { 4393 eAttrAdjust = eJust; 4394 UpdateAdjust( 0 ); 4395 } 4396 4397 void ScInputHandler::ResetDelayTimer() 4398 { 4399 if( pDelayTimer->IsActive() ) 4400 { 4401 pDelayTimer->Stop(); 4402 if ( pInputWin ) 4403 pInputWin->Enable(); 4404 } 4405 } 4406 4407 IMPL_LINK_NOARG( ScInputHandler, DelayTimer, Timer*, void ) 4408 { 4409 if ( !(nullptr == pLastState || SC_MOD()->IsFormulaMode() || SC_MOD()->IsRefDialogOpen())) 4410 return; 4411 4412 //! New method at ScModule to query if function autopilot is open 4413 SfxViewFrame* pViewFrm = SfxViewFrame::Current(); 4414 if ( pViewFrm && pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) ) 4415 { 4416 if ( pInputWin) 4417 { 4418 pInputWin->EnableButtons( false ); 4419 pInputWin->Disable(); 4420 } 4421 } 4422 else if ( !bFormulaMode ) // Keep formula e.g. for help 4423 { 4424 bInOwnChange = true; // disable ModifyHdl (reset below) 4425 4426 pActiveViewSh = nullptr; 4427 mpEditEngine->SetTextCurrentDefaults( OUString() ); 4428 if ( pInputWin ) 4429 { 4430 pInputWin->SetPosString( OUString() ); 4431 pInputWin->SetTextString( OUString() ); 4432 pInputWin->Disable(); 4433 } 4434 4435 bInOwnChange = false; 4436 } 4437 } 4438 4439 void ScInputHandler::InputSelection( const EditView* pView ) 4440 { 4441 SyncViews( pView ); 4442 ShowTipCursor(); 4443 UpdateParenthesis(); // Selection changed -> update parentheses highlighting 4444 4445 // When the selection is changed manually, stop overwriting parentheses 4446 ResetAutoPar(); 4447 4448 if (comphelper::LibreOfficeKit::isActive() && pActiveViewSh) 4449 { 4450 EditView* pActiveView = pTopView ? pTopView : pTableView; 4451 ESelection aSel = pActiveView ? pActiveView->GetSelection() : ESelection(); 4452 ScInputHandler::LOKSendFormulabarUpdate(pActiveView, pActiveViewSh, GetEditString(), aSel); 4453 } 4454 } 4455 4456 void ScInputHandler::InputChanged( const EditView* pView, bool bFromNotify ) 4457 { 4458 if ( !pView ) 4459 return; 4460 4461 UpdateActiveView(); 4462 4463 // #i20282# DataChanged needs to know if this is from the input line's modify handler 4464 bool bFromTopNotify = ( bFromNotify && pView == pTopView ); 4465 4466 bool bNewView = DataChanging(); //FIXME: Is this at all possible? 4467 aCurrentText = pView->GetEditEngine()->GetText(); // Also remember the string 4468 mpEditEngine->SetTextCurrentDefaults( aCurrentText ); 4469 DataChanged( bFromTopNotify ); 4470 bTextValid = true; // Is set to false in DataChanged 4471 4472 if ( pActiveViewSh ) 4473 { 4474 ScViewData& rViewData = pActiveViewSh->GetViewData(); 4475 if ( bNewView ) 4476 rViewData.GetDocShell()->PostEditView( mpEditEngine.get(), aCursorPos ); 4477 4478 rViewData.EditGrowY(); 4479 rViewData.EditGrowX(); 4480 } 4481 4482 SyncViews( pView ); 4483 } 4484 4485 const OUString& ScInputHandler::GetEditString() 4486 { 4487 if (mpEditEngine) 4488 { 4489 aCurrentText = mpEditEngine->GetText(); // Always new from Engine 4490 bTextValid = true; 4491 } 4492 4493 return aCurrentText; 4494 } 4495 4496 Size ScInputHandler::GetTextSize() 4497 { 4498 Size aSize; 4499 if ( mpEditEngine ) 4500 aSize = Size( mpEditEngine->CalcTextWidth(), mpEditEngine->GetTextHeight() ); 4501 4502 return aSize; 4503 } 4504 4505 bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter& rDestEngine ) 4506 { 4507 bool bRet = false; 4508 if (mpEditEngine) 4509 { 4510 // Contains field? 4511 sal_Int32 nParCnt = mpEditEngine->GetParagraphCount(); 4512 SfxItemSet aSet = mpEditEngine->GetAttribs( ESelection(0,0,nParCnt,0) ); 4513 SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, false ); 4514 if ( eFieldState == SfxItemState::DONTCARE || eFieldState == SfxItemState::SET ) 4515 { 4516 // Copy content 4517 std::unique_ptr<EditTextObject> pObj = mpEditEngine->CreateTextObject(); 4518 rDestEngine.SetTextCurrentDefaults(*pObj); 4519 pObj.reset(); 4520 4521 // Delete attributes 4522 for (sal_Int32 i=0; i<nParCnt; i++) 4523 rDestEngine.RemoveCharAttribs( i ); 4524 4525 // Combine paragraphs 4526 while ( nParCnt > 1 ) 4527 { 4528 sal_Int32 nLen = rDestEngine.GetTextLen( 0 ); 4529 ESelection aSel( 0,nLen, 1,0 ); 4530 rDestEngine.QuickInsertText( OUString(' '), aSel ); // Replace line break with space 4531 --nParCnt; 4532 } 4533 4534 bRet = true; 4535 } 4536 } 4537 return bRet; 4538 } 4539 4540 /** 4541 * Methods for FunctionAutoPilot: 4542 * InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr 4543 */ 4544 void ScInputHandler::InputGetSelection( sal_Int32& rStart, sal_Int32& rEnd ) 4545 { 4546 rStart = nFormSelStart; 4547 rEnd = nFormSelEnd; 4548 } 4549 4550 EditView* ScInputHandler::GetFuncEditView() 4551 { 4552 UpdateActiveView(); // Due to pTableView 4553 4554 EditView* pView = nullptr; 4555 if ( pInputWin ) 4556 { 4557 pInputWin->MakeDialogEditView(); 4558 pView = pInputWin->GetEditView(); 4559 } 4560 else 4561 { 4562 if ( eMode != SC_INPUT_TABLE ) 4563 { 4564 bCreatingFuncView = true; // Don't display RangeFinder 4565 SetMode( SC_INPUT_TABLE ); 4566 bCreatingFuncView = false; 4567 if ( pTableView ) 4568 pTableView->GetEditEngine()->SetText( OUString() ); 4569 } 4570 pView = pTableView; 4571 } 4572 4573 return pView; 4574 } 4575 4576 void ScInputHandler::InputSetSelection( sal_Int32 nStart, sal_Int32 nEnd ) 4577 { 4578 if ( nStart <= nEnd ) 4579 { 4580 nFormSelStart = nStart; 4581 nFormSelEnd = nEnd; 4582 } 4583 else 4584 { 4585 nFormSelEnd = nStart; 4586 nFormSelStart = nEnd; 4587 } 4588 4589 EditView* pView = GetFuncEditView(); 4590 if (pView) 4591 pView->SetSelection( ESelection(0,nStart, 0,nEnd) ); 4592 4593 bModified = true; 4594 } 4595 4596 void ScInputHandler::InputReplaceSelection( std::u16string_view aStr ) 4597 { 4598 if (!pRefViewSh) 4599 pRefViewSh = pActiveViewSh; 4600 4601 OSL_ENSURE(nFormSelEnd>=nFormSelStart,"Selection broken..."); 4602 4603 sal_Int32 nOldLen = nFormSelEnd - nFormSelStart; 4604 sal_Int32 nNewLen = aStr.size(); 4605 4606 OUStringBuffer aBuf(aFormText); 4607 if (nOldLen) 4608 aBuf.remove(nFormSelStart, nOldLen); 4609 if (nNewLen) 4610 aBuf.insert(nFormSelStart, aStr); 4611 4612 aFormText = aBuf.makeStringAndClear(); 4613 4614 nFormSelEnd = nFormSelStart + nNewLen; 4615 4616 EditView* pView = GetFuncEditView(); 4617 if (pView) 4618 { 4619 pView->SetEditEngineUpdateLayout( false ); 4620 pView->GetEditEngine()->SetText( aFormText ); 4621 pView->SetSelection( ESelection(0,nFormSelStart, 0,nFormSelEnd) ); 4622 pView->SetEditEngineUpdateLayout( true ); 4623 } 4624 bModified = true; 4625 } 4626 4627 void ScInputHandler::InputTurnOffWinEngine() 4628 { 4629 bInOwnChange = true; // disable ModifyHdl (reset below) 4630 4631 eMode = SC_INPUT_NONE; 4632 /* TODO: it would be better if there was some way to reset the input bar 4633 * engine instead of deleting and having it recreate through 4634 * GetFuncEditView(), but first least invasively let this fix fdo#71667 and 4635 * fdo#72278 without reintroducing fdo#69971. */ 4636 StopInputWinEngine(true); 4637 4638 bInOwnChange = false; 4639 } 4640 4641 /** 4642 * ScInputHdlState 4643 */ 4644 ScInputHdlState::ScInputHdlState( const ScAddress& rCurPos, 4645 const ScAddress& rStartPos, 4646 const ScAddress& rEndPos, 4647 OUString _aString, 4648 const EditTextObject* pData ) 4649 : aCursorPos ( rCurPos ), 4650 aStartPos ( rStartPos ), 4651 aEndPos ( rEndPos ), 4652 aString (std::move( _aString )), 4653 pEditData ( pData ? pData->Clone() : nullptr ) 4654 { 4655 } 4656 4657 ScInputHdlState::ScInputHdlState( const ScInputHdlState& rCpy ) 4658 { 4659 *this = rCpy; 4660 } 4661 4662 ScInputHdlState::~ScInputHdlState() 4663 { 4664 } 4665 4666 bool ScInputHdlState::operator==( const ScInputHdlState& r ) const 4667 { 4668 return ( (aStartPos == r.aStartPos) 4669 && (aEndPos == r.aEndPos) 4670 && (aCursorPos == r.aCursorPos) 4671 && (aString == r.aString) 4672 && ScGlobal::EETextObjEqual( pEditData.get(), r.pEditData.get() ) ); 4673 } 4674 4675 ScInputHdlState& ScInputHdlState::operator=( const ScInputHdlState& r ) 4676 { 4677 if (this != &r) 4678 { 4679 aCursorPos = r.aCursorPos; 4680 aStartPos = r.aStartPos; 4681 aEndPos = r.aEndPos; 4682 aString = r.aString; 4683 pEditData.reset(); 4684 if (r.pEditData) 4685 pEditData = r.pEditData->Clone(); 4686 } 4687 return *this; 4688 } 4689 4690 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 4691
