Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / uibase / shells / langhelper.cxx
blob23ea1abd6dfd9dc286b27d68d363326a6c7a29d7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <string.h>
22 #include <wrtsh.hxx>
23 #include <doc.hxx>
24 #include <docary.hxx>
25 #include <charfmt.hxx>
27 #include <sfx2/bindings.hxx>
28 #include <sfx2/request.hxx>
29 #include <sfx2/sfxdlg.hxx>
30 #include <sfx2/viewfrm.hxx>
31 #include <editeng/eeitem.hxx>
32 #include <editeng/editeng.hxx>
33 #include <editeng/editdata.hxx>
34 #include <editeng/outliner.hxx>
35 #include <editeng/editview.hxx>
36 #include <editeng/langitem.hxx>
38 #include <svl/languageoptions.hxx>
39 #include <svtools/langtab.hxx>
40 #include <svl/slstitm.hxx>
41 #include <svl/stritem.hxx>
42 #include <svx/svxids.hrc>
43 #include <osl/diagnose.h>
45 #include <ndtxt.hxx>
46 #include <pam.hxx>
47 #include <view.hxx>
48 #include <viewopt.hxx>
50 #include <langhelper.hxx>
52 using namespace ::com::sun::star;
54 namespace SwLangHelper
57 void GetLanguageStatus( OutlinerView* pOLV, SfxItemSet& rSet )
59 ESelection aSelection = pOLV->GetSelection();
60 EditView& rEditView=pOLV->GetEditView();
61 EditEngine* pEditEngine=rEditView.GetEditEngine();
63 // the value of used script types
64 const SvtScriptType nScriptType =pOLV->GetSelectedScriptType();
65 OUString aScriptTypesInUse( OUString::number( static_cast<int>(nScriptType) ) );//pEditEngine->GetScriptType(aSelection)
67 // get keyboard language
68 OUString aKeyboardLang;
69 LanguageType nLang = rEditView.GetInputLanguage();
70 if (nLang != LANGUAGE_DONTKNOW && nLang != LANGUAGE_SYSTEM)
71 aKeyboardLang = SvtLanguageTable::GetLanguageString( nLang );
73 // get the language that is in use
74 OUString aCurrentLang("*");
75 SfxItemSet aSet(pOLV->GetAttribs());
76 nLang = SwLangHelper::GetCurrentLanguage( aSet,nScriptType );
77 if (nLang != LANGUAGE_DONTKNOW)
78 aCurrentLang = SvtLanguageTable::GetLanguageString( nLang );
80 // build sequence for status value
81 uno::Sequence< OUString > aSeq{ aCurrentLang,
82 aScriptTypesInUse,
83 aKeyboardLang,
84 SwLangHelper::GetTextForLanguageGuessing( pEditEngine,
85 aSelection ) };
87 // set sequence as status value
88 SfxStringListItem aItem( SID_LANGUAGE_STATUS );
89 aItem.SetStringList( aSeq );
90 rSet.Put( aItem );
93 bool SetLanguageStatus( OutlinerView* pOLV, SfxRequest &rReq, SwView const &rView, SwWrtShell &rSh )
95 bool bRestoreSelection = false;
96 SfxItemSet aEditAttr(pOLV->GetAttribs());
97 ESelection aSelection = pOLV->GetSelection();
98 EditView & rEditView = pOLV->GetEditView();
99 EditEngine * pEditEngine = rEditView.GetEditEngine();
101 // get the language
102 OUString aNewLangText;
104 const SfxStringItem* pItem = rReq.GetArg<SfxStringItem>(SID_LANGUAGE_STATUS);
105 if (pItem)
106 aNewLangText = pItem->GetValue();
108 //!! Remember the view frame right now...
109 //!! (call to GetView().GetViewFrame() will break if the
110 //!! SwTextShell got destroyed meanwhile.)
111 SfxViewFrame& rViewFrame = rView.GetViewFrame();
113 if (aNewLangText == "*" )
115 // open the dialog "Tools/Options/Language Settings - Language"
116 SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
117 ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateVclDialog( rView.GetFrameWeld(), SID_LANGUAGE_OPTIONS ));
118 pDlg->Execute();
120 else
122 // setting the new language...
123 if (!aNewLangText.isEmpty())
125 static const OUStringLiteral aSelectionLangPrefix(u"Current_");
126 static const OUStringLiteral aParagraphLangPrefix(u"Paragraph_");
127 static const OUStringLiteral aDocumentLangPrefix(u"Default_");
129 sal_Int32 nPos = 0;
130 bool bForSelection = true;
131 bool bForParagraph = false;
132 if (-1 != (nPos = aNewLangText.indexOf( aSelectionLangPrefix )))
134 // ... for the current selection
135 aNewLangText = aNewLangText.replaceAt(nPos, aSelectionLangPrefix.getLength(), u"");
136 bForSelection = true;
138 else if (-1 != (nPos = aNewLangText.indexOf( aParagraphLangPrefix )))
140 // ... for the current paragraph language
141 aNewLangText = aNewLangText.replaceAt(nPos, aParagraphLangPrefix.getLength(), u"");
142 bForSelection = true;
143 bForParagraph = true;
145 else if (-1 != (nPos = aNewLangText.indexOf( aDocumentLangPrefix )))
147 // ... as default document language
148 aNewLangText = aNewLangText.replaceAt(nPos, aDocumentLangPrefix.getLength(), u"");
149 bForSelection = false;
152 if (bForParagraph)
154 bRestoreSelection = true;
155 SwLangHelper::SelectPara( rEditView, aSelection );
156 aSelection = pOLV->GetSelection();
158 if (!bForSelection) // document language to be changed...
160 rSh.StartAction();
161 rSh.LockView( true );
162 rSh.Push();
164 // prepare to apply new language to all text in document
165 rSh.SelAll();
166 rSh.ExtendedSelectAll();
169 if (aNewLangText == "LANGUAGE_NONE")
170 SwLangHelper::SetLanguage_None( rSh, pOLV, aSelection, bForSelection, aEditAttr );
171 else if (aNewLangText == "RESET_LANGUAGES")
172 SwLangHelper::ResetLanguages( rSh, pOLV );
173 else
174 SwLangHelper::SetLanguage( rSh, pOLV, aSelection, aNewLangText, bForSelection, aEditAttr );
176 // ugly hack, as it seems that EditView/EditEngine does not update their spellchecking marks
177 // when setting a new language attribute
178 if (bForSelection)
180 if (SwWrtShell* pWrtShell = rView.GetWrtShellPtr())
182 const SwViewOption* pVOpt = pWrtShell->GetViewOptions();
183 EEControlBits nCntrl = pEditEngine->GetControlWord();
184 // turn off
185 nCntrl &= ~EEControlBits::ONLINESPELLING;
186 pEditEngine->SetControlWord(nCntrl);
188 //turn back on
189 if (pVOpt->IsOnlineSpell())
190 nCntrl |= EEControlBits::ONLINESPELLING;
191 else
192 nCntrl &= ~EEControlBits::ONLINESPELLING;
193 pEditEngine->SetControlWord(nCntrl);
195 pEditEngine->CompleteOnlineSpelling();
196 rEditView.Invalidate();
200 if (!bForSelection)
202 // need to release view and restore selection...
203 rSh.Pop(SwCursorShell::PopMode::DeleteCurrent);
204 rSh.LockView( false );
205 rSh.EndAction();
210 // invalidate slot to get the new language displayed
211 rViewFrame.GetBindings().Invalidate( rReq.GetSlot() );
213 rReq.Done();
214 return bRestoreSelection;
217 void SetLanguage( SwWrtShell &rWrtSh, std::u16string_view rLangText, bool bIsForSelection, SfxItemSet &rCoreSet )
219 SetLanguage( rWrtSh, nullptr , ESelection(), rLangText, bIsForSelection, rCoreSet );
222 void SetLanguage( SwWrtShell &rWrtSh, OutlinerView const * pOLV, const ESelection& rSelection, std::u16string_view rLangText, bool bIsForSelection, SfxItemSet &rCoreSet )
224 const LanguageType nLang = SvtLanguageTable::GetLanguageType( rLangText );
225 if (nLang == LANGUAGE_DONTKNOW)
226 return;
228 EditEngine* pEditEngine = pOLV ? pOLV->GetEditView().GetEditEngine() : nullptr;
229 OSL_ENSURE( !pOLV || pEditEngine, "OutlinerView without EditEngine???" );
231 //get ScriptType
232 sal_uInt16 nLangWhichId = 0;
233 bool bIsSingleScriptType = true;
234 switch (SvtLanguageOptions::GetScriptTypeOfLanguage( nLang ))
236 case SvtScriptType::LATIN : nLangWhichId = pEditEngine ? sal_uInt16(EE_CHAR_LANGUAGE) : sal_uInt16(RES_CHRATR_LANGUAGE); break;
237 case SvtScriptType::ASIAN : nLangWhichId = pEditEngine ? sal_uInt16(EE_CHAR_LANGUAGE_CJK) : sal_uInt16(RES_CHRATR_CJK_LANGUAGE); break;
238 case SvtScriptType::COMPLEX : nLangWhichId = pEditEngine ? sal_uInt16(EE_CHAR_LANGUAGE_CTL) : sal_uInt16(RES_CHRATR_CTL_LANGUAGE); break;
239 default:
240 bIsSingleScriptType = false;
241 OSL_FAIL("unexpected case" );
243 if (!bIsSingleScriptType)
244 return;
246 // change language for selection or paragraph
247 // (for paragraph is handled by previously having set the selection to the
248 // whole paragraph)
249 if (bIsForSelection)
251 // apply language to current selection
252 if (pEditEngine)
254 rCoreSet.Put( SvxLanguageItem( nLang, nLangWhichId ));
255 pEditEngine->QuickSetAttribs(rCoreSet, rSelection);
257 else
259 rWrtSh.GetCurAttr( rCoreSet );
260 rCoreSet.Put( SvxLanguageItem( nLang, nLangWhichId ));
261 rWrtSh.SetAttrSet( rCoreSet );
264 else // change language for all text
266 // set document default language
267 switch (nLangWhichId)
269 case EE_CHAR_LANGUAGE : nLangWhichId = RES_CHRATR_LANGUAGE; break;
270 case EE_CHAR_LANGUAGE_CJK : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break;
271 case EE_CHAR_LANGUAGE_CTL : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break;
273 //Set the default document language
274 rWrtSh.SetDefault( SvxLanguageItem( nLang, nLangWhichId ) );
276 //Resolves: fdo#35282 Clear the language from all Text Styles, and
277 //fallback to default document language
278 const SwTextFormatColls *pColls = rWrtSh.GetDoc()->GetTextFormatColls();
279 for(size_t i = 0, nCount = pColls->size(); i < nCount; ++i)
281 SwTextFormatColl &rTextColl = *(*pColls)[ i ];
282 rTextColl.ResetFormatAttr(nLangWhichId);
284 //Resolves: fdo#35282 Clear the language from all Character Styles,
285 //and fallback to default document language
286 const SwCharFormats *pCharFormats = rWrtSh.GetDoc()->GetCharFormats();
287 for(size_t i = 0, nCount = pCharFormats->size(); i < nCount; ++i)
289 SwCharFormat &rCharFormat = *(*pCharFormats)[ i ];
290 rCharFormat.ResetFormatAttr(nLangWhichId);
293 // #i102191: hard set respective language attribute in text document
294 // (for all text in the document - which should be selected by now...)
295 rWrtSh.SetAttrItem( SvxLanguageItem( nLang, nLangWhichId ) );
299 void SetLanguage_None( SwWrtShell &rWrtSh, bool bIsForSelection, SfxItemSet &rCoreSet )
301 SetLanguage_None( rWrtSh,nullptr,ESelection(),bIsForSelection,rCoreSet );
304 void SetLanguage_None( SwWrtShell &rWrtSh, OutlinerView const * pOLV, const ESelection& rSelection, bool bIsForSelection, SfxItemSet &rCoreSet )
306 // EditEngine IDs
307 const sal_uInt16 aLangWhichId_EE[3] =
309 EE_CHAR_LANGUAGE,
310 EE_CHAR_LANGUAGE_CJK,
311 EE_CHAR_LANGUAGE_CTL
314 // Writer IDs
315 const sal_uInt16 aLangWhichId_Writer[3] =
317 RES_CHRATR_LANGUAGE,
318 RES_CHRATR_CJK_LANGUAGE,
319 RES_CHRATR_CTL_LANGUAGE
322 if (bIsForSelection)
324 // change language for selection or paragraph
325 // (for paragraph is handled by previously having set the selection to the
326 // whole paragraph)
328 EditEngine* pEditEngine = pOLV ? pOLV->GetEditView().GetEditEngine() : nullptr;
329 OSL_ENSURE( !pOLV || pEditEngine, "OutlinerView without EditEngine???" );
330 if (pEditEngine)
332 for (sal_uInt16 i : aLangWhichId_EE)
333 rCoreSet.Put( SvxLanguageItem( LANGUAGE_NONE, i ));
334 pEditEngine->QuickSetAttribs(rCoreSet, rSelection);
336 else
338 rWrtSh.GetCurAttr( rCoreSet );
339 for (sal_uInt16 i : aLangWhichId_Writer)
340 rCoreSet.Put( SvxLanguageItem( LANGUAGE_NONE, i ));
341 rWrtSh.SetAttrSet( rCoreSet );
344 else // change language for all text
346 o3tl::sorted_vector<sal_uInt16> aAttribs;
347 for (sal_uInt16 i : aLangWhichId_Writer)
349 rWrtSh.SetDefault( SvxLanguageItem( LANGUAGE_NONE, i ) );
350 aAttribs.insert( i );
353 // set all language attributes to default
354 // (for all text in the document - which should be selected by now...)
355 rWrtSh.ResetAttr( aAttribs );
359 void ResetLanguages( SwWrtShell &rWrtSh, OutlinerView const * pOLV )
361 // reset language for current selection.
362 // The selection should already have been expanded to the whole paragraph or
363 // to all text in the document if those are the ranges where to reset
364 // the language attributes
366 if (pOLV)
368 EditView &rEditView = pOLV->GetEditView();
369 rEditView.RemoveAttribs( true, EE_CHAR_LANGUAGE );
370 rEditView.RemoveAttribs( true, EE_CHAR_LANGUAGE_CJK );
371 rEditView.RemoveAttribs( true, EE_CHAR_LANGUAGE_CTL );
373 else
375 rWrtSh.ResetAttr(
376 { RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE });
380 /// @returns : the language for the selected text that is set for the
381 /// specified attribute (script type).
382 /// If there are more than one languages used LANGUAGE_DONTKNOW will be returned.
383 /// @param nLangWhichId : one of
384 /// RES_CHRATR_LANGUAGE, RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CTL_LANGUAGE,
385 LanguageType GetLanguage( SwWrtShell &rSh, sal_uInt16 nLangWhichId )
387 SfxItemSet aSet( rSh.GetAttrPool(), nLangWhichId, nLangWhichId );
388 rSh.GetCurAttr( aSet );
390 return GetLanguage(aSet,nLangWhichId);
393 LanguageType GetLanguage( SfxItemSet const & aSet, sal_uInt16 nLangWhichId )
396 LanguageType nLang = LANGUAGE_SYSTEM;
398 const SfxPoolItem *pItem = nullptr;
399 SfxItemState nState = aSet.GetItemState( nLangWhichId, true, &pItem );
400 if (nState > SfxItemState::DEFAULT && pItem)
402 // the item is set and can be used
403 nLang = dynamic_cast<const SvxLanguageItem&>(*pItem).GetLanguage();
405 else if (nState == SfxItemState::DEFAULT)
407 // since the attribute is not set: retrieve the default value
408 nLang = dynamic_cast<const SvxLanguageItem&>(aSet.GetPool()->GetDefaultItem( nLangWhichId )).GetLanguage();
410 else if (nState == SfxItemState::DONTCARE)
412 // there is more than one language...
413 nLang = LANGUAGE_DONTKNOW;
415 OSL_ENSURE( nLang != LANGUAGE_SYSTEM, "failed to get the language?" );
417 return nLang;
420 /// @returns: the language in use for the selected text.
421 /// 'In use' means the language(s) matching the script type(s) of the
422 /// selected text. Or in other words, the language a spell checker would use.
423 /// If there is more than one language LANGUAGE_DONTKNOW will be returned.
424 LanguageType GetCurrentLanguage( SwWrtShell &rSh )
426 //set language attribute to use according to the script type
427 sal_uInt16 nLangWhichId = 0;
428 bool bIsSingleScriptType = true;
429 switch (rSh.GetScriptType())
431 case SvtScriptType::LATIN : nLangWhichId = RES_CHRATR_LANGUAGE; break;
432 case SvtScriptType::ASIAN : nLangWhichId = RES_CHRATR_CJK_LANGUAGE; break;
433 case SvtScriptType::COMPLEX : nLangWhichId = RES_CHRATR_CTL_LANGUAGE; break;
434 default: bIsSingleScriptType = false; break;
437 // get language according to the script type(s) in use
438 LanguageType nCurrentLang = LANGUAGE_SYSTEM;
439 if (bIsSingleScriptType)
440 nCurrentLang = GetLanguage( rSh, nLangWhichId );
441 else
443 // check if all script types are set to LANGUAGE_NONE and return
444 // that if this is the case. Otherwise, having multiple script types
445 // in use always means there are several languages in use...
446 const sal_uInt16 aScriptTypes[3] =
448 RES_CHRATR_LANGUAGE,
449 RES_CHRATR_CJK_LANGUAGE,
450 RES_CHRATR_CTL_LANGUAGE
452 nCurrentLang = LANGUAGE_NONE;
453 for (sal_uInt16 aScriptType : aScriptTypes)
455 LanguageType nTmpLang = GetLanguage( rSh, aScriptType );
456 if (nTmpLang != LANGUAGE_NONE)
458 nCurrentLang = LANGUAGE_DONTKNOW;
459 break;
463 OSL_ENSURE( nCurrentLang != LANGUAGE_SYSTEM, "failed to get the language?" );
465 return nCurrentLang;
468 /// @returns: the language in use for the selected text.
469 /// 'In use' means the language(s) matching the script type(s) of the
470 /// selected text. Or in other words, the language a spell checker would use.
471 /// If there is more than one language LANGUAGE_DONTKNOW will be returned.
472 LanguageType GetCurrentLanguage( SfxItemSet const & aSet, SvtScriptType nScriptType )
474 //set language attribute to use according to the script type
475 sal_uInt16 nLangWhichId = 0;
476 bool bIsSingleScriptType = true;
477 switch (nScriptType)
479 case SvtScriptType::LATIN : nLangWhichId = EE_CHAR_LANGUAGE; break;
480 case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break;
481 case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break;
482 default: bIsSingleScriptType = false;
485 // get language according to the script type(s) in use
486 LanguageType nCurrentLang = LANGUAGE_SYSTEM;
487 if (bIsSingleScriptType)
488 nCurrentLang = GetLanguage( aSet, nLangWhichId );
489 else
491 // check if all script types are set to LANGUAGE_NONE and return
492 // that if this is the case. Otherwise, having multiple script types
493 // in use always means there are several languages in use...
494 const sal_uInt16 aScriptTypes[3] =
496 EE_CHAR_LANGUAGE,
497 EE_CHAR_LANGUAGE_CJK,
498 EE_CHAR_LANGUAGE_CTL
500 nCurrentLang = LANGUAGE_NONE;
501 for (sal_uInt16 aScriptType : aScriptTypes)
503 LanguageType nTmpLang = GetLanguage( aSet, aScriptType );
504 if (nTmpLang != LANGUAGE_NONE)
506 nCurrentLang = LANGUAGE_DONTKNOW;
507 break;
511 OSL_ENSURE( nCurrentLang != LANGUAGE_SYSTEM, "failed to get the language?" );
513 return nCurrentLang;
516 OUString GetTextForLanguageGuessing( SwWrtShell const &rSh )
518 // string for guessing language
519 OUString aText;
520 SwPaM *pCursor = rSh.GetCursor();
521 SwTextNode *pNode = pCursor->GetPointNode().GetTextNode();
522 if (pNode)
524 aText = pNode->GetText();
525 if (!aText.isEmpty())
527 sal_Int32 nEnd = pCursor->GetPoint()->GetContentIndex();
528 // at most 100 chars to the left...
529 const sal_Int32 nStt = nEnd > 100 ? nEnd - 100 : 0;
530 // ... and 100 to the right of the cursor position
531 nEnd = aText.getLength() - nEnd > 100 ? nEnd + 100 : aText.getLength();
532 aText = aText.copy( nStt, nEnd - nStt );
535 return aText;
538 OUString GetTextForLanguageGuessing(EditEngine const * rEditEngine, const ESelection& rDocSelection)
540 // string for guessing language
542 // get the full text of the paragraph that the end of selection is in
543 OUString aText = rEditEngine->GetText(rDocSelection.nEndPos);
544 if (!aText.isEmpty())
546 sal_Int32 nStt = 0;
547 sal_Int32 nEnd = rDocSelection.nEndPos;
548 // at most 100 chars to the left...
549 nStt = nEnd > 100 ? nEnd - 100 : 0;
550 // ... and 100 to the right of the cursor position
551 nEnd = aText.getLength() - nEnd > 100 ? nEnd + 100 : aText.getLength();
552 aText = aText.copy( nStt, nEnd - nStt );
555 return aText;
558 void SelectPara( EditView &rEditView, const ESelection &rCurSel )
560 ESelection aParaSel( rCurSel.nStartPara, 0, rCurSel.nStartPara, EE_TEXTPOS_ALL );
561 rEditView.SetSelection( aParaSel );
564 void SelectCurrentPara( SwWrtShell &rWrtSh )
566 // select current para
567 if (!rWrtSh.IsSttPara())
568 rWrtSh.MovePara( GoCurrPara, fnParaStart );
569 if (!rWrtSh.HasMark())
570 rWrtSh.SetMark();
571 rWrtSh.SwapPam();
572 if (!rWrtSh.IsEndPara())
573 rWrtSh.MovePara( GoCurrPara, fnParaEnd );
577 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */