Coverage Report

Created: 2025-11-16 09:57

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