/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * 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 . */ #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 #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 #define ShellClass_ScEditShell #include #include #include #include using namespace ::com::sun::star; SFX_IMPL_INTERFACE(ScEditShell, SfxShell) void ScEditShell::InitInterface_Impl() { GetStaticInterface()->RegisterPopupMenu("celledit"); } ScEditShell::ScEditShell(EditView* pView, ScViewData& rData) : pEditView (pView), rViewData (rData), bPastePossible (false), bIsInsertMode (true) { SetPool( pEditView->GetEditEngine()->GetEmptyItemSet().GetPool() ); SetUndoManager( &pEditView->GetEditEngine()->GetUndoManager() ); SetName("EditCell"); SfxShell::SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::EditCell)); } ScEditShell::~ScEditShell() { if ( mxClipEvtLstnr.is() ) { mxClipEvtLstnr->RemoveListener( rViewData.GetActiveWin() ); // The listener may just now be waiting for the SolarMutex and call the link // afterwards, in spite of RemoveListener. So the link has to be reset, too. mxClipEvtLstnr->ClearCallbackLink(); } } ScInputHandler* ScEditShell::GetMyInputHdl() { return SC_MOD()->GetInputHdl( rViewData.GetViewShell() ); } void ScEditShell::SetEditView(EditView* pView) { pEditView = pView; pEditView->SetInsertMode( bIsInsertMode ); SetPool( pEditView->GetEditEngine()->GetEmptyItemSet().GetPool() ); SetUndoManager( &pEditView->GetEditEngine()->GetUndoManager() ); } static void lcl_RemoveAttribs( EditView& rEditView ) { ScEditEngineDefaulter* pEngine = static_cast(rEditView.GetEditEngine()); bool bOld = pEngine->SetUpdateLayout(false); OUString aName = ScResId( STR_UNDO_DELETECONTENTS ); ViewShellId nViewShellId(-1); if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell()) nViewShellId = pViewSh->GetViewShellId(); pEngine->GetUndoManager().EnterListAction( aName, aName, 0, nViewShellId ); rEditView.RemoveAttribs(true); pEngine->RepeatDefaults(); // paragraph attributes from cell formats must be preserved pEngine->GetUndoManager().LeaveListAction(); pEngine->SetUpdateLayout(bOld); } static void lclInsertCharacter( EditView* pTableView, EditView* pTopView, sal_Unicode cChar ) { OUString aString( cChar ); if( pTableView ) pTableView->InsertText( aString ); if( pTopView ) pTopView->InsertText( aString ); } void ScEditShell::Execute( SfxRequest& rReq ) { const SfxItemSet* pReqArgs = rReq.GetArgs(); sal_uInt16 nSlot = rReq.GetSlot(); SfxBindings& rBindings = rViewData.GetBindings(); ScInputHandler* pHdl = GetMyInputHdl(); OSL_ENSURE(pHdl,"no ScInputHandler"); EditView* pTopView = pHdl->GetTopView(); // Has thee input cell the focus? EditView* pTableView = pHdl->GetTableView(); OSL_ENSURE(pTableView,"no EditView :-("); /* #i91683# No EditView if spell-check dialog is active and positioned on * an error and user immediately (without double click or F2) selected a * text portion of that cell with the mouse and wanted to modify it. */ /* FIXME: Bailing out only cures the symptom and prevents a crash, no edit * action is possible. A real fix somehow would need to create a valid * EditView from the spell-check view. */ if (!pTableView) return; EditEngine* pEngine = pTableView->GetEditEngine(); pHdl->DataChanging(); bool bSetSelIsRef = false; bool bSetModified = true; switch ( nSlot ) { case SID_ATTR_INSERT: case FID_INS_CELL_CONTENTS: // Insert taste, while defined as Acc bIsInsertMode = !pTableView->IsInsertMode(); pTableView->SetInsertMode( bIsInsertMode ); if (pTopView) pTopView->SetInsertMode( bIsInsertMode ); rBindings.Invalidate( SID_ATTR_INSERT ); break; case SID_THES: { OUString aReplaceText; const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_THES_WORD_REPLACE); if (pItem2) aReplaceText = pItem2->GetValue(); if (!aReplaceText.isEmpty()) ReplaceTextWithSynonym( *pEditView, aReplaceText ); } break; case SID_COPY: pTableView->Copy(); bSetModified = false; break; case SID_CUT: pTableView->Cut(); if (pTopView) pTopView->DeleteSelected(); break; case SID_PASTE: { EVControlBits nControl = pTableView->GetControlWord(); if (pTopView) { pTopView->Paste(); pTableView->SetControlWord(nControl | EVControlBits::SINGLELINEPASTE); } pTableView->PasteSpecial(); pTableView->SetControlWord(nControl); } break; case SID_DELETE: pTableView->DeleteSelected(); if (pTopView) pTopView->DeleteSelected(); break; case SID_CELL_FORMAT_RESET: // "Standard" lcl_RemoveAttribs( *pTableView ); if ( pTopView ) lcl_RemoveAttribs( *pTopView ); break; case SID_CLIPBOARD_FORMAT_ITEMS: { SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; const SfxPoolItem* pItem; if ( pReqArgs && pReqArgs->GetItemState(nSlot, true, &pItem) == SfxItemState::SET ) if (auto pIntItem = dynamic_cast( pItem)) nFormat = static_cast(pIntItem->GetValue()); if ( nFormat != SotClipboardFormatId::NONE ) { if (SotClipboardFormatId::STRING == nFormat) pTableView->Paste(); else pTableView->PasteSpecial(); if (pTopView) pTopView->Paste(); } } break; case SID_PASTE_SPECIAL: { SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); ScopedVclPtr pDlg(pFact->CreatePasteDialog(rViewData.GetDialogParent())); SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; pDlg->Insert( SotClipboardFormatId::STRING, OUString() ); pDlg->Insert( SotClipboardFormatId::RTF, OUString() ); pDlg->Insert( SotClipboardFormatId::RICHTEXT, OUString() ); // Do not offer SotClipboardFormatId::STRING_TSVC for // in-cell paste. TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) ); nFormat = pDlg->GetFormat( aDataHelper.GetTransferable() ); pDlg.disposeAndClear(); // while the dialog was open, edit mode may have been stopped if (!SC_MOD()->IsInputMode()) return; if (nFormat != SotClipboardFormatId::NONE) { if (SotClipboardFormatId::STRING == nFormat) pTableView->Paste(); else pTableView->PasteSpecial(); if (pTopView) pTopView->Paste(); } if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr) pViewWindow->GrabFocus(); } break; case SID_PASTE_UNFORMATTED: { pTableView->Paste(); if (pTopView) { pTopView->Paste(); if (vcl::Window* pViewWindow = pTopView->GetWindow()) pViewWindow->GrabFocus(); } } break; case SID_SELECTALL: { sal_Int32 nPar = pEngine->GetParagraphCount(); if (nPar) { sal_Int32 nLen = pEngine->GetTextLen(nPar-1); pTableView->SetSelection(ESelection(0,0,nPar-1,nLen)); if (pTopView) pTopView->SetSelection(ESelection(0,0,nPar-1,nLen)); rBindings.Invalidate( SID_ATTR_CHAR_FONT ); rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT ); rBindings.Invalidate( SID_ATTR_CHAR_POSTURE ); rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE ); rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT ); rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED ); rBindings.Invalidate( SID_ATTR_CHAR_KERNING ); rBindings.Invalidate( SID_ATTR_CHAR_COLOR ); rBindings.Invalidate( SID_SET_SUPER_SCRIPT ); rBindings.Invalidate( SID_SET_SUB_SCRIPT ); } } return; case SID_UNICODE_NOTATION_TOGGLE: { EditView* pActiveView = pHdl->GetActiveView(); if( pActiveView ) { OUString sInput = pEngine->GetText(); ESelection aSel( pActiveView->GetSelection() ); if( aSel.HasRange() ) sInput = pActiveView->GetSelected(); if( aSel.nStartPos > aSel.nEndPos ) aSel.nEndPos = aSel.nStartPos; //calculate a valid end-position by reading logical characters sal_Int32 nUtf16Pos=0; while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) ) { sInput.iterateCodePoints(&nUtf16Pos); if( nUtf16Pos > aSel.nEndPos ) aSel.nEndPos = nUtf16Pos; } ToggleUnicodeCodepoint aToggle; while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) ) --nUtf16Pos; OUString sReplacement = aToggle.ReplacementString(); if( !sReplacement.isEmpty() ) { aSel.nStartPos = aSel.nEndPos - aToggle.StringToReplace().getLength(); pTableView->SetSelection( aSel ); pTableView->InsertText(sReplacement, true); if( pTopView ) { pTopView->SetSelection( aSel ); pTopView->InsertText(sReplacement, true); } } } } break; case SID_CHARMAP: { SvtScriptType nScript = pTableView->GetSelectedScriptType(); sal_uInt16 nFontWhich = ( nScript == SvtScriptType::ASIAN ) ? EE_CHAR_FONTINFO_CJK : ( ( nScript == SvtScriptType::COMPLEX ) ? EE_CHAR_FONTINFO_CTL : EE_CHAR_FONTINFO ); auto const attribs = pTableView->GetAttribs(); const SvxFontItem& rItem = static_cast( attribs.Get(nFontWhich)); OUString aString; std::shared_ptr aNewItem(std::make_shared(EE_CHAR_FONTINFO)); const SfxItemSet *pArgs = rReq.GetArgs(); const SfxPoolItem* pItem = nullptr; if( pArgs ) pArgs->GetItemState(SID_CHARMAP, false, &pItem); if ( pItem ) { aString = static_cast(pItem)->GetValue(); const SfxStringItem* pFontItem = pArgs->GetItemIfSet( SID_ATTR_SPECIALCHAR, false); if ( pFontItem ) { const OUString& aFontName(pFontItem->GetValue()); vcl::Font aFont(aFontName, Size(1,1)); // Size just because CTOR // tdf#125054 see comment in drtxob.cxx, same ID aNewItem = std::make_shared( aFont.GetFamilyType(), aFont.GetFamilyName(), aFont.GetStyleName(), aFont.GetPitch(), aFont.GetCharSet(), ATTR_FONT); } else { aNewItem.reset(rItem.Clone()); } // tdf#125054 force Item to correct intended ID aNewItem->SetWhich(EE_CHAR_FONTINFO); } else { ScViewUtil::ExecuteCharMap(rItem, *rViewData.GetViewShell()); // while the dialog was open, edit mode may have been stopped if (!SC_MOD()->IsInputMode()) return; } if ( !aString.isEmpty() ) { // if string contains WEAK characters, set all fonts SvtScriptType nSetScript; ScDocument& rDoc = rViewData.GetDocument(); if ( rDoc.HasStringWeakCharacters( aString ) ) nSetScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; else nSetScript = rDoc.GetStringScriptType( aString ); SfxItemSet aSet( pTableView->GetEmptyItemSet() ); SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, GetPool() ); aSetItem.PutItemForScriptType( nSetScript, *aNewItem ); aSet.Put( aSetItem.GetItemSet(), false ); // SetAttribs on the View selects a word, when nothing is selected pTableView->GetEditEngine()->QuickSetAttribs( aSet, pTableView->GetSelection() ); pTableView->InsertText(aString); if (pTopView) pTopView->InsertText(aString); SfxStringItem aStringItem( SID_CHARMAP, aString ); SfxStringItem aFontItem( SID_ATTR_SPECIALCHAR, aNewItem->GetFamilyName() ); rReq.AppendItem( aFontItem ); rReq.AppendItem( aStringItem ); rReq.Done(); } if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr) pViewWindow->GrabFocus(); } break; case FID_INSERT_NAME: { ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); ScopedVclPtr pDlg(pFact->CreateScNamePasteDlg(rViewData.GetDialogParent(), rViewData.GetDocShell())); short nRet = pDlg->Execute(); // pDlg is needed below // while the dialog was open, edit mode may have been stopped if (!SC_MOD()->IsInputMode()) return; if ( nRet == BTN_PASTE_NAME ) { std::vector aNames = pDlg->GetSelectedNames(); if (!aNames.empty()) { OUStringBuffer aBuffer; for (const auto& rName : aNames) { aBuffer.append(rName + " "); } const OUString s = aBuffer.makeStringAndClear(); pTableView->InsertText(s); if (pTopView) pTopView->InsertText(s); } } pDlg.disposeAndClear(); if (vcl::Window* pViewWindow = pTopView ? pTopView->GetWindow() : nullptr) pViewWindow->GrabFocus(); } break; case SID_CHAR_DLG_EFFECT: case SID_CHAR_DLG: { SfxItemSet aAttrs( pTableView->GetAttribs() ); SfxObjectShell* pObjSh = rViewData.GetSfxDocShell(); ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); ScopedVclPtr pDlg(pFact->CreateScCharDlg( rViewData.GetDialogParent(), &aAttrs, pObjSh, false)); if (nSlot == SID_CHAR_DLG_EFFECT) { pDlg->SetCurPageId("fonteffects"); } short nRet = pDlg->Execute(); // pDlg is needed below // while the dialog was open, edit mode may have been stopped if (!SC_MOD()->IsInputMode()) return; if ( nRet == RET_OK ) { const SfxItemSet* pOut = pDlg->GetOutputItemSet(); pTableView->SetAttribs( *pOut ); } } break; case SID_TOGGLE_REL: { /* TODO: MLFORMULA: this should work also with multi-line formulas. */ if (pEngine->GetParagraphCount() == 1) { OUString aText = pEngine->GetText(); ESelection aSel = pEditView->GetSelection(); // current View ScDocument& rDoc = rViewData.GetDocument(); ScRefFinder aFinder(aText, rViewData.GetCurPos(), rDoc, rDoc.GetAddressConvention()); aFinder.ToggleRel( aSel.nStartPos, aSel.nEndPos ); if (aFinder.GetFound()) { const OUString& aNew = aFinder.GetText(); ESelection aNewSel( 0,aFinder.GetSelStart(), 0,aFinder.GetSelEnd() ); pEngine->SetText( aNew ); pTableView->SetSelection( aNewSel ); if ( pTopView ) { pTopView->GetEditEngine()->SetText( aNew ); pTopView->SetSelection( aNewSel ); } // reference is being selected -> do not overwrite when typing bSetSelIsRef = true; } } } break; case SID_HYPERLINK_SETLINK: if( pReqArgs ) { const SfxPoolItem* pItem; if ( pReqArgs->GetItemState( SID_HYPERLINK_SETLINK, true, &pItem ) == SfxItemState::SET ) { const SvxHyperlinkItem* pHyper = static_cast(pItem); const OUString& rName = pHyper->GetName(); const OUString& rURL = pHyper->GetURL(); const OUString& rTarget = pHyper->GetTargetFrame(); SvxLinkInsertMode eMode = pHyper->GetInsertMode(); bool bCellLinksOnly = (SC_MOD()->GetAppOptions().GetLinksInsertedLikeMSExcel() && rViewData.GetSfxDocShell()->GetMedium()->GetFilter()->IsMSOFormat()) || comphelper::LibreOfficeKit::isActive(); bool bDone = false; if ( (eMode == HLINK_DEFAULT || eMode == HLINK_FIELD) && !bCellLinksOnly ) { std::unique_ptr aSvxFieldDataPtr(GetURLField()); const SvxURLField* pURLField(static_cast(aSvxFieldDataPtr.get())); if ( pURLField ) { // select old field ESelection aSel = pTableView->GetSelection(); aSel.Adjust(); aSel.nEndPara = aSel.nStartPara; aSel.nEndPos = aSel.nStartPos + 1; pTableView->SetSelection( aSel ); // insert new field SvxURLField aURLField( rURL, rName, SvxURLFormat::Repr ); aURLField.SetTargetFrame( rTarget ); SvxFieldItem aURLItem( aURLField, EE_FEATURE_FIELD ); pTableView->InsertField( aURLItem ); pTableView->SetSelection( aSel ); // select inserted field // now also fields in the Top-View if ( pTopView ) { aSel = pTopView->GetSelection(); aSel.nEndPara = aSel.nStartPara; aSel.nEndPos = aSel.nStartPos + 1; pTopView->SetSelection( aSel ); pTopView->InsertField( aURLItem ); pTopView->SetSelection( aSel ); // select inserted field } bDone = true; } } if (!bDone) { if (bCellLinksOnly) { sal_Int32 nPar = pEngine->GetParagraphCount(); if (nPar) { sal_Int32 nLen = pEngine->GetTextLen(nPar - 1); pTableView->SetSelection(ESelection(0, 0, nPar - 1, nLen)); if (pTopView) pTopView->SetSelection(ESelection(0, 0, nPar - 1, nLen)); } } rViewData.GetViewShell()-> InsertURL( rName, rURL, rTarget, static_cast(eMode) ); // when "Button", the InsertURL in ViewShell turns the EditShell off // thus the immediate return statement return; } } } break; case SID_OPEN_HYPERLINK: { const SvxFieldItem* pFieldItem = pEditView->GetFieldAtSelection(/*AlsoCheckBeforeCursor=*/true); const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr; if (const SvxURLField* pURLField = dynamic_cast(pField)) ScGlobal::OpenURL( pURLField->GetURL(), pURLField->GetTargetFrame(), true ); return; } case SID_EDIT_HYPERLINK: { // Ensure the field is selected first pEditView->SelectFieldAtCursor(); rViewData.GetViewShell()->GetViewFrame().GetDispatcher()->Execute( SID_HYPERLINK_DIALOG); } break; case SID_COPY_HYPERLINK_LOCATION: { const SvxFieldItem* pFieldItem = pEditView->GetFieldAtSelection(/*AlsoCheckBeforeCursor=*/true); const SvxFieldData* pField = pFieldItem ? pFieldItem->GetField() : nullptr; if (const SvxURLField* pURLField = dynamic_cast(pField)) { uno::Reference xClipboard = pEditView->GetClipboard(); vcl::unohelper::TextDataObject::CopyStringTo(pURLField->GetURL(), xClipboard, SfxViewShell::Current()); } } break; case SID_REMOVE_HYPERLINK: { URLFieldHelper::RemoveURLField(*pEditView); } break; case FN_INSERT_SOFT_HYPHEN: lclInsertCharacter( pTableView, pTopView, CHAR_SHY ); break; case FN_INSERT_HARDHYPHEN: lclInsertCharacter( pTableView, pTopView, CHAR_NBHY ); break; case FN_INSERT_HARD_SPACE: lclInsertCharacter( pTableView, pTopView, CHAR_NBSP ); break; case FN_INSERT_NNBSP: lclInsertCharacter( pTableView, pTopView, CHAR_NNBSP ); break; case SID_INSERT_RLM: lclInsertCharacter( pTableView, pTopView, CHAR_RLM ); break; case SID_INSERT_LRM: lclInsertCharacter( pTableView, pTopView, CHAR_LRM ); break; case SID_INSERT_ZWSP: lclInsertCharacter( pTableView, pTopView, CHAR_ZWSP ); break; case SID_INSERT_WJ: lclInsertCharacter( pTableView, pTopView, CHAR_WJ ); break; case SID_INSERT_FIELD_SHEET: { SvxTableField aField(rViewData.GetTabNo()); SvxFieldItem aItem(aField, EE_FEATURE_FIELD); pTableView->InsertField(aItem); } break; case SID_INSERT_FIELD_TITLE: { SvxFileField aField; SvxFieldItem aItem(aField, EE_FEATURE_FIELD); pTableView->InsertField(aItem); } break; case SID_INSERT_FIELD_DATE_VAR: { SvxDateField aField; SvxFieldItem aItem(aField, EE_FEATURE_FIELD); pTableView->InsertField(aItem); } break; } pHdl->DataChanged(false, bSetModified); if (bSetSelIsRef) pHdl->SetSelIsRef(true); } static void lcl_DisableAll( SfxItemSet& rSet ) // disable all slots { SfxWhichIter aIter( rSet ); sal_uInt16 nWhich = aIter.FirstWhich(); while (nWhich) { rSet.DisableItem( nWhich ); nWhich = aIter.NextWhich(); } } void ScEditShell::GetState( SfxItemSet& rSet ) { // When deactivating the view, edit mode is stopped, but the EditShell is left active // (a shell can't be removed from within Deactivate). In that state, the EditView isn't inserted // into the EditEngine, so it can have an invalid selection and must not be used. ScInputHandler* pHdl = GetMyInputHdl(); if ( !rViewData.HasEditView( rViewData.GetActivePart() ) ) { lcl_DisableAll( rSet ); // Some items are actually useful and still applicable when in formula building mode: enable if (pHdl && pHdl->IsFormulaMode()) { rSet.ClearItem(SID_TOGGLE_REL); // F4 Cycle Cell Reference Types rSet.ClearItem(SID_CHARMAP); // Insert Special Characters } return; } EditView* pActiveView = pHdl ? pHdl->GetActiveView() : pEditView; SfxWhichIter aIter( rSet ); sal_uInt16 nWhich = aIter.FirstWhich(); while (nWhich) { switch (nWhich) { case SID_ATTR_INSERT: // Status row { if ( pActiveView ) rSet.Put( SfxBoolItem( nWhich, pActiveView->IsInsertMode() ) ); else { // Here the code used to pass the value 42 and it used // to "work" without warnings because the SfxBoolItem // was based on 'sal_Bool', which is actually 'unsigned // char'. But now it uses actual 'bool', and passing 42 // for a 'bool' parameter causes a warning at least with // MSVC. So use 'true'. I really really hope there is // not code somewhere that retrieves this "boolean" item // and checks it value for the magic value 42... rSet.Put( SfxBoolItem( nWhich, true) ); } } break; case SID_HYPERLINK_GETLINK: { SvxHyperlinkItem aHLinkItem; bool bCellLinksOnly = (SC_MOD()->GetAppOptions().GetLinksInsertedLikeMSExcel() && rViewData.GetSfxDocShell()->GetMedium()->GetFilter()->IsMSOFormat()) || comphelper::LibreOfficeKit::isActive(); std::unique_ptr aSvxFieldDataPtr(GetURLField()); const SvxURLField* pURLField(static_cast(aSvxFieldDataPtr.get())); if (!bCellLinksOnly) { if (pURLField) { aHLinkItem.SetName(pURLField->GetRepresentation()); aHLinkItem.SetURL(pURLField->GetURL()); aHLinkItem.SetTargetFrame(pURLField->GetTargetFrame()); } else if (pActiveView) { // use selected text as name for urls OUString sReturn = pActiveView->GetSelected(); sReturn = sReturn.copy( 0, std::min(sReturn.getLength(), static_cast(255))); aHLinkItem.SetName(comphelper::string::stripEnd(sReturn, ' ')); } } else { if (!pURLField) { aSvxFieldDataPtr = GetFirstURLFieldFromCell(); pURLField = static_cast(aSvxFieldDataPtr.get()); } if (pURLField) { aHLinkItem.SetURL(pURLField->GetURL()); aHLinkItem.SetTargetFrame(pURLField->GetTargetFrame()); } ScDocument& rDoc = rViewData.GetDocument(); SCCOL nPosX = rViewData.GetCurX(); SCROW nPosY = rViewData.GetCurY(); SCTAB nTab = rViewData.GetTabNo(); aHLinkItem.SetName(rDoc.GetString(nPosX, nPosY, nTab)); } rSet.Put(aHLinkItem); } break; case SID_OPEN_HYPERLINK: case SID_EDIT_HYPERLINK: case SID_COPY_HYPERLINK_LOCATION: case SID_REMOVE_HYPERLINK: { if (!URLFieldHelper::IsCursorAtURLField(*pEditView, /*AlsoCheckBeforeCursor=*/true)) rSet.DisableItem (nWhich); } break; case SID_TRANSLITERATE_HALFWIDTH: case SID_TRANSLITERATE_FULLWIDTH: case SID_TRANSLITERATE_HIRAGANA: case SID_TRANSLITERATE_KATAKANA: case SID_INSERT_RLM: case SID_INSERT_LRM: ScViewUtil::HideDisabledSlot( rSet, rViewData.GetBindings(), nWhich ); break; case SID_THES: { OUString aStatusVal; LanguageType nLang = LANGUAGE_NONE; bool bIsLookUpWord = pActiveView && GetStatusValueForThesaurusFromContext(aStatusVal, nLang, *pActiveView); rSet.Put( SfxStringItem( SID_THES, aStatusVal ) ); // disable thesaurus context menu entry if there is nothing to look up bool bCanDoThesaurus = ScModule::HasThesaurusLanguage( nLang ); if (!bIsLookUpWord || !bCanDoThesaurus) rSet.DisableItem( SID_THES ); } break; case SID_INSERT_FIELD_SHEET: case SID_INSERT_FIELD_TITLE: case SID_INSERT_FIELD_DATE_VAR: break; case SID_COPY: case SID_CUT: if (GetObjectShell() && GetObjectShell()->isContentExtractionLocked()) { rSet.DisableItem(SID_COPY); rSet.DisableItem(SID_CUT); } break; } nWhich = aIter.NextWhich(); } } std::unique_ptr ScEditShell::GetURLField() { ScInputHandler* pHdl = GetMyInputHdl(); EditView* pActiveView = pHdl ? pHdl->GetActiveView() : pEditView; if (!pActiveView) return std::unique_ptr(); const SvxFieldData* pField = pActiveView->GetFieldUnderMouseOrInSelectionOrAtCursor(); if (auto pURLField = dynamic_cast(pField)) return pURLField->Clone(); return std::unique_ptr(); } std::unique_ptr ScEditShell::GetFirstURLFieldFromCell() { EditEngine* pEE = GetEditView()->GetEditEngine(); sal_Int32 nParaCount = pEE->GetParagraphCount(); for (sal_Int32 nPara = 0; nPara < nParaCount; ++nPara) { ESelection aSel(nPara, 0); std::vector aPosList; pEE->GetPortions(nPara, aPosList); for (const auto& rPos : aPosList) { aSel.nEndPos = rPos; SfxItemSet aEditSet(pEE->GetAttribs(aSel)); if (aSel.nStartPos + 1 == aSel.nEndPos) { // test if the character is a text field if (const SvxFieldItem* pItem = aEditSet.GetItemIfSet(EE_FEATURE_FIELD, false)) { const SvxFieldData* pField = pItem->GetField(); if (const SvxURLField* pUrlField = dynamic_cast(pField)) { return pUrlField->Clone(); } } } aSel.nStartPos = aSel.nEndPos; } } return std::unique_ptr(); } IMPL_LINK( ScEditShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void ) { bPastePossible = ( pDataHelper->HasFormat( SotClipboardFormatId::STRING ) || pDataHelper->HasFormat( SotClipboardFormatId::RTF ) || pDataHelper->HasFormat( SotClipboardFormatId::RICHTEXT )); SfxBindings& rBindings = rViewData.GetBindings(); rBindings.Invalidate( SID_PASTE ); rBindings.Invalidate( SID_PASTE_SPECIAL ); rBindings.Invalidate( SID_PASTE_UNFORMATTED ); rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS ); } void ScEditShell::GetClipState( SfxItemSet& rSet ) { // Do not offer SotClipboardFormatId::STRING_TSVC for in-cell paste. if ( !mxClipEvtLstnr.is() ) { // create listener mxClipEvtLstnr = new TransferableClipboardListener( LINK( this, ScEditShell, ClipboardChanged ) ); vcl::Window* pWin = rViewData.GetActiveWin(); mxClipEvtLstnr->AddListener( pWin ); // get initial state TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) ); bPastePossible = ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) || aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ); } SfxWhichIter aIter( rSet ); sal_uInt16 nWhich = aIter.FirstWhich(); while (nWhich) { switch (nWhich) { case SID_PASTE: case SID_PASTE_SPECIAL: case SID_PASTE_UNFORMATTED: if( !bPastePossible ) rSet.DisableItem( nWhich ); break; case SID_CLIPBOARD_FORMAT_ITEMS: if( bPastePossible ) { SvxClipboardFormatItem aFormats( SID_CLIPBOARD_FORMAT_ITEMS ); TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( rViewData.GetActiveWin() ) ); if ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ) aFormats.AddClipbrdFormat( SotClipboardFormatId::STRING ); if ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ) aFormats.AddClipbrdFormat( SotClipboardFormatId::RTF ); rSet.Put( aFormats ); } else rSet.DisableItem( nWhich ); break; } nWhich = aIter.NextWhich(); } } static void lcl_InvalidateUnder( SfxBindings& rBindings ) { 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 ); } void ScEditShell::ExecuteAttr(SfxRequest& rReq) { SfxItemSet aSet( pEditView->GetEmptyItemSet() ); SfxBindings& rBindings = rViewData.GetBindings(); const SfxItemSet* pArgs = rReq.GetArgs(); sal_uInt16 nSlot = rReq.GetSlot(); switch ( nSlot ) { case SID_ATTR_CHAR_FONTHEIGHT: case SID_ATTR_CHAR_FONT: { if (pArgs) { // #i78017 establish the same behaviour as in Writer SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; if (nSlot == SID_ATTR_CHAR_FONT) { nScript = pEditView->GetSelectedScriptType(); if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType(); } SfxItemPool& rPool = GetPool(); SvxScriptSetItem aSetItem( nSlot, rPool ); sal_uInt16 nWhich = rPool.GetWhich( nSlot ); aSetItem.PutItemForScriptType( nScript, pArgs->Get( nWhich ) ); aSet.Put( aSetItem.GetItemSet(), false ); } } break; case SID_ATTR_CHAR_COLOR: { if (pArgs) { aSet.Put( pArgs->Get( pArgs->GetPool()->GetWhich( nSlot ) ) ); rBindings.Invalidate( nSlot ); } } break; // Toggles case SID_ATTR_CHAR_WEIGHT: { // #i78017 establish the same behaviour as in Writer SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; SfxItemPool& rPool = GetPool(); bool bOld = false; SvxScriptSetItem aOldSetItem( nSlot, rPool ); aOldSetItem.GetItemSet().Put( pEditView->GetAttribs(), false ); const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript ); if ( pCore && static_cast(pCore)->GetWeight() > WEIGHT_NORMAL ) bOld = true; SvxScriptSetItem aSetItem( nSlot, rPool ); aSetItem.PutItemForScriptType( nScript, SvxWeightItem( bOld ? WEIGHT_NORMAL : WEIGHT_BOLD, EE_CHAR_WEIGHT ) ); aSet.Put( aSetItem.GetItemSet(), false ); rBindings.Invalidate( nSlot ); } break; case SID_ATTR_CHAR_POSTURE: { // #i78017 establish the same behaviour as in Writer SvtScriptType nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; SfxItemPool& rPool = GetPool(); bool bOld = false; SvxScriptSetItem aOldSetItem( nSlot, rPool ); aOldSetItem.GetItemSet().Put( pEditView->GetAttribs(), false ); const SfxPoolItem* pCore = aOldSetItem.GetItemOfScript( nScript ); if ( pCore && static_cast(pCore)->GetValue() != ITALIC_NONE ) bOld = true; SvxScriptSetItem aSetItem( nSlot, rPool ); aSetItem.PutItemForScriptType( nScript, SvxPostureItem( bOld ? ITALIC_NONE : ITALIC_NORMAL, EE_CHAR_ITALIC ) ); aSet.Put( aSetItem.GetItemSet(), false ); rBindings.Invalidate( nSlot ); } break; case SID_ULINE_VAL_NONE: aSet.Put( SvxUnderlineItem( LINESTYLE_NONE, EE_CHAR_UNDERLINE ) ); lcl_InvalidateUnder( rBindings ); break; case SID_ATTR_CHAR_UNDERLINE: case SID_ULINE_VAL_SINGLE: case SID_ULINE_VAL_DOUBLE: case SID_ULINE_VAL_DOTTED: { FontLineStyle eOld = pEditView->GetAttribs().Get(EE_CHAR_UNDERLINE).GetLineStyle(); FontLineStyle eNew = eOld; switch (nSlot) { case SID_ATTR_CHAR_UNDERLINE: if ( pArgs ) { const SvxTextLineItem& rTextLineItem = static_cast< const SvxTextLineItem& >( pArgs->Get( pArgs->GetPool()->GetWhich(nSlot) ) ); eNew = rTextLineItem.GetLineStyle(); } else { eNew = ( eOld != LINESTYLE_NONE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE; } break; case SID_ULINE_VAL_SINGLE: eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE; break; case SID_ULINE_VAL_DOUBLE: eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE; break; case SID_ULINE_VAL_DOTTED: eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED; break; } aSet.Put( SvxUnderlineItem( eNew, EE_CHAR_UNDERLINE ) ); lcl_InvalidateUnder( rBindings ); } break; case SID_ATTR_CHAR_OVERLINE: { FontLineStyle eOld = pEditView->GetAttribs().Get(EE_CHAR_OVERLINE).GetLineStyle(); FontLineStyle eNew = ( eOld != LINESTYLE_NONE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE; aSet.Put( SvxOverlineItem( eNew, EE_CHAR_OVERLINE ) ); rBindings.Invalidate( nSlot ); } break; case SID_ATTR_CHAR_STRIKEOUT: { bool bOld = pEditView->GetAttribs().Get(EE_CHAR_STRIKEOUT).GetValue() != STRIKEOUT_NONE; aSet.Put( SvxCrossedOutItem( bOld ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, EE_CHAR_STRIKEOUT ) ); rBindings.Invalidate( nSlot ); } break; case SID_ATTR_CHAR_SHADOWED: { bool bOld = pEditView->GetAttribs().Get(EE_CHAR_SHADOW).GetValue(); aSet.Put( SvxShadowedItem( !bOld, EE_CHAR_SHADOW ) ); rBindings.Invalidate( nSlot ); } break; case SID_ATTR_CHAR_CONTOUR: { bool bOld = pEditView->GetAttribs().Get(EE_CHAR_OUTLINE).GetValue(); aSet.Put( SvxContourItem( !bOld, EE_CHAR_OUTLINE ) ); rBindings.Invalidate( nSlot ); } break; case SID_SET_SUPER_SCRIPT: { SvxEscapement eOld = static_cast(pEditView->GetAttribs().Get(EE_CHAR_ESCAPEMENT).GetEnumValue()); SvxEscapement eNew = (eOld == SvxEscapement::Superscript) ? SvxEscapement::Off : SvxEscapement::Superscript; aSet.Put( SvxEscapementItem( eNew, EE_CHAR_ESCAPEMENT ) ); rBindings.Invalidate( nSlot ); } break; case SID_SET_SUB_SCRIPT: { SvxEscapement eOld = static_cast(pEditView->GetAttribs().Get(EE_CHAR_ESCAPEMENT).GetEnumValue()); SvxEscapement eNew = (eOld == SvxEscapement::Subscript) ? SvxEscapement::Off : SvxEscapement::Subscript; aSet.Put( SvxEscapementItem( eNew, EE_CHAR_ESCAPEMENT ) ); rBindings.Invalidate( nSlot ); } break; case SID_ATTR_CHAR_KERNING: { if(pArgs) { aSet.Put ( pArgs->Get(pArgs->GetPool()->GetWhich(nSlot))); rBindings.Invalidate( nSlot ); } } break; case SID_GROW_FONT_SIZE: case SID_SHRINK_FONT_SIZE: { SfxObjectShell* pObjSh = SfxObjectShell::Current(); const SvxFontListItem* pFontListItem = static_cast (pObjSh ? pObjSh->GetItem(SID_ATTR_CHAR_FONTLIST) : nullptr); const FontList* pFontList = pFontListItem ? pFontListItem->GetFontList() : nullptr; pEditView->ChangeFontSize( nSlot == SID_GROW_FONT_SIZE, pFontList ); rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); } break; } // apply EditEngine* pEngine = pEditView->GetEditEngine(); bool bOld = pEngine->SetUpdateLayout(false); pEditView->SetAttribs( aSet ); pEngine->SetUpdateLayout(bOld); pEditView->Invalidate(); ScInputHandler* pHdl = GetMyInputHdl(); pHdl->SetModified(); rReq.Done(); } void ScEditShell::GetAttrState(SfxItemSet &rSet) { if ( !rViewData.HasEditView( rViewData.GetActivePart() ) ) { lcl_DisableAll( rSet ); return; } SfxItemSet aAttribs = pEditView->GetAttribs(); rSet.Put( aAttribs ); // choose font info according to selection script type SvtScriptType nScript = pEditView->GetSelectedScriptType(); if (nScript == SvtScriptType::NONE) nScript = ScGlobal::GetDefaultScriptType(); // #i55929# input-language-dependent script type (depends on input language if nothing selected) SvtScriptType nInputScript = nScript; if ( !pEditView->GetSelection().HasRange() ) { LanguageType nInputLang = rViewData.GetActiveWin()->GetInputLanguage(); if (nInputLang != LANGUAGE_DONTKNOW && nInputLang != LANGUAGE_SYSTEM) nInputScript = SvtLanguageOptions::GetScriptTypeOfLanguage( nInputLang ); } // #i55929# according to spec, nInputScript is used for font and font height only if ( rSet.GetItemState( EE_CHAR_FONTINFO ) != SfxItemState::UNKNOWN ) ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_FONTINFO, nInputScript ); if ( rSet.GetItemState( EE_CHAR_FONTHEIGHT ) != SfxItemState::UNKNOWN ) ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_FONTHEIGHT, nInputScript ); if ( rSet.GetItemState( EE_CHAR_WEIGHT ) != SfxItemState::UNKNOWN ) ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_WEIGHT, nScript ); if ( rSet.GetItemState( EE_CHAR_ITALIC ) != SfxItemState::UNKNOWN ) ScViewUtil::PutItemScript( rSet, aAttribs, EE_CHAR_ITALIC, nScript ); // underline SfxItemState eState = aAttribs.GetItemState( EE_CHAR_UNDERLINE ); if ( eState == SfxItemState::DONTCARE ) { rSet.InvalidateItem( SID_ULINE_VAL_NONE ); rSet.InvalidateItem( SID_ULINE_VAL_SINGLE ); rSet.InvalidateItem( SID_ULINE_VAL_DOUBLE ); rSet.InvalidateItem( SID_ULINE_VAL_DOTTED ); } else { FontLineStyle eUnderline = aAttribs.Get(EE_CHAR_UNDERLINE).GetLineStyle(); rSet.Put(SfxBoolItem(SID_ULINE_VAL_SINGLE, eUnderline == LINESTYLE_SINGLE)); rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOUBLE, eUnderline == LINESTYLE_DOUBLE)); rSet.Put(SfxBoolItem(SID_ULINE_VAL_DOTTED, eUnderline == LINESTYLE_DOTTED)); rSet.Put(SfxBoolItem(SID_ULINE_VAL_NONE, eUnderline == LINESTYLE_NONE)); } //! Testing whether brace highlighting is active !!!! ScInputHandler* pHdl = GetMyInputHdl(); if ( pHdl && pHdl->IsFormulaMode() ) rSet.ClearItem( EE_CHAR_WEIGHT ); // Highlighted brace not here SvxEscapement eEsc = static_cast(aAttribs.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); rSet.Put(SfxBoolItem(SID_SET_SUPER_SCRIPT, eEsc == SvxEscapement::Superscript)); rSet.Put(SfxBoolItem(SID_SET_SUB_SCRIPT, eEsc == SvxEscapement::Subscript)); rViewData.GetBindings().Invalidate( SID_SET_SUPER_SCRIPT ); rViewData.GetBindings().Invalidate( SID_SET_SUB_SCRIPT ); eState = aAttribs.GetItemState( EE_CHAR_KERNING ); rViewData.GetBindings().Invalidate( SID_ATTR_CHAR_KERNING ); if ( eState == SfxItemState::DONTCARE ) { rSet.InvalidateItem(EE_CHAR_KERNING); } } OUString ScEditShell::GetSelectionText( bool bWholeWord ) { OUString aStrSelection; if ( rViewData.HasEditView( rViewData.GetActivePart() ) ) { if ( bWholeWord ) { EditEngine* pEngine = pEditView->GetEditEngine(); ESelection aSel = pEditView->GetSelection(); OUString aStrCurrentDelimiters = pEngine->GetWordDelimiters(); pEngine->SetWordDelimiters(" .,;\"'"); aStrSelection = pEngine->GetWord( aSel.nEndPara, aSel.nEndPos ); pEngine->SetWordDelimiters( aStrCurrentDelimiters ); } else { aStrSelection = pEditView->GetSelected(); } } return aStrSelection; } void ScEditShell::ExecuteUndo(const SfxRequest& rReq) { // Undo must be handled here because it's called for both EditViews ScInputHandler* pHdl = GetMyInputHdl(); OSL_ENSURE(pHdl,"no ScInputHandler"); EditView* pTopView = pHdl->GetTopView(); EditView* pTableView = pHdl->GetTableView(); OSL_ENSURE(pTableView,"no EditView"); pHdl->DataChanging(); const SfxItemSet* pReqArgs = rReq.GetArgs(); sal_uInt16 nSlot = rReq.GetSlot(); switch ( nSlot ) { case SID_UNDO: case SID_REDO: { bool bIsUndo = ( nSlot == SID_UNDO ); sal_uInt16 nCount = 1; const SfxPoolItem* pItem; if ( pReqArgs && pReqArgs->GetItemState( nSlot, true, &pItem ) == SfxItemState::SET ) nCount = static_cast(pItem)->GetValue(); for (sal_uInt16 i=0; iUndo(); if (pTopView) pTopView->Undo(); } else { pTableView->Redo(); if (pTopView) pTopView->Redo(); } } } break; } rViewData.GetBindings().InvalidateAll(false); pHdl->DataChanged(); } void ScEditShell::GetUndoState(SfxItemSet &rSet) { // Undo state is taken from normal ViewFrame state function SfxViewFrame& rViewFrm = rViewData.GetViewShell()->GetViewFrame(); if ( GetUndoManager() ) { SfxWhichIter aIter(rSet); sal_uInt16 nWhich = aIter.FirstWhich(); while( nWhich ) { rViewFrm.GetSlotState( nWhich, nullptr, &rSet ); nWhich = aIter.NextWhich(); } } // disable if no action in input line EditView ScInputHandler* pHdl = GetMyInputHdl(); OSL_ENSURE(pHdl,"no ScInputHandler"); EditView* pTopView = pHdl->GetTopView(); if (pTopView) { SfxUndoManager& rTopMgr = pTopView->GetEditEngine()->GetUndoManager(); if ( rTopMgr.GetUndoActionCount() == 0 ) rSet.DisableItem( SID_UNDO ); if ( rTopMgr.GetRedoActionCount() == 0 ) rSet.DisableItem( SID_REDO ); } } void ScEditShell::ExecuteTrans( const SfxRequest& rReq ) { TransliterationFlags nType = ScViewUtil::GetTransliterationType( rReq.GetSlot() ); if ( nType == TransliterationFlags::NONE ) return; ScInputHandler* pHdl = GetMyInputHdl(); assert(pHdl && "no ScInputHandler"); EditView* pTopView = pHdl->GetTopView(); EditView* pTableView = pHdl->GetTableView(); assert(pTableView && "no EditView"); pHdl->DataChanging(); pTableView->TransliterateText( nType ); if (pTopView) pTopView->TransliterateText( nType ); pHdl->DataChanged(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */