diff options
Diffstat (limited to 'sw/source/uibase/shells/langhelper.cxx')
-rw-r--r-- | sw/source/uibase/shells/langhelper.cxx | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/sw/source/uibase/shells/langhelper.cxx b/sw/source/uibase/shells/langhelper.cxx new file mode 100644 index 000000000..0de6c4863 --- /dev/null +++ b/sw/source/uibase/shells/langhelper.cxx @@ -0,0 +1,574 @@ +/* -*- 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 <string.h> + +#include <wrtsh.hxx> +#include <doc.hxx> +#include <docary.hxx> +#include <charfmt.hxx> + +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxdlg.hxx> +#include <sfx2/viewfrm.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editeng.hxx> +#include <editeng/editdata.hxx> +#include <editeng/outliner.hxx> +#include <editeng/editview.hxx> +#include <editeng/langitem.hxx> + +#include <svl/languageoptions.hxx> +#include <svtools/langtab.hxx> +#include <svl/slstitm.hxx> +#include <svl/stritem.hxx> +#include <svx/svxids.hrc> +#include <osl/diagnose.h> + +#include <ndtxt.hxx> +#include <pam.hxx> +#include <view.hxx> +#include <viewopt.hxx> + +#include <langhelper.hxx> + +using namespace ::com::sun::star; + +namespace SwLangHelper +{ + + void GetLanguageStatus( OutlinerView* pOLV, SfxItemSet& rSet ) + { + ESelection aSelection = pOLV->GetSelection(); + EditView& rEditView=pOLV->GetEditView(); + EditEngine* pEditEngine=rEditView.GetEditEngine(); + + // the value of used script types + const SvtScriptType nScriptType =pOLV->GetSelectedScriptType(); + OUString aScriptTypesInUse( OUString::number( static_cast<int>(nScriptType) ) );//pEditEngine->GetScriptType(aSelection) + + // get keyboard language + OUString aKeyboardLang; + LanguageType nLang = rEditView.GetInputLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aKeyboardLang = SvtLanguageTable::GetLanguageString( nLang ); + + // get the language that is in use + OUString aCurrentLang("*"); + SfxItemSet aSet(pOLV->GetAttribs()); + nLang = SwLangHelper::GetCurrentLanguage( aSet,nScriptType ); + if (nLang != LANGUAGE_DONTKNOW) + aCurrentLang = SvtLanguageTable::GetLanguageString( nLang ); + + // build sequence for status value + uno::Sequence< OUString > aSeq{ aCurrentLang, + aScriptTypesInUse, + aKeyboardLang, + SwLangHelper::GetTextForLanguageGuessing( pEditEngine, + aSelection ) }; + + // set sequence as status value + SfxStringListItem aItem( SID_LANGUAGE_STATUS ); + aItem.SetStringList( aSeq ); + rSet.Put( aItem ); + } + + bool SetLanguageStatus( OutlinerView* pOLV, SfxRequest &rReq, SwView const &rView, SwWrtShell &rSh ) + { + bool bRestoreSelection = false; + SfxItemSet aEditAttr(pOLV->GetAttribs()); + ESelection aSelection = pOLV->GetSelection(); + EditView & rEditView = pOLV->GetEditView(); + EditEngine * pEditEngine = rEditView.GetEditEngine(); + + // get the language + OUString aNewLangText; + + const SfxStringItem* pItem = rReq.GetArg<SfxStringItem>(SID_LANGUAGE_STATUS); + if (pItem) + aNewLangText = pItem->GetValue(); + + //!! Remember the view frame right now... + //!! (call to GetView().GetViewFrame() will break if the + //!! SwTextShell got destroyed meanwhile.) + SfxViewFrame *pViewFrame = rView.GetViewFrame(); + + if (aNewLangText == "*" ) + { + // open the dialog "Tools/Options/Language Settings - Language" + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateVclDialog( rView.GetFrameWeld(), SID_LANGUAGE_OPTIONS )); + pDlg->Execute(); + } + else + { + // setting the new language... + if (!aNewLangText.isEmpty()) + { + static const OUStringLiteral aSelectionLangPrefix(u"Current_"); + static const OUStringLiteral aParagraphLangPrefix(u"Paragraph_"); + static const OUStringLiteral aDocumentLangPrefix(u"Default_"); + + sal_Int32 nPos = 0; + bool bForSelection = true; + bool bForParagraph = false; + if (-1 != (nPos = aNewLangText.indexOf( aSelectionLangPrefix ))) + { + // ... for the current selection + aNewLangText = aNewLangText.replaceAt(nPos, aSelectionLangPrefix.getLength(), u""); + bForSelection = true; + } + else if (-1 != (nPos = aNewLangText.indexOf( aParagraphLangPrefix ))) + { + // ... for the current paragraph language + aNewLangText = aNewLangText.replaceAt(nPos, aParagraphLangPrefix.getLength(), u""); + bForSelection = true; + bForParagraph = true; + } + else if (-1 != (nPos = aNewLangText.indexOf( aDocumentLangPrefix ))) + { + // ... as default document language + aNewLangText = aNewLangText.replaceAt(nPos, aDocumentLangPrefix.getLength(), u""); + bForSelection = false; + } + + if (bForParagraph) + { + bRestoreSelection = true; + SwLangHelper::SelectPara( rEditView, aSelection ); + aSelection = pOLV->GetSelection(); + } + if (!bForSelection) // document language to be changed... + { + rSh.StartAction(); + rSh.LockView( true ); + rSh.Push(); + + // prepare to apply new language to all text in document + rSh.SelAll(); + rSh.ExtendedSelectAll(); + } + + if (aNewLangText == "LANGUAGE_NONE") + SwLangHelper::SetLanguage_None( rSh, pOLV, aSelection, bForSelection, aEditAttr ); + else if (aNewLangText == "RESET_LANGUAGES") + SwLangHelper::ResetLanguages( rSh, pOLV ); + else + SwLangHelper::SetLanguage( rSh, pOLV, aSelection, aNewLangText, bForSelection, aEditAttr ); + + // ugly hack, as it seems that EditView/EditEngine does not update their spellchecking marks + // when setting a new language attribute + if (bForSelection) + { + const SwViewOption* pVOpt = rView.GetWrtShellPtr()->GetViewOptions(); + EEControlBits nCntrl = pEditEngine->GetControlWord(); + // turn off + nCntrl &= ~EEControlBits::ONLINESPELLING; + pEditEngine->SetControlWord(nCntrl); + + //turn back on + if (pVOpt->IsOnlineSpell()) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + pEditEngine->SetControlWord(nCntrl); + + pEditEngine->CompleteOnlineSpelling(); + rEditView.Invalidate(); + } + + if (!bForSelection) + { + // need to release view and restore selection... + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + rSh.LockView( false ); + rSh.EndAction(); + } + } + } + + // invalidate slot to get the new language displayed + pViewFrame->GetBindings().Invalidate( rReq.GetSlot() ); + + rReq.Done(); + return bRestoreSelection; + } + + void SetLanguage( SwWrtShell &rWrtSh, std::u16string_view rLangText, bool bIsForSelection, SfxItemSet &rCoreSet ) + { + SetLanguage( rWrtSh, nullptr , ESelection(), rLangText, bIsForSelection, rCoreSet ); + } + + void SetLanguage( SwWrtShell &rWrtSh, OutlinerView const * pOLV, const ESelection& rSelection, std::u16string_view rLangText, bool bIsForSelection, SfxItemSet &rCoreSet ) + { + const LanguageType nLang = SvtLanguageTable::GetLanguageType( rLangText ); + if (nLang == LANGUAGE_DONTKNOW) + return; + + EditEngine* pEditEngine = pOLV ? pOLV->GetEditView().GetEditEngine() : nullptr; + OSL_ENSURE( !pOLV || pEditEngine, "OutlinerView without EditEngine???" ); + + //get ScriptType + sal_uInt16 nLangWhichId = 0; + bool bIsSingleScriptType = true; + switch (SvtLanguageOptions::GetScriptTypeOfLanguage( nLang )) + { + case SvtScriptType::LATIN : nLangWhichId = pEditEngine ? sal_uInt16(EE_CHAR_LANGUAGE) : sal_uInt16(RES_CHRATR_LANGUAGE); break; + case SvtScriptType::ASIAN : nLangWhichId = pEditEngine ? sal_uInt16(EE_CHAR_LANGUAGE_CJK) : sal_uInt16(RES_CHRATR_CJK_LANGUAGE); break; + case SvtScriptType::COMPLEX : nLangWhichId = pEditEngine ? sal_uInt16(EE_CHAR_LANGUAGE_CTL) : sal_uInt16(RES_CHRATR_CTL_LANGUAGE); break; + default: + bIsSingleScriptType = false; + OSL_FAIL("unexpected case" ); + } + if (!bIsSingleScriptType) + return; + + // change language for selection or paragraph + // (for paragraph is handled by previously having set the selection to the + // whole paragraph) + if (bIsForSelection) + { + // apply language to current selection + if (pEditEngine) + { + rCoreSet.Put( SvxLanguageItem( nLang, nLangWhichId )); + pEditEngine->QuickSetAttribs(rCoreSet, rSelection); + } + else + { + rWrtSh.GetCurAttr( rCoreSet ); + rCoreSet.Put( SvxLanguageItem( nLang, nLangWhichId )); + rWrtSh.SetAttrSet( rCoreSet ); + } + } + else // change language for all text + { + // set document default language + switch (nLangWhichId) + { + case EE_CHAR_LANGUAGE : nLangWhichId = RES_CHRATR_LANGUAGE; break; + case EE_CHAR_LANGUAGE_CJK : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break; + case EE_CHAR_LANGUAGE_CTL : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break; + } + //Set the default document language + rWrtSh.SetDefault( SvxLanguageItem( nLang, nLangWhichId ) ); + + //Resolves: fdo#35282 Clear the language from all Text Styles, and + //fallback to default document language + const SwTextFormatColls *pColls = rWrtSh.GetDoc()->GetTextFormatColls(); + for(size_t i = 0, nCount = pColls->size(); i < nCount; ++i) + { + SwTextFormatColl &rTextColl = *(*pColls)[ i ]; + rTextColl.ResetFormatAttr(nLangWhichId); + } + //Resolves: fdo#35282 Clear the language from all Character Styles, + //and fallback to default document language + const SwCharFormats *pCharFormats = rWrtSh.GetDoc()->GetCharFormats(); + for(size_t i = 0, nCount = pCharFormats->size(); i < nCount; ++i) + { + SwCharFormat &rCharFormat = *(*pCharFormats)[ i ]; + rCharFormat.ResetFormatAttr(nLangWhichId); + } + + // #i102191: hard set respective language attribute in text document + // (for all text in the document - which should be selected by now...) + rWrtSh.SetAttrItem( SvxLanguageItem( nLang, nLangWhichId ) ); + } + } + + void SetLanguage_None( SwWrtShell &rWrtSh, bool bIsForSelection, SfxItemSet &rCoreSet ) + { + SetLanguage_None( rWrtSh,nullptr,ESelection(),bIsForSelection,rCoreSet ); + } + + void SetLanguage_None( SwWrtShell &rWrtSh, OutlinerView const * pOLV, const ESelection& rSelection, bool bIsForSelection, SfxItemSet &rCoreSet ) + { + // EditEngine IDs + const sal_uInt16 aLangWhichId_EE[3] = + { + EE_CHAR_LANGUAGE, + EE_CHAR_LANGUAGE_CJK, + EE_CHAR_LANGUAGE_CTL + }; + + // Writer IDs + const sal_uInt16 aLangWhichId_Writer[3] = + { + RES_CHRATR_LANGUAGE, + RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_LANGUAGE + }; + + if (bIsForSelection) + { + // change language for selection or paragraph + // (for paragraph is handled by previously having set the selection to the + // whole paragraph) + + EditEngine* pEditEngine = pOLV ? pOLV->GetEditView().GetEditEngine() : nullptr; + OSL_ENSURE( !pOLV || pEditEngine, "OutlinerView without EditEngine???" ); + if (pEditEngine) + { + for (sal_uInt16 i : aLangWhichId_EE) + rCoreSet.Put( SvxLanguageItem( LANGUAGE_NONE, i )); + pEditEngine->QuickSetAttribs(rCoreSet, rSelection); + } + else + { + rWrtSh.GetCurAttr( rCoreSet ); + for (sal_uInt16 i : aLangWhichId_Writer) + rCoreSet.Put( SvxLanguageItem( LANGUAGE_NONE, i )); + rWrtSh.SetAttrSet( rCoreSet ); + } + } + else // change language for all text + { + o3tl::sorted_vector<sal_uInt16> aAttribs; + for (sal_uInt16 i : aLangWhichId_Writer) + { + rWrtSh.SetDefault( SvxLanguageItem( LANGUAGE_NONE, i ) ); + aAttribs.insert( i ); + } + + // set all language attributes to default + // (for all text in the document - which should be selected by now...) + rWrtSh.ResetAttr( aAttribs ); + } + } + + void ResetLanguages( SwWrtShell &rWrtSh, OutlinerView const * pOLV ) + { + // reset language for current selection. + // The selection should already have been expanded to the whole paragraph or + // to all text in the document if those are the ranges where to reset + // the language attributes + + if (pOLV) + { + EditView &rEditView = pOLV->GetEditView(); + rEditView.RemoveAttribs( true, EE_CHAR_LANGUAGE ); + rEditView.RemoveAttribs( true, EE_CHAR_LANGUAGE_CJK ); + rEditView.RemoveAttribs( true, EE_CHAR_LANGUAGE_CTL ); + } + else + { + rWrtSh.ResetAttr( + { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE }); + } + } + + /// @returns : the language for the selected text that is set for the + /// specified attribute (script type). + /// If there are more than one languages used LANGUAGE_DONTKNOW will be returned. + /// @param nLangWhichId : one of + /// RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE, + LanguageType GetLanguage( SwWrtShell &rSh, sal_uInt16 nLangWhichId ) + { + SfxItemSet aSet( rSh.GetAttrPool(), nLangWhichId, nLangWhichId ); + rSh.GetCurAttr( aSet ); + + return GetLanguage(aSet,nLangWhichId); + } + + LanguageType GetLanguage( SfxItemSet const & aSet, sal_uInt16 nLangWhichId ) + { + + LanguageType nLang = LANGUAGE_SYSTEM; + + const SfxPoolItem *pItem = nullptr; + SfxItemState nState = aSet.GetItemState( nLangWhichId, true, &pItem ); + if (nState > SfxItemState::DEFAULT && pItem) + { + // the item is set and can be used + nLang = dynamic_cast<const SvxLanguageItem&>(*pItem).GetLanguage(); + } + else if (nState == SfxItemState::DEFAULT) + { + // since the attribute is not set: retrieve the default value + nLang = dynamic_cast<const SvxLanguageItem&>(aSet.GetPool()->GetDefaultItem( nLangWhichId )).GetLanguage(); + } + else if (nState == SfxItemState::DONTCARE) + { + // there is more than one language... + nLang = LANGUAGE_DONTKNOW; + } + OSL_ENSURE( nLang != LANGUAGE_SYSTEM, "failed to get the language?" ); + + return nLang; + } + + /// @returns: the language in use for the selected text. + /// 'In use' means the language(s) matching the script type(s) of the + /// selected text. Or in other words, the language a spell checker would use. + /// If there is more than one language LANGUAGE_DONTKNOW will be returned. + LanguageType GetCurrentLanguage( SwWrtShell &rSh ) + { + //set language attribute to use according to the script type + sal_uInt16 nLangWhichId = 0; + bool bIsSingleScriptType = true; + switch (rSh.GetScriptType()) + { + case SvtScriptType::LATIN : nLangWhichId = RES_CHRATR_LANGUAGE; break; + case SvtScriptType::ASIAN : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break; + case SvtScriptType::COMPLEX : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break; + default: bIsSingleScriptType = false; break; + } + + // get language according to the script type(s) in use + LanguageType nCurrentLang = LANGUAGE_SYSTEM; + if (bIsSingleScriptType) + nCurrentLang = GetLanguage( rSh, nLangWhichId ); + else + { + // check if all script types are set to LANGUAGE_NONE and return + // that if this is the case. Otherwise, having multiple script types + // in use always means there are several languages in use... + const sal_uInt16 aScriptTypes[3] = + { + RES_CHRATR_LANGUAGE, + RES_CHRATR_CJK_LANGUAGE, + RES_CHRATR_CTL_LANGUAGE + }; + nCurrentLang = LANGUAGE_NONE; + for (sal_uInt16 aScriptType : aScriptTypes) + { + LanguageType nTmpLang = GetLanguage( rSh, aScriptType ); + if (nTmpLang != LANGUAGE_NONE) + { + nCurrentLang = LANGUAGE_DONTKNOW; + break; + } + } + } + OSL_ENSURE( nCurrentLang != LANGUAGE_SYSTEM, "failed to get the language?" ); + + return nCurrentLang; + } + + /// @returns: the language in use for the selected text. + /// 'In use' means the language(s) matching the script type(s) of the + /// selected text. Or in other words, the language a spell checker would use. + /// If there is more than one language LANGUAGE_DONTKNOW will be returned. + LanguageType GetCurrentLanguage( SfxItemSet const & aSet, SvtScriptType nScriptType ) + { + //set language attribute to use according to the script type + sal_uInt16 nLangWhichId = 0; + bool bIsSingleScriptType = true; + switch (nScriptType) + { + case SvtScriptType::LATIN : nLangWhichId = EE_CHAR_LANGUAGE; break; + case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break; + case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break; + default: bIsSingleScriptType = false; + } + + // get language according to the script type(s) in use + LanguageType nCurrentLang = LANGUAGE_SYSTEM; + if (bIsSingleScriptType) + nCurrentLang = GetLanguage( aSet, nLangWhichId ); + else + { + // check if all script types are set to LANGUAGE_NONE and return + // that if this is the case. Otherwise, having multiple script types + // in use always means there are several languages in use... + const sal_uInt16 aScriptTypes[3] = + { + EE_CHAR_LANGUAGE, + EE_CHAR_LANGUAGE_CJK, + EE_CHAR_LANGUAGE_CTL + }; + nCurrentLang = LANGUAGE_NONE; + for (sal_uInt16 aScriptType : aScriptTypes) + { + LanguageType nTmpLang = GetLanguage( aSet, aScriptType ); + if (nTmpLang != LANGUAGE_NONE) + { + nCurrentLang = LANGUAGE_DONTKNOW; + break; + } + } + } + OSL_ENSURE( nCurrentLang != LANGUAGE_SYSTEM, "failed to get the language?" ); + + return nCurrentLang; + } + + OUString GetTextForLanguageGuessing( SwWrtShell const &rSh ) + { + // string for guessing language + OUString aText; + SwPaM *pCursor = rSh.GetCursor(); + SwTextNode *pNode = pCursor->GetNode().GetTextNode(); + if (pNode) + { + aText = pNode->GetText(); + if (!aText.isEmpty()) + { + sal_Int32 nEnd = pCursor->GetPoint()->nContent.GetIndex(); + // at most 100 chars to the left... + const sal_Int32 nStt = nEnd > 100 ? nEnd - 100 : 0; + // ... and 100 to the right of the cursor position + nEnd = aText.getLength() - nEnd > 100 ? nEnd + 100 : aText.getLength(); + aText = aText.copy( nStt, nEnd - nStt ); + } + } + return aText; + } + + OUString GetTextForLanguageGuessing(EditEngine const * rEditEngine, const ESelection& rDocSelection) + { + // string for guessing language + + // get the full text of the paragraph that the end of selection is in + OUString aText = rEditEngine->GetText(rDocSelection.nEndPos); + if (!aText.isEmpty()) + { + sal_Int32 nStt = 0; + sal_Int32 nEnd = rDocSelection.nEndPos; + // at most 100 chars to the left... + nStt = nEnd > 100 ? nEnd - 100 : 0; + // ... and 100 to the right of the cursor position + nEnd = aText.getLength() - nEnd > 100 ? nEnd + 100 : aText.getLength(); + aText = aText.copy( nStt, nEnd - nStt ); + } + + return aText; + } + + void SelectPara( EditView &rEditView, const ESelection &rCurSel ) + { + ESelection aParaSel( rCurSel.nStartPara, 0, rCurSel.nStartPara, EE_TEXTPOS_ALL ); + rEditView.SetSelection( aParaSel ); + } + + void SelectCurrentPara( SwWrtShell &rWrtSh ) + { + // select current para + if (!rWrtSh.IsSttPara()) + rWrtSh.MovePara( GoCurrPara, fnParaStart ); + if (!rWrtSh.HasMark()) + rWrtSh.SetMark(); + rWrtSh.SwapPam(); + if (!rWrtSh.IsEndPara()) + rWrtSh.MovePara( GoCurrPara, fnParaEnd ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |