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 .
21 #include <vcl/wrkwin.hxx>
22 #include <vcl/dialog.hxx>
23 #include <vcl/msgbox.hxx>
24 #include <vcl/svapp.hxx>
26 #include <impedit.hxx>
27 #include <editeng/editview.hxx>
28 #include <editeng/editeng.hxx>
29 #include <editeng/unolingu.hxx>
30 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
31 #include <com/sun/star/lang/Locale.hpp>
32 #include <editeng/langitem.hxx>
33 #include <editeng/fontitem.hxx>
34 #include <textconv.hxx>
37 using namespace com::sun::star
;
38 using namespace com::sun::star::uno
;
39 using namespace com::sun::star::beans
;
40 using namespace com::sun::star::linguistic2
;
44 TextConvWrapper::TextConvWrapper( vcl::Window
* pWindow
,
45 const Reference
< XComponentContext
>& rxContext
,
46 const lang::Locale
& rSourceLocale
,
47 const lang::Locale
& rTargetLocale
,
48 const vcl::Font
* pTargetFont
,
53 HangulHanjaConversion( pWindow
, rxContext
, rSourceLocale
, rTargetLocale
, pTargetFont
, nOptions
, bIsInteractive
)
54 , m_nConvTextLang(LANGUAGE_NONE
)
57 , m_aConvSel(pView
->GetSelection())
61 , m_bStartDone(bIsStart
)
63 , m_bAllowChange(false)
65 DBG_ASSERT( pWindow
, "TextConvWrapper: window missing" );
67 m_aConvSel
.Adjust(); // make Start <= End
71 TextConvWrapper::~TextConvWrapper()
76 bool TextConvWrapper::ConvNext_impl()
78 // modified version of SvxSpellWrapper::SpellNext
85 if ( m_bStartDone
&& m_bEndDone
)
87 if ( ConvMore_impl() ) // examine another document?
91 ConvStart_impl( SVX_SPELL_BODY
);
98 if ( m_bStartDone
&& m_bEndDone
)
100 if ( ConvMore_impl() ) // examine another document?
104 ConvStart_impl( SVX_SPELL_BODY
);
108 else if (!m_aConvSel
.HasRange())
110 m_bStartChk
= !m_bStartDone
;
111 ConvStart_impl( m_bStartChk
? SVX_SPELL_BODY_START
: SVX_SPELL_BODY_END
);
119 bool TextConvWrapper::FindConvText_impl()
121 // modified version of SvxSpellWrapper::FindSpellError
126 bool bConvert
= true;
130 bFound
= ConvContinue_impl();
137 bConvert
= ConvNext_impl();
145 bool TextConvWrapper::ConvMore_impl()
147 // modified version of SvxSpellWrapper::SpellMore
150 EditEngine
* pEE
= m_pEditView
->GetEditEngine();
151 ImpEditEngine
* pImpEE
= m_pEditView
->GetImpEditEngine();
152 ConvInfo
* pConvInfo
= pImpEE
->GetConvInfo();
153 if ( pConvInfo
->bMultipleDoc
)
155 bMore
= pEE
->ConvertNextDocument();
158 // The text has been entered in this engine ...
159 m_pEditView
->GetImpEditView()->SetEditSelection(
160 pEE
->GetEditDoc().GetStartPaM() );
167 void TextConvWrapper::ConvStart_impl( SvxSpellArea eArea
)
169 // modified version of EditSpellWrapper::SpellStart
171 EditEngine
* pEE
= m_pEditView
->GetEditEngine();
172 ImpEditEngine
* pImpEE
= m_pEditView
->GetImpEditEngine();
173 ConvInfo
* pConvInfo
= pImpEE
->GetConvInfo();
175 if ( eArea
== SVX_SPELL_BODY_START
)
177 // Is called when Spell-forward has reached the end, and to start over
180 pConvInfo
->bConvToEnd
= false;
181 pConvInfo
->aConvTo
= pConvInfo
->aConvStart
;
182 pConvInfo
->aConvContinue
= EPaM( 0, 0 );
183 m_pEditView
->GetImpEditView()->SetEditSelection(
184 pEE
->GetEditDoc().GetStartPaM() );
188 pConvInfo
->bConvToEnd
= true;
189 pConvInfo
->aConvTo
= pImpEE
->CreateEPaM(
190 pEE
->GetEditDoc().GetStartPaM() );
193 else if ( eArea
== SVX_SPELL_BODY_END
)
195 // Is called when Spell-forward starts
196 pConvInfo
->bConvToEnd
= true;
197 if (m_aConvSel
.HasRange())
199 // user selection: convert to end of selection
200 pConvInfo
->aConvTo
.nPara
= m_aConvSel
.nEndPara
;
201 pConvInfo
->aConvTo
.nIndex
= m_aConvSel
.nEndPos
;
202 pConvInfo
->bConvToEnd
= false;
206 // nothing selected: convert to end of document
207 pConvInfo
->aConvTo
= pImpEE
->CreateEPaM(
208 pEE
->GetEditDoc().GetEndPaM() );
211 else if ( eArea
== SVX_SPELL_BODY
)
213 // called by ConvNext_impl...
214 pConvInfo
->aConvContinue
= pConvInfo
->aConvStart
;
215 pConvInfo
->aConvTo
= pImpEE
->CreateEPaM(
216 pEE
->GetEditDoc().GetEndPaM() );
220 OSL_FAIL( "ConvStart_impl: Unknown Area!" );
225 bool TextConvWrapper::ConvContinue_impl()
227 // modified version of EditSpellWrapper::SpellContinue
229 // get next convertible text portion and its language
231 m_nConvTextLang
= LANGUAGE_NONE
;
232 m_pEditView
->GetImpEditEngine()->ImpConvert( m_aConvText
, m_nConvTextLang
,
233 m_pEditView
, GetSourceLanguage(), m_aConvSel
,
234 m_bAllowChange
, GetTargetLanguage(), GetTargetFont() );
235 return !m_aConvText
.isEmpty();
239 void TextConvWrapper::SetLanguageAndFont( const ESelection
&rESel
,
240 LanguageType nLang
, sal_uInt16 nLangWhichId
,
241 const vcl::Font
*pFont
, sal_uInt16 nFontWhichId
)
243 ESelection aOldSel
= m_pEditView
->GetSelection();
244 m_pEditView
->SetSelection( rESel
);
246 // set new language attribute
247 SfxItemSet
aNewSet( m_pEditView
->GetEmptyItemSet() );
248 aNewSet
.Put( SvxLanguageItem( nLang
, nLangWhichId
) );
250 // new font to be set?
251 DBG_ASSERT( pFont
, "target font missing?" );
254 // set new font attribute
255 SvxFontItem aFontItem
= static_cast<const SvxFontItem
&>( aNewSet
.Get( nFontWhichId
) );
256 aFontItem
.SetFamilyName( pFont
->GetName());
257 aFontItem
.SetFamily( pFont
->GetFamily());
258 aFontItem
.SetStyleName( pFont
->GetStyleName());
259 aFontItem
.SetPitch( pFont
->GetPitch());
260 aFontItem
.SetCharSet(pFont
->GetCharSet());
261 aNewSet
.Put( aFontItem
);
264 // apply new attributes
265 m_pEditView
->SetAttribs( aNewSet
);
267 m_pEditView
->SetSelection( aOldSel
);
271 void TextConvWrapper::SelectNewUnit_impl(
272 const sal_Int32 nUnitStart
,
273 const sal_Int32 nUnitEnd
)
275 const bool bOK
= 0 <= nUnitStart
&& 0 <= nUnitEnd
&& nUnitStart
<= nUnitEnd
;
276 DBG_ASSERT( bOK
, "invalid arguments" );
280 ESelection aSelection
= m_pEditView
->GetSelection();
281 DBG_ASSERT( aSelection
.nStartPara
== aSelection
.nEndPara
,
282 "paragraph mismatch in selection" );
283 aSelection
.nStartPos
= (m_nLastPos
+ m_nUnitOffset
+ nUnitStart
);
284 aSelection
.nEndPos
= (m_nLastPos
+ m_nUnitOffset
+ nUnitEnd
);
285 m_pEditView
->SetSelection( aSelection
);
289 void TextConvWrapper::GetNextPortion(
290 OUString
& /* [out] */ rNextPortion
,
291 LanguageType
& /* [out] */ rLangOfPortion
,
292 bool /* [in] */ _bAllowImplicitChangesForNotConvertibleText
)
294 m_bAllowChange
= _bAllowImplicitChangesForNotConvertibleText
;
297 rNextPortion
= m_aConvText
;
298 rLangOfPortion
= m_nConvTextLang
;
301 ESelection aSelection
= m_pEditView
->GetSelection();
302 DBG_ASSERT( aSelection
.nStartPara
== aSelection
.nEndPara
,
303 "paragraph mismatch in selection" );
304 DBG_ASSERT( aSelection
.nStartPos
<= aSelection
.nEndPos
,
305 "start pos > end pos" );
306 m_nLastPos
= aSelection
.nStartPos
;
310 void TextConvWrapper::HandleNewUnit(
311 const sal_Int32 nUnitStart
,
312 const sal_Int32 nUnitEnd
)
314 SelectNewUnit_impl( nUnitStart
, nUnitEnd
);
320 bool IsSimilarChinese( LanguageType nLang1
, LanguageType nLang2
)
322 using namespace editeng
;
323 return (HangulHanjaConversion::IsTraditional(nLang1
) && HangulHanjaConversion::IsTraditional(nLang2
)) ||
324 (HangulHanjaConversion::IsSimplified(nLang1
) && HangulHanjaConversion::IsSimplified(nLang2
));
329 void TextConvWrapper::ReplaceUnit(
330 const sal_Int32 nUnitStart
, const sal_Int32 nUnitEnd
,
331 const OUString
& rOrigText
,
332 const OUString
& rReplaceWith
,
333 const ::com::sun::star::uno::Sequence
< sal_Int32
> &rOffsets
,
334 ReplacementAction eAction
,
335 LanguageType
*pNewUnitLanguage
)
337 const bool bOK
= 0 <= nUnitStart
&& 0 <= nUnitEnd
&& nUnitStart
<= nUnitEnd
;
338 DBG_ASSERT( bOK
, "invalid arguments" );
342 // select current unit
343 SelectNewUnit_impl( nUnitStart
, nUnitEnd
);
345 OUString
aOrigTxt( m_pEditView
->GetSelected() );
346 OUString
aNewTxt( rReplaceWith
);
351 case eReplacementBracketed
:
352 aNewTxt
= aOrigTxt
+ "(" + rReplaceWith
+ ")";
354 case eOriginalBracketed
:
355 aNewTxt
= rReplaceWith
+ "(" + aOrigTxt
+ ")";
357 case eReplacementAbove
:
358 case eOriginalAbove
:
359 case eReplacementBelow
:
360 case eOriginalBelow
:
361 OSL_FAIL( "Rubies not supported" );
364 OSL_FAIL( "unexpected case" );
366 m_nUnitOffset
= m_nUnitOffset
+ nUnitStart
+ aNewTxt
.getLength();
368 // remember current original language for later use
369 ImpEditEngine
*pImpEditEng
= m_pEditView
->GetImpEditEngine();
370 ESelection aOldSel
= m_pEditView
->GetSelection();
371 //EditSelection aOldEditSel = pEditView->GetImpEditView()->GetEditSelection();
374 LanguageType nOldLang
= pImpEditEng
->GetLanguage( pImpEditEng
->CreateSel( aOldSel
).Min() );
377 pImpEditEng
->UndoActionStart( EDITUNDO_INSERT
);
379 // according to FT we should currently not bother about keeping
380 // attributes in Hangul/Hanja conversion and leave that untouched.
381 // Thus we do this only for Chinese translation...
382 bool bIsChineseConversion
= IsChinese( GetSourceLanguage() );
383 if (bIsChineseConversion
)
384 ChangeText( aNewTxt
, rOrigText
, &rOffsets
, &aOldSel
);
386 ChangeText( aNewTxt
, rOrigText
, NULL
, NULL
);
388 // change language and font if necessary
389 if (bIsChineseConversion
)
391 DBG_ASSERT( GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED
|| GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL
,
392 "TextConvWrapper::ReplaceUnit : unexpected target language" );
394 ESelection
aNewSel( aOldSel
);
395 aNewSel
.nStartPos
= aNewSel
.nStartPos
- aNewTxt
.getLength();
397 if (pNewUnitLanguage
)
399 DBG_ASSERT(!IsSimilarChinese( *pNewUnitLanguage
, nOldLang
),
400 "similar language should not be changed!");
401 SetLanguageAndFont( aNewSel
, *pNewUnitLanguage
, EE_CHAR_LANGUAGE_CJK
,
402 GetTargetFont(), EE_CHAR_FONTINFO_CJK
);
406 pImpEditEng
->UndoActionEnd( EDITUNDO_INSERT
);
408 // adjust ConvContinue / ConvTo if necessary
409 ImpEditEngine
* pImpEE
= m_pEditView
->GetImpEditEngine();
410 ConvInfo
* pConvInfo
= pImpEE
->GetConvInfo();
411 sal_Int32 nDelta
= aNewTxt
.getLength() - aOrigTxt
.getLength();
414 // Note: replacement is always done in the current paragraph
415 // which is the one ConvContinue points to
416 pConvInfo
->aConvContinue
.nIndex
= pConvInfo
->aConvContinue
.nIndex
+ nDelta
;
418 // if that is the same as the one where the conversions ends
419 // the end needs to be updated also
420 if (pConvInfo
->aConvTo
.nPara
== pConvInfo
->aConvContinue
.nPara
)
421 pConvInfo
->aConvTo
.nIndex
= pConvInfo
->aConvTo
.nIndex
+ nDelta
;
426 void TextConvWrapper::ChangeText( const OUString
&rNewText
,
427 const OUString
& rOrigText
,
428 const uno::Sequence
< sal_Int32
> *pOffsets
,
429 ESelection
*pESelection
)
431 //!! code is a modified copy of SwHHCWrapper::ChangeText from sw !!
433 DBG_ASSERT( !rNewText
.isEmpty(), "unexpected empty string" );
434 if (rNewText
.isEmpty())
437 if (pOffsets
&& pESelection
) // try to keep as much attributation as possible ?
439 pESelection
->Adjust();
441 // remember cursor start position for later setting of the cursor
442 const sal_Int32 nStartIndex
= pESelection
->nStartPos
;
444 const sal_Int32 nIndices
= pOffsets
->getLength();
445 const sal_Int32
*pIndices
= pOffsets
->getConstArray();
446 const sal_Int32 nConvTextLen
= rNewText
.getLength();
448 sal_Int32 nChgPos
= -1;
449 sal_Int32 nConvChgPos
= -1;
451 // offset to calculate the position in the text taking into
452 // account that text may have been replaced with new text of
453 // different length. Negative values allowed!
454 sal_Int32 nCorrectionOffset
= 0;
456 DBG_ASSERT(nIndices
== 0 || nIndices
== nConvTextLen
,
457 "mismatch between string length and sequence length!" );
459 // find all substrings that need to be replaced (and only those)
462 // get index in original text that matches nPos in new text
464 if (nPos
< nConvTextLen
)
465 nIndex
= nPos
< nIndices
? pIndices
[nPos
] : nPos
;
469 nIndex
= rOrigText
.getLength();
472 // end of string also terminates non-matching char sequence
473 if (nPos
== nConvTextLen
|| rOrigText
[nIndex
] == rNewText
[nPos
])
475 // substring that needs to be replaced found?
476 if (nChgPos
>=0 && nConvChgPos
>=0)
478 const sal_Int32 nChgLen
= nIndex
- nChgPos
;
479 const sal_Int32 nConvChgLen
= nPos
- nConvChgPos
;
480 OUString
aInNew( rNewText
.copy( nConvChgPos
, nConvChgLen
) );
482 // set selection to sub string to be replaced in original text
483 ESelection
aSel( *pESelection
);
484 sal_Int32 nChgInNodeStartIndex
= nStartIndex
+ nCorrectionOffset
+ nChgPos
;
485 aSel
.nStartPos
= nChgInNodeStartIndex
;
486 aSel
.nEndPos
= nChgInNodeStartIndex
+ nChgLen
;
487 m_pEditView
->SetSelection( aSel
);
489 // replace selected sub string with the corresponding
490 // sub string from the new text while keeping as
491 // much from the attributes as possible
492 ChangeText_impl( aInNew
, true );
494 nCorrectionOffset
+= nConvChgLen
- nChgLen
;
502 // begin of non-matching char sequence found ?
503 if (nChgPos
<0 && nConvChgPos
<0)
509 if (nPos
>= nConvTextLen
)
514 // set cursor to the end of the inserted text
515 // (as it would happen after ChangeText_impl (Delete and Insert)
516 // of the whole text in the 'else' branch below)
517 pESelection
->nStartPos
= pESelection
->nEndPos
= nStartIndex
+ nConvTextLen
;
521 ChangeText_impl( rNewText
, false );
526 void TextConvWrapper::ChangeText_impl( const OUString
&rNewText
, bool bKeepAttributes
)
530 // save attributes to be restored
531 SfxItemSet
aSet( m_pEditView
->GetAttribs() );
533 // replace old text and select new text
534 m_pEditView
->InsertText( rNewText
, true );
536 // since 'SetAttribs' below function like merging with the attributes
537 // from the itemset with any existing ones we have to get rid of all
538 // all attributes now. (Those attributes that may take effect left
539 // to the position where the new text gets inserted after the old text
541 m_pEditView
->RemoveAttribs();
542 // apply saved attributes to new inserted text
543 m_pEditView
->SetAttribs( aSet
);
547 m_pEditView
->InsertText( rNewText
);
552 void TextConvWrapper::Convert()
555 ConvStart_impl( SVX_SPELL_BODY_END
);
560 bool TextConvWrapper::HasRubySupport() const
567 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */