bump product version to 5.0.4.1
[LibreOffice.git] / editeng / source / editeng / textconv.cxx
blob060fd80c76c1effd90600e6d912523d4fec7b1b3
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 .
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,
49 sal_Int32 nOptions,
50 bool bIsInteractive,
51 bool bIsStart,
52 EditView* pView ) :
53 HangulHanjaConversion( pWindow, rxContext, rSourceLocale, rTargetLocale, pTargetFont, nOptions, bIsInteractive )
54 , m_nConvTextLang(LANGUAGE_NONE)
55 , m_nUnitOffset(0)
56 , m_nLastPos(0)
57 , m_aConvSel(pView->GetSelection())
58 , m_pEditView(pView)
59 , m_pWin(pWindow)
60 , m_bStartChk(false)
61 , m_bStartDone(bIsStart)
62 , m_bEndDone(false)
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
80 if( m_bStartChk )
81 m_bStartDone = true;
82 else
83 m_bEndDone = true;
85 if ( m_bStartDone && m_bEndDone )
87 if ( ConvMore_impl() ) // examine another document?
89 m_bStartDone = true;
90 m_bEndDone = false;
91 ConvStart_impl( SVX_SPELL_BODY );
92 return true;
94 return false;
98 if ( m_bStartDone && m_bEndDone )
100 if ( ConvMore_impl() ) // examine another document?
102 m_bStartDone = true;
103 m_bEndDone = false;
104 ConvStart_impl( SVX_SPELL_BODY );
105 return true;
108 else if (!m_aConvSel.HasRange())
110 m_bStartChk = !m_bStartDone;
111 ConvStart_impl( m_bStartChk ? SVX_SPELL_BODY_START : SVX_SPELL_BODY_END );
112 return true;
115 return false;
119 bool TextConvWrapper::FindConvText_impl()
121 // modified version of SvxSpellWrapper::FindSpellError
123 bool bFound = false;
125 m_pWin->EnterWait();
126 bool bConvert = true;
128 while ( bConvert )
130 bFound = ConvContinue_impl();
131 if (bFound)
133 bConvert = false;
135 else
137 bConvert = ConvNext_impl();
140 m_pWin->LeaveWait();
141 return bFound;
145 bool TextConvWrapper::ConvMore_impl()
147 // modified version of SvxSpellWrapper::SpellMore
149 bool bMore = false;
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();
156 if ( bMore )
158 // The text has been entered in this engine ...
159 m_pEditView->GetImpEditView()->SetEditSelection(
160 pEE->GetEditDoc().GetStartPaM() );
163 return bMore;
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
178 if ( m_bEndDone )
180 pConvInfo->bConvToEnd = false;
181 pConvInfo->aConvTo = pConvInfo->aConvStart;
182 pConvInfo->aConvContinue = EPaM( 0, 0 );
183 m_pEditView->GetImpEditView()->SetEditSelection(
184 pEE->GetEditDoc().GetStartPaM() );
186 else
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;
204 else
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() );
218 else
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
230 m_aConvText.clear();
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?" );
252 if (pFont)
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" );
277 if (!bOK)
278 return;
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;
296 FindConvText_impl();
297 rNextPortion = m_aConvText;
298 rLangOfPortion = m_nConvTextLang;
299 m_nUnitOffset = 0;
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 );
317 #ifdef DBG_UTIL
318 namespace
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));
327 #endif
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" );
339 if (!bOK)
340 return;
342 // select current unit
343 SelectNewUnit_impl( nUnitStart, nUnitEnd );
345 OUString aOrigTxt( m_pEditView->GetSelected() );
346 OUString aNewTxt( rReplaceWith );
347 switch (eAction)
349 case eExchange :
350 break;
351 case eReplacementBracketed :
352 aNewTxt = aOrigTxt + "(" + rReplaceWith + ")";
353 break;
354 case eOriginalBracketed :
355 aNewTxt = rReplaceWith + "(" + aOrigTxt + ")";
356 break;
357 case eReplacementAbove :
358 case eOriginalAbove :
359 case eReplacementBelow :
360 case eOriginalBelow :
361 OSL_FAIL( "Rubies not supported" );
362 break;
363 default:
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();
373 #ifdef DBG_UTIL
374 LanguageType nOldLang = pImpEditEng->GetLanguage( pImpEditEng->CreateSel( aOldSel ).Min() );
375 #endif
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 );
385 else
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();
412 if (nDelta != 0)
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())
435 return;
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();
447 sal_Int32 nPos = 0;
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)
460 while (true)
462 // get index in original text that matches nPos in new text
463 sal_Int32 nIndex;
464 if (nPos < nConvTextLen)
465 nIndex = nPos < nIndices ? pIndices[nPos] : nPos;
466 else
468 nPos = nConvTextLen;
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;
496 nChgPos = -1;
497 nConvChgPos = -1;
500 else
502 // begin of non-matching char sequence found ?
503 if (nChgPos<0 && nConvChgPos<0)
505 nChgPos = nIndex;
506 nConvChgPos = nPos;
509 if (nPos >= nConvTextLen)
510 break;
511 ++nPos;
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;
519 else
521 ChangeText_impl( rNewText, false );
526 void TextConvWrapper::ChangeText_impl( const OUString &rNewText, bool bKeepAttributes )
528 if (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
540 // was deleted)
541 m_pEditView->RemoveAttribs();
542 // apply saved attributes to new inserted text
543 m_pEditView->SetAttribs( aSet );
545 else
547 m_pEditView->InsertText( rNewText );
552 void TextConvWrapper::Convert()
554 m_bStartChk = false;
555 ConvStart_impl( SVX_SPELL_BODY_END );
556 ConvertDocument();
560 bool TextConvWrapper::HasRubySupport() const
562 return false;
567 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */