Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
21 : #include <vcl/wrkwin.hxx>
22 : #include <vcl/dialog.hxx>
23 : #include <vcl/msgbox.hxx>
24 : #include <vcl/svapp.hxx>
25 :
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>
35 :
36 :
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::lang;
41 : using namespace com::sun::star::linguistic2;
42 :
43 :
44 :
45 0 : TextConvWrapper::TextConvWrapper( Window* pWindow,
46 : const Reference< XComponentContext >& rxContext,
47 : const Locale& rSourceLocale,
48 : const Locale& rTargetLocale,
49 : const Font* pTargetFont,
50 : sal_Int32 nOptions,
51 : bool bIsInteractive,
52 : bool bIsStart,
53 : EditView* pView ) :
54 : HangulHanjaConversion( pWindow, rxContext, rSourceLocale, rTargetLocale, pTargetFont, nOptions, bIsInteractive )
55 : , m_nConvTextLang(LANGUAGE_NONE)
56 : , m_nUnitOffset(0)
57 : , m_nLastPos(0)
58 : , m_aConvSel(pView->GetSelection())
59 : , m_pEditView(pView)
60 : , m_pWin(pWindow)
61 : , m_bStartChk(false)
62 : , m_bStartDone(bIsStart)
63 : , m_bEndDone(false)
64 0 : , m_bAllowChange(false)
65 : {
66 : DBG_ASSERT( pWindow, "TextConvWrapper: window missing" );
67 :
68 0 : m_aConvSel.Adjust(); // make Start <= End
69 0 : }
70 :
71 :
72 0 : TextConvWrapper::~TextConvWrapper()
73 : {
74 0 : }
75 :
76 :
77 0 : bool TextConvWrapper::ConvNext_impl()
78 : {
79 : // modified version of SvxSpellWrapper::SpellNext
80 :
81 0 : if( m_bStartChk )
82 0 : m_bStartDone = true;
83 : else
84 0 : m_bEndDone = true;
85 :
86 0 : if ( m_bStartDone && m_bEndDone )
87 : {
88 0 : if ( ConvMore_impl() ) // examine another document?
89 : {
90 0 : m_bStartDone = true;
91 0 : m_bEndDone = false;
92 0 : ConvStart_impl( SVX_SPELL_BODY );
93 0 : return true;
94 : }
95 0 : return false;
96 :
97 : }
98 :
99 0 : if ( m_bStartDone && m_bEndDone )
100 : {
101 0 : if ( ConvMore_impl() ) // examine another document?
102 : {
103 0 : m_bStartDone = true;
104 0 : m_bEndDone = false;
105 0 : ConvStart_impl( SVX_SPELL_BODY );
106 0 : return true;
107 : }
108 : }
109 0 : else if (!m_aConvSel.HasRange())
110 : {
111 0 : m_bStartChk = !m_bStartDone;
112 0 : ConvStart_impl( m_bStartChk ? SVX_SPELL_BODY_START : SVX_SPELL_BODY_END );
113 0 : return true;
114 : }
115 :
116 0 : return false;
117 : }
118 :
119 :
120 0 : bool TextConvWrapper::FindConvText_impl()
121 : {
122 : // modified version of SvxSpellWrapper::FindSpellError
123 :
124 0 : bool bFound = false;
125 :
126 0 : m_pWin->EnterWait();
127 0 : bool bConvert = true;
128 :
129 0 : while ( bConvert )
130 : {
131 0 : bFound = ConvContinue_impl();
132 0 : if (bFound)
133 : {
134 0 : bConvert = false;
135 : }
136 : else
137 : {
138 0 : ConvEnd_impl();
139 0 : bConvert = ConvNext_impl();
140 : }
141 : }
142 0 : m_pWin->LeaveWait();
143 0 : return bFound;
144 : }
145 :
146 :
147 0 : bool TextConvWrapper::ConvMore_impl()
148 : {
149 : // modified version of SvxSpellWrapper::SpellMore
150 :
151 0 : bool bMore = false;
152 0 : EditEngine* pEE = m_pEditView->GetEditEngine();
153 0 : ImpEditEngine* pImpEE = m_pEditView->GetImpEditEngine();
154 0 : ConvInfo* pConvInfo = pImpEE->GetConvInfo();
155 0 : if ( pConvInfo->bMultipleDoc )
156 : {
157 0 : bMore = pEE->ConvertNextDocument();
158 0 : if ( bMore )
159 : {
160 : // The text has been entered in this engine ...
161 : m_pEditView->GetImpEditView()->SetEditSelection(
162 0 : pEE->GetEditDoc().GetStartPaM() );
163 : }
164 : }
165 0 : return bMore;
166 : }
167 :
168 :
169 0 : void TextConvWrapper::ConvStart_impl( SvxSpellArea eArea )
170 : {
171 : // modified version of EditSpellWrapper::SpellStart
172 :
173 0 : EditEngine* pEE = m_pEditView->GetEditEngine();
174 0 : ImpEditEngine* pImpEE = m_pEditView->GetImpEditEngine();
175 0 : ConvInfo* pConvInfo = pImpEE->GetConvInfo();
176 :
177 0 : if ( eArea == SVX_SPELL_BODY_START )
178 : {
179 : // Is called when Spell-forward has reached the end, and to start over
180 0 : if ( m_bEndDone )
181 : {
182 0 : pConvInfo->bConvToEnd = false;
183 0 : pConvInfo->aConvTo = pConvInfo->aConvStart;
184 0 : pConvInfo->aConvContinue = EPaM( 0, 0 );
185 : m_pEditView->GetImpEditView()->SetEditSelection(
186 0 : pEE->GetEditDoc().GetStartPaM() );
187 : }
188 : else
189 : {
190 0 : pConvInfo->bConvToEnd = true;
191 0 : pConvInfo->aConvTo = pImpEE->CreateEPaM(
192 0 : pEE->GetEditDoc().GetStartPaM() );
193 : }
194 : }
195 0 : else if ( eArea == SVX_SPELL_BODY_END )
196 : {
197 : // Is called when Spell-forward starts
198 0 : pConvInfo->bConvToEnd = true;
199 0 : if (m_aConvSel.HasRange())
200 : {
201 : // user selection: convert to end of selection
202 0 : pConvInfo->aConvTo.nPara = m_aConvSel.nEndPara;
203 0 : pConvInfo->aConvTo.nIndex = m_aConvSel.nEndPos;
204 0 : pConvInfo->bConvToEnd = false;
205 : }
206 : else
207 : {
208 : // nothing selected: convert to end of document
209 0 : pConvInfo->aConvTo = pImpEE->CreateEPaM(
210 0 : pEE->GetEditDoc().GetEndPaM() );
211 : }
212 : }
213 0 : else if ( eArea == SVX_SPELL_BODY )
214 : {
215 : // called by ConvNext_impl...
216 0 : pConvInfo->aConvContinue = pConvInfo->aConvStart;
217 0 : pConvInfo->aConvTo = pImpEE->CreateEPaM(
218 0 : pEE->GetEditDoc().GetEndPaM() );
219 : }
220 : else
221 : {
222 : OSL_FAIL( "ConvStart_impl: Unknown Area!" );
223 : }
224 0 : }
225 :
226 :
227 0 : void TextConvWrapper::ConvEnd_impl()
228 : {
229 0 : }
230 :
231 :
232 0 : bool TextConvWrapper::ConvContinue_impl()
233 : {
234 : // modified version of EditSpellWrapper::SpellContinue
235 :
236 : // get next convertible text portion and its language
237 0 : m_aConvText = OUString();
238 0 : m_nConvTextLang = LANGUAGE_NONE;
239 : m_pEditView->GetImpEditEngine()->ImpConvert( m_aConvText, m_nConvTextLang,
240 0 : m_pEditView, GetSourceLanguage(), m_aConvSel,
241 0 : m_bAllowChange, GetTargetLanguage(), GetTargetFont() );
242 0 : return !m_aConvText.isEmpty();
243 : }
244 :
245 :
246 0 : void TextConvWrapper::SetLanguageAndFont( const ESelection &rESel,
247 : LanguageType nLang, sal_uInt16 nLangWhichId,
248 : const Font *pFont, sal_uInt16 nFontWhichId )
249 : {
250 0 : ESelection aOldSel = m_pEditView->GetSelection();
251 0 : m_pEditView->SetSelection( rESel );
252 :
253 : // set new language attribute
254 0 : SfxItemSet aNewSet( m_pEditView->GetEmptyItemSet() );
255 0 : aNewSet.Put( SvxLanguageItem( nLang, nLangWhichId ) );
256 :
257 : // new font to be set?
258 : DBG_ASSERT( pFont, "target font missing?" );
259 0 : if (pFont)
260 : {
261 : // set new font attribute
262 0 : SvxFontItem aFontItem = (SvxFontItem&) aNewSet.Get( nFontWhichId );
263 0 : aFontItem.SetFamilyName( pFont->GetName());
264 0 : aFontItem.SetFamily( pFont->GetFamily());
265 0 : aFontItem.SetStyleName( pFont->GetStyleName());
266 0 : aFontItem.SetPitch( pFont->GetPitch());
267 0 : aFontItem.SetCharSet(pFont->GetCharSet());
268 0 : aNewSet.Put( aFontItem );
269 : }
270 :
271 : // apply new attributes
272 0 : m_pEditView->SetAttribs( aNewSet );
273 :
274 0 : m_pEditView->SetSelection( aOldSel );
275 0 : }
276 :
277 :
278 0 : void TextConvWrapper::SelectNewUnit_impl(
279 : const sal_Int32 nUnitStart,
280 : const sal_Int32 nUnitEnd )
281 : {
282 0 : const bool bOK = 0 <= nUnitStart && 0 <= nUnitEnd && nUnitStart <= nUnitEnd;
283 : DBG_ASSERT( bOK, "invalid arguments" );
284 0 : if (!bOK)
285 0 : return;
286 :
287 0 : ESelection aSelection = m_pEditView->GetSelection();
288 : DBG_ASSERT( aSelection.nStartPara == aSelection.nEndPara,
289 : "paragraph mismatch in selection" );
290 0 : aSelection.nStartPos = (m_nLastPos + m_nUnitOffset + nUnitStart);
291 0 : aSelection.nEndPos = (m_nLastPos + m_nUnitOffset + nUnitEnd);
292 0 : m_pEditView->SetSelection( aSelection );
293 : }
294 :
295 :
296 0 : void TextConvWrapper::GetNextPortion(
297 : OUString& /* [out] */ rNextPortion,
298 : LanguageType& /* [out] */ rLangOfPortion,
299 : bool /* [in] */ _bAllowImplicitChangesForNotConvertibleText )
300 : {
301 0 : m_bAllowChange = _bAllowImplicitChangesForNotConvertibleText;
302 :
303 0 : FindConvText_impl();
304 0 : rNextPortion = m_aConvText;
305 0 : rLangOfPortion = m_nConvTextLang;
306 0 : m_nUnitOffset = 0;
307 :
308 0 : ESelection aSelection = m_pEditView->GetSelection();
309 : DBG_ASSERT( aSelection.nStartPara == aSelection.nEndPara,
310 : "paragraph mismatch in selection" );
311 : DBG_ASSERT( aSelection.nStartPos <= aSelection.nEndPos,
312 : "start pos > end pos" );
313 0 : m_nLastPos = aSelection.nStartPos;
314 0 : }
315 :
316 :
317 0 : void TextConvWrapper::HandleNewUnit(
318 : const sal_Int32 nUnitStart,
319 : const sal_Int32 nUnitEnd )
320 : {
321 0 : SelectNewUnit_impl( nUnitStart, nUnitEnd );
322 0 : }
323 :
324 : #ifdef DBG_UTIL
325 : namespace
326 : {
327 : bool IsSimilarChinese( LanguageType nLang1, LanguageType nLang2 )
328 : {
329 : using namespace editeng;
330 : return (HangulHanjaConversion::IsTraditional(nLang1) && HangulHanjaConversion::IsTraditional(nLang2)) ||
331 : (HangulHanjaConversion::IsSimplified(nLang1) && HangulHanjaConversion::IsSimplified(nLang2));
332 : }
333 : }
334 : #endif
335 :
336 0 : void TextConvWrapper::ReplaceUnit(
337 : const sal_Int32 nUnitStart, const sal_Int32 nUnitEnd,
338 : const OUString& rOrigText,
339 : const OUString& rReplaceWith,
340 : const ::com::sun::star::uno::Sequence< sal_Int32 > &rOffsets,
341 : ReplacementAction eAction,
342 : LanguageType *pNewUnitLanguage )
343 : {
344 0 : const bool bOK = 0 <= nUnitStart && 0 <= nUnitEnd && nUnitStart <= nUnitEnd;
345 : DBG_ASSERT( bOK, "invalid arguments" );
346 0 : if (!bOK)
347 0 : return;
348 :
349 : // select current unit
350 0 : SelectNewUnit_impl( nUnitStart, nUnitEnd );
351 :
352 0 : OUString aOrigTxt( m_pEditView->GetSelected() );
353 0 : OUString aNewTxt( rReplaceWith );
354 0 : switch (eAction)
355 : {
356 : case eExchange :
357 0 : break;
358 : case eReplacementBracketed :
359 0 : aNewTxt = aOrigTxt + "(" + rReplaceWith + ")";
360 0 : break;
361 : case eOriginalBracketed :
362 0 : aNewTxt = rReplaceWith + "(" + aOrigTxt + ")";
363 0 : break;
364 : case eReplacementAbove :
365 : case eOriginalAbove :
366 : case eReplacementBelow :
367 : case eOriginalBelow :
368 : OSL_FAIL( "Rubies not supported" );
369 0 : break;
370 : default:
371 : OSL_FAIL( "unexpected case" );
372 : }
373 0 : m_nUnitOffset = m_nUnitOffset + nUnitStart + aNewTxt.getLength();
374 :
375 : // remember current original language for kater use
376 0 : ImpEditEngine *pImpEditEng = m_pEditView->GetImpEditEngine();
377 0 : ESelection _aOldSel = m_pEditView->GetSelection();
378 : //EditSelection aOldEditSel = pEditView->GetImpEditView()->GetEditSelection();
379 :
380 : #ifdef DBG_UTIL
381 : LanguageType nOldLang = pImpEditEng->GetLanguage( pImpEditEng->CreateSel( _aOldSel ).Min() );
382 : #endif
383 :
384 0 : pImpEditEng->UndoActionStart( EDITUNDO_INSERT );
385 :
386 : // according to FT we should currently not bother about keeping
387 : // attributes in Hangul/Hanja conversion and leave that untouched.
388 : // Thus we do this only for Chinese translation...
389 0 : bool bIsChineseConversion = IsChinese( GetSourceLanguage() );
390 0 : if (bIsChineseConversion)
391 0 : ChangeText( aNewTxt, rOrigText, &rOffsets, &_aOldSel );
392 : else
393 0 : ChangeText( aNewTxt, rOrigText, NULL, NULL );
394 :
395 : // change language and font if necessary
396 0 : if (bIsChineseConversion)
397 : {
398 : DBG_ASSERT( GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED || GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL,
399 : "TextConvWrapper::ReplaceUnit : unexpected target language" );
400 :
401 0 : ESelection aOldSel = m_pEditView->GetSelection();
402 0 : ESelection aNewSel( aOldSel );
403 0 : aNewSel.nStartPos = aNewSel.nStartPos - aNewTxt.getLength();
404 :
405 0 : if (pNewUnitLanguage)
406 : {
407 : DBG_ASSERT(!IsSimilarChinese( *pNewUnitLanguage, nOldLang ),
408 : "similar language should not be changed!");
409 : SetLanguageAndFont( aNewSel, *pNewUnitLanguage, EE_CHAR_LANGUAGE_CJK,
410 0 : GetTargetFont(), EE_CHAR_FONTINFO_CJK );
411 : }
412 : }
413 :
414 0 : pImpEditEng->UndoActionEnd( EDITUNDO_INSERT );
415 :
416 : // adjust ConvContinue / ConvTo if necessary
417 0 : ImpEditEngine* pImpEE = m_pEditView->GetImpEditEngine();
418 0 : ConvInfo* pConvInfo = pImpEE->GetConvInfo();
419 0 : sal_Int32 nDelta = aNewTxt.getLength() - aOrigTxt.getLength();
420 0 : if (nDelta != 0)
421 : {
422 : // Note: replacement is always done in the current paragraph
423 : // which is the one ConvContinue points to
424 0 : pConvInfo->aConvContinue.nIndex = pConvInfo->aConvContinue.nIndex + nDelta;
425 :
426 : // if that is the same as the one where the conversions ends
427 : // the end needs to be updated also
428 0 : if (pConvInfo->aConvTo.nPara == pConvInfo->aConvContinue.nPara)
429 0 : pConvInfo->aConvTo.nIndex = pConvInfo->aConvTo.nIndex + nDelta;
430 0 : }
431 : }
432 :
433 :
434 0 : void TextConvWrapper::ChangeText( const OUString &rNewText,
435 : const OUString& rOrigText,
436 : const uno::Sequence< sal_Int32 > *pOffsets,
437 : ESelection *pESelection )
438 : {
439 : //!! code is a modified copy of SwHHCWrapper::ChangeText from sw !!
440 :
441 : DBG_ASSERT( !rNewText.isEmpty(), "unexpected empty string" );
442 0 : if (rNewText.isEmpty())
443 0 : return;
444 :
445 0 : if (pOffsets && pESelection) // try to keep as much attributation as possible ?
446 : {
447 0 : pESelection->Adjust();
448 :
449 : // remember cursor start position for later setting of the cursor
450 0 : const sal_Int32 nStartIndex = pESelection->nStartPos;
451 :
452 0 : const sal_Int32 nIndices = pOffsets->getLength();
453 0 : const sal_Int32 *pIndices = pOffsets->getConstArray();
454 0 : const sal_Int32 nConvTextLen = rNewText.getLength();
455 0 : sal_Int32 nPos = 0;
456 0 : sal_Int32 nChgPos = -1;
457 0 : sal_Int32 nConvChgPos = -1;
458 :
459 : // offset to calculate the position in the text taking into
460 : // account that text may have been replaced with new text of
461 : // different length. Negative values allowed!
462 0 : sal_Int32 nCorrectionOffset = 0;
463 :
464 : DBG_ASSERT(nIndices == 0 || nIndices == nConvTextLen,
465 : "mismatch between string length and sequence length!" );
466 :
467 : // find all substrings that need to be replaced (and only those)
468 : while (true)
469 : {
470 : // get index in original text that matches nPos in new text
471 : sal_Int32 nIndex;
472 0 : if (nPos < nConvTextLen)
473 0 : nIndex = nPos < nIndices ? pIndices[nPos] : nPos;
474 : else
475 : {
476 0 : nPos = nConvTextLen;
477 0 : nIndex = rOrigText.getLength();
478 : }
479 :
480 : // end of string also terminates non-matching char sequence
481 0 : if (rOrigText[nIndex] == rNewText[nPos] || nPos == nConvTextLen)
482 : {
483 : // substring that needs to be replaced found?
484 0 : if (nChgPos>=0 && nConvChgPos>=0)
485 : {
486 0 : const sal_Int32 nChgLen = nIndex - nChgPos;
487 0 : const sal_Int32 nConvChgLen = nPos - nConvChgPos;
488 : #ifdef DEBUG
489 : OUString aInOrig( rOrigText.copy( nChgPos, nChgLen ) );
490 : #endif
491 0 : OUString aInNew( rNewText.copy( nConvChgPos, nConvChgLen ) );
492 :
493 : // set selection to sub string to be replaced in original text
494 0 : ESelection aSel( *pESelection );
495 0 : sal_Int32 nChgInNodeStartIndex = nStartIndex + nCorrectionOffset + nChgPos;
496 0 : aSel.nStartPos = nChgInNodeStartIndex;
497 0 : aSel.nEndPos = nChgInNodeStartIndex + nChgLen;
498 0 : m_pEditView->SetSelection( aSel );
499 : #ifdef DEBUG
500 : OUString aSelTxt1( m_pEditView->GetSelected() );
501 : #endif
502 :
503 : // replace selected sub string with the corresponding
504 : // sub string from the new text while keeping as
505 : // much from the attributes as possible
506 0 : ChangeText_impl( aInNew, true );
507 :
508 0 : nCorrectionOffset += nConvChgLen - nChgLen;
509 :
510 0 : nChgPos = -1;
511 0 : nConvChgPos = -1;
512 : }
513 : }
514 : else
515 : {
516 : // begin of non-matching char sequence found ?
517 0 : if (nChgPos<0 && nConvChgPos<0)
518 : {
519 0 : nChgPos = nIndex;
520 0 : nConvChgPos = nPos;
521 : }
522 : }
523 0 : if (nPos >= nConvTextLen)
524 0 : break;
525 0 : ++nPos;
526 : }
527 :
528 : // set cursor to the end of the inserted text
529 : // (as it would happen after ChangeText_impl (Delete and Insert)
530 : // of the whole text in the 'else' branch below)
531 0 : pESelection->nStartPos = pESelection->nEndPos = nStartIndex + nConvTextLen;
532 : }
533 : else
534 : {
535 0 : ChangeText_impl( rNewText, false );
536 : }
537 : }
538 :
539 :
540 0 : void TextConvWrapper::ChangeText_impl( const OUString &rNewText, bool bKeepAttributes )
541 : {
542 0 : if (bKeepAttributes)
543 : {
544 : // save attributes to be restored
545 0 : SfxItemSet aSet( m_pEditView->GetAttribs() );
546 :
547 : #ifdef DEBUG
548 : OUString aSelTxt1( m_pEditView->GetSelected() );
549 : #endif
550 : // replace old text and select new text
551 0 : m_pEditView->InsertText( rNewText, true );
552 : #ifdef DEBUG
553 : OUString aSelTxt2( m_pEditView->GetSelected() );
554 : #endif
555 :
556 : // since 'SetAttribs' below function like merging with the attributes
557 : // from the itemset with any existing ones we have to get rid of all
558 : // all attributes now. (Those attributes that may take effect left
559 : // to the position where the new text gets inserted after the old text
560 : // was deleted)
561 0 : m_pEditView->RemoveAttribs();
562 : // apply saved attributes to new inserted text
563 0 : m_pEditView->SetAttribs( aSet );
564 : }
565 : else
566 : {
567 0 : m_pEditView->InsertText( rNewText );
568 : }
569 0 : }
570 :
571 :
572 0 : void TextConvWrapper::Convert()
573 : {
574 0 : m_bStartChk = false;
575 0 : ConvStart_impl( SVX_SPELL_BODY_END );
576 0 : ConvertDocument();
577 0 : ConvEnd_impl();
578 0 : }
579 :
580 :
581 0 : bool TextConvWrapper::HasRubySupport() const
582 : {
583 0 : return false;
584 : }
585 :
586 :
587 :
588 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|