Coverage Report

Created: 2026-09-28 10:59

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