1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
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
,
84 SwLangHelper::GetTextForLanguageGuessing( pEditEngine
,
87 // set sequence as status value
88 SfxStringListItem
aItem( SID_LANGUAGE_STATUS
);
89 aItem
.SetStringList( aSeq
);
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();
102 OUString aNewLangText
;
104 const SfxStringItem
* pItem
= rReq
.GetArg
<SfxStringItem
>(SID_LANGUAGE_STATUS
);
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
));
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_");
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;
154 bRestoreSelection
= true;
155 SwLangHelper::SelectPara( rEditView
, aSelection
);
156 aSelection
= pOLV
->GetSelection();
158 if (!bForSelection
) // document language to be changed...
161 rSh
.LockView( true );
164 // prepare to apply new language to all text in document
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
);
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
180 if (SwWrtShell
* pWrtShell
= rView
.GetWrtShellPtr())
182 const SwViewOption
* pVOpt
= pWrtShell
->GetViewOptions();
183 EEControlBits nCntrl
= pEditEngine
->GetControlWord();
185 nCntrl
&= ~EEControlBits::ONLINESPELLING
;
186 pEditEngine
->SetControlWord(nCntrl
);
189 if (pVOpt
->IsOnlineSpell())
190 nCntrl
|= EEControlBits::ONLINESPELLING
;
192 nCntrl
&= ~EEControlBits::ONLINESPELLING
;
193 pEditEngine
->SetControlWord(nCntrl
);
195 pEditEngine
->CompleteOnlineSpelling();
196 rEditView
.Invalidate();
202 // need to release view and restore selection...
203 rSh
.Pop(SwCursorShell::PopMode::DeleteCurrent
);
204 rSh
.LockView( false );
210 // invalidate slot to get the new language displayed
211 rViewFrame
.GetBindings().Invalidate( rReq
.GetSlot() );
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
)
228 EditEngine
* pEditEngine
= pOLV
? pOLV
->GetEditView().GetEditEngine() : nullptr;
229 OSL_ENSURE( !pOLV
|| pEditEngine
, "OutlinerView without EditEngine???" );
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;
240 bIsSingleScriptType
= false;
241 OSL_FAIL("unexpected case" );
243 if (!bIsSingleScriptType
)
246 // change language for selection or paragraph
247 // (for paragraph is handled by previously having set the selection to the
251 // apply language to current selection
254 rCoreSet
.Put( SvxLanguageItem( nLang
, nLangWhichId
));
255 pEditEngine
->QuickSetAttribs(rCoreSet
, rSelection
);
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
)
307 const sal_uInt16 aLangWhichId_EE
[3] =
310 EE_CHAR_LANGUAGE_CJK
,
315 const sal_uInt16 aLangWhichId_Writer
[3] =
318 RES_CHRATR_CJK_LANGUAGE
,
319 RES_CHRATR_CTL_LANGUAGE
324 // change language for selection or paragraph
325 // (for paragraph is handled by previously having set the selection to the
328 EditEngine
* pEditEngine
= pOLV
? pOLV
->GetEditView().GetEditEngine() : nullptr;
329 OSL_ENSURE( !pOLV
|| pEditEngine
, "OutlinerView without EditEngine???" );
332 for (sal_uInt16 i
: aLangWhichId_EE
)
333 rCoreSet
.Put( SvxLanguageItem( LANGUAGE_NONE
, i
));
334 pEditEngine
->QuickSetAttribs(rCoreSet
, rSelection
);
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
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
);
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?" );
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
);
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] =
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
;
463 OSL_ENSURE( nCurrentLang
!= LANGUAGE_SYSTEM
, "failed to get the language?" );
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;
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
);
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] =
497 EE_CHAR_LANGUAGE_CJK
,
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
;
511 OSL_ENSURE( nCurrentLang
!= LANGUAGE_SYSTEM
, "failed to get the language?" );
516 OUString
GetTextForLanguageGuessing( SwWrtShell
const &rSh
)
518 // string for guessing language
520 SwPaM
*pCursor
= rSh
.GetCursor();
521 SwTextNode
*pNode
= pCursor
->GetPointNode().GetTextNode();
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
);
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())
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
);
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())
572 if (!rWrtSh
.IsEndPara())
573 rWrtSh
.MovePara( GoCurrPara
, fnParaEnd
);
577 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */