Coverage Report

Created: 2026-07-10 11:04

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