diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /sw/source/uibase/lingu | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/uibase/lingu')
-rw-r--r-- | sw/source/uibase/lingu/hhcwrp.cxx | 680 | ||||
-rw-r--r-- | sw/source/uibase/lingu/hyp.cxx | 124 | ||||
-rw-r--r-- | sw/source/uibase/lingu/olmenu.cxx | 885 | ||||
-rw-r--r-- | sw/source/uibase/lingu/sdrhhcwrap.cxx | 165 | ||||
-rw-r--r-- | sw/source/uibase/lingu/sdrhhcwrap.hxx | 53 |
5 files changed, 1907 insertions, 0 deletions
diff --git a/sw/source/uibase/lingu/hhcwrp.cxx b/sw/source/uibase/lingu/hhcwrp.cxx new file mode 100644 index 0000000000..813f9d043c --- /dev/null +++ b/sw/source/uibase/lingu/hhcwrp.cxx @@ -0,0 +1,680 @@ +/* -*- 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 <hintids.hxx> +#include <view.hxx> +#include <wrtsh.hxx> +#include <swundo.hxx> +#include <splargs.hxx> + +#include <editeng/langitem.hxx> +#include <editeng/fontitem.hxx> +#include <rtl/ustring.hxx> +#include <com/sun/star/text/RubyAdjust.hpp> +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <osl/diagnose.h> +#include <hhcwrp.hxx> +#include "sdrhhcwrap.hxx" +#include <doc.hxx> +#include <docsh.hxx> +#include <mdiexp.hxx> +#include <edtwin.hxx> +#include <contentindex.hxx> +#include <pam.hxx> +#include <swcrsr.hxx> +#include <ndtxt.hxx> +#include <fmtruby.hxx> +#include <breakit.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::linguistic2; +using namespace ::com::sun::star::i18n; + +// Description: Turn off frame/object shell if applicable + +static void lcl_ActivateTextShell( SwWrtShell & rWrtSh ) +{ + if( rWrtSh.IsSelFrameMode() || rWrtSh.IsObjSelected() ) + rWrtSh.EnterStdMode(); +} + +namespace { + +class SwKeepConversionDirectionStateContext +{ +public: + SwKeepConversionDirectionStateContext() + { + //!! hack to transport the current conversion direction state settings + //!! into the next incarnation that iterates over the drawing objects + //!! ( see SwHHCWrapper::~SwHHCWrapper() ) + editeng::HangulHanjaConversion::SetUseSavedConversionDirectionState( true ); + } + + ~SwKeepConversionDirectionStateContext() + { + editeng::HangulHanjaConversion::SetUseSavedConversionDirectionState( false ); + } +}; + +} + +SwHHCWrapper::SwHHCWrapper( + SwView* pSwView, + const uno::Reference< uno::XComponentContext >& rxContext, + LanguageType nSourceLanguage, + LanguageType nTargetLanguage, + const vcl::Font *pTargetFont, + sal_Int32 nConvOptions, + bool bIsInteractive, + bool bStart, bool bOther, bool bSelection ) + : editeng::HangulHanjaConversion(pSwView->GetEditWin().GetFrameWeld(), rxContext, + LanguageTag::convertToLocale( nSourceLanguage ), + LanguageTag::convertToLocale( nTargetLanguage ), + pTargetFont, + nConvOptions, + bIsInteractive ) + , m_pView( pSwView ) + , m_rWrtShell( pSwView->GetWrtShell() ) + , m_nLastPos( 0 ) + , m_nUnitOffset( 0 ) + , m_nPageCount( 0 ) + , m_nPageStart( 0 ) + , m_bIsDrawObj( false ) + , m_bIsOtherContent( bOther ) + , m_bStartChk( bOther ) + , m_bIsSelection( bSelection ) + , m_bStartDone( bOther || bStart ) + , m_bEndDone( false ) +{ +} + +SwHHCWrapper::~SwHHCWrapper() COVERITY_NOEXCEPT_FALSE +{ + m_pConvArgs.reset(); + + SwViewShell::SetCareDialog(nullptr); + + // check for existence of a draw view which means that there are + // (or previously were) draw objects present in the document. + // I.e. we like to check those too. + if ( m_bIsDrawObj /*&& bLastRet*/ && m_pView->GetWrtShell().HasDrawView() ) + { + vcl::Cursor *pSave = m_pView->GetWindow()->GetCursor(); + { + SwKeepConversionDirectionStateContext aContext; + + SdrHHCWrapper aSdrConvWrap( m_pView, GetSourceLanguage(), + GetTargetLanguage(), GetTargetFont(), + GetConversionOptions(), IsInteractive() ); + aSdrConvWrap.StartTextConversion(); + } + m_pView->GetWindow()->SetCursor( pSave ); + } + + if( m_nPageCount ) + ::EndProgress( m_pView->GetDocShell() ); + + // finally for chinese translation we need to change the documents + // default language and font to the new ones to be used. + LanguageType nTargetLang = GetTargetLanguage(); + if (!IsChinese( nTargetLang )) + return; + + SwDoc *pDoc = m_pView->GetDocShell()->GetDoc(); + + //!! Note: This also effects the default language of text boxes (EditEngine/EditView) !! + pDoc->SetDefault( SvxLanguageItem( nTargetLang, RES_CHRATR_CJK_LANGUAGE ) ); + + const vcl::Font *pFont = GetTargetFont(); + if (pFont) + { + SvxFontItem aFontItem( pFont->GetFamilyType(), pFont->GetFamilyName(), + pFont->GetStyleName(), pFont->GetPitch(), + pFont->GetCharSet(), RES_CHRATR_CJK_FONT ); + pDoc->SetDefault( aFontItem ); + } +} + +void SwHHCWrapper::GetNextPortion( + OUString& rNextPortion, + LanguageType& rLangOfPortion, + bool bAllowChanges ) +{ + m_pConvArgs->bAllowImplicitChangesForNotConvertibleText = bAllowChanges; + + FindConvText_impl(); + rNextPortion = m_pConvArgs->aConvText; + rLangOfPortion = m_pConvArgs->nConvTextLang; + + m_nUnitOffset = 0; + + // build last pos from currently selected text + SwPaM* pCursor = m_rWrtShell.GetCursor(); + m_nLastPos = pCursor->Start()->GetContentIndex(); +} + +void SwHHCWrapper::SelectNewUnit_impl( sal_Int32 nUnitStart, sal_Int32 nUnitEnd ) +{ + SwPaM *pCursor = m_rWrtShell.GetCursor(); + pCursor->GetPoint()->SetContent( m_nLastPos ); + pCursor->DeleteMark(); + + m_rWrtShell.Right( SwCursorSkipMode::Chars, /*bExpand*/ false, + o3tl::narrowing<sal_uInt16>(m_nUnitOffset + nUnitStart), true ); + pCursor->SetMark(); + m_rWrtShell.Right( SwCursorSkipMode::Chars, /*bExpand*/ true, + o3tl::narrowing<sal_uInt16>(nUnitEnd - nUnitStart), true ); + // end selection now. Otherwise SHIFT+HOME (extending the selection) + // won't work when the dialog is closed without any replacement. + // (see #116346#) + m_rWrtShell.EndSelect(); +} + +void SwHHCWrapper::HandleNewUnit( + const sal_Int32 nUnitStart, const sal_Int32 nUnitEnd ) +{ + OSL_ENSURE( nUnitStart >= 0 && nUnitEnd >= nUnitStart, "wrong arguments" ); + if (0 > nUnitStart || nUnitStart > nUnitEnd) + return; + + lcl_ActivateTextShell( m_rWrtShell ); + + m_rWrtShell.StartAllAction(); + + // select current unit + SelectNewUnit_impl( nUnitStart, nUnitEnd ); + + m_rWrtShell.EndAllAction(); +} + +void SwHHCWrapper::ChangeText( const OUString &rNewText, + std::u16string_view aOrigText, + const uno::Sequence< sal_Int32 > *pOffsets, + SwPaM *pCursor ) +{ + //!! please see also TextConvWrapper::ChangeText with is a modified + //!! copy of this code + + OSL_ENSURE( !rNewText.isEmpty(), "unexpected empty string" ); + if (rNewText.isEmpty()) + return; + + if (pOffsets && pCursor) // try to keep as much attributation as possible ? + { + // remember cursor start position for later setting of the cursor + const SwPosition *pStart = pCursor->Start(); + const sal_Int32 nStartIndex = pStart->GetContentIndex(); + SwTextNode *pStartTextNode = pStart->GetNode().GetTextNode(); + + const sal_Int32 nIndices = pOffsets->getLength(); + const sal_Int32 *pIndices = pOffsets->getConstArray(); + sal_Int32 nConvTextLen = rNewText.getLength(); + sal_Int32 nPos = 0; + sal_Int32 nChgPos = -1; + sal_Int32 nChgLen = 0; + sal_Int32 nConvChgPos = -1; + sal_Int32 nConvChgLen = 0; + + // offset to calculate the position in the text taking into + // account that text may have been replaced with new text of + // different length. Negative values allowed! + tools::Long nCorrectionOffset = 0; + + OSL_ENSURE(nIndices == 0 || nIndices == nConvTextLen, + "mismatch between string length and sequence length!" ); + + // find all substrings that need to be replaced (and only those) + while (true) + { + // get index in original text that matches nPos in new text + sal_Int32 nIndex; + if (nPos < nConvTextLen) + nIndex = nPos < nIndices ? pIndices[nPos] : nPos; + else + { + nPos = nConvTextLen; + nIndex = aOrigText.size(); + } + + if (nPos == nConvTextLen || /* end of string also terminates non-matching char sequence */ + aOrigText[nIndex] == rNewText[nPos]) + { + // substring that needs to be replaced found? + if (nChgPos != -1 && nConvChgPos != -1) + { + nChgLen = nIndex - nChgPos; + nConvChgLen = nPos - nConvChgPos; + OUString aInNew( rNewText.copy( nConvChgPos, nConvChgLen ) ); + + // set selection to sub string to be replaced in original text + sal_Int32 nChgInNodeStartIndex = nStartIndex + nCorrectionOffset + nChgPos; + OSL_ENSURE( m_rWrtShell.GetCursor()->HasMark(), "cursor misplaced (nothing selected)" ); + m_rWrtShell.GetCursor()->GetMark()->Assign( *pStartTextNode, nChgInNodeStartIndex ); + m_rWrtShell.GetCursor()->GetPoint()->Assign( *pStartTextNode, nChgInNodeStartIndex + nChgLen ); + + // replace selected sub string with the corresponding + // sub string from the new text while keeping as + // much from the attributes as possible + ChangeText_impl( aInNew, true ); + + nCorrectionOffset += nConvChgLen - nChgLen; + + nChgPos = -1; + nConvChgPos = -1; + } + } + else + { + // begin of non-matching char sequence found ? + if (nChgPos == -1 && nConvChgPos == -1) + { + nChgPos = nIndex; + nConvChgPos = nPos; + } + } + if (nPos >= nConvTextLen) + break; + ++nPos; + } + + // set cursor to the end of all the new text + // (as it would happen after ChangeText_impl (Delete and Insert) + // of the whole text in the 'else' branch below) + m_rWrtShell.ClearMark(); + m_rWrtShell.GetCursor()->Start()->Assign( *pStartTextNode, nStartIndex + nConvTextLen ); + } + else + { + ChangeText_impl( rNewText, false ); + } +} + +void SwHHCWrapper::ChangeText_impl( const OUString &rNewText, bool bKeepAttributes ) +{ + if (bKeepAttributes) + { + // get item set with all relevant attributes + SfxItemSetFixed<RES_CHRATR_BEGIN, RES_FRMATR_END> aItemSet( m_rWrtShell.GetAttrPool() ); + // get all attributes spanning the whole selection in order to + // restore those for the new text + m_rWrtShell.GetCurAttr( aItemSet ); + + m_rWrtShell.Delete(true); + m_rWrtShell.Insert( rNewText ); + + // select new inserted text (currently the Point is right after the new text) + if (!m_rWrtShell.GetCursor()->HasMark()) + m_rWrtShell.GetCursor()->SetMark(); + SwPosition *pMark = m_rWrtShell.GetCursor()->GetMark(); + pMark->SetContent( pMark->GetContentIndex() - rNewText.getLength() ); + + // since 'SetAttr' below functions like merging with the attributes + // from the itemset with any existing ones we have to get rid of all + // all attributes now. (Those attributes that may take effect left + // to the position where the new text gets inserted after the old text + // was deleted) + m_rWrtShell.ResetAttr(); + // apply previously saved attributes to new text + m_rWrtShell.SetAttrSet( aItemSet ); + } + else + { + m_rWrtShell.Delete(true); + m_rWrtShell.Insert( rNewText ); + } +} + +void SwHHCWrapper::ReplaceUnit( + const sal_Int32 nUnitStart, const sal_Int32 nUnitEnd, + const OUString& rOrigText, + const OUString& rReplaceWith, + const uno::Sequence< sal_Int32 > &rOffsets, + ReplacementAction eAction, + LanguageType *pNewUnitLanguage ) +{ + OSL_ENSURE( nUnitStart >= 0 && nUnitEnd >= nUnitStart, "wrong arguments" ); + if (nUnitStart < 0 || nUnitEnd < nUnitStart) + return; + + lcl_ActivateTextShell( m_rWrtShell ); + + // replace the current word + m_rWrtShell.StartAllAction(); + + // select current unit + SelectNewUnit_impl( nUnitStart, nUnitEnd ); + + OUString aOrigText( m_rWrtShell.GetSelText() ); + OUString aNewText( rReplaceWith ); + OSL_ENSURE( aOrigText == rOrigText, "!! text mismatch !!" ); + std::unique_ptr<SwFormatRuby> pRuby; + bool bRubyBelow = false; + OUString aNewOrigText; + switch (eAction) + { + case eExchange : + break; + case eReplacementBracketed : + { + aNewText = aOrigText + "(" + rReplaceWith + ")"; + } + break; + case eOriginalBracketed : + { + aNewText = rReplaceWith + "(" + aOrigText + ")"; + } + break; + case eReplacementAbove : + { + pRuby.reset(new SwFormatRuby( rReplaceWith )); + } + break; + case eOriginalAbove : + { + pRuby.reset(new SwFormatRuby( aOrigText )); + aNewOrigText = rReplaceWith; + } + break; + case eReplacementBelow : + { + pRuby.reset(new SwFormatRuby( rReplaceWith )); + bRubyBelow = true; + } + break; + case eOriginalBelow : + { + pRuby.reset(new SwFormatRuby( aOrigText )); + aNewOrigText = rReplaceWith; + bRubyBelow = true; + } + break; + default: + OSL_FAIL("unexpected case" ); + } + m_nUnitOffset += nUnitStart + aNewText.getLength(); + + if (pRuby) + { + m_rWrtShell.StartUndo( SwUndoId::SETRUBYATTR ); + if (!aNewOrigText.isEmpty()) + { + // according to FT we currently should not bother about keeping + // attributes in Hangul/Hanja conversion + ChangeText( aNewOrigText, rOrigText, nullptr, nullptr ); + + //!! since Delete, Insert in 'ChangeText' do not set the WrtShells + //!! bInSelect flag + //!! back to false we do it now manually in order for the selection + //!! to be done properly in the following call to Left. + // We didn't fix it in Delete and Insert since it is currently + // unclear if someone depends on this incorrect behaviour + // of the flag. + m_rWrtShell.EndSelect(); + + m_rWrtShell.Left( SwCursorSkipMode::Chars, true, aNewOrigText.getLength(), true, true ); + } + + pRuby->SetPosition( o3tl::narrowing<sal_uInt16>(bRubyBelow) ); + pRuby->SetAdjustment( RubyAdjust_CENTER ); + + m_rWrtShell.SetAttrItem(*pRuby); + pRuby.reset(); + m_rWrtShell.EndUndo( SwUndoId::SETRUBYATTR ); + } + else + { + m_rWrtShell.StartUndo( SwUndoId::OVERWRITE ); + + // according to FT we should currently not bother about keeping + // attributes in Hangul/Hanja conversion and leave that untouched. + // Thus we do this only for Chinese translation... + const bool bIsChineseConversion = IsChinese( GetSourceLanguage() ); + if (bIsChineseConversion) + ChangeText( aNewText, rOrigText, &rOffsets, m_rWrtShell.GetCursor() ); + else + ChangeText( aNewText, rOrigText, nullptr, nullptr ); + + // change language and font if necessary + if (bIsChineseConversion) + { + m_rWrtShell.SetMark(); + m_rWrtShell.GetCursor()->GetMark()->AdjustContent( -aNewText.getLength() ); + + OSL_ENSURE( GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED || GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL, + "SwHHCWrapper::ReplaceUnit : unexpected target language" ); + + SfxItemSetFixed< + RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT, + RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_LANGUAGE> + aSet( m_rWrtShell.GetAttrPool() ); + if (pNewUnitLanguage) + { + aSet.Put( SvxLanguageItem( *pNewUnitLanguage, RES_CHRATR_CJK_LANGUAGE ) ); + } + + const vcl::Font *pTargetFont = GetTargetFont(); + OSL_ENSURE( pTargetFont, "target font missing?" ); + if (pTargetFont && pNewUnitLanguage) + { + SvxFontItem aFontItem( aSet.Get( RES_CHRATR_CJK_FONT ) ); + aFontItem.SetFamilyName( pTargetFont->GetFamilyName()); + aFontItem.SetFamily( pTargetFont->GetFamilyType()); + aFontItem.SetStyleName( pTargetFont->GetStyleName()); + aFontItem.SetPitch( pTargetFont->GetPitch()); + aFontItem.SetCharSet( pTargetFont->GetCharSet() ); + aSet.Put( aFontItem ); + } + + m_rWrtShell.SetAttrSet( aSet ); + + m_rWrtShell.ClearMark(); + } + + m_rWrtShell.EndUndo( SwUndoId::OVERWRITE ); + } + + m_rWrtShell.EndAllAction(); +} + +bool SwHHCWrapper::HasRubySupport() const +{ + return true; +} + +void SwHHCWrapper::Convert() +{ + OSL_ENSURE( m_pConvArgs == nullptr, "NULL pointer expected" ); + { + SwPaM *pCursor = m_pView->GetWrtShell().GetCursor(); + auto [pSttPos, pEndPos] = pCursor->StartEnd(); // SwPosition* + + if (pSttPos->GetNode().IsTextNode() && + pEndPos->GetNode().IsTextNode()) + { + m_pConvArgs.reset( new SwConversionArgs( GetSourceLanguage(), *pSttPos, *pEndPos ) ); + } + else // we are not in the text (maybe a graphic or OLE object is selected) let's start from the top + { + // get PaM that points to the start of the document + SwNode& rNode = m_pView->GetDocShell()->GetDoc()->GetNodes().GetEndOfContent(); + SwPaM aPam(rNode); + aPam.Move( fnMoveBackward, GoInDoc ); // move to start of document + + pSttPos = aPam.GetPoint(); //! using a PaM here makes sure we will get only text nodes + SwTextNode *pTextNode = pSttPos->GetNode().GetTextNode(); + // just in case we check anyway... + if (!pTextNode || !pTextNode->IsTextNode()) + return; + m_pConvArgs.reset( new SwConversionArgs( GetSourceLanguage(), *pSttPos, *pSttPos ) ); + } + OSL_ENSURE( m_pConvArgs->pStartPos && m_pConvArgs->pStartPos->GetNode().IsTextNode(), + "failed to get proper start text node" ); + OSL_ENSURE( m_pConvArgs->pEndPos && m_pConvArgs->pEndPos->GetNode().IsTextNode(), + "failed to get proper end text node" ); + + // chinese conversion specific settings + OSL_ENSURE( IsChinese( GetSourceLanguage() ) == IsChinese( GetTargetLanguage() ), + "source and target language mismatch?" ); + if (IsChinese( GetTargetLanguage() )) + { + m_pConvArgs->nConvTargetLang = GetTargetLanguage(); + m_pConvArgs->pTargetFont = GetTargetFont(); + m_pConvArgs->bAllowImplicitChangesForNotConvertibleText = true; + } + + // if it is not just a selection and we are about to begin + // with the current conversion for the very first time + // we need to find the start of the current (initial) + // convertible unit in order for the text conversion to give + // the correct result for that. Since it is easier to obtain + // the start of the word we use that though. + if (!pCursor->HasMark()) // is not a selection? + { + // since #118246 / #117803 still occurs if the cursor is placed + // between the two chinese characters to be converted (because both + // of them are words on their own!) using the word boundary here does + // not work. Thus since chinese conversion is not interactive we start + // at the begin of the paragraph to solve the problem, i.e. have the + // TextConversion service get those characters together in the same call. + sal_Int32 nStartIdx = -1; + if (editeng::HangulHanjaConversion::IsChinese( GetSourceLanguage() ) ) + nStartIdx = 0; + else + { + OUString aText( m_pConvArgs->pStartPos->GetNode().GetTextNode()->GetText() ); + const sal_Int32 nPos = m_pConvArgs->pStartPos->GetContentIndex(); + Boundary aBoundary( g_pBreakIt->GetBreakIter()-> + getWordBoundary( aText, nPos, g_pBreakIt->GetLocale( m_pConvArgs->nConvSrcLang ), + WordType::DICTIONARY_WORD, true ) ); + + // valid result found? + if (aBoundary.startPos < aText.getLength() && + aBoundary.startPos != aBoundary.endPos) + { + nStartIdx = aBoundary.startPos; + } + } + + if (nStartIdx != -1) + m_pConvArgs->pStartPos->SetContent( nStartIdx ); + } + } + + if ( m_bIsOtherContent ) + ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::Other ); + else + { + m_bStartChk = false; + ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::BodyEnd ); + } + + ConvertDocument(); + + ConvEnd_impl( m_pConvArgs.get() ); +} + +bool SwHHCWrapper::ConvNext_impl( ) +{ + //! modified version of SvxSpellWrapper::SpellNext + + // no change of direction so the desired region is fully processed + if( m_bStartChk ) + m_bStartDone = true; + else + m_bEndDone = true; + + if( m_bIsOtherContent && m_bStartDone && m_bEndDone ) // document completely checked? + { + return false; + } + + bool bGoOn = false; + + if ( m_bIsOtherContent ) + { + m_bStartChk = false; + ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::Body ); + bGoOn = true; + } + else if ( m_bStartDone && m_bEndDone ) + { + // body region done, ask about special region + if( !m_bIsSelection && m_rWrtShell.HasOtherCnt() ) + { + ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::Other ); + m_bIsOtherContent = bGoOn = true; + } + } + else + { + m_bStartChk = !m_bStartDone; + ConvStart_impl( m_pConvArgs.get(), m_bStartChk ? SvxSpellArea::BodyStart : SvxSpellArea::BodyEnd ); + bGoOn = true; + } + return bGoOn; +} + +void SwHHCWrapper::FindConvText_impl() +{ + //! modified version of SvxSpellWrapper::FindSpellError + + bool bFound = false; + + weld::WaitObject aWait(GetUIParent()); + bool bConv = true; + + while ( bConv ) + { + bFound = ConvContinue_impl( m_pConvArgs.get() ); + if (bFound) + { + bConv = false; + } + else + { + ConvEnd_impl( m_pConvArgs.get() ); + bConv = ConvNext_impl(); + } + } +} + +void SwHHCWrapper::ConvStart_impl( SwConversionArgs /* [out] */ *pConversionArgs, SvxSpellArea eArea ) +{ + m_bIsDrawObj = SvxSpellArea::Other == eArea; + m_pView->SpellStart( eArea, m_bStartDone, m_bEndDone, /* [out] */ pConversionArgs ); +} + +void SwHHCWrapper::ConvEnd_impl( SwConversionArgs const *pConversionArgs ) +{ + m_pView->SpellEnd( pConversionArgs ); +} + +bool SwHHCWrapper::ConvContinue_impl( SwConversionArgs *pConversionArgs ) +{ + bool bProgress = !m_bIsDrawObj && !m_bIsSelection; + pConversionArgs->aConvText.clear(); + pConversionArgs->nConvTextLang = LANGUAGE_NONE; + m_pView->GetWrtShell().SpellContinue( &m_nPageCount, bProgress ? &m_nPageStart : nullptr, pConversionArgs ); + return !pConversionArgs->aConvText.isEmpty(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/lingu/hyp.cxx b/sw/source/uibase/lingu/hyp.cxx new file mode 100644 index 0000000000..caf235bf79 --- /dev/null +++ b/sw/source/uibase/lingu/hyp.cxx @@ -0,0 +1,124 @@ +/* -*- 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 <view.hxx> +#include <edtwin.hxx> +#include <wrtsh.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <com/sun/star/linguistic2/XLinguProperties.hpp> +#include <swwait.hxx> + +#include <hyp.hxx> +#include <mdiexp.hxx> +#include <strings.hrc> + +#include <memory> + +#define PSH (&m_pView->GetWrtShell()) + +using namespace ::com::sun::star; + +// interactive separation +SwHyphWrapper::SwHyphWrapper( SwView* pVw, + uno::Reference< linguistic2::XHyphenator > const &rxHyph, + bool bStart, bool bOther, bool bSelect ) : + SvxSpellWrapper( pVw->GetEditWin().GetFrameWeld(), rxHyph, bStart, bOther ), + m_pView( pVw ), + m_nPageCount( 0 ), + m_nPageStart( 0 ), + m_bInSelection( bSelect ), + m_bInfoBox( false ) +{ + uno::Reference< linguistic2::XLinguProperties > xProp( GetLinguPropertySet() ); + m_bAutomatic = xProp.is() && xProp->getIsHyphAuto(); +} + +void SwHyphWrapper::SpellStart( SvxSpellArea eSpell ) +{ + if( SvxSpellArea::Other == eSpell && m_nPageCount ) + { + ::EndProgress( m_pView->GetDocShell() ); + m_nPageCount = 0; + m_nPageStart = 0; + } + m_pView->HyphStart( eSpell ); +} + +void SwHyphWrapper::SpellContinue() +{ + // for automatic separation, make actions visible only at the end + std::optional<SwWait> oWait; + if( m_bAutomatic ) + { + PSH->StartAllAction(); + oWait.emplace( *m_pView->GetDocShell(), true ); + } + + uno::Reference< uno::XInterface > xHyphWord = m_bInSelection ? + PSH->HyphContinue( nullptr, nullptr ) : + PSH->HyphContinue( &m_nPageCount, &m_nPageStart ); + SetLast( xHyphWord ); + + // for automatic separation, make actions visible only at the end + if( m_bAutomatic ) + { + PSH->EndAllAction(); + oWait.reset(); + } +} + +void SwHyphWrapper::SpellEnd() +{ + PSH->HyphEnd(); + SvxSpellWrapper::SpellEnd(); +} + +bool SwHyphWrapper::SpellMore() +{ + PSH->Push(); + m_bInfoBox = true; + PSH->Combine(); + return false; +} + +void SwHyphWrapper::InsertHyphen( const sal_Int32 nPos ) +{ + if( nPos) + SwEditShell::InsertSoftHyph(nPos + 1); // does nPos == 1 really mean + // insert hyphen after first char? + // (instead of nPos == 0) + else + PSH->HyphIgnore(); +} + +SwHyphWrapper::~SwHyphWrapper() +{ + if( m_nPageCount ) + ::EndProgress( m_pView->GetDocShell() ); + if( m_bInfoBox && !Application::IsHeadlessModeEnabled() ) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(m_pView->GetEditWin().GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SwResId(STR_HYP_OK))); + xInfoBox->run(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/lingu/olmenu.cxx b/sw/source/uibase/lingu/olmenu.cxx new file mode 100644 index 0000000000..acf06f2e41 --- /dev/null +++ b/sw/source/uibase/lingu/olmenu.cxx @@ -0,0 +1,885 @@ +/* -*- 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 <SwRewriter.hxx> +#include <cmdid.h> +#include <strings.hrc> +#include <doc.hxx> +#include <edtwin.hxx> +#include <helpids.h> +#include <langhelper.hxx> +#include <bitmaps.hlst> +#include <olmenu.hxx> +#include <swmodule.hxx> +#include <swtypes.hxx> +#include <swundo.hxx> +#include <utility> +#include <view.hxx> +#include <wrtsh.hxx> + +#include <comphelper/lok.hxx> +#include <comphelper/anytostring.hxx> +#include <comphelper/processfactory.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <editeng/acorrcfg.hxx> +#include <editeng/svxacorr.hxx> +#include <editeng/splwrap.hxx> +#include <editeng/unolingu.hxx> +#include <editeng/editview.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <linguistic/misc.hxx> +#include <rtl/string.hxx> +#include <vcl/commandinfoprovider.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/request.hxx> +#include <sfx2/viewfrm.hxx> +#include <svl/itemset.hxx> +#include <svl/languageoptions.hxx> +#include <svl/stritem.hxx> +#include <svl/voiditem.hxx> +#include <svtools/langtab.hxx> +#include <unotools/lingucfg.hxx> +#include <unotools/linguprops.hxx> +#include <vcl/settings.hxx> +#include <osl/diagnose.h> + +#include <map> + +#include <com/sun/star/document/XDocumentLanguages.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/linguistic2/XLanguageGuessing.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/system/SystemShellExecuteFlags.hpp> +#include <com/sun/star/system/SystemShellExecute.hpp> +#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp> + +using namespace ::com::sun::star; + +/// @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, +/// @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. +// check if nScriptType includes the script type associated to nLang +static bool lcl_checkScriptType( SvtScriptType nScriptType, LanguageType nLang ) +{ + return bool(nScriptType & SvtLanguageOptions::GetScriptTypeOfLanguage( nLang )); +} + +void SwSpellPopup::fillLangPopupMenu( + PopupMenu *pPopupMenu, + sal_uInt16 nLangItemIdStart, + const uno::Sequence< OUString >& aSeq, + SwWrtShell* pWrtSh, + std::map< sal_Int16, OUString > &rLangTable ) +{ + if (!pPopupMenu) + return; + + // set of languages to be displayed in the sub menus + std::set< OUString > aLangItems; + + OUString aCurLang( aSeq[0] ); + SvtScriptType nScriptType = static_cast<SvtScriptType>(aSeq[1].toInt32()); + OUString aKeyboardLang( aSeq[2] ); + OUString aGuessedTextLang( aSeq[3] ); + + if (!aCurLang.isEmpty() && + LANGUAGE_DONTKNOW != SvtLanguageTable::GetLanguageType( aCurLang )) + aLangItems.insert( aCurLang ); + + //2--System + const AllSettings& rAllSettings = Application::GetSettings(); + LanguageType rSystemLanguage = rAllSettings.GetLanguageTag().getLanguageType(); + if (rSystemLanguage != LANGUAGE_DONTKNOW) + { + if (lcl_checkScriptType( nScriptType, rSystemLanguage )) + aLangItems.insert( SvtLanguageTable::GetLanguageString(rSystemLanguage) ); + } + + //3--UI + LanguageType rUILanguage = rAllSettings.GetUILanguageTag().getLanguageType(); + if (rUILanguage != LANGUAGE_DONTKNOW) + { + if (lcl_checkScriptType(nScriptType, rUILanguage )) + aLangItems.insert( SvtLanguageTable::GetLanguageString(rUILanguage) ); + } + + //4--guessed language + if (!aGuessedTextLang.isEmpty()) + { + if (lcl_checkScriptType(nScriptType, SvtLanguageTable::GetLanguageType(aGuessedTextLang))) + aLangItems.insert( aGuessedTextLang ); + } + + //5--keyboard language + if (!aKeyboardLang.isEmpty()) + { + if (lcl_checkScriptType(nScriptType, SvtLanguageTable::GetLanguageType(aKeyboardLang))) + aLangItems.insert( aKeyboardLang ); + } + + //6--all languages used in current document + uno::Reference< css::frame::XModel > xModel; + uno::Reference< css::frame::XController > xController = pWrtSh->GetView().GetViewFrame().GetFrame().GetFrameInterface()->getController(); + if ( xController.is() ) + xModel = xController->getModel(); + uno::Reference< document::XDocumentLanguages > xDocumentLanguages( xModel, uno::UNO_QUERY ); + /*the description of nScriptType flags + LATIN : 0x0001 + ASIAN : 0x0002 + COMPLEX: 0x0004 + */ + const sal_Int16 nMaxCount = 7; + if (xDocumentLanguages.is()) + { + const uno::Sequence< lang::Locale > rLocales( xDocumentLanguages->getDocumentLanguages( static_cast<sal_Int16>(nScriptType), nMaxCount ) ); + for (const lang::Locale& rLocale : rLocales) + { + if (aLangItems.size() == size_t(nMaxCount)) + break; + if (lcl_checkScriptType( nScriptType, SvtLanguageTable::GetLanguageType( rLocale.Language ))) + aLangItems.insert( rLocale.Language ); + } + } + + sal_uInt16 nItemId = nLangItemIdStart; + for (const OUString& aEntryText : aLangItems) + { + if (aEntryText != SvtLanguageTable::GetLanguageString( LANGUAGE_NONE ) && + aEntryText != "*" && // multiple languages in current selection + !aEntryText.isEmpty()) // 'no language found' from language guessing + { + OSL_ENSURE( nLangItemIdStart <= nItemId && nItemId <= nLangItemIdStart + MN_MAX_NUM_LANG, + "nItemId outside of expected range!" ); + pPopupMenu->InsertItem( nItemId, aEntryText, MenuItemBits::RADIOCHECK ); + if (aEntryText == aCurLang) + { + //make a check mark for the current language + pPopupMenu->CheckItem( nItemId ); + } + rLangTable[ nItemId ] = aEntryText; + ++nItemId; + } + } + + pPopupMenu->InsertItem( nLangItemIdStart + MN_NONE_OFFSET, SwResId( STR_LANGSTATUS_NONE ), MenuItemBits::RADIOCHECK ); + if ( SvtLanguageTable::GetLanguageString( LANGUAGE_NONE ) == aCurLang ) + pPopupMenu->CheckItem( nLangItemIdStart + MN_NONE_OFFSET ); + + pPopupMenu->InsertItem( nLangItemIdStart + MN_RESET_OFFSET, SwResId( STR_RESET_TO_DEFAULT_LANGUAGE ) ); + pPopupMenu->InsertItem( nLangItemIdStart + MN_MORE_OFFSET, SwResId( STR_LANGSTATUS_MORE ) ); +} + +SwSpellPopup::SwSpellPopup( + SwWrtShell* pWrtSh, + uno::Reference< linguistic2::XSpellAlternatives > xAlt, + const OUString &rParaText) + : m_aBuilder(nullptr, AllSettings::GetUIRootDir(), "modules/swriter/ui/spellmenu.ui", "") + , m_xPopupMenu(m_aBuilder.get_menu(u"menu")) + , m_nIgnoreWordId(m_xPopupMenu->GetItemId(u"ignoreall")) + , m_nAddMenuId(m_xPopupMenu->GetItemId(u"addmenu")) + , m_nAddId(m_xPopupMenu->GetItemId(u"add")) + , m_nSpellDialogId(m_xPopupMenu->GetItemId(u"spelldialog")) + , m_nCorrectMenuId(m_xPopupMenu->GetItemId(u"correctmenu")) + , m_nCorrectDialogId(m_xPopupMenu->GetItemId(u"correctdialog")) + , m_nLangSelectionMenuId(m_xPopupMenu->GetItemId(u"langselection")) + , m_nLangParaMenuId(m_xPopupMenu->GetItemId(u"langpara")) + , m_nRedlineAcceptId(m_xPopupMenu->GetItemId(u"accept")) + , m_nRedlineRejectId(m_xPopupMenu->GetItemId(u"reject")) + , m_nRedlineNextId(m_xPopupMenu->GetItemId(u"next")) + , m_nRedlinePrevId(m_xPopupMenu->GetItemId(u"prev")) + , m_pSh( pWrtSh ) + , m_xSpellAlt(std::move(xAlt)) + , m_bGrammarResults(false) +{ + OSL_ENSURE(m_xSpellAlt.is(), "no spelling alternatives available"); + + m_xPopupMenu->SetMenuFlags(MenuFlags::NoAutoMnemonics); + bool bUseImagesInMenus = Application::GetSettings().GetStyleSettings().GetUseImagesInMenus(); + + m_nCheckedLanguage = LANGUAGE_NONE; + css::uno::Sequence< OUString > aSuggestions; + if (m_xSpellAlt.is()) + { + m_nCheckedLanguage = LanguageTag( m_xSpellAlt->getLocale() ).getLanguageType(); + aSuggestions = m_xSpellAlt->getAlternatives(); + } + sal_Int16 nStringCount = static_cast< sal_Int16 >( aSuggestions.getLength() ); + + SvtLinguConfig aCfg; + + PopupMenu *pMenu = m_xPopupMenu->GetPopupMenu(m_nCorrectMenuId); + pMenu->SetMenuFlags(MenuFlags::NoAutoMnemonics); + bool bEnable = false; + if( nStringCount ) + { + Image aImage; + OUString aSuggestionImageUrl; + + if (bUseImagesInMenus) + { + uno::Reference< container::XNamed > xNamed( m_xSpellAlt, uno::UNO_QUERY ); + if (xNamed.is()) + { + aSuggestionImageUrl = aCfg.GetSpellAndGrammarContextSuggestionImage( xNamed->getName() ); + aImage = Image( aSuggestionImageUrl ); + } + } + + m_xPopupMenu->InsertSeparator({}, 0); + bEnable = true; + sal_uInt16 nAutoCorrItemId = MN_AUTOCORR_START; + sal_uInt16 nItemId = MN_SUGGESTION_START; + for (sal_uInt16 i = 0; i < nStringCount; ++i) + { + const OUString aEntry = aSuggestions[ i ]; + m_xPopupMenu->InsertItem(nItemId, aEntry, MenuItemBits::NONE, {}, i); + m_xPopupMenu->SetHelpId(nItemId, HID_LINGU_REPLACE); + if (!aSuggestionImageUrl.isEmpty()) + m_xPopupMenu->SetItemImage(nItemId, aImage); + + pMenu->InsertItem( nAutoCorrItemId, aEntry ); + pMenu->SetHelpId( nAutoCorrItemId, HID_LINGU_AUTOCORR); + + ++nAutoCorrItemId; + ++nItemId; + } + } + + uno::Reference< frame::XFrame > xFrame = pWrtSh->GetView().GetViewFrame().GetFrame().GetFrameInterface(); + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame)); + + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:SpellingAndGrammarDialog", aModuleName); + m_xPopupMenu->SetItemText(m_nSpellDialogId, + vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties)); + } + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:AutoCorrectDlg", aModuleName); + m_xPopupMenu->SetItemText(m_nCorrectDialogId, + vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties)); + } + + sal_uInt16 nItemPos = m_xPopupMenu->GetItemPos(m_nIgnoreWordId); + OUString aIgnoreSelection( SwResId( STR_IGNORE_SELECTION ) ); + m_xPopupMenu->InsertItem(MN_IGNORE_SELECTION, aIgnoreSelection, MenuItemBits::NONE, {}, nItemPos); + m_xPopupMenu->SetHelpId(MN_IGNORE_SELECTION, HID_LINGU_IGNORE_SELECTION); + + m_xPopupMenu->EnableItem(m_nCorrectMenuId, bEnable); + + uno::Reference< linguistic2::XLanguageGuessing > xLG = SW_MOD()->GetLanguageGuesser(); + LanguageType nGuessLangWord = LANGUAGE_NONE; + LanguageType nGuessLangPara = LANGUAGE_NONE; + if (m_xSpellAlt.is() && xLG.is()) + { + nGuessLangWord = EditView::CheckLanguage( m_xSpellAlt->getWord(), ::GetSpellChecker(), xLG, false ); + nGuessLangPara = EditView::CheckLanguage( rParaText, ::GetSpellChecker(), xLG, true ); + } + if (nGuessLangWord != LANGUAGE_NONE || nGuessLangPara != LANGUAGE_NONE) + { + // make sure LANGUAGE_NONE gets not used as menu entry + if (nGuessLangWord == LANGUAGE_NONE) + nGuessLangWord = nGuessLangPara; + if (nGuessLangPara == LANGUAGE_NONE) + nGuessLangPara = nGuessLangWord; + } + + pMenu = m_xPopupMenu->GetPopupMenu(m_nAddMenuId); + pMenu->SetMenuFlags(MenuFlags::NoAutoMnemonics); //! necessary to retrieve the correct dictionary name in 'Execute' below + uno::Reference< linguistic2::XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() ); + sal_uInt16 nItemId = MN_DICTIONARIES_START; + if (xDicList.is()) + { + // add the default positive dictionary to dic-list (if not already done). + // This is to ensure that there is at least one dictionary to which + // words could be added. + uno::Reference< linguistic2::XDictionary > xDic( LinguMgr::GetStandardDic() ); + if (xDic.is()) + xDic->setActive( true ); + + m_aDics = xDicList->getDictionaries(); + + for( const uno::Reference< linguistic2::XDictionary >& rDic : std::as_const(m_aDics) ) + { + uno::Reference< linguistic2::XDictionary > xDicTmp = rDic; + if (!xDicTmp.is() || LinguMgr::GetIgnoreAllList() == xDicTmp) + continue; + + uno::Reference< frame::XStorable > xStor( xDicTmp, uno::UNO_QUERY ); + LanguageType nActLanguage = LanguageTag( xDicTmp->getLocale() ).getLanguageType(); + if( xDicTmp->isActive() + && xDicTmp->getDictionaryType() != linguistic2::DictionaryType_NEGATIVE + && (m_nCheckedLanguage == nActLanguage || LANGUAGE_NONE == nActLanguage ) + && (!xStor.is() || !xStor->isReadonly()) ) + { + // the extra 1 is because of the (possible) external + // linguistic entry above + pMenu->InsertItem( nItemId, xDicTmp->getName() ); + m_aDicNameSingle = xDicTmp->getName(); + + if (bUseImagesInMenus) + { + uno::Reference< lang::XServiceInfo > xSvcInfo( xDicTmp, uno::UNO_QUERY ); + if (xSvcInfo.is()) + { + OUString aDictionaryImageUrl( aCfg.GetSpellAndGrammarContextDictionaryImage( + xSvcInfo->getImplementationName() ) ); + if (!aDictionaryImageUrl.isEmpty()) + { + Image aImage( aDictionaryImageUrl ); + pMenu->SetItemImage( nItemId, aImage ); + } + } + } + + ++nItemId; + } + } + } + m_xPopupMenu->EnableItem(m_nAddMenuId, (nItemId - MN_DICTIONARIES_START) > 1); + m_xPopupMenu->EnableItem(m_nAddId, (nItemId - MN_DICTIONARIES_START) == 1); + + //ADD NEW LANGUAGE MENU ITEM + + OUString aScriptTypesInUse( OUString::number( static_cast<int>(pWrtSh->GetScriptType()) ) ); + + // get keyboard language + OUString aKeyboardLang; + SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin(); + LanguageType nLang = rEditWin.GetInputLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aKeyboardLang = SvtLanguageTable::GetLanguageString( nLang ); + + // get the language that is in use + OUString aCurrentLang("*"); + nLang = SwLangHelper::GetCurrentLanguage( *pWrtSh ); + if (nLang != LANGUAGE_DONTKNOW) + aCurrentLang = SvtLanguageTable::GetLanguageString( nLang ); + + // build sequence for status value + uno::Sequence< OUString > aSeq{ aCurrentLang, + aScriptTypesInUse, + aKeyboardLang, + SvtLanguageTable::GetLanguageString(nGuessLangWord) }; + + pMenu = m_xPopupMenu->GetPopupMenu(m_nLangSelectionMenuId); + fillLangPopupMenu( pMenu, MN_SET_LANGUAGE_SELECTION_START, aSeq, pWrtSh, m_aLangTable_Text ); + m_xPopupMenu->EnableItem(m_nLangSelectionMenuId); + + pMenu = m_xPopupMenu->GetPopupMenu(m_nLangParaMenuId); + fillLangPopupMenu( pMenu, MN_SET_LANGUAGE_PARAGRAPH_START, aSeq, pWrtSh, m_aLangTable_Paragraph ); + m_xPopupMenu->EnableItem(m_nLangParaMenuId); + + if (bUseImagesInMenus) + m_xPopupMenu->SetItemImage(m_nSpellDialogId, + vcl::CommandInfoProvider::GetImageForCommand(".uno:SpellingAndGrammarDialog", xFrame)); + + checkRedline(); + m_xPopupMenu->RemoveDisabledEntries( true ); + + InitItemCommands(aSuggestions); +} + +SwSpellPopup::SwSpellPopup( + SwWrtShell *pWrtSh, + const linguistic2::ProofreadingResult &rResult, + sal_Int32 nErrorInResult, + const uno::Sequence< OUString > &rSuggestions, + const OUString &rParaText ) + : m_aBuilder(nullptr, AllSettings::GetUIRootDir(), "modules/swriter/ui/spellmenu.ui", "") + , m_xPopupMenu(m_aBuilder.get_menu(u"menu")) + , m_nIgnoreWordId(m_xPopupMenu->GetItemId(u"ignoreall")) + , m_nAddMenuId(m_xPopupMenu->GetItemId(u"addmenu")) + , m_nAddId(m_xPopupMenu->GetItemId(u"add")) + , m_nSpellDialogId(m_xPopupMenu->GetItemId(u"spelldialog")) + , m_nCorrectMenuId(m_xPopupMenu->GetItemId(u"correctmenu")) + , m_nCorrectDialogId(m_xPopupMenu->GetItemId(u"correctdialog")) + , m_nLangSelectionMenuId(m_xPopupMenu->GetItemId(u"langselection")) + , m_nLangParaMenuId(m_xPopupMenu->GetItemId(u"langpara")) + , m_nRedlineAcceptId(m_xPopupMenu->GetItemId(u"accept")) + , m_nRedlineRejectId(m_xPopupMenu->GetItemId(u"reject")) + , m_nRedlineNextId(m_xPopupMenu->GetItemId(u"next")) + , m_nRedlinePrevId(m_xPopupMenu->GetItemId(u"prev")) + , m_pSh(pWrtSh) + , m_bGrammarResults(true) +{ + m_nCheckedLanguage = LanguageTag::convertToLanguageType( rResult.aLocale ); + bool bUseImagesInMenus = Application::GetSettings().GetStyleSettings().GetUseImagesInMenus(); + + sal_uInt16 nPos = 0; + OUString aMessageText( rResult.aErrors[ nErrorInResult ].aShortComment ); + m_xPopupMenu->InsertSeparator({}, nPos++); + m_xPopupMenu->InsertItem(MN_SHORT_COMMENT, aMessageText, MenuItemBits::NOSELECT, {}, nPos++); + if (bUseImagesInMenus) + m_xPopupMenu->SetItemImage(MN_SHORT_COMMENT, Image(StockImage::Yes, BMP_INFO_16)); + + // Add an item to show detailed infos if the FullCommentURL property is defined + const beans::PropertyValues aProperties = rResult.aErrors[ nErrorInResult ].aProperties; + for ( const auto& rProp : aProperties ) + { + if ( rProp.Name == "FullCommentURL" ) + { + uno::Any aValue = rProp.Value; + aValue >>= m_sExplanationLink; + + if ( !m_sExplanationLink.isEmpty( ) ) + break; + } + } + + if ( !m_sExplanationLink.isEmpty( ) ) + { + m_xPopupMenu->InsertItem(MN_EXPLANATION_LINK, SwResId(STR_EXPLANATION_LINK), MenuItemBits::TEXT | MenuItemBits::HELP, {}, nPos++); + } + + m_xPopupMenu->SetMenuFlags(MenuFlags::NoAutoMnemonics); + + m_xPopupMenu->InsertSeparator({}, nPos++); + if ( rSuggestions.hasElements() ) // suggestions available... + { + Image aImage; + OUString aSuggestionImageUrl; + + if (bUseImagesInMenus) + { + uno::Reference< lang::XServiceInfo > xInfo( rResult.xProofreader, uno::UNO_QUERY ); + if (xInfo.is()) + { + aSuggestionImageUrl = SvtLinguConfig().GetSpellAndGrammarContextSuggestionImage( xInfo->getImplementationName() ); + aImage = Image( aSuggestionImageUrl ); + } + } + + sal_uInt16 nItemId = MN_SUGGESTION_START; + for (const OUString& aEntry : std::as_const(rSuggestions)) + { + m_xPopupMenu->InsertItem(nItemId, aEntry, MenuItemBits::NONE, {}, nPos++); + m_xPopupMenu->SetHelpId(nItemId, HID_LINGU_REPLACE); + if (!aSuggestionImageUrl.isEmpty()) + m_xPopupMenu->SetItemImage(nItemId, aImage); + + ++nItemId; + } + m_xPopupMenu->InsertSeparator({}, nPos++); + } + + uno::Reference< frame::XFrame > xFrame = pWrtSh->GetView().GetViewFrame().GetFrame().GetFrameInterface(); + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame)); + + OUString aIgnoreSelection( SwResId( STR_IGNORE_SELECTION ) ); + auto aCommandProperties = vcl::CommandInfoProvider::GetCommandProperties(".uno:SpellingAndGrammarDialog", aModuleName); + m_xPopupMenu->SetItemText(m_nSpellDialogId, + vcl::CommandInfoProvider::GetPopupLabelForCommand(aCommandProperties)); + sal_uInt16 nItemPos = m_xPopupMenu->GetItemPos(m_nIgnoreWordId); + m_xPopupMenu->InsertItem(MN_IGNORE_SELECTION, aIgnoreSelection, MenuItemBits::NONE, {}, nItemPos); + m_xPopupMenu->SetHelpId(MN_IGNORE_SELECTION, HID_LINGU_IGNORE_SELECTION); + + m_xPopupMenu->EnableItem(m_nCorrectMenuId, false); + m_xPopupMenu->EnableItem(m_nCorrectDialogId, false); + + uno::Reference< linguistic2::XLanguageGuessing > xLG = SW_MOD()->GetLanguageGuesser(); + LanguageType nGuessLangWord = LANGUAGE_NONE; + LanguageType nGuessLangPara = LANGUAGE_NONE; + if (xLG.is()) + { + nGuessLangPara = EditView::CheckLanguage( rParaText, ::GetSpellChecker(), xLG, true ); + } + if (nGuessLangWord != LANGUAGE_NONE || nGuessLangPara != LANGUAGE_NONE) + { + // make sure LANGUAGE_NONE gets not used as menu entry + if (nGuessLangWord == LANGUAGE_NONE) + nGuessLangWord = nGuessLangPara; + if (nGuessLangPara == LANGUAGE_NONE) + nGuessLangPara = nGuessLangWord; + } + + m_xPopupMenu->EnableItem(m_nAddMenuId, false); + m_xPopupMenu->EnableItem(m_nAddId, false); + + //ADD NEW LANGUAGE MENU ITEM + + OUString aScriptTypesInUse( OUString::number( static_cast<int>(pWrtSh->GetScriptType()) ) ); + + // get keyboard language + OUString aKeyboardLang; + SwEditWin& rEditWin = pWrtSh->GetView().GetEditWin(); + LanguageType nLang = rEditWin.GetInputLanguage(); + if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM) + aKeyboardLang = SvtLanguageTable::GetLanguageString( nLang ); + + // get the language that is in use + OUString aCurrentLang("*"); + nLang = SwLangHelper::GetCurrentLanguage( *pWrtSh ); + if (nLang != LANGUAGE_DONTKNOW) + aCurrentLang = SvtLanguageTable::GetLanguageString( nLang ); + + // build sequence for status value + uno::Sequence< OUString > aSeq{ aCurrentLang, + aScriptTypesInUse, + aKeyboardLang, + SvtLanguageTable::GetLanguageString(nGuessLangWord) }; + + PopupMenu *pMenu = m_xPopupMenu->GetPopupMenu(m_nLangSelectionMenuId); + fillLangPopupMenu( pMenu, MN_SET_LANGUAGE_SELECTION_START, aSeq, pWrtSh, m_aLangTable_Text ); + m_xPopupMenu->EnableItem(m_nLangSelectionMenuId); + + pMenu = m_xPopupMenu->GetPopupMenu(m_nLangParaMenuId); + fillLangPopupMenu( pMenu, MN_SET_LANGUAGE_PARAGRAPH_START, aSeq, pWrtSh, m_aLangTable_Paragraph ); + m_xPopupMenu->EnableItem(m_nLangParaMenuId); + + if (bUseImagesInMenus) + m_xPopupMenu->SetItemImage(m_nSpellDialogId, + vcl::CommandInfoProvider::GetImageForCommand(".uno:SpellingAndGrammarDialog", xFrame)); + + checkRedline(); + m_xPopupMenu->RemoveDisabledEntries(true); + + SvtLinguConfig().SetProperty( UPN_IS_GRAMMAR_INTERACTIVE, uno::Any( true )); + + InitItemCommands(rSuggestions); +} + +SwSpellPopup::~SwSpellPopup() {} + +void SwSpellPopup::InitItemCommands(const css::uno::Sequence< OUString >& aSuggestions) +{ + if (!comphelper::LibreOfficeKit::isActive()) + return; + + // None is added only for LOK, it means there is no need to execute anything + m_xPopupMenu->SetItemCommand(MN_SHORT_COMMENT, ".uno:None"); + m_xPopupMenu->SetItemCommand(m_nSpellDialogId, ".uno:SpellingAndGrammarDialog"); + if(m_bGrammarResults) + m_xPopupMenu->SetItemCommand(m_nIgnoreWordId, ".uno:SpellCheckIgnoreAll?Type:string=Grammar"); + else + m_xPopupMenu->SetItemCommand(m_nIgnoreWordId, ".uno:SpellCheckIgnoreAll?Type:string=Spelling"); + if(m_bGrammarResults) + m_xPopupMenu->SetItemCommand(MN_IGNORE_SELECTION, ".uno:SpellCheckIgnore?Type:string=Grammar"); + else + m_xPopupMenu->SetItemCommand(MN_IGNORE_SELECTION, ".uno:SpellCheckIgnore?Type:string=Spelling"); + + for(int i = 0; i < aSuggestions.getLength(); ++i) + { + sal_uInt16 nItemId = MN_SUGGESTION_START + i; + OUString sCommandString = ".uno:SpellCheckApplySuggestion?ApplyRule:string="; + if(m_bGrammarResults) + sCommandString += "Grammar_"; + else if (m_xSpellAlt.is()) + sCommandString += "Spelling_"; + sCommandString += m_xPopupMenu->GetItemText(nItemId); + m_xPopupMenu->SetItemCommand(nItemId, sCommandString); + } + + PopupMenu *pMenu = m_xPopupMenu->GetPopupMenu(m_nLangSelectionMenuId); + m_xPopupMenu->SetItemCommand(m_nLangSelectionMenuId, ".uno:SetSelectionLanguageMenu"); + if(pMenu) + { + for (const auto& item : m_aLangTable_Text) + { + OUString sCommandString = ".uno:LanguageStatus?Language:string=Current_" + item.second; + pMenu->SetItemCommand(item.first, sCommandString); + } + + pMenu->SetItemCommand(MN_SET_SELECTION_NONE, ".uno:LanguageStatus?Language:string=Current_LANGUAGE_NONE"); + pMenu->SetItemCommand(MN_SET_SELECTION_RESET, ".uno:LanguageStatus?Language:string=Current_RESET_LANGUAGES"); + pMenu->SetItemCommand(MN_SET_SELECTION_MORE, ".uno:FontDialog?Page:string=font"); + } + + pMenu = m_xPopupMenu->GetPopupMenu(m_nLangParaMenuId); + m_xPopupMenu->SetItemCommand(m_nLangParaMenuId, ".uno:SetParagraphLanguageMenu"); + if(pMenu) + { + for (const auto& item : m_aLangTable_Paragraph) + { + OUString sCommandString = ".uno:LanguageStatus?Language:string=Paragraph_" + item.second; + pMenu->SetItemCommand(item.first, sCommandString); + } + + pMenu->SetItemCommand(MN_SET_PARA_NONE, ".uno:LanguageStatus?Language:string=Paragraph_LANGUAGE_NONE"); + pMenu->SetItemCommand(MN_SET_PARA_RESET, ".uno:LanguageStatus?Language:string=Paragraph_RESET_LANGUAGES"); + pMenu->SetItemCommand(MN_SET_PARA_MORE, ".uno:FontDialogForParagraph"); + } +} + +void SwSpellPopup::checkRedline() +{ + // Let SwView::GetState() already has the logic on when to disable the + // accept/reject and the next/prev change items, let it do the decision. + + // Build an item set that contains a void item for each menu entry. The + // WhichId of each item is set, so SwView may clear it. + static const sal_uInt16 pRedlineIds[] = { + FN_REDLINE_ACCEPT_DIRECT, + FN_REDLINE_REJECT_DIRECT, + FN_REDLINE_NEXT_CHANGE, + FN_REDLINE_PREV_CHANGE + }; + SwDoc *pDoc = m_pSh->GetDoc(); + SfxItemSetFixed<FN_REDLINE_ACCEPT_DIRECT, FN_REDLINE_PREV_CHANGE> aSet(pDoc->GetAttrPool()); + for (sal_uInt16 nWhich : pRedlineIds) + { + aSet.Put(SfxVoidItem(nWhich)); + } + m_pSh->GetView().GetState(aSet); + + // Enable/disable items based on if the which id of the void items are + // cleared or not. + for (sal_uInt16 nWhich : pRedlineIds) + { + sal_uInt16 nId(0); + if (nWhich == FN_REDLINE_ACCEPT_DIRECT) + nId = m_nRedlineAcceptId; + else if (nWhich == FN_REDLINE_REJECT_DIRECT) + nId = m_nRedlineRejectId; + else if (nWhich == FN_REDLINE_NEXT_CHANGE) + nId = m_nRedlineNextId; + else if (nWhich == FN_REDLINE_PREV_CHANGE) + nId = m_nRedlinePrevId; + m_xPopupMenu->EnableItem(nId, aSet.Get(nWhich).Which() != 0); + } +} + +void SwSpellPopup::Execute( const tools::Rectangle& rWordPos, vcl::Window* pWin ) +{ + sal_uInt16 nRet = m_xPopupMenu->Execute(pWin, pWin->LogicToPixel(rWordPos)); + Execute( nRet ); +} + +void SwSpellPopup::Execute( sal_uInt16 nId ) +{ + if (nId == USHRT_MAX) + return; + + if (/*m_bGrammarResults && */nId == MN_SHORT_COMMENT) + return; // nothing to do since it is the error message (short comment) + + if (MN_SUGGESTION_START <= nId && nId <= MN_SUGGESTION_END) + { + OUString sApplyRule(""); + if(m_bGrammarResults) + sApplyRule += "Grammar_"; + else if (m_xSpellAlt.is()) + sApplyRule += "Spelling_"; + sApplyRule += m_xPopupMenu->GetItemText(nId); + + SfxStringItem aApplyItem(FN_PARAM_1, sApplyRule); + m_pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(SID_SPELLCHECK_APPLY_SUGGESTION, SfxCallMode::SYNCHRON, { &aApplyItem }); + } + else if(MN_AUTOCORR_START <= nId && nId <= MN_AUTOCORR_END) + { + if (m_xSpellAlt.is()) + { + bool bOldIns = m_pSh->IsInsMode(); + m_pSh->SetInsMode(); + + PopupMenu* pMenu = m_xPopupMenu->GetPopupMenu(m_nCorrectMenuId); + assert(pMenu); + OUString aTmp( pMenu->GetItemText(nId) ); + OUString aOrig( m_xSpellAlt->getWord() ); + + // if original word has a trailing . (likely the end of a sentence) + // and the replacement text hasn't, then add it to the replacement + if (!aTmp.isEmpty() && !aOrig.isEmpty() && + aOrig.endsWith(".") && /* !IsAlphaNumeric ??*/ + !aTmp.endsWith(".")) + { + aTmp += "."; + } + + SwRewriter aRewriter; + + aRewriter.AddRule(UndoArg1, m_pSh->GetCursorDescr()); + aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS)); + + OUString aTmpStr = SwResId(STR_START_QUOTE) + + aTmp + SwResId(STR_END_QUOTE); + aRewriter.AddRule(UndoArg3, aTmpStr); + + m_pSh->StartUndo(SwUndoId::UI_REPLACE, &aRewriter); + m_pSh->StartAction(); + + m_pSh->Replace(aTmp, false); + + /* #102505# EndAction/EndUndo moved down since insertion + of temporary auto correction is now undoable two and + must reside in the same undo group.*/ + + // record only if it's NOT already present in autocorrection + SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect(); + + OUString aOrigWord( m_xSpellAlt->getWord() ) ; + OUString aNewWord( pMenu->GetItemText(nId) ); + SvxPrepareAutoCorrect( aOrigWord, aNewWord ); + + pACorr->PutText( aOrigWord, aNewWord, m_nCheckedLanguage ); + + /* #102505# EndAction/EndUndo moved down since insertion + of temporary auto correction is now undoable two and + must reside in the same undo group.*/ + m_pSh->EndAction(); + m_pSh->EndUndo(); + + m_pSh->SetInsMode( bOldIns ); + } + } + else if (nId == m_nSpellDialogId) + { + m_pSh->Left(SwCursorSkipMode::Chars, false, 1, false ); + { + m_pSh->GetView().GetViewFrame().GetDispatcher()-> + Execute( FN_SPELL_GRAMMAR_DIALOG, SfxCallMode::ASYNCHRON ); + } + } + else if (nId == m_nCorrectDialogId) + { + m_pSh->GetView().GetViewFrame().GetDispatcher()->Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON ); + } + else if (nId == MN_IGNORE_SELECTION) + { + SfxStringItem aIgnoreString(FN_PARAM_1, m_bGrammarResults ? OUString("Grammar") : OUString("Spelling")); + m_pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(SID_SPELLCHECK_IGNORE, SfxCallMode::SYNCHRON, { &aIgnoreString }); + } + else if (nId == m_nIgnoreWordId) + { + SfxStringItem aIgnoreString(FN_PARAM_1, m_bGrammarResults ? OUString("Grammar") : OUString("Spelling")); + m_pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(SID_SPELLCHECK_IGNORE_ALL, SfxCallMode::SYNCHRON, { &aIgnoreString }); + } + else if ((MN_DICTIONARIES_START <= nId && nId <= MN_DICTIONARIES_END) || nId == m_nAddId) + { + OUString sWord( m_xSpellAlt->getWord() ); + OUString aDicName; + + if (MN_DICTIONARIES_START <= nId && nId <= MN_DICTIONARIES_END) + { + PopupMenu *pMenu = m_xPopupMenu->GetPopupMenu(m_nAddMenuId); + aDicName = pMenu->GetItemText(nId); + } + else + aDicName = m_aDicNameSingle; + + uno::Reference< linguistic2::XDictionary > xDic; + uno::Reference< linguistic2::XSearchableDictionaryList > xDicList( LinguMgr::GetDictionaryList() ); + if (xDicList.is()) + xDic = xDicList->getDictionaryByName( aDicName ); + + if (xDic.is()) + { + linguistic::DictionaryError nAddRes = linguistic::AddEntryToDic(xDic, sWord, false, OUString()); + // save modified user-dictionary if it is persistent + uno::Reference< frame::XStorable > xSavDic( xDic, uno::UNO_QUERY ); + if (xSavDic.is()) + xSavDic->store(); + + if (linguistic::DictionaryError::NONE != nAddRes && !xDic->getEntry(sWord).is()) + { + SvxDicError(m_pSh->GetView().GetFrameWeld(), nAddRes); + } + } + } + else if ( nId == MN_EXPLANATION_LINK && !m_sExplanationLink.isEmpty() ) + { + try + { + uno::Reference< css::system::XSystemShellExecute > xSystemShellExecute( + css::system::SystemShellExecute::create( ::comphelper::getProcessComponentContext() ) ); + xSystemShellExecute->execute( m_sExplanationLink, OUString(), + css::system::SystemShellExecuteFlags::URIS_ONLY ); + } + catch (const uno::Exception&) + { + uno::Any exc( ::cppu::getCaughtException() ); + OUString msg( ::comphelper::anyToString( exc ) ); + const SolarMutexGuard guard; + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_pSh->GetView().GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, msg)); + xBox->set_title("Explanations"); + xBox->run(); + } + } + else if (nId == m_nRedlineAcceptId || nId == m_nRedlineRejectId + || nId == m_nRedlineNextId || nId == m_nRedlinePrevId) + { + if (nId == m_nRedlineAcceptId) + nId = FN_REDLINE_ACCEPT_DIRECT; + else if (nId == m_nRedlineRejectId) + nId = FN_REDLINE_REJECT_DIRECT; + else if (nId == m_nRedlineNextId) + nId = FN_REDLINE_NEXT_CHANGE; + else if (nId == m_nRedlinePrevId) + nId = FN_REDLINE_PREV_CHANGE; + // Let SwView::Execute() handle the redline actions. + SfxRequest aReq(m_pSh->GetView().GetViewFrame(), nId); + m_pSh->GetView().Execute(aReq); + } + else + { + if (MN_SET_LANGUAGE_SELECTION_START <= nId && nId <= MN_SET_LANGUAGE_SELECTION_END) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Current_" + m_aLangTable_Text[nId]); + m_pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_SELECTION_NONE) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Current_LANGUAGE_NONE"); + m_pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_SELECTION_RESET) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Current_RESET_LANGUAGES"); + m_pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_SELECTION_MORE) + { + SfxStringItem aDlgString(FN_PARAM_1, "font"); + m_pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(SID_CHAR_DLG, SfxCallMode::SYNCHRON, { &aDlgString }); + } + else if (MN_SET_LANGUAGE_PARAGRAPH_START <= nId && nId <= MN_SET_LANGUAGE_PARAGRAPH_END) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Paragraph_" + m_aLangTable_Paragraph[nId]); + m_pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_PARA_NONE) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Paragraph_LANGUAGE_NONE"); + m_pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_PARA_RESET) + { + SfxStringItem aLangString(SID_LANGUAGE_STATUS, "Paragraph_RESET_LANGUAGES"); + m_pSh->GetView().GetViewFrame().GetDispatcher()->ExecuteList(SID_LANGUAGE_STATUS, SfxCallMode::SYNCHRON, { &aLangString }); + } + else if (nId == MN_SET_PARA_MORE) + { + m_pSh->GetView().GetViewFrame().GetDispatcher()->Execute( SID_CHAR_DLG_FOR_PARAGRAPH ); + } + } + + m_pSh->EnterStdMode(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/lingu/sdrhhcwrap.cxx b/sw/source/uibase/lingu/sdrhhcwrap.cxx new file mode 100644 index 0000000000..6df5911fd2 --- /dev/null +++ b/sw/source/uibase/lingu/sdrhhcwrap.cxx @@ -0,0 +1,165 @@ +/* -*- 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 <svx/svdotext.hxx> +#include <svx/svdpagv.hxx> +#include <sfx2/printer.hxx> +#include <svx/svdview.hxx> +#include <osl/diagnose.h> +#include <drawdoc.hxx> +#include "sdrhhcwrap.hxx" +#include <docsh.hxx> +#include <wrtsh.hxx> +#include <view.hxx> +#include <dcontact.hxx> +#include <doc.hxx> +#include <IDocumentDeviceAccess.hxx> +#include <IDocumentDrawModelAccess.hxx> +#include <edtwin.hxx> + +using namespace ::com::sun::star; + +SdrHHCWrapper::SdrHHCWrapper( SwView* pVw, + LanguageType nSourceLanguage, LanguageType nTargetLanguage, + const vcl::Font* pTargetFnt, + sal_Int32 nConvOptions, + bool bInteractive ) : + SdrOutliner(pVw->GetDocShell()->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()-> + GetDrawOutliner().GetEmptyItemSet().GetPool(), + OutlinerMode::TextObject ), + m_pView( pVw ), + m_pTextObj( nullptr ), + m_nOptions( nConvOptions ), + m_nDocIndex( 0 ), + m_nSourceLang( nSourceLanguage ), + m_nTargetLang( nTargetLanguage ), + m_pTargetFont( pTargetFnt ), + m_bIsInteractive( bInteractive ) +{ + SetRefDevice( m_pView->GetDocShell()->GetDoc()->getIDocumentDeviceAccess().getPrinter( false ) ); + + MapMode aMapMode (MapUnit::MapTwip); + SetRefMapMode(aMapMode); + + Size aSize( 1, 1 ); + SetPaperSize( aSize ); + + m_pOutlView.reset( new OutlinerView( this, &(m_pView->GetEditWin()) ) ); + m_pOutlView->GetOutliner()->SetRefDevice(m_pView->GetWrtShell().getIDocumentDeviceAccess().getPrinter( false )); + + // Hack: all SdrTextObj attributes should be transferred to EditEngine + m_pOutlView->SetBackgroundColor( COL_WHITE ); + + InsertView( m_pOutlView.get() ); + Point aPoint( 0, 0 ); + tools::Rectangle aRect( aPoint, aSize ); + m_pOutlView->SetOutputArea( aRect ); +// SetText( NULL ); + ClearModifyFlag(); +} + +SdrHHCWrapper::~SdrHHCWrapper() +{ + if (m_pTextObj) + { + SdrView *pSdrView = m_pView->GetWrtShell().GetDrawView(); + OSL_ENSURE( pSdrView, "SdrHHCWrapper without DrawView?" ); + pSdrView->SdrEndTextEdit( true ); + SetUpdateLayout(false); + m_pOutlView->SetOutputArea( tools::Rectangle( Point(), Size(1, 1) ) ); + } + RemoveView( m_pOutlView.get() ); + m_pOutlView.reset(); +} + +void SdrHHCWrapper::StartTextConversion() +{ + m_pOutlView->StartTextConversion(m_pView->GetFrameWeld(), m_nSourceLang, m_nTargetLang, m_pTargetFont, m_nOptions, m_bIsInteractive, true); +} + +bool SdrHHCWrapper::ConvertNextDocument() +{ + bool bNextDoc = false; + + if ( m_pTextObj ) + { + SdrView *pSdrView = m_pView->GetWrtShell().GetDrawView(); + OSL_ENSURE( pSdrView, "SdrHHCWrapper without DrawView?" ); + pSdrView->SdrEndTextEdit( true ); + SetUpdateLayout(false); + m_pOutlView->SetOutputArea( tools::Rectangle( Point(), Size(1, 1) ) ); + SetPaperSize( Size(1, 1) ); + Clear(); + m_pTextObj = nullptr; + } + + const auto n = m_nDocIndex; + + std::list<SdrTextObj*> aTextObjs; + SwDrawContact::GetTextObjectsFromFormat(aTextObjs, *m_pView->GetDocShell()->GetDoc()); + for (auto const& textObj : aTextObjs) + { + m_pTextObj = textObj; + if (textObj) + { + OutlinerParaObject* pParaObj = textObj->GetOutlinerParaObject(); + if ( pParaObj ) + { + SetPaperSize( textObj->GetLogicRect().GetSize() ); + SetText( *pParaObj ); + + ClearModifyFlag(); + + //!! update mode needs to be set to true otherwise + //!! the call to 'HasConvertibleTextPortion' will not always + //!! work correctly because the document may not be properly + //!! formatted when some information is accessed, and thus + //!! incorrect results get returned. + SetUpdateLayout(true); + if (HasConvertibleTextPortion( m_nSourceLang )) + { + SdrView *pSdrView = m_pView->GetWrtShell().GetDrawView(); + OSL_ENSURE( pSdrView, "SdrHHCWrapper without DrawView?" ); + SdrPageView* pPV = pSdrView->GetSdrPageView(); + m_nDocIndex = n; + bNextDoc = true; + m_pOutlView->SetOutputArea( tools::Rectangle( Point(), Size(1,1))); + SetPaperSize( m_pTextObj->GetLogicRect().GetSize() ); + SetUpdateLayout(true); + m_pView->GetWrtShell().MakeVisible(SwRect(m_pTextObj->GetLogicRect())); + + pSdrView->SdrBeginTextEdit(m_pTextObj, pPV, &m_pView->GetEditWin(), false, this, m_pOutlView.get(), true, true); + } + else + SetUpdateLayout(false); + } + + if ( !bNextDoc ) + m_pTextObj = nullptr; + else + break; + } + } + + ClearModifyFlag(); + + return bNextDoc; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/lingu/sdrhhcwrap.hxx b/sw/source/uibase/lingu/sdrhhcwrap.hxx new file mode 100644 index 0000000000..4965bb520e --- /dev/null +++ b/sw/source/uibase/lingu/sdrhhcwrap.hxx @@ -0,0 +1,53 @@ +/* -*- 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 . + */ +#pragma once + +#include <svx/svdoutl.hxx> + +class SwView; +class SdrTextObj; +class OutlinerView; + +class SdrHHCWrapper : public SdrOutliner +{ + // modified version of SdrSpeller + + SwView* m_pView; + SdrTextObj* m_pTextObj; + std::unique_ptr<OutlinerView> m_pOutlView; + sal_Int32 m_nOptions; + sal_uInt16 m_nDocIndex; + LanguageType m_nSourceLang; + LanguageType m_nTargetLang; + const vcl::Font* m_pTargetFont; + bool m_bIsInteractive; + +public: + SdrHHCWrapper( SwView* pVw, + LanguageType nSourceLanguage, LanguageType nTargetLanguage, + const vcl::Font* pTargetFnt, + sal_Int32 nConvOptions, bool bInteractive ); + + virtual ~SdrHHCWrapper() override; + + virtual bool ConvertNextDocument() override; + void StartTextConversion(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |