Coverage Report

Created: 2026-05-16 09:25

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
bool SwWrtShell::GotoFieldmark(::sw::mark::Fieldmark const * const pMark)
231
0
{
232
0
    (this->*m_fnKillSel)( nullptr, false, ScrollSizeMode::ScrollSizeDefault );
233
0
    bool bRet = SwCursorShell::GotoFieldmark(pMark);
234
0
    if( bRet && IsSelFrameMode() )
235
0
    {
236
0
        UnSelectFrame();
237
0
        LeaveSelFrameMode();
238
0
    }
239
0
    if( IsSelection() )
240
0
    {
241
0
        m_fnKillSel = &SwWrtShell::ResetSelect;
242
0
        m_fnSetCursor = &SwWrtShell::SetCursorKillSel;
243
0
    }
244
0
    return bRet;
245
0
}
246
247
// Invalidate FontWork-Slots
248
249
void SwWrtShell::DrawSelChanged( )
250
32
{
251
32
    static sal_uInt16 const aInval[] =
252
32
    {
253
32
        SID_ATTR_FILL_STYLE, SID_ATTR_FILL_COLOR, SID_ATTR_LINE_STYLE,
254
32
        SID_ATTR_LINE_WIDTH, SID_ATTR_LINE_COLOR,
255
        /*AF: these may be needed for the sidebar.
256
        SID_SVX_AREA_TRANSPARENCY, SID_SVX_AREA_TRANSP_GRADIENT,
257
        SID_SVX_AREA_TRANS_TYPE,
258
        */
259
32
        0
260
32
    };
261
262
32
    GetView().GetViewFrame().GetBindings().Invalidate(aInval);
263
264
32
    bool bOldVal = g_bNoInterrupt;
265
32
    g_bNoInterrupt = true;    // Trick to run AttrChangedNotify by timer.
266
32
    GetView().AttrChangedNotify(nullptr);
267
32
    g_bNoInterrupt = bOldVal;
268
32
}
269
270
void SwWrtShell::GotoMark( const SwMarkName& rName )
271
0
{
272
0
    auto ppMark = getIDocumentMarkAccess()->findMark( rName );
273
0
    if (ppMark == getIDocumentMarkAccess()->getAllMarksEnd())
274
0
        return;
275
0
    MoveBookMark( BOOKMARK_INDEX, *ppMark );
276
0
}
277
278
void SwWrtShell::GotoMark( const ::sw::mark::MarkBase* const pMark )
279
0
{
280
0
    MoveBookMark( BOOKMARK_INDEX, pMark );
281
0
}
282
283
bool SwWrtShell::GoNextBookmark()
284
0
{
285
0
    if ( !getIDocumentMarkAccess()->getBookmarksCount() )
286
0
    {
287
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
288
0
        return false;
289
0
    }
290
0
    LockView( true );
291
0
    bool bRet = MoveBookMark( BOOKMARK_NEXT );
292
0
    if ( !bRet )
293
0
    {
294
0
        MoveBookMark( BOOKMARK_INDEX, *getIDocumentMarkAccess()->getBookmarksBegin() );
295
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
296
0
    }
297
0
    else
298
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
299
0
    LockView( false );
300
0
    ShowCursor();
301
0
    return true;
302
0
}
303
304
bool SwWrtShell::GoPrevBookmark()
305
0
{
306
0
    if ( !getIDocumentMarkAccess()->getBookmarksCount() )
307
0
    {
308
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
309
0
        return false;
310
0
    }
311
0
    LockView( true );
312
0
    bool bRet = MoveBookMark( BOOKMARK_PREV );
313
0
    if ( !bRet )
314
0
    {
315
0
        MoveBookMark( BOOKMARK_INDEX, *( getIDocumentMarkAccess()->getBookmarksEnd() - 1 ) );
316
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
317
0
    }
318
0
    else
319
0
        SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
320
0
    LockView( false );
321
0
    ShowCursor();
322
0
    return true;
323
0
}
324
325
void SwWrtShell::ExecMacro( const SvxMacro& rMacro, OUString* pRet, SbxArray* pArgs )
326
0
{
327
    // execute macro, if it is allowed.
328
0
    if ( IsMacroExecAllowed() )
329
0
    {
330
0
        GetDoc()->ExecMacro( rMacro, pRet, pArgs );
331
0
    }
332
0
}
333
334
sal_uInt16 SwWrtShell::CallEvent( SvMacroItemId nEvent, const SwCallMouseEvent& rCallEvent,
335
                                bool bChkPtr)
336
0
{
337
0
    return GetDoc()->CallEvent( nEvent, rCallEvent, bChkPtr );
338
0
}
339
340
    // If a util::URL-Button is selected, return its util::URL
341
    // otherwise an empty string.
342
bool SwWrtShell::GetURLFromButton( OUString& rURL, OUString& rDescr ) const
343
0
{
344
0
    bool bRet = false;
345
0
    const SdrView *pDView = GetDrawView();
346
0
    if( pDView )
347
0
    {
348
        // A fly is precisely achievable if it is selected.
349
0
        const SdrMarkList &rMarkList = pDView->GetMarkedObjectList();
350
351
0
        if (rMarkList.GetMark(0))
352
0
        {
353
0
            SdrUnoObj* pUnoCtrl = dynamic_cast<SdrUnoObj*>( rMarkList.GetMark(0)->GetMarkedSdrObj() );
354
0
            if (pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor())
355
0
            {
356
0
                const uno::Reference< awt::XControlModel >&  xControlModel = pUnoCtrl->GetUnoControlModel();
357
358
0
                OSL_ENSURE( xControlModel.is(), "UNO-Control without Model" );
359
0
                if( !xControlModel.is() )
360
0
                    return bRet;
361
362
0
                uno::Reference< beans::XPropertySet >  xPropSet(xControlModel, uno::UNO_QUERY);
363
364
0
                uno::Any aTmp;
365
366
0
                uno::Reference< beans::XPropertySetInfo >   xInfo = xPropSet->getPropertySetInfo();
367
0
                if(xInfo->hasPropertyByName( u"ButtonType"_ustr ))
368
0
                {
369
0
                    aTmp = xPropSet->getPropertyValue( u"ButtonType"_ustr );
370
0
                    form::FormButtonType eTmpButtonType;
371
0
                    aTmp >>= eTmpButtonType;
372
0
                    if( form::FormButtonType_URL == eTmpButtonType)
373
0
                    {
374
                        // Label
375
0
                        aTmp = xPropSet->getPropertyValue( u"Label"_ustr );
376
0
                        OUString uTmp;
377
0
                        if( (aTmp >>= uTmp) && !uTmp.isEmpty())
378
0
                        {
379
0
                            rDescr = uTmp;
380
0
                        }
381
382
                        // util::URL
383
0
                        aTmp = xPropSet->getPropertyValue( u"TargetURL"_ustr );
384
0
                        if( (aTmp >>= uTmp) && !uTmp.isEmpty())
385
0
                        {
386
0
                            rURL = uTmp;
387
0
                        }
388
0
                        bRet = true;
389
0
                    }
390
0
                }
391
0
            }
392
0
        }
393
0
    }
394
395
0
    return bRet;
396
0
}
397
398
SwPostItMgr* SwWrtShell::GetPostItMgr()
399
277k
{
400
277k
    return m_rView.GetPostItMgr();
401
277k
}
402
403
void SwWrtShell::SortChapters(const SwOutlineNodes::size_type nOutlineNodePos)
404
0
{
405
0
    auto sort_chapters = [this](const SwNode* pParentNode, int nOutlineLevel)
406
0
    {
407
0
        const SwNode* pEndNode;
408
409
0
        std::vector<const SwTextNode*> vLevelOutlineNodes;
410
0
        auto GetLevelOutlineNodesAndEndNode = [&]()
411
0
        {
412
0
            vLevelOutlineNodes.clear();
413
0
            bool bParentFound = false;
414
0
            pEndNode = &GetNodes().GetEndOfContent();
415
0
            for (const SwNode* pNode : GetNodes().GetOutLineNds())
416
0
            {
417
0
                if (!pNode->IsTextNode())
418
0
                    continue;
419
0
                if (pParentNode && !bParentFound)
420
0
                {
421
0
                    bParentFound = pNode == pParentNode;
422
0
                    continue;
423
0
                }
424
0
                if (pNode->GetTextNode()->GetAttrOutlineLevel() < nOutlineLevel)
425
0
                {
426
0
                    pEndNode = pNode;
427
0
                    break;
428
0
                }
429
0
                if (pNode->GetTextNode()->GetAttrOutlineLevel() == nOutlineLevel)
430
0
                    vLevelOutlineNodes.emplace_back(pNode->GetTextNode());
431
0
            }
432
0
        };
433
434
0
        GetLevelOutlineNodesAndEndNode();
435
436
0
        std::vector<const SwTextNode*> vSortedLevelOutlineNodes = vLevelOutlineNodes;
437
0
        std::stable_sort(vSortedLevelOutlineNodes.begin(), vSortedLevelOutlineNodes.end(),
438
0
                         [](const SwTextNode* a, const SwTextNode* b)
439
0
                         {
440
0
                             const OUString& raText = a->GetText();
441
0
                             const OUString& rbText = b->GetText();
442
0
                             return raText < rbText;
443
0
                         });
444
445
0
        for (size_t i = 0, nSize = vLevelOutlineNodes.size(); i < nSize; i++)
446
0
        {
447
            // Find the position that the sorted node is at in the unsorted vector.
448
            // This is the position of the node in the unsorted vector that is used for the start of
449
            // the range of nodes to be moved in this iteration.
450
0
            size_t j = 0;
451
0
            for (; j < nSize; j++)
452
0
            {
453
0
                if (vSortedLevelOutlineNodes[i] == vLevelOutlineNodes[j])
454
0
                    break;
455
0
            }
456
457
            // The end node in the range is the next entry in the unsorted vector or the pEndNode set
458
            // by GetLevelOutlineNodesAndEndNode or the end of the document.
459
0
            const SwNode* pEndRangeNode;
460
0
            if (j + 1 < nSize)
461
0
                pEndRangeNode = vLevelOutlineNodes[j + 1];
462
0
            else
463
0
                pEndRangeNode = pEndNode;
464
465
0
            SwNodeRange aNodeRange(*vLevelOutlineNodes[j], SwNodeOffset(0), *pEndRangeNode,
466
0
                                   SwNodeOffset(0));
467
468
            // Move the range of nodes to before the node in the unsorted outline vector at the
469
            // current iteration index to match the position of the outline node in the sorted vector.
470
0
            getIDocumentContentOperations().MoveNodeRange(
471
0
                aNodeRange, *const_cast<SwTextNode*>(vLevelOutlineNodes[i]),
472
0
                SwMoveFlags::DEFAULT | SwMoveFlags::CREATEUNDOOBJ);
473
474
0
            GetLevelOutlineNodesAndEndNode();
475
0
        }
476
0
    };
477
478
0
    const SwOutlineNodes& rOutlineNodes = GetNodes().GetOutLineNds();
479
480
0
    if (rOutlineNodes.empty())
481
0
        return;
482
483
0
    StartAction();
484
0
    StartUndo(SwUndoId::SORT_CHAPTERS);
485
486
    // Create an ordered set of outline levels in the outline nodes for use to determine
487
    // the lowest level to use for first sort and to only iterate over higher levels used.
488
0
    std::set<int> aOutlineLevelSet;
489
0
    for (const SwNode* pNode : rOutlineNodes)
490
0
    {
491
0
        int nOutlineLevel = pNode->GetTextNode()->GetAttrOutlineLevel();
492
0
        aOutlineLevelSet.emplace(nOutlineLevel);
493
0
    }
494
495
    // No parent node for the lowest outline level nodes sort.
496
0
    if (nOutlineNodePos == SwOutlineNodes::npos)
497
0
    {
498
0
        sort_chapters(nullptr /*pParentNode*/,
499
0
                      aOutlineLevelSet.extract(aOutlineLevelSet.begin()).value());
500
0
        for (int nOutlineLevel : aOutlineLevelSet)
501
0
        {
502
0
            for (size_t i = 0, nSize = rOutlineNodes.size(); i + 1 < nSize; i++)
503
0
            {
504
0
                if (rOutlineNodes[i]->GetTextNode()->GetAttrOutlineLevel() < nOutlineLevel
505
0
                    && rOutlineNodes[i + 1]->GetTextNode()->GetAttrOutlineLevel() == nOutlineLevel)
506
0
                {
507
0
                    const SwNode* pParentNode = rOutlineNodes[i];
508
0
                    sort_chapters(pParentNode, nOutlineLevel);
509
0
                }
510
0
            }
511
0
        }
512
0
    }
513
0
    else
514
0
    {
515
0
        for (int nOutlineLevel : aOutlineLevelSet)
516
0
        {
517
0
            if (rOutlineNodes[nOutlineNodePos]->GetTextNode()->GetAttrOutlineLevel()
518
0
                < nOutlineLevel)
519
0
            {
520
0
                const SwNode* pParentNode = rOutlineNodes[nOutlineNodePos];
521
0
                sort_chapters(pParentNode, nOutlineLevel);
522
0
            }
523
0
        }
524
0
    }
525
526
0
    EndUndo(SwUndoId::SORT_CHAPTERS);
527
0
    EndAction();
528
0
}
529
530
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */