Coverage Report

Created: 2026-05-16 09:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/sdr/overlay/overlayselection.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/sdr/overlay/overlayselection.hxx>
21
#include <basegfx/polygon/b2dpolygontools.hxx>
22
#include <basegfx/polygon/b2dpolygon.hxx>
23
#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
24
#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
25
#include <svtools/optionsdrawinglayer.hxx>
26
#include <vcl/svapp.hxx>
27
#include <vcl/outdev.hxx>
28
#include <vcl/settings.hxx>
29
#include <drawinglayer/primitive2d/invertprimitive2d.hxx>
30
#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
31
#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
32
#include <svx/sdr/overlay/overlaymanager.hxx>
33
#include <officecfg/Office/Common.hxx>
34
#include <o3tl/sorted_vector.hxx>
35
#include <map>
36
#include <tools/fract.hxx>
37
38
namespace sdr::overlay
39
{
40
        static basegfx::B2DPolyPolygon impCombineRectanglesToPolyPolygon(const std::vector< basegfx::B2DRange >& rRectangles, bool bOffset, double fOffset)
41
0
        {
42
0
            if (!bOffset)
43
0
                return basegfx::utils::combineRectanglesToPolyPolygon(rRectangles);
44
0
            std::vector< basegfx::B2DRange > aGrownRectangles;
45
0
            aGrownRectangles.reserve(rRectangles.size());
46
0
            for (const auto & rInput : rRectangles)
47
0
            {
48
0
                basegfx::B2DRange aRange(rInput);
49
0
                aRange.grow(fOffset);
50
0
                aGrownRectangles.push_back(aRange);
51
0
            }
52
0
            return basegfx::utils::combineRectanglesToPolyPolygon(aGrownRectangles);
53
0
        }
54
55
        // check if wanted type OverlayType::Transparent or OverlayType::Solid
56
        // is possible. If not, fallback to invert mode (classic mode)
57
        static OverlayType impCheckPossibleOverlayType(OverlayType aOverlayType)
58
0
        {
59
0
            if(OverlayType::Invert != aOverlayType)
60
0
            {
61
0
                if(!officecfg::Office::Common::Drawinglayer::TransparentSelection::get())
62
0
                {
63
                    // not possible when switched off by user
64
0
                    return OverlayType::Invert;
65
0
                }
66
0
                else if (const OutputDevice* pOut = Application::GetDefaultDevice())
67
0
                {
68
69
0
                    if(pOut->GetSettings().GetStyleSettings().GetHighContrastMode())
70
0
                    {
71
                        // not possible when in high contrast mode
72
0
                        return  OverlayType::Invert;
73
0
                    }
74
0
                }
75
0
            }
76
77
0
            return aOverlayType;
78
0
        }
79
80
        drawinglayer::primitive2d::Primitive2DContainer OverlaySelection::createOverlayObjectPrimitive2DSequence()
81
0
        {
82
0
            drawinglayer::primitive2d::Primitive2DContainer aRetval;
83
0
            const sal_uInt32 nCount(getRanges().size());
84
85
0
            if(nCount)
86
0
            {
87
                // create range primitives
88
0
                const bool bInvert(OverlayType::Invert == maLastOverlayType);
89
0
                basegfx::BColor aRGBColor(getBaseColor().getBColor());
90
0
                aRetval.resize(nCount);
91
92
0
                if(bInvert)
93
0
                {
94
                    // force color to white for invert to get a full invert
95
0
                    aRGBColor = basegfx::BColor(1.0, 1.0, 1.0);
96
0
                }
97
98
0
                for(sal_uInt32 a(0);a < nCount; a++)
99
0
                {
100
0
                    const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(maRanges[a]));
101
0
                    aRetval[a] =
102
0
                        new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
103
0
                            basegfx::B2DPolyPolygon(aPolygon),
104
0
                            aRGBColor);
105
0
                }
106
107
0
                if(bInvert)
108
0
                {
109
                    // embed all in invert primitive
110
0
                    aRetval = drawinglayer::primitive2d::Primitive2DContainer {
111
0
                            new drawinglayer::primitive2d::InvertPrimitive2D(
112
0
                                std::move(aRetval))
113
0
                    };
114
0
                }
115
0
                else if(maLastOverlayType == OverlayType::Transparent || maLastOverlayType == OverlayType::NoFill)
116
0
                {
117
                    // Determine transparency level
118
0
                    double fTransparence;
119
0
                    if (maLastOverlayType == OverlayType::NoFill)
120
0
                        fTransparence = 1;
121
0
                    else
122
0
                        fTransparence = mnLastTransparence / 100.0;
123
124
                    // embed all rectangles in transparent paint
125
0
                    const drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparence(
126
0
                        new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
127
0
                            std::move(aRetval),
128
0
                            fTransparence));
129
130
0
                    if(mbBorder)
131
0
                    {
132
0
                        aRetval = drawinglayer::primitive2d::Primitive2DContainer {aUnifiedTransparence};
133
134
                        // tdf#161204 Outline with white color to provide contrast
135
0
                        if (mbContrastOutline)
136
0
                        {
137
0
                            basegfx::B2DPolyPolygon aContrastPolyPolygon(basegfx::utils::combineRectanglesToPolyPolygon(getRanges()));
138
0
                            const drawinglayer::primitive2d::Primitive2DReference aContrastSelectionOutline(
139
0
                                new drawinglayer::primitive2d::PolyPolygonHairlinePrimitive2D(
140
0
                                    std::move(aContrastPolyPolygon),
141
0
                                    basegfx::BColor(1.0, 1.0, 1.0)));
142
0
                            aRetval.append(drawinglayer::primitive2d::Primitive2DContainer{aContrastSelectionOutline});
143
0
                        }
144
145
                        // Offset to be applied to the external outline
146
0
                        double fOffset(0);
147
0
                        if (getOverlayManager())
148
0
                            fOffset = getOverlayManager()->getOutputDevice().PixelToLogic(Size(1, 1)).getWidth();
149
150
                        // External outline using themed color
151
0
                        basegfx::B2DPolyPolygon aPolyPolygon(impCombineRectanglesToPolyPolygon(getRanges(), mbContrastOutline, fOffset));
152
0
                        const drawinglayer::primitive2d::Primitive2DReference aSelectionOutline(
153
0
                            new drawinglayer::primitive2d::PolyPolygonHairlinePrimitive2D(
154
0
                                std::move(aPolyPolygon),
155
0
                                aRGBColor));
156
157
                        // Add to result
158
0
                        aRetval.append(drawinglayer::primitive2d::Primitive2DContainer {aSelectionOutline});
159
0
                    }
160
0
                    else
161
0
                    {
162
                        // just add transparent part
163
0
                        aRetval = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparence };
164
0
                    }
165
0
                }
166
0
            }
167
168
0
            return aRetval;
169
0
        }
170
171
        OverlaySelection::OverlaySelection(
172
            OverlayType eType,
173
            const Color& rColor,
174
            std::vector< basegfx::B2DRange >&& rRanges,
175
            bool bBorder,
176
            bool bContrastOutline)
177
0
        :   OverlayObject(rColor),
178
0
            meOverlayType(eType),
179
0
            maRanges(std::move(rRanges)),
180
0
            maLastOverlayType(eType),
181
0
            mnLastTransparence(0),
182
0
            mbBorder(bBorder),
183
0
            mbContrastOutline(bContrastOutline)
184
0
        {
185
            // no AA for selection overlays
186
0
            allowAntiAliase(false);
187
0
        }
188
189
        OverlaySelection::~OverlaySelection()
190
0
        {
191
0
            if(getOverlayManager())
192
0
            {
193
0
                getOverlayManager()->remove(*this);
194
0
            }
195
0
        }
196
197
        drawinglayer::primitive2d::Primitive2DContainer OverlaySelection::getOverlayObjectPrimitive2DSequence() const
198
0
        {
199
            // get current values
200
0
            const OverlayType aNewOverlayType(impCheckPossibleOverlayType(meOverlayType));
201
0
            const sal_uInt16 nNewTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent());
202
203
0
            if(!getPrimitive2DSequence().empty())
204
0
            {
205
0
                if(aNewOverlayType != maLastOverlayType
206
0
                    || nNewTransparence != mnLastTransparence)
207
0
                {
208
                    // conditions of last local decomposition have changed, delete
209
0
                    const_cast< OverlaySelection* >(this)->resetPrimitive2DSequence();
210
0
                }
211
0
            }
212
213
0
            if(getPrimitive2DSequence().empty())
214
0
            {
215
                // remember new values
216
0
                const_cast< OverlaySelection* >(this)->maLastOverlayType = aNewOverlayType;
217
0
                const_cast< OverlaySelection* >(this)->mnLastTransparence = nNewTransparence;
218
0
            }
219
220
            // call base implementation
221
0
            return OverlayObject::getOverlayObjectPrimitive2DSequence();
222
0
        }
223
224
        void OverlaySelection::setRanges(std::vector< basegfx::B2DRange >&& rNew)
225
0
        {
226
0
            if(rNew != maRanges)
227
0
            {
228
0
                maRanges = std::move(rNew);
229
0
                objectChange();
230
0
            }
231
0
        }
232
} // end of namespace
233
234
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */