Coverage Report

Created: 2026-04-09 11:41

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