Coverage Report

Created: 2026-08-14 10:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sw/source/uibase/wrtsh/wrtsh3.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 <svx/svxids.hrc>
21
#include <sfx2/app.hxx>
22
#include <sfx2/bindings.hxx>
23
#include <sfx2/viewfrm.hxx>
24
#include <svx/svdmark.hxx>
25
#include <svx/svdview.hxx>
26
#include <svx/svdouno.hxx>
27
#include <svx/srchdlg.hxx>
28
#include <com/sun/star/form/FormButtonType.hpp>
29
#include <com/sun/star/beans/XPropertySet.hpp>
30
#include <osl/diagnose.h>
31
#include <sfx2/dispatch.hxx>
32
#include <comphelper/lok.hxx>
33
#include <tools/json_writer.hxx>
34
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
35
36
#include <swmodule.hxx>
37
#include <wrtsh.hxx>
38
#include <view.hxx>
39
#include <IMark.hxx>
40
#include <doc.hxx>
41
#include <formatcontentcontrol.hxx>
42
#include <IDocumentUndoRedo.hxx>
43
#include <SwRewriter.hxx>
44
#include <strings.hrc>
45
#include <textcontentcontrol.hxx>
46
47
#include <vector>
48
#include <set>
49
#include <IDocumentContentOperations.hxx>
50
#include <ndtxt.hxx>
51
52
using namespace ::com::sun::star;
53
54
bool SwWrtShell::MoveBookMark( BookMarkMove eFuncId, const ::sw::mark::MarkBase* const pMark)
55
0
{
56
0
    addCurrentPosition();
57
0
    (this->*m_fnKillSel)( nullptr, false, ScrollSizeMode::ScrollSizeDefault );
58
59
0
    bool bRet = true;
60
0
    switch(eFuncId)
61
0
    {
62
0
        case BOOKMARK_INDEX:bRet = SwCursorShell::GotoMark( pMark );break;
63
0
        case BOOKMARK_NEXT: bRet = SwCursorShell::GoNextBookmark();break;
64
0
        case BOOKMARK_PREV: bRet = SwCursorShell::GoPrevBookmark();break;
65
0
        default:;//prevent warning
66
0
    }
67
68
0
    if( bRet && IsSelFrameMode() )
69
0
    {
70
0
        UnSelectFrame();
71
0
        LeaveSelFrameMode();
72
0
    }
73
0
    if( IsSelection() )
74
0
    {
75
0
        m_fnKillSel = &SwWrtShell::ResetSelect;
76
0
        m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
77
0
    }
78
0
    return bRet;
79
0
}
80
81
bool SwWrtShell::GotoField( const SwFormatField& rField )
82
0
{
83
0
    (this->*m_fnKillSel)( nullptr, false, ScrollSizeMode::ScrollSizeDefault );
84
85
0
    bool bRet = SwCursorShell::GotoFormatField( rField );
86
0
    if( bRet && IsSelFrameMode() )
87
0
    {
88
0
        UnSelectFrame();
89
0
        LeaveSelFrameMode();
90
0
    }
91
92
0
    if( IsSelection() )
93
0
    {
94
0
        m_fnKillSel = &SwWrtShell::ResetSelect;
95
0
        m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
96
0
    }
97
98
0
    return bRet;
99
0
}
100
101
bool SwWrtShell::GotoContentControl(const SwFormatContentControl& rContentControl,
102
                                    bool bOnlyRefresh)
103
0
{
104
0
    const std::shared_ptr<SwContentControl>& pContentControl = rContentControl.GetContentControl();
105
0
    if (IsFrameSelected() && pContentControl && pContentControl->GetPicture())
106
0
    {
107
        // A frame is already selected, and its anchor is inside a picture content control.
108
0
        if (pContentControl->GetShowingPlaceHolder())
109
0
        {
110
            // Replace the placeholder image with a real one.
111
0
            GetView().StopShellTimer();
112
0
            if (comphelper::LibreOfficeKit::isActive())
113
0
            {
114
0
                tools::JsonWriter aJson;
115
0
                aJson.put("action", "change-picture");
116
0
                OString pJson(aJson.finishAndGetAsOString());
117
0
                if (SfxViewShell* pNotifySh = GetSfxViewShell())
118
0
                    pNotifySh->libreOfficeKitViewCallback(LOK_CALLBACK_CONTENT_CONTROL,
119
0
                                                              pJson);
120
0
            }
121
0
            else
122
0
            {
123
0
                GetView().GetViewFrame().GetDispatcher()->Execute(SID_CHANGE_PICTURE,
124
0
                                                                   SfxCallMode::SYNCHRON);
125
0
            }
126
0
            pContentControl->SetShowingPlaceHolder(false);
127
0
        }
128
0
        return true;
129
0
    }
130
131
0
    (this->*m_fnKillSel)(nullptr, false, ScrollSizeMode::ScrollSizeDefault);
132
133
0
    bool bRet = SwCursorShell::GotoFormatContentControl(rContentControl);
134
135
0
    if (bRet && pContentControl && pContentControl->GetCheckbox())
136
0
    {
137
        // Checkbox: GotoFormatContentControl() selected the old state.
138
0
        LockView(/*bViewLocked=*/true);
139
0
        OUString aOldState = GetCursorDescr();
140
0
        OUString aNewState;
141
0
        if (pContentControl->GetChecked())
142
0
            aNewState = bOnlyRefresh ? pContentControl->GetCheckedState()
143
0
                                     : pContentControl->GetUncheckedState();
144
0
        else
145
0
            aNewState = bOnlyRefresh ? pContentControl->GetUncheckedState()
146
0
                                     : pContentControl->GetCheckedState();
147
148
0
        SwRewriter aRewriter;
149
0
        aRewriter.AddRule(UndoArg1, aOldState);
150
0
        aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
151
0
        aRewriter.AddRule(UndoArg3, aNewState);
152
0
        GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter);
153
154
        // Toggle the state.
155
0
        pContentControl->SetReadWrite(true);
156
0
        DelLeft();
157
0
        if (!bOnlyRefresh)
158
0
            pContentControl->SetChecked(!pContentControl->GetChecked());
159
0
        Insert(aNewState);
160
0
        pContentControl->SetReadWrite(false);
161
162
0
        GetIDocumentUndoRedo().EndUndo(SwUndoId::REPLACE, &aRewriter);
163
0
        LockView(/*bViewLocked=*/false);
164
0
        ShowCursor();
165
0
    }
166
0
    else if (bRet && pContentControl && pContentControl->GetSelectedListItem())
167
0
    {
168
        // Dropdown: GotoFormatContentControl() selected the old content.
169
0
        size_t nSelectedListItem = *pContentControl->GetSelectedListItem();
170
0
        LockView(/*bViewLocked=*/true);
171
0
        OUString aOldState = GetCursorDescr();
172
0
        OUString aNewState = pContentControl->GetListItems()[nSelectedListItem].ToString();
173
0
        SwRewriter aRewriter;
174
0
        aRewriter.AddRule(UndoArg1, aOldState);
175
0
        aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
176
0
        aRewriter.AddRule(UndoArg3, SwResId(STR_START_QUOTE) + aNewState + SwResId(STR_END_QUOTE));
177
0
        GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter);
178
179
        // Update the content.
180
0
        pContentControl->SetReadWrite(true);
181
0
        DelLeft();
182
0
        pContentControl->SetSelectedListItem(std::nullopt);
183
0
        Insert(aNewState);
184
0
        pContentControl->SetReadWrite(false);
185
186
0
        GetIDocumentUndoRedo().EndUndo(SwUndoId::REPLACE, &aRewriter);
187
0
        LockView(/*bViewLocked=*/false);
188
0
        ShowCursor();
189
0
    }
190
0
    else if (bRet && pContentControl && pContentControl->GetSelectedDate())
191
0
    {
192
        // Date: GotoFormatContentControl() selected the old content.
193
0
        LockView(/*bViewLocked=*/true);
194
0
        OUString aOldState = GetCursorDescr();
195
0
        OUString aNewState = pContentControl->GetDateString();
196
0
        SwRewriter aRewriter;
197
0
        aRewriter.AddRule(UndoArg1, aOldState);
198
0
        aRewriter.AddRule(UndoArg2, SwResId(STR_YIELDS));
199
0
        aRewriter.AddRule(UndoArg3, SwResId(STR_START_QUOTE) + aNewState + SwResId(STR_END_QUOTE));
200
0
        GetIDocumentUndoRedo().StartUndo(SwUndoId::REPLACE, &aRewriter);
201
202
        // Write the doc model.
203
0
        pContentControl->SetCurrentDateValue(*pContentControl->GetSelectedDate());
204
0
        pContentControl->SetSelectedDate(std::nullopt);
205
206
        // Update the content.
207
0
        DelLeft();
208
0
        Insert(aNewState);
209
210
0
        GetIDocumentUndoRedo().EndUndo(SwUndoId::REPLACE, &aRewriter);
211
0
        LockView(/*bViewLocked=*/false);
212
0
        ShowCursor();
213
0
    }
214
215
0
    if (bRet && IsSelFrameMode())
216
0
    {
217
0
        UnSelectFrame();
218
0
        LeaveSelFrameMode();
219
0
    }
220
221
0
    if (IsSelection())
222
0
    {
223
0
        m_fnKillSel = &SwWrtShell::ResetSelect;
224
0
        m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
225
0
    }
226
227
0
    return bRet;
228
0
}
229
230
// Invalidate FontWork-Slots
231
232
void SwWrtShell::DrawSelChanged( )
233
30
{
234
30
    static sal_uInt16 const aInval[] =
235
30
    {
236
30
        SID_ATTR_FILL_STYLE, SID_ATTR_FILL_COLOR, SID_ATTR_LINE_STYLE,
237
30
        SID_ATTR_LINE_WIDTH, SID_ATTR_LINE_COLOR,
238
        /*AF: these may be needed for the sidebar.
239
        SID_SVX_AREA_TRANSPARENCY, SID_SVX_AREA_TRANSP_GRADIENT,
240
        SID_SVX_AREA_TRANS_TYPE,
241
        */
242
30
        0
243
30
    };
244
245
30
    GetView().GetViewFrame().GetBindings().Invalidate(aInval);
246
247
30
    bool bOldVal = g_bNoInterrupt;
248
30
    g_bNoInterrupt = true;    // Trick to run AttrChangedNotify by timer.
249
30
    GetView().AttrChangedNotify(nullptr);
250
30
    g_bNoInterrupt = bOldVal;
251
30
}
252
253
void SwWrtShell::GotoMark( const SwMarkName& rName )
254
0
{
255
0
    auto ppMark = getIDocumentMarkAccess()->findMark( rName );
256
0
    if (ppMark == getIDocumentMarkAccess()->getAllMarksEnd())
257
0
        return;
258
0
    MoveBookMark( BOOKMARK_INDEX, *ppMark );
259
0
}
260
261
void SwWrtShell::GotoMark( const ::sw::mark::MarkBase* const pMark )
262
0
{
263
0
    MoveBookMark( BOOKMARK_INDEX, pMark );
264
0
}
265
266
bool SwWrtShell::GoNextBookmark()
267
0
{
268
0
    if ( !getIDocumentMarkAccess()->getBookmarksCount() )
269
0
    {
270
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
271
0
        return false;
272
0
    }
273
0
    LockView( true );
274
0
    bool bRet = MoveBookMark( BOOKMARK_NEXT );
275
0
    if ( !bRet )
276
0
    {
277
0
        MoveBookMark( BOOKMARK_INDEX, *getIDocumentMarkAccess()->getBookmarksBegin() );
278
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
279
0
    }
280
0
    else
281
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
282
0
    LockView( false );
283
0
    ShowCursor();
284
0
    return true;
285
0
}
286
287
bool SwWrtShell::GoPrevBookmark()
288
0
{
289
0
    if ( !getIDocumentMarkAccess()->getBookmarksCount() )
290
0
    {
291
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
292
0
        return false;
293
0
    }
294
0
    LockView( true );
295
0
    bool bRet = MoveBookMark( BOOKMARK_PREV );
296
0
    if ( !bRet )
297
0
    {
298
0
        MoveBookMark( BOOKMARK_INDEX, *( getIDocumentMarkAccess()->getBookmarksEnd() - 1 ) );
299
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
300
0
    }
301
0
    else
302
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
303
0
    LockView( false );
304
0
    ShowCursor();
305
0
    return true;
306
0
}
307
308
void SwWrtShell::ExecMacro( const SvxMacro& rMacro, OUString* pRet, SbxArray* pArgs )
309
0
{
310
    // execute macro, if it is allowed.
311
0
    if ( IsMacroExecAllowed() )
312
0
    {
313
0
        GetDoc()->ExecMacro( rMacro, pRet, pArgs );
314
0
    }
315
0
}
316
317
sal_uInt16 SwWrtShell::CallEvent( SvMacroItemId nEvent, const SwCallMouseEvent& rCallEvent,
318
                                bool bChkPtr)
319
0
{
320
0
    return GetDoc()->CallEvent( nEvent, rCallEvent, bChkPtr );
321
0
}
322
323
    // If a util::URL-Button is selected, return its util::URL
324
    // otherwise an empty string.
325
bool SwWrtShell::GetURLFromButton( OUString& rURL, OUString& rDescr ) const
326
0
{
327
0
    bool bRet = false;
328
0
    const SdrView *pDView = GetDrawView();
329
0
    if( pDView )
330
0
    {
331
        // A fly is precisely achievable if it is selected.
332
0
        const SdrMarkList &rMarkList = pDView->GetMarkedObjectList();
333
334
0
        if (rMarkList.GetMark(0))
335
0
        {
336
0
            SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( rMarkList.GetMark(0)->GetMarkedSdrObj() );
337
0
            if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor())
338
0
            {
339
0
                const uno::Reference< awt::XControlModel >&  xControlModel = pUnoCtrl->GetUnoControlModel();
340
341
0
                OSL_ENSURE( xControlModel.is(), "UNO-Control without Model" );
342
0
                if( !xControlModel.is() )
343
0
                    return bRet;
344
345
0
                uno::Reference< beans::XPropertySet >  xPropSet(xControlModel, uno::UNO_QUERY);
346
347
0
                uno::Any aTmp;
348
349
0
                uno::Reference< beans::XPropertySetInfo >   xInfo = xPropSet->getPropertySetInfo();
350
0
                if(xInfo->hasPropertyByName( u"ButtonType"_ustr ))
351
0
                {
352
0
                    aTmp = xPropSet->getPropertyValue( u"ButtonType"_ustr );
353
0
                    form::FormButtonType eTmpButtonType;
354
0
                    aTmp >>= eTmpButtonType;
355
0
                    if( form::FormButtonType_URL == eTmpButtonType)
356
0
                    {
357
                        // Label
358
0
                        aTmp = xPropSet->getPropertyValue( u"Label"_ustr );
359
0
                        OUString uTmp;
360
0
                        if( (aTmp >>= uTmp) && !uTmp.isEmpty())
361
0
                        {
362
0
                            rDescr = uTmp;
363
0
                        }
364
365
                        // util::URL
366
0
                        aTmp = xPropSet->getPropertyValue( u"TargetURL"_ustr );
367
0
                        if( (aTmp >>= uTmp) && !uTmp.isEmpty())
368
0
                        {
369
0
                            rURL = uTmp;
370
0
                        }
371
0
                        bRet = true;
372
0
                    }
373
0
                }
374
0
            }
375
0
        }
376
0
    }
377
378
0
    return bRet;
379
0
}
380
381
SwPostItMgr* SwWrtShell::GetPostItMgr()
382
316k
{
383
316k
    return m_rView.GetPostItMgr();
384
316k
}
385
386
void SwWrtShell::SortChapters(const SwOutlineNodes::size_type nOutlineNodePos)
387
0
{
388
0
    auto sort_chapters = [this](const SwNode* pParentNode, int nOutlineLevel)
389
0
    {
390
0
        const SwNode* pEndNode;
391
392
0
        std::vector<const SwTextNode*> vLevelOutlineNodes;
393
0
        auto GetLevelOutlineNodesAndEndNode = [&]()
394
0
        {
395
0
            vLevelOutlineNodes.clear();
396
0
            bool bParentFound = false;
397
0
            pEndNode = &GetNodes().GetEndOfContent();
398
0
            for (const SwNode* pNode : GetNodes().GetOutLineNds())
399
0
            {
400
0
                if (!pNode->IsTextNode())
401
0
                    continue;
402
0
                if (pParentNode && !bParentFound)
403
0
                {
404
0
                    bParentFound = pNode == pParentNode;
405
0
                    continue;
406
0
                }
407
0
                if (pNode->GetTextNode()->GetAttrOutlineLevel() < nOutlineLevel)
408
0
                {
409
0
                    pEndNode = pNode;
410
0
                    break;
411
0
                }
412
0
                if (pNode->GetTextNode()->GetAttrOutlineLevel() == nOutlineLevel)
413
0
                    vLevelOutlineNodes.emplace_back(pNode->GetTextNode());
414
0
            }
415
0
        };
416
417
0
        GetLevelOutlineNodesAndEndNode();
418
419
0
        std::vector<const SwTextNode*> vSortedLevelOutlineNodes = vLevelOutlineNodes;
420
0
        std::stable_sort(vSortedLevelOutlineNodes.begin(), vSortedLevelOutlineNodes.end(),
421
0
                         [](const SwTextNode* a, const SwTextNode* b)
422
0
                         {
423
0
                             const OUString& raText = a->GetText();
424
0
                             const OUString& rbText = b->GetText();
425
0
                             return raText < rbText;
426
0
                         });
427
428
0
        for (size_t i = 0, nSize = vLevelOutlineNodes.size(); i < nSize; i++)
429
0
        {
430
            // Find the position that the sorted node is at in the unsorted vector.
431
            // This is the position of the node in the unsorted vector that is used for the start of
432
            // the range of nodes to be moved in this iteration.
433
0
            size_t j = 0;
434
0
            for (; j < nSize; j++)
435
0
            {
436
0
                if (vSortedLevelOutlineNodes[i] == vLevelOutlineNodes[j])
437
0
                    break;
438
0
            }
439
440
            // The end node in the range is the next entry in the unsorted vector or the pEndNode set
441
            // by GetLevelOutlineNodesAndEndNode or the end of the document.
442
0
            const SwNode* pEndRangeNode;
443
0
            if (j + 1 < nSize)
444
0
                pEndRangeNode = vLevelOutlineNodes[j + 1];
445
0
            else
446
0
                pEndRangeNode = pEndNode;
447
448
0
            SwNodeRange aNodeRange(*vLevelOutlineNodes[j], SwNodeOffset(0), *pEndRangeNode,
449
0
                                   SwNodeOffset(0));
450
451
            // Move the range of nodes to before the node in the unsorted outline vector at the
452
            // current iteration index to match the position of the outline node in the sorted vector.
453
0
            getIDocumentContentOperations().MoveNodeRange(
454
0
                aNodeRange, *const_cast<SwTextNode*>(vLevelOutlineNodes[i]),
455
0
                SwMoveFlags::DEFAULT | SwMoveFlags::CREATEUNDOOBJ);
456
457
0
            GetLevelOutlineNodesAndEndNode();
458
0
        }
459
0
    };
460
461
0
    const SwOutlineNodes& rOutlineNodes = GetNodes().GetOutLineNds();
462
463
0
    if (rOutlineNodes.empty())
464
0
        return;
465
466
0
    StartAction();
467
0
    StartUndo(SwUndoId::SORT_CHAPTERS);
468
469
    // Create an ordered set of outline levels in the outline nodes for use to determine
470
    // the lowest level to use for first sort and to only iterate over higher levels used.
471
0
    std::set<int> aOutlineLevelSet;
472
0
    for (const SwNode* pNode : rOutlineNodes)
473
0
    {
474
0
        int nOutlineLevel = pNode->GetTextNode()->GetAttrOutlineLevel();
475
0
        aOutlineLevelSet.emplace(nOutlineLevel);
476
0
    }
477
478
    // No parent node for the lowest outline level nodes sort.
479
0
    if (nOutlineNodePos == SwOutlineNodes::npos)
480
0
    {
481
0
        sort_chapters(nullptr /*pParentNode*/,
482
0
                      aOutlineLevelSet.extract(aOutlineLevelSet.begin()).value());
483
0
        for (int nOutlineLevel : aOutlineLevelSet)
484
0
        {
485
0
            for (size_t i = 0, nSize = rOutlineNodes.size(); i + 1 < nSize; i++)
486
0
            {
487
0
                if (rOutlineNodes[i]->GetTextNode()->GetAttrOutlineLevel() < nOutlineLevel
488
0
                    && rOutlineNodes[i + 1]->GetTextNode()->GetAttrOutlineLevel() == nOutlineLevel)
489
0
                {
490
0
                    const SwNode* pParentNode = rOutlineNodes[i];
491
0
                    sort_chapters(pParentNode, nOutlineLevel);
492
0
                }
493
0
            }
494
0
        }
495
0
    }
496
0
    else
497
0
    {
498
0
        for (int nOutlineLevel : aOutlineLevelSet)
499
0
        {
500
0
            if (rOutlineNodes[nOutlineNodePos]->GetTextNode()->GetAttrOutlineLevel()
501
0
                < nOutlineLevel)
502
0
            {
503
0
                const SwNode* pParentNode = rOutlineNodes[nOutlineNodePos];
504
0
                sort_chapters(pParentNode, nOutlineLevel);
505
0
            }
506
0
        }
507
0
    }
508
509
0
    EndUndo(SwUndoId::SORT_CHAPTERS);
510
0
    EndAction();
511
0
}
512
513
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */