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