Coverage Report

Created: 2026-04-09 11:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/cctrl/dpcontrol.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 <dpcontrol.hxx>
21
22
#include <vcl/outdev.hxx>
23
#include <vcl/settings.hxx>
24
#include <comphelper/lok.hxx>
25
#include <scitems.hxx>
26
#include <document.hxx>
27
#include <docpool.hxx>
28
#include <patattr.hxx>
29
#include <svtools/colorcfg.hxx>
30
#include <tools/mapunit.hxx>
31
32
ScDPFieldButton::ScDPFieldButton(OutputDevice* pOutDev, const StyleSettings& rStyle, double fZoomY, ScDocument& rDoc):
33
0
    mfZoomY(fZoomY),
34
0
    mrDoc(rDoc),
35
0
    mpOutDev(pOutDev),
36
0
    mrStyle(rStyle),
37
0
    mnToggleIndent(0),
38
0
    mbBaseButton(true),
39
0
    mbPopupButton(false),
40
0
    mbPopupButtonMulti(false),
41
0
    mbToggleButton(false),
42
0
    mbToggleCollapse(false),
43
0
    mbHasHiddenMember(false),
44
0
    mbPopupPressed(false),
45
0
    mbPopupLeft(false)
46
0
{
47
0
}
48
49
ScDPFieldButton::~ScDPFieldButton()
50
0
{
51
0
}
52
53
void ScDPFieldButton::setText(const OUString& rText)
54
0
{
55
0
    maText = rText;
56
0
}
57
58
void ScDPFieldButton::setBoundingBox(const Point& rPos, const Size& rSize, bool bLayoutRTL)
59
0
{
60
0
    maPos = rPos;
61
0
    maSize = rSize;
62
0
    if (bLayoutRTL)
63
0
    {
64
        // rPos is the logical-left position, adjust maPos to visual-left (inside the cell border)
65
0
        maPos.AdjustX( -(maSize.Width() - 1) );
66
0
    }
67
0
}
68
69
void ScDPFieldButton::setDrawBaseButton(bool b)
70
0
{
71
0
    mbBaseButton = b;
72
0
}
73
74
void ScDPFieldButton::setDrawPopupButton(bool b)
75
0
{
76
0
    mbPopupButton = b;
77
0
}
78
79
void ScDPFieldButton::setDrawPopupButtonMulti(bool b)
80
0
{
81
0
    mbPopupButtonMulti = b;
82
0
}
83
84
void ScDPFieldButton::setDrawToggleButton(bool b, bool bCollapse, sal_Int32 nIndent)
85
0
{
86
0
    mbToggleButton = b;
87
0
    mbToggleCollapse = bCollapse;
88
0
    mnToggleIndent = nIndent;
89
0
}
90
91
void ScDPFieldButton::setHasHiddenMember(bool b)
92
0
{
93
0
    mbHasHiddenMember = b;
94
0
}
95
96
void ScDPFieldButton::setPopupPressed(bool b)
97
0
{
98
0
    mbPopupPressed = b;
99
0
}
100
101
void ScDPFieldButton::setPopupLeft(bool b)
102
0
{
103
0
    mbPopupLeft = b;
104
0
}
105
106
void ScDPFieldButton::draw()
107
0
{
108
0
    bool bOldMapEnabled = mpOutDev->IsMapModeEnabled();
109
110
0
    if (mpOutDev->GetMapMode().GetMapUnit() != MapUnit::MapPixel)
111
0
        mpOutDev->EnableMapMode(false);
112
113
0
    if (mbBaseButton)
114
0
    {
115
        // Background
116
0
        tools::Rectangle aRect(maPos, maSize);
117
0
        mpOutDev->SetLineColor(mrStyle.GetFaceColor());
118
0
        mpOutDev->SetFillColor(mrStyle.GetFaceColor());
119
0
        mpOutDev->DrawRect(aRect);
120
121
        // Border lines
122
0
        mpOutDev->SetLineColor(mrStyle.GetLightColor());
123
0
        mpOutDev->DrawLine(maPos, Point(maPos.X(), maPos.Y()+maSize.Height()-1));
124
0
        mpOutDev->DrawLine(maPos, Point(maPos.X()+maSize.Width()-1, maPos.Y()));
125
126
0
        mpOutDev->SetLineColor(mrStyle.GetShadowColor());
127
0
        mpOutDev->DrawLine(Point(maPos.X(), maPos.Y()+maSize.Height()-1),
128
0
                           Point(maPos.X()+maSize.Width()-1, maPos.Y()+maSize.Height()-1));
129
0
        mpOutDev->DrawLine(Point(maPos.X()+maSize.Width()-1, maPos.Y()),
130
0
                           Point(maPos.X()+maSize.Width()-1, maPos.Y()+maSize.Height()-1));
131
132
        // Field name.
133
        // Get the font and size the same way as in scenario selection (lcl_DrawOneFrame in gridwin4.cxx)
134
0
        vcl::Font aTextFont( mrStyle.GetAppFont() );
135
        //  use ScPatternAttr::GetFont only for font size
136
0
        vcl::Font aAttrFont;
137
0
        mrDoc.getCellAttributeHelper().getDefaultCellAttribute().fillFontOnly(aAttrFont, mpOutDev, &mfZoomY);
138
0
        aTextFont.SetFontSize(aAttrFont.GetFontSize());
139
0
        mpOutDev->SetFont(aTextFont);
140
0
        mpOutDev->SetTextColor(mrStyle.GetButtonTextColor());
141
142
0
        Point aTextPos = maPos;
143
0
        tools::Long nTHeight = mpOutDev->GetTextHeight();
144
0
        aTextPos.setX(maPos.getX() + 2); // 2 = Margin
145
0
        aTextPos.setY(maPos.getY() + (maSize.Height()-nTHeight)/2);
146
147
0
        auto popIt = mpOutDev->ScopedPush(vcl::PushFlags::CLIPREGION);
148
0
        mpOutDev->IntersectClipRegion(aRect);
149
0
        mpOutDev->DrawText(aTextPos, maText);
150
0
    }
151
152
0
    if (mbPopupButton || mbPopupButtonMulti)
153
0
        drawPopupButton();
154
155
0
    if (mbToggleButton)
156
0
        drawToggleButton();
157
158
0
    mpOutDev->EnableMapMode(bOldMapEnabled);
159
0
}
160
161
void ScDPFieldButton::getPopupBoundingBox(Point& rPos, Size& rSize) const
162
0
{
163
0
    float fScaleFactor = mpOutDev->GetDPIScaleFactor();
164
165
0
    tools::Long nMaxSize = 18 * fScaleFactor; // Button max size in either dimension
166
167
0
    tools::Long nW = std::min(maSize.getWidth() / 2, nMaxSize);
168
0
    tools::Long nH = std::min(maSize.getHeight(),    nMaxSize);
169
170
0
    double fZoom = mfZoomY > 1.0 ? mfZoomY : 1.0;
171
0
    if (fZoom > 1.0)
172
0
    {
173
0
        nW = fZoom * (nW - 1);
174
0
        nH = fZoom * (nH - 1);
175
0
    }
176
177
    // #i114944# AutoFilter button is left-aligned in RTL.
178
    // DataPilot button is always right-aligned for now, so text output isn't affected.
179
0
    if (mbPopupLeft)
180
0
        rPos.setX(maPos.getX());
181
0
    else
182
0
        rPos.setX(maPos.getX() + maSize.getWidth() - nW);
183
184
0
    rPos.setY(maPos.getY() + maSize.getHeight() - nH);
185
0
    rSize.setWidth(nW);
186
0
    rSize.setHeight(nH);
187
0
}
188
189
void ScDPFieldButton::getToggleBoundingBox(Point& rPos, Size& rSize) const
190
0
{
191
0
    const float fScaleFactor = mpOutDev->GetDPIScaleFactor();
192
193
0
    tools::Long nMaxSize = 13 * fScaleFactor; // Button max size in either dimension
194
0
    tools::Long nMargin = 3 * fScaleFactor;
195
196
0
    tools::Long nIndent = fScaleFactor * o3tl::convert(mnToggleIndent, o3tl::Length::twip, o3tl::Length::px);
197
0
    tools::Long nW = std::min(maSize.getWidth() / 2, nMaxSize);
198
0
    tools::Long nH = std::min(maSize.getHeight(),    nMaxSize);
199
0
    nIndent = std::min(nIndent, maSize.getWidth());
200
201
0
    double fZoom = mfZoomY > 1.0 ? mfZoomY : 1.0;
202
0
    if (fZoom > 1.0)
203
0
    {
204
0
        nW = fZoom * (nW - 1);
205
0
        nH = fZoom * (nH - 1);
206
0
        nIndent = fZoom * (nIndent -1);
207
0
        nMargin = fZoom * (nMargin - 1);
208
0
    }
209
210
    // FIXME: RTL case ?
211
0
    rPos.setX(maPos.getX() + nIndent - nW + nMargin);
212
0
    rPos.setY(maPos.getY() + maSize.getHeight() / 2 - nH / 2 + nMargin);
213
0
    rSize.setWidth(nW - nMargin - 1);
214
0
    rSize.setHeight(nH - nMargin - 1);
215
0
}
216
217
void ScDPFieldButton::drawPopupButton()
218
0
{
219
0
    Point aPos;
220
0
    Size aSize;
221
0
    getPopupBoundingBox(aPos, aSize);
222
223
0
    float fScaleFactor = mpOutDev->GetDPIScaleFactor();
224
225
    // Button background color
226
0
    Color aFaceColor = mrStyle.GetFaceColor();
227
0
    Color aBackgroundColor
228
0
        = mbHasHiddenMember ? mrStyle.GetHighlightColor()
229
0
                            : mbPopupPressed ? mrStyle.GetShadowColor() : aFaceColor;
230
231
    // Button line color
232
0
    mpOutDev->SetLineColor(mrStyle.GetLabelTextColor());
233
    // If the document background is light and face color is dark, use ShadowColor instead
234
0
    Color aDocColor = svtools::ColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
235
0
    if (aDocColor.IsBright() && aFaceColor.IsDark())
236
0
        mpOutDev->SetLineColor(mrStyle.GetShadowColor());
237
238
0
    mpOutDev->SetFillColor(aBackgroundColor);
239
0
    mpOutDev->DrawRect(tools::Rectangle(aPos, aSize));
240
241
    // the arrowhead
242
0
    Color aArrowColor = mbHasHiddenMember ? mrStyle.GetHighlightTextColor() : mrStyle.GetButtonTextColor();
243
    // FIXME: HACK: The following DrawPolygon draws twice in lok rtl mode for some reason.
244
    // => one at the correct location with fill (possibly no outline)
245
    // => and the other at an x offset with outline and without fill
246
    // eg. Replacing this with a DrawRect() does not have any such problems.
247
0
    comphelper::LibreOfficeKit::isActive() ? mpOutDev->SetLineColor() : mpOutDev->SetLineColor(aArrowColor);
248
0
    mpOutDev->SetFillColor(aArrowColor);
249
250
0
    Point aCenter(aPos.X() + (aSize.Width() / 2), aPos.Y() + (aSize.Height() / 2));
251
252
0
    Size aArrowSize(4 * fScaleFactor, 2 * fScaleFactor);
253
254
0
    tools::Polygon aPoly(3);
255
0
    aPoly.SetPoint(Point(aCenter.X() - aArrowSize.Width(), aCenter.Y() - aArrowSize.Height()), 0);
256
0
    aPoly.SetPoint(Point(aCenter.X() + aArrowSize.Width(), aCenter.Y() - aArrowSize.Height()), 1);
257
0
    aPoly.SetPoint(Point(aCenter.X(),                      aCenter.Y() + aArrowSize.Height()), 2);
258
0
    mpOutDev->DrawPolygon(aPoly);
259
260
0
    if (mbHasHiddenMember)
261
0
    {
262
        // tiny little box to display in presence of hidden member(s).
263
0
        Point aBoxPos(aPos.X() + aSize.Width() - 5 * fScaleFactor, aPos.Y() + aSize.Height() - 5 * fScaleFactor);
264
0
        Size aBoxSize(3 * fScaleFactor, 3 * fScaleFactor);
265
0
        mpOutDev->DrawRect(tools::Rectangle(aBoxPos, aBoxSize));
266
0
    }
267
0
}
268
269
void ScDPFieldButton::drawToggleButton()
270
0
{
271
0
    Point aPos;
272
0
    Size aSize;
273
0
    getToggleBoundingBox(aPos, aSize);
274
275
    // Background & outer black border
276
0
    mpOutDev->SetLineColor(COL_BLACK);
277
0
    mpOutDev->SetFillColor();
278
0
    mpOutDev->DrawRect(tools::Rectangle(aPos, aSize));
279
280
0
    Point aCenter(aPos.X() + aSize.getWidth() / 2, aPos.Y() + aSize.getHeight() / 2);
281
282
0
    mpOutDev->DrawLine(
283
0
        Point(aPos.X() + 2, aCenter.Y()),
284
0
        Point(aPos.X() + aSize.getWidth() - 2, aCenter.Y()));
285
286
0
    if (!mbToggleCollapse)
287
0
    {
288
0
        mpOutDev->DrawLine(
289
0
            Point(aCenter.X(), aPos.Y() + 2),
290
0
            Point(aCenter.X(), aPos.Y() + aSize.getHeight() - 2));
291
0
    }
292
0
}
293
294
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */