Coverage Report

Created: 2025-11-16 09:57

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