Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sw/source/uibase/lingu/hhcwrp.cxx
Line
Count
Source
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
#include <hintids.hxx>
21
#include <view.hxx>
22
#include <wrtsh.hxx>
23
#include <swundo.hxx>
24
#include <splargs.hxx>
25
26
#include <editeng/langitem.hxx>
27
#include <editeng/fontitem.hxx>
28
#include <rtl/ustring.hxx>
29
#include <com/sun/star/text/RubyAdjust.hpp>
30
#include <com/sun/star/i18n/XBreakIterator.hpp>
31
#include <osl/diagnose.h>
32
#include <hhcwrp.hxx>
33
#include "sdrhhcwrap.hxx"
34
#include <doc.hxx>
35
#include <docsh.hxx>
36
#include <mdiexp.hxx>
37
#include <edtwin.hxx>
38
#include <contentindex.hxx>
39
#include <pam.hxx>
40
#include <swcrsr.hxx>
41
#include <ndtxt.hxx>
42
#include <fmtruby.hxx>
43
#include <breakit.hxx>
44
45
using namespace ::com::sun::star;
46
using namespace ::com::sun::star::text;
47
using namespace ::com::sun::star::uno;
48
using namespace ::com::sun::star::i18n;
49
50
//     Description: Turn off frame/object shell if applicable
51
52
static void lcl_ActivateTextShell( SwWrtShell & rWrtSh )
53
0
{
54
0
    if( rWrtSh.IsSelFrameMode() || rWrtSh.GetSelectedObjCount() )
55
0
        rWrtSh.EnterStdMode();
56
0
}
57
58
namespace {
59
60
class SwKeepConversionDirectionStateContext
61
{
62
public:
63
    SwKeepConversionDirectionStateContext()
64
0
    {
65
        //!! hack to transport the current conversion direction state settings
66
        //!! into the next incarnation that iterates over the drawing objects
67
        //!! ( see SwHHCWrapper::~SwHHCWrapper() )
68
0
        editeng::HangulHanjaConversion::SetUseSavedConversionDirectionState( true );
69
0
    }
70
71
    ~SwKeepConversionDirectionStateContext()
72
0
    {
73
0
        editeng::HangulHanjaConversion::SetUseSavedConversionDirectionState( false );
74
0
    }
75
};
76
77
}
78
79
SwHHCWrapper::SwHHCWrapper(
80
        SwView& rSwView,
81
        const uno::Reference< uno::XComponentContext >& rxContext,
82
        LanguageType nSourceLanguage,
83
        LanguageType nTargetLanguage,
84
        const vcl::Font *pTargetFont,
85
        sal_Int32 nConvOptions,
86
        bool bIsInteractive,
87
        bool bStart, bool bOther, bool bSelection )
88
0
    : editeng::HangulHanjaConversion(rSwView.GetEditWin().GetFrameWeld(), rxContext,
89
0
                                LanguageTag::convertToLocale( nSourceLanguage ),
90
0
                                LanguageTag::convertToLocale( nTargetLanguage ),
91
0
                                pTargetFont,
92
0
                                nConvOptions,
93
0
                                bIsInteractive )
94
0
    , m_rView( rSwView )
95
0
    , m_rWrtShell( rSwView.GetWrtShell() )
96
0
    , m_nLastPos( 0 )
97
0
    , m_nUnitOffset( 0 )
98
0
    , m_nPageCount( 0 )
99
0
    , m_nPageStart( 0 )
100
0
    , m_bIsDrawObj( false )
101
0
    , m_bIsOtherContent( bOther )
102
0
    , m_bStartChk( bOther )
103
0
    , m_bIsSelection( bSelection )
104
0
    , m_bStartDone( bOther || bStart )
105
0
    , m_bEndDone( false )
106
0
{
107
0
}
108
109
SwHHCWrapper::~SwHHCWrapper() COVERITY_NOEXCEPT_FALSE
110
0
{
111
0
    m_pConvArgs.reset();
112
113
0
    SwViewShell::SetCareDialog(nullptr);
114
115
    // check for existence of a draw view which means that there are
116
    // (or previously were) draw objects present in the document.
117
    // I.e. we like to check those too.
118
0
    if ( m_bIsDrawObj /*&& bLastRet*/ && m_rView.GetWrtShell().HasDrawView() )
119
0
    {
120
0
        vcl::Cursor *pSave = m_rView.GetWindow()->GetCursor();
121
0
        {
122
0
            SwKeepConversionDirectionStateContext aContext;
123
124
0
            SdrHHCWrapper aSdrConvWrap( m_rView, GetSourceLanguage(),
125
0
                    GetTargetLanguage(), GetTargetFont(),
126
0
                    GetConversionOptions(), IsInteractive() );
127
0
            aSdrConvWrap.StartTextConversion();
128
0
        }
129
0
        m_rView.GetWindow()->SetCursor( pSave );
130
0
    }
131
132
0
    if( m_nPageCount )
133
0
        ::EndProgress( m_rView.GetDocShell() );
134
135
    // finally for chinese translation we need to change the documents
136
    // default language and font to the new ones to be used.
137
0
    LanguageType nTargetLang = GetTargetLanguage();
138
0
    if (!IsChinese( nTargetLang ))
139
0
        return;
140
141
0
    SwDoc *pDoc = m_rView.GetDocShell()->GetDoc();
142
143
    //!! Note: This also effects the default language of text boxes (EditEngine/EditView) !!
144
0
    pDoc->SetDefault( SvxLanguageItem( nTargetLang, RES_CHRATR_CJK_LANGUAGE ) );
145
146
0
    const vcl::Font *pFont = GetTargetFont();
147
0
    if (pFont)
148
0
    {
149
0
        SvxFontItem aFontItem( pFont->GetFamilyType(), pFont->GetFamilyName(),
150
0
                pFont->GetStyleName(), pFont->GetPitch(),
151
0
                pFont->GetCharSet(), RES_CHRATR_CJK_FONT );
152
0
        pDoc->SetDefault( aFontItem );
153
0
    }
154
0
}
155
156
void SwHHCWrapper::GetNextPortion(
157
        OUString&           rNextPortion,
158
        LanguageType&       rLangOfPortion,
159
        bool bAllowChanges )
160
0
{
161
0
    m_pConvArgs->bAllowImplicitChangesForNotConvertibleText = bAllowChanges;
162
163
0
    FindConvText_impl();
164
0
    rNextPortion    = m_pConvArgs->aConvText;
165
0
    rLangOfPortion  = m_pConvArgs->nConvTextLang;
166
167
0
    m_nUnitOffset  = 0;
168
169
    // build last pos from currently selected text
170
0
    SwPaM* pCursor = m_rWrtShell.GetCursor();
171
0
    m_nLastPos =  pCursor->Start()->GetContentIndex();
172
0
}
173
174
void SwHHCWrapper::SelectNewUnit_impl( sal_Int32 nUnitStart, sal_Int32 nUnitEnd )
175
0
{
176
0
    SwPaM *pCursor = m_rWrtShell.GetCursor();
177
0
    pCursor->GetPoint()->SetContent( m_nLastPos );
178
0
    pCursor->DeleteMark();
179
180
0
    m_rWrtShell.Right( SwCursorSkipMode::Chars, /*bExpand*/ false,
181
0
                  o3tl::narrowing<sal_uInt16>(m_nUnitOffset + nUnitStart), true );
182
0
    pCursor->SetMark();
183
0
    m_rWrtShell.Right( SwCursorSkipMode::Chars, /*bExpand*/ true,
184
0
                  o3tl::narrowing<sal_uInt16>(nUnitEnd - nUnitStart), true );
185
    // end selection now. Otherwise SHIFT+HOME (extending the selection)
186
    // won't work when the dialog is closed without any replacement.
187
    // (see #116346#)
188
0
    m_rWrtShell.EndSelect();
189
0
}
190
191
void SwHHCWrapper::HandleNewUnit(
192
        const sal_Int32 nUnitStart, const sal_Int32 nUnitEnd )
193
0
{
194
0
    OSL_ENSURE( nUnitStart >= 0 && nUnitEnd >= nUnitStart, "wrong arguments" );
195
0
    if (0 > nUnitStart || nUnitStart > nUnitEnd)
196
0
        return;
197
198
0
    lcl_ActivateTextShell( m_rWrtShell );
199
200
0
    m_rWrtShell.StartAllAction();
201
202
    // select current unit
203
0
    SelectNewUnit_impl( nUnitStart, nUnitEnd );
204
205
0
    m_rWrtShell.EndAllAction();
206
0
}
207
208
void SwHHCWrapper::ChangeText( const OUString &rNewText,
209
        std::u16string_view aOrigText,
210
        const uno::Sequence< sal_Int32 > *pOffsets,
211
        SwPaM *pCursor )
212
0
{
213
    //!! please see also TextConvWrapper::ChangeText with is a modified
214
    //!! copy of this code
215
216
0
    OSL_ENSURE( !rNewText.isEmpty(), "unexpected empty string" );
217
0
    if (rNewText.isEmpty())
218
0
        return;
219
220
0
    if (pOffsets && pCursor)  // try to keep as much attributation as possible ?
221
0
    {
222
        // remember cursor start position for later setting of the cursor
223
0
        const SwPosition *pStart = pCursor->Start();
224
0
        const sal_Int32 nStartIndex = pStart->GetContentIndex();
225
0
        SwTextNode *pStartTextNode = pStart->GetNode().GetTextNode();
226
227
0
        const sal_Int32  nIndices = pOffsets->getLength();
228
0
        const sal_Int32 *pIndices = pOffsets->getConstArray();
229
0
        sal_Int32 nConvTextLen = rNewText.getLength();
230
0
        sal_Int32 nPos = 0;
231
0
        sal_Int32 nChgPos = -1;
232
0
        sal_Int32 nChgLen = 0;
233
0
        sal_Int32 nConvChgPos = -1;
234
0
        sal_Int32 nConvChgLen = 0;
235
236
        // offset to calculate the position in the text taking into
237
        // account that text may have been replaced with new text of
238
        // different length. Negative values allowed!
239
0
        tools::Long nCorrectionOffset = 0;
240
241
0
        OSL_ENSURE(nIndices == 0 || nIndices == nConvTextLen,
242
0
                "mismatch between string length and sequence length!" );
243
244
        // find all substrings that need to be replaced (and only those)
245
0
        while (true)
246
0
        {
247
            // get index in original text that matches nPos in new text
248
0
            sal_Int32 nIndex;
249
0
            if (nPos < nConvTextLen)
250
0
                nIndex = nPos < nIndices ? pIndices[nPos] : nPos;
251
0
            else
252
0
            {
253
0
                nPos   = nConvTextLen;
254
0
                nIndex = aOrigText.size();
255
0
            }
256
257
0
            if (nPos == nConvTextLen || /* end of string also terminates non-matching char sequence */
258
0
                aOrigText[nIndex] == rNewText[nPos])
259
0
            {
260
                // substring that needs to be replaced found?
261
0
                if (nChgPos != -1 && nConvChgPos != -1)
262
0
                {
263
0
                    nChgLen = nIndex - nChgPos;
264
0
                    nConvChgLen = nPos - nConvChgPos;
265
0
                    OUString aInNew( rNewText.copy( nConvChgPos, nConvChgLen ) );
266
267
                    // set selection to sub string to be replaced in original text
268
0
                    sal_Int32 nChgInNodeStartIndex = nStartIndex + nCorrectionOffset + nChgPos;
269
0
                    OSL_ENSURE( m_rWrtShell.GetCursor()->HasMark(), "cursor misplaced (nothing selected)" );
270
0
                    m_rWrtShell.GetCursor()->GetMark()->Assign( *pStartTextNode, nChgInNodeStartIndex );
271
0
                    m_rWrtShell.GetCursor()->GetPoint()->Assign( *pStartTextNode, nChgInNodeStartIndex + nChgLen );
272
273
                    // replace selected sub string with the corresponding
274
                    // sub string from the new text while keeping as
275
                    // much from the attributes as possible
276
0
                    ChangeText_impl( aInNew, true );
277
278
0
                    nCorrectionOffset += nConvChgLen - nChgLen;
279
280
0
                    nChgPos = -1;
281
0
                    nConvChgPos = -1;
282
0
                }
283
0
            }
284
0
            else
285
0
            {
286
                // begin of non-matching char sequence found ?
287
0
                if (nChgPos == -1 && nConvChgPos == -1)
288
0
                {
289
0
                    nChgPos = nIndex;
290
0
                    nConvChgPos = nPos;
291
0
                }
292
0
            }
293
0
            if (nPos >= nConvTextLen)
294
0
                break;
295
0
            ++nPos;
296
0
        }
297
298
        // set cursor to the end of all the new text
299
        // (as it would happen after ChangeText_impl (Delete and Insert)
300
        // of the whole text in the 'else' branch below)
301
0
        m_rWrtShell.ClearMark();
302
0
        m_rWrtShell.GetCursor()->Start()->Assign( *pStartTextNode, nStartIndex + nConvTextLen );
303
0
    }
304
0
    else
305
0
    {
306
0
        ChangeText_impl( rNewText, false );
307
0
    }
308
0
}
309
310
void SwHHCWrapper::ChangeText_impl( const OUString &rNewText, bool bKeepAttributes )
311
0
{
312
0
    if (bKeepAttributes)
313
0
    {
314
        // get item set with all relevant attributes
315
0
        SfxItemSet aItemSet(SfxItemSet::makeFixedSfxItemSet<RES_CHRATR_BEGIN, RES_FRMATR_END>(m_rWrtShell.GetAttrPool()));
316
        // get all attributes spanning the whole selection in order to
317
        // restore those for the new text
318
0
        m_rWrtShell.GetCurAttr( aItemSet );
319
320
0
        m_rWrtShell.Delete(true);
321
0
        m_rWrtShell.Insert( rNewText );
322
323
        // select new inserted text (currently the Point is right after the new text)
324
0
        if (!m_rWrtShell.GetCursor()->HasMark())
325
0
            m_rWrtShell.GetCursor()->SetMark();
326
0
        SwPosition *pMark = m_rWrtShell.GetCursor()->GetMark();
327
0
        pMark->SetContent( pMark->GetContentIndex() - rNewText.getLength() );
328
329
        // since 'SetAttr' below functions like merging with the attributes
330
        // from the itemset with any existing ones we have to get rid of all
331
        // all attributes now. (Those attributes that may take effect left
332
        // to the position where the new text gets inserted after the old text
333
        // was deleted)
334
0
        m_rWrtShell.ResetAttr();
335
        // apply previously saved attributes to new text
336
0
        m_rWrtShell.SetAttrSet( aItemSet );
337
0
    }
338
0
    else
339
0
    {
340
0
        m_rWrtShell.Delete(true);
341
0
        m_rWrtShell.Insert( rNewText );
342
0
    }
343
0
}
344
345
void SwHHCWrapper::ReplaceUnit(
346
         const sal_Int32 nUnitStart, const sal_Int32 nUnitEnd,
347
         const OUString& rOrigText,
348
         const OUString& rReplaceWith,
349
         const uno::Sequence< sal_Int32 > &rOffsets,
350
         ReplacementAction eAction,
351
         LanguageType *pNewUnitLanguage )
352
0
{
353
0
    OSL_ENSURE( nUnitStart >= 0 && nUnitEnd >= nUnitStart, "wrong arguments" );
354
0
    if (nUnitStart < 0 || nUnitEnd < nUnitStart)
355
0
        return;
356
357
0
    lcl_ActivateTextShell( m_rWrtShell );
358
359
    // replace the current word
360
0
    m_rWrtShell.StartAllAction();
361
362
    // select current unit
363
0
    SelectNewUnit_impl( nUnitStart, nUnitEnd );
364
365
0
    OUString aOrigText( m_rWrtShell.GetSelText() );
366
0
    OUString aNewText( rReplaceWith );
367
0
    OSL_ENSURE( aOrigText == rOrigText, "!! text mismatch !!" );
368
0
    std::unique_ptr<SwFormatRuby> pRuby;
369
0
    bool bRubyBelow = false;
370
0
    OUString  aNewOrigText;
371
0
    switch (eAction)
372
0
    {
373
0
        case eExchange :
374
0
        break;
375
0
        case eReplacementBracketed :
376
0
        {
377
0
            aNewText = aOrigText + "(" + rReplaceWith + ")";
378
0
        }
379
0
        break;
380
0
        case eOriginalBracketed :
381
0
        {
382
0
            aNewText = rReplaceWith + "(" + aOrigText + ")";
383
0
        }
384
0
        break;
385
0
        case eReplacementAbove  :
386
0
        {
387
0
            pRuby.reset(new SwFormatRuby( rReplaceWith ));
388
0
        }
389
0
        break;
390
0
        case eOriginalAbove :
391
0
        {
392
0
            pRuby.reset(new SwFormatRuby( aOrigText ));
393
0
            aNewOrigText = rReplaceWith;
394
0
        }
395
0
        break;
396
0
        case eReplacementBelow :
397
0
        {
398
0
            pRuby.reset(new SwFormatRuby( rReplaceWith ));
399
0
            bRubyBelow = true;
400
0
        }
401
0
        break;
402
0
        case eOriginalBelow :
403
0
        {
404
0
            pRuby.reset(new SwFormatRuby( aOrigText ));
405
0
            aNewOrigText = rReplaceWith;
406
0
            bRubyBelow = true;
407
0
        }
408
0
        break;
409
0
        default:
410
0
            OSL_FAIL("unexpected case" );
411
0
    }
412
0
    m_nUnitOffset += nUnitStart + aNewText.getLength();
413
414
0
    if (pRuby)
415
0
    {
416
0
        m_rWrtShell.StartUndo( SwUndoId::SETRUBYATTR );
417
0
        if (!aNewOrigText.isEmpty())
418
0
        {
419
            // according to FT we currently should not bother about keeping
420
            // attributes in Hangul/Hanja conversion
421
0
            ChangeText( aNewOrigText, rOrigText, nullptr, nullptr );
422
423
            //!! since Delete, Insert in 'ChangeText' do not set the WrtShells
424
            //!! bInSelect flag
425
            //!! back to false we do it now manually in order for the selection
426
            //!! to be done properly in the following call to Left.
427
            // We didn't fix it in Delete and Insert since it is currently
428
            // unclear if someone depends on this incorrect behaviour
429
            // of the flag.
430
0
            m_rWrtShell.EndSelect();
431
432
0
            m_rWrtShell.Left( SwCursorSkipMode::Chars, true, aNewOrigText.getLength(), true, true );
433
0
        }
434
435
0
        pRuby->SetPosition( o3tl::narrowing<sal_uInt16>(bRubyBelow) );
436
0
        pRuby->SetAdjustment( RubyAdjust_CENTER );
437
438
0
        m_rWrtShell.SetAttrItem(*pRuby);
439
0
        pRuby.reset();
440
0
        m_rWrtShell.EndUndo( SwUndoId::SETRUBYATTR );
441
0
    }
442
0
    else
443
0
    {
444
0
        m_rWrtShell.StartUndo( SwUndoId::OVERWRITE );
445
446
        // according to FT we should currently not bother about keeping
447
        // attributes in Hangul/Hanja conversion and leave that untouched.
448
        // Thus we do this only for Chinese translation...
449
0
        const bool bIsChineseConversion = IsChinese( GetSourceLanguage() );
450
0
        if (bIsChineseConversion)
451
0
            ChangeText( aNewText, rOrigText, &rOffsets, m_rWrtShell.GetCursor() );
452
0
        else
453
0
            ChangeText( aNewText, rOrigText, nullptr, nullptr );
454
455
        // change language and font if necessary
456
0
        if (bIsChineseConversion)
457
0
        {
458
0
            m_rWrtShell.SetMark();
459
0
            m_rWrtShell.GetCursor()->GetMark()->AdjustContent( -aNewText.getLength() );
460
461
0
            OSL_ENSURE( GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED || GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL,
462
0
                    "SwHHCWrapper::ReplaceUnit : unexpected target language" );
463
464
0
            SfxItemSet aSet(SfxItemSet::makeFixedSfxItemSet<
465
0
                            RES_CHRATR_CJK_FONT, RES_CHRATR_CJK_FONT,
466
0
                            RES_CHRATR_CJK_LANGUAGE, RES_CHRATR_CJK_LANGUAGE>(m_rWrtShell.GetAttrPool()));
467
0
            if (pNewUnitLanguage)
468
0
            {
469
0
                aSet.Put( SvxLanguageItem( *pNewUnitLanguage, RES_CHRATR_CJK_LANGUAGE ) );
470
0
            }
471
472
0
            const vcl::Font *pTargetFont = GetTargetFont();
473
0
            OSL_ENSURE( pTargetFont, "target font missing?" );
474
0
            if (pTargetFont && pNewUnitLanguage)
475
0
            {
476
0
                SvxFontItem aFontItem( aSet.Get( RES_CHRATR_CJK_FONT ) );
477
0
                aFontItem.SetFamilyName(    pTargetFont->GetFamilyName());
478
0
                aFontItem.SetFamily(        pTargetFont->GetFamilyType());
479
0
                aFontItem.SetStyleName(     pTargetFont->GetStyleName());
480
0
                aFontItem.SetPitch(         pTargetFont->GetPitch());
481
0
                aFontItem.SetCharSet( pTargetFont->GetCharSet() );
482
0
                aSet.Put( aFontItem );
483
0
            }
484
485
0
            m_rWrtShell.SetAttrSet( aSet );
486
487
0
            m_rWrtShell.ClearMark();
488
0
        }
489
490
0
        m_rWrtShell.EndUndo( SwUndoId::OVERWRITE );
491
0
    }
492
493
0
    m_rWrtShell.EndAllAction();
494
0
}
495
496
bool SwHHCWrapper::HasRubySupport() const
497
0
{
498
0
    return true;
499
0
}
500
501
void SwHHCWrapper::Convert()
502
0
{
503
0
    OSL_ENSURE( m_pConvArgs == nullptr, "NULL pointer expected" );
504
0
    {
505
0
        SwPaM *pCursor = m_rView.GetWrtShell().GetCursor();
506
0
        auto [pSttPos, pEndPos] = pCursor->StartEnd(); // SwPosition*
507
508
0
        if (pSttPos->GetNode().IsTextNode() &&
509
0
            pEndPos->GetNode().IsTextNode())
510
0
        {
511
0
            m_pConvArgs.reset( new SwConversionArgs( GetSourceLanguage(), *pSttPos, *pEndPos ) );
512
0
        }
513
0
        else    // we are not in the text (maybe a graphic or OLE object is selected) let's start from the top
514
0
        {
515
            // get PaM that points to the start of the document
516
0
            SwNode& rNode = m_rView.GetDocShell()->GetDoc()->GetNodes().GetEndOfContent();
517
0
            SwPaM aPam(rNode);
518
0
            aPam.Move( fnMoveBackward, GoInDoc ); // move to start of document
519
520
0
            pSttPos = aPam.GetPoint();  //! using a PaM here makes sure we will get only text nodes
521
0
            SwTextNode *pTextNode = pSttPos->GetNode().GetTextNode();
522
            // just in case we check anyway...
523
0
            if (!pTextNode || !pTextNode->IsTextNode())
524
0
                return;
525
0
            m_pConvArgs.reset( new SwConversionArgs( GetSourceLanguage(), *pSttPos, *pSttPos ) );
526
0
        }
527
0
        OSL_ENSURE( m_pConvArgs->pStartPos && m_pConvArgs->pStartPos->GetNode().IsTextNode(),
528
0
                "failed to get proper start text node" );
529
0
        OSL_ENSURE( m_pConvArgs->pEndPos && m_pConvArgs->pEndPos->GetNode().IsTextNode(),
530
0
                "failed to get proper end text node" );
531
532
        // chinese conversion specific settings
533
0
        OSL_ENSURE( IsChinese( GetSourceLanguage() ) == IsChinese( GetTargetLanguage() ),
534
0
                "source and target language mismatch?" );
535
0
        if (IsChinese( GetTargetLanguage() ))
536
0
        {
537
0
            m_pConvArgs->nConvTargetLang = GetTargetLanguage();
538
0
            m_pConvArgs->pTargetFont = GetTargetFont();
539
0
            m_pConvArgs->bAllowImplicitChangesForNotConvertibleText = true;
540
0
        }
541
542
        // if it is not just a selection and we are about to begin
543
        // with the current conversion for the very first time
544
        // we need to find the start of the current (initial)
545
        // convertible unit in order for the text conversion to give
546
        // the correct result for that. Since it is easier to obtain
547
        // the start of the word we use that though.
548
0
        if (!pCursor->HasMark())   // is not a selection?
549
0
        {
550
            // since #118246 / #117803 still occurs if the cursor is placed
551
            // between the two chinese characters to be converted (because both
552
            // of them are words on their own!) using the word boundary here does
553
            // not work. Thus since chinese conversion is not interactive we start
554
            // at the begin of the paragraph to solve the problem, i.e. have the
555
            // TextConversion service get those characters together in the same call.
556
0
            sal_Int32 nStartIdx = -1;
557
0
            if (editeng::HangulHanjaConversion::IsChinese( GetSourceLanguage() ) )
558
0
                nStartIdx = 0;
559
0
            else
560
0
            {
561
0
                OUString aText( m_pConvArgs->pStartPos->GetNode().GetTextNode()->GetText() );
562
0
                const sal_Int32 nPos = m_pConvArgs->pStartPos->GetContentIndex();
563
0
                Boundary aBoundary( g_pBreakIt->GetBreakIter()->
564
0
                        getWordBoundary( aText, nPos, g_pBreakIt->GetLocale( m_pConvArgs->nConvSrcLang ),
565
0
                                WordType::DICTIONARY_WORD, true ) );
566
567
                // valid result found?
568
0
                if (aBoundary.startPos < aText.getLength() &&
569
0
                    aBoundary.startPos != aBoundary.endPos)
570
0
                {
571
0
                    nStartIdx = aBoundary.startPos;
572
0
                }
573
0
            }
574
575
0
            if (nStartIdx != -1)
576
0
                m_pConvArgs->pStartPos->SetContent( nStartIdx );
577
0
        }
578
0
    }
579
580
0
    if ( m_bIsOtherContent )
581
0
        ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::Other );
582
0
    else
583
0
    {
584
0
        m_bStartChk = false;
585
0
        ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::BodyEnd );
586
0
    }
587
588
0
    ConvertDocument();
589
590
0
    ConvEnd_impl( m_pConvArgs.get() );
591
0
}
592
593
bool SwHHCWrapper::ConvNext_impl( )
594
0
{
595
    //! modified version of SvxSpellWrapper::SpellNext
596
597
    // no change of direction so the desired region is fully processed
598
0
    if( m_bStartChk )
599
0
        m_bStartDone = true;
600
0
    else
601
0
        m_bEndDone = true;
602
603
0
    if( m_bIsOtherContent && m_bStartDone && m_bEndDone ) // document completely checked?
604
0
    {
605
0
        return false;
606
0
    }
607
608
0
    bool bGoOn = false;
609
610
0
    if ( m_bIsOtherContent )
611
0
    {
612
0
        m_bStartChk = false;
613
0
        ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::Body );
614
0
        bGoOn = true;
615
0
    }
616
0
    else if ( m_bStartDone && m_bEndDone )
617
0
    {
618
        // body region done, ask about special region
619
0
        if( !m_bIsSelection && m_rWrtShell.HasOtherCnt() )
620
0
        {
621
0
            ConvStart_impl( m_pConvArgs.get(), SvxSpellArea::Other );
622
0
            m_bIsOtherContent = bGoOn = true;
623
0
        }
624
0
    }
625
0
    else
626
0
    {
627
0
            m_bStartChk = !m_bStartDone;
628
0
            ConvStart_impl( m_pConvArgs.get(), m_bStartChk ? SvxSpellArea::BodyStart : SvxSpellArea::BodyEnd );
629
0
            bGoOn = true;
630
0
    }
631
0
    return bGoOn;
632
0
}
633
634
void SwHHCWrapper::FindConvText_impl()
635
0
{
636
    //! modified version of SvxSpellWrapper::FindSpellError
637
638
0
    bool bFound = false;
639
640
0
    weld::WaitObject aWait(GetUIParent());
641
0
    bool bConv = true;
642
643
0
    while ( bConv )
644
0
    {
645
0
        bFound = ConvContinue_impl( m_pConvArgs.get() );
646
0
        if (bFound)
647
0
        {
648
0
            bConv = false;
649
0
        }
650
0
        else
651
0
        {
652
0
            ConvEnd_impl( m_pConvArgs.get() );
653
0
            bConv = ConvNext_impl();
654
0
        }
655
0
    }
656
0
}
657
658
void SwHHCWrapper::ConvStart_impl( SwConversionArgs /* [out] */ *pConversionArgs, SvxSpellArea eArea )
659
0
{
660
0
    m_bIsDrawObj = SvxSpellArea::Other == eArea;
661
0
    m_rView.SpellStart( eArea, m_bStartDone, m_bEndDone, /* [out] */ pConversionArgs );
662
0
}
663
664
void SwHHCWrapper::ConvEnd_impl( SwConversionArgs const *pConversionArgs )
665
0
{
666
0
    m_rView.SpellEnd( pConversionArgs );
667
0
}
668
669
bool SwHHCWrapper::ConvContinue_impl( SwConversionArgs *pConversionArgs )
670
0
{
671
0
    bool bProgress = !m_bIsDrawObj && !m_bIsSelection;
672
0
    pConversionArgs->aConvText.clear();
673
0
    pConversionArgs->nConvTextLang = LANGUAGE_NONE;
674
0
    m_rView.GetWrtShell().SpellContinue( &m_nPageCount, bProgress ? &m_nPageStart : nullptr, pConversionArgs );
675
0
    return !pConversionArgs->aConvText.isEmpty();
676
0
}
677
678
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */