/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" //------------------------------------------------------------------ #include "scitems.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inputwin.hxx" #include "tabvwsh.hxx" #include "docsh.hxx" #include "scmod.hxx" #include "uiitems.hxx" #include "global.hxx" #include "sc.hrc" #include "globstr.hrc" #include "patattr.hxx" #include "viewdata.hxx" #include "document.hxx" #include "docpool.hxx" #include "editutil.hxx" #include "collect.hxx" #include "appoptio.hxx" #include "docoptio.hxx" #include "validat.hxx" #include "userlist.hxx" #include "rfindlst.hxx" #include "inputopt.hxx" #include "cell.hxx" // fuer Formel-Preview #include "compiler.hxx" // fuer Formel-Preview #include "editable.hxx" #include "funcdesc.hxx" #define _INPUTHDL_CXX #include "inputhdl.hxx" // max. Ranges im RangeFinder #define RANGEFIND_MAX 32 using namespace formula; // STATIC DATA ----------------------------------------------------------- sal_Bool ScInputHandler::bOptLoaded = sal_False; // App-Optionen ausgewertet sal_Bool ScInputHandler::bAutoComplete = sal_False; // wird in KeyInput gesetzt // delimiters (in addition to ScEditUtil) needed for range finder: // only characters that are allowed in formulas next to references // and the quotation mark (so string constants can be skipped) static const sal_Char __FAR_DATA pMinDelimiters[] = " !\""; extern sal_uInt16 nEditAdjust; //! Member an ViewData //================================================================== static sal_Unicode lcl_getSheetSeparator(ScDocument* pDoc) { ScCompiler aComp(pDoc, ScAddress()); aComp.SetGrammar(pDoc->GetGrammar()); return aComp.GetNativeAddressSymbol(ScCompiler::Convention::SHEET_SEPARATOR); } void ScInputHandler::InitRangeFinder( const String& rFormula ) { DeleteRangeFinder(); ScDocShell* pDocSh = pActiveViewSh->GetViewData()->GetDocShell(); ScDocument* pDoc = pDocSh->GetDocument(); const sal_Unicode cSheetSep = lcl_getSheetSeparator(pDoc); if ( !pActiveViewSh || !SC_MOD()->GetInputOptions().GetRangeFinder() ) return; // String aDelimiters = pEngine->GetWordDelimiters(); String aDelimiters = ScEditUtil::ModifyDelimiters( String::CreateFromAscii( pMinDelimiters ) ); xub_StrLen nColon = aDelimiters.Search(':'); if ( nColon != STRING_NOTFOUND ) aDelimiters.Erase( nColon, 1 ); // Delimiter ohne Doppelpunkt xub_StrLen nDot = aDelimiters.Search(cSheetSep); if ( nDot != STRING_NOTFOUND ) aDelimiters.Erase( nDot, 1 ); // Delimiter ohne Punkt const sal_Unicode* pChar = rFormula.GetBuffer(); xub_StrLen nLen = rFormula.Len(); xub_StrLen nPos = 0; xub_StrLen nStart = 0; sal_uInt16 nCount = 0; ScRange aRange; while ( nPos < nLen && nCount < RANGEFIND_MAX ) { // Trenner ueberlesen while ( nPos 0 && '-' == pChar[nPos] && '[' == pChar[nPos-1] && NULL != pDoc && formula::FormulaGrammar::CONV_XL_R1C1 == pDoc->GetAddressConvention() ) { nPos++; goto handle_r1c1; } if ( nPos > nStart ) { String aTest = rFormula.Copy( nStart, nPos-nStart ); const ScAddress::Details aAddrDetails( pDoc, aCursorPos ); sal_uInt16 nFlags = aRange.ParseAny( aTest, pDoc, aAddrDetails ); if ( nFlags & SCA_VALID ) { // Tabelle setzen, wenn nicht angegeben if ( (nFlags & SCA_TAB_3D) == 0 ) aRange.aStart.SetTab( pActiveViewSh->GetViewData()->GetTabNo() ); if ( (nFlags & SCA_TAB2_3D) == 0 ) aRange.aEnd.SetTab( aRange.aStart.Tab() ); if ( ( nFlags & ( SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2 ) ) == 0 ) { // #i73766# if a single ref was parsed, set the same "abs" flags for ref2, // so Format doesn't output a double ref because of different flags. sal_uInt16 nAbsFlags = nFlags & ( SCA_COL_ABSOLUTE | SCA_ROW_ABSOLUTE | SCA_TAB_ABSOLUTE ); nFlags |= nAbsFlags << 4; } if (!nCount) { pEngine->SetUpdateMode( sal_False ); pRangeFindList = new ScRangeFindList( pDocSh->GetTitle() ); } ScRangeFindData* pNew = new ScRangeFindData( aRange, nFlags, nStart, nPos ); pRangeFindList->Insert( pNew ); ESelection aSel( 0, nStart, 0, nPos ); SfxItemSet aSet( pEngine->GetEmptyItemSet() ); aSet.Put( SvxColorItem( Color( ScRangeFindList::GetColorName( nCount ) ), EE_CHAR_COLOR ) ); pEngine->QuickSetAttribs( aSet, aSel ); ++nCount; } } // letzten Trenner nicht ueberlesen, koennte ja ein Quote sein (?) } if (nCount) { pEngine->SetUpdateMode( sal_True ); pDocSh->Broadcast( SfxSimpleHint( SC_HINT_SHOWRANGEFINDER ) ); } } void lcl_Replace( EditView* pView, const String& rNewStr, const ESelection& rOldSel ) { if ( pView ) { ESelection aOldSel = pView->GetSelection(); if (aOldSel.HasRange()) pView->SetSelection( ESelection( aOldSel.nEndPara, aOldSel.nEndPos, aOldSel.nEndPara, aOldSel.nEndPos ) ); EditEngine* pEngine = pView->GetEditEngine(); pEngine->QuickInsertText( rNewStr, rOldSel ); // Dummy-InsertText fuer Update und Paint // dafuer muss oben die Selektion aufgehoben werden (vor QuickInsertText) pView->InsertText( EMPTY_STRING, sal_False ); xub_StrLen nLen = pEngine->GetTextLen(0); ESelection aSel( 0, nLen, 0, nLen ); pView->SetSelection( aSel ); // Cursor ans Ende } } void ScInputHandler::UpdateRange( sal_uInt16 nIndex, const ScRange& rNew ) { ScTabViewShell* pDocView = pRefViewSh ? pRefViewSh : pActiveViewSh; if ( pDocView && pRangeFindList && nIndex < pRangeFindList->Count() ) { ScRangeFindData* pData = pRangeFindList->GetObject( nIndex ); xub_StrLen nOldStart = pData->nSelStart; xub_StrLen nOldEnd = pData->nSelEnd; ScRange aJustified = rNew; aJustified.Justify(); // Ref in der Formel immer richtigherum anzeigen String aNewStr; ScDocument* pDoc = pDocView->GetViewData()->GetDocument(); const ScAddress::Details aAddrDetails( pDoc, aCursorPos ); aJustified.Format( aNewStr, pData->nFlags, pDoc, aAddrDetails ); ESelection aOldSel( 0, nOldStart, 0, nOldEnd ); DataChanging(); lcl_Replace( pTopView, aNewStr, aOldSel ); lcl_Replace( pTableView, aNewStr, aOldSel ); bInRangeUpdate = sal_True; DataChanged(); bInRangeUpdate = sal_False; long nDiff = aNewStr.Len() - (long)(nOldEnd-nOldStart); pData->aRef = rNew; pData->nSelEnd = (xub_StrLen)(pData->nSelEnd + nDiff); sal_uInt16 nCount = (sal_uInt16) pRangeFindList->Count(); for (sal_uInt16 i=nIndex+1; iGetObject( i ); pNext->nSelStart = (xub_StrLen)(pNext->nSelStart + nDiff); pNext->nSelEnd = (xub_StrLen)(pNext->nSelEnd + nDiff); } } else { DBG_ERROR("UpdateRange: da fehlt was"); } } void ScInputHandler::DeleteRangeFinder() { ScTabViewShell* pPaintView = pRefViewSh ? pRefViewSh : pActiveViewSh; if ( pRangeFindList && pPaintView ) { ScDocShell* pDocSh = pActiveViewSh->GetViewData()->GetDocShell(); pRangeFindList->SetHidden(sal_True); pDocSh->Broadcast( SfxSimpleHint( SC_HINT_SHOWRANGEFINDER ) ); // wegnehmen DELETEZ(pRangeFindList); } } //================================================================== inline String GetEditText(EditEngine* pEng) { return ScEditUtil::GetSpaceDelimitedString(*pEng); } void lcl_RemoveTabs(String& rStr) { xub_StrLen nPos; while ( (nPos=rStr.Search('\t')) != STRING_NOTFOUND ) rStr.SetChar( nPos, ' ' ); } void lcl_RemoveLineEnd(String& rStr) { rStr.ConvertLineEnd(LINEEND_LF); xub_StrLen nPos; while ( (nPos=rStr.Search('\n')) != STRING_NOTFOUND ) rStr.SetChar( nPos, ' ' ); } xub_StrLen lcl_MatchParenthesis( const String& rStr, xub_StrLen nPos ) { int nDir; sal_Unicode c1, c2 = 0; c1 = rStr.GetChar( nPos ); switch ( c1 ) { case '(' : c2 = ')'; nDir = 1; break; case ')' : c2 = '('; nDir = -1; break; case '<' : c2 = '>'; nDir = 1; break; case '>' : c2 = '<'; nDir = -1; break; case '{' : c2 = '}'; nDir = 1; break; case '}' : c2 = '{'; nDir = -1; break; case '[' : c2 = ']'; nDir = 1; break; case ']' : c2 = '['; nDir = -1; break; default: nDir = 0; } if ( !nDir ) return STRING_NOTFOUND; xub_StrLen nLen = rStr.Len(); const sal_Unicode* p0 = rStr.GetBuffer(); register const sal_Unicode* p; const sal_Unicode* p1; sal_uInt16 nQuotes = 0; if ( nPos < nLen / 2 ) { p = p0; p1 = p0 + nPos; } else { p = p0 + nPos; p1 = p0 + nLen; } while ( p < p1 ) { if ( *p++ == '\"' ) nQuotes++; } // Odd number of quotes that we find ourselves in a string sal_Bool bLookInString = ((nQuotes % 2) != 0); sal_Bool bInString = bLookInString; p = p0 + nPos; p1 = (nDir < 0 ? p0 : p0 + nLen) ; sal_uInt16 nLevel = 1; while ( p != p1 && nLevel ) { p += nDir; if ( *p == '\"' ) { bInString = !bInString; if ( bLookInString && !bInString ) p = p1; //That's it then } else if ( bInString == bLookInString ) { if ( *p == c1 ) nLevel++; else if ( *p == c2 ) nLevel--; } } if ( nLevel ) return STRING_NOTFOUND; return (xub_StrLen) (p - p0); } //================================================================== ScInputHandler::ScInputHandler() : pInputWin( NULL ), pEngine( NULL ), pTableView( NULL ), pTopView( NULL ), pColumnData( NULL ), pFormulaData( NULL ), pFormulaDataPara( NULL ), pTipVisibleParent( NULL ), nTipVisible( 0 ), pTipVisibleSecParent( NULL ), nTipVisibleSec( 0 ), nAutoPos( SCPOS_INVALID ), bUseTab( sal_False ), bTextValid( sal_True ), nFormSelStart( 0 ), nFormSelEnd( 0 ), nAutoPar( 0 ), eMode( SC_INPUT_NONE ), bModified( sal_False ), bSelIsRef( sal_False ), bFormulaMode( sal_False ), bInRangeUpdate( sal_False ), bParenthesisShown( sal_False ), bCreatingFuncView( sal_False ), bInEnterHandler( sal_False ), bCommandErrorShown( sal_False ), bInOwnChange( sal_False ), bProtected( sal_False ), bCellHasPercentFormat( sal_False ), nValidation( 0 ), eAttrAdjust( SVX_HOR_JUSTIFY_STANDARD ), aScaleX( 1,1 ), aScaleY( 1,1 ), pRefViewSh( NULL ), pLastPattern( NULL ), pEditDefaults( NULL ), bLastIsSymbol( sal_False ), pLastState( NULL ), pDelayTimer( NULL ), pRangeFindList( NULL ) { // The InputHandler is constructed with the view, so SfxViewShell::Current // doesn't have the right view yet. pActiveViewSh is updated in NotifyChange. pActiveViewSh = NULL; // Bindings (nur noch fuer Invalidate benutzt) werden bei Bedarf aktuell geholt } __EXPORT ScInputHandler::~ScInputHandler() { // Wenn dies der Applikations-InputHandler ist, wird der dtor erst nach SfxApplication::Main // gerufen, darf sich also auf keine Sfx-Funktionen mehr verlassen if ( !SFX_APP()->IsDowning() ) // inplace EnterHandler(); // Eingabe noch abschliessen if (SC_MOD()->GetRefInputHdl()==this) SC_MOD()->SetRefInputHdl(NULL); if ( pInputWin && pInputWin->GetInputHandler() == this ) pInputWin->SetInputHandler( NULL ); delete pRangeFindList; delete pEditDefaults; delete pEngine; delete pLastState; delete pDelayTimer; delete pColumnData; delete pFormulaData; delete pFormulaDataPara; } void ScInputHandler::SetRefScale( const Fraction& rX, const Fraction& rY ) { if ( rX != aScaleX || rY != aScaleY ) { aScaleX = rX; aScaleY = rY; if (pEngine) { MapMode aMode( MAP_100TH_MM, Point(), aScaleX, aScaleY ); pEngine->SetRefMapMode( aMode ); } } } void ScInputHandler::UpdateRefDevice() { if (!pEngine) return; sal_Bool bTextWysiwyg = SC_MOD()->GetInputOptions().GetTextWysiwyg(); bool bInPlace = pActiveViewSh && pActiveViewSh->GetViewFrame()->GetFrame().IsInPlace(); sal_uInt32 nCtrl = pEngine->GetControlWord(); if ( bTextWysiwyg || bInPlace ) nCtrl |= EE_CNTRL_FORMAT100; // EditEngine default: always format for 100% else nCtrl &= ~EE_CNTRL_FORMAT100; // when formatting for screen, use the actual MapMode pEngine->SetControlWord( nCtrl ); if ( bTextWysiwyg && pActiveViewSh ) pEngine->SetRefDevice( pActiveViewSh->GetViewData()->GetDocument()->GetPrinter() ); else pEngine->SetRefDevice( NULL ); MapMode aMode( MAP_100TH_MM, Point(), aScaleX, aScaleY ); pEngine->SetRefMapMode( aMode ); // SetRefDevice(NULL) uses VirtualDevice, SetRefMapMode forces creation of a local VDev, // so the DigitLanguage can be safely modified (might use an own VDev instead of NULL). if ( !( bTextWysiwyg && pActiveViewSh ) ) { pEngine->GetRefDevice()->SetDigitLanguage( SC_MOD()->GetOptDigitLanguage() ); } } void ScInputHandler::ImplCreateEditEngine() { if ( !pEngine ) { if ( pActiveViewSh ) { const ScDocument* pDoc = pActiveViewSh->GetViewData()->GetDocShell()->GetDocument(); pEngine = new ScFieldEditEngine( pDoc->GetEnginePool(), pDoc->GetEditPool() ); } else pEngine = new ScFieldEditEngine( EditEngine::CreatePool(), NULL, sal_True ); pEngine->SetWordDelimiters( ScEditUtil::ModifyDelimiters( pEngine->GetWordDelimiters() ) ); UpdateRefDevice(); // also sets MapMode pEngine->SetPaperSize( Size( 1000000, 1000000 ) ); pEditDefaults = new SfxItemSet( pEngine->GetEmptyItemSet() ); pEngine->SetControlWord( pEngine->GetControlWord() | EE_CNTRL_AUTOCORRECT ); pEngine->SetModifyHdl( LINK( this, ScInputHandler, ModifyHdl ) ); } } void ScInputHandler::UpdateAutoCorrFlag() { sal_uLong nCntrl = pEngine->GetControlWord(); sal_uLong nOld = nCntrl; // don't use pLastPattern here (may be invalid because of AutoStyle) sal_Bool bDisable = bLastIsSymbol || bFormulaMode; if ( bDisable ) nCntrl &= ~EE_CNTRL_AUTOCORRECT; else nCntrl |= EE_CNTRL_AUTOCORRECT; if ( nCntrl != nOld ) pEngine->SetControlWord(nCntrl); } void ScInputHandler::UpdateSpellSettings( sal_Bool bFromStartTab ) { if ( pActiveViewSh ) { ScViewData* pViewData = pActiveViewSh->GetViewData(); sal_Bool bOnlineSpell = pViewData->GetDocument()->GetDocOptions().IsAutoSpell(); // SetDefaultLanguage is independent of the language attributes, // ScGlobal::GetEditDefaultLanguage is always used. // It must be set every time in case the office language was changed. pEngine->SetDefaultLanguage( ScGlobal::GetEditDefaultLanguage() ); // if called for changed options, update flags only if already editing // if called from StartTable, always update flags if ( bFromStartTab || eMode != SC_INPUT_NONE ) { sal_uLong nCntrl = pEngine->GetControlWord(); sal_uLong nOld = nCntrl; if( bOnlineSpell ) nCntrl |= EE_CNTRL_ONLINESPELLING; else nCntrl &= ~EE_CNTRL_ONLINESPELLING; // kein AutoCorrect auf Symbol-Font (EditEngine wertet Default nicht aus) if ( pLastPattern && pLastPattern->IsSymbolFont() ) nCntrl &= ~EE_CNTRL_AUTOCORRECT; else nCntrl |= EE_CNTRL_AUTOCORRECT; if ( nCntrl != nOld ) pEngine->SetControlWord(nCntrl); ScDocument* pDoc = pViewData->GetDocument(); pDoc->ApplyAsianEditSettings( *pEngine ); pEngine->SetDefaultHorizontalTextDirection( (EEHorizontalTextDirection)pDoc->GetEditTextDirection( pViewData->GetTabNo() ) ); pEngine->SetFirstWordCapitalization( sal_False ); } // language is set separately, so the speller is needed only if online // spelling is active if ( bOnlineSpell ) { com::sun::star::uno::Reference xXSpellChecker1( LinguMgr::GetSpellChecker() ); pEngine->SetSpeller( xXSpellChecker1 ); } sal_Bool bHyphen = pLastPattern && ((const SfxBoolItem&)pLastPattern->GetItem(ATTR_HYPHENATE)).GetValue(); if ( bHyphen ) { com::sun::star::uno::Reference xXHyphenator( LinguMgr::GetHyphenator() ); pEngine->SetHyphenator( xXHyphenator ); } } } // // Funktionen/Bereichsnamen etc. als Tip-Hilfe // #define SC_STRTYPE_FUNCTIONS 1 // die anderen Typen sind in ScDocument::GetFormulaEntries festgelegt void ScInputHandler::GetFormulaData() { if ( pActiveViewSh ) { ScDocument* pDoc = pActiveViewSh->GetViewData()->GetDocShell()->GetDocument(); if ( pFormulaData ) pFormulaData->FreeAll(); else pFormulaData = new TypedScStrCollection; if( pFormulaDataPara ) pFormulaDataPara->FreeAll(); else pFormulaDataPara = new TypedScStrCollection; // MRU-Funktionen aus dem Funktions-Autopiloten // wie in ScPosWnd::FillFunctions (inputwin.cxx) const ScAppOptions& rOpt = SC_MOD()->GetAppOptions(); sal_uInt16 nMRUCount = rOpt.GetLRUFuncListCount(); const sal_uInt16* pMRUList = rOpt.GetLRUFuncList(); const ScFunctionList* pFuncList = ScGlobal::GetStarCalcFunctionList(); sal_uLong nListCount = pFuncList->GetCount(); if (pMRUList) { for (sal_uInt16 i=0; iGetFunction( j ); if ( pDesc->nFIndex == nId && pDesc->pFuncName ) { String aEntry = *pDesc->pFuncName; aEntry.AppendAscii(RTL_CONSTASCII_STRINGPARAM( "()" )); TypedStrData* pData = new TypedStrData( aEntry, 0.0, SC_STRTYPE_FUNCTIONS ); if (!pFormulaData->Insert(pData)) delete pData; break; // nicht weitersuchen } } } } for(sal_uLong i=0;iGetFunction( i ); if ( pDesc->pFuncName ) { pDesc->initArgumentInfo(); String aEntry = pDesc->GetSignature(); TypedStrData* pData = new TypedStrData( aEntry, 0.0, SC_STRTYPE_FUNCTIONS ); if (!pFormulaDataPara->Insert(pData)) delete pData; } } pDoc->GetFormulaEntries( *pFormulaData ); pDoc->GetFormulaEntries( *pFormulaDataPara ); } } IMPL_LINK( ScInputHandler, ShowHideTipVisibleParentListener, VclWindowEvent*, pEvent ) { if( pEvent->GetId() == VCLEVENT_OBJECT_DYING || pEvent->GetId() == VCLEVENT_WINDOW_HIDE ) HideTip(); return 0; } IMPL_LINK( ScInputHandler, ShowHideTipVisibleSecParentListener, VclWindowEvent*, pEvent ) { if( pEvent->GetId() == VCLEVENT_OBJECT_DYING || pEvent->GetId() == VCLEVENT_WINDOW_HIDE ) HideTipBelow(); return 0; } void ScInputHandler::HideTip() { if ( nTipVisible ) { if (pTipVisibleParent) pTipVisibleParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) ); Help::HideTip( nTipVisible ); nTipVisible = 0; pTipVisibleParent = NULL; } aManualTip.Erase(); } void ScInputHandler::HideTipBelow() { if ( nTipVisibleSec ) { if (pTipVisibleSecParent) pTipVisibleSecParent->RemoveEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) ); Help::HideTip( nTipVisibleSec ); nTipVisibleSec = 0; pTipVisibleSecParent = NULL; } aManualTip.Erase(); } void ScInputHandler::ShowTipCursor() { HideTip(); HideTipBelow(); EditView* pActiveView = pTopView ? pTopView : pTableView; ScDocShell* pDocSh = pActiveViewSh->GetViewData()->GetDocShell(); const sal_Unicode cSep = ScCompiler::GetNativeSymbol(ocSep).GetChar(0); const sal_Unicode cSheetSep = lcl_getSheetSeparator(pDocSh->GetDocument()); if ( bFormulaMode && pActiveView && pFormulaDataPara && pEngine->GetParagraphCount() == 1 ) { String aFormula = pEngine->GetText( (sal_uInt16) 0 ); ESelection aSel = pActiveView->GetSelection(); aSel.Adjust(); xub_StrLen nLeftParentPos = 0; if( aSel.nEndPos ) { if ( aFormula.Len() < aSel.nEndPos ) return; xub_StrLen nPos = aSel.nEndPos; String aSelText = aFormula.Copy( 0, nPos ); xub_StrLen nNextFStart = 0; xub_StrLen nNextFEnd = 0; xub_StrLen nArgPos = 0; const IFunctionDescription* ppFDesc; ::std::vector< ::rtl::OUString> aArgs; sal_uInt16 nArgs; sal_Bool bFound = sal_False; FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr()); while( !bFound ) { aSelText.AppendAscii( RTL_CONSTASCII_STRINGPARAM( ")" ) ); nLeftParentPos = lcl_MatchParenthesis( aSelText, aSelText.Len()-1 ); if( nLeftParentPos != STRING_NOTFOUND ) { sal_Unicode c = ( nLeftParentPos > 0 ) ? aSelText.GetChar( nLeftParentPos-1 ) : 0; if( !((c >= 'A' && c<= 'Z') || (c>= 'a' && c<= 'z' )) ) continue; nNextFStart = aHelper.GetFunctionStart( aSelText, nLeftParentPos, sal_True); if( aHelper.GetNextFunc( aSelText, sal_False, nNextFStart, &nNextFEnd, &ppFDesc, &aArgs ) ) { if( ppFDesc->getFunctionName().getLength() ) { nArgPos = aHelper.GetArgStart( aSelText, nNextFStart, 0 ); nArgs = static_cast(ppFDesc->getParameterCount()); sal_uInt16 nActive = 0; sal_uInt16 nCount = 0; sal_uInt16 nCountSemicolon = 0; sal_uInt16 nCountDot = 0; sal_uInt16 nStartPosition = 0; sal_uInt16 nEndPosition = 0; sal_Bool bFlag = sal_False; String aNew; sal_uInt16 nParAutoPos = SCPOS_INVALID; if( pFormulaDataPara->FindText( ppFDesc->getFunctionName(), aNew, nParAutoPos, sal_False ) ) { for( sal_uInt16 i=0; i < nArgs; i++ ) { xub_StrLen nLength = static_cast(aArgs[i].getLength()); if( nArgPos <= aSelText.Len()-1 ) { nActive = i+1; bFlag = sal_True; } nArgPos+=nLength+1; } if( bFlag ) { nCountSemicolon = aNew.GetTokenCount(cSep)-1; nCountDot = aNew.GetTokenCount(cSheetSep)-1; if( !nCountSemicolon ) { for( sal_uInt16 i = 0; i < aNew.Len(); i++ ) { sal_Unicode cNext = aNew.GetChar( i ); if( cNext == '(' ) { nStartPosition = i+1; } } } else if( !nCountDot ) { for( sal_uInt16 i = 0; i < aNew.Len(); i++ ) { sal_Unicode cNext = aNew.GetChar( i ); if( cNext == '(' ) { nStartPosition = i+1; } else if( cNext == cSep ) { nCount ++; nEndPosition = i; if( nCount == nActive ) { break; } nStartPosition = nEndPosition+1; } } } else { for( sal_uInt16 i = 0; i < aNew.Len(); i++ ) { sal_Unicode cNext = aNew.GetChar( i ); if( cNext == '(' ) { nStartPosition = i+1; } else if( cNext == cSep ) { nCount ++; nEndPosition = i; if( nCount == nActive ) { break; } nStartPosition = nEndPosition+1; } else if( cNext == cSheetSep ) { continue; } } } if( nStartPosition ) { aNew.Insert( 0x25BA, nStartPosition ); ShowTipBelow( aNew ); bFound = sal_True; } } else { ShowTipBelow( aNew ); bFound = sal_True; } } } } } else { sal_uInt16 nPosition = 0; String aText = pEngine->GetWord( 0, aSel.nEndPos-1 ); if( aText.GetChar( aSel.nEndPos-1 ) == '=' ) { break; } String aNew; sal_uInt16 nParAutoPos = SCPOS_INVALID; nPosition = aText.Len()+1; if( pFormulaDataPara->FindText( aText, aNew, nParAutoPos, sal_False ) ) { if( aFormula.GetChar( nPosition ) =='(' ) { ShowTipBelow( aNew ); bFound = sal_True; } else break; } else { break; } } } } } } void ScInputHandler::ShowTip( const String& rText ) { // aManualTip muss hinterher von aussen gesetzt werden HideTip(); EditView* pActiveView = pTopView ? pTopView : pTableView; if (pActiveView) { Point aPos; pTipVisibleParent = pActiveView->GetWindow(); Cursor* pCur = pActiveView->GetCursor(); if (pCur) aPos = pTipVisibleParent->LogicToPixel( pCur->GetPos() ); aPos = pTipVisibleParent->OutputToScreenPixel( aPos ); Rectangle aRect( aPos, aPos ); sal_uInt16 nAlign = QUICKHELP_LEFT|QUICKHELP_BOTTOM; nTipVisible = Help::ShowTip(pTipVisibleParent, aRect, rText, nAlign); pTipVisibleParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleParentListener ) ); } } void ScInputHandler::ShowTipBelow( const String& rText ) { HideTipBelow(); EditView* pActiveView = pTopView ? pTopView : pTableView; if ( pActiveView ) { Point aPos; pTipVisibleSecParent = pActiveView->GetWindow(); Cursor* pCur = pActiveView->GetCursor(); if ( pCur ) { Point aLogicPos = pCur->GetPos(); aLogicPos.Y() += pCur->GetHeight(); aPos = pTipVisibleSecParent->LogicToPixel( aLogicPos ); } aPos = pTipVisibleSecParent->OutputToScreenPixel( aPos ); Rectangle aRect( aPos, aPos ); sal_uInt16 nAlign = QUICKHELP_LEFT | QUICKHELP_TOP | QUICKHELP_NOEVADEPOINTER; nTipVisibleSec = Help::ShowTip(pTipVisibleSecParent, aRect, rText, nAlign); pTipVisibleSecParent->AddEventListener( LINK( this, ScInputHandler, ShowHideTipVisibleSecParentListener ) ); } } void ScInputHandler::UseFormulaData() { EditView* pActiveView = pTopView ? pTopView : pTableView; ScDocShell* pDocSh = pActiveViewSh->GetViewData()->GetDocShell(); const sal_Unicode cSep = ScCompiler::GetNativeSymbol(ocSep).GetChar(0); const sal_Unicode cSheetSep = lcl_getSheetSeparator(pDocSh->GetDocument()); // Formeln duerfen nur 1 Absatz haben if ( pActiveView && pFormulaData && pEngine->GetParagraphCount() == 1 ) { String aTotal = pEngine->GetText( (sal_uInt16) 0 ); ESelection aSel = pActiveView->GetSelection(); aSel.Adjust(); // #59348# Durch Differenzen zwischen Tabelle und Eingabezeile // (z.B. Clipboard mit Zeilenumbruechen) kann es sein, dass die Selektion // nicht mehr zur EditEngine passt. Dann halt kommentarlos abbrechen: if ( aSel.nEndPos > aTotal.Len() ) return; // steht der Cursor am Ende eines Wortes? if ( aSel.nEndPos > 0 ) { xub_StrLen nPos = aSel.nEndPos; String aFormula = aTotal.Copy( 0, nPos );; xub_StrLen nLeftParentPos = 0; xub_StrLen nNextFStart = 0; xub_StrLen nNextFEnd = 0; xub_StrLen nArgPos = 0; const IFunctionDescription* ppFDesc; ::std::vector< ::rtl::OUString> aArgs; sal_uInt16 nArgs; sal_Bool bFound = sal_False; String aText = pEngine->GetWord( 0, aSel.nEndPos-1 ); if ( aText.Len() ) { String aNew; nAutoPos = SCPOS_INVALID; if ( pFormulaData->FindText( aText, aNew, nAutoPos, sal_False ) ) { ShowTip( aNew ); aAutoSearch = aText; } } FormulaHelper aHelper(ScGlobal::GetStarCalcFunctionMgr()); while( !bFound ) { aFormula.AppendAscii( RTL_CONSTASCII_STRINGPARAM( ")" ) ); nLeftParentPos = lcl_MatchParenthesis( aFormula, aFormula.Len()-1 ); if( nLeftParentPos == STRING_NOTFOUND ) break; // #160063# nLeftParentPos can be 0 if a parenthesis is inserted before the formula sal_Unicode c = ( nLeftParentPos > 0 ) ? aFormula.GetChar( nLeftParentPos-1 ) : 0; if( !((c >= 'A' && c<= 'Z') || (c>= 'a' && c<= 'z') ) ) continue; nNextFStart = aHelper.GetFunctionStart( aFormula, nLeftParentPos, sal_True); if( aHelper.GetNextFunc( aFormula, sal_False, nNextFStart, &nNextFEnd, &ppFDesc, &aArgs ) ) { if( ppFDesc->getFunctionName().getLength() ) { nArgPos = aHelper.GetArgStart( aFormula, nNextFStart, 0 ); nArgs = static_cast(ppFDesc->getParameterCount()); sal_uInt16 nActive = 0; sal_uInt16 nCount = 0; sal_uInt16 nCountSemicolon = 0; sal_uInt16 nCountDot = 0; sal_uInt16 nStartPosition = 0; sal_uInt16 nEndPosition = 0; sal_Bool bFlag = sal_False; String aNew; sal_uInt16 nParAutoPos = SCPOS_INVALID; if( pFormulaDataPara->FindText( ppFDesc->getFunctionName(), aNew, nParAutoPos, sal_False ) ) { for( sal_uInt16 i=0; i < nArgs; i++ ) { xub_StrLen nLength = static_cast(aArgs[i].getLength()); if( nArgPos <= aFormula.Len()-1 ) { nActive = i+1; bFlag = sal_True; } nArgPos+=nLength+1; } if( bFlag ) { nCountSemicolon = aNew.GetTokenCount(cSep)-1; nCountDot = aNew.GetTokenCount(cSheetSep)-1; if( !nCountSemicolon ) { for( sal_uInt16 i = 0; i < aNew.Len(); i++ ) { sal_Unicode cNext = aNew.GetChar( i ); if( cNext == '(' ) { nStartPosition = i+1; } } } else if( !nCountDot ) { for( sal_uInt16 i = 0; i < aNew.Len(); i++ ) { sal_Unicode cNext = aNew.GetChar( i ); if( cNext == '(' ) { nStartPosition = i+1; } else if( cNext == cSep ) { nCount ++; nEndPosition = i; if( nCount == nActive ) { break; } nStartPosition = nEndPosition+1; } } } else { for( sal_uInt16 i = 0; i < aNew.Len(); i++ ) { sal_Unicode cNext = aNew.GetChar( i ); if( cNext == '(' ) { nStartPosition = i+1; } else if( cNext == cSep ) { nCount ++; nEndPosition = i; if( nCount == nActive ) { break; } nStartPosition = nEndPosition+1; } else if( cNext == cSheetSep ) { continue; } } } if( nStartPosition ) { aNew.Insert( 0x25BA, nStartPosition ); ShowTipBelow( aNew ); bFound = sal_True; } } else { ShowTipBelow( aNew ); bFound = sal_True; } } } } } } } } void ScInputHandler::NextFormulaEntry( sal_Bool bBack ) { EditView* pActiveView = pTopView ? pTopView : pTableView; if ( pActiveView && pFormulaData ) { String aNew; if ( pFormulaData->FindText( aAutoSearch, aNew, nAutoPos, bBack ) ) ShowTip( aNew ); // als QuickHelp anzeigen } // bei Tab wird vorher immer HideCursor gerufen if (pActiveView) pActiveView->ShowCursor(); } void lcl_CompleteFunction( EditView* pView, const String& rInsert, sal_Bool& rParInserted ) { if (pView) { ESelection aSel = pView->GetSelection(); --aSel.nStartPos; --aSel.nEndPos; pView->SetSelection(aSel); pView->SelectCurrentWord(); String aInsStr = rInsert; xub_StrLen nInsLen = aInsStr.Len(); sal_Bool bDoParen = ( nInsLen > 1 && aInsStr.GetChar(nInsLen-2) == '(' && aInsStr.GetChar(nInsLen-1) == ')' ); if ( bDoParen ) { // Klammern hinter Funktionsnamen nicht einfuegen, wenn direkt dahinter // schon eine Klammer steht (z.B. wenn der Funktionsname geaendert wurde, // #39393#). ESelection aWordSel = pView->GetSelection(); String aOld = pView->GetEditEngine()->GetText((sal_uInt16)0); sal_Unicode cNext = aOld.GetChar(aWordSel.nEndPos); if ( cNext == '(' ) { bDoParen = sal_False; aInsStr.Erase( nInsLen - 2 ); // Klammern weglassen } } pView->InsertText( aInsStr, sal_False ); if ( bDoParen ) // Cursor zwischen die Klammern setzen { aSel = pView->GetSelection(); --aSel.nStartPos; --aSel.nEndPos; pView->SetSelection(aSel); rParInserted = sal_True; } } } void ScInputHandler::PasteFunctionData() { if ( pFormulaData && nAutoPos != SCPOS_INVALID ) { TypedStrData* pData = (*pFormulaData)[nAutoPos]; if (pData) { String aInsert = pData->GetString(); sal_Bool bParInserted = sal_False; DataChanging(); // kann nicht neu sein lcl_CompleteFunction( pTopView, aInsert, bParInserted ); lcl_CompleteFunction( pTableView, aInsert, bParInserted ); DataChanged(); ShowTipCursor(); if (bParInserted) AutoParAdded(); } } HideTip(); EditView* pActiveView = pTopView ? pTopView : pTableView; if (pActiveView) pActiveView->ShowCursor(); } // // Selektion berechnen und als Tip-Hilfe anzeigen // String lcl_Calculate( const String& rFormula, ScDocument* pDoc, const ScAddress &rPos ) { //! mit ScFormulaDlg::CalcValue zusammenfassen und ans Dokument verschieben !!!! //! (Anfuehrungszeichen bei Strings werden nur hier eingefuegt) String aValue; if (rFormula.Len()) { ScFormulaCell* pCell = new ScFormulaCell( pDoc, rPos, rFormula ); // #35521# HACK! um bei ColRowNames kein #REF! zu bekommen, // wenn ein Name eigentlich als Bereich in die Gesamt-Formel // eingefuegt wird, bei der Einzeldarstellung aber als // single-Zellbezug interpretiert wird sal_Bool bColRowName = pCell->HasColRowName(); if ( bColRowName ) { // ColRowName im RPN-Code? if ( pCell->GetCode()->GetCodeLen() <= 1 ) { // ==1: einzelner ist als Parameter immer Bereich // ==0: es waere vielleicht einer, wenn.. String aBraced( '(' ); aBraced += rFormula; aBraced += ')'; delete pCell; pCell = new ScFormulaCell( pDoc, rPos, aBraced ); } else bColRowName = sal_False; } sal_uInt16 nErrCode = pCell->GetErrCode(); if ( nErrCode == 0 ) { SvNumberFormatter& aFormatter = *(pDoc->GetFormatTable()); Color* pColor; if ( pCell->IsValue() ) { double n = pCell->GetValue(); sal_uLong nFormat = aFormatter.GetStandardFormat( n, 0, pCell->GetFormatType(), ScGlobal::eLnge ); aFormatter.GetInputLineString( n, nFormat, aValue ); //! display OutputString but insert InputLineString } else { String aStr; pCell->GetString( aStr ); sal_uLong nFormat = aFormatter.GetStandardFormat( pCell->GetFormatType(), ScGlobal::eLnge); aFormatter.GetOutputString( aStr, nFormat, aValue, &pColor ); aValue.Insert('"',0); // in Anfuehrungszeichen aValue+='"'; //! Anfuehrungszeichen im String escapen ???? } ScRange aTestRange; if ( bColRowName || (aTestRange.Parse(rFormula) & SCA_VALID) ) aValue.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " ..." )); // Bereich } else aValue = ScGlobal::GetErrorString(nErrCode); delete pCell; } return aValue; } void ScInputHandler::FormulaPreview() { String aValue; EditView* pActiveView = pTopView ? pTopView : pTableView; if ( pActiveView && pActiveViewSh ) { String aPart = pActiveView->GetSelected(); if (!aPart.Len()) aPart = pEngine->GetText((sal_uInt16)0); ScDocument* pDoc = pActiveViewSh->GetViewData()->GetDocShell()->GetDocument(); aValue = lcl_Calculate( aPart, pDoc, aCursorPos ); } if (aValue.Len()) { ShowTip( aValue ); // als QuickHelp anzeigen aManualTip = aValue; // nach ShowTip setzen nAutoPos = SCPOS_INVALID; // Formel-Autocomplete aufheben } } void ScInputHandler::PasteManualTip() { // drei Punkte am Ende -> Bereichsreferenz -> nicht einfuegen // (wenn wir mal Matrix-Konstanten haben, kann das geaendert werden) xub_StrLen nTipLen = aManualTip.Len(); if ( nTipLen && ( nTipLen < 3 || !aManualTip.Copy( nTipLen-3 ).EqualsAscii("...") ) ) { DataChanging(); // kann nicht neu sein String aInsert = aManualTip; EditView* pActiveView = pTopView ? pTopView : pTableView; if (!pActiveView->HasSelection()) { // nichts selektiert -> alles selektieren xub_StrLen nOldLen = pEngine->GetTextLen(0); ESelection aAllSel( 0, 0, 0, nOldLen ); if ( pTopView ) pTopView->SetSelection( aAllSel ); if ( pTableView ) pTableView->SetSelection( aAllSel ); } ESelection aSel = pActiveView->GetSelection(); aSel.Adjust(); DBG_ASSERT( !aSel.nStartPara && !aSel.nEndPara, "Zuviele Absaetze in Formel" ); if ( !aSel.nStartPos ) // Selektion ab Anfang? { if ( aSel.nEndPos == pEngine->GetTextLen(0) ) { // alles selektiert -> Anfuehrungszeichen weglassen if ( aInsert.GetChar(0) == '"' ) aInsert.Erase(0,1); xub_StrLen nInsLen = aInsert.Len(); if ( nInsLen && aInsert.GetChar(nInsLen-1) == '"' ) aInsert.Erase( nInsLen-1 ); } else if ( aSel.nEndPos ) { // nicht alles selektiert -> Gleichheitszeichen nicht ueberschreiben //! doppelte Gleichheitszeichen auch ??? aSel.nStartPos = 1; if ( pTopView ) pTopView->SetSelection( aSel ); if ( pTableView ) pTableView->SetSelection( aSel ); } } if ( pTopView ) pTopView->InsertText( aInsert, sal_True ); if ( pTableView ) pTableView->InsertText( aInsert, sal_True ); DataChanged(); } HideTip(); } void ScInputHandler::ResetAutoPar() { nAutoPar = 0; } void ScInputHandler::AutoParAdded() { ++nAutoPar; // closing parenthesis can be overwritten } sal_Bool ScInputHandler::CursorAtClosingPar() { // test if the cursor is before a closing parenthesis // selection from SetReference has been removed before EditView* pActiveView = pTopView ? pTopView : pTableView; if ( pActiveView && !pActiveView->HasSelection() && bFormulaMode ) { ESelection aSel = pActiveView->GetSelection(); xub_StrLen nPos = aSel.nStartPos; String aFormula = pEngine->GetText((sal_uInt16)0); if ( nPos < aFormula.Len() && aFormula.GetChar(nPos) == ')' ) return sal_True; } return sal_False; } void ScInputHandler::SkipClosingPar() { // this is called when a ')' is typed and the cursor is before a ')' // that can be overwritten -> just set the cursor behind the ')' EditView* pActiveView = pTopView ? pTopView : pTableView; if (pActiveView) { ESelection aSel = pActiveView->GetSelection(); ++aSel.nStartPos; ++aSel.nEndPos; // this is in a formula (only one paragraph), so the selection // can be used directly for the TopView if ( pTopView ) pTopView->SetSelection( aSel ); if ( pTableView ) pTableView->SetSelection( aSel ); } DBG_ASSERT(nAutoPar, "SkipClosingPar: count is wrong"); --nAutoPar; } // // Auto-Eingabe // void ScInputHandler::GetColData() { if ( pActiveViewSh ) { ScDocument* pDoc = pActiveViewSh->GetViewData()->GetDocShell()->GetDocument(); if ( pColumnData ) pColumnData->FreeAll(); else { pColumnData = new TypedScStrCollection; pColumnData->SetCaseSensitive( sal_True ); // equal strings are handled in FindText } pDoc->GetDataEntries( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab(), *pColumnData, sal_True ); } } void ScInputHandler::UseColData() // beim Tippen { EditView* pActiveView = pTopView ? pTopView : pTableView; if ( pActiveView && pColumnData ) { // nur anpassen, wenn Cursor am Ende steht ESelection aSel = pActiveView->GetSelection(); aSel.Adjust(); sal_uInt32 nParCnt = pEngine->GetParagraphCount(); if ( aSel.nEndPara+1 == nParCnt ) { xub_StrLen nParLen = pEngine->GetTextLen( aSel.nEndPara ); if ( aSel.nEndPos == nParLen ) { String aText = GetEditText(pEngine); if (aText.Len()) { String aNew; nAutoPos = SCPOS_INVALID; // nix if ( pColumnData->FindText( aText, aNew, nAutoPos, sal_False ) ) { // #45434# durch dBase Import etc. koennen Umbrueche im String sein, // das wuerde hier mehrere Absaetze ergeben -> nicht gut //! GetExactMatch funktioniert dann auch nicht lcl_RemoveLineEnd( aNew ); // Absaetze beibehalten, nur den Rest anfuegen //! genaue Ersetzung im EnterHandler !!! // ein Space zwischen Absaetzen: sal_uLong nEdLen = pEngine->GetTextLen() + nParCnt - 1; String aIns = aNew.Copy( (xub_StrLen)nEdLen ); // selection must be "backwards", so the cursor stays behind the last // typed character ESelection aSelection( aSel.nEndPara, aSel.nEndPos + aIns.Len(), aSel.nEndPara, aSel.nEndPos ); // when editing in input line, apply to both edit views if ( pTableView ) { pTableView->InsertText( aIns, sal_False ); pTableView->SetSelection( aSelection ); } if ( pTopView ) { pTopView->InsertText( aIns, sal_False ); pTopView->SetSelection( aSelection ); } aAutoSearch = aText; // zum Weitersuchen - nAutoPos ist gesetzt if ( aText.Len() == aNew.Len() ) { // Wenn der eingegebene Text gefunden wurde, TAB nur dann // verschlucken, wenn noch etwas kommt String aDummy; sal_uInt16 nNextPos = nAutoPos; bUseTab = pColumnData->FindText( aText, aDummy, nNextPos, sal_False ); } else bUseTab = sal_True; } } } } } } void ScInputHandler::NextAutoEntry( sal_Bool bBack ) { EditView* pActiveView = pTopView ? pTopView : pTableView; if ( pActiveView && pColumnData ) { if ( nAutoPos != SCPOS_INVALID && aAutoSearch.Len() ) { // stimmt die Selektion noch? (kann per Maus geaendert sein) ESelection aSel = pActiveView->GetSelection(); aSel.Adjust(); sal_uInt32 nParCnt = pEngine->GetParagraphCount(); if ( aSel.nEndPara+1 == nParCnt && aSel.nStartPara == aSel.nEndPara ) { String aText = GetEditText(pEngine); xub_StrLen nSelLen = aSel.nEndPos - aSel.nStartPos; xub_StrLen nParLen = pEngine->GetTextLen( aSel.nEndPara ); if ( aSel.nEndPos == nParLen && aText.Len() == aAutoSearch.Len() + nSelLen ) { String aNew; if ( pColumnData->FindText( aAutoSearch, aNew, nAutoPos, bBack ) ) { bInOwnChange = sal_True; // disable ModifyHdl (reset below) lcl_RemoveLineEnd( aNew ); String aIns = aNew.Copy( aAutoSearch.Len() ); // when editing in input line, apply to both edit views if ( pTableView ) { pTableView->DeleteSelected(); pTableView->InsertText( aIns, sal_False ); pTableView->SetSelection( ESelection( aSel.nEndPara, aSel.nStartPos + aIns.Len(), aSel.nEndPara, aSel.nStartPos ) ); } if ( pTopView ) { pTopView->DeleteSelected(); pTopView->InsertText( aIns, sal_False ); pTopView->SetSelection( ESelection( aSel.nEndPara, aSel.nStartPos + aIns.Len(), aSel.nEndPara, aSel.nStartPos ) ); } bInOwnChange = sal_False; } else { // mehr gibts nicht Sound::Beep(); } } } } } // bei Tab wird vorher immer HideCursor gerufen if (pActiveView) pActiveView->ShowCursor(); } // // Klammern hervorheben // void ScInputHandler::UpdateParenthesis() { // Klammern suchen //! Klammer-Hervorhebung einzeln abschaltbar ???? sal_Bool bFound = sal_False; if ( bFormulaMode && eMode != SC_INPUT_TOP ) { if ( pTableView && !pTableView->HasSelection() ) // Selektion ist immer unten { ESelection aSel = pTableView->GetSelection(); if (aSel.nStartPos) { // Das Zeichen links vom Cursor wird angeschaut xub_StrLen nPos = aSel.nStartPos - 1; String aFormula = pEngine->GetText((sal_uInt16)0); sal_Unicode c = aFormula.GetChar(nPos); if ( c == '(' || c == ')' ) { xub_StrLen nOther = lcl_MatchParenthesis( aFormula, nPos ); if ( nOther != STRING_NOTFOUND ) { SfxItemSet aSet( pEngine->GetEmptyItemSet() ); aSet.Put( SvxWeightItem( WEIGHT_BOLD, EE_CHAR_WEIGHT ) ); //! Unterscheidung, wenn die Zelle schon fett ist !!!! if (bParenthesisShown) { // alte Hervorhebung wegnehmen sal_uInt32 nCount = pEngine->GetParagraphCount(); for (sal_uInt32 i=0; iQuickRemoveCharAttribs( i, EE_CHAR_WEIGHT ); } ESelection aSelThis( 0,nPos, 0,nPos+1 ); pEngine->QuickSetAttribs( aSet, aSelThis ); ESelection aSelOther( 0,nOther, 0,nOther+1 ); pEngine->QuickSetAttribs( aSet, aSelOther ); // Dummy-InsertText fuer Update und Paint (Selektion ist leer) pTableView->InsertText( EMPTY_STRING, sal_False ); bFound = sal_True; } } } // mark parenthesis right of cursor if it will be overwritten (nAutoPar) // with different color (COL_LIGHTBLUE) ?? } } // alte Hervorhebung wegnehmen, wenn keine neue gesetzt if ( bParenthesisShown && !bFound && pTableView ) { sal_uInt32 nCount = pEngine->GetParagraphCount(); for (sal_uInt32 i=0; iRemoveCharAttribs( i, EE_CHAR_WEIGHT ); } bParenthesisShown = bFound; } void ScInputHandler::ViewShellGone(ScTabViewShell* pViewSh) // wird synchron aufgerufen! { if ( pViewSh == pActiveViewSh ) { delete pLastState; pLastState = NULL; pLastPattern = NULL; } if ( pViewSh == pRefViewSh ) { //! Die Eingabe kommt aus dem EnterHandler nicht mehr an // Trotzdem wird immerhin der Editmodus beendet EnterHandler(); bFormulaMode = sal_False; pRefViewSh = NULL; SFX_APP()->Broadcast( SfxSimpleHint( FID_REFMODECHANGED ) ); SC_MOD()->SetRefInputHdl(NULL); if (pInputWin) pInputWin->SetFormulaMode(sal_False); UpdateAutoCorrFlag(); } pActiveViewSh = PTR_CAST( ScTabViewShell, SfxViewShell::Current() ); if ( pActiveViewSh && pActiveViewSh == pViewSh ) { DBG_ERROR("pActiveViewSh weg"); pActiveViewSh = NULL; } if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() ) UpdateRefDevice(); // don't keep old document's printer as RefDevice } void ScInputHandler::UpdateActiveView() { ImplCreateEditEngine(); // #i20588# Don't rely on focus to find the active edit view. Instead, the // active pane at the start of editing is now stored (GetEditActivePart). // GetActiveWin (the currently active pane) fails for ref input across the // panes of a split view. Window* pShellWin = pActiveViewSh ? pActiveViewSh->GetWindowByPos( pActiveViewSh->GetViewData()->GetEditActivePart() ) : NULL; sal_uInt16 nCount = pEngine->GetViewCount(); if (nCount > 0) { pTableView = pEngine->GetView(0); for (sal_uInt16 i=1; iGetView(i); Window* pWin = pThis->GetWindow(); if ( pWin==pShellWin ) pTableView = pThis; } } else pTableView = NULL; if (pInputWin) pTopView = pInputWin->GetEditView(); else pTopView = NULL; } void ScInputHandler::StopInputWinEngine( sal_Bool bAll ) { if (pInputWin) pInputWin->StopEditEngine( bAll ); pTopView = NULL; // invalid now } EditView* ScInputHandler::GetActiveView() { UpdateActiveView(); return pTopView ? pTopView : pTableView; } void ScInputHandler::ForgetLastPattern() { pLastPattern = NULL; if ( !pLastState && pActiveViewSh ) pActiveViewSh->UpdateInputHandler( sal_True ); // Status neu holen else NotifyChange( pLastState, sal_True ); } void ScInputHandler::UpdateAdjust( sal_Unicode cTyped ) { SvxAdjust eSvxAdjust; switch (eAttrAdjust) { case SVX_HOR_JUSTIFY_STANDARD: { sal_Bool bNumber = sal_False; if (cTyped) // neu angefangen bNumber = (cTyped>='0' && cTyped<='9'); // nur Ziffern sind Zahlen else if ( pActiveViewSh ) { ScDocument* pDoc = pActiveViewSh->GetViewData()->GetDocShell()->GetDocument(); bNumber = ( pDoc->GetCellType( aCursorPos ) == CELLTYPE_VALUE ); } eSvxAdjust = bNumber ? SVX_ADJUST_RIGHT : SVX_ADJUST_LEFT; } break; case SVX_HOR_JUSTIFY_BLOCK: eSvxAdjust = SVX_ADJUST_BLOCK; break; case SVX_HOR_JUSTIFY_CENTER: eSvxAdjust = SVX_ADJUST_CENTER; break; case SVX_HOR_JUSTIFY_RIGHT: eSvxAdjust = SVX_ADJUST_RIGHT; break; default: // SVX_HOR_JUSTIFY_LEFT eSvxAdjust = SVX_ADJUST_LEFT; break; } sal_Bool bAsianVertical = pLastPattern && ((const SfxBoolItem&)pLastPattern->GetItem( ATTR_STACKED )).GetValue() && ((const SfxBoolItem&)pLastPattern->GetItem( ATTR_VERTICAL_ASIAN )).GetValue(); if ( bAsianVertical ) { // always edit at top of cell -> LEFT when editing vertically eSvxAdjust = SVX_ADJUST_LEFT; } pEditDefaults->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) ); pEngine->SetDefaults( *pEditDefaults ); nEditAdjust = sal::static_int_cast(eSvxAdjust); //! set at ViewData or with PostEditView pEngine->SetVertical( bAsianVertical ); } void ScInputHandler::RemoveAdjust() { // harte Ausrichtungs-Attribute loeschen sal_Bool bUndo = pEngine->IsUndoEnabled(); if ( bUndo ) pEngine->EnableUndo( sal_False ); // RemoveParaAttribs removes all paragraph attributes, including EE_PARA_JUST #if 0 sal_Bool bChange = sal_False; sal_uInt32 nCount = pEngine->GetParagraphCount(); for (sal_uInt32 i=0; iGetParaAttribs( i ); if ( rOld.GetItemState( EE_PARA_JUST ) == SFX_ITEM_SET ) { SfxItemSet aNew( rOld ); aNew.ClearItem( EE_PARA_JUST ); pEngine->SetParaAttribs( i, aNew ); bChange = sal_True; } } #endif // #89403# non-default paragraph attributes (e.g. from clipboard) // must be turned into character attributes pEngine->RemoveParaAttribs(); if ( bUndo ) pEngine->EnableUndo( sal_True ); // ER 31.08.00 Only called in EnterHandler, don't change view anymore. #if 0 if (bChange) { EditView* pActiveView = pTopView ? pTopView : pTableView; pActiveView->ShowCursor( sal_False, sal_True ); } #endif } void ScInputHandler::RemoveRangeFinder() { // pRangeFindList und Farben loeschen pEngine->SetUpdateMode(sal_False); sal_uInt32 nCount = pEngine->GetParagraphCount(); // koennte gerade neu eingefuegt worden sein for (sal_uInt32 i=0; iQuickRemoveCharAttribs( i, EE_CHAR_COLOR ); pEngine->SetUpdateMode(sal_True); EditView* pActiveView = pTopView ? pTopView : pTableView; pActiveView->ShowCursor( sal_False, sal_True ); DeleteRangeFinder(); // loescht die Liste und die Markierungen auf der Tabelle } sal_Bool ScInputHandler::StartTable( sal_Unicode cTyped, sal_Bool bFromCommand ) { // returns sal_True if a new edit mode was started sal_Bool bNewTable = sal_False; if (!bModified && ValidCol(aCursorPos.Col())) { if (pActiveViewSh) { ImplCreateEditEngine(); UpdateActiveView(); SyncViews(); ScDocument* pDoc = pActiveViewSh->GetViewData()->GetDocShell()->GetDocument(); const ScMarkData& rMark = pActiveViewSh->GetViewData()->GetMarkData(); ScEditableTester aTester; if ( rMark.IsMarked() || rMark.IsMultiMarked() ) aTester.TestSelection( pDoc, rMark ); else aTester.TestSelectedBlock( pDoc, aCursorPos.Col(),aCursorPos.Row(), aCursorPos.Col(),aCursorPos.Row(), rMark ); if ( aTester.IsEditable() ) { // UpdateMode is enabled again in ScViewData::SetEditEngine (and not needed otherwise) pEngine->SetUpdateMode( sal_False ); // Attribute in EditEngine uebernehmen const ScPatternAttr* pPattern = pDoc->GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); if (pPattern != pLastPattern) { // Prozent-Format? const SfxItemSet& rAttrSet = pPattern->GetItemSet(); const SfxPoolItem* pItem; if ( SFX_ITEM_SET == rAttrSet.GetItemState( ATTR_VALUE_FORMAT, sal_True, &pItem ) ) { sal_uLong nFormat = ((const SfxUInt32Item*)pItem)->GetValue(); bCellHasPercentFormat = ( NUMBERFORMAT_PERCENT == pDoc->GetFormatTable()->GetType( nFormat ) ); } else bCellHasPercentFormat = sal_False; // Default: kein Prozent // Gueltigkeit angegeben? if ( SFX_ITEM_SET == rAttrSet.GetItemState( ATTR_VALIDDATA, sal_True, &pItem ) ) nValidation = ((const SfxUInt32Item*)pItem)->GetValue(); else nValidation = 0; // EditEngine Defaults // Hier auf keinen Fall SetParaAttribs, weil die EditEngine evtl. // schon gefuellt ist (bei Edit-Zellen). // SetParaAttribs wuerde dann den Inhalt aendern //! ER 30.08.00 The SetDefaults is now (since MUST/src602 //! EditEngine changes) implemented as a SetParaAttribs. //! Any problems? pPattern->FillEditItemSet( pEditDefaults ); pEngine->SetDefaults( *pEditDefaults ); pLastPattern = pPattern; bLastIsSymbol = pPattern->IsSymbolFont(); // Background color must be known for automatic font color. // For transparent cell background, the document background color must be used. Color aBackCol = ((const SvxBrushItem&) pPattern->GetItem( ATTR_BACKGROUND )).GetColor(); ScModule* pScMod = SC_MOD(); // #105733# SvtAccessibilityOptions::GetIsForBorders is no longer used (always assumed sal_True) if ( aBackCol.GetTransparency() > 0 || Application::GetSettings().GetStyleSettings().GetHighContrastMode() ) aBackCol.SetColor( pScMod->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor ); pEngine->SetBackgroundColor( aBackCol ); // Ausrichtung eAttrAdjust = (SvxCellHorJustify)((const SvxHorJustifyItem&)pPattern-> GetItem(ATTR_HOR_JUSTIFY)).GetValue(); if ( eAttrAdjust == SVX_HOR_JUSTIFY_REPEAT && static_cast(pPattern->GetItem(ATTR_LINEBREAK)).GetValue() ) { // #i31843# "repeat" with "line breaks" is treated as default alignment eAttrAdjust = SVX_HOR_JUSTIFY_STANDARD; } } // UpdateSpellSettings enables online spelling if needed // -> also call if attributes are unchanged UpdateSpellSettings( sal_True ); // uses pLastPattern // Edit-Engine fuellen String aStr; if (bTextValid) { pEngine->SetText(aCurrentText); aStr = aCurrentText; bTextValid = sal_False; aCurrentText.Erase(); } else aStr = GetEditText(pEngine); if (aStr.Len() > 3 && // Matrix-Formel ? aStr.GetChar(0) == '{' && aStr.GetChar(1) == '=' && aStr.GetChar(aStr.Len()-1) == '}') { aStr.Erase(0,1); aStr.Erase(aStr.Len()-1,1); pEngine->SetText(aStr); if ( pInputWin ) pInputWin->SetTextString(aStr); } UpdateAdjust( cTyped ); if ( bAutoComplete ) GetColData(); if ( ( aStr.GetChar(0) == '=' || aStr.GetChar(0) == '+' || aStr.GetChar(0) == '-' ) && !cTyped && !bCreatingFuncView ) InitRangeFinder(aStr); // Formel wird editiert -> RangeFinder bNewTable = sal_True; // -> PostEditView-Aufruf } else { bProtected = sal_True; eMode = SC_INPUT_NONE; StopInputWinEngine( sal_True ); UpdateFormulaMode(); if ( pActiveViewSh && ( !bFromCommand || !bCommandErrorShown ) ) { // #97673# Prevent repeated error messages for the same cell from command events // (for keyboard events, multiple messages are wanted). // Set the flag before showing the error message because the command handler // for the next IME command may be called when showing the dialog. if ( bFromCommand ) bCommandErrorShown = sal_True; pActiveViewSh->GetActiveWin()->GrabFocus(); pActiveViewSh->ErrorMessage(aTester.GetMessageId()); } } } if (!bProtected && pInputWin) pInputWin->SetOkCancelMode(); } return bNewTable; } void lcl_SetTopSelection( EditView* pEditView, ESelection& rSel ) { DBG_ASSERT( rSel.nStartPara==0 && rSel.nEndPara==0, "SetTopSelection: Para != 0" ); EditEngine* pEngine = pEditView->GetEditEngine(); sal_uInt32 nCount = pEngine->GetParagraphCount(); if (nCount > 1) { xub_StrLen nParLen = pEngine->GetTextLen(rSel.nStartPara); while (rSel.nStartPos > nParLen && rSel.nStartPara+1 < nCount) { rSel.nStartPos -= nParLen + 1; // incl. Leerzeichen vom Umbruch nParLen = pEngine->GetTextLen(++rSel.nStartPara); } nParLen = pEngine->GetTextLen(rSel.nEndPara); while (rSel.nEndPos > nParLen && rSel.nEndPara+1 < nCount) { rSel.nEndPos -= nParLen + 1; // incl. Leerzeichen vom Umbruch nParLen = pEngine->GetTextLen(++rSel.nEndPara); } } ESelection aSel = pEditView->GetSelection(); if ( rSel.nStartPara != aSel.nStartPara || rSel.nEndPara != aSel.nEndPara || rSel.nStartPos != aSel.nStartPos || rSel.nEndPos != aSel.nEndPos ) pEditView->SetSelection( rSel ); } void ScInputHandler::SyncViews( EditView* pSourceView ) { ESelection aSel; if (pSourceView) { aSel = pSourceView->GetSelection(); if (pTopView && pTopView != pSourceView) pTopView->SetSelection( aSel ); if (pTableView && pTableView != pSourceView) lcl_SetTopSelection( pTableView, aSel ); } else if (pTopView && pTableView) { aSel = pTopView->GetSelection(); lcl_SetTopSelection( pTableView, aSel ); } } IMPL_LINK( ScInputHandler, ModifyHdl, void *, EMPTYARG ) { if ( !bInOwnChange && ( eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE ) && pEngine && pEngine->GetUpdateMode() && pInputWin ) { // #102745# update input line from ModifyHdl for changes that are not // wrapped by DataChanging/DataChanged calls (like Drag&Drop) String aText = GetEditText(pEngine); lcl_RemoveTabs(aText); pInputWin->SetTextString(aText); } return 0; } sal_Bool ScInputHandler::DataChanging( sal_Unicode cTyped, sal_Bool bFromCommand ) // return sal_True = new view created { bInOwnChange = sal_True; // disable ModifyHdl (reset in DataChanged) if ( eMode == SC_INPUT_NONE ) return StartTable( cTyped, bFromCommand ); else return sal_False; } void ScInputHandler::DataChanged( sal_Bool bFromTopNotify, sal_Bool bSetModified ) { ImplCreateEditEngine(); if (eMode==SC_INPUT_NONE) eMode = SC_INPUT_TYPE; if ( eMode == SC_INPUT_TOP && pTopView && !bFromTopNotify ) { // table EditEngine is formatted below, input line needs formatting after paste // #i20282# not when called from the input line's modify handler pTopView->GetEditEngine()->QuickFormatDoc( sal_True ); // #i23720# QuickFormatDoc hides the cursor, but can't show it again because it // can't safely access the EditEngine's current view, so the cursor has to be // shown again here. pTopView->ShowCursor(); } if (bSetModified) bModified = sal_True; bSelIsRef = sal_False; if ( pRangeFindList && !bInRangeUpdate ) RemoveRangeFinder(); // Attribute und Markierung loeschen UpdateParenthesis(); // Hervorhebung der Klammern neu // ER 31.08.00 New SetDefaults sets ParaAttribs, don't clear them away ... // RemoveAdjust(); // #40255# harte Ausrichtungs-Attribute loeschen if (eMode==SC_INPUT_TYPE || eMode==SC_INPUT_TABLE) { String aText = GetEditText(pEngine); lcl_RemoveTabs(aText); if ( pInputWin ) pInputWin->SetTextString(aText); } // wenn der Cursor vor dem Absatzende steht, werden Teile rechts rausgeschoben // (unabhaengig von eMode) -> View anpassen! // wenn der Cursor am Ende steht, reicht der Status-Handler an der ViewData // #93767# first make sure the status handler is called now if the cursor // is outside the visible area pEngine->QuickFormatDoc(); EditView* pActiveView = pTopView ? pTopView : pTableView; if (pActiveView && pActiveViewSh) { ScViewData* pViewData = pActiveViewSh->GetViewData(); sal_Bool bNeedGrow = ( nEditAdjust != SVX_ADJUST_LEFT ); // rechtsbuendig immer if (!bNeedGrow) { // Cursor vor dem Ende? ESelection aSel = pActiveView->GetSelection(); aSel.Adjust(); bNeedGrow = ( aSel.nEndPos != pEngine->GetTextLen(aSel.nEndPara) ); } if (!bNeedGrow) { bNeedGrow = pViewData->GetDocument()->IsLayoutRTL( pViewData->GetTabNo() ); } if (bNeedGrow) { // adjust inplace view pViewData->EditGrowY(); pViewData->EditGrowX(); } } UpdateFormulaMode(); bTextValid = sal_False; // Aenderungen sind nur in der Edit-Engine bInOwnChange = sal_False; } void ScInputHandler::UpdateFormulaMode() { SfxApplication* pSfxApp = SFX_APP(); if ( pEngine->GetParagraphCount() == 1 && ( pEngine->GetText(0).GetChar(0) == '=' || pEngine->GetText(0).GetChar(0) == '+' || pEngine->GetText(0).GetChar(0) == '-' ) && !bProtected ) { if (!bFormulaMode) { bFormulaMode = sal_True; pRefViewSh = pActiveViewSh; pSfxApp->Broadcast( SfxSimpleHint( FID_REFMODECHANGED ) ); SC_MOD()->SetRefInputHdl(this); if (pInputWin) pInputWin->SetFormulaMode(sal_True); if ( bAutoComplete ) GetFormulaData(); UpdateParenthesis(); UpdateAutoCorrFlag(); } } else // ausschalten { if (bFormulaMode) { ShowRefFrame(); bFormulaMode = sal_False; pRefViewSh = NULL; pSfxApp->Broadcast( SfxSimpleHint( FID_REFMODECHANGED ) ); SC_MOD()->SetRefInputHdl(NULL); if (pInputWin) pInputWin->SetFormulaMode(sal_False); UpdateAutoCorrFlag(); } } } void ScInputHandler::ShowRefFrame() { // #123169# Modifying pActiveViewSh here would interfere with the bInEnterHandler / bRepeat // checks in NotifyChange, and lead to keeping the wrong value in pActiveViewSh. // A local variable is used instead. ScTabViewShell* pVisibleSh = PTR_CAST( ScTabViewShell, SfxViewShell::Current() ); if ( pRefViewSh && pRefViewSh != pVisibleSh ) { sal_Bool bFound = sal_False; SfxViewFrame* pRefFrame = pRefViewSh->GetViewFrame(); SfxViewFrame* pOneFrame = SfxViewFrame::GetFirst(); while ( pOneFrame && !bFound ) { if ( pOneFrame == pRefFrame ) bFound = sal_True; pOneFrame = SfxViewFrame::GetNext( *pOneFrame ); } if (bFound) { // Hier wird sich darauf verlassen, dass Activate synchron funktioniert // (dabei wird pActiveViewSh umgesetzt) pRefViewSh->SetActive(); // Appear und SetViewFrame // pLastState wird im NotifyChange aus dem Activate richtig gesetzt } else { DBG_ERROR("ViewFrame fuer Referenzeingabe ist nicht mehr da"); } } } void ScInputHandler::RemoveSelection() { EditView* pActiveView = pTopView ? pTopView : pTableView; if (!pActiveView) return; ESelection aSel = pActiveView->GetSelection(); aSel.nStartPara = aSel.nEndPara; aSel.nStartPos = aSel.nEndPos; if (pTableView) pTableView->SetSelection( aSel ); if (pTopView) pTopView->SetSelection( aSel ); } void ScInputHandler::InvalidateAttribs() { SfxViewFrame* pViewFrm = SfxViewFrame::Current(); if (pViewFrm) { SfxBindings& rBindings = pViewFrm->GetBindings(); rBindings.Invalidate( SID_ATTR_CHAR_FONT ); rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); rBindings.Invalidate( SID_ATTR_CHAR_COLOR ); rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT ); rBindings.Invalidate( SID_ATTR_CHAR_POSTURE ); rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE ); rBindings.Invalidate( SID_ULINE_VAL_NONE ); rBindings.Invalidate( SID_ULINE_VAL_SINGLE ); rBindings.Invalidate( SID_ULINE_VAL_DOUBLE ); rBindings.Invalidate( SID_ULINE_VAL_DOTTED ); rBindings.Invalidate( SID_HYPERLINK_GETLINK ); rBindings.Invalidate( SID_ATTR_CHAR_KERNING ); rBindings.Invalidate( SID_SET_SUPER_SCRIPT ); rBindings.Invalidate( SID_SET_SUB_SCRIPT ); rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT ); rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED ); } } // // --------------- public Methoden -------------------------------------------- // void ScInputHandler::SetMode( ScInputMode eNewMode ) { if ( eMode == eNewMode ) return; ImplCreateEditEngine(); if (bProtected) { eMode = SC_INPUT_NONE; StopInputWinEngine( sal_True ); if (pActiveViewSh) pActiveViewSh->GetActiveWin()->GrabFocus(); return; } bInOwnChange = sal_True; // disable ModifyHdl (reset below) ScInputMode eOldMode = eMode; eMode = eNewMode; if (eOldMode == SC_INPUT_TOP && eNewMode != eOldMode) StopInputWinEngine( sal_False ); if (eMode==SC_INPUT_TOP || eMode==SC_INPUT_TABLE) { if (eOldMode == SC_INPUT_NONE) // not when switching between modes { if (StartTable(0, sal_False)) // 0 = look at existing document content for text or number { if (pActiveViewSh) pActiveViewSh->GetViewData()->GetDocShell()->PostEditView( pEngine, aCursorPos ); } } sal_uInt32 nPara = pEngine->GetParagraphCount()-1; xub_StrLen nLen = pEngine->GetText(nPara).Len(); sal_uInt16 nCount = pEngine->GetViewCount(); for (sal_uInt32 i=0; iGetView(i)-> SetSelection( ESelection( nPara, nLen, nPara, nLen ) ); } pEngine->GetView(i)->ShowCursor(sal_False); } } UpdateActiveView(); if (eMode==SC_INPUT_TABLE || eMode==SC_INPUT_TYPE) { if (pTableView) pTableView->SetEditEngineUpdateMode(sal_True); } else { if (pTopView) pTopView->SetEditEngineUpdateMode(sal_True); } if (eNewMode != eOldMode) UpdateFormulaMode(); bInOwnChange = sal_False; } //---------------------------------------------------------------------------------------- // lcl_IsNumber - sal_True, wenn nur Ziffern (dann keine Autokorrektur) sal_Bool lcl_IsNumber(const String& rString) { xub_StrLen nLen = rString.Len(); for (xub_StrLen i=0; i '9' ) return sal_False; } return sal_True; } void lcl_SelectionToEnd( EditView* pView ) { if ( pView ) { EditEngine* pEngine = pView->GetEditEngine(); sal_uInt32 nParCnt = pEngine->GetParagraphCount(); if ( nParCnt == 0 ) nParCnt = 1; ESelection aSel( nParCnt-1, pEngine->GetTextLen(nParCnt-1) ); // empty selection, cursor at the end pView->SetSelection( aSel ); } } void ScInputHandler::EnterHandler( sal_uInt8 nBlockMode ) { // #62806# Bei Makro-Aufrufen fuer Gueltigkeit kann Tod und Teufel passieren, // darum dafuer sorgen, dass EnterHandler nicht verschachtelt gerufen wird: if (bInEnterHandler) return; bInEnterHandler = sal_True; bInOwnChange = sal_True; // disable ModifyHdl (reset below) ImplCreateEditEngine(); sal_Bool bMatrix = ( nBlockMode == SC_ENTER_MATRIX ); SfxApplication* pSfxApp = SFX_APP(); EditTextObject* pObject = NULL; ScPatternAttr* pCellAttrs = NULL; sal_Bool bAttrib = sal_False; // Formatierung vorhanden ? sal_Bool bForget = sal_False; // wegen Gueltigkeit streichen ? String aString = GetEditText(pEngine); EditView* pActiveView = pTopView ? pTopView : pTableView; if (bModified && pActiveView && aString.Len() && !lcl_IsNumber(aString)) { if ( pColumnData && nAutoPos != SCPOS_INVALID ) { // #i47125# If AutoInput appended something, do the final AutoCorrect // with the cursor at the end of the input. lcl_SelectionToEnd(pTopView); lcl_SelectionToEnd(pTableView); } if (pTopView) pTopView->CompleteAutoCorrect(); // #59759# CompleteAutoCorrect fuer beide Views if (pTableView) pTableView->CompleteAutoCorrect(); aString = GetEditText(pEngine); } lcl_RemoveTabs(aString); // Test, ob zulaessig (immer mit einfachem String) if ( bModified && nValidation && pActiveViewSh ) { ScDocument* pDoc = pActiveViewSh->GetViewData()->GetDocument(); const ScValidationData* pData = pDoc->GetValidationEntry( nValidation ); if (pData && pData->HasErrMsg()) { // #i67990# don't use pLastPattern in EnterHandler const ScPatternAttr* pPattern = pDoc->GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); sal_Bool bOk = pData->IsDataValid( aString, *pPattern, aCursorPos ); if (!bOk) { if ( pActiveViewSh ) // falls aus MouseButtonDown gekommen pActiveViewSh->StopMarking(); // (die InfoBox verschluckt das MouseButtonUp) //! es gibt noch Probleme, wenn die Eingabe durch Aktivieren einer //! anderen View ausgeloest wurde Window* pParent = Application::GetDefDialogParent(); if ( pData->DoError( pParent, aString, aCursorPos ) ) bForget = sal_True; // Eingabe nicht uebernehmen } } } // check for input into DataPilot table if ( bModified && pActiveViewSh && !bForget ) { ScDocument* pDoc = pActiveViewSh->GetViewData()->GetDocument(); ScDPObject* pDPObj = pDoc->GetDPAtCursor( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); if ( pDPObj ) { // any input within the DataPilot table is either a valid renaming // or an invalid action - normal cell input is always aborted pActiveViewSh->DataPilotInput( aCursorPos, aString ); bForget = sal_True; } } pEngine->CompleteOnlineSpelling(); sal_Bool bSpellErrors = !bFormulaMode && pEngine->HasOnlineSpellErrors(); if ( bSpellErrors ) { // #i3820# If the spell checker flags numerical input as error, // it still has to be treated as number, not EditEngine object. if ( pActiveViewSh ) { ScDocument* pDoc = pActiveViewSh->GetViewData()->GetDocument(); // #i67990# don't use pLastPattern in EnterHandler const ScPatternAttr* pPattern = pDoc->GetPattern( aCursorPos.Col(), aCursorPos.Row(), aCursorPos.Tab() ); SvNumberFormatter* pFormatter = pDoc->GetFormatTable(); // without conditional format, as in ScColumn::SetString sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter ); double nVal; if ( pFormatter->IsNumberFormat( aString, nFormat, nVal ) ) { bSpellErrors = sal_False; // ignore the spelling errors } } } // After RemoveAdjust, the EditView must not be repainted (has wrong font size etc). // SetUpdateMode must come after CompleteOnlineSpelling. // The view is hidden in any case below (Broadcast). pEngine->SetUpdateMode( sal_False ); if ( bModified && !bForget ) // was wird eingeben (Text/Objekt) ? { sal_uInt32 nParCnt = pEngine->GetParagraphCount(); if ( nParCnt == 0 ) nParCnt = 1; ESelection aSel( 0, 0, nParCnt-1, pEngine->GetTextLen(nParCnt-1) ); SfxItemSet aOldAttribs = pEngine->GetAttribs( aSel ); const SfxPoolItem* pItem = NULL; // find common (cell) attributes before RemoveAdjust if ( pActiveViewSh ) { SfxItemSet* pCommonAttrs = NULL; for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END; nId++) { SfxItemState eState = aOldAttribs.GetItemState( nId, sal_False, &pItem ); if ( eState == SFX_ITEM_SET && nId != EE_CHAR_ESCAPEMENT && nId != EE_CHAR_PAIRKERNING && nId != EE_CHAR_KERNING && nId != EE_CHAR_XMLATTRIBS && *pItem != pEditDefaults->Get(nId) ) { if ( !pCommonAttrs ) pCommonAttrs = new SfxItemSet( pEngine->GetEmptyItemSet() ); pCommonAttrs->Put( *pItem ); } } if ( pCommonAttrs ) { ScDocument* pDoc = pActiveViewSh->GetViewData()->GetDocument(); pCellAttrs = new ScPatternAttr( pDoc->GetPool() ); pCellAttrs->GetFromEditItemSet( pCommonAttrs ); delete pCommonAttrs; } } // clear ParaAttribs (including adjustment) RemoveAdjust(); // check if EditObject is needed if ( bSpellErrors || nParCnt > 1 ) bAttrib = sal_True; else { for (sal_uInt16 nId = EE_CHAR_START; nId <= EE_CHAR_END && !bAttrib; nId++) { SfxItemState eState = aOldAttribs.GetItemState( nId, sal_False, &pItem ); if (eState == SFX_ITEM_DONTCARE) bAttrib = sal_True; else if (eState == SFX_ITEM_SET) { // keep same items in EditEngine as in ScEditAttrTester if ( nId == EE_CHAR_ESCAPEMENT || nId == EE_CHAR_PAIRKERNING || nId == EE_CHAR_KERNING || nId == EE_CHAR_XMLATTRIBS ) { if ( *pItem != pEditDefaults->Get(nId) ) bAttrib = sal_True; } } } // Feldbefehle enthalten? SfxItemState eFieldState = aOldAttribs.GetItemState( EE_FEATURE_FIELD, sal_False ); if ( eFieldState == SFX_ITEM_DONTCARE || eFieldState == SFX_ITEM_SET ) bAttrib = sal_True; // not converted characters? SfxItemState eConvState = aOldAttribs.GetItemState( EE_FEATURE_NOTCONV, sal_False ); if ( eConvState == SFX_ITEM_DONTCARE || eConvState == SFX_ITEM_SET ) bAttrib = sal_True; // Formeln immer als Formeln erkennen (#38309#) // (der Test vorher ist trotzdem noetig wegen Zell-Attributen) } if (bMatrix) bAttrib = sal_False; if (bAttrib) { sal_uLong nCtrl = pEngine->GetControlWord(); sal_uLong nWantBig = bSpellErrors ? EE_CNTRL_ALLOWBIGOBJS : 0; if ( ( nCtrl & EE_CNTRL_ALLOWBIGOBJS ) != nWantBig ) pEngine->SetControlWord( (nCtrl & ~EE_CNTRL_ALLOWBIGOBJS) | nWantBig ); pObject = pEngine->CreateTextObject(); } else if (bAutoComplete) // Gross-/Kleinschreibung anpassen { if (pColumnData) pColumnData->GetExactMatch( aString ); //! effizienter in der Liste suchen (ScUserList, nur einmal ToUpper) sal_uInt16 nIndex; ScUserListData* pData = ScGlobal::GetUserList()->GetData(aString); if ( pData && pData->GetSubIndex( aString, nIndex ) ) aString = pData->GetSubStr( nIndex ); } } // don't rely on ShowRefFrame switching the active view synchronously // execute the function directly on the correct view's bindings instead // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh; if (bFormulaMode) { ShowRefFrame(); if (pExecuteSh) { pExecuteSh->SetTabNo(aCursorPos.Tab()); pExecuteSh->ActiveGrabFocus(); } bFormulaMode = sal_False; pSfxApp->Broadcast( SfxSimpleHint( FID_REFMODECHANGED ) ); SC_MOD()->SetRefInputHdl(NULL); if (pInputWin) pInputWin->SetFormulaMode(sal_False); UpdateAutoCorrFlag(); } pRefViewSh = NULL; // auch ohne FormulaMode wegen Funktions-AP DeleteRangeFinder(); ResetAutoPar(); sal_Bool bOldMod = bModified; bModified = sal_False; bSelIsRef = sal_False; eMode = SC_INPUT_NONE; StopInputWinEngine( sal_True ); // #123344# Text input (through number formats) or ApplySelectionPattern modify // the cell's attributes, so pLastPattern is no longer valid pLastPattern = NULL; if (bOldMod && !bProtected && !bForget) { // keine typographische Anfuehrungszeichen in Formeln if ( aString.GetChar(0) == '=' ) { SvxAutoCorrect* pAuto = SvxAutoCorrCfg::Get()->GetAutoCorrect(); if ( pAuto ) { sal_Unicode cReplace = pAuto->GetStartDoubleQuote(); if( !cReplace ) cReplace = ScGlobal::pLocaleData->getDoubleQuotationMarkStart().GetChar(0); if ( cReplace != '"' ) aString.SearchAndReplaceAll( cReplace, '"' ); cReplace = pAuto->GetEndDoubleQuote(); if( !cReplace ) cReplace = ScGlobal::pLocaleData->getDoubleQuotationMarkEnd().GetChar(0); if ( cReplace != '"' ) aString.SearchAndReplaceAll( cReplace, '"' ); cReplace = pAuto->GetStartSingleQuote(); if( !cReplace ) cReplace = ScGlobal::pLocaleData->getQuotationMarkStart().GetChar(0); if ( cReplace != '\'' ) aString.SearchAndReplaceAll( cReplace, '\'' ); cReplace = pAuto->GetEndSingleQuote(); if( !cReplace ) cReplace = ScGlobal::pLocaleData->getQuotationMarkEnd().GetChar(0); if ( cReplace != '\'' ) aString.SearchAndReplaceAll( cReplace, '\'' ); } } pSfxApp->Broadcast( SfxSimpleHint( FID_KILLEDITVIEW_NOPAINT ) ); if ( pExecuteSh ) { SfxBindings& rBindings = pExecuteSh->GetViewFrame()->GetBindings(); sal_uInt16 nId = FID_INPUTLINE_ENTER; if ( nBlockMode == SC_ENTER_BLOCK ) nId = FID_INPUTLINE_BLOCK; else if ( nBlockMode == SC_ENTER_MATRIX ) nId = FID_INPUTLINE_MATRIX; ScInputStatusItem aItem( FID_INPUTLINE_STATUS, aCursorPos, aCursorPos, aCursorPos, aString, pObject ); const SfxPoolItem* aArgs[2]; aArgs[0] = &aItem; aArgs[1] = NULL; rBindings.Execute( nId, aArgs ); } delete pLastState; // pLastState enthaelt noch den alten Text pLastState = NULL; } else pSfxApp->Broadcast( SfxSimpleHint( FID_KILLEDITVIEW ) ); if ( bOldMod && pExecuteSh && pCellAttrs && !bForget ) { // mit Eingabe zusammenfassen ? pExecuteSh->ApplySelectionPattern( *pCellAttrs, sal_True, sal_True ); pExecuteSh->AdjustBlockHeight(); } delete pCellAttrs; delete pObject; HideTip(); HideTipBelow(); nFormSelStart = nFormSelEnd = 0; aFormText.Erase(); bInOwnChange = sal_False; bInEnterHandler = sal_False; } void ScInputHandler::CancelHandler() { bInOwnChange = sal_True; // disable ModifyHdl (reset below) ImplCreateEditEngine(); bModified = sal_False; // don't rely on ShowRefFrame switching the active view synchronously // execute the function directly on the correct view's bindings instead // pRefViewSh is reset in ShowRefFrame - get pointer before ShowRefFrame call ScTabViewShell* pExecuteSh = pRefViewSh ? pRefViewSh : pActiveViewSh; if (bFormulaMode) { ShowRefFrame(); if (pExecuteSh) { pExecuteSh->SetTabNo(aCursorPos.Tab()); pExecuteSh->ActiveGrabFocus(); } bFormulaMode = sal_False; SFX_APP()->Broadcast( SfxSimpleHint( FID_REFMODECHANGED ) ); SC_MOD()->SetRefInputHdl(NULL); if (pInputWin) pInputWin->SetFormulaMode(sal_False); UpdateAutoCorrFlag(); } pRefViewSh = NULL; // auch ohne FormulaMode wegen Funktions-AP DeleteRangeFinder(); ResetAutoPar(); eMode = SC_INPUT_NONE; StopInputWinEngine( sal_True ); if (pExecuteSh) pExecuteSh->StopEditShell(); aCursorPos.Set(MAXCOL+1,0,0); // Flag, dass ungueltig pEngine->SetText(String()); if ( !pLastState && pExecuteSh ) pExecuteSh->UpdateInputHandler( sal_True ); // Status neu holen else NotifyChange( pLastState, sal_True ); nFormSelStart = nFormSelEnd = 0; aFormText.Erase(); bInOwnChange = sal_False; } sal_Bool ScInputHandler::IsModalMode( SfxObjectShell* pDocSh ) { // Referenzen auf unbenanntes Dokument gehen nicht return bFormulaMode && pRefViewSh && pRefViewSh->GetViewData()->GetDocument()->GetDocumentShell() != pDocSh && !pDocSh->HasName(); } void ScInputHandler::AddRefEntry() { const sal_Unicode cSep = ScCompiler::GetNativeSymbol(ocSep).GetChar(0); UpdateActiveView(); if (!pTableView && !pTopView) return; // z.B. FillMode DataChanging(); // kann nicht neu sein RemoveSelection(); if (pTableView) pTableView->InsertText( cSep, sal_False ); if (pTopView) pTopView->InsertText( cSep, sal_False ); DataChanged(); } void ScInputHandler::SetReference( const ScRange& rRef, ScDocument* pDoc ) { HideTip(); sal_Bool bOtherDoc = ( pRefViewSh && pRefViewSh->GetViewData()->GetDocument() != pDoc ); if (bOtherDoc) if (!pDoc->GetDocumentShell()->HasName()) { // Referenzen auf unbenanntes Dokument gehen nicht // (SetReference sollte dann auch nicht gerufen werden) return; } UpdateActiveView(); if (!pTableView && !pTopView) return; // z.B. FillMode // nie das "=" ueberschreiben! EditView* pActiveView = pTopView ? pTopView : pTableView; ESelection aSel = pActiveView->GetSelection(); aSel.Adjust(); if ( aSel.nStartPara == 0 && aSel.nStartPos == 0 ) return; DataChanging(); // kann nicht neu sein // Selektion umdrehen, falls rueckwaerts (noetig ???) if (pTableView) { ESelection aTabSel = pTableView->GetSelection(); if (aTabSel.nStartPos > aTabSel.nEndPos && aTabSel.nStartPara == aTabSel.nEndPara) { aTabSel.Adjust(); pTableView->SetSelection(aTabSel); } } if (pTopView) { ESelection aTopSel = pTopView->GetSelection(); if (aTopSel.nStartPos > aTopSel.nEndPos && aTopSel.nStartPara == aTopSel.nEndPara) { aTopSel.Adjust(); pTopView->SetSelection(aTopSel); } } // String aus Referenz erzeugen String aRefStr; const ScAddress::Details aAddrDetails( pDoc, aCursorPos ); if (bOtherDoc) { // Referenz auf anderes Dokument DBG_ASSERT(rRef.aStart.Tab()==rRef.aEnd.Tab(), "nStartTab!=nEndTab"); String aTmp; rRef.Format( aTmp, SCA_VALID|SCA_TAB_3D, pDoc, aAddrDetails ); // immer 3d SfxObjectShell* pObjSh = pDoc->GetDocumentShell(); // #i75893# convert escaped URL of the document to something user friendly // String aFileName = pObjSh->GetMedium()->GetName(); String aFileName = pObjSh->GetMedium()->GetURLObject().GetMainURL( INetURLObject::DECODE_UNAMBIGUOUS ); aRefStr = '\''; aRefStr += aFileName; aRefStr.AppendAscii(RTL_CONSTASCII_STRINGPARAM( "'#" )); aRefStr += aTmp; } else { if ( ( rRef.aStart.Tab() != aCursorPos.Tab() || rRef.aStart.Tab() != rRef.aEnd.Tab() ) && pDoc ) rRef.Format( aRefStr, SCA_VALID|SCA_TAB_3D, pDoc, aAddrDetails ); else rRef.Format( aRefStr, SCA_VALID, pDoc, aAddrDetails ); } if (pTableView || pTopView) { if (pTableView) pTableView->InsertText( aRefStr, sal_True ); if (pTopView) pTopView->InsertText( aRefStr, sal_True ); DataChanged(); } bSelIsRef = sal_True; } void ScInputHandler::InsertFunction( const String& rFuncName, sal_Bool bAddPar ) { if ( eMode == SC_INPUT_NONE ) { DBG_ERROR("InsertFunction, nicht im Eingabemodus"); return; } UpdateActiveView(); if (!pTableView && !pTopView) return; // z.B. FillMode DataChanging(); // kann nicht neu sein String aText = rFuncName; if (bAddPar) aText.AppendAscii(RTL_CONSTASCII_STRINGPARAM( "()" )); if (pTableView) { pTableView->InsertText( aText, sal_False ); if (bAddPar) { ESelection aSel = pTableView->GetSelection(); --aSel.nStartPos; --aSel.nEndPos; pTableView->SetSelection(aSel); } } if (pTopView) { pTopView->InsertText( aText, sal_False ); if (bAddPar) { ESelection aSel = pTopView->GetSelection(); --aSel.nStartPos; --aSel.nEndPos; pTopView->SetSelection(aSel); } } DataChanged(); if (bAddPar) AutoParAdded(); } void ScInputHandler::ClearText() { if ( eMode == SC_INPUT_NONE ) { DBG_ERROR("ClearText, nicht im Eingabemodus"); return; } UpdateActiveView(); if (!pTableView && !pTopView) return; // z.B. FillMode DataChanging(); // darf nicht neu sein String aEmpty; if (pTableView) { pTableView->GetEditEngine()->SetText( aEmpty ); pTableView->SetSelection( ESelection(0,0, 0,0) ); } if (pTopView) { pTopView->GetEditEngine()->SetText( aEmpty ); pTopView->SetSelection( ESelection(0,0, 0,0) ); } DataChanged(); } sal_Bool ScInputHandler::KeyInput( const KeyEvent& rKEvt, sal_Bool bStartEdit /* = sal_False */ ) { if (!bOptLoaded) { bAutoComplete = SC_MOD()->GetAppOptions().GetAutoComplete(); bOptLoaded = sal_True; } KeyCode aCode = rKEvt.GetKeyCode(); sal_uInt16 nModi = aCode.GetModifier(); sal_Bool bShift = aCode.IsShift(); sal_Bool bControl = aCode.IsMod1(); sal_Bool bAlt = aCode.IsMod2(); sal_uInt16 nCode = aCode.GetCode(); sal_Unicode nChar = rKEvt.GetCharCode(); // Alt-Return is accepted, everything else with ALT, or CTRL-TAB are not: if (( bAlt && !bControl && nCode != KEY_RETURN ) || ( bControl && aCode.GetCode() == KEY_TAB )) return sal_False; sal_Bool bInputLine = ( eMode==SC_INPUT_TOP ); sal_Bool bUsed = sal_False; sal_Bool bSkip = sal_False; sal_Bool bDoEnter = sal_False; switch ( nCode ) { case KEY_RETURN: if (bControl && !bShift && !bInputLine) bDoEnter = sal_True; else if ( nModi == 0 && nTipVisible && pFormulaData && nAutoPos != SCPOS_INVALID ) { PasteFunctionData(); bUsed = sal_True; } else if ( nModi == 0 && nTipVisible && aManualTip.Len() ) { PasteManualTip(); bUsed = sal_True; } else { sal_uInt8 nMode = SC_ENTER_NORMAL; if ( bShift && bControl ) nMode = SC_ENTER_MATRIX; else if ( bAlt ) nMode = SC_ENTER_BLOCK; EnterHandler( nMode ); if (pActiveViewSh) pActiveViewSh->MoveCursorEnter( bShift && !bControl ); bUsed = sal_True; } break; case KEY_TAB: if (!bControl && !bAlt) { if ( pFormulaData && nTipVisible && nAutoPos != SCPOS_INVALID ) { // blaettern NextFormulaEntry( bShift ); } else if ( pColumnData && bUseTab && nAutoPos != SCPOS_INVALID ) { // in den Eintraegen der AutoEingabe blaettern NextAutoEntry( bShift ); } else { EnterHandler(); // TabKeyInput gibt auf manchen Rechnern unter W95 Stackueberlaeufe, // darum direkter Aufruf: if (pActiveViewSh) pActiveViewSh->FindNextUnprot( bShift ); } bUsed = sal_True; } break; case KEY_ESCAPE: if ( nTipVisible ) { HideTip(); bUsed = sal_True; } else if( nTipVisibleSec ) { HideTipBelow(); bUsed = sal_True; } else if (eMode != SC_INPUT_NONE) { CancelHandler(); bUsed = sal_True; } else bSkip = sal_True; break; case KEY_F2: if ( !bShift && !bControl && !bAlt && eMode == SC_INPUT_TABLE ) { eMode = SC_INPUT_TYPE; bUsed = sal_True; } break; } // Cursortasten nur ausfuehren, wenn schon im Edit-Modus // z.B. wegen Shift-Ctrl-PageDn (ist nicht als Accelerator definiert) sal_Bool bCursorKey = EditEngine::DoesKeyMoveCursor(rKEvt); sal_Bool bInsKey = ( nCode == KEY_INSERT && !nModi ); // Insert wie Cursortasten behandeln if ( !bUsed && !bSkip && ( bDoEnter || EditEngine::DoesKeyChangeText(rKEvt) || ( eMode != SC_INPUT_NONE && ( bCursorKey || bInsKey ) ) ) ) { HideTip(); HideTipBelow(); if (bSelIsRef) { RemoveSelection(); bSelIsRef = sal_False; } UpdateActiveView(); sal_Bool bNewView = DataChanging( nChar ); if (bProtected) // Zelle geschuetzt? bUsed = sal_True; // Key-Event nicht weiterleiten else // Aenderungen erlaubt { if (bNewView ) // neu anlegen { if (pActiveViewSh) pActiveViewSh->GetViewData()->GetDocShell()->PostEditView( pEngine, aCursorPos ); UpdateActiveView(); if (eMode==SC_INPUT_NONE) if (pTableView || pTopView) { String aStrLoP; if ( bStartEdit && bCellHasPercentFormat && ((nChar >= '0' && nChar <= '9') || nChar == '-') ) aStrLoP = '%'; if (pTableView) { pTableView->GetEditEngine()->SetText( aStrLoP ); if ( aStrLoP.Len() ) pTableView->SetSelection( ESelection(0,0, 0,0) ); // before the '%' // don't call SetSelection if the string is empty anyway, // to avoid breaking the bInitial handling in ScViewData::EditGrowY } if (pTopView) { pTopView->GetEditEngine()->SetText( aStrLoP ); if ( aStrLoP.Len() ) pTopView->SetSelection( ESelection(0,0, 0,0) ); // before the '%' } } SyncViews(); } if (pTableView || pTopView) { // pActiveView->SetEditEngineUpdateMode(sal_True); //! gibt Muell !!!! if (bDoEnter) { if (pTableView) if( pTableView->PostKeyEvent( KeyEvent( CHAR_CR, KeyCode(KEY_RETURN) ) ) ) bUsed = sal_True; if (pTopView) if( pTopView->PostKeyEvent( KeyEvent( CHAR_CR, KeyCode(KEY_RETURN) ) ) ) bUsed = sal_True; } else if ( nAutoPar && nChar == ')' && CursorAtClosingPar() ) { SkipClosingPar(); bUsed = sal_True; } else { if (pTableView) if ( pTableView->PostKeyEvent( rKEvt ) ) bUsed = sal_True; if (pTopView) if ( pTopView->PostKeyEvent( rKEvt ) ) bUsed = sal_True; } // Auto-Eingabe: if ( bUsed && bAutoComplete ) { bUseTab = sal_False; nAutoPos = SCPOS_INVALID; // do not search further KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction(); if ( nChar && nChar != 8 && nChar != 127 && // no 'backspace', no 'delete' KEYFUNC_CUT != eFunc) // and no 'CTRL-X' { if (bFormulaMode) UseFormulaData(); else UseColData(); } } // when the selection is changed manually or an opening parenthesis // is typed, stop overwriting parentheses if ( bUsed && nChar == '(' ) ResetAutoPar(); if ( KEY_INSERT == nCode ) { SfxViewFrame* pViewFrm = SfxViewFrame::Current(); if (pViewFrm) pViewFrm->GetBindings().Invalidate( SID_ATTR_INSERT ); } if( bUsed && bFormulaMode && ( bCursorKey || bInsKey || nCode == KEY_DELETE || nCode == KEY_BACKSPACE ) ) { ShowTipCursor(); } } // #i114511# don't count cursor keys as modification sal_Bool bSetModified = !bCursorKey; DataChanged(sal_False, bSetModified); // also calls UpdateParenthesis() InvalidateAttribs(); //! in DataChanged ? } } if (pTopView && eMode != SC_INPUT_NONE) SyncViews(); return bUsed; } sal_Bool ScInputHandler::InputCommand( const CommandEvent& rCEvt, sal_Bool bForce ) { sal_Bool bUsed = sal_False; if ( rCEvt.GetCommand() == COMMAND_CURSORPOS ) { // #90346# for COMMAND_CURSORPOS, do as little as possible, because // with remote VCL, even a ShowCursor will generate another event. if ( eMode != SC_INPUT_NONE ) { UpdateActiveView(); if (pTableView || pTopView) { if (pTableView) pTableView->Command( rCEvt ); else if (pTopView) // call only once pTopView->Command( rCEvt ); bUsed = sal_True; } } } else { if ( bForce || eMode != SC_INPUT_NONE ) { if (!bOptLoaded) { bAutoComplete = SC_MOD()->GetAppOptions().GetAutoComplete(); bOptLoaded = sal_True; } HideTip(); HideTipBelow(); if ( bSelIsRef ) { RemoveSelection(); bSelIsRef = sal_False; } UpdateActiveView(); sal_Bool bNewView = DataChanging( 0, sal_True ); if (bProtected) // cell protected bUsed = sal_True; // event is used else // changes allowed { if (bNewView) // create new edit view { if (pActiveViewSh) pActiveViewSh->GetViewData()->GetDocShell()->PostEditView( pEngine, aCursorPos ); UpdateActiveView(); if (eMode==SC_INPUT_NONE) if (pTableView || pTopView) { String aStrLoP; if (pTableView) { pTableView->GetEditEngine()->SetText( aStrLoP ); pTableView->SetSelection( ESelection(0,0, 0,0) ); } if (pTopView) { pTopView->GetEditEngine()->SetText( aStrLoP ); pTopView->SetSelection( ESelection(0,0, 0,0) ); } } SyncViews(); } if (pTableView || pTopView) { if (pTableView) pTableView->Command( rCEvt ); if (pTopView) pTopView->Command( rCEvt ); bUsed = sal_True; if ( rCEvt.GetCommand() == COMMAND_ENDEXTTEXTINPUT ) { // AutoInput after ext text input nAutoPos = SCPOS_INVALID; if (bFormulaMode) UseFormulaData(); else UseColData(); } } DataChanged(); // calls UpdateParenthesis() InvalidateAttribs(); //! in DataChanged ? } } if (pTopView && eMode != SC_INPUT_NONE) SyncViews(); } return bUsed; } void ScInputHandler::NotifyChange( const ScInputHdlState* pState, sal_Bool bForce, ScTabViewShell* pSourceSh, sal_Bool bStopEditing) { // #62806# Wenn der Aufruf aus einem Makro-Aufruf im EnterHandler kommt, // gleich abbrechen und nicht den Status durcheinander bringen if (bInEnterHandler) return; sal_Bool bRepeat = (pState == pLastState); if (!bRepeat && pState && pLastState) bRepeat = sal::static_int_cast(*pState == *pLastState); if (bRepeat && !bForce) return; bInOwnChange = sal_True; // disable ModifyHdl (reset below) if ( pState && !pLastState ) // wieder enablen bForce = sal_True; sal_Bool bHadObject = pLastState && pLastState->GetEditData(); //! Before EditEngine gets eventually created (so it gets the right pools) if ( pSourceSh ) pActiveViewSh = pSourceSh; else pActiveViewSh = PTR_CAST(ScTabViewShell, SfxViewShell::Current()); ImplCreateEditEngine(); if ( pState != pLastState ) { delete pLastState; pLastState = pState ? new ScInputHdlState( *pState ) : NULL; } if ( pState && pActiveViewSh ) { ScModule* pScMod = SC_MOD(); if ( pState ) { sal_Bool bIgnore = sal_False; // hier auch fremde Referenzeingabe beruecksichtigen (z.B. Funktions-AP), // FormEditData falls gerade von der Hilfe auf Calc umgeschaltet wird: if ( !bFormulaMode && !pScMod->IsFormulaMode() && !pScMod->GetFormEditData() ) { if ( bModified ) { if (pState->GetPos() != aCursorPos) { if (!bProtected) EnterHandler(); } else bIgnore = sal_True; } if ( !bIgnore /* || bRepeat */ ) { const ScAddress& rSPos = pState->GetStartPos(); const ScAddress& rEPos = pState->GetEndPos(); const EditTextObject* pData = pState->GetEditData(); String aString = pState->GetString(); sal_Bool bTxtMod = sal_False; ScDocShell* pDocSh = pActiveViewSh->GetViewData()->GetDocShell(); ScDocument* pDoc = pDocSh->GetDocument(); aCursorPos = pState->GetPos(); if ( pData /* || bRepeat */ ) bTxtMod = sal_True; else if ( bHadObject ) bTxtMod = sal_True; else if ( bTextValid ) bTxtMod = ( aString != aCurrentText ); else bTxtMod = ( aString != GetEditText(pEngine) ); if ( bTxtMod || bForce ) { if (pData) { pEngine->SetText( *pData ); aString = GetEditText(pEngine); lcl_RemoveTabs(aString); bTextValid = sal_False; aCurrentText.Erase(); } else { aCurrentText = aString; bTextValid = sal_True; //! erst nur als String merken } if ( pInputWin ) pInputWin->SetTextString(aString); } if ( pInputWin ) // Bereichsanzeige { String aPosStr; const ScAddress::Details aAddrDetails( pDoc, aCursorPos ); // Ist der Bereich ein Name? //! per Timer suchen ??? if ( pActiveViewSh ) pActiveViewSh->GetViewData()->GetDocument()-> GetRangeAtBlock( ScRange( rSPos, rEPos ), &aPosStr ); if ( !aPosStr.Len() ) // kein Name -> formatieren { sal_uInt16 nFlags = 0; if( aAddrDetails.eConv == formula::FormulaGrammar::CONV_XL_R1C1 ) nFlags |= SCA_COL_ABSOLUTE | SCA_ROW_ABSOLUTE; if ( rSPos != rEPos ) { ScRange r(rSPos, rEPos); nFlags |= (nFlags << 4); r.Format( aPosStr, SCA_VALID | nFlags, pDoc, aAddrDetails ); } else aCursorPos.Format( aPosStr, SCA_VALID | nFlags, pDoc, aAddrDetails ); } // Disable the accessible VALUE_CHANGE event sal_Bool bIsSuppressed = pInputWin->IsAccessibilityEventsSuppressed(sal_False); pInputWin->SetAccessibilityEventsSuppressed(sal_True); pInputWin->SetPosString(aPosStr); pInputWin->SetAccessibilityEventsSuppressed(bIsSuppressed); pInputWin->SetSumAssignMode(); } if (bStopEditing) SFX_APP()->Broadcast( SfxSimpleHint( FID_KILLEDITVIEW ) ); // As long as the content is not edited, turn off online spelling. // Online spelling is turned back on in StartTable, after setting // the right language from cell attributes. sal_uLong nCntrl = pEngine->GetControlWord(); if ( nCntrl & EE_CNTRL_ONLINESPELLING ) pEngine->SetControlWord( nCntrl & ~EE_CNTRL_ONLINESPELLING ); bModified = sal_False; bSelIsRef = sal_False; bProtected = sal_False; bCommandErrorShown = sal_False; } } } // bProtected = sal_False; if ( pInputWin) { if(!pScMod->IsFormulaMode()&& !pScMod->IsRefDialogOpen()) //BugID 54702 { //Wenn RefDialog offen, dann nicht enablen if ( !pInputWin->IsEnabled()) { pInputWin->Enable(); if(pDelayTimer ) { DELETEZ( pDelayTimer ); } } } else if(pScMod->IsRefDialogOpen()) { // Da jedes Dokument eigenes InputWin hat, sollte if ( !pDelayTimer ) // nochmals Timer gestartet werden, da sonst Ein- { // gabezeile evt. noch aktiv ist. pDelayTimer = new Timer; pDelayTimer->SetTimeout( 500 ); // 100ms Verzoegerung pDelayTimer->SetTimeoutHdl( LINK( this, ScInputHandler, DelayTimer ) ); pDelayTimer->Start(); } } } } else // !pState || !pActiveViewSh { if ( !pDelayTimer ) { pDelayTimer = new Timer; pDelayTimer->SetTimeout( 500 ); // 100ms Verzoegerung pDelayTimer->SetTimeoutHdl( LINK( this, ScInputHandler, DelayTimer ) ); pDelayTimer->Start(); } } HideTip(); HideTipBelow(); bInOwnChange = sal_False; } void ScInputHandler::UpdateCellAdjust( SvxCellHorJustify eJust ) { eAttrAdjust = eJust; UpdateAdjust( 0 ); } void ScInputHandler::ResetDelayTimer() { if(pDelayTimer!=NULL) { DELETEZ( pDelayTimer ); if ( pInputWin) { pInputWin->Enable(); } } } IMPL_LINK( ScInputHandler, DelayTimer, Timer*, pTimer ) { if ( pTimer == pDelayTimer ) { DELETEZ( pDelayTimer ); if ( NULL == pLastState || SC_MOD()->IsFormulaMode() || SC_MOD()->IsRefDialogOpen()) { //! new method at ScModule to query if function autopilot is open SfxViewFrame* pViewFrm = SfxViewFrame::Current(); if ( pViewFrm && pViewFrm->GetChildWindow( SID_OPENDLG_FUNCTION ) ) { if ( pInputWin) { pInputWin->EnableButtons( sal_False ); pInputWin->Disable(); } } else if ( !bFormulaMode ) // #39210# Formel auch z.B. bei Hilfe behalten { bInOwnChange = sal_True; // disable ModifyHdl (reset below) pActiveViewSh = NULL; pEngine->SetText( EMPTY_STRING ); if ( pInputWin ) { pInputWin->SetPosString( EMPTY_STRING ); pInputWin->SetTextString( EMPTY_STRING ); pInputWin->Disable(); } bInOwnChange = sal_False; } } } return 0; } void ScInputHandler::InputSelection( EditView* pView ) { SyncViews( pView ); ShowTipCursor(); UpdateParenthesis(); // Selektion geaendert -> Klammer-Hervorhebung neu // when the selection is changed manually, stop overwriting parentheses ResetAutoPar(); } void ScInputHandler::InputChanged( EditView* pView, sal_Bool bFromNotify ) { ESelection aSelection = pView->GetSelection(); UpdateActiveView(); // #i20282# DataChanged needs to know if this is from the input line's modify handler sal_Bool bFromTopNotify = ( bFromNotify && pView == pTopView ); sal_Bool bNewView = DataChanging(); //! kann das hier ueberhaupt sein? aCurrentText = pView->GetEditEngine()->GetText(); // auch den String merken pEngine->SetText( aCurrentText ); DataChanged( bFromTopNotify ); bTextValid = sal_True; // wird in DataChanged auf sal_False gesetzt if ( pActiveViewSh ) { ScViewData* pViewData = pActiveViewSh->GetViewData(); if ( bNewView ) pViewData->GetDocShell()->PostEditView( pEngine, aCursorPos ); pViewData->EditGrowY(); pViewData->EditGrowX(); } SyncViews( pView ); } const String& ScInputHandler::GetEditString() { if (pEngine) { aCurrentText = pEngine->GetText(); // immer neu aus Engine bTextValid = sal_True; } return aCurrentText; } Size ScInputHandler::GetTextSize() { Size aSize; if ( pEngine ) aSize = Size( pEngine->CalcTextWidth(), pEngine->GetTextHeight() ); return aSize; } sal_Bool ScInputHandler::GetTextAndFields( ScEditEngineDefaulter& rDestEngine ) { sal_Bool bRet = sal_False; if (pEngine) { // Feldbefehle enthalten? sal_uInt32 nParCnt = pEngine->GetParagraphCount(); SfxItemSet aSet = pEngine->GetAttribs( ESelection(0,0,nParCnt,0) ); SfxItemState eFieldState = aSet.GetItemState( EE_FEATURE_FIELD, sal_False ); if ( eFieldState == SFX_ITEM_DONTCARE || eFieldState == SFX_ITEM_SET ) { // Inhalt kopieren EditTextObject* pObj = pEngine->CreateTextObject(); rDestEngine.SetText(*pObj); delete pObj; // Attribute loeschen for (sal_uInt32 i=0; i 1 ) { xub_StrLen nLen = rDestEngine.GetTextLen( 0 ); ESelection aSel( 0,nLen, 1,0 ); rDestEngine.QuickInsertText( ' ', aSel ); // Umbruch durch Space ersetzen --nParCnt; } bRet = sal_True; } } return bRet; } //------------------------------------------------------------------------ // Methoden fuer FunktionsAutopiloten: // InputGetSelection, InputSetSelection, InputReplaceSelection, InputGetFormulaStr //------------------------------------------------------------------------ void ScInputHandler::InputGetSelection( xub_StrLen& rStart, xub_StrLen& rEnd ) { rStart = nFormSelStart; rEnd = nFormSelEnd; } //------------------------------------------------------------------------ EditView* ScInputHandler::GetFuncEditView() { UpdateActiveView(); // wegen pTableView EditView* pView = NULL; if ( pInputWin ) { pInputWin->MakeDialogEditView(); pView = pInputWin->GetEditView(); } else { if ( eMode != SC_INPUT_TABLE ) { bCreatingFuncView = sal_True; // RangeFinder nicht anzeigen SetMode( SC_INPUT_TABLE ); bCreatingFuncView = sal_False; if ( pTableView ) pTableView->GetEditEngine()->SetText( EMPTY_STRING ); } pView = pTableView; } return pView; } //------------------------------------------------------------------------ void ScInputHandler::InputSetSelection( xub_StrLen nStart, xub_StrLen nEnd ) { if ( nStart <= nEnd ) { nFormSelStart = nStart; nFormSelEnd = nEnd; } else { nFormSelEnd = nStart; nFormSelStart = nEnd; } EditView* pView = GetFuncEditView(); if (pView) pView->SetSelection( ESelection(0,nStart, 0,nEnd) ); bModified = sal_True; } //------------------------------------------------------------------------ void ScInputHandler::InputReplaceSelection( const String& rStr ) { if (!pRefViewSh) pRefViewSh = pActiveViewSh; DBG_ASSERT(nFormSelEnd>=nFormSelStart,"Selektion kaputt..."); xub_StrLen nOldLen = nFormSelEnd-nFormSelStart; xub_StrLen nNewLen = rStr.Len(); if (nOldLen) aFormText.Erase( nFormSelStart, nOldLen ); if (nNewLen) aFormText.Insert( rStr, nFormSelStart ); nFormSelEnd = nFormSelStart + nNewLen; EditView* pView = GetFuncEditView(); if (pView) { pView->SetEditEngineUpdateMode( sal_False ); // pView->InsertText( rStr, sal_True ); pView->GetEditEngine()->SetText( aFormText ); pView->SetSelection( ESelection(0,nFormSelStart, 0,nFormSelEnd) ); pView->SetEditEngineUpdateMode( sal_True ); } bModified = sal_True; } //------------------------------------------------------------------------ String ScInputHandler::InputGetFormulaStr() { return aFormText; //! eigene Membervariable? } //======================================================================== // ScInputHdlState //======================================================================== ScInputHdlState::ScInputHdlState( const ScAddress& rCurPos, const ScAddress& rStartPos, const ScAddress& rEndPos, const String& rString, const EditTextObject* pData ) : aCursorPos ( rCurPos ), aStartPos ( rStartPos ), aEndPos ( rEndPos ), aString ( rString ), pEditData ( pData ? pData->Clone() : NULL ) { } //------------------------------------------------------------------------ ScInputHdlState::ScInputHdlState( const ScInputHdlState& rCpy ) : pEditData ( NULL ) { *this = rCpy; } //------------------------------------------------------------------------ ScInputHdlState::~ScInputHdlState() { delete pEditData; } //------------------------------------------------------------------------ int ScInputHdlState::operator==( const ScInputHdlState& r ) const { return ( (aStartPos == r.aStartPos) && (aEndPos == r.aEndPos) && (aCursorPos == r.aCursorPos) && (aString == r.aString) && ScGlobal::EETextObjEqual( pEditData, r.pEditData ) ); } //------------------------------------------------------------------------ ScInputHdlState& ScInputHdlState::operator=( const ScInputHdlState& r ) { delete pEditData; aCursorPos = r.aCursorPos; aStartPos = r.aStartPos; aEndPos = r.aEndPos; aString = r.aString; pEditData = r.pEditData ? r.pEditData->Clone() : NULL; return *this; }