Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/vcl/source/control/combobox.cxx
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <vcl/toolkit/combobox.hxx>
21
22
#include <set>
23
24
#include <comphelper/string.hxx>
25
26
#include <vcl/builder.hxx>
27
#include <vcl/commandevent.hxx>
28
#include <vcl/event.hxx>
29
#include <vcl/uitest/uiobject.hxx>
30
31
#include <accessibility/vclxaccessibledropdowncombobox.hxx>
32
#include <accessibility/vclxaccessiblecombobox.hxx>
33
#include <listbox.hxx>
34
#include <comphelper/lok.hxx>
35
#include <o3tl/string_view.hxx>
36
37
struct ComboBoxBounds
38
{
39
    Point aSubEditPos;
40
    Size aSubEditSize;
41
42
    Point aButtonPos;
43
    Size aButtonSize;
44
};
45
46
static void lcl_GetSelectedEntries( ::std::set< sal_Int32 >& rSelectedPos, std::u16string_view rText, sal_Unicode cTokenSep, const ImplEntryList& rEntryList )
47
0
{
48
0
    if (rText.empty())
49
0
        return;
50
51
0
    sal_Int32 nIdx{0};
52
0
    do {
53
0
        const sal_Int32 nPos = rEntryList.FindEntry(comphelper::string::strip(o3tl::getToken(rText, 0, cTokenSep, nIdx), ' '));
54
0
        if ( nPos != LISTBOX_ENTRY_NOTFOUND )
55
0
            rSelectedPos.insert( nPos );
56
0
    } while (nIdx>=0);
57
0
}
58
59
ComboBox::ComboBox(vcl::Window *const pParent, WinBits const nStyle)
60
0
    : Edit( WindowType::COMBOBOX )
61
0
    , m_nDDHeight(0)
62
0
    , m_cMultiSep(0)
63
0
    , m_isDDAutoSize(false)
64
0
    , m_isSyntheticModify(false)
65
0
    , m_isKeyBoardModify(false)
66
0
    , m_isMatchCase(false)
67
0
    , m_nMaxWidthChars(0)
68
0
    , m_nWidthInChars(-1)
69
0
{
70
0
    ImplInitComboBoxData();
71
0
    ImplInit( pParent, nStyle );
72
0
    SetWidthInChars(-1);
73
0
}
Unexecuted instantiation: ComboBox::ComboBox(vcl::Window*, long)
Unexecuted instantiation: ComboBox::ComboBox(vcl::Window*, long)
74
75
ComboBox::~ComboBox()
76
0
{
77
0
    disposeOnce();
78
0
}
79
80
void ComboBox::dispose()
81
0
{
82
0
    m_pSubEdit.disposeAndClear();
83
84
0
    VclPtr< ImplListBox > pImplLB = m_pImplLB;
85
0
    m_pImplLB.reset();
86
0
    pImplLB.disposeAndClear();
87
88
0
    m_pFloatWin.disposeAndClear();
89
0
    m_pBtn.disposeAndClear();
90
0
    Edit::dispose();
91
0
}
92
93
void ComboBox::ImplInitComboBoxData()
94
0
{
95
0
    m_pSubEdit.disposeAndClear();
96
0
    m_pBtn              = nullptr;
97
0
    m_pImplLB           = nullptr;
98
0
    m_pFloatWin         = nullptr;
99
100
0
    m_nDDHeight         = 0;
101
0
    m_isDDAutoSize      = true;
102
0
    m_isSyntheticModify = false;
103
0
    m_isKeyBoardModify  = false;
104
0
    m_isMatchCase       = false;
105
0
    m_cMultiSep         = ';';
106
0
    m_nMaxWidthChars    = -1;
107
0
    m_nWidthInChars     = -1;
108
0
}
109
110
void ComboBox::ImplCalcEditHeight()
111
0
{
112
0
    sal_Int32 nLeft, nTop, nRight, nBottom;
113
0
    GetBorder( nLeft, nTop, nRight, nBottom );
114
0
    m_nDDHeight = static_cast<sal_uInt16>(m_pSubEdit->GetTextHeight() + nTop + nBottom + 4);
115
0
    if ( !IsDropDownBox() )
116
0
        m_nDDHeight += 4;
117
118
0
    const tools::Rectangle aCtrlRegion(Point(0, 0), Size(10, 10));
119
0
    tools::Rectangle aBoundRegion;
120
0
    tools::Rectangle aContentRegion;
121
0
    ImplControlValue aControlValue;
122
0
    const ControlType eType = IsDropDownBox() ? ControlType::Combobox : ControlType::Editbox;
123
124
0
    if( GetNativeControlRegion( eType, ControlPart::Entire,
125
0
                                aCtrlRegion,
126
0
                                ControlState::ENABLED,
127
0
                                aControlValue,
128
0
                                aBoundRegion, aContentRegion ) )
129
0
    {
130
0
        const tools::Long nNCHeight = aBoundRegion.GetHeight();
131
0
        if (m_nDDHeight < nNCHeight)
132
0
            m_nDDHeight = sal::static_int_cast<sal_uInt16>(nNCHeight);
133
0
    }
134
0
}
135
136
void ComboBox::ImplInit(vcl::Window* pParent, WinBits eStyle)
137
0
{
138
0
    const bool bNoBorder = (eStyle & WB_NOBORDER) != 0;
139
140
0
    if (!(eStyle & WB_DROPDOWN))
141
0
    {
142
0
        eStyle &= ~WB_BORDER;
143
0
        eStyle |= WB_NOBORDER;
144
0
    }
145
0
    else
146
0
    {
147
0
        if ( !bNoBorder )
148
0
            eStyle |= WB_BORDER;
149
0
    }
150
151
0
    Edit::ImplInit(pParent, eStyle);
152
0
    SetBackground();
153
154
    // DropDown ?
155
0
    WinBits eEditStyle = eStyle & (WB_LEFT | WB_RIGHT | WB_CENTER);
156
0
    WinBits eListStyle = eStyle;
157
0
    if (eStyle & WB_DROPDOWN)
158
0
    {
159
0
        m_pFloatWin = VclPtr<ImplListBoxFloatingWindow>::Create( this );
160
        // For Kit jsdialogs we don't need or want a buffer the size of
161
        // the ComboBox dropdown taking up memory which is unnecessary
162
        // in that case.
163
0
        if (!comphelper::LibreOfficeKit::isActive())
164
0
        {
165
0
            if (!IsNativeControlSupported(ControlType::Pushbutton, ControlPart::Focus))
166
0
                m_pFloatWin->RequestDoubleBuffering(true);
167
0
        }
168
0
        m_pFloatWin->SetAutoWidth( true );
169
0
        m_pFloatWin->SetPopupModeEndHdl(LINK(this, ComboBox, ImplPopupModeEndHdl));
170
171
0
        m_pBtn = VclPtr<ImplBtn>::Create( this, WB_NOLIGHTBORDER | WB_RECTSTYLE );
172
0
        ImplInitDropDownButton( m_pBtn );
173
0
        m_pBtn->SetMBDownHdl(LINK(this, ComboBox, ImplClickBtnHdl));
174
0
        m_pBtn->Show();
175
176
0
        eEditStyle |= WB_NOBORDER;
177
0
        eListStyle &= ~WB_BORDER;
178
0
        eListStyle |= WB_NOBORDER;
179
0
    }
180
0
    else
181
0
    {
182
0
        if ( !bNoBorder )
183
0
        {
184
0
            eEditStyle |= WB_BORDER;
185
0
            eListStyle &= ~WB_NOBORDER;
186
0
            eListStyle |= WB_BORDER;
187
0
        }
188
0
    }
189
190
0
    m_pSubEdit.reset(VclPtr<Edit>::Create(this, eEditStyle));
191
0
    m_pSubEdit->EnableRTL( false );
192
0
    SetSubEdit( m_pSubEdit );
193
0
    m_pSubEdit->SetPosPixel( Point() );
194
0
    EnableAutocomplete( true );
195
0
    m_pSubEdit->Show();
196
197
0
    vcl::Window* pLBParent = this;
198
0
    if (m_pFloatWin)
199
0
        pLBParent = m_pFloatWin;
200
0
    m_pImplLB = VclPtr<ImplListBox>::Create(pLBParent, eListStyle | WB_SIMPLEMODE | WB_AUTOHSCROLL);
201
0
    m_pImplLB->SetPosPixel( Point() );
202
0
    m_pImplLB->SetSelectHdl(LINK(this, ComboBox, ImplSelectHdl));
203
0
    m_pImplLB->SetCancelHdl( LINK(this, ComboBox, ImplCancelHdl));
204
0
    m_pImplLB->SetDoubleClickHdl(LINK(this, ComboBox, ImplDoubleClickHdl));
205
0
    m_pImplLB->SetSelectionChangedHdl(LINK(this, ComboBox, ImplSelectionChangedHdl));
206
0
    m_pImplLB->SetListItemSelectHdl(LINK(this, ComboBox, ImplListItemSelectHdl));
207
0
    m_pImplLB->Show();
208
209
0
    if (m_pFloatWin)
210
0
        m_pFloatWin->SetImplListBox(m_pImplLB);
211
0
    else
212
0
        GetMainWindow()->AllowGrabFocus( true );
213
214
0
    ImplCalcEditHeight();
215
216
0
    SetCompoundControl( true );
217
0
}
218
219
WinBits ComboBox::ImplInitStyle(WinBits eStyle)
220
0
{
221
0
    if (!(eStyle & WB_NOTABSTOP))
222
0
        eStyle |= WB_TABSTOP;
223
224
0
    if (!(eStyle & WB_NOGROUP))
225
0
        eStyle |= WB_GROUP;
226
227
0
    return eStyle;
228
0
}
229
230
void ComboBox::EnableAutocomplete( bool bEnable, bool bMatchCase )
231
0
{
232
0
    m_isMatchCase = bMatchCase;
233
234
0
    if ( bEnable )
235
0
        m_pSubEdit->SetAutocompleteHdl(LINK(this, ComboBox, ImplAutocompleteHdl));
236
0
    else
237
0
        m_pSubEdit->SetAutocompleteHdl( Link<Edit&,void>() );
238
0
}
239
240
bool ComboBox::IsAutocompleteEnabled() const
241
0
{
242
0
    return m_pSubEdit->GetAutocompleteHdl().IsSet();
243
0
}
244
245
IMPL_LINK_NOARG(ComboBox, ImplClickBtnHdl, void*, void)
246
0
{
247
0
    CallEventListeners( VclEventId::DropdownPreOpen );
248
0
    m_pSubEdit->GrabFocus();
249
0
    if (!m_pImplLB->GetEntryList().GetMRUCount())
250
0
        ImplUpdateFloatSelection();
251
0
    else
252
0
        m_pImplLB->SelectEntry( 0 , true );
253
0
    m_pBtn->SetPressed( true );
254
0
    SetSelection( Selection( 0, SELECTION_MAX ) );
255
0
    m_pFloatWin->StartFloat( true );
256
0
    CallEventListeners( VclEventId::DropdownOpen );
257
258
0
    ImplClearLayoutData();
259
0
    if (m_pImplLB)
260
0
        m_pImplLB->GetMainWindow()->ImplClearLayoutData();
261
0
}
262
263
IMPL_LINK_NOARG(ComboBox, ImplPopupModeEndHdl, FloatingWindow*, void)
264
0
{
265
0
    if (m_pFloatWin->IsPopupModeCanceled())
266
0
    {
267
0
        if (!m_pImplLB->GetEntryList().IsEntryPosSelected(
268
0
                    m_pFloatWin->GetPopupModeStartSaveSelection()))
269
0
        {
270
0
            m_pImplLB->SelectEntry(m_pFloatWin->GetPopupModeStartSaveSelection(), true);
271
0
            const bool bTravelSelect = m_pImplLB->IsTravelSelect();
272
0
            m_pImplLB->SetTravelSelect( true );
273
0
            Select();
274
0
            m_pImplLB->SetTravelSelect( bTravelSelect );
275
0
        }
276
0
    }
277
278
0
    ImplClearLayoutData();
279
0
    if (m_pImplLB)
280
0
        m_pImplLB->GetMainWindow()->ImplClearLayoutData();
281
282
0
    m_pBtn->SetPressed( false );
283
0
    CallEventListeners( VclEventId::DropdownClose );
284
0
}
285
286
IMPL_LINK(ComboBox, ImplAutocompleteHdl, Edit&, rEdit, void)
287
0
{
288
0
    const Selection aSel = rEdit.GetSelection();
289
290
0
    {
291
0
        const OUString aFullText = rEdit.GetText();
292
0
        const OUString aStartText = aFullText.copy( 0, static_cast<sal_Int32>(aSel.Max()) );
293
0
        sal_Int32 nStart = m_pImplLB->GetCurrentPos();
294
295
0
        if ( nStart == LISTBOX_ENTRY_NOTFOUND )
296
0
            nStart = 0;
297
298
0
        sal_Int32 nPos = LISTBOX_ENTRY_NOTFOUND;
299
0
        if (!m_isMatchCase)
300
0
        {
301
            // Try match case insensitive from current position
302
0
            nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, nStart, true);
303
0
            if ( nPos == LISTBOX_ENTRY_NOTFOUND )
304
                // Try match case insensitive, but from start
305
0
                nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, 0, true);
306
0
        }
307
308
0
        if ( nPos == LISTBOX_ENTRY_NOTFOUND )
309
            // Try match full from current position
310
0
            nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, nStart, false);
311
0
        if ( nPos == LISTBOX_ENTRY_NOTFOUND )
312
            //  Match full, but from start
313
0
            nPos = m_pImplLB->GetEntryList().FindMatchingEntry(aStartText, 0, false);
314
315
0
        if ( nPos != LISTBOX_ENTRY_NOTFOUND )
316
0
        {
317
0
            const OUString aText = m_pImplLB->GetEntryList().GetEntryText( nPos );
318
0
            Selection aSelection( aText.getLength(), aStartText.getLength() );
319
0
            rEdit.SetText( aText, aSelection );
320
0
        }
321
0
    }
322
0
}
323
324
IMPL_LINK_NOARG(ComboBox, ImplSelectHdl, LinkParamNone*, void)
325
0
{
326
0
    const bool bPopup = IsInDropDown();
327
0
    bool bCallSelect = false;
328
0
    if (m_pImplLB->IsSelectionChanged() || bPopup)
329
0
    {
330
0
        OUString aText;
331
0
        if (IsMultiSelectionEnabled())
332
0
        {
333
0
            aText = m_pSubEdit->GetText();
334
335
            // remove all entries to which there is an entry, but which is not selected
336
0
            sal_Int32 nIndex = 0;
337
0
            while ( nIndex >= 0 )
338
0
            {
339
0
                const sal_Int32 nPrevIndex = nIndex;
340
0
                std::u16string_view aToken = o3tl::getToken(aText, 0, m_cMultiSep, nIndex );
341
0
                const sal_Int32 nTokenLen = aToken.size();
342
0
                aToken = comphelper::string::strip(aToken, ' ');
343
0
                const sal_Int32 nP = m_pImplLB->GetEntryList().FindEntry( aToken );
344
0
                if ((nP != LISTBOX_ENTRY_NOTFOUND) && (!m_pImplLB->GetEntryList().IsEntryPosSelected(nP)))
345
0
                {
346
0
                    aText = aText.replaceAt( nPrevIndex, nTokenLen, u"" );
347
0
                    nIndex = nIndex - nTokenLen;
348
0
                    sal_Int32 nSepCount=0;
349
0
                    if ((nPrevIndex+nSepCount < aText.getLength()) && (aText[nPrevIndex+nSepCount] == m_cMultiSep))
350
0
                    {
351
0
                        nIndex--;
352
0
                        ++nSepCount;
353
0
                    }
354
0
                    aText = aText.replaceAt( nPrevIndex, nSepCount, u"" );
355
0
                }
356
0
                aText = comphelper::string::strip(aText, ' ');
357
0
            }
358
359
            // attach missing entries
360
0
            ::std::set< sal_Int32 > aSelInText;
361
0
            lcl_GetSelectedEntries( aSelInText, aText, m_cMultiSep, m_pImplLB->GetEntryList() );
362
0
            const sal_Int32 nSelectedEntries = m_pImplLB->GetEntryList().GetSelectedEntryCount();
363
0
            for ( sal_Int32 n = 0; n < nSelectedEntries; n++ )
364
0
            {
365
0
                sal_Int32 nP = m_pImplLB->GetEntryList().GetSelectedEntryPos( n );
366
0
                if ( !aSelInText.count( nP ) )
367
0
                {
368
0
                    if (!aText.isEmpty() && (aText[aText.getLength()-1] != m_cMultiSep))
369
0
                        aText += OUStringChar(m_cMultiSep);
370
0
                    if ( !aText.isEmpty() )
371
0
                        aText += " ";   // slightly loosen
372
0
                    aText += m_pImplLB->GetEntryList().GetEntryText( nP ) +
373
0
                        OUStringChar(m_cMultiSep);
374
0
                }
375
0
            }
376
0
            aText = comphelper::string::stripEnd( aText, m_cMultiSep );
377
0
        }
378
0
        else
379
0
        {
380
0
            aText = m_pImplLB->GetEntryList().GetSelectedEntry( 0 );
381
0
        }
382
383
0
        m_pSubEdit->SetText( aText );
384
385
0
        switch (GetSettings().GetStyleSettings().GetComboBoxTextSelectionMode())
386
0
        {
387
0
            case ComboBoxTextSelectionMode::SelectText:
388
0
            {
389
0
                Selection aNewSelection(0, aText.getLength());
390
0
                if (IsMultiSelectionEnabled())
391
0
                    aNewSelection.Min() = aText.getLength();
392
0
                m_pSubEdit->SetSelection(aNewSelection);
393
0
                break;
394
0
            }
395
0
            case ComboBoxTextSelectionMode::CursorToStart:
396
0
            {
397
0
                Selection aNewSelection(0, 0);
398
0
                m_pSubEdit->SetSelection(aNewSelection);
399
0
                break;
400
0
            }
401
0
            case ComboBoxTextSelectionMode::CursorToEnd:
402
0
            {
403
0
                m_pSubEdit->SetCursorAtLast();
404
0
                break;
405
0
            }
406
0
            default:
407
0
                assert(false && "Unhandled ComboBoxTextSelectionMode case");
408
0
                break;
409
0
        }
410
411
0
        bCallSelect = true;
412
0
    }
413
414
    // #84652# Call GrabFocus and EndPopupMode before calling Select/Modify, but after changing the text
415
0
    bool bMenuSelect = bPopup && !m_pImplLB->IsTravelSelect() && (!IsMultiSelectionEnabled() || !m_pImplLB->GetSelectModifier());
416
0
    if (bMenuSelect)
417
0
    {
418
0
        m_pFloatWin->EndPopupMode();
419
0
        GrabFocus();
420
0
    }
421
422
0
    if ( bCallSelect )
423
0
    {
424
0
        m_isKeyBoardModify = !bMenuSelect;
425
0
        m_pSubEdit->SetModifyFlag();
426
0
        m_isSyntheticModify = true;
427
0
        Modify();
428
0
        m_isSyntheticModify = false;
429
0
        Select();
430
0
        m_isKeyBoardModify = false;
431
0
    }
432
0
}
433
434
bool ComboBox::IsSyntheticModify() const
435
0
{
436
0
    return m_isSyntheticModify;
437
0
}
438
439
bool ComboBox::IsModifyByKeyboard() const
440
0
{
441
0
    return m_isKeyBoardModify;
442
0
}
443
444
IMPL_LINK_NOARG(ComboBox, ImplListItemSelectHdl, LinkParamNone*, void)
445
0
{
446
0
    CallEventListeners(VclEventId::DropdownSelect);
447
0
}
448
449
IMPL_LINK_NOARG(ComboBox, ImplCancelHdl, LinkParamNone*, void)
450
0
{
451
0
    if (IsInDropDown())
452
0
        m_pFloatWin->EndPopupMode();
453
0
}
454
455
IMPL_LINK( ComboBox, ImplSelectionChangedHdl, sal_Int32, nChanged, void)
456
0
{
457
0
    if (!m_pImplLB->IsTrackingSelect())
458
0
    {
459
0
        if (!m_pSubEdit->IsReadOnly() && m_pImplLB->GetEntryList().IsEntryPosSelected(nChanged))
460
0
            m_pSubEdit->SetText(m_pImplLB->GetEntryList().GetEntryText(nChanged));
461
0
    }
462
0
}
463
464
IMPL_LINK_NOARG(ComboBox, ImplDoubleClickHdl, ImplListBoxWindow*, void)
465
0
{
466
0
    DoubleClick();
467
0
}
468
469
void ComboBox::ToggleDropDown()
470
0
{
471
0
    if( !IsDropDownBox() )
472
0
        return;
473
474
0
    if (m_pFloatWin->IsInPopupMode())
475
0
        m_pFloatWin->EndPopupMode();
476
0
    else
477
0
    {
478
0
        m_pSubEdit->GrabFocus();
479
0
        if (!m_pImplLB->GetEntryList().GetMRUCount())
480
0
            ImplUpdateFloatSelection();
481
0
        else
482
0
            m_pImplLB->SelectEntry( 0 , true );
483
0
        CallEventListeners( VclEventId::DropdownPreOpen );
484
0
        m_pBtn->SetPressed( true );
485
0
        SetSelection( Selection( 0, SELECTION_MAX ) );
486
0
        m_pFloatWin->StartFloat(true);
487
0
        CallEventListeners( VclEventId::DropdownOpen );
488
0
    }
489
0
}
490
491
void ComboBox::Select()
492
0
{
493
0
    ImplCallEventListenersAndHandler( VclEventId::ComboboxSelect, [this] () { m_SelectHdl.Call(*this); } );
494
0
}
495
496
void ComboBox::DoubleClick()
497
0
{
498
0
    ImplCallEventListenersAndHandler( VclEventId::ComboboxDoubleClick, [] () {} );
499
0
}
500
501
0
bool ComboBox::IsAutoSizeEnabled() const { return m_isDDAutoSize; }
502
503
void ComboBox::EnableAutoSize( bool bAuto )
504
0
{
505
0
    m_isDDAutoSize = bAuto;
506
0
    if (m_pFloatWin)
507
0
    {
508
0
        if (bAuto && !m_pFloatWin->GetDropDownLineCount())
509
0
        {
510
            // Adapt to GetListBoxMaximumLineCount here; was on fixed number of five before
511
0
            AdaptDropDownLineCountToMaximum();
512
0
        }
513
0
        else if ( !bAuto )
514
0
        {
515
0
            m_pFloatWin->SetDropDownLineCount(0);
516
0
        }
517
0
    }
518
0
}
519
520
void ComboBox::SetDropDownLineCount( sal_uInt16 nLines )
521
0
{
522
0
    if (m_pFloatWin)
523
0
        m_pFloatWin->SetDropDownLineCount(nLines);
524
0
}
525
526
void ComboBox::AdaptDropDownLineCountToMaximum()
527
0
{
528
    // Adapt to maximum allowed number.
529
    // Limit for LOK as we can't render outside of the dialog canvas.
530
0
    if (comphelper::LibreOfficeKit::isActive())
531
0
        SetDropDownLineCount(11);
532
0
    else
533
0
        SetDropDownLineCount(GetSettings().GetStyleSettings().GetListBoxMaximumLineCount());
534
0
}
535
536
sal_uInt16 ComboBox::GetDropDownLineCount() const
537
0
{
538
0
    sal_uInt16 nLines = 0;
539
0
    if (m_pFloatWin)
540
0
        nLines = m_pFloatWin->GetDropDownLineCount();
541
0
    return nLines;
542
0
}
543
544
void ComboBox::setPosSizePixel(tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight,
545
                               PosSizeFlags eFlags)
546
0
{
547
0
    if (IsDropDownBox() && (eFlags & PosSizeFlags::Size))
548
0
    {
549
0
        Size aPrefSz = m_pFloatWin->GetPrefSize();
550
0
        if ((eFlags & PosSizeFlags::Height) && (nHeight >= 2*m_nDDHeight))
551
0
            aPrefSz.setHeight( nHeight-m_nDDHeight );
552
0
        if (eFlags & PosSizeFlags::Width)
553
0
            aPrefSz.setWidth( nWidth );
554
0
        m_pFloatWin->SetPrefSize(aPrefSz);
555
556
0
        if (IsAutoSizeEnabled())
557
0
            nHeight = m_nDDHeight;
558
0
    }
559
560
0
    Edit::setPosSizePixel(nX, nY, nWidth, nHeight, eFlags);
561
0
}
562
563
void ComboBox::Resize()
564
0
{
565
0
    Control::Resize();
566
567
0
    if (m_pSubEdit)
568
0
    {
569
0
        Size aOutSz = GetOutputSizePixel();
570
0
        if( IsDropDownBox() )
571
0
        {
572
0
            ComboBoxBounds aBounds(calcComboBoxDropDownComponentBounds(aOutSz,
573
0
                GetWindow(GetWindowType::Border)->GetOutputSizePixel()));
574
0
            m_pSubEdit->SetPosSizePixel(aBounds.aSubEditPos, aBounds.aSubEditSize);
575
0
            m_pBtn->SetPosSizePixel(aBounds.aButtonPos, aBounds.aButtonSize);
576
0
        }
577
0
        else
578
0
        {
579
0
            m_pSubEdit->SetSizePixel(Size(aOutSz.Width(), m_nDDHeight));
580
0
            m_pImplLB->setPosSizePixel(0, m_nDDHeight,
581
0
                    aOutSz.Width(), aOutSz.Height() - m_nDDHeight);
582
0
            if ( !GetText().isEmpty() )
583
0
                ImplUpdateFloatSelection();
584
0
        }
585
0
    }
586
587
    // adjust the size of the FloatingWindow even when invisible
588
    // as KEY_PGUP/DOWN is being processed...
589
0
    if (m_pFloatWin)
590
0
        m_pFloatWin->SetSizePixel(m_pFloatWin->CalcFloatSize(m_pFloatWin->GetParentRect()));
591
0
}
592
593
0
bool ComboBox::IsDropDownBox() const { return m_pFloatWin != nullptr; }
594
595
void ComboBox::FillLayoutData() const
596
0
{
597
0
    mxLayoutData.emplace();
598
0
    AppendLayoutData( *m_pSubEdit );
599
0
    m_pSubEdit->SetLayoutDataParent( this );
600
0
    ImplListBoxWindow* rMainWindow = GetMainWindow();
601
0
    if (m_pFloatWin)
602
0
    {
603
        // dropdown mode
604
0
        if (m_pFloatWin->IsReallyVisible())
605
0
        {
606
0
            AppendLayoutData( *rMainWindow );
607
0
            rMainWindow->SetLayoutDataParent( this );
608
0
        }
609
0
    }
610
0
    else
611
0
    {
612
0
        AppendLayoutData( *rMainWindow );
613
0
        rMainWindow->SetLayoutDataParent( this );
614
0
    }
615
0
}
616
617
void ComboBox::StateChanged(StateChangedType eType)
618
0
{
619
0
    Edit::StateChanged(eType);
620
621
0
    if (eType == StateChangedType::ReadOnly)
622
0
    {
623
0
        m_pImplLB->SetReadOnly( IsReadOnly() );
624
0
        if (m_pBtn)
625
0
            m_pBtn->Enable( IsEnabled() && !IsReadOnly() );
626
0
    }
627
0
    else if (eType == StateChangedType::Enable)
628
0
    {
629
0
        m_pSubEdit->Enable( IsEnabled() );
630
0
        m_pImplLB->Enable( IsEnabled() && !IsReadOnly() );
631
0
        if (m_pBtn)
632
0
            m_pBtn->Enable( IsEnabled() && !IsReadOnly() );
633
0
        Invalidate();
634
0
    }
635
0
    else if (eType == StateChangedType::UpdateMode)
636
0
    {
637
0
        m_pImplLB->SetUpdateMode( IsUpdateMode() );
638
0
    }
639
0
    else if (eType == StateChangedType::Zoom)
640
0
    {
641
0
        m_pImplLB->SetZoom( GetZoom() );
642
0
        m_pSubEdit->SetZoom( GetZoom() );
643
0
        ImplCalcEditHeight();
644
0
        Resize();
645
0
    }
646
0
    else if (eType == StateChangedType::ControlFont)
647
0
    {
648
0
        m_pImplLB->SetControlFont( GetControlFont() );
649
0
        m_pSubEdit->SetControlFont( GetControlFont() );
650
0
        ImplCalcEditHeight();
651
0
        Resize();
652
0
    }
653
0
    else if (eType == StateChangedType::ControlForeground)
654
0
    {
655
0
        m_pImplLB->SetControlForeground( GetControlForeground() );
656
0
        m_pSubEdit->SetControlForeground( GetControlForeground() );
657
0
    }
658
0
    else if (eType == StateChangedType::ControlBackground)
659
0
    {
660
0
        m_pImplLB->SetControlBackground( GetControlBackground() );
661
0
        m_pSubEdit->SetControlBackground( GetControlBackground() );
662
0
    }
663
0
    else if (eType == StateChangedType::Style)
664
0
    {
665
0
        SetStyle( ImplInitStyle( GetStyle() ) );
666
0
        GetMainWindow()->EnableSort( ( GetStyle() & WB_SORT ) != 0 );
667
0
    }
668
0
    else if (eType == StateChangedType::Mirroring)
669
0
    {
670
0
        if (m_pBtn)
671
0
        {
672
0
            m_pBtn->EnableRTL( IsRTLEnabled() );
673
0
            ImplInitDropDownButton( m_pBtn );
674
0
        }
675
0
        m_pSubEdit->CompatStateChanged( StateChangedType::Mirroring );
676
0
        m_pImplLB->EnableRTL( IsRTLEnabled() );
677
0
        Resize();
678
0
    }
679
0
}
680
681
void ComboBox::DataChanged( const DataChangedEvent& rDCEvt )
682
0
{
683
0
    Control::DataChanged( rDCEvt );
684
685
0
    if ( !((rDCEvt.GetType() == DataChangedEventType::FONTS) ||
686
0
         (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
687
0
         ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
688
0
          (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) )
689
0
        return;
690
691
0
    if (m_pBtn)
692
0
    {
693
0
        m_pBtn->GetOutDev()->SetSettings( GetSettings() );
694
0
        ImplInitDropDownButton( m_pBtn );
695
0
    }
696
0
    Resize();
697
0
    m_pImplLB->Resize(); // not called by ComboBox::Resize() if ImplLB is unchanged
698
699
0
    SetBackground();    // due to a hack in Window::UpdateSettings the background must be reset
700
                        // otherwise it will overpaint NWF drawn comboboxes
701
0
}
702
703
bool ComboBox::EventNotify( NotifyEvent& rNEvt )
704
0
{
705
0
    bool bDone = false;
706
0
    if ((rNEvt.GetType() == NotifyEventType::KEYINPUT)
707
0
        && (rNEvt.GetWindow() == m_pSubEdit)
708
0
        && !IsReadOnly())
709
0
    {
710
0
        KeyEvent aKeyEvt = *rNEvt.GetKeyEvent();
711
0
        const sal_uInt16 nKeyCode = aKeyEvt.GetKeyCode().GetCode();
712
0
        switch( nKeyCode )
713
0
        {
714
0
            case KEY_UP:
715
0
            case KEY_DOWN:
716
0
            case KEY_PAGEUP:
717
0
            case KEY_PAGEDOWN:
718
0
            {
719
0
                ImplUpdateFloatSelection();
720
0
                if ((nKeyCode == KEY_DOWN) && m_pFloatWin
721
0
                    && !m_pFloatWin->IsInPopupMode()
722
0
                    && aKeyEvt.GetKeyCode().IsMod2())
723
0
                {
724
0
                    CallEventListeners( VclEventId::DropdownPreOpen );
725
0
                    m_pBtn->SetPressed( true );
726
0
                    if (m_pImplLB->GetEntryList().GetMRUCount())
727
0
                        m_pImplLB->SelectEntry( 0 , true );
728
0
                    SetSelection( Selection( 0, SELECTION_MAX ) );
729
0
                    m_pFloatWin->StartFloat(false);
730
0
                    CallEventListeners( VclEventId::DropdownOpen );
731
0
                    bDone = true;
732
0
                }
733
0
                else if ((nKeyCode == KEY_UP) && m_pFloatWin
734
0
                        && m_pFloatWin->IsInPopupMode()
735
0
                        && aKeyEvt.GetKeyCode().IsMod2())
736
0
                {
737
0
                    m_pFloatWin->EndPopupMode();
738
0
                    bDone = true;
739
0
                }
740
0
                else
741
0
                {
742
0
                    bDone = m_pImplLB->ProcessKeyInput( aKeyEvt );
743
0
                }
744
0
            }
745
0
            break;
746
747
0
            case KEY_RETURN:
748
0
            {
749
0
                if ((rNEvt.GetWindow() == m_pSubEdit) && IsInDropDown())
750
0
                {
751
0
                    m_pImplLB->ProcessKeyInput( aKeyEvt );
752
0
                    bDone = true;
753
0
                }
754
0
            }
755
0
            break;
756
0
        }
757
0
    }
758
0
    else if ((rNEvt.GetType() == NotifyEventType::LOSEFOCUS) && m_pFloatWin)
759
0
    {
760
0
        if (m_pFloatWin->HasChildPathFocus())
761
0
            m_pSubEdit->GrabFocus();
762
0
        else if (m_pFloatWin->IsInPopupMode() && !HasChildPathFocus(true))
763
0
            m_pFloatWin->EndPopupMode();
764
0
    }
765
0
    else if( (rNEvt.GetType() == NotifyEventType::COMMAND) &&
766
0
             (rNEvt.GetCommandEvent()->GetCommand() == CommandEventId::Wheel) &&
767
0
             (rNEvt.GetWindow() == m_pSubEdit) )
768
0
    {
769
0
        MouseWheelBehaviour nWheelBehavior( GetSettings().GetMouseSettings().GetWheelBehavior() );
770
0
        if  (   ( nWheelBehavior == MouseWheelBehaviour::ALWAYS )
771
0
            ||  (   ( nWheelBehavior == MouseWheelBehaviour::FocusOnly )
772
0
                &&  HasChildPathFocus()
773
0
                )
774
0
            )
775
0
        {
776
0
            bDone = m_pImplLB->HandleWheelAsCursorTravel(*rNEvt.GetCommandEvent(), *this);
777
0
        }
778
0
        else
779
0
        {
780
0
            bDone = false;  // don't eat this event, let the default handling happen (i.e. scroll the context)
781
0
        }
782
0
    }
783
0
    else if ((rNEvt.GetType() == NotifyEventType::MOUSEBUTTONDOWN)
784
0
            && (rNEvt.GetWindow() == GetMainWindow()))
785
0
    {
786
0
        m_pSubEdit->GrabFocus();
787
0
    }
788
789
0
    return bDone || Edit::EventNotify( rNEvt );
790
0
}
791
792
void ComboBox::SetText( const OUString& rStr )
793
0
{
794
0
    CallEventListeners( VclEventId::ComboboxSetText );
795
796
0
    Edit::SetText( rStr );
797
0
    ImplUpdateFloatSelection();
798
0
}
799
800
void ComboBox::SetText( const OUString& rStr, const Selection& rNewSelection )
801
0
{
802
0
    CallEventListeners( VclEventId::ComboboxSetText );
803
804
0
    Edit::SetText( rStr, rNewSelection );
805
0
    ImplUpdateFloatSelection();
806
0
}
807
808
void ComboBox::Modify()
809
0
{
810
0
    if (!m_isSyntheticModify)
811
0
        ImplUpdateFloatSelection();
812
813
0
    Edit::Modify();
814
0
}
815
816
void ComboBox::ImplUpdateFloatSelection()
817
0
{
818
0
    if (!m_pImplLB || !m_pSubEdit)
819
0
        return;
820
821
    // move text in the ListBox into the visible region
822
0
    m_pImplLB->SetCallSelectionChangedHdl( false );
823
0
    if (!IsMultiSelectionEnabled())
824
0
    {
825
0
        OUString        aSearchStr( m_pSubEdit->GetText() );
826
0
        sal_Int32       nSelect = LISTBOX_ENTRY_NOTFOUND;
827
0
        bool        bSelect = true;
828
829
0
        if (m_pImplLB->GetCurrentPos() != LISTBOX_ENTRY_NOTFOUND)
830
0
        {
831
0
            OUString aCurrent = m_pImplLB->GetEntryList().GetEntryText(
832
0
                                    m_pImplLB->GetCurrentPos());
833
0
            if ( aCurrent == aSearchStr )
834
0
                nSelect = m_pImplLB->GetCurrentPos();
835
0
        }
836
837
0
        if ( nSelect == LISTBOX_ENTRY_NOTFOUND )
838
0
            nSelect = m_pImplLB->GetEntryList().FindEntry( aSearchStr );
839
0
        if ( nSelect == LISTBOX_ENTRY_NOTFOUND )
840
0
        {
841
0
            nSelect = m_pImplLB->GetEntryList().FindMatchingEntry( aSearchStr, 0, true );
842
0
            bSelect = false;
843
0
        }
844
845
0
        if( nSelect != LISTBOX_ENTRY_NOTFOUND )
846
0
        {
847
0
            if (!m_pImplLB->IsVisible(nSelect))
848
0
                m_pImplLB->ShowProminentEntry( nSelect );
849
0
            m_pImplLB->SelectEntry( nSelect, bSelect );
850
0
        }
851
0
        else
852
0
        {
853
0
            nSelect = m_pImplLB->GetEntryList().GetSelectedEntryPos( 0 );
854
0
            if( nSelect != LISTBOX_ENTRY_NOTFOUND )
855
0
                m_pImplLB->SelectEntry( nSelect, false );
856
0
            m_pImplLB->ResetCurrentPos();
857
0
        }
858
0
    }
859
0
    else
860
0
    {
861
0
        ::std::set< sal_Int32 > aSelInText;
862
0
        lcl_GetSelectedEntries(aSelInText, m_pSubEdit->GetText(), m_cMultiSep, m_pImplLB->GetEntryList());
863
0
        for (sal_Int32 n = 0; n < m_pImplLB->GetEntryList().GetEntryCount(); n++)
864
0
            m_pImplLB->SelectEntry( n, aSelInText.count( n ) != 0 );
865
0
    }
866
0
    m_pImplLB->SetCallSelectionChangedHdl( true );
867
0
}
868
869
sal_Int32 ComboBox::InsertEntry(const OUString& rStr, sal_Int32 const nPos)
870
0
{
871
0
    assert(nPos >= 0 && COMBOBOX_MAX_ENTRIES > m_pImplLB->GetEntryList().GetEntryCount());
872
873
0
    sal_Int32 nRealPos;
874
0
    if (nPos == COMBOBOX_APPEND)
875
0
        nRealPos = nPos;
876
0
    else
877
0
    {
878
0
        const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
879
0
        assert(nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
880
0
        nRealPos = nPos + nMRUCount;
881
0
    }
882
883
0
    nRealPos = m_pImplLB->InsertEntry( nRealPos, rStr );
884
0
    nRealPos -= m_pImplLB->GetEntryList().GetMRUCount();
885
0
    CallEventListeners( VclEventId::ComboboxItemAdded, reinterpret_cast<void*>(static_cast<sal_IntPtr>(nRealPos)) );
886
0
    return nRealPos;
887
0
}
888
889
sal_Int32 ComboBox::InsertEntryWithImage(
890
        const OUString& rStr, const Image& rImage, sal_Int32 const nPos)
891
0
{
892
0
    assert(nPos >= 0 && COMBOBOX_MAX_ENTRIES > m_pImplLB->GetEntryList().GetEntryCount());
893
894
0
    sal_Int32 nRealPos;
895
0
    if (nPos == COMBOBOX_APPEND)
896
0
        nRealPos = nPos;
897
0
    else
898
0
    {
899
0
        const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
900
0
        assert(nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
901
0
        nRealPos = nPos + nMRUCount;
902
0
    }
903
904
0
    nRealPos = m_pImplLB->InsertEntry( nRealPos, rStr, rImage );
905
0
    nRealPos -= m_pImplLB->GetEntryList().GetMRUCount();
906
0
    CallEventListeners( VclEventId::ComboboxItemAdded, reinterpret_cast<void*>(static_cast<sal_IntPtr>(nRealPos)) );
907
0
    return nRealPos;
908
0
}
909
910
void ComboBox::RemoveEntryAt(sal_Int32 const nPos)
911
0
{
912
0
    const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
913
0
    assert(nPos >= 0 && nPos <= COMBOBOX_MAX_ENTRIES - nMRUCount);
914
0
    m_pImplLB->RemoveEntry( nPos + nMRUCount );
915
0
    CallEventListeners( VclEventId::ComboboxItemRemoved, reinterpret_cast<void*>(static_cast<sal_IntPtr>(nPos)) );
916
0
}
917
918
void ComboBox::Clear()
919
0
{
920
0
    if (!m_pImplLB)
921
0
        return;
922
0
    m_pImplLB->Clear();
923
0
    CallEventListeners( VclEventId::ComboboxItemRemoved, reinterpret_cast<void*>(sal_IntPtr(-1)) );
924
0
}
925
926
Image ComboBox::GetEntryImage( sal_Int32 nPos ) const
927
0
{
928
0
    if (m_pImplLB->GetEntryList().HasEntryImage(nPos))
929
0
        return m_pImplLB->GetEntryList().GetEntryImage( nPos );
930
0
    return Image();
931
0
}
932
933
sal_Int32 ComboBox::GetEntryPos( std::u16string_view rStr ) const
934
0
{
935
0
    sal_Int32 nPos = m_pImplLB->GetEntryList().FindEntry( rStr );
936
0
    if ( nPos != LISTBOX_ENTRY_NOTFOUND )
937
0
        nPos -= m_pImplLB->GetEntryList().GetMRUCount();
938
0
    return nPos;
939
0
}
940
941
OUString ComboBox::GetEntry( sal_Int32 nPos ) const
942
0
{
943
0
    const sal_Int32 nMRUCount = m_pImplLB->GetEntryList().GetMRUCount();
944
0
    if (nPos < 0 || nPos > COMBOBOX_MAX_ENTRIES - nMRUCount)
945
0
        return OUString();
946
947
0
    return m_pImplLB->GetEntryList().GetEntryText( nPos + nMRUCount );
948
0
}
949
950
sal_Int32 ComboBox::GetEntryCount() const
951
0
{
952
0
    if (!m_pImplLB)
953
0
        return 0;
954
0
    return m_pImplLB->GetEntryList().GetEntryCount() - m_pImplLB->GetEntryList().GetMRUCount();
955
0
}
956
957
bool ComboBox::IsTravelSelect() const
958
0
{
959
0
    return m_pImplLB->IsTravelSelect();
960
0
}
961
962
bool ComboBox::IsInDropDown() const
963
0
{
964
    // when the dropdown is dismissed, first mbInPopupMode is set to false, and on the next event iteration then
965
    // mbPopupMode is set to false
966
0
    return m_pFloatWin && m_pFloatWin->IsInPopupMode() && m_pFloatWin->ImplIsInPrivatePopupMode();
967
0
}
968
969
bool ComboBox::IsMultiSelectionEnabled() const
970
0
{
971
0
    return m_pImplLB->IsMultiSelectionEnabled();
972
0
}
973
974
0
void ComboBox::SetSelectHdl(const Link<ComboBox&,void>& rLink) { m_SelectHdl = rLink; }
975
976
void ComboBox::SetEntryActivateHdl(const Link<Edit&,bool>& rLink)
977
0
{
978
0
    if (!m_pSubEdit)
979
0
        return;
980
0
    m_pSubEdit->SetActivateHdl(rLink);
981
0
}
982
983
Size ComboBox::GetOptimalSize() const
984
0
{
985
0
    return CalcMinimumSize();
986
0
}
987
988
tools::Long ComboBox::getMaxWidthScrollBarAndDownButton() const
989
0
{
990
0
    tools::Long nButtonDownWidth = 0;
991
992
0
    vcl::Window *pBorder = GetWindow( GetWindowType::Border );
993
0
    ImplControlValue aControlValue;
994
0
    tools::Rectangle aContent, aBound;
995
996
    // use the full extent of the control
997
0
    const tools::Rectangle aArea(Point(), pBorder->GetOutputSizePixel());
998
999
0
    if ( GetNativeControlRegion(ControlType::Combobox, ControlPart::ButtonDown,
1000
0
        aArea, ControlState::NONE, aControlValue, aBound, aContent) )
1001
0
    {
1002
0
        nButtonDownWidth = aContent.getOpenWidth();
1003
0
    }
1004
1005
0
    const tools::Long nScrollBarWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
1006
1007
0
    return std::max(nScrollBarWidth, nButtonDownWidth);
1008
0
}
1009
1010
Size ComboBox::CalcMinimumSize() const
1011
0
{
1012
0
    Size aSz;
1013
1014
0
    if (!m_pImplLB)
1015
0
        return aSz;
1016
1017
0
    if (!IsDropDownBox())
1018
0
    {
1019
0
        aSz = m_pImplLB->CalcSize( m_pImplLB->GetEntryList().GetEntryCount() );
1020
0
        aSz.AdjustHeight(m_nDDHeight );
1021
0
    }
1022
0
    else
1023
0
    {
1024
0
        aSz.setHeight( Edit::CalcMinimumSizeForText(GetText()).Height() );
1025
1026
0
        if (m_nWidthInChars!= -1)
1027
0
            aSz.setWidth(m_nWidthInChars * approximate_digit_width());
1028
0
        else
1029
0
            aSz.setWidth(m_pImplLB->GetMaxEntryWidth());
1030
0
    }
1031
1032
0
    if (m_nMaxWidthChars != -1)
1033
0
    {
1034
0
        tools::Long nMaxWidth = m_nMaxWidthChars * approximate_char_width();
1035
0
        aSz.setWidth( std::min(aSz.Width(), nMaxWidth) );
1036
0
    }
1037
1038
0
    if (IsDropDownBox())
1039
0
        aSz.AdjustWidth(getMaxWidthScrollBarAndDownButton() );
1040
1041
0
    ComboBoxBounds aBounds(calcComboBoxDropDownComponentBounds(
1042
0
        Size(0xFFFF, 0xFFFF), Size(0xFFFF, 0xFFFF)));
1043
0
    aSz.AdjustWidth(aBounds.aSubEditPos.X()*2 );
1044
1045
0
    aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
1046
1047
0
    aSz = CalcWindowSize( aSz );
1048
0
    return aSz;
1049
0
}
1050
1051
Size ComboBox::CalcAdjustedSize( const Size& rPrefSize ) const
1052
0
{
1053
0
    Size aSz = rPrefSize;
1054
0
    sal_Int32 nLeft, nTop, nRight, nBottom;
1055
0
    static_cast<vcl::Window*>(const_cast<ComboBox *>(this))->GetBorder( nLeft, nTop, nRight, nBottom );
1056
0
    aSz.AdjustHeight( -(nTop+nBottom) );
1057
0
    if ( !IsDropDownBox() )
1058
0
    {
1059
0
        tools::Long nEntryHeight = CalcBlockSize( 1, 1 ).Height();
1060
0
        tools::Long nLines = aSz.Height() / nEntryHeight;
1061
0
        if ( nLines < 1 )
1062
0
            nLines = 1;
1063
0
        aSz.setHeight( nLines * nEntryHeight );
1064
0
        aSz.AdjustHeight(m_nDDHeight );
1065
0
    }
1066
0
    else
1067
0
    {
1068
0
        aSz.setHeight( m_nDDHeight );
1069
0
    }
1070
0
    aSz.AdjustHeight(nTop+nBottom );
1071
1072
0
    aSz = CalcWindowSize( aSz );
1073
0
    return aSz;
1074
0
}
1075
1076
Size ComboBox::CalcBlockSize( sal_uInt16 nColumns, sal_uInt16 nLines ) const
1077
0
{
1078
    // show ScrollBars where appropriate
1079
0
    const Size aMinSz = CalcMinimumSize();
1080
0
    Size aSz;
1081
1082
    // height
1083
0
    if ( nLines )
1084
0
    {
1085
0
        if ( !IsDropDownBox() )
1086
0
            aSz.setHeight( m_pImplLB->CalcSize( nLines ).Height() + m_nDDHeight );
1087
0
        else
1088
0
            aSz.setHeight( m_nDDHeight );
1089
0
    }
1090
0
    else
1091
0
        aSz.setHeight( aMinSz.Height() );
1092
1093
    // width
1094
0
    if ( nColumns )
1095
0
        aSz.setWidth( nColumns * approximate_char_width() );
1096
0
    else
1097
0
        aSz.setWidth( aMinSz.Width() );
1098
1099
0
    if ( IsDropDownBox() )
1100
0
        aSz.AdjustWidth(getMaxWidthScrollBarAndDownButton() );
1101
1102
0
    if ( !IsDropDownBox() )
1103
0
    {
1104
0
        if ( aSz.Width() < aMinSz.Width() )
1105
0
            aSz.AdjustHeight(GetSettings().GetStyleSettings().GetScrollBarSize() );
1106
0
        if ( aSz.Height() < aMinSz.Height() )
1107
0
            aSz.AdjustWidth(GetSettings().GetStyleSettings().GetScrollBarSize() );
1108
0
    }
1109
1110
0
    aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
1111
1112
0
    aSz = CalcWindowSize( aSz );
1113
0
    return aSz;
1114
0
}
1115
1116
tools::Long ComboBox::GetDropDownEntryHeight() const
1117
0
{
1118
0
    return m_pImplLB->GetEntryHeight();
1119
0
}
1120
1121
void ComboBox::GetMaxVisColumnsAndLines( sal_uInt16& rnCols, sal_uInt16& rnLines ) const
1122
0
{
1123
0
    const tools::Long nCharWidth = GetTextWidth(OUString(u'x'));
1124
0
    if ( !IsDropDownBox() )
1125
0
    {
1126
0
        const Size aOutSz = GetMainWindow()->GetOutputSizePixel();
1127
0
        rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1;
1128
0
        rnLines = static_cast<sal_uInt16>(aOutSz.Height()/GetDropDownEntryHeight());
1129
0
    }
1130
0
    else
1131
0
    {
1132
0
        const Size aOutSz = m_pSubEdit->GetOutputSizePixel();
1133
0
        rnCols = (nCharWidth > 0) ? static_cast<sal_uInt16>(aOutSz.Width()/nCharWidth) : 1;
1134
0
        rnLines = 1;
1135
0
    }
1136
0
}
1137
1138
css::uno::Reference<css::accessibility::XAccessible> ComboBox::CreateAccessible()
1139
0
{
1140
0
    const bool bIsDropDownBox = (GetStyle() & WB_DROPDOWN) == WB_DROPDOWN;
1141
0
    if (bIsDropDownBox)
1142
0
        return new VCLXAccessibleDropDownComboBox(this);
1143
1144
0
    return new VCLXAccessibleComboBox(this);
1145
0
}
1146
1147
void ComboBox::Draw(OutputDevice* pDev, const Point& rPos, SystemTextColorFlags eFlags)
1148
0
{
1149
0
    GetMainWindow()->ApplySettings(*pDev);
1150
1151
0
    const Point aPos = pDev->LogicToPixel( rPos );
1152
0
    const Size aSize = GetSizePixel();
1153
0
    const vcl::Font aFont = GetMainWindow()->GetDrawPixelFont( pDev );
1154
1155
0
    pDev->Push();
1156
0
    pDev->SetMapMode();
1157
0
    pDev->SetFont( aFont );
1158
0
    pDev->SetTextFillColor();
1159
1160
    // Border/Background
1161
0
    pDev->SetLineColor();
1162
0
    pDev->SetFillColor();
1163
0
    const bool bBorder = (GetStyle() & WB_BORDER);
1164
0
    const bool bBackground = IsControlBackground();
1165
0
    if ( bBorder || bBackground )
1166
0
    {
1167
0
        tools::Rectangle aRect( aPos, aSize );
1168
        // aRect.Top() += nEditHeight;
1169
0
        if ( bBorder )
1170
0
        {
1171
0
            ImplDrawFrame( pDev, aRect );
1172
0
        }
1173
0
        if ( bBackground )
1174
0
        {
1175
0
            pDev->SetFillColor( GetControlBackground() );
1176
0
            pDev->DrawRect( aRect );
1177
0
        }
1178
0
    }
1179
1180
    // contents
1181
0
    if ( !IsDropDownBox() )
1182
0
    {
1183
0
        const tools::Long nOnePixel = GetDrawPixel(pDev, 1);
1184
0
        const tools::Long nTextHeight = pDev->GetTextHeight();
1185
0
        const tools::Long nEditHeight = nTextHeight + 6 * nOnePixel;
1186
0
        DrawTextFlags eTextStyle = DrawTextFlags::VCenter;
1187
1188
        // First, draw the edit part
1189
0
        Size aOrigSize(m_pSubEdit->GetSizePixel());
1190
0
        m_pSubEdit->SetSizePixel(Size(aSize.Width(), nEditHeight));
1191
0
        m_pSubEdit->Draw(pDev, aPos, eFlags);
1192
0
        m_pSubEdit->SetSizePixel(aOrigSize);
1193
1194
        // Second, draw the listbox
1195
0
        if ( GetStyle() & WB_CENTER )
1196
0
            eTextStyle |= DrawTextFlags::Center;
1197
0
        else if ( GetStyle() & WB_RIGHT )
1198
0
            eTextStyle |= DrawTextFlags::Right;
1199
0
        else
1200
0
            eTextStyle |= DrawTextFlags::Left;
1201
1202
0
        if (eFlags & SystemTextColorFlags::Mono)
1203
0
        {
1204
0
            pDev->SetTextColor( COL_BLACK );
1205
0
        }
1206
0
        else
1207
0
        {
1208
0
            if ( !IsEnabled() )
1209
0
            {
1210
0
                const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
1211
0
                pDev->SetTextColor( rStyleSettings.GetDisableColor() );
1212
0
            }
1213
0
            else
1214
0
            {
1215
0
                pDev->SetTextColor( GetTextColor() );
1216
0
            }
1217
0
        }
1218
1219
0
        tools::Rectangle aClip( aPos, aSize );
1220
0
        pDev->IntersectClipRegion( aClip );
1221
0
        sal_Int32 nLines = static_cast<sal_Int32>( nTextHeight > 0 ? (aSize.Height()-nEditHeight)/nTextHeight : 1 );
1222
0
        if ( !nLines )
1223
0
            nLines = 1;
1224
0
        const sal_Int32 nTEntry = IsReallyVisible() ? m_pImplLB->GetTopEntry() : 0;
1225
1226
0
        tools::Rectangle aTextRect( aPos, aSize );
1227
1228
0
        aTextRect.AdjustLeft(3*nOnePixel );
1229
0
        aTextRect.AdjustRight( -(3*nOnePixel) );
1230
0
        aTextRect.AdjustTop(nEditHeight + nOnePixel );
1231
0
        aTextRect.SetBottom( aTextRect.Top() + nTextHeight );
1232
1233
        // the drawing starts here
1234
0
        for ( sal_Int32 n = 0; n < nLines; ++n )
1235
0
        {
1236
0
            pDev->DrawText(aTextRect, m_pImplLB->GetEntryList().GetEntryText(n + nTEntry), eTextStyle);
1237
0
            aTextRect.AdjustTop(nTextHeight );
1238
0
            aTextRect.AdjustBottom(nTextHeight );
1239
0
        }
1240
0
    }
1241
1242
0
    pDev->Pop();
1243
1244
    // Call Edit::Draw after restoring the MapMode...
1245
0
    if ( IsDropDownBox() )
1246
0
    {
1247
0
        const Size aOrigSize(m_pSubEdit->GetSizePixel());
1248
0
        m_pSubEdit->SetSizePixel(GetSizePixel());
1249
0
        m_pSubEdit->Draw(pDev, rPos, eFlags);
1250
0
        m_pSubEdit->SetSizePixel(aOrigSize);
1251
        // DD-Button ?
1252
0
    }
1253
0
}
1254
1255
void ComboBox::SetUserDrawHdl(const Link<UserDrawEvent*, void>& rLink)
1256
0
{
1257
0
    m_pImplLB->SetUserDrawHdl(rLink);
1258
0
}
1259
1260
void ComboBox::SetUserItemSize( const Size& rSz )
1261
0
{
1262
0
    GetMainWindow()->SetUserItemSize( rSz );
1263
0
}
1264
1265
void ComboBox::EnableUserDraw( bool bUserDraw )
1266
0
{
1267
0
    GetMainWindow()->EnableUserDraw( bUserDraw );
1268
0
}
1269
1270
bool ComboBox::IsUserDrawEnabled() const
1271
0
{
1272
0
    return GetMainWindow()->IsUserDrawEnabled();
1273
0
}
1274
1275
void ComboBox::DrawEntry(const UserDrawEvent& rEvt)
1276
0
{
1277
0
    GetMainWindow()->DrawEntry(*rEvt.GetRenderContext(), rEvt.GetItemId(), /*bDrawImage*/false, /*bDrawText*/false);
1278
0
}
1279
1280
void ComboBox::AddSeparator( sal_Int32 n )
1281
0
{
1282
0
    m_pImplLB->AddSeparator( n );
1283
0
}
1284
1285
void ComboBox::SetMRUEntries( std::u16string_view rEntries )
1286
0
{
1287
0
    m_pImplLB->SetMRUEntries( rEntries, ';' );
1288
0
}
1289
1290
OUString ComboBox::GetMRUEntries() const
1291
0
{
1292
0
    return m_pImplLB ? m_pImplLB->GetMRUEntries( ';' ) : OUString();
1293
0
}
1294
1295
void ComboBox::SetMaxMRUCount( sal_Int32 n )
1296
0
{
1297
0
    m_pImplLB->SetMaxMRUCount( n );
1298
0
}
1299
1300
sal_Int32 ComboBox::GetMaxMRUCount() const
1301
0
{
1302
0
    return m_pImplLB ? m_pImplLB->GetMaxMRUCount() : 0;
1303
0
}
1304
1305
sal_uInt16 ComboBox::GetDisplayLineCount() const
1306
0
{
1307
0
    return m_pImplLB ? m_pImplLB->GetDisplayLineCount() : 0;
1308
0
}
1309
1310
void ComboBox::SetEntryData( sal_Int32 nPos, void* pNewData )
1311
0
{
1312
0
    m_pImplLB->SetEntryData( nPos + m_pImplLB->GetEntryList().GetMRUCount(), pNewData );
1313
0
}
1314
1315
void* ComboBox::GetEntryData( sal_Int32 nPos ) const
1316
0
{
1317
0
    return m_pImplLB->GetEntryList().GetEntryData(
1318
0
            nPos + m_pImplLB->GetEntryList().GetMRUCount() );
1319
0
}
1320
1321
sal_Int32 ComboBox::GetTopEntry() const
1322
0
{
1323
0
    sal_Int32 nPos = GetEntryCount() ? m_pImplLB->GetTopEntry() : LISTBOX_ENTRY_NOTFOUND;
1324
0
    if (nPos < m_pImplLB->GetEntryList().GetMRUCount())
1325
0
        nPos = 0;
1326
0
    return nPos;
1327
0
}
1328
1329
tools::Rectangle ComboBox::GetDropDownPosSizePixel() const
1330
0
{
1331
0
    return m_pFloatWin
1332
0
        ? m_pFloatWin->GetWindowExtentsRelative(*this)
1333
0
        : tools::Rectangle();
1334
0
}
1335
1336
const Wallpaper& ComboBox::GetDisplayBackground() const
1337
0
{
1338
0
    if (!m_pSubEdit->IsBackground())
1339
0
        return Control::GetDisplayBackground();
1340
1341
0
    const Wallpaper& rBack = m_pSubEdit->GetBackground();
1342
0
    if( ! rBack.IsBitmap() &&
1343
0
        ! rBack.IsGradient() &&
1344
0
        rBack == Wallpaper(COL_TRANSPARENT)
1345
0
        )
1346
0
        return Control::GetDisplayBackground();
1347
0
    return rBack;
1348
0
}
1349
1350
sal_Int32 ComboBox::GetSelectedEntryCount() const
1351
0
{
1352
0
    return m_pImplLB->GetEntryList().GetSelectedEntryCount();
1353
0
}
1354
1355
sal_Int32 ComboBox::GetSelectedEntryPos( sal_Int32 nIndex ) const
1356
0
{
1357
0
    sal_Int32 nPos = m_pImplLB->GetEntryList().GetSelectedEntryPos( nIndex );
1358
0
    if ( nPos != LISTBOX_ENTRY_NOTFOUND )
1359
0
    {
1360
0
        if (nPos < m_pImplLB->GetEntryList().GetMRUCount())
1361
0
            nPos = m_pImplLB->GetEntryList().FindEntry(m_pImplLB->GetEntryList().GetEntryText(nPos));
1362
0
        nPos = sal::static_int_cast<sal_Int32>(nPos - m_pImplLB->GetEntryList().GetMRUCount());
1363
0
    }
1364
0
    return nPos;
1365
0
}
1366
1367
bool ComboBox::IsEntryPosSelected( sal_Int32 nPos ) const
1368
0
{
1369
0
    return m_pImplLB->GetEntryList().IsEntryPosSelected(
1370
0
            nPos + m_pImplLB->GetEntryList().GetMRUCount() );
1371
0
}
1372
1373
void ComboBox::SelectEntryPos( sal_Int32 nPos, bool bSelect)
1374
0
{
1375
0
    if (nPos < m_pImplLB->GetEntryList().GetEntryCount())
1376
0
        m_pImplLB->SelectEntry(
1377
0
            nPos + m_pImplLB->GetEntryList().GetMRUCount(), bSelect);
1378
0
}
1379
1380
void ComboBox::SetNoSelection()
1381
0
{
1382
0
    m_pImplLB->SetNoSelection();
1383
0
    m_pSubEdit->SetText(OUString());
1384
0
}
1385
1386
tools::Rectangle ComboBox::GetBoundingRectangle( sal_Int32 nItem ) const
1387
0
{
1388
0
    tools::Rectangle aRect = GetMainWindow()->GetBoundingRectangle( nItem );
1389
0
    const tools::Rectangle aOffset = GetMainWindow()->GetWindowExtentsRelative( *static_cast<vcl::Window*>(const_cast<ComboBox *>(this)) );
1390
0
    aRect.Move( aOffset.Left(), aOffset.Top() );
1391
0
    return aRect;
1392
0
}
1393
1394
void ComboBox::SetBorderStyle( WindowBorderStyle nBorderStyle )
1395
0
{
1396
0
    Window::SetBorderStyle( nBorderStyle );
1397
0
    if ( !IsDropDownBox() )
1398
0
    {
1399
0
        m_pSubEdit->SetBorderStyle(nBorderStyle);
1400
0
        m_pImplLB->SetBorderStyle( nBorderStyle );
1401
0
    }
1402
0
}
1403
1404
void ComboBox::SetHighlightColor( const Color& rColor )
1405
0
{
1406
0
    AllSettings aSettings(GetSettings());
1407
0
    StyleSettings aStyle(aSettings.GetStyleSettings());
1408
0
    aStyle.SetHighlightColor(rColor);
1409
0
    aSettings.SetStyleSettings(aStyle);
1410
0
    SetSettings(aSettings);
1411
1412
0
    AllSettings aSettingsSubEdit(m_pSubEdit->GetSettings());
1413
0
    StyleSettings aStyleSubEdit(aSettingsSubEdit.GetStyleSettings());
1414
0
    aStyleSubEdit.SetHighlightColor(rColor);
1415
0
    aSettingsSubEdit.SetStyleSettings(aStyleSubEdit);
1416
0
    m_pSubEdit->SetSettings(aSettings);
1417
1418
0
    m_pImplLB->SetHighlightColor(rColor);
1419
0
}
1420
1421
void ComboBox::SetHighlightTextColor( const Color& rColor )
1422
0
{
1423
0
    AllSettings aSettings(GetSettings());
1424
0
    StyleSettings aStyle(aSettings.GetStyleSettings());
1425
0
    aStyle.SetHighlightTextColor(rColor);
1426
0
    aSettings.SetStyleSettings(aStyle);
1427
0
    SetSettings(aSettings);
1428
1429
0
    AllSettings aSettingsSubEdit(m_pSubEdit->GetSettings());
1430
0
    StyleSettings aStyleSubEdit(aSettingsSubEdit.GetStyleSettings());
1431
0
    aStyleSubEdit.SetHighlightTextColor(rColor);
1432
0
    aSettingsSubEdit.SetStyleSettings(aStyleSubEdit);
1433
0
    m_pSubEdit->SetSettings(aSettings);
1434
1435
0
    m_pImplLB->SetHighlightTextColor(rColor);
1436
0
}
1437
1438
ImplListBoxWindow* ComboBox::GetMainWindow() const
1439
0
{
1440
0
    return m_pImplLB->GetMainWindow();
1441
0
}
1442
1443
tools::Long ComboBox::GetIndexForPoint( const Point& rPoint, sal_Int32& rPos ) const
1444
0
{
1445
0
    if( !HasLayoutData() )
1446
0
        FillLayoutData();
1447
1448
    // check whether rPoint fits at all
1449
0
    tools::Long nIndex = Control::GetIndexForPoint( rPoint );
1450
0
    if( nIndex != -1 )
1451
0
    {
1452
        // point must be either in main list window
1453
        // or in impl window (dropdown case)
1454
0
        ImplListBoxWindow* rMain = GetMainWindow();
1455
1456
        // convert coordinates to ImplListBoxWindow pixel coordinate space
1457
0
        Point aConvPoint = LogicToPixel( rPoint );
1458
0
        const AbsoluteScreenPixelPoint aConvPointAbs = OutputToAbsoluteScreenPixel( aConvPoint );
1459
0
        aConvPoint = rMain->AbsoluteScreenToOutputPixel( aConvPointAbs );
1460
0
        aConvPoint = rMain->PixelToLogic( aConvPoint );
1461
1462
        // try to find entry
1463
0
        sal_Int32 nEntry = rMain->GetEntryPosForPoint( aConvPoint );
1464
0
        if( nEntry == LISTBOX_ENTRY_NOTFOUND )
1465
0
            nIndex = -1;
1466
0
        else
1467
0
            rPos = nEntry;
1468
0
    }
1469
1470
    // get line relative index
1471
0
    if( nIndex != -1 )
1472
0
        nIndex = ToRelativeLineIndex( nIndex );
1473
1474
0
    return nIndex;
1475
0
}
1476
1477
ComboBoxBounds ComboBox::calcComboBoxDropDownComponentBounds(
1478
    const Size &rOutSz, const Size &rBorderOutSz) const
1479
0
{
1480
0
    ComboBoxBounds aBounds;
1481
1482
0
    tools::Long    nTop = 0;
1483
0
    const tools::Long nBottom = rOutSz.Height();
1484
1485
0
    vcl::Window *pBorder = GetWindow(GetWindowType::Border);
1486
0
    ImplControlValue aControlValue;
1487
0
    Point aPoint;
1488
0
    tools::Rectangle aContent, aBound;
1489
1490
    // use the full extent of the control
1491
0
    tools::Rectangle aArea( aPoint, rBorderOutSz );
1492
1493
0
    if (GetNativeControlRegion(ControlType::Combobox, ControlPart::ButtonDown,
1494
0
            aArea, ControlState::NONE, aControlValue, aBound, aContent) )
1495
0
    {
1496
        // convert back from border space to local coordinates
1497
0
        aPoint = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aPoint));
1498
0
        aContent.Move(-aPoint.X(), -aPoint.Y());
1499
1500
0
        aBounds.aButtonPos = Point(aContent.Left(), nTop);
1501
0
        aBounds.aButtonSize = Size(aContent.getOpenWidth(), (nBottom-nTop));
1502
1503
        // adjust the size of the edit field
1504
0
        if (GetNativeControlRegion(ControlType::Combobox, ControlPart::SubEdit,
1505
0
                    aArea, ControlState::NONE, aControlValue, aBound, aContent) )
1506
0
        {
1507
            // convert back from border space to local coordinates
1508
0
            aContent.Move(-aPoint.X(), -aPoint.Y());
1509
1510
            // use the themes drop down size
1511
0
            aBounds.aSubEditPos = aContent.TopLeft();
1512
0
            aBounds.aSubEditSize = aContent.GetSize();
1513
0
        }
1514
0
        else
1515
0
        {
1516
            // use the themes drop down size for the button
1517
0
            aBounds.aSubEditSize = Size(rOutSz.Width() - aContent.getOpenWidth(), rOutSz.Height());
1518
0
        }
1519
0
    }
1520
0
    else
1521
0
    {
1522
0
        tools::Long nSBWidth = GetSettings().GetStyleSettings().GetScrollBarSize();
1523
0
        nSBWidth = CalcZoom( nSBWidth );
1524
0
        aBounds.aSubEditSize = Size(rOutSz.Width() - nSBWidth, rOutSz.Height());
1525
0
        aBounds.aButtonPos = Point(rOutSz.Width() - nSBWidth, nTop);
1526
0
        aBounds.aButtonSize = Size(nSBWidth, (nBottom-nTop));
1527
0
    }
1528
0
    return aBounds;
1529
0
}
1530
1531
void ComboBox::SetWidthInChars(sal_Int32 nWidthInChars)
1532
0
{
1533
0
    if (nWidthInChars != m_nWidthInChars)
1534
0
    {
1535
0
        m_nWidthInChars = nWidthInChars;
1536
0
        queue_resize();
1537
0
    }
1538
0
}
1539
1540
void ComboBox::setMaxWidthChars(sal_Int32 nWidth)
1541
0
{
1542
0
    if (nWidth != m_nMaxWidthChars)
1543
0
    {
1544
0
        m_nMaxWidthChars = nWidth;
1545
0
        queue_resize();
1546
0
    }
1547
0
}
1548
1549
bool ComboBox::set_property(const OUString &rKey, const OUString &rValue)
1550
0
{
1551
0
    if (rKey == "width-chars")
1552
0
        SetWidthInChars(rValue.toInt32());
1553
0
    else if (rKey == "max-width-chars")
1554
0
        setMaxWidthChars(rValue.toInt32());
1555
0
    else if (rKey == "can-focus")
1556
0
    {
1557
        // as far as I can see in Gtk, setting a ComboBox as can.focus means
1558
        // the focus gets stuck in it, so try here to behave like gtk does
1559
        // with the settings that work, i.e. can.focus of false doesn't
1560
        // set the hard WB_NOTABSTOP
1561
0
        WinBits eBits = GetStyle();
1562
0
        eBits &= ~(WB_TABSTOP|WB_NOTABSTOP);
1563
0
        if (toBool(rValue))
1564
0
            eBits |= WB_TABSTOP;
1565
0
        SetStyle(eBits);
1566
0
    }
1567
0
    else if (rKey == "placeholder-text")
1568
0
        SetPlaceholderText(rValue);
1569
0
    else
1570
0
        return Control::set_property(rKey, rValue);
1571
0
    return true;
1572
0
}
1573
1574
FactoryFunction ComboBox::GetUITestFactory() const
1575
0
{
1576
0
    return ComboBoxUIObject::create;
1577
0
}
1578
1579
void ComboBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
1580
0
{
1581
0
    Control::DumpAsPropertyTree(rJsonWriter);
1582
1583
0
    {
1584
0
        auto entriesNode = rJsonWriter.startArray("entries");
1585
0
        for (int i = 0; i < GetEntryCount(); ++i)
1586
0
        {
1587
0
            rJsonWriter.putSimpleValue(GetEntry(i));
1588
0
        }
1589
0
    }
1590
1591
0
    {
1592
0
        auto selectedNode = rJsonWriter.startArray("selectedEntries");
1593
0
        for (int i = 0; i < GetSelectedEntryCount(); ++i)
1594
0
        {
1595
0
            rJsonWriter.putSimpleValue(OUString::number(GetSelectedEntryPos(i)));
1596
0
        }
1597
0
    }
1598
1599
0
    rJsonWriter.put("selectedCount", GetSelectedEntryCount());
1600
1601
0
    if (IsUserDrawEnabled())
1602
0
        rJsonWriter.put("customEntryRenderer", true);
1603
0
}
1604
1605
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */