Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/editeng/source/misc/splwrap.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 <config_wasm_strip.h>
21
22
#include <rtl/ustring.hxx>
23
#include <i18nlangtag/languagetag.hxx>
24
#include <vcl/svapp.hxx>
25
#include <vcl/vclenum.hxx>
26
#include <vcl/weld/MessageDialog.hxx>
27
#include <vcl/weld/WaitObject.hxx>
28
#include <svtools/langtab.hxx>
29
30
#include <vcl/errinf.hxx>
31
#include <editeng/unolingu.hxx>
32
#include <com/sun/star/frame/XStorable.hpp>
33
#include <com/sun/star/linguistic2/XLinguProperties.hpp>
34
#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
35
#include <com/sun/star/linguistic2/XHyphenator.hpp>
36
#include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
37
#include <com/sun/star/linguistic2/XDictionary.hpp>
38
39
#include <editeng/svxenum.hxx>
40
#include <editeng/splwrap.hxx>
41
#include <editeng/edtdlg.hxx>
42
#include <editeng/eerdll.hxx>
43
#include <editeng/editrids.hrc>
44
#include <editeng/editerr.hxx>
45
46
#include <map>
47
#include <memory>
48
49
using namespace ::com::sun::star;
50
using namespace ::com::sun::star::uno;
51
using namespace ::com::sun::star::linguistic2;
52
53
54
// misc functions ---------------------------------------------
55
56
void SvxPrepareAutoCorrect( OUString &rOldText, std::u16string_view rNewText )
57
0
{
58
    // This function should be used to strip (or add) trailing '.' from
59
    // the strings before passing them on to the autocorrect function in
60
    // order that the autocorrect function will hopefully
61
    // works properly with normal words and abbreviations (with trailing '.')
62
    // independent of if they are at the end of the sentence or not.
63
    //
64
    // rOldText: text to be replaced
65
    // rNewText: replacement text
66
67
0
    sal_Int32 nOldLen = rOldText.getLength();
68
0
    sal_Int32 nNewLen = rNewText.size();
69
0
    if (nOldLen && nNewLen)
70
0
    {
71
0
        bool bOldHasDot = '.' == rOldText[ nOldLen - 1 ],
72
0
             bNewHasDot = '.' == rNewText[ nNewLen - 1 ];
73
0
        if (bOldHasDot && !bNewHasDot
74
0
            /*this is: !(bOldHasDot && bNewHasDot) && bOldHasDot*/)
75
0
            rOldText = rOldText.copy( 0, nOldLen - 1 );
76
0
    }
77
0
}
78
79
0
#define SVX_LANG_NEED_CHECK         0
80
0
#define SVX_LANG_OK                 1
81
0
#define SVX_LANG_MISSING            2
82
0
#define SVX_LANG_MISSING_DO_WARN    3
83
84
typedef std::map< LanguageType, sal_uInt16 >   LangCheckState_map_t;
85
86
static LangCheckState_map_t & GetLangCheckState()
87
0
{
88
0
    static LangCheckState_map_t aLangCheckState;
89
0
    return aLangCheckState;
90
0
}
91
92
void SvxSpellWrapper::ShowLanguageErrors()
93
0
{
94
    // display message boxes for languages not available for
95
    // spellchecking or hyphenation
96
0
    LangCheckState_map_t &rLCS = GetLangCheckState();
97
0
    for (auto const& elem : rLCS)
98
0
    {
99
0
        LanguageType nLang = elem.first;
100
0
        sal_uInt16   nVal  = elem.second;
101
0
        sal_uInt16 nTmpSpell = nVal & 0x00FF;
102
0
        sal_uInt16 nTmpHyph  = (nVal >> 8) & 0x00FF;
103
104
0
        if (SVX_LANG_MISSING_DO_WARN == nTmpSpell)
105
0
        {
106
0
            OUString aErr( SvtLanguageTable::GetLanguageString( nLang ) );
107
0
            ErrorHandler::HandleError(
108
0
                ErrCodeMsg( ERRCODE_SVX_LINGU_LANGUAGENOTEXISTS, aErr ) );
109
0
            nTmpSpell = SVX_LANG_MISSING;
110
0
        }
111
0
        if (SVX_LANG_MISSING_DO_WARN == nTmpHyph)
112
0
        {
113
0
            OUString aErr( SvtLanguageTable::GetLanguageString( nLang ) );
114
0
            ErrorHandler::HandleError(
115
0
                ErrCodeMsg( ERRCODE_SVX_LINGU_LANGUAGENOTEXISTS, aErr ) );
116
0
            nTmpHyph = SVX_LANG_MISSING;
117
0
        }
118
119
0
        rLCS[ nLang ] = (nTmpHyph << 8) | nTmpSpell;
120
0
    }
121
122
0
}
123
124
SvxSpellWrapper::~SvxSpellWrapper()
125
0
{
126
0
}
127
128
/*--------------------------------------------------------------------
129
 *  Description: Constructor, the test sequence is determined
130
 *
131
 *  !bStart && !bOtherCntnt:    BODY_END,   BODY_START, OTHER
132
 *  !bStart && bOtherCntnt:     OTHER,      BODY
133
 *  bStart && !bOtherCntnt:     BODY_END,   OTHER
134
 *  bStart && bOtherCntnt:      OTHER
135
 *
136
 --------------------------------------------------------------------*/
137
138
SvxSpellWrapper::SvxSpellWrapper( weld::Widget* pWn,
139
    const bool bStart, const bool bIsAllRight ) :
140
141
0
    pWin        ( pWn ),
142
0
    bOtherCntnt ( false ),
143
0
    bStartChk   ( false ),
144
0
    bRevAllowed ( true ),
145
0
    bAllRight   ( bIsAllRight )
146
0
{
147
0
    Reference< linguistic2::XLinguProperties >  xProp( LinguMgr::GetLinguPropertySet() );
148
0
    bool bWrapReverse = xProp.is() && xProp->getIsWrapReverse();
149
0
    bReverse = bWrapReverse;
150
0
    bStartDone = !bReverse && bStart;
151
0
    bEndDone   = bReverse && bStart;
152
0
}
153
154
155
SvxSpellWrapper::SvxSpellWrapper( weld::Widget* pWn,
156
        Reference< XHyphenator > const &xHyphenator,
157
        const bool bStart, const bool bOther ) :
158
0
    pWin        ( pWn ),
159
0
    xHyph       ( xHyphenator ),
160
0
    bOtherCntnt ( bOther ),
161
0
    bReverse    ( false ),
162
0
    bStartDone  ( bOther || bStart ),
163
0
    bEndDone    ( false ),
164
0
    bStartChk   ( bOther ),
165
0
    bRevAllowed ( false ),
166
0
    bAllRight   ( true )
167
0
{
168
0
}
169
170
171
sal_Int16 SvxSpellWrapper::CheckSpellLang(
172
        Reference< XSpellChecker1 > const & xSpell, LanguageType nLang)
173
0
{
174
0
    LangCheckState_map_t &rLCS = GetLangCheckState();
175
176
0
    LangCheckState_map_t::iterator aIt( rLCS.find( nLang ) );
177
0
    sal_uInt16 nVal = aIt == rLCS.end() ? SVX_LANG_NEED_CHECK : aIt->second;
178
179
0
    if (aIt == rLCS.end())
180
0
        rLCS[ nLang ] = nVal;
181
182
0
    if (SVX_LANG_NEED_CHECK == (nVal & 0x00FF))
183
0
    {
184
0
        sal_uInt16 nTmpVal = SVX_LANG_MISSING_DO_WARN;
185
0
        if (xSpell.is()  &&  xSpell->hasLanguage( static_cast<sal_uInt16>(nLang) ))
186
0
            nTmpVal = SVX_LANG_OK;
187
0
        nVal &= 0xFF00;
188
0
        nVal |= nTmpVal;
189
190
0
        rLCS[ nLang ] = nVal;
191
0
    }
192
193
0
    return static_cast<sal_Int16>(nVal);
194
0
}
195
196
sal_Int16 SvxSpellWrapper::CheckHyphLang(
197
        Reference< XHyphenator > const & xHyph, LanguageType nLang)
198
0
{
199
0
    LangCheckState_map_t &rLCS = GetLangCheckState();
200
201
0
    LangCheckState_map_t::iterator aIt( rLCS.find( nLang ) );
202
0
    sal_uInt16 nVal = aIt == rLCS.end() ? 0 : aIt->second;
203
204
0
    if (aIt == rLCS.end())
205
0
        rLCS[ nLang ] = nVal;
206
207
0
    if (SVX_LANG_NEED_CHECK == ((nVal >> 8) & 0x00FF))
208
0
    {
209
0
        sal_uInt16 nTmpVal = SVX_LANG_MISSING_DO_WARN;
210
0
        if (xHyph.is()  &&  xHyph->hasLocale( LanguageTag::convertToLocale( nLang ) ))
211
0
            nTmpVal = SVX_LANG_OK;
212
0
        nVal &= 0x00FF;
213
0
        nVal |= nTmpVal << 8;
214
215
0
        rLCS[ nLang ] = nVal;
216
0
    }
217
218
0
    return static_cast<sal_Int16>(nVal);
219
0
}
220
221
222
void SvxSpellWrapper::SpellStart( SvxSpellArea /*eSpell*/ )
223
0
{ // Here, the necessary preparations be made for SpellContinue in the
224
0
} // given area.
225
226
227
bool SvxSpellWrapper::SpellMore()
228
0
{
229
0
    return false; // Should additional documents be examined?
230
0
}
231
232
233
void SvxSpellWrapper::SpellEnd()
234
0
{   // Area is complete, tidy up if necessary
235
236
    // display error for last language not found
237
0
    ShowLanguageErrors();
238
0
}
239
240
void SvxSpellWrapper::SpellContinue()
241
0
{
242
0
}
243
244
void SvxSpellWrapper::ReplaceAll( const OUString & )
245
0
{   // Replace Word from the Replace list
246
0
}
247
248
void SvxSpellWrapper::InsertHyphen( const sal_Int32 )
249
0
{   // inserting and deleting Hyphen
250
0
}
251
252
// Testing of the document areas in the order specified by the flags
253
void SvxSpellWrapper::SpellDocument( )
254
0
{
255
#if ENABLE_WASM_STRIP_HUNSPELL
256
    return;
257
#else
258
0
    if ( bOtherCntnt )
259
0
    {
260
0
        bReverse = false;
261
0
        SpellStart( SvxSpellArea::Other );
262
0
    }
263
0
    else
264
0
    {
265
0
        bStartChk = bReverse;
266
0
        SpellStart( bReverse ? SvxSpellArea::BodyStart : SvxSpellArea::BodyEnd );
267
0
    }
268
269
0
    if ( !FindSpellError() )
270
0
        return;
271
272
0
    Reference< XHyphenatedWord >        xHyphWord( GetLast(), UNO_QUERY );
273
274
0
    if (xHyphWord.is())
275
0
    {
276
0
        EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
277
0
        ScopedVclPtr<AbstractHyphenWordDialog> pDlg(pFact->CreateHyphenWordDialog(
278
0
                        pWin,
279
0
                        xHyphWord->getWord(),
280
0
                        LanguageTag( xHyphWord->getLocale() ).getLanguageType(),
281
0
                        xHyph, this ));
282
0
        pDlg->Execute();
283
0
    }
284
0
#endif
285
0
}
286
287
288
// Select the next area
289
290
291
bool SvxSpellWrapper::SpellNext( )
292
0
{
293
0
    Reference< linguistic2::XLinguProperties >  xProp( LinguMgr::GetLinguPropertySet() );
294
0
    bool bWrapReverse = xProp.is() && xProp->getIsWrapReverse();
295
0
    bool bActRev = bRevAllowed && bWrapReverse;
296
297
    // bActRev is the direction after Spell checking, bReverse is the one
298
    // at the beginning.
299
0
    if( bActRev == bReverse )
300
0
    {                           // No change of direction, thus is the
301
0
        if( bStartChk )         // desired area ( bStartChk )
302
0
            bStartDone = true;  // completely processed.
303
0
        else
304
0
            bEndDone = true;
305
0
    }
306
0
    else if( bReverse == bStartChk ) //For a change of direction, an area can
307
0
    {                          // be processed during certain circumstances
308
0
        if( bStartChk )        // If the first part is spell checked in backwards
309
0
            bEndDone = true;   // and this is reversed in the process, then
310
0
        else                   // then the end part is processed (and vice-versa).
311
0
            bStartDone = true;
312
0
    }
313
314
0
    bReverse = bActRev;
315
0
    if( bOtherCntnt && bStartDone && bEndDone ) // Document has been fully checked?
316
0
    {
317
0
        if ( SpellMore() )  // spell check another document?
318
0
        {
319
0
            bOtherCntnt = false;
320
0
            bStartDone = !bReverse;
321
0
            bEndDone  = bReverse;
322
0
            SpellStart( SvxSpellArea::Body );
323
0
            return true;
324
0
        }
325
0
        return false;
326
0
    }
327
328
0
    bool bGoOn = false;
329
330
0
    if ( bOtherCntnt )
331
0
    {
332
0
        bStartChk = false;
333
0
        SpellStart( SvxSpellArea::Body );
334
0
        bGoOn = true;
335
0
    }
336
0
    else if ( bStartDone && bEndDone )
337
0
    {
338
0
        if ( SpellMore() )  // check another document?
339
0
        {
340
0
            bOtherCntnt = false;
341
0
            bStartDone = !bReverse;
342
0
            bEndDone  = bReverse;
343
0
            SpellStart( SvxSpellArea::Body );
344
0
            return true;
345
0
        }
346
0
    }
347
0
    else
348
0
    {
349
        // a BODY_area done, ask for the other BODY_area
350
0
        xWait.reset();
351
352
0
        TranslateId pResId = bReverse ? RID_SVXSTR_QUERY_BW_CONTINUE : RID_SVXSTR_QUERY_CONTINUE;
353
0
        std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
354
0
                                                                 VclMessageType::Question, VclButtonsType::YesNo,
355
0
                                                                 EditResId(pResId)));
356
0
        if (xBox->run() != RET_YES)
357
0
        {
358
            // sacrifice the other area if necessary ask for special area
359
0
            xWait.reset(new weld::WaitObject(pWin));
360
0
            bStartDone = bEndDone = true;
361
0
            return SpellNext();
362
0
        }
363
0
        else
364
0
        {
365
0
            bStartChk = !bStartDone;
366
0
            SpellStart( bStartChk ? SvxSpellArea::BodyStart : SvxSpellArea::BodyEnd );
367
0
            bGoOn = true;
368
0
        }
369
0
        xWait.reset(new weld::WaitObject(pWin));
370
0
    }
371
0
    return bGoOn;
372
0
}
373
374
375
Reference< XDictionary >  SvxSpellWrapper::GetAllRightDic()
376
0
{
377
0
    Reference< XDictionary >  xDic;
378
379
0
    Reference< XSearchableDictionaryList >  xDicList( LinguMgr::GetDictionaryList() );
380
0
    if (xDicList.is())
381
0
    {
382
0
        for (const Reference<XDictionary>& xTmp : xDicList->getDictionaries())
383
0
        {
384
0
            if (xTmp.is())
385
0
            {
386
0
                if ( xTmp->isActive() &&
387
0
                     xTmp->getDictionaryType() != DictionaryType_NEGATIVE &&
388
0
                     LanguageTag( xTmp->getLocale() ).getLanguageType() == LANGUAGE_NONE )
389
0
                {
390
0
                    Reference< frame::XStorable >  xStor( xTmp, UNO_QUERY );
391
0
                    if (xStor.is() && xStor->hasLocation() && !xStor->isReadonly())
392
0
                    {
393
0
                        xDic = xTmp;
394
0
                        break;
395
0
                    }
396
0
                }
397
0
            }
398
0
        }
399
400
0
        if (!xDic.is())
401
0
        {
402
0
            xDic = LinguMgr::GetStandardDic();
403
0
            if (xDic.is())
404
0
                xDic->setActive( true );
405
0
        }
406
0
    }
407
408
0
    return xDic;
409
0
}
410
411
412
bool SvxSpellWrapper::FindSpellError()
413
0
{
414
0
    ShowLanguageErrors();
415
416
0
    xWait.reset(new weld::WaitObject(pWin));
417
0
    bool bSpell = true;
418
419
0
    Reference< XDictionary >  xAllRightDic;
420
0
    if (IsAllRight())
421
0
        xAllRightDic = GetAllRightDic();
422
423
0
    while ( bSpell )
424
0
    {
425
0
        SpellContinue();
426
427
0
        Reference< XSpellAlternatives >     xAlt( GetLast(), UNO_QUERY );
428
0
        Reference< XHyphenatedWord >        xHyphWord( GetLast(), UNO_QUERY );
429
430
0
        if (xAlt.is())
431
0
        {
432
0
            if (IsAllRight() && xAllRightDic.is())
433
0
            {
434
0
                xAllRightDic->add( xAlt->getWord(), false, OUString() );
435
0
            }
436
0
            else
437
0
            {
438
                // look up in ChangeAllList for misspelled word
439
0
                Reference< XDictionary >    xChangeAllList =
440
0
                        LinguMgr::GetChangeAllList();
441
0
                Reference< XDictionaryEntry >   xEntry;
442
0
                if (xChangeAllList.is())
443
0
                    xEntry = xChangeAllList->getEntry( xAlt->getWord() );
444
445
0
                if (xEntry.is())
446
0
                {
447
                    // replace word without asking
448
0
                    ReplaceAll( xEntry->getReplacementText() );
449
0
                }
450
0
                else
451
0
                    bSpell = false;
452
0
            }
453
0
        }
454
0
        else if (xHyphWord.is())
455
0
            bSpell = false;
456
0
        else
457
0
        {
458
0
            SpellEnd();
459
0
            bSpell = SpellNext();
460
0
        }
461
0
    }
462
0
    xWait.reset();
463
0
    return GetLast().is();
464
0
}
465
466
467
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */