Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sw/source/uibase/dialog/SwSpellDialogChildWindow.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 <memory>
21
#include <SwSpellDialogChildWindow.hxx>
22
#include <vcl/svapp.hxx>
23
#include <vcl/weld/MessageDialog.hxx>
24
#include <editeng/svxacorr.hxx>
25
#include <editeng/acorrcfg.hxx>
26
#include <sfx2/basedlgs.hxx>
27
#include <sfx2/bindings.hxx>
28
#include <sfx2/dispatch.hxx>
29
#include <sfx2/viewfrm.hxx>
30
#include <wrtsh.hxx>
31
#include <sfx2/printer.hxx>
32
#include <svx/svdoutl.hxx>
33
#include <svx/svdview.hxx>
34
#include <unotools/linguprops.hxx>
35
#include <unotools/lingucfg.hxx>
36
#include <osl/diagnose.h>
37
#include <doc.hxx>
38
#include <IDocumentDeviceAccess.hxx>
39
#include <IDocumentDrawModelAccess.hxx>
40
#include <docsh.hxx>
41
#include <drawdoc.hxx>
42
#include <dcontact.hxx>
43
#include <edtwin.hxx>
44
#include <pam.hxx>
45
#include <drawbase.hxx>
46
#include <unotextrange.hxx>
47
#include <strings.hrc>
48
#include <cmdid.h>
49
50
using namespace ::com::sun::star;
51
using namespace ::com::sun::star::uno;
52
using namespace ::com::sun::star::linguistic2;
53
54
SFX_IMPL_CHILDWINDOW_WITHID(SwSpellDialogChildWindow, FN_SPELL_GRAMMAR_DIALOG)
55
56
struct SpellState
57
{
58
    bool                m_bInitialCall;
59
    bool                m_bLockFocus; // lock the focus notification while a modal dialog is active
60
    bool                m_bLostFocus;
61
62
    // restart and progress information
63
    bool                m_bBodySpelled;  // body already spelled
64
    bool                m_bOtherSpelled; // frames, footnotes, headers and footers spelled
65
    bool                m_bStartedInOther; // started the spelling inside of the _other_ area
66
    bool                m_bStartedInSelection; // there was an initial text selection
67
    std::unique_ptr<SwPaM>
68
                        pOtherCursor; // position where the spelling inside the _other_ area started
69
    bool                m_bDrawingsSpelled; // all drawings spelled
70
    rtl::Reference<SwXTextRange> m_xStartRange; // text range that marks the start of spelling
71
    const SdrObject*    m_pStartDrawing; // draw text object spelling started in
72
    ESelection          m_aStartDrawingSelection; // draw text start selection
73
    bool                m_bRestartDrawing; // the first selected drawing object is found again
74
75
    // lose/get focus information to decide if spelling can be continued
76
    ShellMode           m_eSelMode;
77
    const SwNode*       m_pPointNode;
78
    const SwNode*       m_pMarkNode;
79
    sal_Int32           m_nPointPos;
80
    sal_Int32           m_nMarkPos;
81
    const SdrOutliner*  m_pOutliner;
82
    ESelection          m_aESelection;
83
84
    // iterating over draw text objects
85
    std::list<SdrTextObj*> m_aTextObjects;
86
    bool                m_bTextObjectsCollected;
87
88
    SpellState() :
89
0
        m_bInitialCall(true),
90
0
        m_bLockFocus(false),
91
0
        m_bLostFocus(false),
92
0
        m_bBodySpelled(false),
93
0
        m_bOtherSpelled(false),
94
0
        m_bStartedInOther(false),
95
0
        m_bStartedInSelection(false),
96
0
        m_bDrawingsSpelled(false),
97
0
        m_pStartDrawing(nullptr),
98
0
        m_bRestartDrawing(false),
99
100
0
        m_eSelMode(ShellMode::Object), // initially invalid
101
0
        m_pPointNode(nullptr),
102
0
        m_pMarkNode(nullptr),
103
0
        m_nPointPos(0),
104
0
        m_nMarkPos(0),
105
0
        m_pOutliner(nullptr),
106
0
        m_bTextObjectsCollected(false)
107
0
        {}
108
109
    // reset state in ::InvalidateSpellDialog
110
    void    Reset()
111
0
            {   m_bInitialCall = true;
112
0
                m_bBodySpelled = m_bOtherSpelled = m_bDrawingsSpelled = false;
113
0
                m_xStartRange = nullptr;
114
0
                m_pStartDrawing = nullptr;
115
0
                m_bRestartDrawing = false;
116
0
                m_bTextObjectsCollected = false;
117
0
                m_aTextObjects.clear();
118
0
                m_bStartedInOther = false;
119
0
                pOtherCursor.reset();
120
0
            }
121
};
122
123
static void lcl_LeaveDrawText(SwWrtShell& rSh)
124
0
{
125
0
    if(rSh.GetDrawView())
126
0
    {
127
0
        rSh.GetDrawView()->SdrEndTextEdit( true );
128
0
        Point aPt(LONG_MIN, LONG_MIN);
129
        // go out of the frame
130
0
        rSh.SelectObj(aPt, SW_LEAVE_FRAME);
131
0
        rSh.EnterStdMode();
132
0
        rSh.GetView().AttrChangedNotify(nullptr);
133
0
    }
134
0
}
135
136
static void lcl_updateSpellStateCursorPos(SpellState& rSpellState, const SwWrtShell& rWrtShell)
137
0
{
138
0
    rSpellState.m_eSelMode = rWrtShell.GetView().GetShellMode();
139
0
    rSpellState.m_pPointNode = nullptr;
140
0
    rSpellState.m_pMarkNode = nullptr;
141
0
    rSpellState.m_nPointPos = 0;
142
0
    rSpellState.m_nMarkPos = 0;
143
0
    rSpellState.m_pOutliner = nullptr;
144
145
0
    switch(rSpellState.m_eSelMode)
146
0
    {
147
0
        case ShellMode::Text:
148
0
        case ShellMode::ListText:
149
0
        case ShellMode::TableText:
150
0
        case ShellMode::TableListText:
151
0
        {
152
            // store a node pointer and a pam-position to be able to check on next GetFocus();
153
0
            const SwPaM& rCursor = *rWrtShell.GetCursor();
154
0
            rSpellState.m_pPointNode = &rCursor.GetPointNode();
155
0
            rSpellState.m_pMarkNode = &rCursor.GetMarkNode();
156
0
            rSpellState.m_nPointPos = rCursor.GetPoint()->GetContentIndex();
157
0
            rSpellState.m_nMarkPos = rCursor.GetMark()->GetContentIndex();
158
159
0
        }
160
0
        break;
161
0
        case ShellMode::DrawText:
162
0
        {
163
0
            const SdrView& rSdrView = *rWrtShell.GetDrawView();
164
0
            const SdrOutliner* pOutliner = rSdrView.GetTextEditOutliner();
165
0
            rSpellState.m_pOutliner = pOutliner;
166
0
            const OutlinerView* pOLV = rSdrView.GetTextEditOutlinerView();
167
0
            OSL_ENSURE(pOutliner && pOLV, "no Outliner/OutlinerView in SwSpellDialogChildWindow::LoseFocus()");
168
0
            if (pOLV)
169
0
                rSpellState.m_aESelection = pOLV->GetSelection();
170
0
        }
171
0
        break;
172
0
        default:;// prevent warning
173
0
    }
174
0
}
175
176
SwSpellDialogChildWindow::SwSpellDialogChildWindow (
177
            vcl::Window* _pParent,
178
            sal_uInt16 nId,
179
            SfxBindings* pBindings,
180
            SfxChildWinInfo* /*pInfo*/)
181
0
    : svx::SpellDialogChildWindow (
182
0
        _pParent, nId, pBindings)
183
0
    , m_bIsGrammarCheckingOn(false)
184
0
    , m_pSpellState(new SpellState)
185
0
{
186
0
    SvtLinguConfig().GetProperty( UPN_IS_GRAMMAR_INTERACTIVE ) >>= m_bIsGrammarCheckingOn;
187
0
}
188
189
SwSpellDialogChildWindow::~SwSpellDialogChildWindow ()
190
0
{
191
0
    SwWrtShell* pWrtShell = GetWrtShell_Impl();
192
0
    if(!m_pSpellState->m_bInitialCall && pWrtShell)
193
0
        pWrtShell->SpellEnd();
194
0
    m_pSpellState.reset();
195
0
}
196
197
SfxChildWinInfo SwSpellDialogChildWindow::GetInfo() const
198
0
{
199
0
    SfxChildWinInfo aInfo = svx::SpellDialogChildWindow::GetInfo();
200
0
    aInfo.bVisible = false;
201
0
    return aInfo;
202
0
}
203
204
svx::SpellPortions SwSpellDialogChildWindow::GetNextWrongSentence(bool bRecheck)
205
0
{
206
0
    svx::SpellPortions aRet;
207
0
    SwWrtShell* pWrtShell = GetWrtShell_Impl();
208
0
    if(pWrtShell)
209
0
    {
210
0
        bool bNoDictionaryAvailable = pWrtShell->GetDoc()->IsDictionaryMissing();
211
212
0
        if (!bRecheck)
213
0
        {
214
            // first set continuation point for spell/grammar check to the
215
            // end of the current sentence
216
0
            SwEditShell::MoveContinuationPosToEndOfCheckedSentence();
217
0
        }
218
219
0
        ShellMode eSelMode = pWrtShell->GetView().GetShellMode();
220
0
        bool bDrawText = ShellMode::DrawText == eSelMode;
221
0
        bool bNormalText =
222
0
            ShellMode::TableText == eSelMode ||
223
0
            ShellMode::ListText == eSelMode ||
224
0
            ShellMode::TableListText == eSelMode ||
225
0
            ShellMode::Text == eSelMode;
226
        // Writer text outside of the body
227
0
        bool bOtherText = false;
228
229
0
        if( m_pSpellState->m_bInitialCall )
230
0
        {
231
            // if no text selection exists the cursor has to be set into the text
232
0
            if(!bDrawText && !bNormalText)
233
0
            {
234
0
                MakeTextSelection_Impl(*pWrtShell, eSelMode);
235
                // the selection type has to be checked again - both text types are possible
236
0
                if(pWrtShell->GetSelectionType() & SelectionType::DrawObjectEditMode)
237
0
                    bDrawText = true;
238
0
                bNormalText = !bDrawText;
239
0
            }
240
0
            if(bNormalText)
241
0
            {
242
                // set cursor to the start of the sentence
243
0
                if(!pWrtShell->HasSelection())
244
0
                    pWrtShell->GoStartSentence();
245
0
                else
246
0
                {
247
0
                    pWrtShell->ExpandToSentenceBorders();
248
0
                    m_pSpellState->m_bStartedInSelection = true;
249
0
                }
250
                // determine if the selection is outside of the body text
251
0
                bOtherText = !(pWrtShell->GetFrameType(nullptr,true) & FrameTypeFlags::BODY);
252
0
                if(bOtherText)
253
0
                {
254
0
                    m_pSpellState->pOtherCursor.reset( new SwPaM(*pWrtShell->GetCursor()->GetPoint()) );
255
0
                    m_pSpellState->m_bStartedInOther = true;
256
0
                    pWrtShell->SpellStart( SwDocPositions::OtherStart, SwDocPositions::OtherEnd, SwDocPositions::Curr );
257
0
                }
258
0
                else
259
0
                {
260
                    // mark the start position only if not at start of doc
261
0
                    if(!pWrtShell->IsStartOfDoc())
262
0
                    {
263
                        // Record the position *before* the current cursor, as
264
                        // the word at the current cursor can possibly be
265
                        // replaced by a spellcheck correction which invalidates
266
                        // an XTextRange at this position.
267
0
                        SwDoc *pDoc = pWrtShell->GetDoc();
268
0
                        auto pStart = pWrtShell->GetCursor()->Start();
269
0
                        auto pUnoCursor = pDoc->CreateUnoCursor(*pStart);
270
0
                        pUnoCursor->Left( 1 );
271
0
                        pStart = pUnoCursor->Start();
272
0
                        m_pSpellState->m_xStartRange
273
0
                            = SwXTextRange::CreateXTextRange(*pDoc, *pStart, nullptr);
274
0
                    }
275
0
                    pWrtShell->SpellStart( SwDocPositions::Start, SwDocPositions::End, SwDocPositions::Curr );
276
0
                }
277
0
            }
278
0
            else
279
0
            {
280
0
                SdrView* pSdrView = pWrtShell->GetDrawView();
281
0
                m_pSpellState->m_pStartDrawing = pSdrView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj();
282
0
                OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView();
283
                // start checking at the top of the drawing object
284
0
                pOLV->SetSelection( ESelection() );
285
0
                m_pSpellState->m_aStartDrawingSelection = ESelection();
286
/*
287
Note: spelling in a selection only, or starting in a mid of a drawing object requires
288
further changes elsewhere. (Especially if it should work in sc and sd as well.)
289
The code below would only be part of the solution.
290
(Keeping it as a comment for the time being)
291
                ESelection aCurSel( pOLV->GetSelection() );
292
                ESelection aSentenceSel( pOLV->GetEditView().GetEditEngine()->SelectSentence( aCurSel ) );
293
                if (!aCurSel.HasRange())
294
                {
295
                    aSentenceSel.nEndPara = aSentenceSel.nStartPara;
296
                    aSentenceSel.nEndPos  = aSentenceSel.nStartPos;
297
                }
298
                pOLV->SetSelection( aSentenceSel );
299
                m_pSpellState->m_aStartDrawingSelection = aSentenceSel;
300
*/
301
0
            }
302
303
0
            m_pSpellState->m_bInitialCall = false;
304
0
        }
305
0
        if( bDrawText )
306
0
        {
307
            // spell inside of the current draw text
308
0
            if(!SpellDrawText_Impl(*pWrtShell, aRet))
309
0
            {
310
0
                if(!FindNextDrawTextError_Impl(*pWrtShell) || !SpellDrawText_Impl(*pWrtShell, aRet))
311
0
                {
312
0
                    lcl_LeaveDrawText(*pWrtShell);
313
                    // now the drawings have been spelled
314
0
                    m_pSpellState->m_bDrawingsSpelled = true;
315
                    // the spelling continues at the other content
316
                    // if there's any that has not been spelled yet
317
0
                    if(!m_pSpellState->m_bOtherSpelled && pWrtShell->HasOtherCnt())
318
0
                    {
319
0
                        pWrtShell->SpellStart(SwDocPositions::OtherStart, SwDocPositions::OtherEnd, SwDocPositions::OtherStart );
320
0
                        if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn))
321
0
                        {
322
0
                            pWrtShell->SpellEnd();
323
0
                            m_pSpellState->m_bOtherSpelled = true;
324
0
                        }
325
0
                    }
326
0
                    else
327
0
                        m_pSpellState->m_bOtherSpelled = true;
328
                    // if no result has been found try at the body text - completely
329
0
                    if(!m_pSpellState->m_bBodySpelled && aRet.empty())
330
0
                    {
331
0
                        pWrtShell->SpellStart(SwDocPositions::Start, SwDocPositions::End, SwDocPositions::Start );
332
0
                        if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn))
333
0
                        {
334
0
                            m_pSpellState->m_bBodySpelled = true;
335
0
                            pWrtShell->SpellEnd();
336
0
                        }
337
0
                    }
338
339
0
                }
340
0
            }
341
0
        }
342
0
        else
343
0
        {
344
            // spell inside of the Writer text
345
0
            if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn))
346
0
            {
347
                // if there is a selection (within body or header/footer text)
348
                // then spell/grammar checking should not move outside of it.
349
0
                if (!m_pSpellState->m_bStartedInSelection)
350
0
                {
351
                    // find out which text has been spelled body or other
352
0
                    bOtherText = !(pWrtShell->GetFrameType(nullptr,true) & FrameTypeFlags::BODY);
353
0
                    if(bOtherText && m_pSpellState->m_bStartedInOther && m_pSpellState->pOtherCursor)
354
0
                    {
355
0
                        m_pSpellState->m_bStartedInOther = false;
356
0
                        pWrtShell->SetSelection(*m_pSpellState->pOtherCursor);
357
0
                        pWrtShell->SpellEnd();
358
0
                        m_pSpellState->pOtherCursor.reset();
359
0
                        pWrtShell->SpellStart(SwDocPositions::OtherStart, SwDocPositions::Curr, SwDocPositions::OtherStart );
360
0
                        (void)pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn);
361
0
                    }
362
0
                    if(aRet.empty())
363
0
                    {
364
                        // end spelling
365
0
                        pWrtShell->SpellEnd();
366
0
                        if(bOtherText)
367
0
                        {
368
0
                            m_pSpellState->m_bOtherSpelled = true;
369
                            // has the body been spelled?
370
0
                            if(!m_pSpellState->m_bBodySpelled)
371
0
                            {
372
0
                                pWrtShell->SpellStart(SwDocPositions::Start, SwDocPositions::End, SwDocPositions::Start );
373
0
                                if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn))
374
0
                                {
375
0
                                    m_pSpellState->m_bBodySpelled = true;
376
0
                                    pWrtShell->SpellEnd();
377
0
                                }
378
0
                            }
379
0
                        }
380
0
                        else
381
0
                        {
382
0
                             m_pSpellState->m_bBodySpelled = true;
383
0
                             if(!m_pSpellState->m_bOtherSpelled && pWrtShell->HasOtherCnt())
384
0
                             {
385
0
                                pWrtShell->SpellStart(SwDocPositions::OtherStart, SwDocPositions::OtherEnd, SwDocPositions::OtherStart );
386
0
                                if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn))
387
0
                                {
388
0
                                    pWrtShell->SpellEnd();
389
0
                                    m_pSpellState->m_bOtherSpelled = true;
390
0
                                }
391
0
                             }
392
0
                             else
393
0
                                 m_pSpellState->m_bOtherSpelled = true;
394
0
                        }
395
0
                    }
396
397
                    // search for a draw text object that contains error and spell it
398
0
                    if(aRet.empty() &&
399
0
                            (m_pSpellState->m_bDrawingsSpelled ||
400
0
                            !FindNextDrawTextError_Impl(*pWrtShell) || !SpellDrawText_Impl(*pWrtShell, aRet)))
401
0
                    {
402
0
                        lcl_LeaveDrawText(*pWrtShell);
403
0
                        m_pSpellState->m_bDrawingsSpelled = true;
404
0
                    }
405
0
                }
406
0
            }
407
0
        }
408
        // now only the rest of the body text can be spelled -
409
        // if the spelling started inside of the body
410
0
        bool bCloseMessage = true;
411
0
        if(aRet.empty() && !m_pSpellState->m_bStartedInSelection)
412
0
        {
413
0
            OSL_ENSURE(m_pSpellState->m_bDrawingsSpelled &&
414
0
                        m_pSpellState->m_bOtherSpelled && m_pSpellState->m_bBodySpelled,
415
0
                        "not all parts of the document are already spelled");
416
0
            if( m_pSpellState->m_xStartRange.is() && !bNoDictionaryAvailable )
417
0
            {
418
0
                LockFocusNotification( true );
419
0
                std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(GetController()->getDialog(),
420
0
                                                                                        VclMessageType::Question, VclButtonsType::YesNo, SwResId(STR_QUERY_SPELL_CONTINUE)));
421
0
                sal_uInt16 nRet = xBox->run();
422
0
                if (RET_YES == nRet)
423
0
                {
424
0
                    SwUnoInternalPaM aPam(*pWrtShell->GetDoc());
425
0
                    if (::sw::XTextRangeToSwPaM(aPam,
426
0
                                m_pSpellState->m_xStartRange))
427
0
                    {
428
0
                        pWrtShell->SetSelection(aPam);
429
0
                        pWrtShell->SpellStart(SwDocPositions::Start, SwDocPositions::Curr, SwDocPositions::Start);
430
0
                        if(!pWrtShell->SpellSentence(aRet, m_bIsGrammarCheckingOn))
431
0
                            pWrtShell->SpellEnd();
432
0
                    }
433
0
                    m_pSpellState->m_xStartRange = nullptr;
434
0
                    LockFocusNotification( false );
435
0
                }
436
0
                else
437
0
                    bCloseMessage = false; // no closing message if a wrap around has been denied
438
0
            }
439
0
        }
440
441
0
        lcl_updateSpellStateCursorPos(*m_pSpellState, *pWrtShell);
442
443
0
        if( aRet.empty() && bCloseMessage )
444
0
        {
445
0
            LockFocusNotification( true );
446
0
            OUString sInfo( SwResId( bNoDictionaryAvailable ? STR_DICTIONARY_UNAVAILABLE : STR_SPELLING_COMPLETED ) );
447
0
            auto xSpellController = GetController();
448
            // #i84610#
449
0
            std::unique_ptr<weld::MessageDialog> xBox(
450
0
                Application::CreateMessageDialog( xSpellController->getDialog(),
451
0
                                                  VclMessageType::Info,
452
0
                                                  VclButtonsType::Ok,
453
0
                                                  sInfo ) );
454
0
            xBox->run();
455
0
            LockFocusNotification( false );
456
0
            xSpellController->getDialog()->grab_focus();
457
0
        }
458
0
    }
459
0
    return aRet;
460
0
}
461
462
void SwSpellDialogChildWindow::ApplyChangedSentence(const svx::SpellPortions& rChanged, bool bRecheck)
463
0
{
464
0
    SwWrtShell* pWrtShell = GetWrtShell_Impl();
465
0
    if(!pWrtShell || m_pSpellState->m_bInitialCall)
466
0
        return;
467
468
0
    ShellMode eSelMode = pWrtShell->GetView().GetShellMode();
469
0
    bool bDrawText = ShellMode::DrawText == eSelMode;
470
0
    bool bNormalText =
471
0
        ShellMode::TableText == eSelMode ||
472
0
        ShellMode::ListText == eSelMode ||
473
0
        ShellMode::TableListText == eSelMode ||
474
0
        ShellMode::Text == eSelMode;
475
476
    // evaluate if the same sentence should be rechecked or not.
477
    // Sentences that got grammar checked should always be rechecked in order
478
    // to detect possible errors that get introduced with the changes
479
0
    bRecheck |= SwEditShell::HasLastSentenceGotGrammarChecked();
480
481
0
    if(bNormalText)
482
0
        pWrtShell->ApplyChangedSentence(rChanged, bRecheck);
483
0
    else if(bDrawText )
484
0
    {
485
0
        SdrView* pDrView = pWrtShell->GetDrawView();
486
0
        SdrOutliner *pOutliner = pDrView->GetTextEditOutliner();
487
0
        pOutliner->ApplyChangedSentence(pDrView->GetTextEditOutlinerView()->GetEditView(), rChanged, bRecheck);
488
0
    }
489
0
}
490
491
void SwSpellDialogChildWindow::AddAutoCorrection(
492
        const OUString& rOld, const OUString& rNew, LanguageType eLanguage)
493
0
{
494
0
    SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
495
0
    pACorr->PutText( rOld, rNew, eLanguage );
496
0
}
497
498
bool SwSpellDialogChildWindow::HasAutoCorrection()
499
0
{
500
0
    return true;
501
0
}
502
503
bool SwSpellDialogChildWindow::HasGrammarChecking()
504
0
{
505
0
    return SvtLinguConfig().HasGrammarChecker();
506
0
}
507
508
bool SwSpellDialogChildWindow::IsGrammarChecking()
509
0
{
510
0
    return m_bIsGrammarCheckingOn;
511
0
}
512
513
void SwSpellDialogChildWindow::SetGrammarChecking(bool bOn)
514
0
{
515
0
    uno::Any aVal;
516
0
    aVal <<= bOn;
517
0
    m_bIsGrammarCheckingOn = bOn;
518
0
    SvtLinguConfig().SetProperty( UPN_IS_GRAMMAR_INTERACTIVE, aVal );
519
    // set current spell position to the start of the current sentence to
520
    // continue with this sentence after grammar checking state has been changed
521
0
    SwWrtShell* pWrtShell = GetWrtShell_Impl();
522
0
    if(!pWrtShell)
523
0
        return;
524
525
0
    ShellMode eSelMode = pWrtShell->GetView().GetShellMode();
526
0
    bool bDrawText = ShellMode::DrawText == eSelMode;
527
0
    bool bNormalText =
528
0
        ShellMode::TableText == eSelMode ||
529
0
        ShellMode::ListText == eSelMode ||
530
0
        ShellMode::TableListText == eSelMode ||
531
0
        ShellMode::Text == eSelMode;
532
0
    if( bNormalText )
533
0
        SwEditShell::PutSpellingToSentenceStart();
534
0
    else if( bDrawText )
535
0
    {
536
0
        SdrView*     pSdrView = pWrtShell->GetDrawView();
537
0
        SdrOutliner* pOutliner = pSdrView ? pSdrView->GetTextEditOutliner() : nullptr;
538
0
        OSL_ENSURE(pOutliner, "No Outliner in SwSpellDialogChildWindow::SetGrammarChecking");
539
0
        if(pOutliner)
540
0
        {
541
0
            pOutliner->PutSpellingToSentenceStart( pSdrView->GetTextEditOutlinerView()->GetEditView() );
542
0
        }
543
0
    }
544
0
}
545
546
void SwSpellDialogChildWindow::GetFocus(bool bForceResume)
547
0
{
548
0
    if(m_pSpellState->m_bLockFocus)
549
0
        return;
550
0
    SwWrtShell* pWrtShell = GetWrtShell_Impl();
551
0
    bool bInvalidate = bForceResume || !pWrtShell || m_pSpellState->m_bInitialCall;
552
0
    if (!bInvalidate)
553
0
    {
554
0
        ShellMode eSelMode = pWrtShell->GetView().GetShellMode();
555
0
        if(eSelMode != m_pSpellState->m_eSelMode)
556
0
        {
557
            // prevent initial invalidation
558
0
            if(m_pSpellState->m_bLostFocus)
559
0
                bInvalidate = true;
560
0
        }
561
0
        else
562
0
        {
563
0
            switch(m_pSpellState->m_eSelMode)
564
0
            {
565
0
                case ShellMode::Text:
566
0
                case ShellMode::ListText:
567
0
                case ShellMode::TableText:
568
0
                case ShellMode::TableListText:
569
0
                {
570
0
                    SwPaM* pCursor = pWrtShell->GetCursor();
571
0
                    if(m_pSpellState->m_pPointNode != &pCursor->GetPointNode() ||
572
0
                        m_pSpellState->m_pMarkNode != &pCursor->GetMarkNode()||
573
0
                        m_pSpellState->m_nPointPos != pCursor->GetPoint()->GetContentIndex()||
574
0
                        m_pSpellState->m_nMarkPos != pCursor->GetMark()->GetContentIndex())
575
0
                            bInvalidate = true;
576
0
                }
577
0
                break;
578
0
                case ShellMode::DrawText:
579
0
                {
580
0
                    SdrView*     pSdrView = pWrtShell->GetDrawView();
581
0
                    SdrOutliner* pOutliner = pSdrView ? pSdrView->GetTextEditOutliner() : nullptr;
582
0
                    if(!pOutliner || m_pSpellState->m_pOutliner != pOutliner)
583
0
                        bInvalidate = true;
584
0
                    else
585
0
                    {
586
0
                        OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView();
587
0
                        OSL_ENSURE(pOLV, "no OutlinerView in SwSpellDialogChildWindow::GetFocus()");
588
0
                        if(!pOLV || m_pSpellState->m_aESelection != pOLV->GetSelection())
589
0
                            bInvalidate = true;
590
0
                    }
591
0
                }
592
0
                break;
593
0
                default: bInvalidate = true;
594
0
            }
595
0
        }
596
0
    }
597
598
0
    if(bInvalidate)
599
0
        InvalidateSpellDialog();
600
0
}
601
602
void SwSpellDialogChildWindow::LoseFocus()
603
0
{
604
    // prevent initial invalidation
605
0
    m_pSpellState->m_bLostFocus = true;
606
0
    if(m_pSpellState->m_bLockFocus)
607
0
        return;
608
0
    SwWrtShell* pWrtShell = GetWrtShell_Impl();
609
0
    if(pWrtShell)
610
0
        lcl_updateSpellStateCursorPos(*m_pSpellState, *pWrtShell);
611
0
    else
612
0
        m_pSpellState->m_eSelMode = ShellMode::Object;
613
0
}
614
615
void SwSpellDialogChildWindow::InvalidateSpellDialog()
616
0
{
617
0
    SwWrtShell* pWrtShell = GetWrtShell_Impl();
618
0
    if(!m_pSpellState->m_bInitialCall && pWrtShell)
619
0
        pWrtShell->SpellEnd(nullptr, false);
620
0
    m_pSpellState->Reset();
621
0
    svx::SpellDialogChildWindow::InvalidateSpellDialog();
622
0
}
623
624
SwWrtShell* SwSpellDialogChildWindow::GetWrtShell_Impl()
625
0
{
626
0
    SfxDispatcher* pDispatch = GetBindings().GetDispatcher();
627
0
    SwView* pView = nullptr;
628
0
    if(pDispatch)
629
0
    {
630
0
        sal_uInt16 nShellIdx = 0;
631
0
        SfxShell* pShell;
632
0
        while(nullptr != (pShell = pDispatch->GetShell(nShellIdx++)))
633
0
            if(auto pSwView = dynamic_cast< SwView *>( pShell ))
634
0
            {
635
0
                pView = pSwView;
636
0
                break;
637
0
            }
638
0
    }
639
0
    return pView ? pView->GetWrtShellPtr(): nullptr;
640
0
}
641
642
// set the cursor into the body text - necessary if any object is selected
643
// on start of the spelling dialog
644
void SwSpellDialogChildWindow::MakeTextSelection_Impl(SwWrtShell& rShell, ShellMode eSelMode)
645
0
{
646
0
    SwView& rView = rShell.GetView();
647
0
    switch(eSelMode)
648
0
    {
649
0
        case ShellMode::Text:
650
0
        case ShellMode::ListText:
651
0
        case ShellMode::TableText:
652
0
        case ShellMode::TableListText:
653
0
        case ShellMode::DrawText:
654
0
            OSL_FAIL("text already active in SwSpellDialogChildWindow::MakeTextSelection_Impl()");
655
0
        break;
656
657
0
        case ShellMode::Frame:
658
0
        {
659
0
            rShell.UnSelectFrame();
660
0
            rShell.LeaveSelFrameMode();
661
0
            rView.AttrChangedNotify(nullptr);
662
0
        }
663
0
        break;
664
665
0
        case ShellMode::Draw:
666
0
        case ShellMode::DrawForm:
667
0
        case ShellMode::Bezier:
668
0
            if(FindNextDrawTextError_Impl(rShell))
669
0
            {
670
0
                rView.AttrChangedNotify(nullptr);
671
0
                break;
672
0
            }
673
0
            [[fallthrough]]; // to deselect the object
674
0
        case ShellMode::Graphic:
675
0
        case ShellMode::Object:
676
0
        {
677
0
            if ( rShell.IsDrawCreate() )
678
0
            {
679
0
                rView.GetDrawFuncPtr()->BreakCreate();
680
0
                rView.AttrChangedNotify(nullptr);
681
0
            }
682
0
            else if ( rShell.HasSelection() || rView.IsDrawMode() )
683
0
            {
684
0
                SdrView *pSdrView = rShell.GetDrawView();
685
0
                if(pSdrView && pSdrView->GetMarkedObjectList().GetMarkCount() != 0 &&
686
0
                    pSdrView->GetHdlList().GetFocusHdl())
687
0
                {
688
0
                    const_cast<SdrHdlList&>(pSdrView->GetHdlList()).ResetFocusHdl();
689
0
                }
690
0
                else
691
0
                {
692
0
                    rView.LeaveDrawCreate();
693
0
                    Point aPt(LONG_MIN, LONG_MIN);
694
                    // go out of the frame
695
0
                    rShell.SelectObj(aPt, SW_LEAVE_FRAME);
696
0
                    SfxBindings& rBind = rView.GetViewFrame().GetBindings();
697
0
                    rBind.Invalidate( SID_ATTR_SIZE );
698
0
                    rShell.EnterStdMode();
699
0
                    rView.AttrChangedNotify(nullptr);
700
0
                }
701
0
            }
702
0
        }
703
0
        break;
704
0
        default:; // prevent warning
705
0
    }
706
0
}
707
708
// select the next draw text object that has a spelling error
709
bool SwSpellDialogChildWindow::FindNextDrawTextError_Impl(SwWrtShell& rSh)
710
0
{
711
0
    bool bNextDoc = false;
712
0
    SdrView* pDrView = rSh.GetDrawView();
713
0
    if(!pDrView)
714
0
        return bNextDoc;
715
0
    SwView& rView = rSh.GetView();
716
0
    SwDoc* pDoc = rView.GetDocShell()->GetDoc();
717
0
    const SdrMarkList& rMarkList = pDrView->GetMarkedObjectList();
718
    // start at the current draw object - if there is any selected
719
0
    SdrTextObj* pCurrentTextObj = nullptr;
720
0
    if ( rMarkList.GetMarkCount() == 1 )
721
0
    {
722
0
        SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
723
0
        if( auto pSdrTextObj = DynCastSdrTextObj( pObj ) )
724
0
            pCurrentTextObj = pSdrTextObj;
725
0
    }
726
    // at first fill the list of drawing objects
727
0
    if(!m_pSpellState->m_bTextObjectsCollected )
728
0
    {
729
0
        m_pSpellState->m_bTextObjectsCollected = true;
730
0
        SwDrawContact::GetTextObjectsFromFormat(m_pSpellState->m_aTextObjects, *pDoc);
731
0
        if(pCurrentTextObj)
732
0
        {
733
0
            m_pSpellState->m_aTextObjects.remove(pCurrentTextObj);
734
0
            m_pSpellState->m_aTextObjects.push_back(pCurrentTextObj);
735
0
        }
736
0
    }
737
0
    if(!m_pSpellState->m_aTextObjects.empty())
738
0
    {
739
0
        Reference< XSpellChecker1 >  xSpell( GetSpellChecker() );
740
0
        while(!bNextDoc && !m_pSpellState->m_aTextObjects.empty())
741
0
        {
742
0
            std::list<SdrTextObj*>::iterator aStart = m_pSpellState->m_aTextObjects.begin();
743
0
            SdrTextObj* pTextObj = *aStart;
744
0
            if(m_pSpellState->m_pStartDrawing == pTextObj)
745
0
                m_pSpellState->m_bRestartDrawing = true;
746
0
            m_pSpellState->m_aTextObjects.erase(aStart);
747
0
            OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject();
748
0
            if ( pParaObj )
749
0
            {
750
0
                bool bHasSpellError = false;
751
0
                {
752
0
                    SdrOutliner aTmpOutliner(pDoc->getIDocumentDrawModelAccess().GetDrawModel()->
753
0
                                             GetDrawOutliner().GetEmptyItemSet().GetPool(),
754
0
                                                OutlinerMode::TextObject );
755
0
                    aTmpOutliner.SetRefDevice( pDoc->getIDocumentDeviceAccess().getPrinter( false ) );
756
0
                    MapMode aMapMode (MapUnit::MapTwip);
757
0
                    aTmpOutliner.SetRefMapMode(aMapMode);
758
0
                    aTmpOutliner.SetPaperSize( pTextObj->GetLogicRect().GetSize() );
759
0
                    aTmpOutliner.SetSpeller( xSpell );
760
761
0
                    OutlinerView aOutlView( aTmpOutliner, &(rView.GetEditWin()) );
762
0
                    aOutlView.GetOutliner().SetRefDevice( rSh.getIDocumentDeviceAccess().getPrinter( false ) );
763
0
                    aTmpOutliner.InsertView( &aOutlView );
764
0
                    Size aSize(1,1);
765
0
                    tools::Rectangle aRect( Point(), aSize );
766
0
                    aOutlView.SetOutputArea( aRect );
767
0
                    aTmpOutliner.SetText( *pParaObj );
768
0
                    aTmpOutliner.ClearModifyFlag();
769
0
                    bHasSpellError = EESpellState::Ok != aTmpOutliner.HasSpellErrors();
770
0
                    aTmpOutliner.RemoveView( &aOutlView );
771
0
                }
772
0
                if(bHasSpellError)
773
0
                {
774
                    // now the current one has to be deselected
775
0
                    if(pCurrentTextObj)
776
0
                        pDrView->SdrEndTextEdit( true );
777
                    // and the found one should be activated
778
0
                    rSh.MakeVisible(SwRect(pTextObj->GetLogicRect()));
779
0
                    Point aTmp( 0,0 );
780
0
                    rSh.SelectObj( aTmp, 0, pTextObj );
781
0
                    SdrPageView* pPV = pDrView->GetSdrPageView();
782
0
                    rView.BeginTextEdit( pTextObj, pPV, &rView.GetEditWin(), false, true );
783
0
                    rView.AttrChangedNotify(nullptr);
784
0
                    bNextDoc = true;
785
0
                }
786
0
            }
787
0
        }
788
0
    }
789
0
    return bNextDoc;
790
0
}
791
792
bool SwSpellDialogChildWindow::SpellDrawText_Impl(SwWrtShell& rSh, svx::SpellPortions& rPortions)
793
0
{
794
0
    bool bRet = false;
795
0
    SdrView*     pSdrView = rSh.GetDrawView();
796
0
    SdrOutliner* pOutliner = pSdrView ? pSdrView->GetTextEditOutliner() : nullptr;
797
0
    OSL_ENSURE(pOutliner, "No Outliner in SwSpellDialogChildWindow::SpellDrawText_Impl");
798
0
    if(pOutliner)
799
0
    {
800
0
        bRet = pOutliner->SpellSentence(pSdrView->GetTextEditOutlinerView()->GetEditView(), rPortions);
801
        // find out if the current selection is in the first spelled drawing object
802
        // and behind the initial selection
803
0
        if(bRet && m_pSpellState->m_bRestartDrawing)
804
0
        {
805
0
            OutlinerView* pOLV = pSdrView->GetTextEditOutlinerView();
806
0
            ESelection aCurrentSelection = pOLV->GetSelection();
807
0
            if (m_pSpellState->m_aStartDrawingSelection.end < aCurrentSelection.end)
808
0
            {
809
0
                bRet = false;
810
0
                rPortions.clear();
811
0
            }
812
0
        }
813
0
    }
814
0
    return bRet;
815
0
}
816
817
void SwSpellDialogChildWindow::LockFocusNotification(bool bLock)
818
0
{
819
0
    if (!m_pSpellState)
820
0
    {
821
0
        return;
822
0
    }
823
824
0
    OSL_ENSURE(m_pSpellState->m_bLockFocus != bLock, "invalid locking - no change of state");
825
0
    m_pSpellState->m_bLockFocus = bLock;
826
0
}
827
828
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */