1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: textconv.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
33 #include <eeng_pch.hxx>
35 #include <impedit.hxx>
36 #include <svx/editview.hxx>
37 #include <svx/editeng.hxx>
38 #include <unolingu.hxx>
39 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
40 #include <com/sun/star/lang/Locale.hpp>
41 #include <svx/langitem.hxx>
42 #include <fontitem.hxx>
43 #include "textconv.hxx"
46 using ::rtl::OUString
;
47 using namespace com::sun::star
;
48 using namespace com::sun::star::uno
;
49 using namespace com::sun::star::beans
;
50 using namespace com::sun::star::lang
;
51 using namespace com::sun::star::linguistic2
;
53 #define C2U(cChar) OUString::createFromAscii(cChar)
55 //////////////////////////////////////////////////////////////////////
57 TextConvWrapper::TextConvWrapper( Window
* pWindow
,
58 const Reference
< XMultiServiceFactory
>& rxMSF
,
59 const Locale
& rSourceLocale
,
60 const Locale
& rTargetLocale
,
61 const Font
* pTargetFont
,
63 sal_Bool bIsInteractive
,
66 HangulHanjaConversion( pWindow
, rxMSF
, rSourceLocale
, rTargetLocale
, pTargetFont
, nOptions
, bIsInteractive
)
68 DBG_ASSERT( pWindow
, "TextConvWrapper: window missing" );
70 nConvTextLang
= LANGUAGE_NONE
;
73 bStartChk
= sal_False
;
74 bStartDone
= bIsStart
;
79 aConvSel
= pEditView
->GetSelection();
80 aConvSel
.Adjust(); // make Start <= End
82 bAllowChange
= sal_False
;
86 TextConvWrapper::~TextConvWrapper()
91 sal_Bool
TextConvWrapper::ConvNext_impl()
93 // modified version of SvxSpellWrapper::SpellNext
96 bStartDone
= sal_True
;
100 if ( bStartDone
&& bEndDone
)
102 if ( ConvMore_impl() ) // ein weiteres Dokument pruefen?
104 bStartDone
= sal_True
;
105 bEndDone
= sal_False
;
106 ConvStart_impl( SVX_SPELL_BODY
);
113 //ResMgr* pMgr = DIALOG_MGR();
114 sal_Bool bGoOn
= sal_False
;
116 if ( bStartDone
&& bEndDone
)
118 if ( ConvMore_impl() ) // ein weiteres Dokument pruefen?
120 bStartDone
= sal_True
;
121 bEndDone
= sal_False
;
122 ConvStart_impl( SVX_SPELL_BODY
);
128 // Ein BODY_Bereich erledigt, Frage nach dem anderen BODY_Bereich
132 sal_uInt16 nResId = bReverse ? RID_SVXQB_BW_CONTINUE : RID_SVXQB_CONTINUE;
133 QueryBox aBox( pWin, ResId( nResId, pMgr ) );
134 if ( aBox.Execute() != RET_YES )
136 // Verzicht auf den anderen Bereich, ggf. Frage nach Sonderbereich
138 bStartDone = bEndDone = sal_True;
139 return ConvNext_impl();
144 if (!aConvSel
.HasRange())
146 bStartChk
= !bStartDone
;
147 ConvStart_impl( bStartChk
? SVX_SPELL_BODY_START
: SVX_SPELL_BODY_END
);
159 sal_Bool
TextConvWrapper::FindConvText_impl()
161 // modified version of SvxSpellWrapper::FindSpellError
163 //ShowLanguageErrors();
165 sal_Bool bFound
= sal_False
;
168 sal_Bool bConvert
= sal_True
;
172 bFound
= ConvContinue_impl();
175 bConvert
= sal_False
;
180 bConvert
= ConvNext_impl();
188 sal_Bool
TextConvWrapper::ConvMore_impl()
190 // modified version of SvxSpellWrapper::SpellMore
192 sal_Bool bMore
= sal_False
;
193 ImpEditEngine
* pImpEE
= pEditView
->GetImpEditEngine();
194 ConvInfo
* pConvInfo
= pImpEE
->GetConvInfo();
195 if ( pConvInfo
->bMultipleDoc
)
197 bMore
= pImpEE
->GetEditEnginePtr()->ConvertNextDocument();
200 // Der Text wurde in diese Engine getreten...
201 pEditView
->GetImpEditView()->SetEditSelection(
202 pImpEE
->GetEditDoc().GetStartPaM() );
209 void TextConvWrapper::ConvStart_impl( SvxSpellArea eArea
)
211 // modified version of EditSpellWrapper::SpellStart
213 ImpEditEngine
* pImpEE
= pEditView
->GetImpEditEngine();
214 ConvInfo
* pConvInfo
= pImpEE
->GetConvInfo();
216 if ( eArea
== SVX_SPELL_BODY_START
)
218 // Wird gerufen, wenn Spell-Forwad am Ende angekomment ist
219 // und soll von vorne beginnen
222 pConvInfo
->bConvToEnd
= sal_False
;
223 pConvInfo
->aConvTo
= pConvInfo
->aConvStart
;
224 pConvInfo
->aConvContinue
= EPaM( 0, 0 );
225 pEditView
->GetImpEditView()->SetEditSelection(
226 pImpEE
->GetEditDoc().GetStartPaM() );
230 pConvInfo
->bConvToEnd
= sal_True
;
231 pConvInfo
->aConvTo
= pImpEE
->CreateEPaM(
232 pImpEE
->GetEditDoc().GetStartPaM() );
235 else if ( eArea
== SVX_SPELL_BODY_END
)
237 // Wird gerufen, wenn Spell-Forwad gestartet wird
238 pConvInfo
->bConvToEnd
= sal_True
;
239 if (aConvSel
.HasRange())
241 // user selection: convert to end of selection
242 pConvInfo
->aConvTo
.nPara
= aConvSel
.nEndPara
;
243 pConvInfo
->aConvTo
.nIndex
= aConvSel
.nEndPos
;
244 pConvInfo
->bConvToEnd
= sal_False
;
248 // nothing selected: convert to end of document
249 pConvInfo
->aConvTo
= pImpEE
->CreateEPaM(
250 pImpEE
->GetEditDoc().GetEndPaM() );
253 else if ( eArea
== SVX_SPELL_BODY
)
255 // called by ConvNext_impl...
256 pConvInfo
->aConvContinue
= pConvInfo
->aConvStart
;
257 pConvInfo
->aConvTo
= pImpEE
->CreateEPaM(
258 pImpEE
->GetEditDoc().GetEndPaM() );
259 // pSpellInfo->bSpellToEnd = sal_True;
263 DBG_ERROR( "ConvStart_impl: Unknown Area!" );
268 void TextConvWrapper::ConvEnd_impl()
273 sal_Bool
TextConvWrapper::ConvContinue_impl()
275 // modified version of EditSpellWrapper::SpellContinue
277 // get next convertible text portion and its language
278 aConvText
= rtl::OUString();
279 nConvTextLang
= LANGUAGE_NONE
;
280 pEditView
->GetImpEditEngine()->ImpConvert( aConvText
, nConvTextLang
,
281 pEditView
, GetSourceLanguage(), aConvSel
,
282 bAllowChange
, GetTargetLanguage(), GetTargetFont() );
283 return aConvText
.getLength() != 0;
287 void TextConvWrapper::SetLanguageAndFont( const ESelection
&rESel
,
288 LanguageType nLang
, USHORT nLangWhichId
,
289 const Font
*pFont
, USHORT nFontWhichId
)
291 ESelection aOldSel
= pEditView
->GetSelection();
292 pEditView
->SetSelection( rESel
);
294 // set new language attribute
295 SfxItemSet
aNewSet( pEditView
->GetEmptyItemSet() );
296 aNewSet
.Put( SvxLanguageItem( nLang
, nLangWhichId
) );
298 // new font to be set?
299 DBG_ASSERT( pFont
, "target font missing?" );
302 // set new font attribute
303 SvxFontItem aFontItem
= (SvxFontItem
&) aNewSet
.Get( nFontWhichId
);
304 aFontItem
.GetFamilyName() = pFont
->GetName();
305 aFontItem
.GetFamily() = pFont
->GetFamily();
306 aFontItem
.GetStyleName() = pFont
->GetStyleName();
307 aFontItem
.GetPitch() = pFont
->GetPitch();
308 aFontItem
.GetCharSet() = pFont
->GetCharSet();
309 aNewSet
.Put( aFontItem
);
312 // apply new attributes
313 pEditView
->SetAttribs( aNewSet
);
315 pEditView
->SetSelection( aOldSel
);
319 void TextConvWrapper::SelectNewUnit_impl(
320 const sal_Int32 nUnitStart
,
321 const sal_Int32 nUnitEnd
)
323 BOOL bOK
= 0 <= nUnitStart
&& 0 <= nUnitEnd
&& nUnitStart
<= nUnitEnd
;
324 DBG_ASSERT( bOK
, "invalid arguments" );
328 ESelection aSelection
= pEditView
->GetSelection();
329 DBG_ASSERT( aSelection
.nStartPara
== aSelection
.nEndPara
,
330 "paragraph mismatch in selection" );
331 aSelection
.nStartPos
= (USHORT
) (nLastPos
+ nUnitOffset
+ nUnitStart
);
332 aSelection
.nEndPos
= (USHORT
) (nLastPos
+ nUnitOffset
+ nUnitEnd
);
333 pEditView
->SetSelection( aSelection
);
337 void TextConvWrapper::GetNextPortion(
338 ::rtl::OUString
& /* [out] */ rNextPortion
,
339 LanguageType
& /* [out] */ rLangOfPortion
,
340 sal_Bool
/* [in] */ _bAllowImplicitChangesForNotConvertibleText
)
342 bAllowChange
= _bAllowImplicitChangesForNotConvertibleText
;
345 rNextPortion
= aConvText
;
346 rLangOfPortion
= nConvTextLang
;
349 ESelection aSelection
= pEditView
->GetSelection();
350 DBG_ASSERT( aSelection
.nStartPara
== aSelection
.nEndPara
,
351 "paragraph mismatch in selection" );
352 DBG_ASSERT( aSelection
.nStartPos
<= aSelection
.nEndPos
,
353 "start pos > end pos" );
354 nLastPos
= aSelection
.nStartPos
;
358 void TextConvWrapper::HandleNewUnit(
359 const sal_Int32 nUnitStart
,
360 const sal_Int32 nUnitEnd
)
362 SelectNewUnit_impl( nUnitStart
, nUnitEnd
);
366 void TextConvWrapper::ReplaceUnit(
367 const sal_Int32 nUnitStart
, const sal_Int32 nUnitEnd
,
368 const ::rtl::OUString
& rOrigText
,
369 const ::rtl::OUString
& rReplaceWith
,
370 const ::com::sun::star::uno::Sequence
< sal_Int32
> &rOffsets
,
371 ReplacementAction eAction
,
372 LanguageType
*pNewUnitLanguage
)
374 BOOL bOK
= 0 <= nUnitStart
&& 0 <= nUnitEnd
&& nUnitStart
<= nUnitEnd
;
375 DBG_ASSERT( bOK
, "invalid arguments" );
379 static OUString
aBracketedStart( C2U( "(" ) );
380 static OUString
aBracketedEnd( C2U( ")" ) );
382 // select current unit
383 SelectNewUnit_impl( nUnitStart
, nUnitEnd
);
385 OUString
aOrigTxt( pEditView
->GetSelected() );
386 OUString
aNewTxt( rReplaceWith
);
392 case eReplacementBracketed
:
393 (((aNewTxt
= aOrigTxt
) += aBracketedStart
) += rReplaceWith
) += aBracketedEnd
;
395 case eOriginalBracketed
:
396 (((aNewTxt
= rReplaceWith
) += aBracketedStart
) += aOrigTxt
) += aBracketedEnd
;
398 case eReplacementAbove
:
399 case eOriginalAbove
:
400 case eReplacementBelow
:
401 case eOriginalBelow
:
402 DBG_ERROR( "Rubies not supported" );
405 DBG_ERROR( "unexpected case" );
407 nUnitOffset
= sal::static_int_cast
< USHORT
>(
408 nUnitOffset
+ nUnitStart
+ aNewTxt
.getLength());
410 // remember current original language for kater use
411 ImpEditEngine
*pImpEditEng
= pEditView
->GetImpEditEngine();
412 ESelection _aOldSel
= pEditView
->GetSelection();
413 //EditSelection aOldEditSel = pEditView->GetImpEditView()->GetEditSelection();
416 LanguageType nOldLang
= pImpEditEng
->GetLanguage( pImpEditEng
->CreateSel( _aOldSel
).Min() );
419 pImpEditEng
->UndoActionStart( EDITUNDO_INSERT
);
421 // according to FT we should currently not bother about keeping
422 // attributes in Hangul/Hanja conversion and leave that untouched.
423 // Thus we do this only for Chinese translation...
424 sal_Bool bIsChineseConversion
= IsChinese( GetSourceLanguage() );
425 if (bIsChineseConversion
)
426 ChangeText( aNewTxt
, rOrigText
, &rOffsets
, &_aOldSel
);
428 ChangeText( aNewTxt
, rOrigText
, NULL
, NULL
);
430 // change language and font if necessary
431 if (bIsChineseConversion
)
433 DBG_ASSERT( GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED
|| GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL
,
434 "TextConvWrapper::ReplaceUnit : unexpected target language" );
436 ESelection aOldSel
= pEditView
->GetSelection();
437 ESelection
aNewSel( aOldSel
);
438 aNewSel
.nStartPos
= sal::static_int_cast
< xub_StrLen
>(
439 aNewSel
.nStartPos
- aNewTxt
.getLength());
440 // DBG_ASSERT( aOldSel.nEndPos >= 0, "error while building selection" );
442 if (pNewUnitLanguage
)
444 DBG_ASSERT(!IsSimilarChinese( *pNewUnitLanguage
, nOldLang
),
445 "similar language should not be changed!");
446 SetLanguageAndFont( aNewSel
, *pNewUnitLanguage
, EE_CHAR_LANGUAGE_CJK
,
447 GetTargetFont(), EE_CHAR_FONTINFO_CJK
);
451 pImpEditEng
->UndoActionEnd( EDITUNDO_INSERT
);
453 // adjust ConvContinue / ConvTo if necessary
454 ImpEditEngine
* pImpEE
= pEditView
->GetImpEditEngine();
455 ConvInfo
* pConvInfo
= pImpEE
->GetConvInfo();
456 sal_Int32 nDelta
= aNewTxt
.getLength() - aOrigTxt
.getLength();
459 // Note: replacement is always done in the current paragraph
460 // which is the one ConvContinue points to
461 pConvInfo
->aConvContinue
.nIndex
= sal::static_int_cast
< USHORT
>(
462 pConvInfo
->aConvContinue
.nIndex
+ nDelta
);
464 // if that is the same as the one where the conversions ends
465 // the end needs to be updated also
466 if (pConvInfo
->aConvTo
.nPara
== pConvInfo
->aConvContinue
.nPara
)
467 pConvInfo
->aConvTo
.nIndex
= sal::static_int_cast
< USHORT
>(
468 pConvInfo
->aConvTo
.nIndex
+ nDelta
);
473 void TextConvWrapper::ChangeText( const String
&rNewText
,
474 const OUString
& rOrigText
,
475 const uno::Sequence
< sal_Int32
> *pOffsets
,
476 ESelection
*pESelection
)
478 //!! code is a modifed copy of SwHHCWrapper::ChangeText from sw !!
480 DBG_ASSERT( rNewText
.Len() != 0, "unexpected empty string" );
481 if (rNewText
.Len() == 0)
484 if (pOffsets
&& pESelection
) // try to keep as much attributation as possible ?
486 pESelection
->Adjust();
488 // remember cursor start position for later setting of the cursor
489 const xub_StrLen nStartIndex
= pESelection
->nStartPos
;
491 const sal_Int32 nIndices
= pOffsets
->getLength();
492 const sal_Int32
*pIndices
= pOffsets
->getConstArray();
493 xub_StrLen nConvTextLen
= rNewText
.Len();
495 xub_StrLen nChgPos
= STRING_NOTFOUND
;
496 xub_StrLen nChgLen
= 0;
497 xub_StrLen nConvChgPos
= STRING_NOTFOUND
;
498 xub_StrLen nConvChgLen
= 0;
500 // offset to calculate the position in the text taking into
501 // account that text may have been replaced with new text of
502 // different length. Negative values allowed!
503 long nCorrectionOffset
= 0;
505 DBG_ASSERT(nIndices
== 0 || nIndices
== nConvTextLen
,
506 "mismatch between string length and sequence length!" );
508 // find all substrings that need to be replaced (and only those)
511 // get index in original text that matches nPos in new text
513 if (nPos
< nConvTextLen
)
514 nIndex
= (sal_Int32
) nPos
< nIndices
? (xub_StrLen
) pIndices
[nPos
] : nPos
;
518 nIndex
= static_cast< xub_StrLen
>( rOrigText
.getLength() );
521 if (rOrigText
.getStr()[nIndex
] == rNewText
.GetChar(nPos
) ||
522 nPos
== nConvTextLen
/* end of string also terminates non-matching char sequence */)
524 // substring that needs to be replaced found?
525 if (nChgPos
!= STRING_NOTFOUND
&& nConvChgPos
!= STRING_NOTFOUND
)
527 nChgLen
= nIndex
- nChgPos
;
528 nConvChgLen
= nPos
- nConvChgPos
;
530 String
aInOrig( rOrigText
.copy( nChgPos
, nChgLen
) );
532 String
aInNew( rNewText
.Copy( nConvChgPos
, nConvChgLen
) );
534 // set selection to sub string to be replaced in original text
535 ESelection
aSel( *pESelection
);
536 xub_StrLen nChgInNodeStartIndex
= static_cast< xub_StrLen
>( nStartIndex
+ nCorrectionOffset
+ nChgPos
);
537 aSel
.nStartPos
= nChgInNodeStartIndex
;
538 aSel
.nEndPos
= nChgInNodeStartIndex
+ nChgLen
;
539 pEditView
->SetSelection( aSel
);
541 String
aSelTxt1( pEditView
->GetSelected() );
544 // replace selected sub string with the corresponding
545 // sub string from the new text while keeping as
546 // much from the attributes as possible
547 ChangeText_impl( aInNew
, sal_True
);
549 nCorrectionOffset
+= nConvChgLen
- nChgLen
;
551 nChgPos
= STRING_NOTFOUND
;
552 nConvChgPos
= STRING_NOTFOUND
;
557 // begin of non-matching char sequence found ?
558 if (nChgPos
== STRING_NOTFOUND
&& nConvChgPos
== STRING_NOTFOUND
)
564 if (nPos
>= nConvTextLen
)
569 // set cursor to the end of the inserted text
570 // (as it would happen after ChangeText_impl (Delete and Insert)
571 // of the whole text in the 'else' branch below)
572 pESelection
->nStartPos
= pESelection
->nEndPos
= nStartIndex
+ nConvTextLen
;
576 ChangeText_impl( rNewText
, sal_False
);
581 void TextConvWrapper::ChangeText_impl( const String
&rNewText
, sal_Bool bKeepAttributes
)
585 // save attributes to be restored
586 SfxItemSet
aSet( pEditView
->GetAttribs() );
589 String
aSelTxt1( pEditView
->GetSelected() );
591 // replace old text and select new text
592 pEditView
->InsertText( rNewText
, sal_True
);
594 String
aSelTxt2( pEditView
->GetSelected() );
597 // since 'SetAttribs' below function like merging with the attributes
598 // from the itemset with any existing ones we have to get rid of all
599 // all attributes now. (Those attributes that may take effect left
600 // to the position where the new text gets inserted after the old text
602 pEditView
->RemoveAttribs();
603 // apply saved attributes to new inserted text
604 pEditView
->SetAttribs( aSet
);
608 pEditView
->InsertText( rNewText
);
613 void TextConvWrapper::Convert()
615 bStartChk
= sal_False
;
616 ConvStart_impl( SVX_SPELL_BODY_END
);
622 sal_Bool
TextConvWrapper::HasRubySupport() const
627 //////////////////////////////////////////////////////////////////////