Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/vcl/source/window/layout.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
10
#include <sal/config.h>
11
12
// Needed since LLVM 15 libc++ (hence the ignored -Wunused-macros for older libc++) when
13
// #include <boost/multi_array.hpp> below includes Boost 1.79.0
14
// workdir/UnpackedTarball/boost/boost/functional.hpp using std::unary_function, but must
15
// come very early here in case <functional> is already (indirectly) included earlier:
16
#include <config_libcxx.h>
17
#if HAVE_LIBCPP
18
#if defined __clang__
19
#pragma clang diagnostic push
20
#pragma clang diagnostic ignored "-Wunused-macros"
21
#endif
22
// [-loplugin:reservedid]:
23
#define _LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION
24
#if defined __clang__
25
#pragma clang diagnostic pop
26
#endif
27
#endif
28
29
#include <string_view>
30
31
#include <config_features.h>
32
#include <com/sun/star/accessibility/AccessibleRole.hpp>
33
#include <comphelper/base64.hxx>
34
#include <comphelper/lok.hxx>
35
#include <o3tl/enumarray.hxx>
36
#include <o3tl/enumrange.hxx>
37
#include <o3tl/string_view.hxx>
38
#include <tools/stream.hxx>
39
#include <utility>
40
#include <vcl/builder.hxx>
41
#include <vcl/toolkit/button.hxx>
42
#include <vcl/cvtgrf.hxx>
43
#include <vcl/decoview.hxx>
44
#include <vcl/help.hxx>
45
#include <vcl/toolkit/dialog.hxx>
46
#include <vcl/layout.hxx>
47
#include <vcl/toolkit/scrbar.hxx>
48
#include <vcl/salnativewidgets.hxx>
49
#include <vcl/stdtext.hxx>
50
#include <vcl/split.hxx>
51
#include <vcl/svapp.hxx>
52
#include <vcl/settings.hxx>
53
#include <vcl/virdev.hxx>
54
#include <bitmaps.hlst>
55
#include <messagedialog.hxx>
56
#include <svdata.hxx>
57
#include <window.h>
58
#include <boost/multi_array.hpp>
59
#include <vcl/toolkit/vclmedit.hxx>
60
#include <vcl/uitest/uiobject.hxx>
61
#include <sal/log.hxx>
62
#include <tools/json_writer.hxx>
63
64
VclContainer::VclContainer(vcl::Window *pParent, WinBits nStyle)
65
0
    : Window(WindowType::CONTAINER)
66
0
    , m_bLayoutDirty(true)
67
0
{
68
0
    ImplInit(pParent, nStyle, nullptr);
69
0
    EnableChildTransparentMode();
70
0
    SetPaintTransparent(true);
71
0
    SetBackground();
72
0
}
73
74
sal_uInt16 VclContainer::getDefaultAccessibleRole() const
75
0
{
76
0
    return css::accessibility::AccessibleRole::PANEL;
77
0
}
78
79
Size VclContainer::GetOptimalSize() const
80
0
{
81
0
    return calculateRequisition();
82
0
}
83
84
void VclContainer::setLayoutPosSize(vcl::Window &rWindow, const Point &rPos, const Size &rSize)
85
0
{
86
0
    sal_Int32 nBorderWidth = rWindow.get_border_width();
87
0
    sal_Int32 nLeft = rWindow.get_margin_start() + nBorderWidth;
88
0
    sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth;
89
0
    sal_Int32 nRight = rWindow.get_margin_end() + nBorderWidth;
90
0
    sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth;
91
0
    Point aPos(rPos.X() + nLeft, rPos.Y() + nTop);
92
0
    Size aSize(rSize.Width() - nLeft - nRight, rSize.Height() - nTop - nBottom);
93
0
    rWindow.SetPosSizePixel(aPos, aSize);
94
0
}
95
96
void VclContainer::setLayoutAllocation(vcl::Window &rChild, const Point &rAllocPos, const Size &rChildAlloc)
97
0
{
98
0
    VclAlign eHalign = rChild.get_halign();
99
0
    VclAlign eValign = rChild.get_valign();
100
101
    //typical case
102
0
    if (eHalign == VclAlign::Fill && eValign == VclAlign::Fill)
103
0
    {
104
0
        setLayoutPosSize(rChild, rAllocPos, rChildAlloc);
105
0
        return;
106
0
    }
107
108
0
    Point aChildPos(rAllocPos);
109
0
    Size aChildSize(rChildAlloc);
110
0
    Size aChildPreferredSize(getLayoutRequisition(rChild));
111
112
0
    switch (eHalign)
113
0
    {
114
0
        case VclAlign::Fill:
115
0
            break;
116
0
        case VclAlign::Start:
117
0
            if (aChildPreferredSize.Width() < rChildAlloc.Width())
118
0
                aChildSize.setWidth( aChildPreferredSize.Width() );
119
0
            break;
120
0
        case VclAlign::End:
121
0
            if (aChildPreferredSize.Width() < rChildAlloc.Width())
122
0
                aChildSize.setWidth( aChildPreferredSize.Width() );
123
0
            aChildPos.AdjustX(rChildAlloc.Width() );
124
0
            aChildPos.AdjustX( -(aChildSize.Width()) );
125
0
            break;
126
0
        case VclAlign::Center:
127
0
            if (aChildPreferredSize.Width() < aChildSize.Width())
128
0
                aChildSize.setWidth( aChildPreferredSize.Width() );
129
0
            aChildPos.AdjustX((rChildAlloc.Width() - aChildSize.Width()) / 2 );
130
0
            break;
131
0
    }
132
133
0
    switch (eValign)
134
0
    {
135
0
        case VclAlign::Fill:
136
0
            break;
137
0
        case VclAlign::Start:
138
0
            if (aChildPreferredSize.Height() < rChildAlloc.Height())
139
0
                aChildSize.setHeight( aChildPreferredSize.Height() );
140
0
            break;
141
0
        case VclAlign::End:
142
0
            if (aChildPreferredSize.Height() < rChildAlloc.Height())
143
0
                aChildSize.setHeight( aChildPreferredSize.Height() );
144
0
            aChildPos.AdjustY(rChildAlloc.Height() );
145
0
            aChildPos.AdjustY( -(aChildSize.Height()) );
146
0
            break;
147
0
        case VclAlign::Center:
148
0
            if (aChildPreferredSize.Height() < aChildSize.Height())
149
0
                aChildSize.setHeight( aChildPreferredSize.Height() );
150
0
            aChildPos.AdjustY((rChildAlloc.Height() - aChildSize.Height()) / 2 );
151
0
            break;
152
0
    }
153
154
0
    setLayoutPosSize(rChild, aChildPos, aChildSize);
155
0
}
156
157
namespace
158
{
159
    Size subtractBorder(const vcl::Window &rWindow, const Size& rSize)
160
0
    {
161
0
        sal_Int32 nBorderWidth = rWindow.get_border_width();
162
0
        sal_Int32 nLeft = rWindow.get_margin_start() + nBorderWidth;
163
0
        sal_Int32 nTop = rWindow.get_margin_top() + nBorderWidth;
164
0
        sal_Int32 nRight = rWindow.get_margin_end() + nBorderWidth;
165
0
        sal_Int32 nBottom = rWindow.get_margin_bottom() + nBorderWidth;
166
0
        Size aSize(rSize);
167
0
        return Size(aSize.Width() + nLeft + nRight, aSize.Height() + nTop + nBottom);
168
0
    }
169
}
170
171
Size VclContainer::getLayoutRequisition(const vcl::Window &rWindow)
172
0
{
173
0
    return subtractBorder(rWindow, rWindow.get_preferred_size());
174
0
}
175
176
void VclContainer::SetPosSizePixel(const Point& rAllocPos, const Size& rAllocation)
177
0
{
178
0
    bool bSizeChanged = rAllocation != GetOutputSizePixel();
179
0
    Window::SetPosSizePixel(rAllocPos, rAllocation);
180
0
    if (m_bLayoutDirty || bSizeChanged)
181
0
    {
182
0
        m_bLayoutDirty = false;
183
0
        setAllocation(rAllocation);
184
0
    }
185
0
}
186
187
void VclContainer::SetPosPixel(const Point& rAllocPos)
188
0
{
189
0
    Point aAllocPos = rAllocPos;
190
0
    sal_Int32 nBorderWidth = get_border_width();
191
0
    aAllocPos.AdjustX(nBorderWidth + get_margin_start() );
192
0
    aAllocPos.AdjustY(nBorderWidth + get_margin_top() );
193
194
0
    if (aAllocPos != GetPosPixel())
195
0
        Window::SetPosPixel(aAllocPos);
196
0
}
197
198
void VclContainer::SetSizePixel(const Size& rAllocation)
199
0
{
200
0
    Size aAllocation = rAllocation;
201
0
    sal_Int32 nBorderWidth = get_border_width();
202
0
    aAllocation.AdjustWidth( -(nBorderWidth*2 + get_margin_start() + get_margin_end()) );
203
0
    aAllocation.AdjustHeight( -(nBorderWidth*2 + get_margin_top() + get_margin_bottom()) );
204
0
    bool bSizeChanged = aAllocation != GetSizePixel();
205
0
    if (bSizeChanged)
206
0
        Window::SetSizePixel(aAllocation);
207
0
    if (m_bLayoutDirty || bSizeChanged)
208
0
    {
209
0
        m_bLayoutDirty = false;
210
0
        setAllocation(aAllocation);
211
0
    }
212
0
}
213
214
void VclContainer::queue_resize(StateChangedType eReason)
215
0
{
216
0
    m_bLayoutDirty = true;
217
0
    Window::queue_resize(eReason);
218
0
}
219
220
// support for screenshot context menu
221
void VclContainer::Command(const CommandEvent& rCEvt)
222
0
{
223
0
    if (CommandEventId::ContextMenu == rCEvt.GetCommand())
224
0
    {
225
0
        auto pParent = GetParent();
226
0
        if (pParent)
227
0
        {
228
0
            CommandEvent aCEvt(rCEvt.GetMousePosPixel() + GetPosPixel(), rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData());
229
0
            pParent->Command(aCEvt);
230
0
            return;
231
0
        }
232
0
    }
233
234
    // call parent (do not consume)
235
0
    Window::Command(rCEvt);
236
0
}
237
238
void VclBox::accumulateMaxes(const Size &rChildSize, Size &rSize) const
239
0
{
240
0
    tools::Long nSecondaryChildDimension = getSecondaryDimension(rChildSize);
241
0
    tools::Long nSecondaryBoxDimension = getSecondaryDimension(rSize);
242
0
    setSecondaryDimension(rSize, std::max(nSecondaryChildDimension, nSecondaryBoxDimension));
243
244
0
    tools::Long nPrimaryChildDimension = getPrimaryDimension(rChildSize);
245
0
    tools::Long nPrimaryBoxDimension = getPrimaryDimension(rSize);
246
0
    if (m_bHomogeneous)
247
0
        setPrimaryDimension(rSize, std::max(nPrimaryBoxDimension, nPrimaryChildDimension));
248
0
    else
249
0
        setPrimaryDimension(rSize, nPrimaryBoxDimension + nPrimaryChildDimension);
250
0
}
251
252
Size VclBox::calculateRequisition() const
253
0
{
254
0
    sal_uInt16 nVisibleChildren = 0;
255
256
0
    Size aSize;
257
0
    for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
258
0
    {
259
0
        if (!pChild->IsVisible())
260
0
            continue;
261
0
        ++nVisibleChildren;
262
0
        Size aChildSize = getLayoutRequisition(*pChild);
263
264
0
        tools::Long nPrimaryDimension = getPrimaryDimension(aChildSize);
265
0
        nPrimaryDimension += pChild->get_padding() * 2;
266
0
        setPrimaryDimension(aChildSize, nPrimaryDimension);
267
268
0
        accumulateMaxes(aChildSize, aSize);
269
0
    }
270
271
0
    return finalizeMaxes(aSize, nVisibleChildren);
272
0
}
273
274
void VclBox::setAllocation(const Size &rAllocation)
275
0
{
276
0
    sal_uInt16 nVisibleChildren = 0, nExpandChildren = 0;
277
0
    for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
278
0
    {
279
0
        if (!pChild->IsVisible())
280
0
            continue;
281
0
        ++nVisibleChildren;
282
0
        bool bExpand = getPrimaryDimensionChildExpand(*pChild);
283
0
        if (bExpand)
284
0
            ++nExpandChildren;
285
0
    }
286
287
0
    if (!nVisibleChildren)
288
0
        return;
289
290
0
    tools::Long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);
291
292
0
    tools::Long nHomogeneousDimension = 0, nExtraSpace = 0;
293
0
    if (m_bHomogeneous)
294
0
    {
295
0
        nHomogeneousDimension = (nAllocPrimaryDimension -
296
0
            (nVisibleChildren - 1) * m_nSpacing) / nVisibleChildren;
297
0
    }
298
0
    else if (nExpandChildren)
299
0
    {
300
0
        Size aRequisition = calculateRequisition();
301
0
        tools::Long nPrimaryDimension = getPrimaryDimension(rAllocation);
302
0
        nExtraSpace = (nPrimaryDimension - getPrimaryDimension(aRequisition)) / nExpandChildren;
303
0
    }
304
305
    //Split into those we pack from the start onwards, and those we pack from the end backwards
306
0
    o3tl::enumarray<VclPackType,std::vector<vcl::Window*>> aWindows;
307
0
    for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
308
0
    {
309
0
        if (!pChild->IsVisible())
310
0
            continue;
311
312
0
        VclPackType ePacking = pChild->get_pack_type();
313
0
        aWindows[ePacking].push_back(pChild);
314
0
    }
315
316
    //See VclBuilder::sortIntoBestTabTraversalOrder for why they are in visual
317
    //order under the parent which requires us to reverse them here to
318
    //pack from the end back
319
0
    std::reverse(aWindows[VclPackType::End].begin(),aWindows[VclPackType::End].end());
320
321
0
    for (VclPackType ePackType : o3tl::enumrange<VclPackType>())
322
0
    {
323
0
        Point aPos(0, 0);
324
0
        if (ePackType == VclPackType::End)
325
0
        {
326
0
            tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aPos);
327
0
            setPrimaryCoordinate(aPos, nPrimaryCoordinate + nAllocPrimaryDimension);
328
0
        }
329
330
0
        for (auto const& window : aWindows[ePackType])
331
0
        {
332
0
            vcl::Window *pChild = window;
333
334
0
            tools::Long nPadding = pChild->get_padding();
335
336
0
            Size aBoxSize;
337
0
            if (m_bHomogeneous)
338
0
                setPrimaryDimension(aBoxSize, nHomogeneousDimension);
339
0
            else
340
0
            {
341
0
                aBoxSize = getLayoutRequisition(*pChild);
342
0
                tools::Long nPrimaryDimension = getPrimaryDimension(aBoxSize);
343
0
                nPrimaryDimension += nPadding * 2;
344
0
                if (getPrimaryDimensionChildExpand(*pChild))
345
0
                    nPrimaryDimension += nExtraSpace;
346
0
                setPrimaryDimension(aBoxSize, nPrimaryDimension);
347
0
            }
348
0
            setSecondaryDimension(aBoxSize, getSecondaryDimension(rAllocation));
349
350
0
            Point aChildPos(aPos);
351
0
            Size aChildSize(aBoxSize);
352
0
            tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aPos);
353
354
0
            bool bFill = pChild->get_fill();
355
0
            if (bFill)
356
0
            {
357
0
                setPrimaryDimension(aChildSize, std::max(static_cast<tools::Long>(1),
358
0
                    std::min(getPrimaryDimension(rAllocation), getPrimaryDimension(aBoxSize) - nPadding * 2)));
359
360
0
                setPrimaryCoordinate(aChildPos, nPrimaryCoordinate + nPadding);
361
0
            }
362
0
            else
363
0
            {
364
0
                setPrimaryDimension(aChildSize,
365
0
                    getPrimaryDimension(getLayoutRequisition(*pChild)));
366
367
0
                setPrimaryCoordinate(aChildPos, nPrimaryCoordinate +
368
0
                    (getPrimaryDimension(aBoxSize) - getPrimaryDimension(aChildSize)) / 2);
369
0
            }
370
371
0
            tools::Long nDiff = getPrimaryDimension(aBoxSize) + m_nSpacing;
372
0
            if (ePackType == VclPackType::Start)
373
0
                setPrimaryCoordinate(aPos, nPrimaryCoordinate + nDiff);
374
0
            else
375
0
            {
376
0
                setPrimaryCoordinate(aPos, nPrimaryCoordinate - nDiff);
377
0
                setPrimaryCoordinate(aChildPos, getPrimaryCoordinate(aChildPos) -
378
0
                    getPrimaryDimension(aBoxSize));
379
0
            }
380
381
0
            setLayoutAllocation(*pChild, aChildPos, aChildSize);
382
0
        }
383
0
    }
384
0
}
385
386
bool VclBox::set_property(const OUString &rKey, const OUString &rValue)
387
0
{
388
0
    if (rKey == "spacing")
389
0
        set_spacing(rValue.toInt32());
390
0
    else if (rKey == "homogeneous")
391
0
        set_homogeneous(toBool(rValue));
392
0
    else
393
0
        return VclContainer::set_property(rKey, rValue);
394
0
    return true;
395
0
}
396
397
void VclBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
398
0
{
399
0
    VclContainer::DumpAsPropertyTree(rJsonWriter);
400
0
    rJsonWriter.put("vertical", m_bVerticalContainer);
401
0
}
402
403
sal_uInt16 VclBox::getDefaultAccessibleRole() const
404
0
{
405
    // fdo#74284 call Boxes Panels, keep them as "Filler" under
406
    // at least Linux seeing as that's what Gtk3 did for GtkBoxes.
407
    // Though now with Gtk4 that uses GTK_ACCESSIBLE_ROLE_GROUP
408
    // which maps to ATSPI_ROLE_PANEL
409
#if defined(_WIN32)
410
    return css::accessibility::AccessibleRole::PANEL;
411
#else
412
0
    static sal_uInt16 eRole = Application::GetToolkitName() == "gtk4" ?
413
0
                              css::accessibility::AccessibleRole::PANEL :
414
0
                              css::accessibility::AccessibleRole::FILLER;
415
0
    return eRole;
416
0
#endif
417
0
}
418
419
0
#define DEFAULT_CHILD_MIN_WIDTH 85
420
0
#define DEFAULT_CHILD_MIN_HEIGHT 27
421
422
Size VclBox::finalizeMaxes(const Size &rSize, sal_uInt16 nVisibleChildren) const
423
0
{
424
0
    Size aRet;
425
426
0
    if (nVisibleChildren)
427
0
    {
428
0
        tools::Long nPrimaryDimension = getPrimaryDimension(rSize);
429
0
        if (m_bHomogeneous)
430
0
            nPrimaryDimension *= nVisibleChildren;
431
0
        setPrimaryDimension(aRet, nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
432
0
        setSecondaryDimension(aRet, getSecondaryDimension(rSize));
433
0
    }
434
435
0
    return aRet;
436
0
}
437
438
Size VclButtonBox::addReqGroups(const VclButtonBox::Requisition &rReq) const
439
0
{
440
0
    Size aRet;
441
442
0
    tools::Long nMainGroupDimension = getPrimaryDimension(rReq.m_aMainGroupSize);
443
0
    tools::Long nSubGroupDimension = getPrimaryDimension(rReq.m_aSubGroupSize);
444
445
0
    setPrimaryDimension(aRet, nMainGroupDimension + nSubGroupDimension);
446
447
0
    setSecondaryDimension(aRet,
448
0
        std::max(getSecondaryDimension(rReq.m_aMainGroupSize),
449
0
        getSecondaryDimension(rReq.m_aSubGroupSize)));
450
451
0
    return aRet;
452
0
}
453
454
static tools::Long getMaxNonOutlier(const std::vector<tools::Long> &rG, tools::Long nAvgDimension)
455
0
{
456
0
    tools::Long nMaxDimensionNonOutlier = 0;
457
0
    for (auto const& nPrimaryChildDimension : rG)
458
0
    {
459
0
        if (nPrimaryChildDimension < nAvgDimension * 1.5)
460
0
        {
461
0
            nMaxDimensionNonOutlier = std::max(nPrimaryChildDimension,
462
0
                nMaxDimensionNonOutlier);
463
0
        }
464
0
    }
465
0
    return nMaxDimensionNonOutlier;
466
0
}
467
468
static std::vector<tools::Long> setButtonSizes(const std::vector<tools::Long> &rG,
469
    const std::vector<bool> &rNonHomogeneous,
470
    tools::Long nAvgDimension, tools::Long nMaxNonOutlier, tools::Long nMinWidth)
471
0
{
472
0
    std::vector<tools::Long> aVec;
473
    //set everything < 1.5 times the average to the same width, leave the
474
    //outliers un-touched
475
0
    std::vector<bool>::const_iterator aJ = rNonHomogeneous.begin();
476
0
    auto nNonOutlierWidth = std::max(nMaxNonOutlier, nMinWidth);
477
0
    for (auto const& nPrimaryChildDimension : rG)
478
0
    {
479
0
        bool bNonHomogeneous = *aJ;
480
0
        if (!bNonHomogeneous && nPrimaryChildDimension < nAvgDimension * 1.5)
481
0
        {
482
0
            aVec.push_back(nNonOutlierWidth);
483
0
        }
484
0
        else
485
0
        {
486
0
            aVec.push_back(std::max(nPrimaryChildDimension, nMinWidth));
487
0
        }
488
0
        ++aJ;
489
0
    }
490
0
    return aVec;
491
0
}
492
493
VclButtonBox::Requisition VclButtonBox::calculatePrimarySecondaryRequisitions() const
494
0
{
495
0
    Requisition aReq;
496
497
0
    Size aMainGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
498
0
    Size aSubGroupSize(DEFAULT_CHILD_MIN_WIDTH, DEFAULT_CHILD_MIN_HEIGHT); //to-do, pull from theme
499
500
0
    tools::Long nMinMainGroupPrimary = getPrimaryDimension(aMainGroupSize);
501
0
    tools::Long nMinSubGroupPrimary = getPrimaryDimension(aSubGroupSize);
502
0
    tools::Long nMainGroupSecondary = getSecondaryDimension(aMainGroupSize);
503
0
    tools::Long nSubGroupSecondary = getSecondaryDimension(aSubGroupSize);
504
505
0
    bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center);
506
507
0
    std::vector<tools::Long> aMainGroupSizes;
508
0
    std::vector<bool> aMainGroupNonHomogeneous;
509
0
    std::vector<tools::Long> aSubGroupSizes;
510
0
    std::vector<bool> aSubGroupNonHomogeneous;
511
512
0
    for (const vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
513
0
    {
514
0
        if (!pChild->IsVisible())
515
0
            continue;
516
0
        Size aChildSize = getLayoutRequisition(*pChild);
517
0
        if (bIgnoreSecondaryPacking || !pChild->get_secondary())
518
0
        {
519
            //set the max secondary dimension
520
0
            nMainGroupSecondary = std::max(nMainGroupSecondary, getSecondaryDimension(aChildSize));
521
            //collect the primary dimensions
522
0
            aMainGroupSizes.push_back(getPrimaryDimension(aChildSize));
523
0
            aMainGroupNonHomogeneous.push_back(pChild->get_non_homogeneous());
524
0
        }
525
0
        else
526
0
        {
527
0
            nSubGroupSecondary = std::max(nSubGroupSecondary, getSecondaryDimension(aChildSize));
528
0
            aSubGroupSizes.push_back(getPrimaryDimension(aChildSize));
529
0
            aSubGroupNonHomogeneous.push_back(pChild->get_non_homogeneous());
530
0
        }
531
0
    }
532
533
0
    if (m_bHomogeneous)
534
0
    {
535
0
        tools::Long nMaxMainDimension = aMainGroupSizes.empty() ? 0 :
536
0
            *std::max_element(aMainGroupSizes.begin(), aMainGroupSizes.end());
537
0
        nMaxMainDimension = std::max(nMaxMainDimension, nMinMainGroupPrimary);
538
0
        tools::Long nMaxSubDimension = aSubGroupSizes.empty() ? 0 :
539
0
            *std::max_element(aSubGroupSizes.begin(), aSubGroupSizes.end());
540
0
        nMaxSubDimension = std::max(nMaxSubDimension, nMinSubGroupPrimary);
541
0
        tools::Long nMaxDimension = std::max(nMaxMainDimension, nMaxSubDimension);
542
0
        aReq.m_aMainGroupDimensions.resize(aMainGroupSizes.size(), nMaxDimension);
543
0
        aReq.m_aSubGroupDimensions.resize(aSubGroupSizes.size(), nMaxDimension);
544
0
    }
545
0
    else
546
0
    {
547
        //Ideally set everything to the same size, but find outlier widgets
548
        //that are way wider than the average and leave them
549
        //at their natural size and set the remainder to share the
550
        //max size of the remaining members of the buttonbox
551
0
        tools::Long nAccDimension = std::accumulate(aMainGroupSizes.begin(),
552
0
            aMainGroupSizes.end(), 0);
553
0
        nAccDimension = std::accumulate(aSubGroupSizes.begin(),
554
0
            aSubGroupSizes.end(), nAccDimension);
555
556
0
        size_t nTotalSize = aMainGroupSizes.size() + aSubGroupSizes.size();
557
558
0
        tools::Long nAvgDimension = nTotalSize ? nAccDimension / nTotalSize : 0;
559
560
0
        tools::Long nMaxMainNonOutlier = getMaxNonOutlier(aMainGroupSizes,
561
0
            nAvgDimension);
562
0
        tools::Long nMaxSubNonOutlier = getMaxNonOutlier(aSubGroupSizes,
563
0
            nAvgDimension);
564
0
        tools::Long nMaxNonOutlier = std::max(nMaxMainNonOutlier, nMaxSubNonOutlier);
565
566
0
        aReq.m_aMainGroupDimensions = setButtonSizes(aMainGroupSizes,
567
0
            aMainGroupNonHomogeneous,
568
0
            nAvgDimension, nMaxNonOutlier, nMinMainGroupPrimary);
569
0
        aReq.m_aSubGroupDimensions = setButtonSizes(aSubGroupSizes,
570
0
            aSubGroupNonHomogeneous,
571
0
            nAvgDimension, nMaxNonOutlier, nMinSubGroupPrimary);
572
0
    }
573
574
0
    if (!aReq.m_aMainGroupDimensions.empty())
575
0
    {
576
0
        setSecondaryDimension(aReq.m_aMainGroupSize, nMainGroupSecondary);
577
0
        setPrimaryDimension(aReq.m_aMainGroupSize,
578
0
            std::accumulate(aReq.m_aMainGroupDimensions.begin(),
579
0
                aReq.m_aMainGroupDimensions.end(), 0));
580
0
    }
581
0
    if (!aReq.m_aSubGroupDimensions.empty())
582
0
    {
583
0
        setSecondaryDimension(aReq.m_aSubGroupSize, nSubGroupSecondary);
584
0
        setPrimaryDimension(aReq.m_aSubGroupSize,
585
0
            std::accumulate(aReq.m_aSubGroupDimensions.begin(),
586
0
                aReq.m_aSubGroupDimensions.end(), 0));
587
0
    }
588
589
0
    return aReq;
590
0
}
591
592
Size VclButtonBox::addSpacing(const Size &rSize, sal_uInt16 nVisibleChildren) const
593
0
{
594
0
    Size aRet;
595
596
0
    if (nVisibleChildren)
597
0
    {
598
0
        tools::Long nPrimaryDimension = getPrimaryDimension(rSize);
599
0
        setPrimaryDimension(aRet,
600
0
            nPrimaryDimension + m_nSpacing * (nVisibleChildren-1));
601
0
        setSecondaryDimension(aRet, getSecondaryDimension(rSize));
602
0
    }
603
604
0
    return aRet;
605
0
}
606
607
Size VclButtonBox::calculateRequisition() const
608
0
{
609
0
    Requisition aReq(calculatePrimarySecondaryRequisitions());
610
0
    sal_uInt16 nVisibleChildren = aReq.m_aMainGroupDimensions.size() +
611
0
        aReq.m_aSubGroupDimensions.size();
612
0
    return addSpacing(addReqGroups(aReq), nVisibleChildren);
613
0
}
614
615
bool VclButtonBox::set_property(const OUString &rKey, const OUString &rValue)
616
0
{
617
0
    if (rKey == "layout-style")
618
0
    {
619
0
        VclButtonBoxStyle eStyle = VclButtonBoxStyle::Default;
620
0
        if (rValue == "spread")
621
0
            eStyle = VclButtonBoxStyle::Spread;
622
0
        else if (rValue == "edge")
623
0
            eStyle = VclButtonBoxStyle::Edge;
624
0
        else if (rValue == "start")
625
0
            eStyle = VclButtonBoxStyle::Start;
626
0
        else if (rValue == "end")
627
0
            eStyle = VclButtonBoxStyle::End;
628
0
        else if (rValue == "center")
629
0
            eStyle = VclButtonBoxStyle::Center;
630
0
        else
631
0
        {
632
0
            SAL_WARN("vcl.layout", "unknown layout style " << rValue);
633
0
        }
634
0
        m_eLayoutStyle = eStyle;
635
0
    }
636
0
    else
637
0
        return VclBox::set_property(rKey, rValue);
638
0
    return true;
639
0
}
640
641
void VclButtonBox::setAllocation(const Size &rAllocation)
642
0
{
643
0
    Requisition aReq(calculatePrimarySecondaryRequisitions());
644
645
0
    if (aReq.m_aMainGroupDimensions.empty() && aReq.m_aSubGroupDimensions.empty())
646
0
        return;
647
648
0
    tools::Long nAllocPrimaryDimension = getPrimaryDimension(rAllocation);
649
650
0
    Point aMainGroupPos, aOtherGroupPos;
651
0
    int nSpacing = m_nSpacing;
652
653
    //To-Do, other layout styles
654
0
    switch (m_eLayoutStyle)
655
0
    {
656
0
        case VclButtonBoxStyle::Start:
657
0
            if (!aReq.m_aSubGroupDimensions.empty())
658
0
            {
659
0
                tools::Long nOtherPrimaryDimension = getPrimaryDimension(
660
0
                    addSpacing(aReq.m_aSubGroupSize, aReq.m_aSubGroupDimensions.size()));
661
0
                setPrimaryCoordinate(aOtherGroupPos,
662
0
                    nAllocPrimaryDimension - nOtherPrimaryDimension);
663
0
            }
664
0
            break;
665
0
        case VclButtonBoxStyle::Spread:
666
0
            if (!aReq.m_aMainGroupDimensions.empty())
667
0
            {
668
0
                tools::Long nMainPrimaryDimension = getPrimaryDimension(
669
0
                    addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
670
0
                tools::Long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
671
0
                nExtraSpace += (aReq.m_aMainGroupDimensions.size()-1) * nSpacing;
672
0
                nSpacing = nExtraSpace/(aReq.m_aMainGroupDimensions.size()+1);
673
0
                setPrimaryCoordinate(aMainGroupPos, nSpacing);
674
0
            }
675
0
            break;
676
0
        case VclButtonBoxStyle::Center:
677
0
            if (!aReq.m_aMainGroupDimensions.empty())
678
0
            {
679
0
                tools::Long nMainPrimaryDimension = getPrimaryDimension(
680
0
                    addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
681
0
                tools::Long nExtraSpace = nAllocPrimaryDimension - nMainPrimaryDimension;
682
0
                setPrimaryCoordinate(aMainGroupPos, nExtraSpace/2);
683
0
            }
684
0
            break;
685
0
        default:
686
0
            SAL_WARN("vcl.layout", "todo unimplemented layout style");
687
0
            [[fallthrough]];
688
0
        case VclButtonBoxStyle::Default:
689
0
        case VclButtonBoxStyle::End:
690
0
            if (!aReq.m_aMainGroupDimensions.empty())
691
0
            {
692
0
                tools::Long nMainPrimaryDimension = getPrimaryDimension(
693
0
                    addSpacing(aReq.m_aMainGroupSize, aReq.m_aMainGroupDimensions.size()));
694
0
                setPrimaryCoordinate(aMainGroupPos,
695
0
                    nAllocPrimaryDimension - nMainPrimaryDimension);
696
0
            }
697
0
            break;
698
0
    }
699
700
0
    Size aChildSize;
701
0
    setSecondaryDimension(aChildSize, getSecondaryDimension(rAllocation));
702
703
0
    std::vector<tools::Long>::const_iterator aPrimaryI = aReq.m_aMainGroupDimensions.begin();
704
0
    std::vector<tools::Long>::const_iterator aSecondaryI = aReq.m_aSubGroupDimensions.begin();
705
0
    bool bIgnoreSecondaryPacking = (m_eLayoutStyle == VclButtonBoxStyle::Spread || m_eLayoutStyle == VclButtonBoxStyle::Center);
706
0
    for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
707
0
    {
708
0
        if (!pChild->IsVisible())
709
0
            continue;
710
711
0
        if (bIgnoreSecondaryPacking || !pChild->get_secondary())
712
0
        {
713
0
            tools::Long nMainGroupPrimaryDimension = *aPrimaryI++;
714
0
            setPrimaryDimension(aChildSize, nMainGroupPrimaryDimension);
715
0
            setLayoutAllocation(*pChild, aMainGroupPos, aChildSize);
716
0
            tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aMainGroupPos);
717
0
            setPrimaryCoordinate(aMainGroupPos, nPrimaryCoordinate + nMainGroupPrimaryDimension + nSpacing);
718
0
        }
719
0
        else
720
0
        {
721
0
            tools::Long nSubGroupPrimaryDimension = *aSecondaryI++;
722
0
            setPrimaryDimension(aChildSize, nSubGroupPrimaryDimension);
723
0
            setLayoutAllocation(*pChild, aOtherGroupPos, aChildSize);
724
0
            tools::Long nPrimaryCoordinate = getPrimaryCoordinate(aOtherGroupPos);
725
0
            setPrimaryCoordinate(aOtherGroupPos, nPrimaryCoordinate + nSubGroupPrimaryDimension + nSpacing);
726
0
        }
727
0
    }
728
0
}
729
730
void VclButtonBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
731
0
{
732
0
    VclBox::DumpAsPropertyTree(rJsonWriter);
733
0
    rJsonWriter.put("type", "buttonbox");
734
735
0
    switch(m_eLayoutStyle)
736
0
    {
737
0
        case VclButtonBoxStyle::Default:
738
0
            rJsonWriter.put("layoutstyle", "default");
739
0
            break;
740
741
0
        case VclButtonBoxStyle::Spread:
742
0
            rJsonWriter.put("layoutstyle", "spread");
743
0
            break;
744
745
0
        case VclButtonBoxStyle::Edge:
746
0
            rJsonWriter.put("layoutstyle", "edge");
747
0
            break;
748
749
0
        case VclButtonBoxStyle::Center:
750
0
            rJsonWriter.put("layoutstyle", "center");
751
0
            break;
752
753
0
        case VclButtonBoxStyle::Start:
754
0
            rJsonWriter.put("layoutstyle", "start");
755
0
            break;
756
757
0
        case VclButtonBoxStyle::End:
758
0
            rJsonWriter.put("layoutstyle", "end");
759
0
            break;
760
0
    }
761
0
}
762
763
namespace {
764
765
struct ButtonOrder
766
{
767
    std::u16string_view m_aType;
768
    int m_nPriority;
769
};
770
771
}
772
773
static int getButtonPriority(std::u16string_view rType)
774
0
{
775
0
    static const size_t N_TYPES = 6;
776
0
    static const ButtonOrder aDiscardCancelSave[N_TYPES] =
777
0
    {
778
0
        { u"discard", 0 },
779
0
        { u"cancel", 1 },
780
0
        { u"no", 2 },
781
0
        { u"save", 3 },
782
0
        { u"yes", 3 },
783
0
        { u"ok", 3 }
784
0
    };
785
786
0
    static const ButtonOrder aSaveDiscardCancel[N_TYPES] =
787
0
    {
788
0
        { u"save", 0 },
789
0
        { u"yes", 0 },
790
0
        { u"ok", 0 },
791
0
        { u"discard", 1 },
792
0
        { u"no", 1 },
793
0
        { u"cancel", 2 }
794
0
    };
795
796
0
    const ButtonOrder* pOrder = &aDiscardCancelSave[0];
797
798
0
    const OUString &rEnv = Application::GetDesktopEnvironment();
799
800
0
    if (rEnv.equalsIgnoreAsciiCase("windows") ||
801
0
        rEnv.equalsIgnoreAsciiCase("lxqt") ||
802
0
        rEnv.startsWithIgnoreAsciiCase("plasma"))
803
0
    {
804
0
        pOrder = &aSaveDiscardCancel[0];
805
0
    }
806
807
0
    for (size_t i = 0; i < N_TYPES; ++i, ++pOrder)
808
0
    {
809
0
        if (rType == pOrder->m_aType)
810
0
            return pOrder->m_nPriority;
811
0
    }
812
813
0
    return -1;
814
0
}
815
816
namespace {
817
818
class sortButtons
819
{
820
    bool m_bVerticalContainer;
821
public:
822
    explicit sortButtons(bool bVerticalContainer)
823
0
        : m_bVerticalContainer(bVerticalContainer)
824
0
    {
825
0
    }
826
    bool operator()(const vcl::Window *pA, const vcl::Window *pB) const;
827
};
828
829
}
830
831
bool sortButtons::operator()(const vcl::Window *pA, const vcl::Window *pB) const
832
0
{
833
    //sort into two groups of pack start and pack end
834
0
    VclPackType ePackA = pA->get_pack_type();
835
0
    VclPackType ePackB = pB->get_pack_type();
836
0
    if (ePackA < ePackB)
837
0
        return true;
838
0
    if (ePackA > ePackB)
839
0
        return false;
840
0
    bool bPackA = pA->get_secondary();
841
0
    bool bPackB = pB->get_secondary();
842
0
    if (!m_bVerticalContainer)
843
0
    {
844
        //for horizontal boxes group secondaries before primaries
845
0
        if (bPackA > bPackB)
846
0
            return true;
847
0
        if (bPackA < bPackB)
848
0
            return false;
849
0
    }
850
0
    else
851
0
    {
852
        //for vertical boxes group secondaries after primaries
853
0
        if (bPackA < bPackB)
854
0
            return true;
855
0
        if (bPackA > bPackB)
856
0
            return false;
857
0
    }
858
859
    //now order within groups according to platform rules
860
0
    return getButtonPriority(pA->get_id()) < getButtonPriority(pB->get_id());
861
0
}
862
863
void sort_native_button_order(const VclBox& rContainer)
864
0
{
865
0
    std::vector<vcl::Window*> aChilds;
866
0
    for (vcl::Window* pChild = rContainer.GetWindow(GetWindowType::FirstChild); pChild;
867
0
        pChild = pChild->GetWindow(GetWindowType::Next))
868
0
    {
869
0
        aChilds.push_back(pChild);
870
0
    }
871
872
    //sort child order within parent so that we match the platform
873
    //button order
874
0
    std::stable_sort(aChilds.begin(), aChilds.end(), sortButtons(rContainer.get_orientation()));
875
0
    BuilderUtils::reorderWithinParent(aChilds, true);
876
0
}
877
878
namespace {
879
880
struct GridEntry
881
{
882
    VclPtr<vcl::Window> pChild;
883
    sal_Int32 nSpanWidth;
884
    sal_Int32 nSpanHeight;
885
    int x;
886
    int y;
887
    GridEntry()
888
0
        : pChild(nullptr)
889
0
        , nSpanWidth(0)
890
0
        , nSpanHeight(0)
891
0
        , x(-1)
892
0
        , y(-1)
893
0
    {
894
0
    }
895
};
896
897
}
898
899
typedef boost::multi_array<GridEntry, 2> array_type;
900
901
#if defined _MSC_VER
902
#pragma warning(push)
903
#pragma warning(disable : 4459)
904
#pragma warning(disable : 4996)
905
#endif
906
static array_type assembleGrid(const VclGrid &rGrid)
907
0
{
908
#if defined _MSC_VER
909
#pragma warning(pop)
910
#endif
911
0
    array_type A;
912
913
0
    for (vcl::Window* pChild = rGrid.GetWindow(GetWindowType::FirstChild); pChild;
914
0
        pChild = pChild->GetWindow(GetWindowType::Next))
915
0
    {
916
0
        sal_Int32 nLeftAttach = std::max<sal_Int32>(pChild->get_grid_left_attach(), 0);
917
0
        sal_Int32 nWidth = pChild->get_grid_width();
918
0
        sal_Int32 nMaxXPos = nLeftAttach+nWidth-1;
919
920
0
        sal_Int32 nTopAttach = std::max<sal_Int32>(pChild->get_grid_top_attach(), 0);
921
0
        sal_Int32 nHeight = pChild->get_grid_height();
922
0
        sal_Int32 nMaxYPos = nTopAttach+nHeight-1;
923
924
0
        sal_Int32 nCurrentMaxXPos = A.shape()[0]-1;
925
0
        sal_Int32 nCurrentMaxYPos = A.shape()[1]-1;
926
0
        if (nMaxXPos > nCurrentMaxXPos || nMaxYPos > nCurrentMaxYPos)
927
0
        {
928
0
            nCurrentMaxXPos = std::max(nMaxXPos, nCurrentMaxXPos);
929
0
            nCurrentMaxYPos = std::max(nMaxYPos, nCurrentMaxYPos);
930
0
            A.resize(boost::extents[nCurrentMaxXPos+1][nCurrentMaxYPos+1]);
931
0
        }
932
933
#if defined _MSC_VER
934
#pragma warning(push)
935
#pragma warning(disable : 4459)
936
#pragma warning(disable : 4996)
937
#endif
938
0
        GridEntry &rEntry = A[nLeftAttach][nTopAttach];
939
#if defined _MSC_VER
940
#pragma warning(pop)
941
#endif
942
0
        rEntry.pChild = pChild;
943
0
        rEntry.nSpanWidth = nWidth;
944
0
        rEntry.nSpanHeight = nHeight;
945
0
        rEntry.x = nLeftAttach;
946
0
        rEntry.y = nTopAttach;
947
948
0
        for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
949
0
        {
950
0
            for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
951
0
            {
952
0
                GridEntry &rSpan = A[nLeftAttach+nSpanX][nTopAttach+nSpanY];
953
0
                rSpan.x = nLeftAttach;
954
0
                rSpan.y = nTopAttach;
955
0
            }
956
0
        }
957
0
    }
958
959
    //see if we have any empty rows/cols
960
0
    sal_Int32 nMaxX = A.shape()[0];
961
0
    sal_Int32 nMaxY = A.shape()[1];
962
963
0
    std::vector<bool> aNonEmptyCols(nMaxX);
964
0
    std::vector<bool> aNonEmptyRows(nMaxY);
965
966
0
    for (sal_Int32 x = 0; x < nMaxX; ++x)
967
0
    {
968
0
        for (sal_Int32 y = 0; y < nMaxY; ++y)
969
0
        {
970
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
971
#pragma GCC diagnostic push
972
#pragma GCC diagnostic ignored "-Wdangling-reference"
973
#endif
974
0
            const GridEntry &rEntry = A[x][y];
975
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
976
#pragma GCC diagnostic pop
977
#endif
978
0
            const vcl::Window *pChild = rEntry.pChild;
979
0
            if (pChild && pChild->IsVisible())
980
0
            {
981
0
                aNonEmptyCols[x] = true;
982
0
                if (rGrid.get_column_homogeneous())
983
0
                {
984
0
                    for (sal_Int32 nSpanX = 1; nSpanX < rEntry.nSpanWidth; ++nSpanX)
985
0
                        aNonEmptyCols[x+nSpanX] = true;
986
0
                }
987
0
                aNonEmptyRows[y] = true;
988
0
                if (rGrid.get_row_homogeneous())
989
0
                {
990
0
                    for (sal_Int32 nSpanY = 1; nSpanY < rEntry.nSpanHeight; ++nSpanY)
991
0
                        aNonEmptyRows[y+nSpanY] = true;
992
0
                }
993
0
            }
994
0
        }
995
0
    }
996
997
0
    if (!rGrid.get_column_homogeneous())
998
0
    {
999
        //reduce the spans of elements that span empty columns
1000
0
        for (sal_Int32 x = 0; x < nMaxX; ++x)
1001
0
        {
1002
0
            std::set<GridEntry*> candidates;
1003
0
            for (sal_Int32 y = 0; y < nMaxY; ++y)
1004
0
            {
1005
0
                if (aNonEmptyCols[x])
1006
0
                    continue;
1007
0
                GridEntry &rSpan = A[x][y];
1008
                //cell x/y is spanned by the widget at cell rSpan.x/rSpan.y,
1009
                //just points back to itself if there's no cell spanning
1010
0
                if ((rSpan.x == -1) || (rSpan.y == -1))
1011
0
                {
1012
                    //there is no entry for this cell, i.e. this is a cell
1013
                    //with no widget in it, or spanned by any other widget
1014
0
                    continue;
1015
0
                }
1016
0
                GridEntry &rEntry = A[rSpan.x][rSpan.y];
1017
0
                candidates.insert(&rEntry);
1018
0
            }
1019
0
            for (auto const& candidate : candidates)
1020
0
            {
1021
0
                GridEntry *pEntry = candidate;
1022
0
                --pEntry->nSpanWidth;
1023
0
            }
1024
0
        }
1025
0
    }
1026
1027
0
    if (!rGrid.get_row_homogeneous())
1028
0
    {
1029
        //reduce the spans of elements that span empty rows
1030
0
        for (sal_Int32 y = 0; y < nMaxY; ++y)
1031
0
        {
1032
0
            std::set<GridEntry*> candidates;
1033
0
            for (sal_Int32 x = 0; x < nMaxX; ++x)
1034
0
            {
1035
0
                if (aNonEmptyRows[y])
1036
0
                    continue;
1037
0
                GridEntry &rSpan = A[x][y];
1038
                //cell x/y is spanned by the widget at cell rSpan.x/rSpan.y,
1039
                //just points back to itself if there's no cell spanning
1040
0
                if ((rSpan.x == -1) || (rSpan.y == -1))
1041
0
                {
1042
                    //there is no entry for this cell, i.e. this is a cell
1043
                    //with no widget in it, or spanned by any other widget
1044
0
                    continue;
1045
0
                }
1046
0
                GridEntry &rEntry = A[rSpan.x][rSpan.y];
1047
0
                candidates.insert(&rEntry);
1048
0
            }
1049
0
            for (auto const& candidate : candidates)
1050
0
            {
1051
0
                GridEntry *pEntry = candidate;
1052
0
                --pEntry->nSpanHeight;
1053
0
            }
1054
0
        }
1055
0
    }
1056
1057
0
    sal_Int32 nNonEmptyCols = std::count(aNonEmptyCols.begin(), aNonEmptyCols.end(), true);
1058
0
    sal_Int32 nNonEmptyRows = std::count(aNonEmptyRows.begin(), aNonEmptyRows.end(), true);
1059
1060
    //make new grid without empty rows and columns
1061
#if defined _MSC_VER
1062
#pragma warning(push)
1063
#pragma warning(disable : 4459)
1064
#pragma warning(disable : 4996)
1065
#endif
1066
0
    array_type B(boost::extents[nNonEmptyCols][nNonEmptyRows]);
1067
#if defined _MSC_VER
1068
#pragma warning(pop)
1069
#endif
1070
0
    for (sal_Int32 x = 0, x2 = 0; x < nMaxX; ++x)
1071
0
    {
1072
0
        if (!aNonEmptyCols[x])
1073
0
            continue;
1074
0
        for (sal_Int32 y = 0, y2 = 0; y < nMaxY; ++y)
1075
0
        {
1076
0
            if (!aNonEmptyRows[y])
1077
0
                continue;
1078
0
            GridEntry &rEntry = A[x][y];
1079
0
            B[x2][y2++] = rEntry;
1080
0
        }
1081
0
        ++x2;
1082
0
    }
1083
1084
0
    return B;
1085
0
}
1086
1087
static bool isNullGrid(const array_type &A)
1088
0
{
1089
0
    sal_Int32 nMaxX = A.shape()[0];
1090
0
    sal_Int32 nMaxY = A.shape()[1];
1091
1092
0
    return !nMaxX || !nMaxY;
1093
0
}
1094
1095
static void calcMaxs(const array_type &A, std::vector<VclGrid::Value> &rWidths, std::vector<VclGrid::Value> &rHeights)
1096
0
{
1097
0
    sal_Int32 nMaxX = A.shape()[0];
1098
0
    sal_Int32 nMaxY = A.shape()[1];
1099
1100
0
    rWidths.resize(nMaxX);
1101
0
    rHeights.resize(nMaxY);
1102
1103
    //first use the non spanning entries to set default width/heights
1104
0
    for (sal_Int32 x = 0; x < nMaxX; ++x)
1105
0
    {
1106
0
        for (sal_Int32 y = 0; y < nMaxY; ++y)
1107
0
        {
1108
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
1109
#pragma GCC diagnostic push
1110
#pragma GCC diagnostic ignored "-Wdangling-reference"
1111
#elif defined _MSC_VER
1112
#pragma warning(push)
1113
#pragma warning(disable : 4459)
1114
#pragma warning(disable : 4996)
1115
#endif
1116
0
            const GridEntry &rEntry = A[x][y];
1117
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
1118
#pragma GCC diagnostic pop
1119
#elif defined _MSC_VER
1120
#pragma warning(pop)
1121
#endif
1122
0
            const vcl::Window *pChild = rEntry.pChild;
1123
0
            if (!pChild || !pChild->IsVisible())
1124
0
                continue;
1125
1126
0
            sal_Int32 nWidth = rEntry.nSpanWidth;
1127
0
            sal_Int32 nHeight = rEntry.nSpanHeight;
1128
1129
0
            for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
1130
0
                rWidths[x+nSpanX].m_bExpand |= pChild->get_hexpand();
1131
1132
0
            for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
1133
0
                rHeights[y+nSpanY].m_bExpand |= pChild->get_vexpand();
1134
1135
0
            if (nWidth == 1 || nHeight == 1)
1136
0
            {
1137
0
                Size aChildSize = VclContainer::getLayoutRequisition(*pChild);
1138
0
                if (nWidth == 1)
1139
0
                    rWidths[x].m_nValue = std::max(rWidths[x].m_nValue, aChildSize.Width());
1140
0
                if (nHeight == 1)
1141
0
                    rHeights[y].m_nValue = std::max(rHeights[y].m_nValue, aChildSize.Height());
1142
0
            }
1143
0
        }
1144
0
    }
1145
1146
    //now use the spanning entries and split any extra sizes across expanding rows/cols
1147
    //where possible
1148
0
    for (sal_Int32 x = 0; x < nMaxX; ++x)
1149
0
    {
1150
0
        for (sal_Int32 y = 0; y < nMaxY; ++y)
1151
0
        {
1152
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
1153
#pragma GCC diagnostic push
1154
#pragma GCC diagnostic ignored "-Wdangling-reference"
1155
#endif
1156
0
            const GridEntry &rEntry = A[x][y];
1157
#if defined __GNUC__ && !defined __clang__ && __GNUC__ >= 13 && __GNUC__ <= 16
1158
#pragma GCC diagnostic pop
1159
#endif
1160
0
            const vcl::Window *pChild = rEntry.pChild;
1161
0
            if (!pChild || !pChild->IsVisible())
1162
0
                continue;
1163
1164
0
            sal_Int32 nWidth = rEntry.nSpanWidth;
1165
0
            sal_Int32 nHeight = rEntry.nSpanHeight;
1166
1167
0
            if (nWidth == 1 && nHeight == 1)
1168
0
                continue;
1169
1170
0
            Size aChildSize = VclContainer::getLayoutRequisition(*pChild);
1171
1172
0
            if (nWidth > 1)
1173
0
            {
1174
0
                sal_Int32 nExistingWidth = 0;
1175
0
                for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
1176
0
                    nExistingWidth += rWidths[x+nSpanX].m_nValue;
1177
1178
0
                sal_Int32 nExtraWidth = aChildSize.Width() - nExistingWidth;
1179
1180
0
                if (nExtraWidth > 0)
1181
0
                {
1182
0
                    bool bForceExpandAll = false;
1183
0
                    sal_Int32 nExpandables = 0;
1184
0
                    for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
1185
0
                        if (rWidths[x+nSpanX].m_bExpand)
1186
0
                            ++nExpandables;
1187
0
                    if (nExpandables == 0)
1188
0
                    {
1189
0
                        nExpandables = nWidth;
1190
0
                        bForceExpandAll = true;
1191
0
                    }
1192
1193
0
                    for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
1194
0
                    {
1195
0
                        if (rWidths[x+nSpanX].m_bExpand || bForceExpandAll)
1196
0
                            rWidths[x+nSpanX].m_nValue += nExtraWidth/nExpandables;
1197
0
                    }
1198
0
                }
1199
0
            }
1200
1201
0
            if (nHeight > 1)
1202
0
            {
1203
0
                sal_Int32 nExistingHeight = 0;
1204
0
                for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
1205
0
                    nExistingHeight += rHeights[y+nSpanY].m_nValue;
1206
1207
0
                sal_Int32 nExtraHeight = aChildSize.Height() - nExistingHeight;
1208
1209
0
                if (nExtraHeight > 0)
1210
0
                {
1211
0
                    bool bForceExpandAll = false;
1212
0
                    sal_Int32 nExpandables = 0;
1213
0
                    for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
1214
0
                        if (rHeights[y+nSpanY].m_bExpand)
1215
0
                            ++nExpandables;
1216
0
                    if (nExpandables == 0)
1217
0
                    {
1218
0
                        nExpandables = nHeight;
1219
0
                        bForceExpandAll = true;
1220
0
                    }
1221
1222
0
                    for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
1223
0
                    {
1224
0
                        if (rHeights[y+nSpanY].m_bExpand || bForceExpandAll)
1225
0
                            rHeights[y+nSpanY].m_nValue += nExtraHeight/nExpandables;
1226
0
                    }
1227
0
                }
1228
0
            }
1229
0
        }
1230
0
    }
1231
0
}
1232
1233
static bool compareValues(const VclGrid::Value &i, const VclGrid::Value &j)
1234
0
{
1235
0
    return i.m_nValue < j.m_nValue;
1236
0
}
1237
1238
static VclGrid::Value accumulateValues(const VclGrid::Value &i, const VclGrid::Value &j)
1239
0
{
1240
0
    VclGrid::Value aRet;
1241
0
    aRet.m_nValue = i.m_nValue + j.m_nValue;
1242
0
    aRet.m_bExpand = i.m_bExpand || j.m_bExpand;
1243
0
    return aRet;
1244
0
}
1245
1246
Size VclGrid::calculateRequisition() const
1247
0
{
1248
0
    return calculateRequisitionForSpacings(get_row_spacing(), get_column_spacing());
1249
0
}
1250
1251
Size VclGrid::calculateRequisitionForSpacings(sal_Int32 nRowSpacing, sal_Int32 nColSpacing) const
1252
0
{
1253
0
    array_type A = assembleGrid(*this);
1254
1255
0
    if (isNullGrid(A))
1256
0
        return Size();
1257
1258
0
    std::vector<Value> aWidths;
1259
0
    std::vector<Value> aHeights;
1260
0
    calcMaxs(A, aWidths, aHeights);
1261
1262
0
    tools::Long nTotalWidth = 0;
1263
0
    if (get_column_homogeneous())
1264
0
    {
1265
0
        nTotalWidth = std::max_element(aWidths.begin(), aWidths.end(), compareValues)->m_nValue;
1266
0
        nTotalWidth *= aWidths.size();
1267
0
    }
1268
0
    else
1269
0
    {
1270
0
        nTotalWidth = std::accumulate(aWidths.begin(), aWidths.end(), Value(), accumulateValues).m_nValue;
1271
0
    }
1272
1273
0
    nTotalWidth += nColSpacing * (aWidths.size()-1);
1274
1275
0
    tools::Long nTotalHeight = 0;
1276
0
    if (get_row_homogeneous())
1277
0
    {
1278
0
        nTotalHeight = std::max_element(aHeights.begin(), aHeights.end(), compareValues)->m_nValue;
1279
0
        nTotalHeight *= aHeights.size();
1280
0
    }
1281
0
    else
1282
0
    {
1283
0
        nTotalHeight = std::accumulate(aHeights.begin(), aHeights.end(), Value(), accumulateValues).m_nValue;
1284
0
    }
1285
1286
0
    nTotalHeight += nRowSpacing * (aHeights.size()-1);
1287
1288
0
    return Size(nTotalWidth, nTotalHeight);
1289
0
}
1290
1291
void VclGrid::setAllocation(const Size& rAllocation)
1292
0
{
1293
0
    array_type A = assembleGrid(*this);
1294
1295
0
    if (isNullGrid(A))
1296
0
        return;
1297
1298
0
    sal_Int32 nMaxX = A.shape()[0];
1299
0
    sal_Int32 nMaxY = A.shape()[1];
1300
1301
0
    Size aRequisition;
1302
0
    std::vector<Value> aWidths(nMaxX);
1303
0
    std::vector<Value> aHeights(nMaxY);
1304
0
    if (!get_column_homogeneous() || !get_row_homogeneous())
1305
0
    {
1306
0
        aRequisition = calculateRequisition();
1307
0
        calcMaxs(A, aWidths, aHeights);
1308
0
    }
1309
1310
0
    sal_Int32 nColSpacing(get_column_spacing());
1311
0
    sal_Int32 nRowSpacing(get_row_spacing());
1312
1313
0
    tools::Long nAvailableWidth = rAllocation.Width();
1314
0
    if (nMaxX)
1315
0
        nAvailableWidth -= nColSpacing * (nMaxX - 1);
1316
0
    if (get_column_homogeneous())
1317
0
    {
1318
0
        for (sal_Int32 x = 0; x < nMaxX; ++x)
1319
0
            aWidths[x].m_nValue = nAvailableWidth/nMaxX;
1320
0
    }
1321
0
    else if (rAllocation.Width() != aRequisition.Width())
1322
0
    {
1323
0
        sal_Int32 nExpandables = 0;
1324
0
        for (sal_Int32 x = 0; x < nMaxX; ++x)
1325
0
            if (aWidths[x].m_bExpand)
1326
0
                ++nExpandables;
1327
0
        tools::Long nExtraWidthForExpanders = nExpandables ? (rAllocation.Width() - aRequisition.Width()) / nExpandables : 0;
1328
1329
        //We don't fit and there is no volunteer to be shrunk
1330
0
        if (!nExpandables && rAllocation.Width() < aRequisition.Width())
1331
0
        {
1332
            //first reduce spacing
1333
0
            while (nColSpacing)
1334
0
            {
1335
0
                nColSpacing /= 2;
1336
0
                aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing);
1337
0
                if (aRequisition.Width() <= rAllocation.Width())
1338
0
                    break;
1339
0
            }
1340
1341
            //share out the remaining pain to everyone
1342
0
            tools::Long nExtraWidth = (rAllocation.Width() - aRequisition.Width()) / nMaxX;
1343
1344
0
            for (sal_Int32 x = 0; x < nMaxX; ++x)
1345
0
                aWidths[x].m_nValue += nExtraWidth;
1346
0
        }
1347
1348
0
        if (nExtraWidthForExpanders)
1349
0
        {
1350
0
            for (sal_Int32 x = 0; x < nMaxX; ++x)
1351
0
                if (aWidths[x].m_bExpand)
1352
0
                    aWidths[x].m_nValue += nExtraWidthForExpanders;
1353
0
        }
1354
0
    }
1355
1356
0
    tools::Long nAvailableHeight = rAllocation.Height();
1357
0
    if (nMaxY)
1358
0
        nAvailableHeight -= nRowSpacing * (nMaxY - 1);
1359
0
    if (get_row_homogeneous())
1360
0
    {
1361
0
        for (sal_Int32 y = 0; y < nMaxY; ++y)
1362
0
            aHeights[y].m_nValue = nAvailableHeight/nMaxY;
1363
0
    }
1364
0
    else if (rAllocation.Height() != aRequisition.Height())
1365
0
    {
1366
0
        sal_Int32 nExpandables = 0;
1367
0
        for (sal_Int32 y = 0; y < nMaxY; ++y)
1368
0
            if (aHeights[y].m_bExpand)
1369
0
                ++nExpandables;
1370
0
        tools::Long nExtraHeightForExpanders = nExpandables ? (rAllocation.Height() - aRequisition.Height()) / nExpandables : 0;
1371
1372
        //We don't fit and there is no volunteer to be shrunk
1373
0
        if (!nExpandables && rAllocation.Height() < aRequisition.Height())
1374
0
        {
1375
            //first reduce spacing
1376
0
            while (nRowSpacing)
1377
0
            {
1378
0
                nRowSpacing /= 2;
1379
0
                aRequisition = calculateRequisitionForSpacings(nRowSpacing, nColSpacing);
1380
0
                if (aRequisition.Height() <= rAllocation.Height())
1381
0
                    break;
1382
0
            }
1383
1384
            //share out the remaining pain to everyone
1385
0
            tools::Long nExtraHeight = (rAllocation.Height() - aRequisition.Height()) / nMaxY;
1386
1387
0
            for (sal_Int32 y = 0; y < nMaxY; ++y)
1388
0
                aHeights[y].m_nValue += nExtraHeight;
1389
0
        }
1390
1391
0
        if (nExtraHeightForExpanders)
1392
0
        {
1393
0
            for (sal_Int32 y = 0; y < nMaxY; ++y)
1394
0
                if (aHeights[y].m_bExpand)
1395
0
                    aHeights[y].m_nValue += nExtraHeightForExpanders;
1396
0
        }
1397
0
    }
1398
1399
0
    Point aAllocPos(0, 0);
1400
0
    for (sal_Int32 x = 0; x < nMaxX; ++x)
1401
0
    {
1402
0
        for (sal_Int32 y = 0; y < nMaxY; ++y)
1403
0
        {
1404
#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
1405
#pragma GCC diagnostic push
1406
#pragma GCC diagnostic ignored "-Wdangling-reference"
1407
#endif
1408
0
            GridEntry &rEntry = A[x][y];
1409
#if defined __GNUC__ && !defined __clang__ && __GNUC__ == 13
1410
#pragma GCC diagnostic pop
1411
#endif
1412
0
            vcl::Window *pChild = rEntry.pChild;
1413
0
            if (pChild)
1414
0
            {
1415
0
                Size aChildAlloc(0, 0);
1416
1417
0
                sal_Int32 nWidth = rEntry.nSpanWidth;
1418
0
                for (sal_Int32 nSpanX = 0; nSpanX < nWidth; ++nSpanX)
1419
0
                    aChildAlloc.AdjustWidth(aWidths[x+nSpanX].m_nValue );
1420
0
                aChildAlloc.AdjustWidth(nColSpacing*(nWidth-1) );
1421
1422
0
                sal_Int32 nHeight = rEntry.nSpanHeight;
1423
0
                for (sal_Int32 nSpanY = 0; nSpanY < nHeight; ++nSpanY)
1424
0
                    aChildAlloc.AdjustHeight(aHeights[y+nSpanY].m_nValue );
1425
0
                aChildAlloc.AdjustHeight(nRowSpacing*(nHeight-1) );
1426
1427
0
                setLayoutAllocation(*pChild, aAllocPos, aChildAlloc);
1428
0
            }
1429
0
            aAllocPos.AdjustY(aHeights[y].m_nValue + nRowSpacing );
1430
0
        }
1431
0
        aAllocPos.AdjustX(aWidths[x].m_nValue + nColSpacing );
1432
0
        aAllocPos.setY( 0 );
1433
0
    }
1434
0
}
1435
1436
void VclGrid::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1437
0
{
1438
0
    VclContainer::DumpAsPropertyTree(rJsonWriter);
1439
0
    rJsonWriter.put("type", "grid");
1440
0
}
1441
1442
bool VclGrid::set_property(const OUString &rKey, const OUString &rValue)
1443
0
{
1444
0
    if (rKey == "row-spacing")
1445
0
        set_row_spacing(rValue.toInt32());
1446
0
    else if (rKey == "column-spacing")
1447
0
        set_column_spacing(rValue.toInt32());
1448
0
    else if (rKey == "row-homogeneous")
1449
0
        m_bRowHomogeneous = toBool(rValue);
1450
0
    else if (rKey == "column-homogeneous")
1451
0
        m_bColumnHomogeneous = toBool(rValue);
1452
0
    else if (rKey == "n-rows")
1453
0
        /*nothing to do*/;
1454
0
    else
1455
0
        return VclContainer::set_property(rKey, rValue);
1456
0
    return true;
1457
0
}
1458
1459
const vcl::Window *VclBin::get_child() const
1460
0
{
1461
0
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();
1462
1463
0
    return pWindowImpl->mpFirstChild;
1464
0
}
1465
1466
vcl::Window *VclBin::get_child()
1467
0
{
1468
0
    return const_cast<vcl::Window*>(std::as_const(*this).get_child());
1469
0
}
1470
1471
Size VclBin::calculateRequisition() const
1472
0
{
1473
0
    const vcl::Window *pChild = get_child();
1474
0
    if (pChild && pChild->IsVisible())
1475
0
        return getLayoutRequisition(*pChild);
1476
0
    return Size(0, 0);
1477
0
}
1478
1479
void VclBin::setAllocation(const Size &rAllocation)
1480
0
{
1481
0
    vcl::Window *pChild = get_child();
1482
0
    if (pChild && pChild->IsVisible())
1483
0
        setLayoutAllocation(*pChild, Point(0, 0), rAllocation);
1484
0
}
1485
1486
VclFrame::~VclFrame()
1487
0
{
1488
0
    disposeOnce();
1489
0
}
1490
1491
void VclFrame::dispose()
1492
0
{
1493
0
    m_pLabel.reset();
1494
0
    VclBin::dispose();
1495
0
}
1496
1497
//To-Do, hook a DecorationView into VclFrame ?
1498
1499
Size VclFrame::calculateRequisition() const
1500
0
{
1501
0
    Size aRet(0, 0);
1502
1503
0
    const vcl::Window *pChild = get_child();
1504
0
    const vcl::Window *pLabel = get_label_widget();
1505
1506
0
    if (pChild && pChild->IsVisible())
1507
0
        aRet = getLayoutRequisition(*pChild);
1508
1509
0
    if (pLabel && pLabel->IsVisible())
1510
0
    {
1511
0
        Size aLabelSize = getLayoutRequisition(*pLabel);
1512
0
        aRet.AdjustHeight(aLabelSize.Height() );
1513
0
        aRet.setWidth( std::max(aLabelSize.Width(), aRet.Width()) );
1514
0
    }
1515
1516
0
    return aRet;
1517
0
}
1518
1519
void VclFrame::setAllocation(const Size &rAllocation)
1520
0
{
1521
    //SetBackground( Color(0xFF, 0x00, 0xFF) );
1522
1523
0
    Size aAllocation(rAllocation);
1524
0
    Point aChildPos;
1525
1526
0
    vcl::Window *pChild = get_child();
1527
0
    vcl::Window *pLabel = get_label_widget();
1528
1529
0
    if (pLabel && pLabel->IsVisible())
1530
0
    {
1531
0
        Size aLabelSize = getLayoutRequisition(*pLabel);
1532
0
        aLabelSize.setHeight( std::min(aLabelSize.Height(), aAllocation.Height()) );
1533
0
        aLabelSize.setWidth( std::min(aLabelSize.Width(), aAllocation.Width()) );
1534
0
        setLayoutAllocation(*pLabel, aChildPos, aLabelSize);
1535
0
        aAllocation.AdjustHeight( -(aLabelSize.Height()) );
1536
0
        aChildPos.AdjustY(aLabelSize.Height() );
1537
0
    }
1538
1539
0
    if (pChild && pChild->IsVisible())
1540
0
        setLayoutAllocation(*pChild, aChildPos, aAllocation);
1541
0
}
1542
1543
IMPL_LINK(VclFrame, WindowEventListener, VclWindowEvent&, rEvent, void)
1544
0
{
1545
0
    if (rEvent.GetId() == VclEventId::ObjectDying)
1546
0
        designate_label(nullptr);
1547
0
}
1548
1549
void VclFrame::designate_label(vcl::Window *pWindow)
1550
0
{
1551
0
    assert(!pWindow || pWindow->GetParent() == this);
1552
0
    if (m_pLabel)
1553
0
        m_pLabel->RemoveEventListener(LINK(this, VclFrame, WindowEventListener));
1554
0
    m_pLabel = pWindow;
1555
0
    if (m_pLabel)
1556
0
        m_pLabel->AddEventListener(LINK(this, VclFrame, WindowEventListener));
1557
0
}
1558
1559
const vcl::Window *VclFrame::get_label_widget() const
1560
0
{
1561
0
    if (m_pLabel)
1562
0
        return m_pLabel;
1563
0
    assert(GetChildCount() <= 2);
1564
    //The label widget is normally the first (of two) children
1565
0
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();
1566
0
    if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //no label exists
1567
0
        return nullptr;
1568
0
    return pWindowImpl->mpFirstChild;
1569
0
}
1570
1571
vcl::Window *VclFrame::get_label_widget()
1572
0
{
1573
0
    return const_cast<vcl::Window*>(std::as_const(*this).get_label_widget());
1574
0
}
1575
1576
const vcl::Window *VclFrame::get_child() const
1577
0
{
1578
    //The child widget is the normally the last (of two) children
1579
0
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();
1580
0
    assert(GetChildCount() == 2 || pWindowImpl->mbInDispose);
1581
0
    if (!m_pLabel)
1582
0
        return pWindowImpl->mpLastChild;
1583
0
    if (pWindowImpl->mpFirstChild == pWindowImpl->mpLastChild) //only label exists
1584
0
        return nullptr;
1585
0
    return pWindowImpl->mpLastChild;
1586
0
}
1587
1588
vcl::Window *VclFrame::get_child()
1589
0
{
1590
0
    return const_cast<vcl::Window*>(std::as_const(*this).get_child());
1591
0
}
1592
1593
void VclFrame::set_label(const OUString &rLabel)
1594
0
{
1595
0
    vcl::Window *pLabel = get_label_widget();
1596
0
    assert(pLabel);
1597
0
    pLabel->SetText(rLabel);
1598
0
}
1599
1600
OUString VclFrame::get_label() const
1601
0
{
1602
0
    const vcl::Window *pLabel = get_label_widget();
1603
0
    assert(pLabel);
1604
0
    return pLabel->GetText();
1605
0
}
1606
1607
OUString VclFrame::getDefaultAccessibleName() const
1608
0
{
1609
0
    const vcl::Window *pLabel = get_label_widget();
1610
0
    if (pLabel)
1611
0
        return pLabel->GetAccessibleName();
1612
0
    return VclBin::getDefaultAccessibleName();
1613
0
}
1614
1615
void VclFrame::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1616
0
{
1617
0
    VclBin::DumpAsPropertyTree(rJsonWriter);
1618
0
    rJsonWriter.put("type", "frame");
1619
0
}
1620
1621
class DisclosureButton final : public CheckBox
1622
{
1623
    virtual void ImplDrawCheckBoxState(vcl::RenderContext& rRenderContext) override
1624
0
    {
1625
        /* HACK: DisclosureButton is currently assuming, that the disclosure sign
1626
           will fit into the rectangle occupied by a normal checkbox on all themes.
1627
           If this does not hold true for some theme, ImplGetCheckImageSize
1628
           would have to be overridden for DisclosureButton; also GetNativeControlRegion
1629
           for ControlType::ListNode would have to be implemented and taken into account
1630
        */
1631
1632
0
        tools::Rectangle aStateRect(GetStateRect());
1633
1634
0
        ImplControlValue aControlValue(GetState() == TRISTATE_TRUE ? ButtonValue::On : ButtonValue::Off);
1635
0
        tools::Rectangle aCtrlRegion(aStateRect);
1636
0
        ControlState nState = ControlState::NONE;
1637
1638
0
        if (HasFocus())
1639
0
            nState |= ControlState::FOCUSED;
1640
0
        if (GetButtonState() & DrawButtonFlags::Default)
1641
0
            nState |= ControlState::DEFAULT;
1642
0
        if (Window::IsEnabled())
1643
0
            nState |= ControlState::ENABLED;
1644
0
        if (IsMouseOver() && GetMouseRect().Contains(GetPointerPosPixel()))
1645
0
            nState |= ControlState::ROLLOVER;
1646
1647
0
        if (rRenderContext.DrawNativeControl(ControlType::ListNode, ControlPart::Entire, aCtrlRegion,
1648
0
                                              nState, aControlValue, OUString()))
1649
0
            return;
1650
1651
0
        ImplSVCtrlData& rCtrlData(ImplGetSVData()->maCtrlData);
1652
0
        if (!rCtrlData.moDisclosurePlus)
1653
0
            rCtrlData.moDisclosurePlus.emplace(StockImage::Yes, SV_DISCLOSURE_PLUS);
1654
0
        if (!rCtrlData.moDisclosureMinus)
1655
0
            rCtrlData.moDisclosureMinus.emplace(StockImage::Yes, SV_DISCLOSURE_MINUS);
1656
1657
0
        Image* pImg
1658
0
            = IsChecked() ? &*rCtrlData.moDisclosureMinus : &*rCtrlData.moDisclosurePlus;
1659
1660
0
        DrawImageFlags nStyle = DrawImageFlags::NONE;
1661
0
        if (!IsEnabled())
1662
0
            nStyle |= DrawImageFlags::Disable;
1663
1664
0
        Size aSize(aStateRect.GetSize());
1665
0
        Size aImgSize(pImg->GetSizePixel());
1666
0
        Point aOff((aSize.Width() - aImgSize.Width()) / 2,
1667
0
                   (aSize.Height() - aImgSize.Height()) / 2);
1668
0
        aOff += aStateRect.TopLeft();
1669
0
        rRenderContext.DrawImage(aOff, *pImg, nStyle);
1670
0
    }
1671
1672
public:
1673
    explicit DisclosureButton(vcl::Window* pParent)
1674
0
        : CheckBox(pParent, 0)
1675
0
    {
1676
0
    }
1677
1678
    virtual void KeyInput( const KeyEvent& rKEvt ) override
1679
0
    {
1680
0
        vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
1681
1682
0
        if( !aKeyCode.GetModifier()  &&
1683
0
            ( ( aKeyCode.GetCode() == KEY_ADD ) ||
1684
0
              ( aKeyCode.GetCode() == KEY_SUBTRACT ) )
1685
0
            )
1686
0
        {
1687
0
            Check( aKeyCode.GetCode() == KEY_ADD );
1688
0
        }
1689
0
        else
1690
0
            CheckBox::KeyInput( rKEvt );
1691
0
    }
1692
};
1693
1694
VclExpander::VclExpander(vcl::Window *pParent)
1695
0
    : VclBin(pParent)
1696
0
    , m_bResizeTopLevel(false)
1697
0
    , m_pDisclosureButton(VclPtr<DisclosureButton>::Create(this))
1698
0
{
1699
0
    m_pDisclosureButton->SetToggleHdl(LINK(this, VclExpander, ClickHdl));
1700
0
    m_pDisclosureButton->Show();
1701
0
}
Unexecuted instantiation: VclExpander::VclExpander(vcl::Window*)
Unexecuted instantiation: VclExpander::VclExpander(vcl::Window*)
1702
1703
VclExpander::~VclExpander()
1704
0
{
1705
0
    disposeOnce();
1706
0
}
1707
1708
bool VclExpander::get_expanded() const
1709
0
{
1710
0
    return m_pDisclosureButton->IsChecked();
1711
0
}
1712
1713
void VclExpander::set_expanded(bool bExpanded)
1714
0
{
1715
0
    m_pDisclosureButton->Check(bExpanded);
1716
0
}
1717
1718
void VclExpander::set_label(const OUString& rLabel)
1719
0
{
1720
0
    m_pDisclosureButton->SetText(rLabel);
1721
0
}
1722
1723
OUString VclExpander::get_label() const
1724
0
{
1725
0
    return m_pDisclosureButton->GetText();
1726
0
}
1727
1728
void VclExpander::dispose()
1729
0
{
1730
0
    m_pDisclosureButton.disposeAndClear();
1731
0
    VclBin::dispose();
1732
0
}
1733
1734
const vcl::Window *VclExpander::get_child() const
1735
0
{
1736
0
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();
1737
1738
0
    assert(pWindowImpl->mpFirstChild == m_pDisclosureButton);
1739
1740
0
    return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next);
1741
0
}
1742
1743
vcl::Window *VclExpander::get_child()
1744
0
{
1745
0
    return const_cast<vcl::Window*>(std::as_const(*this).get_child());
1746
0
}
1747
1748
Size VclExpander::calculateRequisition() const
1749
0
{
1750
0
    Size aRet(0, 0);
1751
1752
0
    WindowImpl* pWindowImpl = ImplGetWindowImpl();
1753
1754
0
    const vcl::Window *pChild = get_child();
1755
0
    const vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild ? pWindowImpl->mpLastChild.get() : nullptr;
1756
1757
0
    if (pChild && pChild->IsVisible() && m_pDisclosureButton->IsChecked())
1758
0
        aRet = getLayoutRequisition(*pChild);
1759
1760
0
    Size aExpanderSize = getLayoutRequisition(*m_pDisclosureButton);
1761
1762
0
    if (pLabel && pLabel->IsVisible())
1763
0
    {
1764
0
        Size aLabelSize = getLayoutRequisition(*pLabel);
1765
0
        aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) );
1766
0
        aExpanderSize.AdjustWidth(aLabelSize.Width() );
1767
0
    }
1768
1769
0
    aRet.AdjustHeight(aExpanderSize.Height() );
1770
0
    aRet.setWidth( std::max(aExpanderSize.Width(), aRet.Width()) );
1771
1772
0
    return aRet;
1773
0
}
1774
1775
void VclExpander::setAllocation(const Size &rAllocation)
1776
0
{
1777
0
    Size aAllocation(rAllocation);
1778
0
    Point aChildPos;
1779
1780
0
    WindowImpl* pWindowImpl = ImplGetWindowImpl();
1781
1782
    //The label widget is the last (of two) children
1783
0
    vcl::Window *pChild = get_child();
1784
0
    vcl::Window *pLabel = pChild != pWindowImpl->mpLastChild.get() ? pWindowImpl->mpLastChild.get() : nullptr;
1785
1786
0
    Size aButtonSize = getLayoutRequisition(*m_pDisclosureButton);
1787
0
    Size aLabelSize;
1788
0
    Size aExpanderSize = aButtonSize;
1789
0
    if (pLabel && pLabel->IsVisible())
1790
0
    {
1791
0
        aLabelSize = getLayoutRequisition(*pLabel);
1792
0
        aExpanderSize.setHeight( std::max(aExpanderSize.Height(), aLabelSize.Height()) );
1793
0
        aExpanderSize.AdjustWidth(aLabelSize.Width() );
1794
0
    }
1795
1796
0
    aExpanderSize.setHeight( std::min(aExpanderSize.Height(), aAllocation.Height()) );
1797
0
    aExpanderSize.setWidth( std::min(aExpanderSize.Width(), aAllocation.Width()) );
1798
1799
0
    aButtonSize.setHeight( std::min(aButtonSize.Height(), aExpanderSize.Height()) );
1800
0
    aButtonSize.setWidth( std::min(aButtonSize.Width(), aExpanderSize.Width()) );
1801
1802
0
    tools::Long nExtraExpanderHeight = aExpanderSize.Height() - aButtonSize.Height();
1803
0
    Point aButtonPos(aChildPos.X(), aChildPos.Y() + nExtraExpanderHeight/2);
1804
0
    setLayoutAllocation(*m_pDisclosureButton, aButtonPos, aButtonSize);
1805
1806
0
    if (pLabel && pLabel->IsVisible())
1807
0
    {
1808
0
        aLabelSize.setHeight( std::min(aLabelSize.Height(), aExpanderSize.Height()) );
1809
0
        aLabelSize.setWidth( std::min(aLabelSize.Width(),
1810
0
            aExpanderSize.Width() - aButtonSize.Width()) );
1811
1812
0
        tools::Long nExtraLabelHeight = aExpanderSize.Height() - aLabelSize.Height();
1813
0
        Point aLabelPos(aChildPos.X() + aButtonSize.Width(), aChildPos.Y() + nExtraLabelHeight/2);
1814
0
        setLayoutAllocation(*pLabel, aLabelPos, aLabelSize);
1815
0
    }
1816
1817
0
    aAllocation.AdjustHeight( -(aExpanderSize.Height()) );
1818
0
    aChildPos.AdjustY(aExpanderSize.Height() );
1819
1820
0
    if (pChild && pChild->IsVisible())
1821
0
    {
1822
0
        if (!m_pDisclosureButton->IsChecked())
1823
0
            aAllocation = Size();
1824
0
        setLayoutAllocation(*pChild, aChildPos, aAllocation);
1825
0
    }
1826
0
}
1827
1828
bool VclExpander::set_property(const OUString &rKey, const OUString &rValue)
1829
0
{
1830
0
    if (rKey == "expanded")
1831
0
        set_expanded(toBool(rValue));
1832
0
    else if (rKey == "resize-toplevel")
1833
0
        m_bResizeTopLevel = toBool(rValue);
1834
0
    else
1835
0
        return VclBin::set_property(rKey, rValue);
1836
0
    return true;
1837
0
}
1838
1839
void VclExpander::StateChanged(StateChangedType nType)
1840
0
{
1841
0
    VclBin::StateChanged( nType );
1842
1843
0
    if (nType == StateChangedType::InitShow)
1844
0
    {
1845
0
        vcl::Window *pChild = get_child();
1846
0
        if (pChild)
1847
0
            pChild->Show(m_pDisclosureButton->IsChecked());
1848
0
    }
1849
0
}
1850
1851
const vcl::Window *VclExpander::get_label_widget() const
1852
0
{
1853
0
    return m_pDisclosureButton;
1854
0
}
1855
1856
vcl::Window *VclExpander::get_label_widget()
1857
0
{
1858
0
    return const_cast<vcl::Window*>(std::as_const(*this).get_label_widget());
1859
0
}
1860
1861
void VclExpander::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1862
0
{
1863
0
    VclContainer::DumpAsPropertyTree(rJsonWriter);
1864
0
    rJsonWriter.put("type", "expander");
1865
0
}
1866
1867
FactoryFunction VclExpander::GetUITestFactory() const
1868
0
{
1869
0
    return ExpanderUIObject::create;
1870
0
}
1871
1872
IMPL_LINK( VclExpander, ClickHdl, CheckBox&, rBtn, void )
1873
0
{
1874
0
    vcl::Window *pChild = get_child();
1875
0
    if (pChild)
1876
0
    {
1877
0
        pChild->Show(rBtn.IsChecked());
1878
0
        queue_resize();
1879
0
        Dialog* pResizeDialog = m_bResizeTopLevel ? GetParentDialog() : nullptr;
1880
0
        if (pResizeDialog)
1881
0
            pResizeDialog->setOptimalLayoutSize(true);
1882
0
    }
1883
0
    maExpandedHdl.Call(*this);
1884
0
}
1885
1886
VclScrolledWindow::VclScrolledWindow(vcl::Window *pParent)
1887
0
    : VclBin(pParent, WB_HIDE | WB_CLIPCHILDREN | WB_AUTOHSCROLL | WB_AUTOVSCROLL | WB_TABSTOP)
1888
0
    , m_bUserManagedScrolling(false)
1889
0
    , m_eDrawFrameStyle(DrawFrameStyle::NONE)
1890
0
    , m_eDrawFrameFlags(DrawFrameFlags::WindowBorder)
1891
0
    , m_pVScroll(VclPtr<ScrollBar>::Create(this, WB_HIDE | WB_VERT))
1892
0
    , m_pHScroll(VclPtr<ScrollBar>::Create(this, WB_HIDE | WB_HORZ))
1893
0
    , m_aScrollBarBox(VclPtr<ScrollBarBox>::Create(this, WB_HIDE))
1894
0
{
1895
0
    SetType(WindowType::SCROLLWINDOW);
1896
1897
0
    AllSettings aAllSettings = GetSettings();
1898
0
    StyleSettings aStyle = aAllSettings.GetStyleSettings();
1899
0
    aStyle.SetMonoColor(aStyle.GetShadowColor());
1900
0
    aAllSettings.SetStyleSettings(aStyle);
1901
0
    GetOutDev()->SetSettings(aAllSettings);
1902
1903
0
    Link<ScrollBar*,void> aLink( LINK( this, VclScrolledWindow, ScrollBarHdl ) );
1904
0
    m_pVScroll->SetScrollHdl(aLink);
1905
0
    m_pHScroll->SetScrollHdl(aLink);
1906
1907
0
    m_nBorderWidth = CalcBorderWidth();
1908
0
}
Unexecuted instantiation: VclScrolledWindow::VclScrolledWindow(vcl::Window*)
Unexecuted instantiation: VclScrolledWindow::VclScrolledWindow(vcl::Window*)
1909
1910
int VclScrolledWindow::CalcBorderWidth() const
1911
0
{
1912
0
    if (m_eDrawFrameStyle == DrawFrameStyle::NONE)
1913
0
        return 0;
1914
0
    const tools::Rectangle aRect(tools::Rectangle(Point(0, 0), Size(100, 100)));
1915
0
    DecorationView aDecoView(const_cast<OutputDevice*>(GetOutDev()));
1916
    // don't actually draw anything, just measure what size it would be and the diff is the desired border size to reserve
1917
0
    const tools::Rectangle aContentRect = aDecoView.DrawFrame(aRect, m_eDrawFrameStyle, m_eDrawFrameFlags | DrawFrameFlags::NoDraw);
1918
0
    const auto nBorderWidth = (aRect.GetWidth() - aContentRect.GetWidth()) / 2;
1919
0
    return std::max<int>(nBorderWidth, 1);
1920
0
}
1921
1922
void VclScrolledWindow::dispose()
1923
0
{
1924
0
    m_pVScroll.disposeAndClear();
1925
0
    m_pHScroll.disposeAndClear();
1926
0
    m_aScrollBarBox.disposeAndClear();
1927
0
    VclBin::dispose();
1928
0
}
1929
1930
IMPL_LINK_NOARG(VclScrolledWindow, ScrollBarHdl, ScrollBar*, void)
1931
0
{
1932
0
    vcl::Window *pChild = get_child();
1933
0
    if (!pChild)
1934
0
        return;
1935
1936
0
    assert(dynamic_cast<VclViewport*>(pChild) && "scrolledwindow child should be a Viewport");
1937
1938
0
    pChild = pChild->GetWindow(GetWindowType::FirstChild);
1939
1940
0
    if (!pChild)
1941
0
        return;
1942
1943
0
    Point aWinPos(-m_pHScroll->GetThumbPos(), -m_pVScroll->GetThumbPos());
1944
0
    pChild->SetPosPixel(aWinPos);
1945
0
}
1946
1947
const vcl::Window *VclScrolledWindow::get_child() const
1948
0
{
1949
0
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();
1950
0
    assert(GetChildCount() == 4 || pWindowImpl->mbInDispose);
1951
0
    return pWindowImpl->mpLastChild;
1952
0
}
1953
1954
vcl::Window *VclScrolledWindow::get_child()
1955
0
{
1956
0
    return const_cast<vcl::Window*>(std::as_const(*this).get_child());
1957
0
}
1958
1959
Size VclScrolledWindow::calculateRequisition() const
1960
0
{
1961
0
    Size aRet(0, 0);
1962
1963
0
    const vcl::Window *pChild = get_child();
1964
0
    if (pChild && pChild->IsVisible())
1965
0
        aRet = getLayoutRequisition(*pChild);
1966
1967
0
    if (GetStyle() & WB_VSCROLL)
1968
0
        aRet.AdjustWidth(getLayoutRequisition(*m_pVScroll).Width() );
1969
1970
0
    if (GetStyle() & WB_HSCROLL)
1971
0
        aRet.AdjustHeight(getLayoutRequisition(*m_pHScroll).Height() );
1972
1973
0
    aRet.AdjustHeight(2 * m_nBorderWidth);
1974
0
    aRet.AdjustWidth(2 * m_nBorderWidth);
1975
1976
0
    return aRet;
1977
0
}
1978
1979
void VclScrolledWindow::InitScrollBars(const Size &rRequest)
1980
0
{
1981
0
    const vcl::Window *pChild = get_child();
1982
0
    if (!pChild || !pChild->IsVisible())
1983
0
        return;
1984
1985
0
    Size aOutSize(getVisibleChildSize());
1986
1987
0
    m_pVScroll->SetRangeMax(rRequest.Height());
1988
0
    m_pVScroll->SetVisibleSize(aOutSize.Height());
1989
0
    m_pVScroll->SetPageSize(16);
1990
1991
0
    m_pHScroll->SetRangeMax(rRequest.Width());
1992
0
    m_pHScroll->SetVisibleSize(aOutSize.Width());
1993
0
    m_pHScroll->SetPageSize(16);
1994
1995
0
    m_pVScroll->Scroll();
1996
0
    m_pHScroll->Scroll();
1997
0
}
1998
1999
void VclScrolledWindow::doSetAllocation(const Size &rAllocation, bool bRetryOnFailure)
2000
0
{
2001
0
    Size aChildReq;
2002
2003
0
    vcl::Window *pChild = get_child();
2004
0
    if (pChild && pChild->IsVisible())
2005
0
        aChildReq = getLayoutRequisition(*pChild);
2006
2007
0
    tools::Long nAvailHeight = rAllocation.Height() - 2 * m_nBorderWidth;
2008
0
    tools::Long nAvailWidth = rAllocation.Width() - 2 * m_nBorderWidth;
2009
2010
    // vert. ScrollBar
2011
0
    bool bShowVScroll;
2012
0
    if (GetStyle() & WB_AUTOVSCROLL)
2013
0
        bShowVScroll = nAvailHeight < aChildReq.Height();
2014
0
    else
2015
0
        bShowVScroll = (GetStyle() & WB_VSCROLL) != 0;
2016
2017
0
    if (bShowVScroll)
2018
0
        nAvailWidth -= getLayoutRequisition(*m_pVScroll).Width();
2019
2020
    // horz. ScrollBar
2021
0
    bool bShowHScroll;
2022
0
    if (GetStyle() & WB_AUTOHSCROLL)
2023
0
    {
2024
0
        bShowHScroll = nAvailWidth < aChildReq.Width();
2025
2026
0
        if (bShowHScroll)
2027
0
            nAvailHeight -= getLayoutRequisition(*m_pHScroll).Height();
2028
2029
0
        if (GetStyle() & WB_AUTOVSCROLL)
2030
0
            bShowVScroll = nAvailHeight < aChildReq.Height();
2031
0
    }
2032
0
    else
2033
0
        bShowHScroll = (GetStyle() & WB_HSCROLL) != 0;
2034
2035
0
    if (m_pHScroll->IsVisible() != bShowHScroll)
2036
0
        m_pHScroll->Show(bShowHScroll);
2037
0
    if (m_pVScroll->IsVisible() != bShowVScroll)
2038
0
        m_pVScroll->Show(bShowVScroll);
2039
2040
0
    Size aInnerSize(rAllocation);
2041
0
    aInnerSize.AdjustWidth(-2 * m_nBorderWidth);
2042
0
    aInnerSize.AdjustHeight(-2 * m_nBorderWidth);
2043
2044
0
    bool bBothVisible = m_pVScroll->IsVisible() && m_pHScroll->IsVisible();
2045
0
    auto nScrollBarWidth = getLayoutRequisition(*m_pVScroll).Width();
2046
0
    auto nScrollBarHeight = getLayoutRequisition(*m_pHScroll).Height();
2047
2048
0
    if (m_pVScroll->IsVisible())
2049
0
    {
2050
0
        Point aScrollPos(rAllocation.Width() - nScrollBarWidth - m_nBorderWidth, m_nBorderWidth);
2051
0
        Size aScrollSize(nScrollBarWidth, rAllocation.Height() - 2 * m_nBorderWidth);
2052
0
        if (bBothVisible)
2053
0
            aScrollSize.AdjustHeight(-nScrollBarHeight);
2054
0
        setLayoutAllocation(*m_pVScroll, aScrollPos, aScrollSize);
2055
0
        aInnerSize.AdjustWidth( -nScrollBarWidth );
2056
0
    }
2057
2058
0
    if (m_pHScroll->IsVisible())
2059
0
    {
2060
0
        Point aScrollPos(m_nBorderWidth, rAllocation.Height() - nScrollBarHeight);
2061
0
        Size aScrollSize(rAllocation.Width() - 2 * m_nBorderWidth, nScrollBarHeight);
2062
0
        if (bBothVisible)
2063
0
            aScrollSize.AdjustWidth(-nScrollBarWidth);
2064
0
        setLayoutAllocation(*m_pHScroll, aScrollPos, aScrollSize);
2065
0
        aInnerSize.AdjustHeight( -nScrollBarHeight );
2066
0
    }
2067
2068
0
    if (bBothVisible)
2069
0
    {
2070
0
        Point aBoxPos(aInnerSize.Width() + m_nBorderWidth, aInnerSize.Height() + m_nBorderWidth);
2071
0
        m_aScrollBarBox->SetPosSizePixel(aBoxPos, Size(nScrollBarWidth, nScrollBarHeight));
2072
0
        m_aScrollBarBox->Show();
2073
0
    }
2074
0
    else
2075
0
    {
2076
0
        m_aScrollBarBox->Hide();
2077
0
    }
2078
2079
0
    if (pChild && pChild->IsVisible())
2080
0
    {
2081
0
        assert(dynamic_cast<VclViewport*>(pChild) && "scrolledwindow child should be a Viewport");
2082
2083
0
        WinBits nOldBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL));
2084
2085
0
        setLayoutAllocation(*pChild, Point(m_nBorderWidth, m_nBorderWidth), aInnerSize);
2086
2087
        // tdf#128758 if the layout allocation triggered some callback that
2088
        // immediately invalidates the layout by adding scrollbars then
2089
        // normally this would simply retrigger layout and another toplevel
2090
        // attempt is made later. But the initial layout attempt blocks
2091
        // relayouts, so just make another single effort here.
2092
0
        WinBits nNewBits = (GetStyle() & (WB_AUTOVSCROLL | WB_VSCROLL | WB_AUTOHSCROLL | WB_HSCROLL));
2093
0
        if (nOldBits != nNewBits && bRetryOnFailure)
2094
0
        {
2095
0
            doSetAllocation(rAllocation, false);
2096
0
            return;
2097
0
        }
2098
0
    }
2099
2100
0
    if (!m_bUserManagedScrolling)
2101
0
        InitScrollBars(aChildReq);
2102
0
}
2103
2104
void VclScrolledWindow::setAllocation(const Size &rAllocation)
2105
0
{
2106
0
    doSetAllocation(rAllocation, true);
2107
0
}
2108
2109
Size VclScrolledWindow::getVisibleChildSize() const
2110
0
{
2111
0
    Size aRet(GetSizePixel());
2112
0
    if (m_pVScroll->IsVisible())
2113
0
        aRet.AdjustWidth( -(m_pVScroll->GetSizePixel().Width()) );
2114
0
    if (m_pHScroll->IsVisible())
2115
0
        aRet.AdjustHeight( -(m_pHScroll->GetSizePixel().Height()) );
2116
0
    aRet.AdjustHeight(-2 * m_nBorderWidth);
2117
0
    aRet.AdjustWidth(-2 * m_nBorderWidth);
2118
0
    return aRet;
2119
0
}
2120
2121
bool VclScrolledWindow::set_property(const OUString &rKey, const OUString &rValue)
2122
0
{
2123
0
    if (rKey == "shadow-type" || rKey == "name")
2124
0
    {
2125
0
        if (rKey == "shadow-type")
2126
0
        {
2127
            // despite the style names, this looks like the best mapping
2128
0
            if (rValue == "in")
2129
0
                m_eDrawFrameStyle = DrawFrameStyle::Out;
2130
0
            else if (rValue == "out")
2131
0
                m_eDrawFrameStyle = DrawFrameStyle::In;
2132
0
            else if (rValue == "etched-in")
2133
0
                m_eDrawFrameStyle = DrawFrameStyle::DoubleOut;
2134
0
            else if (rValue == "etched-out")
2135
0
                m_eDrawFrameStyle = DrawFrameStyle::DoubleIn;
2136
0
            else if (rValue == "none")
2137
0
                m_eDrawFrameStyle = DrawFrameStyle::NONE;
2138
0
        }
2139
0
        else if (rKey == "name")
2140
0
        {
2141
0
            m_eDrawFrameFlags = DrawFrameFlags::WindowBorder;
2142
0
            if (rValue == "monoborder")
2143
0
                m_eDrawFrameFlags |= DrawFrameFlags::Mono;
2144
0
        }
2145
2146
0
        auto nBorderWidth = CalcBorderWidth();
2147
0
        if (m_nBorderWidth != nBorderWidth)
2148
0
        {
2149
0
            m_nBorderWidth = nBorderWidth;
2150
0
            queue_resize();
2151
0
        }
2152
2153
0
        return true;
2154
0
    }
2155
2156
0
    bool bRet = VclBin::set_property(rKey, rValue);
2157
0
    m_pVScroll->Show((GetStyle() & WB_VSCROLL) != 0);
2158
0
    m_pHScroll->Show((GetStyle() & WB_HSCROLL) != 0);
2159
0
    return bRet;
2160
0
}
2161
2162
bool VclScrolledWindow::EventNotify(NotifyEvent& rNEvt)
2163
0
{
2164
0
    bool bDone = false;
2165
0
    if ( rNEvt.GetType() == NotifyEventType::COMMAND )
2166
0
    {
2167
0
        const CommandEvent& rCEvt = *rNEvt.GetCommandEvent();
2168
0
        if ( rCEvt.GetCommand() == CommandEventId::Wheel )
2169
0
        {
2170
0
            const CommandWheelData* pData = rCEvt.GetWheelData();
2171
0
            if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
2172
0
            {
2173
                // tdf#140537 only handle scroll commands in the valid shown scrollbars
2174
0
                bDone = HandleScrollCommand(rCEvt,
2175
0
                                            m_pHScroll->IsVisible() ? m_pHScroll : nullptr,
2176
0
                                            m_pVScroll->IsVisible() ? m_pVScroll : nullptr);
2177
0
            }
2178
0
        }
2179
0
        else if (rCEvt.GetCommand() == CommandEventId::GesturePan)
2180
0
        {
2181
0
            bDone = HandleScrollCommand(rCEvt, m_pHScroll->IsVisible() ? m_pHScroll : nullptr,
2182
0
                                        m_pVScroll->IsVisible() ? m_pVScroll : nullptr);
2183
0
        }
2184
0
    }
2185
2186
0
    return bDone || VclBin::EventNotify( rNEvt );
2187
0
}
2188
2189
void VclScrolledWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
2190
0
{
2191
0
    VclBin::Paint(rRenderContext, rRect);
2192
0
    if (m_eDrawFrameStyle == DrawFrameStyle::NONE)
2193
0
        return;
2194
0
    const tools::Rectangle aRect(tools::Rectangle(Point(0,0), GetSizePixel()));
2195
0
    DecorationView aDecoView(&rRenderContext);
2196
0
    const tools::Rectangle aContentRect = aDecoView.DrawFrame(aRect, m_eDrawFrameStyle, m_eDrawFrameFlags);
2197
0
    const auto nBorderWidth = (aRect.GetWidth() - aContentRect.GetWidth()) / 2;
2198
0
    SAL_WARN_IF(nBorderWidth > m_nBorderWidth, "vcl.layout", "desired border at paint " <<
2199
0
                nBorderWidth << " is larger than expected " << m_nBorderWidth);
2200
0
}
2201
2202
namespace {
2203
void lcl_dumpScrollbar(::tools::JsonWriter& rJsonWriter, const ScrollBar& rScrollBar)
2204
0
{
2205
0
    rJsonWriter.put("lower", rScrollBar.GetRangeMin());
2206
0
    rJsonWriter.put("upper", rScrollBar.GetRangeMax());
2207
0
    rJsonWriter.put("step_increment", rScrollBar.GetLineSize());
2208
0
    rJsonWriter.put("page_increment", rScrollBar.GetPageSize());
2209
0
    rJsonWriter.put("value", rScrollBar.GetThumbPos());
2210
0
    rJsonWriter.put("page_size", rScrollBar.GetVisibleSize());
2211
0
}
2212
};
2213
2214
void VclScrolledWindow::DumpAsPropertyTree(::tools::JsonWriter& rJsonWriter)
2215
0
{
2216
0
    VclBin::DumpAsPropertyTree(rJsonWriter);
2217
2218
0
    rJsonWriter.put("user_managed_scrolling", m_bUserManagedScrolling);
2219
2220
0
    {
2221
0
        auto aVertical = rJsonWriter.startNode("vertical");
2222
2223
0
        ScrollBar& rScrollBar = getVertScrollBar();
2224
0
        lcl_dumpScrollbar(rJsonWriter, rScrollBar);
2225
2226
0
        WinBits nWinBits = GetStyle();
2227
0
        if (nWinBits & WB_VSCROLL)
2228
0
            rJsonWriter.put("policy", "always");
2229
0
        else if (nWinBits & WB_AUTOVSCROLL)
2230
0
            rJsonWriter.put("policy", "auto");
2231
0
        else
2232
0
            rJsonWriter.put("policy", "never");
2233
0
    }
2234
2235
0
    {
2236
0
        auto aHorizontal = rJsonWriter.startNode("horizontal");
2237
2238
0
        ScrollBar& rScrollBar = getHorzScrollBar();
2239
0
        lcl_dumpScrollbar(rJsonWriter, rScrollBar);
2240
2241
0
        WinBits nWinBits = GetStyle();
2242
0
        if (nWinBits & WB_HSCROLL)
2243
0
            rJsonWriter.put("policy", "always");
2244
0
        else if (nWinBits & WB_AUTOHSCROLL)
2245
0
            rJsonWriter.put("policy", "auto");
2246
0
        else
2247
0
            rJsonWriter.put("policy", "never");
2248
0
    }
2249
0
}
2250
2251
void VclViewport::setAllocation(const Size &rAllocation)
2252
0
{
2253
0
    vcl::Window *pChild = get_child();
2254
0
    if (!(pChild && pChild->IsVisible()))
2255
0
        return;
2256
2257
0
    Size aReq(getLayoutRequisition(*pChild));
2258
0
    aReq.setWidth( std::max(aReq.Width(), rAllocation.Width()) );
2259
0
    aReq.setHeight( std::max(aReq.Height(), rAllocation.Height()) );
2260
0
    Point aKeepPos(pChild->GetPosPixel());
2261
0
    if (m_bInitialAllocation)
2262
0
    {
2263
0
        aKeepPos = Point(0, 0);
2264
0
        m_bInitialAllocation = false;
2265
0
    }
2266
0
    setLayoutAllocation(*pChild, aKeepPos, aReq);
2267
0
}
2268
2269
const vcl::Window *VclEventBox::get_child() const
2270
0
{
2271
0
    const WindowImpl* pWindowImpl = ImplGetWindowImpl();
2272
2273
0
    assert(pWindowImpl->mpFirstChild.get() == m_aEventBoxHelper.get());
2274
2275
0
    return pWindowImpl->mpFirstChild->GetWindow(GetWindowType::Next);
2276
0
}
2277
2278
vcl::Window *VclEventBox::get_child()
2279
0
{
2280
0
    return const_cast<vcl::Window*>(std::as_const(*this).get_child());
2281
0
}
2282
2283
void VclEventBox::setAllocation(const Size& rAllocation)
2284
0
{
2285
0
    Point aChildPos(0, 0);
2286
0
    for (vcl::Window *pChild = GetWindow(GetWindowType::FirstChild); pChild; pChild = pChild->GetWindow(GetWindowType::Next))
2287
0
    {
2288
0
        if (!pChild->IsVisible())
2289
0
            continue;
2290
0
        setLayoutAllocation(*pChild, aChildPos, rAllocation);
2291
0
    }
2292
0
}
2293
2294
Size VclEventBox::calculateRequisition() const
2295
0
{
2296
0
    Size aRet(0, 0);
2297
2298
0
    for (const vcl::Window* pChild = get_child(); pChild;
2299
0
        pChild = pChild->GetWindow(GetWindowType::Next))
2300
0
    {
2301
0
        if (!pChild->IsVisible())
2302
0
            continue;
2303
0
        Size aChildSize = getLayoutRequisition(*pChild);
2304
0
        aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) );
2305
0
        aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) );
2306
0
    }
2307
2308
0
    return aRet;
2309
0
}
2310
2311
void VclEventBox::Command(const CommandEvent&)
2312
0
{
2313
    //discard events by default to block them reaching children
2314
0
}
2315
2316
VclEventBox::~VclEventBox()
2317
0
{
2318
0
    disposeOnce();
2319
0
}
2320
2321
void VclEventBox::dispose()
2322
0
{
2323
0
    m_aEventBoxHelper.disposeAndClear();
2324
0
    VclBin::dispose();
2325
0
}
2326
2327
void VclSizeGroup::trigger_queue_resize()
2328
0
{
2329
    //sufficient to trigger one widget to trigger all of them
2330
0
    if (!m_aWindows.empty())
2331
0
    {
2332
0
        (*m_aWindows.begin())->queue_resize();
2333
0
    }
2334
0
}
2335
2336
void VclSizeGroup::set_ignore_hidden(bool bIgnoreHidden)
2337
0
{
2338
0
    if (bIgnoreHidden != m_bIgnoreHidden)
2339
0
    {
2340
0
        m_bIgnoreHidden = bIgnoreHidden;
2341
0
        trigger_queue_resize();
2342
0
    }
2343
0
}
2344
2345
void VclSizeGroup::set_mode(VclSizeGroupMode eMode)
2346
0
{
2347
0
    if (eMode != m_eMode)
2348
0
    {
2349
0
        m_eMode = eMode;
2350
0
        trigger_queue_resize();
2351
0
    }
2352
2353
0
}
2354
2355
void VclSizeGroup::set_property(const OUString &rKey, const OUString &rValue)
2356
0
{
2357
0
    if (rKey == "ignore-hidden")
2358
0
        set_ignore_hidden(toBool(rValue));
2359
0
    else if (rKey == "mode")
2360
0
    {
2361
0
        VclSizeGroupMode eMode = VclSizeGroupMode::Horizontal;
2362
0
        if (rValue == "none")
2363
0
            eMode = VclSizeGroupMode::NONE;
2364
0
        else if (rValue == "horizontal")
2365
0
            eMode = VclSizeGroupMode::Horizontal;
2366
0
        else if (rValue == "vertical")
2367
0
            eMode = VclSizeGroupMode::Vertical;
2368
0
        else if (rValue == "both")
2369
0
            eMode = VclSizeGroupMode::Both;
2370
0
        else
2371
0
        {
2372
0
            SAL_WARN("vcl.layout", "unknown size group mode" << rValue);
2373
0
        }
2374
0
        set_mode(eMode);
2375
0
    }
2376
0
    else
2377
0
    {
2378
0
        SAL_INFO("vcl.layout", "unhandled property: " << rKey);
2379
0
    }
2380
0
}
2381
2382
void MessageDialog::create_message_area()
2383
0
{
2384
0
    setDeferredProperties();
2385
2386
0
    if (m_pGrid)
2387
0
        return;
2388
2389
0
    VclContainer *pContainer = get_content_area();
2390
0
    assert(pContainer);
2391
2392
0
    m_pGrid.reset( VclPtr<VclGrid>::Create(pContainer) );
2393
0
    m_pGrid->reorderWithinParent(0);
2394
0
    m_pGrid->set_column_spacing(12);
2395
0
    m_pMessageBox.reset(VclPtr<VclVBox>::Create(m_pGrid));
2396
0
    m_pMessageBox->set_grid_left_attach(1);
2397
0
    m_pMessageBox->set_grid_top_attach(0);
2398
0
    m_pMessageBox->set_spacing(GetTextHeight());
2399
2400
0
    m_pImage = VclPtr<FixedImage>::Create(m_pGrid, WB_CENTER | WB_VCENTER | WB_3DLOOK);
2401
0
    switch (m_eMessageType)
2402
0
    {
2403
0
        case VclMessageType::Info:
2404
0
            m_pImage->SetImage(GetStandardInfoBoxImage());
2405
0
            break;
2406
0
        case VclMessageType::Warning:
2407
0
            m_pImage->SetImage(GetStandardWarningBoxImage());
2408
0
            break;
2409
0
        case VclMessageType::Question:
2410
0
            m_pImage->SetImage(GetStandardQueryBoxImage());
2411
0
            break;
2412
0
        case VclMessageType::Error:
2413
0
            m_pImage->SetImage(GetStandardErrorBoxImage());
2414
0
            break;
2415
0
        case VclMessageType::Other:
2416
0
            break;
2417
0
    }
2418
0
    m_pImage->set_grid_left_attach(0);
2419
0
    m_pImage->set_grid_top_attach(0);
2420
0
    m_pImage->set_valign(VclAlign::Start);
2421
0
    m_pImage->Show(m_eMessageType != VclMessageType::Other);
2422
2423
0
    WinBits nWinStyle = WB_CLIPCHILDREN | WB_LEFT | WB_VCENTER | WB_NOLABEL | WB_NOTABSTOP;
2424
2425
0
    bool bHasSecondaryText = !m_sSecondaryString.isEmpty();
2426
2427
0
    m_pPrimaryMessage = VclPtr<VclMultiLineEdit>::Create(m_pMessageBox, nWinStyle);
2428
0
    m_pPrimaryMessage->SetPaintTransparent(true);
2429
0
    m_pPrimaryMessage->EnableCursor(false);
2430
2431
0
    m_pPrimaryMessage->set_hexpand(true);
2432
0
    m_pPrimaryMessage->SetText(m_sPrimaryString);
2433
0
    m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty());
2434
2435
0
    m_pSecondaryMessage = VclPtr<VclMultiLineEdit>::Create(m_pMessageBox, nWinStyle);
2436
0
    m_pSecondaryMessage->SetPaintTransparent(true);
2437
0
    m_pSecondaryMessage->EnableCursor(false);
2438
0
    m_pSecondaryMessage->set_hexpand(true);
2439
0
    m_pSecondaryMessage->SetText(m_sSecondaryString);
2440
0
    m_pSecondaryMessage->Show(bHasSecondaryText);
2441
2442
0
    MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, bHasSecondaryText ? m_pSecondaryMessage.get() : nullptr);
2443
2444
0
    VclButtonBox *pButtonBox = get_action_area();
2445
0
    assert(pButtonBox);
2446
2447
0
    VclPtr<PushButton> pBtn;
2448
0
    short nDefaultResponse = get_default_response();
2449
0
    switch (m_eButtonsType)
2450
0
    {
2451
0
        case VclButtonsType::NONE:
2452
0
            break;
2453
0
        case VclButtonsType::Ok:
2454
0
            pBtn.reset( VclPtr<OKButton>::Create(pButtonBox) );
2455
0
            pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
2456
0
            pBtn->Show();
2457
0
            pBtn->set_id(u"ok"_ustr);
2458
0
            add_button(pBtn, RET_OK, true);
2459
0
            nDefaultResponse = RET_OK;
2460
0
            break;
2461
0
        case VclButtonsType::Close:
2462
0
            pBtn.reset( VclPtr<CloseButton>::Create(pButtonBox) );
2463
0
            pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
2464
0
            pBtn->Show();
2465
0
            pBtn->set_id(u"close"_ustr);
2466
0
            add_button(pBtn, RET_CLOSE, true);
2467
0
            nDefaultResponse = RET_CLOSE;
2468
0
            break;
2469
0
        case VclButtonsType::Cancel:
2470
0
            pBtn.reset( VclPtr<CancelButton>::Create(pButtonBox) );
2471
0
            pBtn->SetStyle(pBtn->GetStyle() & WB_DEFBUTTON);
2472
0
            pBtn->Show();
2473
0
            pBtn->set_id(u"cancel"_ustr);
2474
0
            add_button(pBtn, RET_CANCEL, true);
2475
0
            nDefaultResponse = RET_CANCEL;
2476
0
            break;
2477
0
        case VclButtonsType::YesNo:
2478
0
            pBtn = VclPtr<PushButton>::Create(pButtonBox);
2479
0
            pBtn->SetText(GetStandardText(StandardButtonType::Yes));
2480
0
            pBtn->Show();
2481
0
            pBtn->set_id(u"yes"_ustr);
2482
0
            add_button(pBtn, RET_YES, true);
2483
2484
0
            pBtn.reset( VclPtr<PushButton>::Create(pButtonBox) );
2485
0
            pBtn->SetText(GetStandardText(StandardButtonType::No));
2486
0
            pBtn->Show();
2487
0
            pBtn->set_id(u"no"_ustr);
2488
0
            add_button(pBtn, RET_NO, true);
2489
0
            nDefaultResponse = RET_NO;
2490
0
            break;
2491
0
        case VclButtonsType::OkCancel:
2492
0
            pBtn.reset( VclPtr<OKButton>::Create(pButtonBox) );
2493
0
            pBtn->Show();
2494
0
            pBtn->set_id(u"ok"_ustr);
2495
0
            add_button(pBtn, RET_OK, true);
2496
2497
0
            pBtn.reset( VclPtr<CancelButton>::Create(pButtonBox) );
2498
0
            pBtn->Show();
2499
0
            pBtn->set_id(u"cancel"_ustr);
2500
0
            add_button(pBtn, RET_CANCEL, true);
2501
0
            nDefaultResponse = RET_CANCEL;
2502
0
            break;
2503
0
    }
2504
0
    set_default_response(nDefaultResponse);
2505
0
    sort_native_button_order(*pButtonBox);
2506
0
    m_pMessageBox->Show();
2507
0
    m_pGrid->Show();
2508
0
}
2509
2510
void MessageDialog::create_owned_areas()
2511
0
{
2512
#if defined _WIN32
2513
    set_border_width(3);
2514
#else
2515
0
    set_border_width(12);
2516
0
#endif
2517
0
    m_pOwnedContentArea.reset(VclPtr<VclVBox>::Create(this, false, 24));
2518
0
    set_content_area(m_pOwnedContentArea);
2519
0
    m_pOwnedContentArea->Show();
2520
0
    m_pOwnedActionArea.reset( VclPtr<VclHButtonBox>::Create(m_pOwnedContentArea) );
2521
0
    set_action_area(m_pOwnedActionArea);
2522
0
    m_pOwnedActionArea->Show();
2523
0
}
2524
2525
MessageDialog::MessageDialog(vcl::Window* pParent, WinBits nStyle)
2526
0
    : Dialog(pParent, nStyle)
2527
0
    , m_eButtonsType(VclButtonsType::NONE)
2528
0
    , m_eMessageType(VclMessageType::Info)
2529
0
    , m_pOwnedContentArea(nullptr)
2530
0
    , m_pOwnedActionArea(nullptr)
2531
0
    , m_pGrid(nullptr)
2532
0
    , m_pMessageBox(nullptr)
2533
0
    , m_pImage(nullptr)
2534
0
    , m_pPrimaryMessage(nullptr)
2535
0
    , m_pSecondaryMessage(nullptr)
2536
0
{
2537
0
    SetType(WindowType::MESSBOX);
2538
0
}
Unexecuted instantiation: MessageDialog::MessageDialog(vcl::Window*, long)
Unexecuted instantiation: MessageDialog::MessageDialog(vcl::Window*, long)
2539
2540
MessageDialog::MessageDialog(vcl::Window* pParent,
2541
    OUString aMessage,
2542
    VclMessageType eMessageType,
2543
    VclButtonsType eButtonsType)
2544
0
    : Dialog(pParent, WB_MOVEABLE | WB_3DLOOK | WB_CLOSEABLE)
2545
0
    , m_eButtonsType(eButtonsType)
2546
0
    , m_eMessageType(eMessageType)
2547
0
    , m_pGrid(nullptr)
2548
0
    , m_pMessageBox(nullptr)
2549
0
    , m_pImage(nullptr)
2550
0
    , m_pPrimaryMessage(nullptr)
2551
0
    , m_pSecondaryMessage(nullptr)
2552
0
    , m_sPrimaryString(std::move(aMessage))
2553
0
{
2554
0
    SetType(WindowType::MESSBOX);
2555
0
    create_owned_areas();
2556
0
    create_message_area();
2557
2558
0
    switch (m_eMessageType)
2559
0
    {
2560
0
        case VclMessageType::Info:
2561
0
            SetText(GetStandardInfoBoxText());
2562
0
            break;
2563
0
        case VclMessageType::Warning:
2564
0
            SetText(GetStandardWarningBoxText());
2565
0
            break;
2566
0
        case VclMessageType::Question:
2567
0
            SetText(GetStandardQueryBoxText());
2568
0
            break;
2569
0
        case VclMessageType::Error:
2570
0
            SetText(GetStandardErrorBoxText());
2571
0
            SetTaskBarState(VclTaskBarStates::Error);
2572
0
            break;
2573
0
        case VclMessageType::Other:
2574
0
            SetText(Application::GetDisplayName());
2575
0
            break;
2576
0
    }
2577
0
}
Unexecuted instantiation: MessageDialog::MessageDialog(vcl::Window*, rtl::OUString, VclMessageType, VclButtonsType)
Unexecuted instantiation: MessageDialog::MessageDialog(vcl::Window*, rtl::OUString, VclMessageType, VclButtonsType)
2578
2579
void MessageDialog::dispose()
2580
0
{
2581
0
    SetTaskBarState(VclTaskBarStates::Normal);
2582
2583
0
    disposeOwnedButtons();
2584
0
    m_pPrimaryMessage.disposeAndClear();
2585
0
    m_pSecondaryMessage.disposeAndClear();
2586
0
    m_pImage.disposeAndClear();
2587
0
    m_pMessageBox.disposeAndClear();
2588
0
    m_pGrid.disposeAndClear();
2589
0
    m_pOwnedActionArea.disposeAndClear();
2590
0
    m_pOwnedContentArea.disposeAndClear();
2591
0
    Dialog::dispose();
2592
0
}
2593
2594
MessageDialog::~MessageDialog()
2595
0
{
2596
0
    disposeOnce();
2597
0
}
2598
2599
void MessageDialog::SetMessagesWidths(vcl::Window const *pParent,
2600
    VclMultiLineEdit *pPrimaryMessage, VclMultiLineEdit *pSecondaryMessage)
2601
0
{
2602
0
    if (pSecondaryMessage)
2603
0
    {
2604
0
        assert(pPrimaryMessage);
2605
0
        vcl::Font aFont = pParent->GetSettings().GetStyleSettings().GetLabelFont();
2606
0
        aFont.SetFontSize(Size(0, aFont.GetFontSize().Height() * 1.2));
2607
0
        aFont.SetWeight(WEIGHT_BOLD);
2608
0
        pPrimaryMessage->SetControlFont(aFont);
2609
0
        pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 44);
2610
0
        pSecondaryMessage->SetMaxTextWidth(pSecondaryMessage->approximate_char_width() * 60);
2611
0
    }
2612
0
    else
2613
0
        pPrimaryMessage->SetMaxTextWidth(pPrimaryMessage->approximate_char_width() * 60);
2614
0
}
2615
2616
OUString const & MessageDialog::get_primary_text() const
2617
0
{
2618
0
    const_cast<MessageDialog*>(this)->setDeferredProperties();
2619
2620
0
    return m_sPrimaryString;
2621
0
}
2622
2623
OUString const & MessageDialog::get_secondary_text() const
2624
0
{
2625
0
    const_cast<MessageDialog*>(this)->setDeferredProperties();
2626
2627
0
    return m_sSecondaryString;
2628
0
}
2629
2630
bool MessageDialog::set_property(const OUString &rKey, const OUString &rValue)
2631
0
{
2632
0
    if (rKey == "text")
2633
0
        set_primary_text(rValue);
2634
0
    else if (rKey == "secondary-text")
2635
0
        set_secondary_text(rValue);
2636
0
    else if (rKey == "message-type")
2637
0
    {
2638
0
        VclMessageType eMode = VclMessageType::Info;
2639
0
        if (rValue == "info")
2640
0
            eMode = VclMessageType::Info;
2641
0
        else if (rValue == "warning")
2642
0
            eMode = VclMessageType::Warning;
2643
0
        else if (rValue == "question")
2644
0
            eMode = VclMessageType::Question;
2645
0
        else if (rValue == "error")
2646
0
            eMode = VclMessageType::Error;
2647
0
        else if (rValue == "other")
2648
0
            eMode = VclMessageType::Other;
2649
0
        else
2650
0
        {
2651
0
            SAL_WARN("vcl.layout", "unknown message type mode" << rValue);
2652
0
        }
2653
0
        m_eMessageType = eMode;
2654
0
    }
2655
0
    else if (rKey == "buttons")
2656
0
    {
2657
0
        m_eButtonsType = BuilderBase::mapGtkToVclButtonsType(rValue);
2658
0
    }
2659
0
    else
2660
0
        return Dialog::set_property(rKey, rValue);
2661
0
    return true;
2662
0
}
2663
2664
void MessageDialog::set_primary_text(const OUString &rPrimaryString)
2665
0
{
2666
0
    m_sPrimaryString = rPrimaryString;
2667
0
    if (m_pPrimaryMessage)
2668
0
    {
2669
0
        m_pPrimaryMessage->SetText(m_sPrimaryString);
2670
0
        m_pPrimaryMessage->Show(!m_sPrimaryString.isEmpty());
2671
0
        MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr);
2672
0
    }
2673
0
}
2674
2675
void MessageDialog::set_secondary_text(const OUString &rSecondaryString)
2676
0
{
2677
0
    m_sSecondaryString = rSecondaryString;
2678
0
    if (m_pSecondaryMessage)
2679
0
    {
2680
0
        m_pSecondaryMessage->SetText("\n" + m_sSecondaryString);
2681
0
        m_pSecondaryMessage->Show(!m_sSecondaryString.isEmpty());
2682
0
        MessageDialog::SetMessagesWidths(this, m_pPrimaryMessage, !m_sSecondaryString.isEmpty() ? m_pSecondaryMessage.get() : nullptr);
2683
0
    }
2684
0
}
2685
2686
void MessageDialog::StateChanged(StateChangedType nType)
2687
0
{
2688
0
    Dialog::StateChanged(nType);
2689
0
    if (nType == StateChangedType::InitShow)
2690
0
    {
2691
        // MessageBox should be at least as wide as to see the title
2692
0
        auto nTitleWidth = CalcTitleWidth();
2693
        // Extra-Width for Close button
2694
0
        nTitleWidth += mpWindowImpl->mnTopBorder;
2695
0
        if (get_preferred_size().Width() < nTitleWidth)
2696
0
        {
2697
0
            set_width_request(nTitleWidth);
2698
0
            DoInitialLayout();
2699
0
        }
2700
0
    }
2701
0
}
2702
2703
VclPaned::VclPaned(vcl::Window *pParent, bool bVertical)
2704
0
    : VclContainer(pParent, WB_HIDE | WB_CLIPCHILDREN)
2705
0
    , m_pSplitter(VclPtr<Splitter>::Create(this, bVertical ? WB_VSCROLL : WB_HSCROLL))
2706
0
    , m_nPosition(-1)
2707
0
{
2708
0
    m_pSplitter->SetBackground(Wallpaper(Application::GetSettings().GetStyleSettings().GetFaceColor()));
2709
0
    m_pSplitter->Show();
2710
0
}
2711
2712
void VclPaned::dispose()
2713
0
{
2714
0
    m_pSplitter.disposeAndClear();
2715
0
    VclContainer::dispose();
2716
0
}
2717
2718
VclVPaned::VclVPaned(vcl::Window *pParent)
2719
0
    : VclPaned(pParent, true)
2720
0
{
2721
0
    m_pSplitter->SetSplitHdl(LINK(this, VclVPaned, SplitHdl));
2722
0
}
Unexecuted instantiation: VclVPaned::VclVPaned(vcl::Window*)
Unexecuted instantiation: VclVPaned::VclVPaned(vcl::Window*)
2723
2724
IMPL_LINK(VclVPaned, SplitHdl, Splitter*, pSplitter, void)
2725
0
{
2726
0
    tools::Long nSize = pSplitter->GetSplitPosPixel();
2727
0
    Size aSplitterSize(m_pSplitter->GetSizePixel());
2728
0
    Size aAllocation(GetSizePixel());
2729
0
    arrange(aAllocation, nSize, aAllocation.Height() - nSize - aSplitterSize.Height());
2730
0
}
2731
2732
void VclVPaned::arrange(const Size& rAllocation, tools::Long nFirstHeight, tools::Long nSecondHeight)
2733
0
{
2734
0
    Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height());
2735
0
    Size aFirstChildSize(rAllocation.Width(), nFirstHeight);
2736
0
    Size aSecondChildSize(rAllocation.Width(), nSecondHeight);
2737
0
    int nElement = 0;
2738
0
    for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2739
0
        pChild = pChild->GetWindow(GetWindowType::Next))
2740
0
    {
2741
0
        if (!pChild->IsVisible())
2742
0
            continue;
2743
0
        if (nElement == 0)
2744
0
        {
2745
0
            Point aSplitterPos(0, aFirstChildSize.Height());
2746
0
            setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize);
2747
0
            m_nPosition = aSplitterPos.Y() + aSplitterSize.Height() / 2;
2748
0
        }
2749
0
        else if (nElement == 1)
2750
0
        {
2751
0
            Point aChildPos(0, 0);
2752
0
            setLayoutAllocation(*pChild, aChildPos, aFirstChildSize);
2753
0
        }
2754
0
        else if (nElement == 2)
2755
0
        {
2756
0
            Point aChildPos(0, aFirstChildSize.Height() + aSplitterSize.Height());
2757
0
            setLayoutAllocation(*pChild, aChildPos, aSecondChildSize);
2758
0
        }
2759
0
        ++nElement;
2760
0
    }
2761
0
}
2762
2763
void VclVPaned::set_position(tools::Long nPosition)
2764
0
{
2765
0
    VclPaned::set_position(nPosition);
2766
2767
0
    Size aAllocation(GetSizePixel());
2768
0
    Size aSplitterSize(m_pSplitter->GetSizePixel());
2769
2770
0
    nPosition -= aSplitterSize.Height() / 2;
2771
2772
0
    arrange(aAllocation, nPosition, aAllocation.Height() - nPosition - aSplitterSize.Height());
2773
0
}
2774
2775
void VclVPaned::setAllocation(const Size& rAllocation)
2776
0
{
2777
    //supporting "shrink" could be done by adjusting the allowed drag rectangle
2778
0
    m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation));
2779
0
    Size aSplitterSize(rAllocation.Width(), getLayoutRequisition(*m_pSplitter).Height());
2780
0
    const tools::Long nHeight = rAllocation.Height() - aSplitterSize.Height();
2781
2782
0
    tools::Long nFirstHeight = 0;
2783
0
    tools::Long nSecondHeight = 0;
2784
0
    bool bFirstCanResize = true;
2785
0
    bool bSecondCanResize = true;
2786
0
    const bool bInitialAllocation = get_position() < 0;
2787
0
    int nElement = 0;
2788
0
    for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2789
0
        pChild = pChild->GetWindow(GetWindowType::Next))
2790
0
    {
2791
0
        if (!pChild->IsVisible())
2792
0
            continue;
2793
0
        if (nElement == 1)
2794
0
        {
2795
0
            if (bInitialAllocation)
2796
0
                nFirstHeight = getLayoutRequisition(*pChild).Height();
2797
0
            else
2798
0
                nFirstHeight = pChild->GetSizePixel().Height() + pChild->get_margin_top() + pChild->get_margin_bottom();
2799
0
            bFirstCanResize = pChild->get_expand();
2800
0
        }
2801
0
        else if (nElement == 2)
2802
0
        {
2803
0
            if (bInitialAllocation)
2804
0
                nSecondHeight = getLayoutRequisition(*pChild).Height();
2805
0
            else
2806
0
                nSecondHeight = pChild->GetSizePixel().Height() + pChild->get_margin_top() + pChild->get_margin_bottom();
2807
0
            bSecondCanResize = pChild->get_expand();
2808
0
        }
2809
0
        ++nElement;
2810
0
    }
2811
0
    tools::Long nHeightRequest = nFirstHeight + nSecondHeight;
2812
0
    tools::Long nHeightDiff = nHeight - nHeightRequest;
2813
0
    if (bFirstCanResize == bSecondCanResize)
2814
0
        nFirstHeight += nHeightDiff/2;
2815
0
    else if (bFirstCanResize)
2816
0
        nFirstHeight += nHeightDiff;
2817
0
    arrange(rAllocation, nFirstHeight, rAllocation.Height() - nFirstHeight - aSplitterSize.Height());
2818
0
}
2819
2820
Size VclVPaned::calculateRequisition() const
2821
0
{
2822
0
    Size aRet(0, 0);
2823
2824
0
    for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2825
0
        pChild = pChild->GetWindow(GetWindowType::Next))
2826
0
    {
2827
0
        if (!pChild->IsVisible())
2828
0
            continue;
2829
0
        Size aChildSize = getLayoutRequisition(*pChild);
2830
0
        aRet.setWidth( std::max(aRet.Width(), aChildSize.Width()) );
2831
0
        aRet.AdjustHeight(aChildSize.Height() );
2832
0
    }
2833
2834
0
    return aRet;
2835
0
}
2836
2837
VclHPaned::VclHPaned(vcl::Window *pParent)
2838
0
    : VclPaned(pParent, false)
2839
0
{
2840
0
    m_pSplitter->SetSplitHdl(LINK(this, VclHPaned, SplitHdl));
2841
0
}
Unexecuted instantiation: VclHPaned::VclHPaned(vcl::Window*)
Unexecuted instantiation: VclHPaned::VclHPaned(vcl::Window*)
2842
2843
IMPL_LINK(VclHPaned, SplitHdl, Splitter*, pSplitter, void)
2844
0
{
2845
0
    tools::Long nSize = pSplitter->GetSplitPosPixel();
2846
0
    Size aSplitterSize(m_pSplitter->GetSizePixel());
2847
0
    Size aAllocation(GetSizePixel());
2848
0
    arrange(aAllocation, nSize, aAllocation.Width() - nSize - aSplitterSize.Width());
2849
0
}
2850
2851
void VclHPaned::arrange(const Size& rAllocation, tools::Long nFirstWidth, tools::Long nSecondWidth)
2852
0
{
2853
0
    Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height());
2854
0
    Size aFirstChildSize(nFirstWidth, rAllocation.Height());
2855
0
    Size aSecondChildSize(nSecondWidth, rAllocation.Height());
2856
0
    int nElement = 0;
2857
0
    for (vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2858
0
        pChild = pChild->GetWindow(GetWindowType::Next))
2859
0
    {
2860
0
        if (!pChild->IsVisible())
2861
0
            continue;
2862
0
        if (nElement == 0)
2863
0
        {
2864
0
            Point aSplitterPos(aFirstChildSize.Width(), 0);
2865
0
            setLayoutAllocation(*m_pSplitter, aSplitterPos, aSplitterSize);
2866
0
            m_nPosition = aSplitterPos.X() + aSplitterSize.Width() / 2;
2867
0
        }
2868
0
        else if (nElement == 1)
2869
0
        {
2870
0
            Point aChildPos(0, 0);
2871
0
            setLayoutAllocation(*pChild, aChildPos, aFirstChildSize);
2872
0
        }
2873
0
        else if (nElement == 2)
2874
0
        {
2875
0
            Point aChildPos(aFirstChildSize.Width() + aSplitterSize.Width(), 0);
2876
0
            setLayoutAllocation(*pChild, aChildPos, aSecondChildSize);
2877
0
        }
2878
0
        ++nElement;
2879
0
    }
2880
0
}
2881
2882
void VclHPaned::set_position(tools::Long nPosition)
2883
0
{
2884
0
    VclPaned::set_position(nPosition);
2885
2886
0
    Size aAllocation(GetSizePixel());
2887
0
    Size aSplitterSize(m_pSplitter->GetSizePixel());
2888
2889
0
    nPosition -= aSplitterSize.Width() / 2;
2890
2891
0
    arrange(aAllocation, nPosition, aAllocation.Width() - nPosition - aSplitterSize.Width());
2892
0
}
2893
2894
void VclHPaned::setAllocation(const Size& rAllocation)
2895
0
{
2896
    //supporting "shrink" could be done by adjusting the allowed drag rectangle
2897
0
    m_pSplitter->SetDragRectPixel(tools::Rectangle(Point(0, 0), rAllocation));
2898
0
    Size aSplitterSize(getLayoutRequisition(*m_pSplitter).Width(), rAllocation.Height());
2899
0
    const tools::Long nWidth = rAllocation.Width() - aSplitterSize.Width();
2900
2901
0
    tools::Long nFirstWidth = 0;
2902
0
    tools::Long nSecondWidth = 0;
2903
0
    bool bFirstCanResize = true;
2904
0
    bool bSecondCanResize = true;
2905
0
    const bool bInitialAllocation = get_position() < 0;
2906
0
    int nElement = 0;
2907
0
    for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2908
0
        pChild = pChild->GetWindow(GetWindowType::Next))
2909
0
    {
2910
0
        if (!pChild->IsVisible())
2911
0
            continue;
2912
0
        if (nElement == 1)
2913
0
        {
2914
0
            if (bInitialAllocation)
2915
0
                nFirstWidth = getLayoutRequisition(*pChild).Width();
2916
0
            else
2917
0
                nFirstWidth = pChild->GetSizePixel().Width() + pChild->get_margin_start() + pChild->get_margin_end();
2918
0
            bFirstCanResize = pChild->get_expand();
2919
0
        }
2920
0
        else if (nElement == 2)
2921
0
        {
2922
0
            if (bInitialAllocation)
2923
0
                nSecondWidth = getLayoutRequisition(*pChild).Width();
2924
0
            else
2925
0
                nSecondWidth = pChild->GetSizePixel().Width() + pChild->get_margin_start() + pChild->get_margin_end();
2926
0
            bSecondCanResize = pChild->get_expand();
2927
0
        }
2928
0
        ++nElement;
2929
0
    }
2930
0
    tools::Long nWidthRequest = nFirstWidth + nSecondWidth;
2931
0
    tools::Long nWidthDiff = nWidth - nWidthRequest;
2932
0
    if (bFirstCanResize == bSecondCanResize)
2933
0
        nFirstWidth += nWidthDiff/2;
2934
0
    else if (bFirstCanResize)
2935
0
        nFirstWidth += nWidthDiff;
2936
0
    arrange(rAllocation, nFirstWidth, rAllocation.Width() - nFirstWidth - aSplitterSize.Width());
2937
0
}
2938
2939
Size VclHPaned::calculateRequisition() const
2940
0
{
2941
0
    Size aRet(0, 0);
2942
2943
0
    for (const vcl::Window* pChild = GetWindow(GetWindowType::FirstChild); pChild;
2944
0
        pChild = pChild->GetWindow(GetWindowType::Next))
2945
0
    {
2946
0
        if (!pChild->IsVisible())
2947
0
            continue;
2948
0
        Size aChildSize = getLayoutRequisition(*pChild);
2949
0
        aRet.setHeight( std::max(aRet.Height(), aChildSize.Height()) );
2950
0
        aRet.AdjustWidth(aChildSize.Width() );
2951
0
    }
2952
2953
0
    return aRet;
2954
0
}
2955
2956
Size getLegacyBestSizeForChildren(const vcl::Window &rWindow)
2957
0
{
2958
0
    tools::Rectangle aBounds;
2959
2960
0
    for (const vcl::Window* pChild = rWindow.GetWindow(GetWindowType::FirstChild); pChild;
2961
0
        pChild = pChild->GetWindow(GetWindowType::Next))
2962
0
    {
2963
0
        if (!pChild->IsVisible())
2964
0
            continue;
2965
2966
0
        tools::Rectangle aChildBounds(pChild->GetPosPixel(), pChild->GetSizePixel());
2967
0
        aBounds.Union(aChildBounds);
2968
0
    }
2969
2970
0
    if (aBounds.IsEmpty())
2971
0
        return rWindow.GetSizePixel();
2972
2973
0
    Size aRet(aBounds.GetSize());
2974
0
    Point aTopLeft(aBounds.TopLeft());
2975
0
    aRet.AdjustWidth(aTopLeft.X()*2 );
2976
0
    aRet.AdjustHeight(aTopLeft.Y()*2 );
2977
2978
0
    return aRet;
2979
0
}
2980
2981
vcl::Window* getNonLayoutParent(vcl::Window *pWindow)
2982
0
{
2983
0
    while (pWindow)
2984
0
    {
2985
0
        pWindow = pWindow->GetParent();
2986
0
        if (!pWindow || !isContainerWindow(*pWindow))
2987
0
            break;
2988
0
    }
2989
0
    return pWindow;
2990
0
}
2991
2992
bool isVisibleInLayout(const vcl::Window *pWindow)
2993
0
{
2994
0
    bool bVisible = true;
2995
0
    while (bVisible)
2996
0
    {
2997
0
        bVisible = pWindow->IsVisible();
2998
0
        pWindow = pWindow->GetParent();
2999
0
        if (!pWindow || !isContainerWindow(*pWindow))
3000
0
            break;
3001
0
    }
3002
0
    return bVisible;
3003
0
}
3004
3005
bool isEnabledInLayout(const vcl::Window *pWindow)
3006
0
{
3007
0
    bool bEnabled = true;
3008
0
    while (bEnabled)
3009
0
    {
3010
0
        bEnabled = pWindow->IsEnabled();
3011
0
        pWindow = pWindow->GetParent();
3012
0
        if (!pWindow || !isContainerWindow(*pWindow))
3013
0
            break;
3014
0
    }
3015
0
    return bEnabled;
3016
0
}
3017
3018
bool isLayoutEnabled(const vcl::Window *pWindow)
3019
46.0k
{
3020
    //Child is a container => we're layout enabled
3021
46.0k
    const vcl::Window *pChild = pWindow ? pWindow->GetWindow(GetWindowType::FirstChild) : nullptr;
3022
46.0k
    return pChild && isContainerWindow(*pChild) && !pChild->GetWindow(GetWindowType::Next);
3023
46.0k
}
3024
3025
void VclDrawingArea::RequestHelp(const HelpEvent& rHelpEvent)
3026
0
{
3027
0
    if (!(rHelpEvent.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON)))
3028
0
        return;
3029
3030
0
    Point aPos(ScreenToOutputPixel(rHelpEvent.GetMousePosPixel()));
3031
0
    tools::Rectangle aHelpArea(aPos.X(), aPos.Y());
3032
0
    OUString sHelpTip = m_aQueryTooltipHdl.Call(aHelpArea);
3033
0
    if (sHelpTip.isEmpty())
3034
0
    {
3035
0
        Control::RequestHelp(rHelpEvent);
3036
0
        return;
3037
0
    }
3038
0
    Point aPt = OutputToScreenPixel(aHelpArea.TopLeft());
3039
0
    aHelpArea.SetLeft(aPt.X());
3040
0
    aHelpArea.SetTop(aPt.Y());
3041
0
    aPt = OutputToScreenPixel(aHelpArea.BottomRight());
3042
0
    aHelpArea.SetRight(aPt.X());
3043
0
    aHelpArea.SetBottom(aPt.Y());
3044
    // tdf#125369 recover newline support of tdf#101779
3045
0
    QuickHelpFlags eHelpWinStyle = sHelpTip.indexOf('\n') != -1 ? QuickHelpFlags::TipStyleBalloon : QuickHelpFlags::NONE;
3046
0
    Help::ShowQuickHelp(this, aHelpArea, sHelpTip, eHelpWinStyle);
3047
0
}
3048
3049
void VclDrawingArea::StartDrag(sal_Int8, const Point&)
3050
0
{
3051
0
    if (m_aStartDragHdl.Call(this))
3052
0
        return;
3053
3054
0
    if (!m_xTransferHelper.is())
3055
0
        return;
3056
3057
0
    m_xTransferHelper->StartDrag(this, m_nDragAction);
3058
0
}
3059
3060
OUString VclDrawingArea::GetSurroundingText() const
3061
0
{
3062
0
    if (!m_aGetSurroundingHdl.IsSet())
3063
0
        return Control::GetSurroundingText();
3064
0
    OUString sSurroundingText;
3065
0
    m_aGetSurroundingHdl.Call(sSurroundingText);
3066
0
    return sSurroundingText;
3067
0
}
3068
3069
Selection VclDrawingArea::GetSurroundingTextSelection() const
3070
0
{
3071
0
    if (!m_aGetSurroundingHdl.IsSet())
3072
0
        return Control::GetSurroundingTextSelection();
3073
0
    OUString sSurroundingText;
3074
0
    int nCursor = m_aGetSurroundingHdl.Call(sSurroundingText);
3075
0
    return Selection(nCursor, nCursor);
3076
0
}
3077
3078
bool VclDrawingArea::DeleteSurroundingText(const Selection& rSelection)
3079
0
{
3080
0
    if (!m_aDeleteSurroundingHdl.IsSet())
3081
0
        return Control::DeleteSurroundingText(rSelection);
3082
0
    return m_aDeleteSurroundingHdl.Call(rSelection);
3083
0
}
3084
3085
VclHPaned::~VclHPaned()
3086
0
{
3087
0
}
3088
3089
VclVPaned::~VclVPaned()
3090
0
{
3091
0
}
3092
3093
VclPaned::~VclPaned()
3094
0
{
3095
0
    disposeOnce();
3096
0
}
3097
3098
VclScrolledWindow::~VclScrolledWindow()
3099
0
{
3100
0
    disposeOnce();
3101
0
}
3102
3103
void VclDrawingArea::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
3104
0
{
3105
0
    Control::DumpAsPropertyTree(rJsonWriter);
3106
0
    rJsonWriter.put("type", "drawingarea");
3107
3108
0
    ScopedVclPtrInstance<VirtualDevice> pDevice;
3109
0
    OutputDevice* pRefDevice = GetOutDev();
3110
0
    Size aRenderSize(pRefDevice->PixelToLogic(GetOutputSizePixel()));
3111
0
    Size aOutputSize = GetSizePixel();
3112
0
    pDevice->SetOutputSize(aRenderSize);
3113
0
    tools::Rectangle aRect(Point(0,0), aRenderSize);
3114
3115
    // Dark mode support
3116
0
    pDevice->DrawWallpaper(aRect, pRefDevice->GetBackground());
3117
3118
0
    Paint(*pDevice, aRect);
3119
3120
0
    Bitmap aImage = pDevice->GetBitmap(Point(0,0), aRenderSize);
3121
0
    aImage.Scale(aOutputSize);
3122
0
    rJsonWriter.put("imagewidth", aRenderSize.Width());
3123
0
    rJsonWriter.put("imageheight", aRenderSize.Height());
3124
3125
0
    SvMemoryStream aOStm(65535, 65535);
3126
0
    if(GraphicConverter::Export(aOStm, aImage, ConvertDataFormat::PNG) == ERRCODE_NONE)
3127
0
    {
3128
0
        css::uno::Sequence<sal_Int8> aSeq( static_cast<sal_Int8 const *>(aOStm.GetData()), aOStm.Tell());
3129
0
        OStringBuffer aBuffer("data:image/png;base64,");
3130
0
        ::comphelper::Base64::encode(aBuffer, aSeq);
3131
0
        rJsonWriter.put("image", aBuffer);
3132
0
    }
3133
0
    rJsonWriter.put("text", GetQuickHelpText());
3134
0
}
3135
3136
FactoryFunction VclDrawingArea::GetUITestFactory() const
3137
0
{
3138
0
    if (m_pFactoryFunction)
3139
0
        return m_pFactoryFunction;
3140
0
    return DrawingAreaUIObject::create;
3141
0
}
3142
3143
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */