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/docvw/SidebarTxtControl.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 "SidebarTxtControl.hxx"
23
24
#include <docsh.hxx>
25
#include <doc.hxx>
26
27
#include <PostItMgr.hxx>
28
29
#include <cmdid.h>
30
#include <strings.hrc>
31
32
#include <unotools/securityoptions.hxx>
33
#include <officecfg/Office/Common.hxx>
34
35
#include <sfx2/viewfrm.hxx>
36
#include <sfx2/bindings.hxx>
37
#include <sfx2/dispatch.hxx>
38
#include <sfx2/sfxhelp.hxx>
39
40
#include <vcl/commandevent.hxx>
41
#include <vcl/event.hxx>
42
#include <vcl/ptrstyle.hxx>
43
#include <vcl/rendercontext/AntialiasingFlags.hxx>
44
#include <vcl/svapp.hxx>
45
#include <vcl/gradient.hxx>
46
#include <vcl/settings.hxx>
47
48
#include <editeng/outliner.hxx>
49
#include <editeng/editeng.hxx>
50
#include <editeng/editview.hxx>
51
#include <editeng/flditem.hxx>
52
53
#include <com/sun/star/awt/GradientStyle.hpp>
54
55
#include <uitool.hxx>
56
#include <view.hxx>
57
#include <wrtsh.hxx>
58
#include <AnnotationWin.hxx>
59
#include <IDocumentDeviceAccess.hxx>
60
#if ENABLE_YRS
61
#include <IDocumentState.hxx>
62
#include <swmodule.hxx>
63
#endif
64
#include <redline.hxx>
65
#include <memory>
66
67
namespace sw::sidebarwindows {
68
69
SidebarTextControl::SidebarTextControl(sw::annotation::SwAnnotationWin& rSidebarWin,
70
                                       SwView& rDocView,
71
                                       SwPostItMgr& rPostItMgr)
72
0
    : mrSidebarWin(rSidebarWin)
73
0
    , mrDocView(rDocView)
74
0
    , mrPostItMgr(rPostItMgr)
75
0
    , mbMouseDownGainingFocus(false)
76
0
{
77
0
}
78
79
EditView* SidebarTextControl::GetEditView() const
80
0
{
81
0
    OutlinerView* pOutlinerView = mrSidebarWin.GetOutlinerView();
82
0
    if (!pOutlinerView)
83
0
        return nullptr;
84
0
    return &pOutlinerView->GetEditView();
85
0
}
86
87
EditEngine* SidebarTextControl::GetEditEngine() const
88
0
{
89
0
    OutlinerView* pOutlinerView = mrSidebarWin.GetOutlinerView();
90
0
    if (!pOutlinerView)
91
0
        return nullptr;
92
0
    return &pOutlinerView->GetEditView().getEditEngine();
93
0
}
94
95
void SidebarTextControl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
96
0
{
97
0
    Size aSize(0, 0);
98
0
    pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
99
100
0
    SetOutputSizePixel(aSize);
101
102
0
    weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
103
104
0
    EnableRTL(false);
105
106
0
    const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
107
0
    Color aBgColor = rStyleSettings.GetWindowColor();
108
109
0
    OutputDevice& rDevice = pDrawingArea->get_ref_device();
110
111
0
    rDevice.SetMapMode(MapMode(MapUnit::MapTwip));
112
0
    rDevice.SetBackground(aBgColor);
113
114
0
    Size aOutputSize(rDevice.PixelToLogic(aSize));
115
116
0
    EditView* pEditView = GetEditView();
117
0
    pEditView->setEditViewCallbacks(this);
118
119
0
    EditEngine& rEditEngine = pEditView->getEditEngine();
120
    // For tdf#143443 note we want an 'infinite' height initially (which is the
121
    // editengines default). For tdf#144686 it is helpful if the initial width
122
    // is the "SidebarWidth" so the calculated text height is always meaningful
123
    // for layout in the sidebar.
124
0
    Size aPaperSize(mrPostItMgr.GetSidebarWidth(), rEditEngine.GetPaperSize().Height());
125
0
    rEditEngine.SetPaperSize(aPaperSize);
126
0
    rEditEngine.SetRefDevice(mrDocView.GetWrtShell().getIDocumentDeviceAccess().getReferenceDevice(false));
127
128
0
    pEditView->SetOutputArea(tools::Rectangle(Point(0, 0), aOutputSize));
129
0
    pEditView->SetBackgroundColor(aBgColor);
130
131
0
    pDrawingArea->set_cursor(PointerStyle::Text);
132
133
0
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
134
0
    InitAccessible();
135
0
#endif
136
0
}
137
138
void SidebarTextControl::SetCursorLogicPosition(const Point& rPosition, bool bPoint, bool bClearMark)
139
0
{
140
0
    Point aMousePos = EditViewOutputDevice().PixelToLogic(rPosition);
141
0
    m_xEditView->SetCursorLogicPosition(aMousePos, bPoint, bClearMark);
142
0
}
143
144
void SidebarTextControl::GetFocus()
145
0
{
146
0
    WeldEditView::GetFocus();
147
0
    if ( !mrSidebarWin.IsMouseOver() )
148
0
        Invalidate();
149
0
    mrSidebarWin.SetActiveSidebarWin();
150
0
}
151
152
void SidebarTextControl::LoseFocus()
153
0
{
154
    // write the visible text back into the SwField
155
0
    mrSidebarWin.UpdateData();
156
157
0
    WeldEditView::LoseFocus();
158
0
    if ( !mrSidebarWin.IsMouseOver() )
159
0
    {
160
0
        Invalidate();
161
0
    }
162
    // set false for autoscroll to typing location
163
0
    mrSidebarWin.LockView(false);
164
0
}
165
166
OUString SidebarTextControl::RequestHelp(tools::Rectangle& rHelpRect)
167
0
{
168
0
    if (EditView* pEditView = GetEditView())
169
0
    {
170
0
        Point aPos = rHelpRect.TopLeft();
171
172
0
        const OutputDevice& rOutDev = pEditView->GetOutputDevice();
173
0
        Point aLogicClick = rOutDev.PixelToLogic(aPos);
174
0
        const SvxFieldItem* pItem = pEditView->GetField(aLogicClick);
175
0
        if (pItem)
176
0
        {
177
0
            const SvxFieldData* pField = pItem->GetField();
178
0
            const SvxURLField* pURL = dynamic_cast<const SvxURLField*>( pField  );
179
0
            if (pURL)
180
0
            {
181
0
                rHelpRect = tools::Rectangle(aPos, Size(50, 10));
182
0
                return SfxHelp::GetURLHelpText(pURL->GetURL());
183
0
            }
184
0
        }
185
0
    }
186
187
0
    TranslateId pResId;
188
0
    switch( mrSidebarWin.GetLayoutStatus() )
189
0
    {
190
0
        case SwPostItHelper::INSERTED:  pResId = STR_REDLINE_INSERT; break;
191
0
        case SwPostItHelper::DELETED:   pResId = STR_REDLINE_DELETE; break;
192
0
        default: break;
193
0
    }
194
195
0
    SwContentAtPos aContentAtPos( IsAttrAtPos::Redline );
196
0
    if ( pResId &&
197
0
         mrDocView.GetWrtShell().GetContentAtPos( mrSidebarWin.GetAnchorPos(), aContentAtPos ) )
198
0
    {
199
0
        OUString sText = SwResId(pResId) + ": " +
200
0
                        aContentAtPos.aFnd.pRedl->GetAuthorString() + " - " +
201
0
                        GetAppLangDateTimeString( aContentAtPos.aFnd.pRedl->GetTimeStamp() );
202
0
        return sText;
203
0
    }
204
205
0
    return OUString();
206
0
}
207
208
#if ENABLE_YRS
209
void SidebarTextControl::EditViewInvalidate(const tools::Rectangle& rRect)
210
{
211
    SAL_INFO("sw.yrs", "YRS EditViewInvalidate");
212
    mrDocView.GetDocShell()->GetDoc()->getIDocumentState().YrsNotifyCursorUpdate();
213
    mrDocView.GetDocShell()->GetDoc()->getIDocumentState().YrsCommitModified(true);
214
    return WeldEditView::EditViewInvalidate(rRect);
215
}
216
217
void SidebarTextControl::EditViewSelectionChange()
218
{
219
    SAL_INFO("sw.yrs", "YRS EditViewSelectionChange");
220
    mrDocView.GetDocShell()->GetDoc()->getIDocumentState().YrsNotifyCursorUpdate();
221
    return WeldEditView::EditViewSelectionChange();
222
}
223
#endif
224
225
void SidebarTextControl::EditViewScrollStateChange()
226
0
{
227
0
    mrSidebarWin.SetScrollbar();
228
0
}
229
230
void SidebarTextControl::DrawForPage(OutputDevice* pDev, const Point& rPt)
231
0
{
232
    //Take the control's height, but overwrite the scrollbar area if there was one
233
0
    OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
234
0
    Size aSize(rDevice.PixelToLogic(GetOutputSizePixel()));
235
236
0
    if (OutlinerView* pOutlinerView = mrSidebarWin.GetOutlinerView())
237
0
    {
238
0
        pOutlinerView->GetOutliner().SetPaperSize(aSize);
239
0
        pOutlinerView->GetOutliner().DrawText_ToRectangle(*pDev, tools::Rectangle(rPt, aSize));
240
0
    }
241
242
0
    if ( mrSidebarWin.GetLayoutStatus()!=SwPostItHelper::DELETED )
243
0
        return;
244
245
0
    auto popIt = pDev->ScopedPush(vcl::PushFlags::LINECOLOR);
246
247
0
    pDev->SetLineColor(mrSidebarWin.GetChangeColor());
248
0
    Point aBottomRight(rPt);
249
0
    aBottomRight.Move(aSize);
250
0
    pDev->DrawLine(rPt,  aBottomRight);
251
252
0
    Point aTopRight(rPt);
253
0
    aTopRight.Move(Size(aSize.Width(), 0));
254
255
0
    Point aBottomLeft(rPt);
256
0
    aBottomLeft.Move(Size(0, aSize.Height()));
257
258
0
    pDev->DrawLine(aTopRight, aBottomLeft);
259
0
}
260
261
void SidebarTextControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
262
0
{
263
0
    Size aSize = GetOutputSizePixel();
264
0
    Point aPos;
265
266
0
    if (!rRenderContext.GetSettings().GetStyleSettings().GetHighContrastMode())
267
0
    {
268
0
        if (mrSidebarWin.IsMouseOverSidebarWin() || HasFocus())
269
0
        {
270
0
            rRenderContext.DrawGradient(tools::Rectangle(aPos, rRenderContext.PixelToLogic(aSize)),
271
0
                                        Gradient(css::awt::GradientStyle_LINEAR, mrSidebarWin.ColorDark(), mrSidebarWin.ColorDark()));
272
0
        }
273
0
        else
274
0
        {
275
0
            rRenderContext.DrawGradient(tools::Rectangle(aPos, rRenderContext.PixelToLogic(aSize)),
276
0
                           Gradient(css::awt::GradientStyle_LINEAR, mrSidebarWin.ColorLight(), mrSidebarWin.ColorDark()));
277
0
        }
278
0
    }
279
280
0
    DoPaint(rRenderContext, rRect);
281
282
#if ENABLE_YRS
283
    if (EditView *const pEditView{GetEditView()})
284
    {
285
        rRenderContext.Push(vcl::PushFlags::ALL);
286
        rRenderContext.SetClipRegion();
287
288
        ::std::vector<::std::pair<OUString, ::std::vector<tools::Rectangle>>> rects;
289
        pEditView->YrsGetSelectionRectangles(rects);
290
        for (auto const& it : rects)
291
        {
292
            ::std::size_t const authorId{SwModule::get()->InsertRedlineAuthor(it.first)};
293
            Color const color{SwPostItMgr::GetColorAnchor(authorId)};
294
            PaintSelection(rRenderContext, rRect, it.second, color);
295
        }
296
297
        rRenderContext.Pop();
298
    }
299
#endif
300
301
0
    if (mrSidebarWin.GetLayoutStatus() != SwPostItHelper::DELETED)
302
0
        return;
303
304
0
    const AntialiasingFlags nFormerAntialiasing( rRenderContext.GetAntialiasing() );
305
0
    const bool bIsAntiAliasing = officecfg::Office::Common::Drawinglayer::AntiAliasing::get();
306
0
    if ( bIsAntiAliasing )
307
0
        rRenderContext.SetAntialiasing(AntialiasingFlags::Enable);
308
0
    rRenderContext.SetLineColor(mrSidebarWin.GetChangeColor());
309
0
    rRenderContext.DrawLine(rRenderContext.PixelToLogic(aPos),
310
0
                            rRenderContext.PixelToLogic(aPos + Point(aSize.Width(),
311
0
                                                                     aSize.Height() * 0.95)));
312
0
    rRenderContext.DrawLine(rRenderContext.PixelToLogic(aPos + Point(aSize.Width(),
313
0
                                                                     0)),
314
0
                            rRenderContext.PixelToLogic(aPos + Point(0,
315
0
                                                                     aSize.Height() * 0.95)));
316
0
    if ( bIsAntiAliasing )
317
0
        rRenderContext.SetAntialiasing(nFormerAntialiasing);
318
0
}
319
320
void SidebarTextControl::MakeVisible()
321
0
{
322
    //let's make sure we see our note
323
0
    mrPostItMgr.MakeVisible(&mrSidebarWin);
324
0
}
325
326
bool SidebarTextControl::KeyInput( const KeyEvent& rKeyEvt )
327
0
{
328
0
    const vcl::KeyCode& rKeyCode = rKeyEvt.GetKeyCode();
329
0
    const sal_uInt16 nKey = rKeyCode.GetCode();
330
0
    if (nKey == KEY_F12 && getenv("SW_DEBUG"))
331
0
    {
332
0
        if (rKeyEvt.GetKeyCode().IsShift())
333
0
        {
334
0
            mrDocView.GetDocShell()->GetDoc()->dumpAsXml();
335
0
            return true;
336
0
        }
337
0
    }
338
339
0
    bool bDone = false;
340
341
0
    if ( ( rKeyCode.IsMod1() && rKeyCode.IsMod2() ) &&
342
0
         ( (nKey == KEY_PAGEUP) || (nKey == KEY_PAGEDOWN) ) )
343
0
    {
344
0
        mrSidebarWin.SwitchToPostIt(nKey);
345
0
        bDone = true;
346
0
    }
347
0
    else if ( nKey == KEY_ESCAPE ||
348
0
              ( rKeyCode.IsMod1() &&
349
0
                ( nKey == KEY_PAGEUP ||
350
0
                  nKey == KEY_PAGEDOWN ) ) )
351
0
    {
352
0
        mrSidebarWin.SwitchToFieldPos();
353
0
        bDone = true;
354
0
    }
355
0
    else if ( rKeyCode.GetFullCode() == KEY_INSERT )
356
0
    {
357
0
        mrSidebarWin.ToggleInsMode();
358
0
        bDone = true;
359
0
    }
360
0
    else
361
0
    {
362
0
        tools::Long aOldHeight = mrSidebarWin.GetPostItTextHeight();
363
364
        /// HACK: need to switch off processing of Undo/Redo in Outliner
365
0
        if ( !( (nKey == KEY_Z || nKey == KEY_Y) && rKeyCode.IsMod1()) )
366
0
        {
367
0
            bool bIsProtected = mrSidebarWin.IsReadOnlyOrProtected();
368
0
            if ( !bIsProtected || !EditEngine::DoesKeyChangeText(rKeyEvt) )
369
0
            {
370
0
                EditView* pEditView = GetEditView();
371
0
                bDone = pEditView && pEditView->PostKeyEvent(rKeyEvt);
372
0
            }
373
0
            else
374
0
                mrDocView.GetWrtShell().InfoReadOnlyDialog(false);
375
0
        }
376
0
        if (bDone)
377
0
        {
378
0
            MakeVisible();
379
0
            mrSidebarWin.ResizeIfNecessary( aOldHeight, mrSidebarWin.GetPostItTextHeight() );
380
0
        }
381
0
        else
382
0
        {
383
            // write back data first when showing navigator
384
0
            if ( nKey==KEY_F5 )
385
0
                mrSidebarWin.UpdateData();
386
0
            bDone = mrDocView.KeyInput(rKeyEvt);
387
0
        }
388
0
    }
389
390
0
    mrDocView.GetViewFrame().GetBindings().InvalidateAll(false);
391
392
0
    return bDone;
393
0
}
394
395
bool SidebarTextControl::MouseButtonDown(const MouseEvent& rMEvt)
396
0
{
397
0
    if (EditView* pEditView = GetEditView())
398
0
    {
399
0
        bool bExecuteMod = SvtSecurityOptions::IsOptionSet( SvtSecurityOptions::EOption::CtrlClickHyperlink);
400
401
0
        if ( !bExecuteMod || (rMEvt.GetModifier() == KEY_MOD1))
402
0
        {
403
0
            const OutputDevice& rOutDev = pEditView->GetOutputDevice();
404
0
            Point aLogicClick = rOutDev.PixelToLogic(rMEvt.GetPosPixel());
405
0
            if (const SvxFieldItem* pItem = pEditView->GetField(aLogicClick))
406
0
            {
407
0
                const SvxFieldData* pField = pItem->GetField();
408
0
                const SvxURLField* pURL = dynamic_cast<const SvxURLField*>( pField  );
409
0
                if ( pURL )
410
0
                {
411
0
                    pEditView->MouseButtonDown( rMEvt );
412
0
                    SwWrtShell &rSh = mrDocView.GetWrtShell();
413
0
                    const OUString& sURL( pURL->GetURL() );
414
0
                    const OUString& sTarget( pURL->GetTargetFrame() );
415
0
                    ::LoadURL(rSh, sURL, LoadUrlFlags::NONE, sTarget);
416
0
                    return true;
417
0
                }
418
0
            }
419
0
        }
420
0
    }
421
422
0
    mbMouseDownGainingFocus = !HasFocus();
423
0
    GrabFocus();
424
425
0
    bool bRet = WeldEditView::MouseButtonDown(rMEvt);
426
427
0
    mrDocView.GetViewFrame().GetBindings().InvalidateAll(false);
428
429
0
    return bRet;
430
0
}
431
432
bool SidebarTextControl::MouseButtonUp(const MouseEvent& rMEvt)
433
0
{
434
0
    bool bRet = WeldEditView::MouseButtonUp(rMEvt);
435
436
0
    if (mbMouseDownGainingFocus)
437
0
    {
438
0
        MakeVisible();
439
0
        mbMouseDownGainingFocus = false;
440
0
    }
441
442
0
    return bRet;
443
0
}
444
445
bool SidebarTextControl::MouseMove(const MouseEvent& rMEvt)
446
0
{
447
0
    if (rMEvt.IsEnterWindow())
448
0
        GetDrawingArea()->set_cursor(PointerStyle::Text);
449
0
    return WeldEditView::MouseMove(rMEvt);
450
0
}
451
452
IMPL_LINK( SidebarTextControl, OnlineSpellCallback, SpellCallbackInfo&, rInfo, void )
453
0
{
454
0
    if ( rInfo.nCommand == SpellCallbackCommand::STARTSPELLDLG )
455
0
    {
456
0
        mrDocView.GetViewFrame().GetDispatcher()->Execute( FN_SPELL_GRAMMAR_DIALOG, SfxCallMode::ASYNCHRON);
457
0
    }
458
0
}
459
460
bool SidebarTextControl::Command( const CommandEvent& rCEvt )
461
0
{
462
0
    EditView* pEditView = GetEditView();
463
464
0
    if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
465
0
    {
466
0
        if (IsMouseCaptured())
467
0
            ReleaseMouse();
468
469
0
        bool bUsedSpellPopup = false;
470
0
        if ( !mrSidebarWin.IsReadOnlyOrProtected() &&
471
0
             pEditView &&
472
0
             pEditView->IsWrongSpelledWordAtPos( rCEvt.GetMousePosPixel(), true ))
473
0
        {
474
0
            Link<SpellCallbackInfo&,void> aLink = LINK(this, SidebarTextControl, OnlineSpellCallback);
475
0
            bUsedSpellPopup = pEditView->ExecuteSpellPopup(rCEvt.GetMousePosPixel(), aLink);
476
0
        }
477
0
        if (!bUsedSpellPopup)
478
0
        {
479
0
            Point aPos;
480
0
            if (rCEvt.IsMouseEvent())
481
0
                aPos = rCEvt.GetMousePosPixel();
482
0
            else
483
0
            {
484
0
                const Size aSize = GetOutputSizePixel();
485
0
                aPos = Point( aSize.getWidth()/2, aSize.getHeight()/2 );
486
0
            }
487
488
            // tdf#164072: don't use SwAnnotationWin itself as popup parent because it
489
            // may get disposed while the popup menu is still executed
490
0
            vcl::Window* pParentWin = mrSidebarWin.GetParent();
491
0
            assert(pParentWin);
492
0
            aPos += mrSidebarWin.GetWindowExtentsRelative(*pParentWin).TopLeft();
493
0
            SfxDispatcher::ExecutePopup(pParentWin, &aPos);
494
0
        }
495
0
        return true;
496
0
    }
497
0
    else if (rCEvt.GetCommand() == CommandEventId::Wheel)
498
0
    {
499
        // if no scrollbar, or extra keys held scroll the document and consume
500
        // this event, otherwise don't consume and let the event get to the
501
        // surrounding scrolled window
502
0
        if (!mrSidebarWin.IsScrollbarVisible())
503
0
        {
504
0
            mrDocView.HandleWheelCommands(rCEvt);
505
0
            return true;
506
0
        }
507
0
        else
508
0
        {
509
0
            const CommandWheelData* pData = rCEvt.GetWheelData();
510
0
            if (pData->IsShift() || pData->IsMod1() || pData->IsMod2())
511
0
            {
512
0
                mrDocView.HandleWheelCommands(rCEvt);
513
0
                return true;
514
0
            }
515
0
        }
516
0
    }
517
518
0
    return WeldEditView::Command(rCEvt);
519
0
}
520
521
} // end of namespace sw::sidebarwindows
522
523
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */