Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/svdraw/sdrpaintwindow.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 <comphelper/lok.hxx>
21
#include <osl/diagnose.h>
22
#include <svx/sdrpaintwindow.hxx>
23
#include <sdr/overlay/overlaymanagerbuffered.hxx>
24
#include <svx/svdpntv.hxx>
25
#include <vcl/gdimtf.hxx>
26
#include <vcl/svapp.hxx>
27
#include <vcl/settings.hxx>
28
#include <vcl/vclevent.hxx>
29
#include <vcl/window.hxx>
30
#include <svtools/optionsdrawinglayer.hxx>
31
#include <officecfg/Office/Common.hxx>
32
#include <set>
33
#include <vector>
34
35
namespace {
36
37
//rhbz#1007697 do this in two loops, one to collect the candidates
38
//and another to update them because updating a candidate can
39
//trigger the candidate to be deleted, so asking for its
40
//sibling after that is going to fail hard
41
class CandidateMgr
42
{
43
    std::vector<VclPtr<vcl::Window> > m_aCandidates;
44
    std::set<VclPtr<vcl::Window> > m_aDeletedCandidates;
45
    DECL_LINK(WindowEventListener, VclWindowEvent&, void);
46
public:
47
    void PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect);
48
    ~CandidateMgr();
49
};
50
51
}
52
53
IMPL_LINK(CandidateMgr, WindowEventListener, VclWindowEvent&, rEvent, void)
54
0
{
55
0
    vcl::Window* pWindow = rEvent.GetWindow();
56
0
    if (rEvent.GetId() == VclEventId::ObjectDying)
57
0
    {
58
0
        m_aDeletedCandidates.insert(pWindow);
59
0
    }
60
0
}
61
62
CandidateMgr::~CandidateMgr()
63
3
{
64
3
    for (VclPtr<vcl::Window>& pCandidate : m_aCandidates)
65
0
    {
66
0
        if (m_aDeletedCandidates.contains(pCandidate))
67
0
            continue;
68
0
        pCandidate->RemoveEventListener(LINK(this, CandidateMgr, WindowEventListener));
69
0
    }
70
3
}
71
72
void PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect)
73
3
{
74
3
    if (!rWindow.IsChildTransparentModeEnabled())
75
0
        return;
76
77
3
    CandidateMgr aManager;
78
3
    aManager.PaintTransparentChildren(rWindow, rPixelRect);
79
3
}
80
81
void CandidateMgr::PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect)
82
3
{
83
3
    vcl::Window * pCandidate = rWindow.GetWindow( GetWindowType::LastChild );
84
3
    while (pCandidate)
85
0
    {
86
0
        if (pCandidate->IsPaintTransparent())
87
0
        {
88
0
            const tools::Rectangle aCandidatePosSizePixel(
89
0
                            pCandidate->GetPosPixel(),
90
0
                            pCandidate->GetSizePixel());
91
92
0
            if (aCandidatePosSizePixel.Overlaps(rPixelRect))
93
0
            {
94
0
                m_aCandidates.emplace_back(pCandidate);
95
0
                pCandidate->AddEventListener(LINK(this, CandidateMgr, WindowEventListener));
96
0
            }
97
0
        }
98
0
        pCandidate = pCandidate->GetWindow( GetWindowType::Prev );
99
0
    }
100
101
3
    for (const auto& rpCandidate : m_aCandidates)
102
0
    {
103
0
        pCandidate = rpCandidate.get();
104
0
        if (m_aDeletedCandidates.contains(pCandidate))
105
0
            continue;
106
        //rhbz#1007697 this can cause the window itself to be
107
        //deleted. So we are listening to see if that happens
108
        //and if so, then skip the update
109
0
        pCandidate->Invalidate(InvalidateFlags::NoTransparent|InvalidateFlags::Children);
110
        // important: actually paint the child here!
111
0
        if (m_aDeletedCandidates.contains(pCandidate))
112
0
            continue;
113
0
        pCandidate->PaintImmediately();
114
0
    }
115
3
}
116
117
SdrPreRenderDevice::SdrPreRenderDevice(OutputDevice& rOriginal)
118
0
:   mpOutputDevice(&rOriginal),
119
0
    mpPreRenderDevice(VclPtr<VirtualDevice>::Create())
120
0
{
121
0
}
122
123
SdrPreRenderDevice::~SdrPreRenderDevice()
124
0
{
125
0
    mpPreRenderDevice.disposeAndClear();
126
0
}
127
128
void SdrPreRenderDevice::PreparePreRenderDevice()
129
0
{
130
    // compare size of mpPreRenderDevice with size of visible area
131
0
    if(mpPreRenderDevice->GetOutputSizePixel() != mpOutputDevice->GetOutputSizePixel())
132
0
    {
133
0
        mpPreRenderDevice->SetOutputSizePixel(mpOutputDevice->GetOutputSizePixel());
134
0
    }
135
136
    // Also compare the MapModes for zoom/scroll changes
137
0
    if(mpPreRenderDevice->GetMapMode() != mpOutputDevice->GetMapMode())
138
0
    {
139
0
        mpPreRenderDevice->SetMapMode(mpOutputDevice->GetMapMode());
140
0
    }
141
142
    // #i29186#
143
0
    mpPreRenderDevice->SetDrawMode(mpOutputDevice->GetDrawMode());
144
0
    mpPreRenderDevice->SetSettings(mpOutputDevice->GetSettings());
145
0
}
146
147
void SdrPreRenderDevice::OutputPreRenderDevice(const vcl::Region& rExpandedRegion)
148
0
{
149
    // region to pixels
150
0
    const vcl::Region aRegionPixel(mpOutputDevice->LogicToPixel(rExpandedRegion));
151
    //RegionHandle aRegionHandle(aRegionPixel.BeginEnumRects());
152
    //Rectangle aRegionRectanglePixel;
153
154
    // MapModes off
155
0
    bool bMapModeWasEnabledDest(mpOutputDevice->IsMapModeEnabled());
156
0
    bool bMapModeWasEnabledSource(mpPreRenderDevice->IsMapModeEnabled());
157
0
    mpOutputDevice->EnableMapMode(false);
158
0
    mpPreRenderDevice->EnableMapMode(false);
159
160
0
    RectangleVector aRectangles;
161
0
    aRegionPixel.GetRegionRectangles(aRectangles);
162
163
0
    for(const auto& rRect : aRectangles)
164
0
    {
165
        // for each rectangle, copy the area
166
0
        const Point aTopLeft(rRect.TopLeft());
167
0
        const Size aSize(rRect.GetSize());
168
169
0
        mpOutputDevice->DrawOutDev(
170
0
            aTopLeft, aSize,
171
0
            aTopLeft, aSize,
172
0
            *mpPreRenderDevice);
173
0
    }
174
175
0
    mpOutputDevice->EnableMapMode(bMapModeWasEnabledDest);
176
0
    mpPreRenderDevice->EnableMapMode(bMapModeWasEnabledSource);
177
0
}
178
179
void SdrPaintView::InitOverlayManager(const rtl::Reference<sdr::overlay::OverlayManager> & xOverlayManager)
180
3
{
181
3
    Color aColA(SvtOptionsDrawinglayer::GetStripeColorA());
182
3
    Color aColB(SvtOptionsDrawinglayer::GetStripeColorB());
183
184
3
    if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
185
0
    {
186
0
        aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor();
187
0
        aColB.Invert();
188
0
    }
189
190
3
    xOverlayManager->setStripeColorA(aColA);
191
3
    xOverlayManager->setStripeColorB(aColB);
192
3
    xOverlayManager->setStripeLengthPixel(officecfg::Office::Common::Drawinglayer::StripeLength::get());
193
3
}
194
195
rtl::Reference<sdr::overlay::OverlayManager> SdrPaintView::CreateOverlayManager(OutputDevice& rOutputDevice) const
196
53.8k
{
197
53.8k
    rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager;
198
    // is it a window?
199
53.8k
    if (OUTDEV_WINDOW == rOutputDevice.GetOutDevType())
200
3
    {
201
3
        vcl::Window* pWindow = rOutputDevice.GetOwnerWindow();
202
        // decide which OverlayManager to use
203
3
        if (IsBufferedOverlayAllowed() && !pWindow->SupportsDoubleBuffering())
204
0
        {
205
            // buffered OverlayManager, buffers its background and refreshes from there
206
            // for pure overlay changes (no system redraw). The 3rd parameter specifies
207
            // whether that refresh itself will use a 2nd vdev to avoid flickering.
208
            // Also hand over the old OverlayManager if existent; this means to take over
209
            // the registered OverlayObjects from it
210
0
            xOverlayManager = sdr::overlay::OverlayManagerBuffered::create(rOutputDevice);
211
0
        }
212
3
        else
213
3
        {
214
            // unbuffered OverlayManager, just invalidates places where changes
215
            // take place
216
            // Also hand over the old OverlayManager if existent; this means to take over
217
            // the registered OverlayObjects from it
218
3
            xOverlayManager = sdr::overlay::OverlayManager::create(rOutputDevice);
219
3
        }
220
221
3
        OSL_ENSURE(xOverlayManager.is(), "SdrPaintWindow::SdrPaintWindow: Could not allocate an overlayManager (!)");
222
223
        // Request a repaint so that the buffered overlay manager fills
224
        // its buffer properly.  This is a workaround for missing buffer
225
        // updates.
226
3
        if (!comphelper::LibreOfficeKit::isActive())
227
3
        {
228
3
            pWindow->Invalidate();
229
3
        }
230
231
3
        InitOverlayManager(xOverlayManager);
232
3
    }
233
53.8k
    return xOverlayManager;
234
53.8k
}
235
236
void SdrPaintWindow::impCreateOverlayManager()
237
53.8k
{
238
    // not yet one created?
239
53.8k
    if(!mxOverlayManager.is())
240
53.8k
        mxOverlayManager = mrPaintView.CreateOverlayManager(GetOutputDevice());
241
53.8k
}
242
243
SdrPaintWindow::SdrPaintWindow(SdrPaintView& rNewPaintView, OutputDevice& rOut)
244
57.8k
:   mpOutputDevice(&rOut),
245
57.8k
    mrPaintView(rNewPaintView),
246
57.8k
    mbTemporaryTarget(false), // #i72889#
247
57.8k
    mbOutputToWindow(OUTDEV_WINDOW == mpOutputDevice->GetOutDevType()),
248
57.8k
    mpPatched(nullptr)
249
57.8k
{
250
57.8k
}
251
252
SdrPaintWindow::~SdrPaintWindow()
253
57.8k
{
254
57.8k
    mxOverlayManager.clear();
255
256
57.8k
    mpPreRenderDevice.reset();
257
57.8k
}
258
259
rtl::Reference< sdr::overlay::OverlayManager > const & SdrPaintWindow::GetOverlayManager() const
260
0
{
261
0
    if(!mxOverlayManager.is())
262
0
    {
263
        // Create buffered overlay manager by default.
264
0
        const_cast< SdrPaintWindow* >(this)->impCreateOverlayManager();
265
0
    }
266
267
0
    return mxOverlayManager;
268
0
}
269
270
tools::Rectangle SdrPaintWindow::GetVisibleArea() const
271
0
{
272
0
    Size aVisSizePixel(GetOutputDevice().GetOutputSizePixel());
273
0
    return GetOutputDevice().PixelToLogic(tools::Rectangle(Point(0,0), aVisSizePixel));
274
0
}
275
276
bool SdrPaintWindow::OutputToRecordingMetaFile() const
277
0
{
278
0
    GDIMetaFile* pMetaFile = mpOutputDevice->GetConnectMetaFile();
279
0
    return (pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
280
0
}
281
282
void SdrPaintWindow::PreparePreRenderDevice()
283
53.8k
{
284
53.8k
    const bool bPrepareBufferedOutput(
285
53.8k
        mrPaintView.IsBufferedOutputAllowed()
286
0
        && !OutputToPrinter()
287
0
        && !mpOutputDevice->IsVirtual()
288
0
        && !OutputToRecordingMetaFile());
289
290
53.8k
    if(bPrepareBufferedOutput)
291
0
    {
292
0
        if(!mpPreRenderDevice)
293
0
        {
294
0
            mpPreRenderDevice.reset(new SdrPreRenderDevice(*mpOutputDevice));
295
0
        }
296
0
        mpPreRenderDevice->PreparePreRenderDevice();
297
0
    }
298
53.8k
    else
299
53.8k
    {
300
53.8k
        mpPreRenderDevice.reset();
301
53.8k
    }
302
53.8k
}
303
304
void SdrPaintWindow::OutputPreRenderDevice(const vcl::Region& rExpandedRegion)
305
53.8k
{
306
53.8k
    if(mpPreRenderDevice)
307
0
    {
308
0
        mpPreRenderDevice->OutputPreRenderDevice(rExpandedRegion);
309
0
    }
310
53.8k
}
311
312
// #i73602# add flag if buffer shall be used
313
void SdrPaintWindow::DrawOverlay(const vcl::Region& rRegion)
314
53.8k
{
315
    // ## force creation of OverlayManager since the first repaint needs to
316
    // save the background to get a controlled start into overlay mechanism
317
53.8k
    impCreateOverlayManager();
318
319
53.8k
    if(mxOverlayManager.is() && !OutputToPrinter())
320
3
    {
321
3
        if(mpPreRenderDevice)
322
0
        {
323
0
            mxOverlayManager->completeRedraw(rRegion, &mpPreRenderDevice->GetPreRenderDevice());
324
0
        }
325
3
        else
326
3
        {
327
3
            mxOverlayManager->completeRedraw(rRegion);
328
3
        }
329
3
    }
330
53.8k
}
331
332
333
void SdrPaintWindow::SetRedrawRegion(const vcl::Region& rNew)
334
53.8k
{
335
53.8k
    maRedrawRegion = rNew;
336
53.8k
}
337
338
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */